Skip to content
Permalink
Browse files

Several SDL-related features and enhancements.

- Render the scene in a thread. This allows the main thread to
  run an event loop and detect buttons being pressed (which is used
  to abort the rendering at any time).
- Pressing "F12" takes a screenshot
- Clicking on the rendered image traces a special "debugging" ray
  (can be used to debug issues).
  • Loading branch information...
anrieff committed May 29, 2018
1 parent 78c02ee commit f5d3793bf9e3664f71e88182f03d5c0095f591d1
Showing with 158 additions and 2 deletions.
  1. +5 −0 src/bitmap.h
  2. +21 −2 src/main.cpp
  3. +128 −0 src/sdl.cpp
  4. +4 −0 src/sdl.h
@@ -49,6 +49,11 @@ class Bitmap {
bool loadEXR(const char* filename); //!< Loads an EXR file
bool saveEXR(const char* filename); //!< Saves the image into the EXR format, preserving the dynamic range, using Half for storage.

enum OutputFormat { /// the two supported writing formats
outputFormat_BMP,
outputFormat_EXR,
};

virtual bool loadImage(const char* filename); //!< Loads an image (the format is detected from extension)
virtual bool saveImage(const char* filename); //!< Save the bitmap to an image (the format is detected from extension)
};
@@ -296,6 +296,7 @@ void render()

for (int y = 0; y < frameHeight(); y++) {
for (int x = 0; x < frameWidth(); x++) {
if (wantToQuit) return;
Color avg(0, 0, 0);
for (int i = 0; i < samplesPerPixel; i++) {
if (scene.camera->stereoSeparation == 0) {
@@ -338,6 +339,13 @@ void render()
printf("Frame took %d ms\n", elapsed);
}

int renderSceneThread(void* /*unused*/)
{
render();
rendering = false;
return 0;
}

bool parseCmdLine(int argc, char** argv)
{
if (argc == 1) return true;
@@ -350,6 +358,17 @@ bool parseCmdLine(int argc, char** argv)
}
}

void debugRayTrace(int x, int y)
{
// trace a test ("debugging") ray through a clicked pixel on the screen
Ray ray = scene.camera->getScreenRay(x, y);
ray.flags |= RF_DEBUG;
if (scene.settings.gi)
pathtrace(ray, Color(1, 1, 1), getRandomGen());
else
raytrace(ray);
}

int main(int argc, char** argv)
{
if (!parseCmdLine(argc, argv)) return -1;
@@ -358,9 +377,9 @@ int main(int argc, char** argv)
initGraphics(scene.settings.frameWidth, scene.settings.frameHeight);
scene.beginRender();
scene.beginFrame();
render();
renderScene_threaded();
displayVFB(vfb);
waitForUserExit();
if (!wantToQuit) waitForUserExit();
closeGraphics();
printf("Exited cleanly\n");
return 0;
@@ -28,8 +28,13 @@
#endif // _WIN32
#include <stdio.h>
#include "sdl.h"
#include "bitmap.h"

SDL_Surface* screen = NULL;
volatile bool rendering; // used in main/worker thread synchronization
SDL_Thread *render_thread;
SDL_mutex *render_lock;
bool render_async, wantToQuit = false;

/// try to create a frame window with the given dimensions
bool initGraphics(int frameWidth, int frameHeight)
@@ -105,3 +110,126 @@ int frameHeight(void)
if (screen) return screen->h;
return 0;
}

// find an unused file name like 'fray_0005.bmp'
static void findUnusedFN(char fn[], const char* suffix)
{
char temp[256];
int idx = 0;
while (idx < 10000) {
sprintf(temp, "fray_%04d.bmp", idx);
FILE* f = fopen(temp, "rb");
if (!f) {
sprintf(temp, "fray_%04d.exr", idx);
f = fopen(temp, "rb");
}
if (!f) break; // file doesn't exist - use that
fclose(f);
idx++;
}
sprintf(fn, "fray_%04d.%s", idx, suffix);
}

bool takeScreenshot(const char* filename)
{
extern Color vfb[VFB_MAX_SIZE][VFB_MAX_SIZE]; // from main.cpp

Bitmap bmp;
bmp.generateEmptyImage(frameWidth(), frameHeight());
for (int y = 0; y < frameHeight(); y++)
for (int x = 0; x < frameWidth(); x++)
bmp.setPixel(x, y, vfb[y][x]);
bool res = bmp.saveImage(filename);
if (res) printf("Saved a screenshot as `%s'\n", filename);
else printf("Failed to take a screenshot\n");
return res;
}

bool takeScreenshotAuto(Bitmap::OutputFormat fmt)
{
char fn[256];
findUnusedFN(fn, fmt == Bitmap::outputFormat_BMP ? "bmp" : "exr");
return takeScreenshot(fn);
}

static void handleEvent(SDL_Event& ev)
{
switch (ev.type) {
case SDL_QUIT:
wantToQuit = true;
return;
case SDL_KEYDOWN:
{
switch (ev.key.keysym.sym) {
case SDLK_ESCAPE:
wantToQuit = true;
return;
case SDLK_F12:
// Shift+F12: screenshot in EXR; else, do it in (gamma-compressed) BMP.
if (ev.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT))
takeScreenshotAuto(Bitmap::outputFormat_EXR);
else
takeScreenshotAuto(Bitmap::outputFormat_BMP);
break;
default:
break;
}
break;
}
case SDL_MOUSEBUTTONDOWN:
{
// raytrace a single ray at the given pixel
extern void debugRayTrace(int x, int y);
debugRayTrace(ev.button.x, ev.button.y);
break;
}
default:
break;
}
}

class MutexRAII {
SDL_mutex* mutex;
public:
MutexRAII(SDL_mutex* _mutex)
{
mutex = _mutex;
SDL_mutexP(mutex);
}
~MutexRAII()
{
SDL_mutexV(mutex);
}
};

bool renderScene_threaded(void)
{
render_async = true;
rendering = true;
extern int renderSceneThread(void*);
render_thread = SDL_CreateThread(renderSceneThread, NULL);

if(render_thread == NULL) { //Failed to start for some bloody reason
rendering = render_async = false;
return false;
}

while (!wantToQuit) {
{
MutexRAII raii(render_lock);
if (!rendering) break;
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
handleEvent(ev);
if (wantToQuit) break;
}
}
SDL_Delay(100);
}
rendering = false;
SDL_WaitThread(render_thread, NULL);
render_thread = NULL;

render_async = false;
return true;
}
@@ -26,9 +26,13 @@
#include "color.h"
#include "constants.h"

extern volatile bool rendering; // used in main/worker thread synchronization
extern bool wantToQuit;

bool initGraphics(int frameWidth, int frameHeight);
void closeGraphics(void);
void displayVFB(Color vfb[VFB_MAX_SIZE][VFB_MAX_SIZE]); //!< displays the VFB (Virtual framebuffer) to the real one.
void waitForUserExit(void); //!< Pause. Wait until the user closes the application
int frameWidth(void); //!< returns the frame width (pixels)
int frameHeight(void); //!< returns the frame height (pixels)
bool renderScene_threaded();

0 comments on commit f5d3793

Please sign in to comment.
You can’t perform that action at this time.