source: trunk/packages/vizservers/vtkvis/RpVtkRenderServer.cpp @ 2573

Last change on this file since 2573 was 2573, checked in by ldelgass, 13 years ago

Merge vtkvis_threaded branch to trunk. (Threading off by default in Makefile)

  • Property svn:eol-style set to native
File size: 10.5 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2011, Purdue Research Foundation
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
15#ifdef WANT_TRACE
16#include <sys/time.h>
17#endif
18
19#include <string>
20#include <sstream>
21
22#include "Trace.h"
23#include "ReadBuffer.h"
24#include "RpVtkRenderServer.h"
25#include "RpVtkRendererCmd.h"
26#include "RpVtkRenderer.h"
27#include "PPMWriter.h"
28#include "TGAWriter.h"
29#ifdef USE_THREADS
30#include <pthread.h>
31#include "ResponseQueue.h"
32#endif
33
34using namespace Rappture::VtkVis;
35
36int Rappture::VtkVis::g_fdIn = STDIN_FILENO; ///< Input file descriptor
37int Rappture::VtkVis::g_fdOut = STDOUT_FILENO; ///< Output file descriptor
38FILE *Rappture::VtkVis::g_fOut = stdout; ///< Output file handle
39FILE *Rappture::VtkVis::g_fLog = NULL; ///< Trace logging file handle
40Renderer *Rappture::VtkVis::g_renderer = NULL; ///< Main render worker
41ReadBuffer *Rappture::VtkVis::g_inBufPtr = NULL; ///< Socket read buffer
42
43#define ELAPSED_TIME(t1, t2) \
44    ((t1).tv_sec == (t2).tv_sec ? (((t2).tv_usec - (t1).tv_usec)/1.0e+3f) : \
45     (((t2).tv_sec - (t1).tv_sec))*1.0e+3f + (float)((t2).tv_usec - (t1).tv_usec)/1.0e+3f)
46
47#ifdef USE_THREADS
48static void
49queueFrame(ResponseQueue *queue, vtkUnsignedCharArray *imgData)
50{
51#ifdef DEBUG
52    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
53        double xywh[4];
54        g_renderer->getScreenWorldCoords(xywh);
55        TRACE("Image bbox: %g %g %g %g",
56              xywh[0],
57              (xywh[1] + xywh[3]),
58              (xywh[0] + xywh[2]),
59              xywh[1]);
60    }
61
62#ifdef RENDER_TARGA
63    writeTGAFile("/tmp/frame.tga",
64                 imgData->GetPointer(0),
65                 g_renderer->getWindowWidth(),
66                 g_renderer->getWindowHeight(),
67                 TARGA_BYTES_PER_PIXEL);
68#else
69    writeTGAFile("/tmp/frame.tga",
70                 imgData->GetPointer(0),
71                 g_renderer->getWindowWidth(),
72                 g_renderer->getWindowHeight(),
73                 TARGA_BYTES_PER_PIXEL,
74                 true);
75#endif  /*RENDER_TARGA*/
76
77#else
78    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
79        double xywh[4];
80        g_renderer->getCameraZoomRegion(xywh);
81        std::ostringstream oss;
82        oss.precision(12);
83        // Send upper left and lower right corners as bbox
84        oss << "nv>image -type image -bbox {"
85            << std::scientific
86            << xywh[0] << " "
87            << xywh[1] << " "
88            << xywh[2] << " "
89            << xywh[3] << "} -bytes";
90
91#ifdef RENDER_TARGA
92        queueTGA(queue, oss.str().c_str(),
93                 imgData->GetPointer(0),
94                 g_renderer->getWindowWidth(),
95                 g_renderer->getWindowHeight(),
96                 TARGA_BYTES_PER_PIXEL);
97#else
98        queuePPM(queue, oss.str().c_str(),
99                 imgData->GetPointer(0),
100                 g_renderer->getWindowWidth(),
101                 g_renderer->getWindowHeight());
102#endif  /*RENDER_TARGA*/
103    } else {
104#ifdef RENDER_TARGA
105        queueTGA(queue, "nv>image -type image -bytes",
106                 imgData->GetPointer(0),
107                 g_renderer->getWindowWidth(),
108                 g_renderer->getWindowHeight(),
109                 TARGA_BYTES_PER_PIXEL);
110#else
111        queuePPM(queue, "nv>image -type image -bytes",
112                 imgData->GetPointer(0),
113                 g_renderer->getWindowWidth(),
114                 g_renderer->getWindowHeight());
115#endif  /*RENDER_TARGA*/
116    }
117#endif  /*DEBUG*/
118}
119
120#else
121
122static void
123writeFrame(int fd, vtkUnsignedCharArray *imgData)
124{
125#ifdef DEBUG
126    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
127        double xywh[4];
128        g_renderer->getScreenWorldCoords(xywh);
129        TRACE("Image bbox: %g %g %g %g",
130              xywh[0],
131              (xywh[1] + xywh[3]),
132              (xywh[0] + xywh[2]),
133              xywh[1]);
134    }
135
136#ifdef RENDER_TARGA
137    writeTGAFile("/tmp/frame.tga",
138                 imgData->GetPointer(0),
139                 g_renderer->getWindowWidth(),
140                 g_renderer->getWindowHeight(),
141                 TARGA_BYTES_PER_PIXEL);
142#else
143    writeTGAFile("/tmp/frame.tga",
144                 imgData->GetPointer(0),
145                 g_renderer->getWindowWidth(),
146                 g_renderer->getWindowHeight(),
147                 TARGA_BYTES_PER_PIXEL,
148                 true);
149#endif  /*RENDER_TARGA*/
150
151#else
152    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
153        double xywh[4];
154        g_renderer->getCameraZoomRegion(xywh);
155        std::ostringstream oss;
156        oss.precision(12);
157        // Send upper left and lower right corners as bbox
158        oss << "nv>image -type image -bbox {"
159            << std::scientific
160            << xywh[0] << " "
161            << xywh[1] << " "
162            << xywh[2] << " "
163            << xywh[3] << "} -bytes";
164
165#ifdef RENDER_TARGA
166        writeTGA(fd, oss.str().c_str(),
167                 imgData->GetPointer(0),
168                 g_renderer->getWindowWidth(),
169                 g_renderer->getWindowHeight(),
170                 TARGA_BYTES_PER_PIXEL);
171#else
172        writePPM(fd, oss.str().c_str(),
173                 imgData->GetPointer(0),
174                 g_renderer->getWindowWidth(),
175                 g_renderer->getWindowHeight());
176#endif  /*RENDER_TARGA*/
177    } else {
178#ifdef RENDER_TARGA
179        writeTGA(fd, "nv>image -type image -bytes",
180                 imgData->GetPointer(0),
181                 g_renderer->getWindowWidth(),
182                 g_renderer->getWindowHeight(),
183                 TARGA_BYTES_PER_PIXEL);
184#else
185        writePPM(fd, "nv>image -type image -bytes",
186                 imgData->GetPointer(0),
187                 g_renderer->getWindowWidth(),
188                 g_renderer->getWindowHeight());
189#endif  /*RENDER_TARGA*/
190    }
191#endif  /*DEBUG*/
192}
193#endif /*USE_THREADS*/
194
195static void
196initService()
197{
198    const char *user = getenv("USER");
199    char *logName = NULL;
200    int logNameLen = 0;
201
202    if (user == NULL) {
203        logNameLen = 19+1;
204        logName = (char *)calloc(logNameLen, sizeof(char));
205        strncpy(logName, "/tmp/vtkvis_log.txt", logNameLen);
206    }
207    else {
208        logNameLen = 16+strlen(user)+4+1;
209        logName = (char *)calloc(logNameLen, sizeof(char));
210        strncpy(logName, "/tmp/vtkvis_log_", logNameLen);
211        strncat(logName, user, strlen(user));
212        strncat(logName, ".txt", 4);
213    }
214
215    // open log and map stderr to log file
216    g_fLog = fopen(logName, "w");
217    close(STDERR_FILENO);
218    dup2(fileno(g_fLog), STDERR_FILENO);
219    // flush junk
220    fflush(stderr);
221
222    // clean up malloc'd memory
223    if (logName != NULL) {
224        free(logName);
225    }
226}
227
228static void
229exitService()
230{
231    // close log file
232    if (g_fLog != NULL) {
233        fclose(g_fLog);
234        g_fLog = NULL;
235    }
236}
237
238#ifdef USE_THREADS
239static void *
240readerThread(void *clientData)
241{
242    ResponseQueue *queue = (ResponseQueue *)clientData;
243    Tcl_Interp *interp;
244
245    TRACE("Starting reader thread");
246    interp = (Tcl_Interp *)queue->clientData();
247    vtkSmartPointer<vtkUnsignedCharArray> imgData =
248        vtkSmartPointer<vtkUnsignedCharArray>::New();
249    for (;;) {
250        if (processCommands(interp, g_inBufPtr, g_fdOut) < 0)
251            break;
252
253        if (g_renderer->render()) {
254            TRACE("Rendering new frame");
255            g_renderer->getRenderedFrame(imgData);
256            queueFrame(queue, imgData);
257        } else {
258            TRACE("No render required");
259        }
260
261        if (g_inBufPtr->status() == ReadBuffer::ENDFILE)
262            break;
263    }   
264    return NULL;
265}
266
267static void *
268writerThread(void *clientData)
269{
270    ResponseQueue *queue = (ResponseQueue *)clientData;
271
272    TRACE("Starting writer thread");
273    for (;;) {
274        Response *response;
275
276        response = queue->dequeue();
277        if (fwrite(response->message(), sizeof(char), response->length(),
278                   g_fOut) != response->length()) {
279            ERROR("short write while trying to write %ld bytes",
280                  response->length());
281        }
282        fflush(g_fOut);
283        delete response;
284        if (feof(g_fOut))
285            break;
286    }   
287    return NULL;
288}
289
290#endif  /*USE_THREADS*/
291
292int
293main(int argc, char *argv[])
294{
295    // Ignore SIGPIPE.  **Is this needed? **
296    signal(SIGPIPE, SIG_IGN);
297    initService();
298    initLog();
299
300    TRACE("Starting VTKVis Server");
301
302    /* This synchronizes the client with the server, so that the client
303     * doesn't start writing commands before the server is ready. It could
304     * also be used to supply information about the server (version, memory
305     * size, etc). */
306    fprintf(g_fOut, "VtkVis 1.0\n");
307    fflush(g_fOut);
308
309    g_renderer = new Renderer();
310    g_inBufPtr = new ReadBuffer(g_fdIn, 1<<12);
311
312    Tcl_Interp *interp = Tcl_CreateInterp();
313
314#ifdef USE_THREADS
315    ResponseQueue *queue = new ResponseQueue((void *)interp);
316    initTcl(interp, (ClientData)queue);
317
318    pthread_t readerThreadId, writerThreadId;
319    if (pthread_create(&readerThreadId, NULL, &readerThread, queue) < 0) {
320        ERROR("Can't create reader thread: %s", strerror(errno));
321    }
322    if (pthread_create(&writerThreadId, NULL, &writerThread, queue) < 0) {
323        ERROR("Can't create writer thread: %s", strerror(errno));
324    }
325    if (pthread_join(readerThreadId, NULL) < 0) {
326        ERROR("Can't join reader thread: %s", strerror(errno));
327    } else {
328        TRACE("Reader thread exited");
329    }
330    // Writer thread is probably blocked on sem_wait, so cancel instead
331    // of joining
332    if (pthread_cancel(writerThreadId) < 0) {
333        ERROR("Can't cancel writer thread: %s", strerror(errno));
334    } else {
335        TRACE("Cancelled writer thread");
336    }
337
338    TRACE("Deleting ResponseQueue");
339    delete queue;
340    queue = NULL;
341#else
342    initTcl(interp, (ClientData)NULL);
343
344    vtkSmartPointer<vtkUnsignedCharArray> imgData =
345        vtkSmartPointer<vtkUnsignedCharArray>::New();
346    for (;;) {
347        if (processCommands(interp, g_inBufPtr, g_fdOut) < 0)
348            break;
349
350        if (g_renderer->render()) {
351            TRACE("Rendering new frame");
352            g_renderer->getRenderedFrame(imgData);
353            writeFrame(g_fdOut, imgData);
354        } else {
355            TRACE("No render required");
356        }
357
358        if (g_inBufPtr->status() == ReadBuffer::ENDFILE)
359            break;
360    }
361#endif
362
363    TRACE("Stopping Tcl interpreter");
364    exitTcl(interp);
365    interp = NULL;
366
367    TRACE("Deleting ReadBuffer");
368    delete g_inBufPtr;
369    g_inBufPtr = NULL;
370
371    TRACE("Deleting renderer");
372    delete g_renderer;
373    g_renderer = NULL;
374
375    TRACE("Exiting VTKVis Server");
376
377    closeLog();
378    exitService();
379
380    return 0;
381}
Note: See TracBrowser for help on using the repository browser.