source: branches/1.3/packages/vizservers/vtkvis/RenderServer.cpp @ 3844

Last change on this file since 3844 was 3844, checked in by ldelgass, 11 years ago

Sync with trunk. Branch now differs only from trunk by r3722 (branch is version
1.3, trunk is version 1.4)

  • Property svn:eol-style set to native
File size: 16.1 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2004-2012  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 <vtksys/SystemInformation.hxx>
26
27#include "Trace.h"
28#include "ReadBuffer.h"
29#include "RenderServer.h"
30#include "RendererCmd.h"
31#include "Renderer.h"
32#include "PPMWriter.h"
33#include "TGAWriter.h"
34#ifdef USE_THREADS
35#include <pthread.h>
36#include "ResponseQueue.h"
37#endif
38#include <md5.h>
39
40using namespace VtkVis;
41
42Stats VtkVis::g_stats;
43
44int VtkVis::g_statsFile = -1; ///< Stats output file descriptor.
45int VtkVis::g_fdIn = STDIN_FILENO; ///< Input file descriptor
46int VtkVis::g_fdOut = STDOUT_FILENO; ///< Output file descriptor
47FILE *VtkVis::g_fOut = stdout; ///< Output file handle
48FILE *VtkVis::g_fLog = NULL; ///< Trace logging file handle
49Renderer *VtkVis::g_renderer = NULL; ///< Main render worker
50ReadBuffer *VtkVis::g_inBufPtr = NULL; ///< Socket read buffer
51
52#ifdef USE_THREADS
53static void
54queueFrame(ResponseQueue *queue, vtkUnsignedCharArray *imgData)
55{
56#ifdef DEBUG
57    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
58        double xywh[4];
59        g_renderer->getScreenWorldCoords(xywh);
60        TRACE("Image bbox: %g %g %g %g",
61              xywh[0],
62              (xywh[1] + xywh[3]),
63              (xywh[0] + xywh[2]),
64              xywh[1]);
65    }
66
67#ifdef RENDER_TARGA
68    writeTGAFile("/tmp/frame.tga",
69                 imgData->GetPointer(0),
70                 g_renderer->getWindowWidth(),
71                 g_renderer->getWindowHeight(),
72                 TARGA_BYTES_PER_PIXEL);
73#else
74    writeTGAFile("/tmp/frame.tga",
75                 imgData->GetPointer(0),
76                 g_renderer->getWindowWidth(),
77                 g_renderer->getWindowHeight(),
78                 TARGA_BYTES_PER_PIXEL,
79                 true);
80#endif  /*RENDER_TARGA*/
81
82#else
83    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
84        double xywh[4];
85        g_renderer->getCameraZoomRegion(xywh);
86        std::ostringstream oss;
87        oss.precision(12);
88        // Send upper left and lower right corners as bbox
89        oss << "nv>image -type image -bbox {"
90            << std::scientific
91            << xywh[0] << " "
92            << xywh[1] << " "
93            << xywh[2] << " "
94            << xywh[3] << "} -bytes";
95
96#ifdef RENDER_TARGA
97        queueTGA(queue, oss.str().c_str(),
98                 imgData->GetPointer(0),
99                 g_renderer->getWindowWidth(),
100                 g_renderer->getWindowHeight(),
101                 TARGA_BYTES_PER_PIXEL);
102#else
103        queuePPM(queue, oss.str().c_str(),
104                 imgData->GetPointer(0),
105                 g_renderer->getWindowWidth(),
106                 g_renderer->getWindowHeight());
107#endif  /*RENDER_TARGA*/
108    } else {
109#ifdef RENDER_TARGA
110        queueTGA(queue, "nv>image -type image -bytes",
111                 imgData->GetPointer(0),
112                 g_renderer->getWindowWidth(),
113                 g_renderer->getWindowHeight(),
114                 TARGA_BYTES_PER_PIXEL);
115#else
116        queuePPM(queue, "nv>image -type image -bytes",
117                 imgData->GetPointer(0),
118                 g_renderer->getWindowWidth(),
119                 g_renderer->getWindowHeight());
120#endif  /*RENDER_TARGA*/
121    }
122#endif  /*DEBUG*/
123}
124
125#else
126
127static void
128writeFrame(int fd, vtkUnsignedCharArray *imgData)
129{
130#ifdef DEBUG
131    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
132        double xywh[4];
133        g_renderer->getScreenWorldCoords(xywh);
134        TRACE("Image bbox: %g %g %g %g",
135              xywh[0],
136              (xywh[1] + xywh[3]),
137              (xywh[0] + xywh[2]),
138              xywh[1]);
139    }
140
141#ifdef RENDER_TARGA
142    writeTGAFile("/tmp/frame.tga",
143                 imgData->GetPointer(0),
144                 g_renderer->getWindowWidth(),
145                 g_renderer->getWindowHeight(),
146                 TARGA_BYTES_PER_PIXEL);
147#else
148    writeTGAFile("/tmp/frame.tga",
149                 imgData->GetPointer(0),
150                 g_renderer->getWindowWidth(),
151                 g_renderer->getWindowHeight(),
152                 TARGA_BYTES_PER_PIXEL,
153                 true);
154#endif  /*RENDER_TARGA*/
155
156#else
157    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
158        double xywh[4];
159        g_renderer->getCameraZoomRegion(xywh);
160        std::ostringstream oss;
161        oss.precision(12);
162        // Send upper left and lower right corners as bbox
163        oss << "nv>image -type image -bbox {"
164            << std::scientific
165            << xywh[0] << " "
166            << xywh[1] << " "
167            << xywh[2] << " "
168            << xywh[3] << "} -bytes";
169
170#ifdef RENDER_TARGA
171        writeTGA(fd, oss.str().c_str(),
172                 imgData->GetPointer(0),
173                 g_renderer->getWindowWidth(),
174                 g_renderer->getWindowHeight(),
175                 TARGA_BYTES_PER_PIXEL);
176#else
177        writePPM(fd, oss.str().c_str(),
178                 imgData->GetPointer(0),
179                 g_renderer->getWindowWidth(),
180                 g_renderer->getWindowHeight());
181#endif  /*RENDER_TARGA*/
182    } else {
183#ifdef RENDER_TARGA
184        writeTGA(fd, "nv>image -type image -bytes",
185                 imgData->GetPointer(0),
186                 g_renderer->getWindowWidth(),
187                 g_renderer->getWindowHeight(),
188                 TARGA_BYTES_PER_PIXEL);
189#else
190        writePPM(fd, "nv>image -type image -bytes",
191                 imgData->GetPointer(0),
192                 g_renderer->getWindowWidth(),
193                 g_renderer->getWindowHeight());
194#endif  /*RENDER_TARGA*/
195    }
196#endif  /*DEBUG*/
197}
198#endif /*USE_THREADS*/
199
200static int
201sendAck(ClientData clientData, int fdOut)
202{
203    std::ostringstream oss;
204    oss << "nv>ok -token " << g_stats.nCommands <<  "\n";
205    int nBytes = oss.str().length();
206
207    TRACE("Sending OK for commands through %lu", g_stats.nCommands);
208#ifdef USE_THREADS
209    queueResponse(clientData, oss.str().c_str(), nBytes, Response::VOLATILE, Response::OK);
210#else
211    if (write(fdOut, oss.str().c_str(), nBytes) < 0) {
212        ERROR("write failed: %s", strerror(errno));
213        return -1;
214    }
215#endif
216    return 0;
217}
218
219int
220VtkVis::getStatsFile(Tcl_Interp *interp, Tcl_Obj *objPtr)
221{
222    Tcl_DString ds;
223    Tcl_Obj **objv;
224    int objc;
225    int i;
226    char fileName[33];
227    const char *path;
228    md5_state_t state;
229    md5_byte_t digest[16];
230    char *string;
231    int length;
232
233    if ((objPtr == NULL) || (g_statsFile >= 0)) {
234        return g_statsFile;
235    }
236    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
237        return -1;
238    }
239    Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("pid", 3));
240    Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewIntObj(getpid()));
241    string = Tcl_GetStringFromObj(objPtr, &length);
242
243    md5_init(&state);
244    md5_append(&state, (const md5_byte_t *)string, length);
245    md5_finish(&state, digest);
246    for (i = 0; i < 16; i++) {
247        sprintf(fileName + i * 2, "%02x", digest[i]);
248    }
249    Tcl_DStringInit(&ds);
250    Tcl_DStringAppend(&ds, STATSDIR, -1);
251    Tcl_DStringAppend(&ds, "/", 1);
252    Tcl_DStringAppend(&ds, fileName, 32);
253    path = Tcl_DStringValue(&ds);
254
255    g_statsFile = open(path, O_EXCL | O_CREAT | O_WRONLY, 0600);
256    Tcl_DStringFree(&ds);
257    if (g_statsFile < 0) {
258        ERROR("can't open \"%s\": %s", fileName, strerror(errno));
259        return -1;
260    }
261    return g_statsFile;
262}
263
264int
265VtkVis::writeToStatsFile(int f, const char *s, size_t length)
266{
267    if (f >= 0) {
268        ssize_t numWritten;
269
270        numWritten = write(f, s, length);
271        if (numWritten == (ssize_t)length) {
272            close(dup(f));
273        }
274    }
275    return 0;
276}
277
278static int
279serverStats(int code)
280{
281    double start, finish;
282    char buf[BUFSIZ];
283    Tcl_DString ds;
284    int result;
285    int f;
286
287    {
288        struct timeval tv;
289
290        /* Get ending time.  */
291        gettimeofday(&tv, NULL);
292        finish = CVT2SECS(tv);
293        tv = g_stats.start;
294        start = CVT2SECS(tv);
295    }
296    /*
297     * Session information:
298     *   - Name of render server
299     *   - Process ID
300     *   - Hostname where server is running
301     *   - Start date of session
302     *   - Start date of session in seconds
303     *   - Number of data sets loaded from client
304     *   - Number of data bytes total loaded from client
305     *   - Number of frames returned
306     *   - Number of bytes total returned (in frames)
307     *   - Number of commands received
308     *   - Total elapsed time of all commands
309     *   - Total elapsed time of session
310     *   - Exit code of vizserver
311     *   - User time
312     *   - System time
313     *   - User time of children
314     *   - System time of children
315     */
316
317    Tcl_DStringInit(&ds);
318   
319    Tcl_DStringAppendElement(&ds, "render_stop");
320    /* renderer */
321    Tcl_DStringAppendElement(&ds, "renderer");
322    Tcl_DStringAppendElement(&ds, "vtkvis");
323    /* pid */
324    Tcl_DStringAppendElement(&ds, "pid");
325    sprintf(buf, "%d", getpid());
326    Tcl_DStringAppendElement(&ds, buf);
327    /* host */
328    Tcl_DStringAppendElement(&ds, "host");
329    gethostname(buf, BUFSIZ-1);
330    buf[BUFSIZ-1] = '\0';
331    Tcl_DStringAppendElement(&ds, buf);
332    /* date */
333    Tcl_DStringAppendElement(&ds, "date");
334    strcpy(buf, ctime(&g_stats.start.tv_sec));
335    buf[strlen(buf) - 1] = '\0';
336    Tcl_DStringAppendElement(&ds, buf);
337    /* date_secs */
338    Tcl_DStringAppendElement(&ds, "date_secs");
339    sprintf(buf, "%ld", g_stats.start.tv_sec);
340    Tcl_DStringAppendElement(&ds, buf);
341    /* num_data_sets */
342    Tcl_DStringAppendElement(&ds, "num_data_sets");
343    sprintf(buf, "%lu", (unsigned long int)g_stats.nDataSets);
344    Tcl_DStringAppendElement(&ds, buf);
345    /* data_set_bytes */
346    Tcl_DStringAppendElement(&ds, "data_set_bytes");
347    sprintf(buf, "%lu", (unsigned long int)g_stats.nDataBytes);
348    Tcl_DStringAppendElement(&ds, buf);
349    /* num_frames */
350    Tcl_DStringAppendElement(&ds, "num_frames");
351    sprintf(buf, "%lu", (unsigned long int)g_stats.nFrames);
352    Tcl_DStringAppendElement(&ds, buf);
353    /* frame_bytes */
354    Tcl_DStringAppendElement(&ds, "frame_bytes");
355    sprintf(buf, "%lu", (unsigned long int)g_stats.nFrameBytes);
356    Tcl_DStringAppendElement(&ds, buf);
357    /* num_commands */
358    Tcl_DStringAppendElement(&ds, "num_commands");
359    sprintf(buf, "%lu", (unsigned long int)g_stats.nCommands);
360    Tcl_DStringAppendElement(&ds, buf);
361    /* cmd_time */
362    Tcl_DStringAppendElement(&ds, "cmd_time");
363    sprintf(buf, "%g", g_stats.cmdTime);
364    Tcl_DStringAppendElement(&ds, buf);
365    /* session_time */
366    Tcl_DStringAppendElement(&ds, "session_time");
367    sprintf(buf, "%g", finish - start);
368    Tcl_DStringAppendElement(&ds, buf);
369    /* status */
370    Tcl_DStringAppendElement(&ds, "status");
371    sprintf(buf, "%d", code);
372    Tcl_DStringAppendElement(&ds, buf);
373    {
374        long clocksPerSec = sysconf(_SC_CLK_TCK);
375        double clockRes = 1.0 / clocksPerSec;
376        struct tms tms;
377
378        memset(&tms, 0, sizeof(tms));
379        times(&tms);
380        /* utime */
381        Tcl_DStringAppendElement(&ds, "utime");
382        sprintf(buf, "%g", tms.tms_utime * clockRes);
383        Tcl_DStringAppendElement(&ds, buf);
384        /* stime */
385        Tcl_DStringAppendElement(&ds, "stime");
386        sprintf(buf, "%g", tms.tms_stime * clockRes);
387        Tcl_DStringAppendElement(&ds, buf);
388        /* cutime */
389        Tcl_DStringAppendElement(&ds, "cutime");
390        sprintf(buf, "%g", tms.tms_cutime * clockRes);
391        Tcl_DStringAppendElement(&ds, buf);
392        /* cstime */
393        Tcl_DStringAppendElement(&ds, "cstime");
394        sprintf(buf, "%g", tms.tms_cstime * clockRes);
395        Tcl_DStringAppendElement(&ds, buf);
396    }
397    Tcl_DStringAppend(&ds, "\n", -1);
398    f = getStatsFile(NULL, NULL);
399    result = writeToStatsFile(f, Tcl_DStringValue(&ds),
400                              Tcl_DStringLength(&ds));
401    close(f);
402    Tcl_DStringFree(&ds);
403    return result;
404}
405
406static void
407initService()
408{
409    TRACE("Enter");
410
411    const char *user = getenv("USER");
412    char *logName = NULL;
413    int logNameLen = 0;
414
415    if (user == NULL) {
416        logNameLen = 19+1;
417        logName = (char *)calloc(logNameLen, sizeof(char));
418        strncpy(logName, "/tmp/vtkvis_log.txt", logNameLen);
419    }
420    else {
421        logNameLen = 16+strlen(user)+4+1;
422        logName = (char *)calloc(logNameLen, sizeof(char));
423        strncpy(logName, "/tmp/vtkvis_log_", logNameLen);
424        strncat(logName, user, strlen(user));
425        strncat(logName, ".txt", 4);
426    }
427
428    // open log and map stderr to log file
429    g_fLog = fopen(logName, "w");
430    close(STDERR_FILENO);
431    dup2(fileno(g_fLog), STDERR_FILENO);
432    // flush junk
433    fflush(stderr);
434
435    // clean up malloc'd memory
436    if (logName != NULL) {
437        free(logName);
438    }
439
440    TRACE("Leave");
441}
442
443static void
444exitService()
445{
446    TRACE("Enter");
447
448    serverStats(0);
449
450    // close log file
451    if (g_fLog != NULL) {
452        fclose(g_fLog);
453        g_fLog = NULL;
454    }
455}
456
457#ifdef USE_THREADS
458
459static void *
460writerThread(void *clientData)
461{
462    ResponseQueue *queue = (ResponseQueue *)clientData;
463
464    TRACE("Starting writer thread");
465    for (;;) {
466        Response *response = queue->dequeue();
467        if (response == NULL)
468            continue;
469        if (fwrite(response->message(), sizeof(char), response->length(),
470                   g_fOut) != response->length()) {
471            ERROR("short write while trying to write %ld bytes",
472                  response->length());
473        }
474        fflush(g_fOut);
475        TRACE("Wrote response of type %d", response->type());
476        delete response;
477        if (feof(g_fOut))
478            break;
479    }   
480    return NULL;
481}
482
483#endif  /*USE_THREADS*/
484
485int
486main(int argc, char *argv[])
487{
488    // Ignore SIGPIPE.  **Is this needed? **
489    signal(SIGPIPE, SIG_IGN);
490    initService();
491    initLog();
492
493    memset(&g_stats, 0, sizeof(g_stats));
494    gettimeofday(&g_stats.start, NULL);
495
496    TRACE("Starting VTKVis Server");
497
498#ifdef WANT_TRACE
499    vtksys::SystemInformation::SetStackTraceOnError(1);
500#endif
501
502    /* This synchronizes the client with the server, so that the client
503     * doesn't start writing commands before the server is ready. It could
504     * also be used to supply information about the server (version, memory
505     * size, etc). */
506    fprintf(g_fOut, "VtkVis %s (build %s)\n", VTKVIS_VERSION_STRING, SVN_VERSION);
507    fflush(g_fOut);
508
509    g_renderer = new Renderer();
510    g_inBufPtr = new ReadBuffer(g_fdIn, 1<<12);
511
512    Tcl_Interp *interp = Tcl_CreateInterp();
513    ClientData clientData = NULL;
514#ifdef USE_THREADS
515    ResponseQueue *queue = new ResponseQueue();
516    clientData = (ClientData)queue;
517    initTcl(interp, clientData);
518
519    pthread_t writerThreadId;
520    if (pthread_create(&writerThreadId, NULL, &writerThread, queue) < 0) {
521        ERROR("Can't create writer thread: %s", strerror(errno));
522    }
523#else
524    initTcl(interp, clientData);
525#endif
526
527    vtkSmartPointer<vtkUnsignedCharArray> imgData =
528        vtkSmartPointer<vtkUnsignedCharArray>::New();
529
530    // Start main server loop
531    for (;;) {
532        if (processCommands(interp, clientData, g_inBufPtr, g_fdOut) < 0)
533            break;
534
535        if (g_renderer->render()) {
536            TRACE("Rendering new frame");
537            g_renderer->getRenderedFrame(imgData);
538#ifdef USE_THREADS
539            queueFrame(queue, imgData);
540#else
541            writeFrame(g_fdOut, imgData);
542#endif
543            g_stats.nFrames++;
544            g_stats.nFrameBytes += imgData->GetDataSize() * imgData->GetDataTypeSize();
545        } else {
546            TRACE("No render required");
547            sendAck(clientData, g_fdOut);
548        }
549
550        if (g_inBufPtr->status() == ReadBuffer::ENDFILE)
551            break;
552    }
553#ifdef USE_THREADS
554    // Writer thread is probably blocked on sem_wait, so cancel instead
555    // of joining
556    if (pthread_cancel(writerThreadId) < 0) {
557        ERROR("Can't cancel writer thread: %s", strerror(errno));
558    } else {
559        TRACE("Cancelled writer thread");
560    }
561
562    TRACE("Deleting ResponseQueue");
563    delete queue;
564    queue = NULL;
565#endif
566
567    TRACE("Stopping Tcl interpreter");
568    exitTcl(interp);
569    interp = NULL;
570
571    TRACE("Deleting ReadBuffer");
572    delete g_inBufPtr;
573    g_inBufPtr = NULL;
574
575    TRACE("Deleting renderer");
576    delete g_renderer;
577    g_renderer = NULL;
578
579    TRACE("Exiting VTKVis Server");
580
581    closeLog();
582    exitService();
583
584    return 0;
585}
Note: See TracBrowser for help on using the repository browser.