diff --git a/src/pbrt/gpu/cudagl.h b/src/pbrt/gpu/cudagl.h index deee2a57..e87fca03 100644 --- a/src/pbrt/gpu/cudagl.h +++ b/src/pbrt/gpu/cudagl.h @@ -63,12 +63,13 @@ class CUDAOutputBuffer { void setDevice(int32_t device_idx) { m_device_idx = device_idx; } void setStream(CUstream stream) { m_stream = stream; } - void resize(int32_t width, int32_t height); - // Allocate or update device pointer as necessary for CUDA access - PIXEL_FORMAT* map(); + PIXEL_FORMAT *map(); void unmap(); + void StartAsynchronousReadback(); + const PIXEL_FORMAT *GetReadbackPixels(); + int32_t width() const { return m_width; } int32_t height() const { return m_height; } @@ -82,9 +83,13 @@ class CUDAOutputBuffer { int32_t m_width = 0u; int32_t m_height = 0u; - cudaGraphicsResource* m_cuda_gfx_resource = nullptr; + cudaGraphicsResource *m_cuda_gfx_resource = nullptr; GLuint m_pbo = 0u; - PIXEL_FORMAT* m_device_pixels = nullptr; + PIXEL_FORMAT *m_device_pixels = nullptr; + PIXEL_FORMAT *m_host_pixels = nullptr; + + bool readbackActive = false; + cudaEvent_t readbackFinishedEvent; CUstream m_stream = 0u; int32_t m_device_idx = 0; @@ -102,26 +107,6 @@ CUDAOutputBuffer::CUDAOutputBuffer(int32_t width, int32_t height) if (!is_display_device) LOG_FATAL("GL interop is only available on display device."); - resize(width, height); -} - -template -CUDAOutputBuffer::~CUDAOutputBuffer() { - makeCurrent(); - - if (m_pbo != 0u) { - GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, 0)); - GL_CHECK(glDeleteBuffers(1, &m_pbo)); - } -} - -template -void CUDAOutputBuffer::resize(int32_t width, int32_t height) { - CHECK(width > 0 && height > 0); - - if (m_width == width && m_height == height) - return; - m_width = width; m_height = height; @@ -136,6 +121,19 @@ void CUDAOutputBuffer::resize(int32_t width, int32_t height) { CUDA_CHECK(cudaGraphicsGLRegisterBuffer(&m_cuda_gfx_resource, m_pbo, cudaGraphicsMapFlagsWriteDiscard)); + + CUDA_CHECK(cudaEventCreate(&readbackFinishedEvent)); + CUDA_CHECK(cudaMallocHost(&m_host_pixels, m_width * m_height * sizeof(PIXEL_FORMAT))); +} + +template +CUDAOutputBuffer::~CUDAOutputBuffer() { + makeCurrent(); + + if (m_pbo != 0u) { + GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, 0)); + GL_CHECK(glDeleteBuffers(1, &m_pbo)); + } } template @@ -171,6 +169,31 @@ void CUDAOutputBuffer::deletePBO() { m_pbo = 0; } +template +void CUDAOutputBuffer::StartAsynchronousReadback() { + CHECK(!readbackActive); + + makeCurrent(); + + CUDA_CHECK(cudaMemcpyAsync(m_host_pixels, m_device_pixels, + m_width * m_height * sizeof(PIXEL_FORMAT), + cudaMemcpyDeviceToHost)); + CUDA_CHECK(cudaEventRecord(readbackFinishedEvent)); + readbackActive = true; +} + +template +const PIXEL_FORMAT *CUDAOutputBuffer::GetReadbackPixels() { + if (!readbackActive) + return nullptr; + + makeCurrent(); + + CUDA_CHECK(cudaEventSynchronize(readbackFinishedEvent)); + readbackActive = false; + return m_host_pixels; +} + } // end namespace pbrt #endif // PBRT_GPU_CUDAGL_H diff --git a/src/pbrt/util/gui.cpp b/src/pbrt/util/gui.cpp index 131b71d5..ee4ec774 100644 --- a/src/pbrt/util/gui.cpp +++ b/src/pbrt/util/gui.cpp @@ -9,6 +9,8 @@ #include #endif PBRT_BUILD_GPU_RENDERER #include +#include +#include #define GL_CHECK(call) \ do { \ @@ -297,7 +299,7 @@ static void glfwErrorCallback(int error, const char* desc) { LOG_ERROR("GLFW [%d]: %s", error, desc); } -void GUI::keyboardCallback(GLFWwindow* window, int key, int scan, int action, int mods) { +void GUI::keyboardCallback(GLFWwindow *window, int key, int scan, int action, int mods) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GLFW_TRUE); @@ -318,14 +320,22 @@ void GUI::keyboardCallback(GLFWwindow* window, int key, int scan, int action, in doKey(GLFW_KEY_W, 'w'); doKey(GLFW_KEY_Q, 'q'); doKey(GLFW_KEY_E, 'e'); + doKey(GLFW_KEY_B, (mods & GLFW_MOD_SHIFT) ? 'B' : 'b'); - doKey(GLFW_KEY_R, 'r'); + doKey(GLFW_KEY_C, 'c'); doKey(GLFW_KEY_EQUAL, '='); doKey(GLFW_KEY_MINUS, '-'); + doKey(GLFW_KEY_LEFT, 'L'); doKey(GLFW_KEY_RIGHT, 'R'); doKey(GLFW_KEY_UP, 'U'); doKey(GLFW_KEY_DOWN, 'D'); + + if (key == GLFW_KEY_R && action == GLFW_PRESS && + glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) + recordFrames = !recordFrames; + else + doKey(GLFW_KEY_R, 'r'); } bool GUI::processKeys() { @@ -361,6 +371,10 @@ bool GUI::processKeys() { handleNeedsReset('r', [&](Transform t) { return Transform(); }); // No reset needed for these. + if (keysDown.find('c') != keysDown.end()) { + keysDown.erase(keysDown.find('c')); + printCameraTransform = true; + } if (keysDown.find('b') != keysDown.end()) { keysDown.erase(keysDown.find('b')); exposure *= 1.125f; @@ -452,6 +466,34 @@ DisplayState GUI::RefreshDisplay() { glfwSwapBuffers(window); glfwPollEvents(); + if (recordFrames) { + const RGB *fb = nullptr; +#ifdef PBRT_BUILD_GPU_RENDERER + if (cudaFramebuffer) + fb = cudaFramebuffer->GetReadbackPixels(); + else +#endif + fb = cpuFramebuffer; + + if (fb) { + Image image(PixelFormat::Float, {width, height}, {"R", "G", "B"}); + std::memcpy(image.RawPointer({0, 0}), fb, width * height * sizeof(RGB)); + +//CO RunAsync([](Image image, int frameNumber) { + // TODO: set metadata for e.g. current camera position... + ImageMetadata metadata; + image.Write(StringPrintf("pbrt-frame%05d.exr", frameNumber), metadata); +//CO return 0; // FIXME: RunAsync() doesn't like lambdas that return void.. +//CO }, std::move(image), frameNumber); + + ++frameNumber; + } +#ifdef PBRT_BUILD_GPU_RENDERER + if (cudaFramebuffer) + cudaFramebuffer->StartAsynchronousReadback(); +#endif + } + if (glfwWindowShouldClose(window)) return DisplayState::EXIT; else if (processKeys()) diff --git a/src/pbrt/util/gui.h b/src/pbrt/util/gui.h index 7e927b5a..1750969d 100644 --- a/src/pbrt/util/gui.h +++ b/src/pbrt/util/gui.h @@ -48,8 +48,12 @@ class GUI { DisplayState RefreshDisplay(); + // It's a little messy that the state of values controlled via the UI + // are just public variables here but it's probably not worth putting + // an abstraction layer on top of all this at this point. Transform GetCameraTransform() const { return movingFromCamera; } Float exposure = 1.f; + bool printCameraTransform = false; void keyboardCallback(GLFWwindow *window, int key, int scan, int action, int mods); @@ -60,6 +64,8 @@ class GUI { Float moveScale = 1.f; Transform movingFromCamera; Vector2i resolution; + bool recordFrames = false; + int frameNumber = 0; #ifdef PBRT_BUILD_GPU_RENDERER CUDAOutputBuffer *cudaFramebuffer = nullptr; diff --git a/src/pbrt/wavefront/integrator.cpp b/src/pbrt/wavefront/integrator.cpp index 1d74ba03..3d917062 100644 --- a/src/pbrt/wavefront/integrator.cpp +++ b/src/pbrt/wavefront/integrator.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -293,6 +294,7 @@ Float WavefrontPathIntegrator::Render() { // FIXME: camera animation; whatever... Transform renderFromCamera = camera.GetCameraTransform().RenderFromCamera().startTransform; Transform cameraFromRender = Inverse(renderFromCamera); + Transform cameraFromWorld = camera.GetCameraTransform().CameraFromWorld(camera.SampleTime(0.f)); if (Options->interactive) { if (!Options->displayServer.empty()) ErrorExit("--interactive and --display-server cannot be used at the same time."); @@ -432,6 +434,16 @@ Float WavefrontPathIntegrator::Render() { UpdateFramebufferFromFilm(pixelBounds, gui->exposure, rgb); gui->UnmapFramebuffer(); + if (gui->printCameraTransform) { + SquareMatrix<4> cfw = (Inverse(gui->GetCameraTransform()) * cameraFromWorld).GetMatrix(); + Printf("Current camera transform:\nTransform [ "); + for (int i = 0; i < 16; ++i) + Printf("%f ", cfw[i % 4][i / 4]); + Printf("]\n"); + std::fflush(stdout); + gui->printCameraTransform = false; + } + DisplayState state = gui->RefreshDisplay(); if (state == DisplayState::EXIT) break;