source: geovis/trunk/RenderServer.cpp @ 4632

Last change on this file since 4632 was 4632, checked in by ldelgass, 10 years ago

Add pin annotations for testing

File size: 11.6 KB
RevLine 
[3998]1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2013  HUBzero Foundation, LLC
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <cstdio>
9#include <cstring>
10#include <cstdlib>
11#include <cerrno>
12#include <unistd.h>
13#include <signal.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <fcntl.h>
17#include <sys/time.h>
18#include <sys/times.h>
19
20#include <string>
21#include <sstream>
22
23#include <tcl.h>
24
25#include "Trace.h"
26#include "ReadBuffer.h"
27#include "RenderServer.h"
28#include "RendererCmd.h"
29#include "Renderer.h"
[4349]30#include "Stats.h"
[3998]31#include "PPMWriter.h"
32#include "TGAWriter.h"
33#ifdef USE_THREADS
34#include <pthread.h>
35#include "ResponseQueue.h"
[4028]36#ifdef USE_READ_THREAD
37#include "CommandQueue.h"
38#endif
[3998]39#endif
40
41using namespace GeoVis;
42
43Stats GeoVis::g_stats;
44
45int GeoVis::g_statsFile = -1; ///< Stats output file descriptor.
46int GeoVis::g_fdIn = STDIN_FILENO; ///< Input file descriptor
47int GeoVis::g_fdOut = STDOUT_FILENO; ///< Output file descriptor
[4105]48FILE *GeoVis::g_fOut = NULL; ///< Output file handle
[3998]49FILE *GeoVis::g_fLog = NULL; ///< Trace logging file handle
50Renderer *GeoVis::g_renderer = NULL; ///< Main render worker
51ReadBuffer *GeoVis::g_inBufPtr = NULL; ///< Socket read buffer
[4028]52#ifdef USE_THREADS
53ResponseQueue *GeoVis::g_outQueue = NULL;
54#ifdef USE_READER_THREAD
55CommandQueue *GeoVis::g_inQueue = NULL;
56#endif
57#endif
[3998]58
[4328]59static int
60queueViewpoint()
61{
62    osgEarth::Viewpoint view = g_renderer->getViewpoint();
63
64    std::ostringstream oss;
65    size_t len = 0;
66    oss << "nv>camera get "
67        << view.x() << " "
68        << view.y() << " "
69        << view.z() << " "
70        << view.getHeading() << " "
71        << view.getPitch() << " "
72        << view.getRange()
73        << " {" << ((view.getSRS() == NULL) ? "" : view.getSRS()->getHorizInitString()) << "}"
74        << " {" << ((view.getSRS() == NULL) ? "" : view.getSRS()->getVertInitString()) << "}"
75        << "\n";
[4424]76    std::string ostr = oss.str();
77    len = ostr.size();
[3998]78#ifdef USE_THREADS
[4424]79    queueResponse(ostr.c_str(), len, Response::VOLATILE);
[4328]80#else
[4424]81    ssize_t bytesWritten = SocketWrite(ostr.c_str(), len);
[4328]82
83    if (bytesWritten < 0) {
84        return TCL_ERROR;
85    }
86#endif /*USE_THREADS*/
87    return TCL_OK;
88}
89
90#ifdef USE_THREADS
[3998]91static void
92queueFrame(ResponseQueue *queue, const unsigned char *imgData)
93{
[4009]94#ifdef DEBUG_WRITE_FRAME_FILE
[3998]95
96#ifdef RENDER_TARGA
97    writeTGAFile("/tmp/frame.tga",
98                 imgData,
99                 g_renderer->getWindowWidth(),
100                 g_renderer->getWindowHeight(),
101                 TARGA_BYTES_PER_PIXEL);
102#else
103    writeTGAFile("/tmp/frame.tga",
104                 imgData,
105                 g_renderer->getWindowWidth(),
106                 g_renderer->getWindowHeight(),
[4028]107                 TARGA_BYTES_PER_PIXEL,
108                 true);
[3998]109#endif  /*RENDER_TARGA*/
110
111#else
112 
113#ifdef RENDER_TARGA
114    queueTGA(queue, "nv>image -type image -bytes",
115             imgData,
116             g_renderer->getWindowWidth(),
117             g_renderer->getWindowHeight(),
118             TARGA_BYTES_PER_PIXEL);
119#else
[4632]120    char cmd[256];
121    sprintf(cmd, "nv>image -type image -token %lu -bytes", g_stats.nCommands);
122    queuePPM(queue, cmd,
[3998]123             imgData,
124             g_renderer->getWindowWidth(),
125             g_renderer->getWindowHeight());
126#endif  /*RENDER_TARGA*/
[4028]127#endif  /*DEBUG_WRITE_FRAME_FILE*/
[3998]128}
129
130#else
131
132static void
133writeFrame(int fd, const unsigned char *imgData)
134{
[4028]135#ifdef DEBUG_WRITE_FRAME_FILE
[3998]136
137#ifdef RENDER_TARGA
138    writeTGAFile("/tmp/frame.tga",
139                 imgData,
140                 g_renderer->getWindowWidth(),
141                 g_renderer->getWindowHeight(),
142                 TARGA_BYTES_PER_PIXEL);
143#else
144    writeTGAFile("/tmp/frame.tga",
145                 imgData,
146                 g_renderer->getWindowWidth(),
147                 g_renderer->getWindowHeight(),
148                 TARGA_BYTES_PER_PIXEL,
149                 true);
150#endif  /*RENDER_TARGA*/
151
152#else
153
154#ifdef RENDER_TARGA
[4028]155    writeTGA(fd, "nv>image -type image -bytes",
156             imgData,
157             g_renderer->getWindowWidth(),
158             g_renderer->getWindowHeight(),
159             TARGA_BYTES_PER_PIXEL);
[3998]160#else
[4028]161    writePPM(fd, "nv>image -type image -bytes",
162             imgData,
163             g_renderer->getWindowWidth(),
164             g_renderer->getWindowHeight());
[3998]165#endif  /*RENDER_TARGA*/
[4028]166#endif  /*DEBUG_WRITE_FRAME_FILE*/
[3998]167}
168#endif /*USE_THREADS*/
169
170static int
171sendAck()
172{
173    std::ostringstream oss;
[4424]174    oss << "nv>ok -token " << g_stats.nCommands << "\n";
175    std::string ostr = oss.str();
176    int nBytes = ostr.length();
[3998]177
178    TRACE("Sending OK for commands through %lu", g_stats.nCommands);
179#ifdef USE_THREADS
[4424]180    queueResponse(ostr.c_str(), nBytes, Response::VOLATILE, Response::OK);
[3998]181#else
[4424]182    if (write(g_fdOut, ostr.c_str(), nBytes) < 0) {
[3998]183        ERROR("write failed: %s", strerror(errno));
184        return -1;
185    }
186#endif
187    return 0;
188}
189
190static void
191initService()
192{
[4105]193    g_fOut = fdopen(g_fdOut, "w");
194    // If running without socket, use stdout for debugging
195    if (g_fOut == NULL && g_fdOut != STDOUT_FILENO) {
196        g_fdOut = STDOUT_FILENO;
197        g_fOut = fdopen(g_fdOut, "w");
198    }
[3998]199
200    const char *user = getenv("USER");
201    char *logName = NULL;
202    int logNameLen = 0;
203
204    if (user == NULL) {
205        logNameLen = 19+1;
206        logName = (char *)calloc(logNameLen, sizeof(char));
207        strncpy(logName, "/tmp/geovis_log.txt", logNameLen);
[4009]208    } else {
[3998]209        logNameLen = 16+strlen(user)+4+1;
210        logName = (char *)calloc(logNameLen, sizeof(char));
211        strncpy(logName, "/tmp/geovis_log_", logNameLen);
212        strncat(logName, user, strlen(user));
213        strncat(logName, ".txt", 4);
214    }
215
216    // open log and map stderr to log file
217    g_fLog = fopen(logName, "w");
218    dup2(fileno(g_fLog), STDERR_FILENO);
[4105]219    // If we are writing to socket, map stdout to log
220    if (g_fdOut != STDOUT_FILENO) {
221        dup2(fileno(g_fLog), STDOUT_FILENO);
222    }
[3998]223
[4105]224    fflush(stdout);
225
[3998]226    // clean up malloc'd memory
227    if (logName != NULL) {
228        free(logName);
229    }
230}
231
232static void
233exitService()
234{
235    TRACE("Enter");
236
[4349]237    serverStats(g_stats, 0);
[3998]238
239    // close log file
240    if (g_fLog != NULL) {
241        fclose(g_fLog);
242        g_fLog = NULL;
243    }
244}
245
246#ifdef USE_THREADS
247
[4028]248#ifdef USE_READ_THREAD
[3998]249static void *
[4028]250readerThread(void *clientData)
251{
252    Tcl_Interp *interp = (Tcl_Interp *)clientData;
253
254    TRACE("Starting reader thread");
255
256    queueCommands(interp, NULL, g_inBufPtr);
257
258    return NULL;
259}
260#endif
261
262static void *
[3998]263writerThread(void *clientData)
264{
265    ResponseQueue *queue = (ResponseQueue *)clientData;
266
267    TRACE("Starting writer thread");
268    for (;;) {
269        Response *response = queue->dequeue();
270        if (response == NULL)
271            continue;
272        if (fwrite(response->message(), sizeof(unsigned char), response->length(),
273                   g_fOut) != response->length()) {
274            ERROR("short write while trying to write %ld bytes",
275                  response->length());
276        }
277        fflush(g_fOut);
278        TRACE("Wrote response of type %d", response->type());
279        delete response;
280        if (feof(g_fOut))
281            break;
282    }   
283    return NULL;
284}
285
286#endif  /*USE_THREADS*/
287
288int
289main(int argc, char *argv[])
290{
291    // Ignore SIGPIPE.  **Is this needed? **
292    signal(SIGPIPE, SIG_IGN);
[4107]293
294    //const char *resourcePath = NULL;
295    while (1) {
296        int c = getopt(argc, argv, "p:i:o:");
297        if (c == -1) {
298            break;
299        }
300        switch (c) {
301        case 'p':
302            //resourcePath = optarg;
303            break;
304        case 'i': {
305            int fd = atoi(optarg);
306            if (fd >=0 && fd < 5) {
307                g_fdIn = fd;
308            }
309        }
310            break;
311        case 'o': {
312            int fd = atoi(optarg);
313            if (fd >=0 && fd < 5) {
314                g_fdOut = fd;
315            }
316        }
317            break;
318        case '?':
319            break;
320        default:
321            return 1;
322        }
323    }
324
[3998]325    initService();
326    initLog();
327
328    memset(&g_stats, 0, sizeof(g_stats));
329    gettimeofday(&g_stats.start, NULL);
330
331    TRACE("Starting GeoVis Server");
332
[4107]333    // Sanity check: log descriptor can't be used for client IO
334    if (fileno(g_fLog) == g_fdIn) {
335        ERROR("Invalid input file descriptor");
336        return 1;
337    }
338    if (fileno(g_fLog) == g_fdOut) {
339        ERROR("Invalid output file descriptor");
340        return 1;
341    }
342    TRACE("File descriptors: in %d out %d log %d", g_fdIn, g_fdOut, fileno(g_fLog));
343
[3998]344    /* This synchronizes the client with the server, so that the client
345     * doesn't start writing commands before the server is ready. It could
346     * also be used to supply information about the server (version, memory
347     * size, etc). */
348    fprintf(g_fOut, "GeoVis %s (build %s)\n", GEOVIS_VERSION_STRING, SVN_VERSION);
349    fflush(g_fOut);
350
351    g_renderer = new Renderer();
352    g_inBufPtr = new ReadBuffer(g_fdIn, 1<<12);
353
354    Tcl_Interp *interp = Tcl_CreateInterp();
355
356#ifdef USE_THREADS
[4028]357    g_outQueue = new ResponseQueue();
[3998]358
359    pthread_t writerThreadId;
[4028]360    if (pthread_create(&writerThreadId, NULL, &writerThread, g_outQueue) < 0) {
[3998]361        ERROR("Can't create writer thread: %s", strerror(errno));
362    }
363#endif
364    initTcl(interp, NULL);
365
366    osg::ref_ptr<osg::Image> imgData;
367
368    // Start main server loop
369    for (;;) {
[4025]370        long timeout = g_renderer->getTimeout();
[4628]371#ifdef SLEEP_AFTER_QUEUE_FRAME
372        g_renderer->markFrameStart();
373#endif
[4028]374        int cmdStatus = processCommands(interp, NULL, g_inBufPtr, g_fdOut, timeout);
375        if (cmdStatus < 0)
[3998]376            break;
377
378        if (g_renderer->render()) {
[4009]379            TRACE("Rendered new frame");
380            imgData = g_renderer->getRenderedFrame();
381            if (imgData == NULL) {
382                ERROR("Empty image");
383            } else {
384                TRACE("Image: %d x %d", imgData->s(), imgData->t());
[4293]385
386                if (imgData->s() == g_renderer->getWindowWidth() &&
387                    imgData->t() == g_renderer->getWindowHeight()) {
[4328]388                    queueViewpoint();
[3998]389#ifdef USE_THREADS
[4293]390                    queueFrame(g_outQueue, imgData->data());
[3998]391#else
[4293]392                    writeFrame(g_fdOut, imgData->data());
[3998]393#endif
[4293]394                }
395                g_stats.nFrames++;
396                g_stats.nFrameBytes += imgData->s() * imgData->t() * 3;
[4028]397            }
[4628]398#ifdef SLEEP_AFTER_QUEUE_FRAME
399            g_renderer->markFrameEnd();
400#endif
[3998]401        } else {
[4028]402            //TRACE("No render required");
403            if (cmdStatus > 1) {
404                sendAck();
[4632]405            } else {
406                TRACE("No render required and status = %d", cmdStatus);
[4028]407            }
[3998]408        }
409
[4307]410#if 0
[4014]411        double x, y, z;
412        if (g_renderer->getMousePoint(&x, &y, &z)) {
413            // send coords to client
414            size_t length;
415            char mesg[256];
416
417            length = snprintf(mesg, sizeof(mesg),
[4320]418                              "nv>map coords %g %g %g\n", x, y, z);
[4014]419
420            queueResponse(mesg, length, Response::VOLATILE);
421        }
[4307]422#endif
[4014]423
[3998]424        if (g_inBufPtr->status() == ReadBuffer::ENDFILE)
425            break;
426    }
427#ifdef USE_THREADS
428    // Writer thread is probably blocked on sem_wait, so cancel instead
429    // of joining
430    if (pthread_cancel(writerThreadId) < 0) {
431        ERROR("Can't cancel writer thread: %s", strerror(errno));
432    } else {
433        TRACE("Cancelled writer thread");
434    }
435
436    TRACE("Deleting ResponseQueue");
[4028]437    delete g_outQueue;
438    g_outQueue = NULL;
[3998]439#endif
440
441    TRACE("Stopping Tcl interpreter");
442    exitTcl(interp);
443    interp = NULL;
444
445    TRACE("Deleting ReadBuffer");
446    delete g_inBufPtr;
447    g_inBufPtr = NULL;
448
449    TRACE("Deleting renderer");
450    delete g_renderer;
451    g_renderer = NULL;
452
453    TRACE("Exiting GeoVis Server");
454
455    closeLog();
456    exitService();
457
458    return 0;
459}
Note: See TracBrowser for help on using the repository browser.