diff --git a/include/neural-graphics-primitives/testbed.h b/include/neural-graphics-primitives/testbed.h index cb5c9dea856bf57a40085457176fa2c8d017ed8a..4490d0b77ed00a8d7d80947746d01c0b7991afbe 100644 --- a/include/neural-graphics-primitives/testbed.h +++ b/include/neural-graphics-primitives/testbed.h @@ -462,6 +462,8 @@ public: fs::path training_data_path() const; void init_window(int resw, int resh, bool hidden = false, bool second_window = false); void destroy_window(); + void init_vr(); + void update_vr_performance_settings(); void apply_camera_smoothing(float elapsed_ms); int find_best_training_view(int default_view); bool begin_frame(); @@ -582,6 +584,7 @@ public: EMeshRenderMode m_mesh_render_mode = EMeshRenderMode::VertexNormals; uint32_t m_seed = 1337; + #ifdef NGP_GUI GLFWwindow* m_glfw_window = nullptr; struct SecondWindow { @@ -606,8 +609,6 @@ public: OpenXRHMD::FrameInfoPtr m_vr_frame_info; bool m_vr_depth_reproject = false; - void init_vr(); - void update_vr_performance_settings(); void set_n_views(size_t n_views); std::function<bool()> m_keyboard_event_callback; diff --git a/src/python_api.cu b/src/python_api.cu index fc95fe7b7a638e1ff69239dde456fd2fcc0c998c..951d7953c38b5b4dd827007ee8745bd70c2d300e 100644 --- a/src/python_api.cu +++ b/src/python_api.cu @@ -362,14 +362,15 @@ PYBIND11_MODULE(pyngp, m) { .def("load_training_data", &Testbed::load_training_data, py::call_guard<py::gil_scoped_release>(), "Load training data from a given path.") .def("clear_training_data", &Testbed::clear_training_data, "Clears training data to free up GPU memory.") // General control -#ifdef NGP_GUI .def("init_window", &Testbed::init_window, "Init a GLFW window that shows real-time progress and a GUI. 'second_window' creates a second copy of the output in its own window.", py::arg("width"), py::arg("height"), py::arg("hidden") = false, py::arg("second_window") = false ) - .def("init_vr", &Testbed::init_vr, "Init rendering to a connected and active VR headset. Requires a GUI window to have been previously created via `init_window`.") + .def("destroy_window", &Testbed::destroy_window, "Destroy the window again.") + .def("init_vr", &Testbed::init_vr, "Init rendering to a connected and active VR headset. Requires a window to have been previously created via `init_window`.") +#ifdef NGP_GUI .def_readwrite("keyboard_event_callback", &Testbed::m_keyboard_event_callback) .def("is_key_pressed", [](py::object& obj, int key) { return ImGui::IsKeyPressed(key); }) .def("is_key_down", [](py::object& obj, int key) { return ImGui::IsKeyDown(key); }) @@ -391,7 +392,6 @@ PYBIND11_MODULE(pyngp, m) { py::arg("fps") = 30.f, py::arg("shutter_fraction") = 1.0f ) - .def("destroy_window", &Testbed::destroy_window, "Destroy the window again.") .def("train", &Testbed::train, py::call_guard<py::gil_scoped_release>(), "Perform a single training step with a specified batch size.") .def("reset", &Testbed::reset_network, py::arg("reset_density_grid") = true, "Reset training.") .def("reset_accumulation", &Testbed::reset_accumulation, "Reset rendering accumulation.", diff --git a/src/testbed.cu b/src/testbed.cu index 78a5164e6ca175439a5c660d1b8e0d1ad8de2440..70aa0bcc2584165796e83e70f67558c20c904ad7 100644 --- a/src/testbed.cu +++ b/src/testbed.cu @@ -2875,87 +2875,6 @@ void Testbed::create_second_window() { glBindVertexArray(0); } -void Testbed::init_vr() { - try { - if (!m_glfw_window) { - throw std::runtime_error{"`init_window` must be called before `init_vr`"}; - } - -#if defined(XR_USE_PLATFORM_WIN32) - m_hmd = std::make_unique<OpenXRHMD>(wglGetCurrentDC(), glfwGetWGLContext(m_glfw_window)); -#elif defined(XR_USE_PLATFORM_XLIB) - Display* xDisplay = glfwGetX11Display(); - GLXContext glxContext = glfwGetGLXContext(m_glfw_window); - - int glxFBConfigXID = 0; - glXQueryContext(xDisplay, glxContext, GLX_FBCONFIG_ID, &glxFBConfigXID); - int attributes[3] = { GLX_FBCONFIG_ID, glxFBConfigXID, 0 }; - int nelements = 1; - GLXFBConfig* pglxFBConfig = glXChooseFBConfig(xDisplay, 0, attributes, &nelements); - if (nelements != 1 || !pglxFBConfig) { - throw std::runtime_error{"init_vr(): Couldn't obtain GLXFBConfig"}; - } - - GLXFBConfig glxFBConfig = *pglxFBConfig; - - XVisualInfo* visualInfo = glXGetVisualFromFBConfig(xDisplay, glxFBConfig); - if (!visualInfo) { - throw std::runtime_error{"init_vr(): Couldn't obtain XVisualInfo"}; - } - - m_hmd = std::make_unique<OpenXRHMD>(xDisplay, visualInfo->visualid, glxFBConfig, glXGetCurrentDrawable(), glxContext); -#elif defined(XR_USE_PLATFORM_WAYLAND) - m_hmd = std::make_unique<OpenXRHMD>(glfwGetWaylandDisplay()); -#endif - - // Enable aggressive optimizations to make the VR experience smooth. - update_vr_performance_settings(); - - // If multiple GPUs are available, shoot for 60 fps in VR. - // Otherwise, it wouldn't be realistic to expect more than 30. - m_dynamic_res_target_fps = m_devices.size() > 1 ? 60 : 30; - m_background_color = {0.0f, 0.0f, 0.0f, 0.0f}; - } catch (const std::runtime_error& e) { - if (std::string{e.what()}.find("XR_ERROR_FORM_FACTOR_UNAVAILABLE") != std::string::npos) { - throw std::runtime_error{"Could not initialize VR. Ensure that SteamVR, OculusVR, or any other OpenXR-compatible runtime is running. Also set it as the active OpenXR runtime."}; - } else { - throw std::runtime_error{fmt::format("Could not initialize VR: {}", e.what())}; - } - } -} - -void Testbed::update_vr_performance_settings() { - if (m_hmd) { - auto blend_mode = m_hmd->environment_blend_mode(); - - // DLSS is instrumental in getting VR to look good. Enable if possible. - // If the environment is blended in (such as in XR/AR applications), - // DLSS causes jittering at object sillhouettes (doesn't deal well with alpha), - // and hence stays disabled. - m_dlss = (blend_mode == EEnvironmentBlendMode::Opaque) && m_dlss_provider; - - // Foveated rendering is similarly vital in getting high performance without losing - // resolution in the middle of the view. - m_foveated_rendering = true; - - // Large minimum transmittance results in another 20-30% performance increase - // at the detriment of some transparent edges. Not super noticeable, though. - m_nerf.render_min_transmittance = 0.2f; - - // Many VR runtimes perform optical flow for automatic reprojection / motion smoothing. - // This breaks down for solid-color background, sometimes leading to artifacts. Hence: - // set background color to transparent and, in spherical_checkerboard_kernel(...), - // blend a checkerboard. If the user desires a solid background nonetheless, they can - // set the background color to have an alpha value of 1.0 manually via the GUI or via Python. - m_render_transparency_as_checkerboard = (blend_mode == EEnvironmentBlendMode::Opaque); - } else { - m_dlss = (m_testbed_mode == ETestbedMode::Nerf) && m_dlss_provider; - m_foveated_rendering = false; - m_nerf.render_min_transmittance = 0.01f; - m_render_transparency_as_checkerboard = false; - } -} - void Testbed::set_n_views(size_t n_views) { while (m_views.size() > n_views) { m_views.pop_back(); @@ -3168,6 +3087,93 @@ void Testbed::destroy_window() { #endif //NGP_GUI } +void Testbed::init_vr() { +#ifndef NGP_GUI + throw std::runtime_error{"init_vr failed: NGP was built without GUI support"}; +#else + try { + if (!m_glfw_window) { + throw std::runtime_error{"`init_window` must be called before `init_vr`"}; + } + +#if defined(XR_USE_PLATFORM_WIN32) + m_hmd = std::make_unique<OpenXRHMD>(wglGetCurrentDC(), glfwGetWGLContext(m_glfw_window)); +#elif defined(XR_USE_PLATFORM_XLIB) + Display* xDisplay = glfwGetX11Display(); + GLXContext glxContext = glfwGetGLXContext(m_glfw_window); + + int glxFBConfigXID = 0; + glXQueryContext(xDisplay, glxContext, GLX_FBCONFIG_ID, &glxFBConfigXID); + int attributes[3] = { GLX_FBCONFIG_ID, glxFBConfigXID, 0 }; + int nelements = 1; + GLXFBConfig* pglxFBConfig = glXChooseFBConfig(xDisplay, 0, attributes, &nelements); + if (nelements != 1 || !pglxFBConfig) { + throw std::runtime_error{"init_vr(): Couldn't obtain GLXFBConfig"}; + } + + GLXFBConfig glxFBConfig = *pglxFBConfig; + + XVisualInfo* visualInfo = glXGetVisualFromFBConfig(xDisplay, glxFBConfig); + if (!visualInfo) { + throw std::runtime_error{"init_vr(): Couldn't obtain XVisualInfo"}; + } + + m_hmd = std::make_unique<OpenXRHMD>(xDisplay, visualInfo->visualid, glxFBConfig, glXGetCurrentDrawable(), glxContext); +#elif defined(XR_USE_PLATFORM_WAYLAND) + m_hmd = std::make_unique<OpenXRHMD>(glfwGetWaylandDisplay()); +#endif + + // Enable aggressive optimizations to make the VR experience smooth. + update_vr_performance_settings(); + + // If multiple GPUs are available, shoot for 60 fps in VR. + // Otherwise, it wouldn't be realistic to expect more than 30. + m_dynamic_res_target_fps = m_devices.size() > 1 ? 60 : 30; + m_background_color = {0.0f, 0.0f, 0.0f, 0.0f}; + } catch (const std::runtime_error& e) { + if (std::string{e.what()}.find("XR_ERROR_FORM_FACTOR_UNAVAILABLE") != std::string::npos) { + throw std::runtime_error{"Could not initialize VR. Ensure that SteamVR, OculusVR, or any other OpenXR-compatible runtime is running. Also set it as the active OpenXR runtime."}; + } else { + throw std::runtime_error{fmt::format("Could not initialize VR: {}", e.what())}; + } + } +#endif //NGP_GUI +} + +void Testbed::update_vr_performance_settings() { +#ifdef NGP_GUI + if (m_hmd) { + auto blend_mode = m_hmd->environment_blend_mode(); + + // DLSS is instrumental in getting VR to look good. Enable if possible. + // If the environment is blended in (such as in XR/AR applications), + // DLSS causes jittering at object sillhouettes (doesn't deal well with alpha), + // and hence stays disabled. + m_dlss = (blend_mode == EEnvironmentBlendMode::Opaque) && m_dlss_provider; + + // Foveated rendering is similarly vital in getting high performance without losing + // resolution in the middle of the view. + m_foveated_rendering = true; + + // Large minimum transmittance results in another 20-30% performance increase + // at the detriment of some transparent edges. Not super noticeable, though. + m_nerf.render_min_transmittance = 0.2f; + + // Many VR runtimes perform optical flow for automatic reprojection / motion smoothing. + // This breaks down for solid-color background, sometimes leading to artifacts. Hence: + // set background color to transparent and, in spherical_checkerboard_kernel(...), + // blend a checkerboard. If the user desires a solid background nonetheless, they can + // set the background color to have an alpha value of 1.0 manually via the GUI or via Python. + m_render_transparency_as_checkerboard = (blend_mode == EEnvironmentBlendMode::Opaque); + } else { + m_dlss = (m_testbed_mode == ETestbedMode::Nerf) && m_dlss_provider; + m_foveated_rendering = false; + m_nerf.render_min_transmittance = 0.01f; + m_render_transparency_as_checkerboard = false; + } +#endif //NGP_GUI +} + bool Testbed::frame() { #ifdef NGP_GUI if (m_render_window) {