diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3db93652b..d4d3d99697 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,7 +55,7 @@ We also use pre-commit hooks to ensure linting and style enforcement. Install th ## Documentation - Our documentation style is based on Magnum / Corrade and uses [a similar build system](https://mcss.mosra.cz/documentation/doxygen/). -- A good example of the documentation style is in esp::gfx::DepthUnprojection (DepthUnprojection.h). +- The gfx_batch library is a good example of the documentation style. - Documentation of PRs is highly encouraged! ## Development Tips diff --git a/data/test_assets/screenshots/ReplayBatchRendererTest_env0.exr b/data/test_assets/screenshots/ReplayBatchRendererTest_env0.exr new file mode 100644 index 0000000000..fee2444334 Binary files /dev/null and b/data/test_assets/screenshots/ReplayBatchRendererTest_env0.exr differ diff --git a/data/test_assets/screenshots/ReplayBatchRendererTest_env1.exr b/data/test_assets/screenshots/ReplayBatchRendererTest_env1.exr new file mode 100644 index 0000000000..9f85e489b5 Binary files /dev/null and b/data/test_assets/screenshots/ReplayBatchRendererTest_env1.exr differ diff --git a/data/test_assets/screenshots/ReplayBatchRendererTest_env2.exr b/data/test_assets/screenshots/ReplayBatchRendererTest_env2.exr new file mode 100644 index 0000000000..c19b86e87c Binary files /dev/null and b/data/test_assets/screenshots/ReplayBatchRendererTest_env2.exr differ diff --git a/data/test_assets/screenshots/ReplayBatchRendererTest_env3.exr b/data/test_assets/screenshots/ReplayBatchRendererTest_env3.exr new file mode 100644 index 0000000000..844039404d Binary files /dev/null and b/data/test_assets/screenshots/ReplayBatchRendererTest_env3.exr differ diff --git a/src/cmake/FindMagnumPlugins.cmake b/src/cmake/FindMagnumPlugins.cmake index 5e835b0aab..4bebafbde9 100644 --- a/src/cmake/FindMagnumPlugins.cmake +++ b/src/cmake/FindMagnumPlugins.cmake @@ -446,7 +446,7 @@ foreach(_component ${MagnumPlugins_FIND_COMPONENTS}) # config if appropriate find_package(OpenEXR REQUIRED MODULE) set_property(TARGET MagnumPlugins::${_component} APPEND PROPERTY - INTERFACE_LINK_LIBRARIES OpenEXR::IlmImf) + INTERFACE_LINK_LIBRARIES OpenEXR::OpenEXR) # No special setup for the OpenDdl library # OpenGexImporter has no dependencies diff --git a/src/esp/gfx/CMakeLists.txt b/src/esp/gfx/CMakeLists.txt index 6eb835d44b..0e4691b4d2 100644 --- a/src/esp/gfx/CMakeLists.txt +++ b/src/esp/gfx/CMakeLists.txt @@ -6,8 +6,6 @@ set( gfx_SOURCES CubeMap.cpp CubeMap.h - DepthUnprojection.cpp - DepthUnprojection.h Drawable.cpp Drawable.h DrawableGroup.cpp @@ -108,10 +106,8 @@ find_package(MagnumPlugins REQUIRED GltfImporter StbImageImporter StbImageConver find_package(MagnumIntegration REQUIRED Eigen) find_package(Corrade REQUIRED Utility) -# TODO: enable the following flag and fix the compilation warnings -# set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) -corrade_add_resource(ShaderResources ../../shaders/Shaders.conf) -list(APPEND gfx_SOURCES ${ShaderResources}) +corrade_add_resource(GfxShaderResources ../../shaders/gfx/Shaders.conf) +list(APPEND gfx_SOURCES ${GfxShaderResources}) corrade_add_resource(PbrImageResources ../../../data/pbr/PbrImages.conf) list(APPEND gfx_SOURCES ${PbrImageResources}) @@ -122,14 +118,6 @@ add_library( ) if(BUILD_WITH_CUDA) - # We currently don't directly depend on gfx_batch for anything, just taking - # the header-only CUDA helpers from there, so gfx_batch isn't in - # target_link_libraries(). - target_include_directories( - gfx PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES} - ${CMAKE_CURRENT_LIST_DIR}/../gfx_batch/cuda_helpers - ) - target_link_libraries(gfx PUBLIC ${CUDART_LIBRARY}) endif() @@ -138,6 +126,7 @@ target_link_libraries( PUBLIC assets core io + gfx_batch physics Magnum::AnyImageImporter Magnum::AnySceneImporter diff --git a/src/esp/gfx/DoubleSphereCameraShader.cpp b/src/esp/gfx/DoubleSphereCameraShader.cpp index c3ed631868..2a58155301 100644 --- a/src/esp/gfx/DoubleSphereCameraShader.cpp +++ b/src/esp/gfx/DoubleSphereCameraShader.cpp @@ -17,7 +17,7 @@ // compiled into static library, it must be explicitly initialized via this // macro, and should be called *outside* of any namespace. static void importShaderResources() { - CORRADE_RESOURCE_INITIALIZE(ShaderResources) + CORRADE_RESOURCE_INITIALIZE(GfxShaderResources) } namespace Mn = Magnum; @@ -28,7 +28,7 @@ namespace gfx { DoubleSphereCameraShader::DoubleSphereCameraShader( CubeMapShaderBase::Flags flags) : CubeMapShaderBase(flags) { - if (!Cr::Utility::Resource::hasGroup("default-shaders")) { + if (!Cr::Utility::Resource::hasGroup("gfx-shaders")) { importShaderResources(); } @@ -40,7 +40,7 @@ DoubleSphereCameraShader::DoubleSphereCameraShader( // this is not the file name, but the group name in the config file // see Shaders.conf in the shaders folder - const Cr::Utility::Resource rs{"default-shaders"}; + const Cr::Utility::Resource rs{"gfx-shaders"}; Mn::GL::Shader vert{glVersion, Mn::GL::Shader::Type::Vertex}; Mn::GL::Shader frag{glVersion, Mn::GL::Shader::Type::Fragment}; diff --git a/src/esp/gfx/EquirectangularShader.cpp b/src/esp/gfx/EquirectangularShader.cpp index d88ebb2e7d..34ef74531c 100644 --- a/src/esp/gfx/EquirectangularShader.cpp +++ b/src/esp/gfx/EquirectangularShader.cpp @@ -18,7 +18,7 @@ // When the resource is compiled into static library, it must be explicitly // initialized via this macro, and should be called *outside* of any namespace. static void importShaderResources() { - CORRADE_RESOURCE_INITIALIZE(ShaderResources) + CORRADE_RESOURCE_INITIALIZE(GfxShaderResources) } namespace Mn = Magnum; @@ -33,7 +33,7 @@ EquirectangularShader::EquirectangularShader(Flags flags) "EquirectangularShader::EquirectangularShader(): shader " "flags cannot be empty.", ); - if (!Cr::Utility::Resource::hasGroup("default-shaders")) { + if (!Cr::Utility::Resource::hasGroup("gfx-shaders")) { importShaderResources(); } @@ -45,7 +45,7 @@ EquirectangularShader::EquirectangularShader(Flags flags) // this is not the file name, but the group name in the config file // see Shaders.conf in the shaders folder - const Cr::Utility::Resource rs{"default-shaders"}; + const Cr::Utility::Resource rs{"gfx-shaders"}; Mn::GL::Shader vert{glVersion, Mn::GL::Shader::Type::Vertex}; Mn::GL::Shader frag{glVersion, Mn::GL::Shader::Type::Fragment}; diff --git a/src/esp/gfx/GaussianFilterShader.cpp b/src/esp/gfx/GaussianFilterShader.cpp index ad87352e90..ebc0db9fe6 100644 --- a/src/esp/gfx/GaussianFilterShader.cpp +++ b/src/esp/gfx/GaussianFilterShader.cpp @@ -18,7 +18,7 @@ namespace Cr = Corrade; namespace Mn = Magnum; static void importShaderResources() { - CORRADE_RESOURCE_INITIALIZE(ShaderResources) + CORRADE_RESOURCE_INITIALIZE(GfxShaderResources) } namespace esp { @@ -29,11 +29,11 @@ enum { }; GaussianFilterShader::GaussianFilterShader() { - if (!Corrade::Utility::Resource::hasGroup("default-shaders")) { + if (!Corrade::Utility::Resource::hasGroup("gfx-shaders")) { importShaderResources(); } - const Corrade::Utility::Resource rs{"default-shaders"}; + const Corrade::Utility::Resource rs{"gfx-shaders"}; #ifdef MAGNUM_TARGET_WEBGL Mn::GL::Version glVersion = Mn::GL::Version::GLES300; diff --git a/src/esp/gfx/PTexMeshShader.cpp b/src/esp/gfx/PTexMeshShader.cpp index 949774a8c4..bfa2b494fc 100644 --- a/src/esp/gfx/PTexMeshShader.cpp +++ b/src/esp/gfx/PTexMeshShader.cpp @@ -20,7 +20,7 @@ // compiled into static library, it must be explicitly initialized via this // macro, and should be called // *outside* of any namespace. static void importShaderResources() { - CORRADE_RESOURCE_INITIALIZE(ShaderResources) + CORRADE_RESOURCE_INITIALIZE(GfxShaderResources) } namespace Mn = Magnum; @@ -40,12 +40,12 @@ enum TextureBindingPointIndex : uint8_t { PTexMeshShader::PTexMeshShader() { MAGNUM_ASSERT_GL_VERSION_SUPPORTED(Mn::GL::Version::GL410); - if (!Corrade::Utility::Resource::hasGroup("default-shaders")) { + if (!Corrade::Utility::Resource::hasGroup("gfx-shaders")) { importShaderResources(); } // this is not the file name, but the group name in the config file - const Corrade::Utility::Resource rs{"default-shaders"}; + const Corrade::Utility::Resource rs{"gfx-shaders"}; Mn::GL::Shader vert{Mn::GL::Version::GL410, Mn::GL::Shader::Type::Vertex}; Mn::GL::Shader geom{Mn::GL::Version::GL410, Mn::GL::Shader::Type::Geometry}; diff --git a/src/esp/gfx/PbrEquiRectangularToCubeMapShader.cpp b/src/esp/gfx/PbrEquiRectangularToCubeMapShader.cpp index 3b128841be..3275435d9c 100644 --- a/src/esp/gfx/PbrEquiRectangularToCubeMapShader.cpp +++ b/src/esp/gfx/PbrEquiRectangularToCubeMapShader.cpp @@ -19,7 +19,7 @@ // compiled into static library, it must be explicitly initialized via this // macro, and should be called *outside* of any namespace. static void importShaderResources() { - CORRADE_RESOURCE_INITIALIZE(ShaderResources) + CORRADE_RESOURCE_INITIALIZE(GfxShaderResources) } namespace Mn = Magnum; @@ -33,7 +33,7 @@ enum { }; PbrEquiRectangularToCubeMapShader::PbrEquiRectangularToCubeMapShader() { - if (!Corrade::Utility::Resource::hasGroup("default-shaders")) { + if (!Corrade::Utility::Resource::hasGroup("gfx-shaders")) { importShaderResources(); } @@ -45,7 +45,7 @@ PbrEquiRectangularToCubeMapShader::PbrEquiRectangularToCubeMapShader() { // this is not the file name, but the group name in the config file // see Shaders.conf in the shaders folder - const Cr::Utility::Resource rs{"default-shaders"}; + const Cr::Utility::Resource rs{"gfx-shaders"}; Mn::GL::Shader vert{glVersion, Mn::GL::Shader::Type::Vertex}; Mn::GL::Shader frag{glVersion, Mn::GL::Shader::Type::Fragment}; diff --git a/src/esp/gfx/PbrPrecomputedMapShader.cpp b/src/esp/gfx/PbrPrecomputedMapShader.cpp index cd61b46ca8..38540ab79a 100644 --- a/src/esp/gfx/PbrPrecomputedMapShader.cpp +++ b/src/esp/gfx/PbrPrecomputedMapShader.cpp @@ -25,7 +25,7 @@ // compiled into static library, it must be explicitly initialized via this // macro, and should be called *outside* of any namespace. static void importShaderResources() { - CORRADE_RESOURCE_INITIALIZE(ShaderResources) + CORRADE_RESOURCE_INITIALIZE(GfxShaderResources) } namespace Mn = Magnum; @@ -41,7 +41,7 @@ PbrPrecomputedMapShader::PbrPrecomputedMapShader(Flags flags) : flags_(flags) { "Flag::IrradianceMap and " "Flag::PrefilteredMap are mutually exclusive.", ); - if (!Corrade::Utility::Resource::hasGroup("default-shaders")) { + if (!Corrade::Utility::Resource::hasGroup("gfx-shaders")) { importShaderResources(); } @@ -53,7 +53,7 @@ PbrPrecomputedMapShader::PbrPrecomputedMapShader(Flags flags) : flags_(flags) { // this is not the file name, but the group name in the config file // see Shaders.conf in the shaders folder - const Cr::Utility::Resource rs{"default-shaders"}; + const Cr::Utility::Resource rs{"gfx-shaders"}; Mn::GL::Shader vert{glVersion, Mn::GL::Shader::Type::Vertex}; Mn::GL::Shader frag{glVersion, Mn::GL::Shader::Type::Fragment}; diff --git a/src/esp/gfx/PbrShader.cpp b/src/esp/gfx/PbrShader.cpp index 57230bd648..4e16b33a17 100644 --- a/src/esp/gfx/PbrShader.cpp +++ b/src/esp/gfx/PbrShader.cpp @@ -30,7 +30,7 @@ // compiled into static library, it must be explicitly initialized via this // macro, and should be called *outside* of any namespace. static void importShaderResources() { - CORRADE_RESOURCE_INITIALIZE(ShaderResources) + CORRADE_RESOURCE_INITIALIZE(GfxShaderResources) } namespace Mn = Magnum; @@ -41,7 +41,7 @@ namespace gfx { PbrShader::PbrShader(Flags originalFlags, unsigned int lightCount) : flags_(originalFlags), lightCount_(lightCount) { - if (!Cr::Utility::Resource::hasGroup("default-shaders")) { + if (!Cr::Utility::Resource::hasGroup("gfx-shaders")) { importShaderResources(); } @@ -74,7 +74,7 @@ PbrShader::PbrShader(Flags originalFlags, unsigned int lightCount) // this is not the file name, but the group name in the config file // see Shaders.conf in the shaders folder - const Cr::Utility::Resource rs{"default-shaders"}; + const Cr::Utility::Resource rs{"gfx-shaders"}; Mn::GL::Shader vert{glVersion, Mn::GL::Shader::Type::Vertex}; Mn::GL::Shader frag{glVersion, Mn::GL::Shader::Type::Fragment}; diff --git a/src/esp/gfx/RenderTarget.cpp b/src/esp/gfx/RenderTarget.cpp index 9f7ccccf4f..000f38bc63 100644 --- a/src/esp/gfx/RenderTarget.cpp +++ b/src/esp/gfx/RenderTarget.cpp @@ -22,7 +22,7 @@ #include "RenderTarget.h" #include "esp/sensor/VisualSensor.h" -#include "esp/gfx/DepthUnprojection.h" +#include "esp/gfx_batch/DepthUnprojection.h" #ifdef ESP_BUILD_WITH_CUDA #include @@ -46,7 +46,7 @@ const Mn::GL::Framebuffer::ColorAttachment UnprojectedDepthBufferAttachment = struct RenderTarget::Impl { Impl(const Mn::Vector2i& size, const Mn::Vector2& depthUnprojection, - DepthShader* depthShader, + gfx_batch::DepthShader* depthShader, Flags flags, const sensor::VisualSensor* visualSensor) : colorBuffer_{}, @@ -61,8 +61,9 @@ struct RenderTarget::Impl { flags_{flags}, visualSensor_{visualSensor} { if (depthShader_) { - CORRADE_INTERNAL_ASSERT(depthShader_->flags() & - DepthShader::Flag::UnprojectExistingDepth); + CORRADE_INTERNAL_ASSERT( + depthShader_->flags() & + gfx_batch::DepthShader::Flag::UnprojectExistingDepth); } if (flags_ & Flag::RgbaAttachment) { @@ -148,7 +149,7 @@ struct RenderTarget::Impl { CORRADE_INTERNAL_ASSERT(depthShader_ != nullptr); CORRADE_ASSERT( flags_ & Flag::DepthTextureAttachment, - "RenderTarget::Impl::unporojectDepthGPU(): this render target " + "RenderTarget::Impl::unprojectDepthGPU(): this render target " "was not created with depth texture enabled.", ); initDepthUnprojector(); @@ -220,8 +221,7 @@ struct RenderTarget::Impl { Mn::GL::PixelFormat::DepthComponent, Mn::GL::PixelType::Float, view.size(), view.data()}; framebuffer_.read(framebuffer_.viewport(), depthBufferView); - unprojectDepth(depthUnprojection_, - Cr::Containers::arrayCast(view.data())); + gfx_batch::unprojectDepth(depthUnprojection_, view.pixels()); } } @@ -353,7 +353,7 @@ struct RenderTarget::Impl { Mn::GL::Framebuffer framebuffer_; Mn::Vector2 depthUnprojection_; - DepthShader* depthShader_; + gfx_batch::DepthShader* depthShader_; Mn::GL::Renderbuffer unprojectedDepth_; Mn::GL::Mesh depthUnprojectionMesh_; Mn::GL::Framebuffer depthUnprojectionFrameBuffer_; @@ -371,7 +371,7 @@ struct RenderTarget::Impl { RenderTarget::RenderTarget(const Mn::Vector2i& size, const Mn::Vector2& depthUnprojection, - DepthShader* depthShader, + gfx_batch::DepthShader* depthShader, Flags flags, const sensor::VisualSensor* visualSensor) : pimpl_(spimpl::make_unique_impl(size, diff --git a/src/esp/gfx/RenderTarget.h b/src/esp/gfx/RenderTarget.h index 8afdb6832d..1bf6c97318 100644 --- a/src/esp/gfx/RenderTarget.h +++ b/src/esp/gfx/RenderTarget.h @@ -10,7 +10,6 @@ #include "esp/core/Esp.h" -#include "esp/gfx/DepthUnprojection.h" #include "esp/gfx/Renderer.h" namespace esp { @@ -18,6 +17,9 @@ namespace esp { namespace sensor { class VisualSensor; } +namespace gfx_batch { +class DepthShader; +} namespace gfx { @@ -70,7 +72,7 @@ class RenderTarget { */ RenderTarget(const Magnum::Vector2i& size, const Magnum::Vector2& depthUnprojection, - DepthShader* depthShader, + gfx_batch::DepthShader* depthShader, Flags flags = {Flag::RgbaAttachment | Flag::ObjectIdAttachment | Flag::DepthTextureAttachment}, const sensor::VisualSensor* visualSensor = nullptr); @@ -78,7 +80,7 @@ class RenderTarget { /** * @brief Constructor * @param size The size of the underlying framebuffers in WxH - * @param depthUnprojection Depth unrpojection parameters. See @ref + * @param depthUnprojection Depth unprojection parameters. See @ref * calculateDepthUnprojection() * @param visualSensor (optional) The visual sensor for this render * target diff --git a/src/esp/gfx/Renderer.cpp b/src/esp/gfx/Renderer.cpp index 7fbda18e0f..664d44be56 100644 --- a/src/esp/gfx/Renderer.cpp +++ b/src/esp/gfx/Renderer.cpp @@ -22,10 +22,10 @@ #include #include "esp/core/Check.h" -#include "esp/gfx/DepthUnprojection.h" #include "esp/gfx/GaussianFilterShader.h" #include "esp/gfx/RenderTarget.h" #include "esp/gfx/TextureVisualizerShader.h" +#include "esp/gfx_batch/DepthUnprojection.h" #include "esp/sensor/VisualSensor.h" #include "esp/sim/Simulator.h" @@ -336,8 +336,8 @@ struct Renderer::Impl { "depthUnprojection matrix", ); if (!depthShader_) { - depthShader_ = std::make_unique( - DepthShader::Flag::UnprojectExistingDepth); + depthShader_ = std::make_unique( + gfx_batch::DepthShader::Flag::UnprojectExistingDepth); } RenderTarget::Flags renderTargetFlags = {}; @@ -381,7 +381,7 @@ struct Renderer::Impl { WindowlessContext* context_; bool contextIsOwned_ = true; // TODO: shall we use shader resource manager from now? - std::unique_ptr depthShader_; + std::unique_ptr depthShader_; const Flags flags_; #ifdef ESP_BUILD_WITH_BACKGROUND_RENDERER std::unique_ptr backgroundRenderer_ = nullptr; @@ -431,7 +431,8 @@ struct Renderer::Impl { if (type == RendererShaderType::DepthShader) { shaderManager_.set( shader.key(), - new DepthShader{DepthShader::Flag::UnprojectExistingDepth}, + new gfx_batch::DepthShader{ + gfx_batch::DepthShader::Flag::UnprojectExistingDepth}, Mn::ResourceDataState::Final, Mn::ResourcePolicy::Resident); } else if (type == RendererShaderType::DepthTextureVisualizer) { shaderManager_.set( diff --git a/src/esp/gfx/TextureVisualizerShader.cpp b/src/esp/gfx/TextureVisualizerShader.cpp index cc4a6a65f5..e30bd6cfb0 100644 --- a/src/esp/gfx/TextureVisualizerShader.cpp +++ b/src/esp/gfx/TextureVisualizerShader.cpp @@ -18,7 +18,7 @@ namespace Cr = Corrade; namespace Mn = Magnum; static void importShaderResources() { - CORRADE_RESOURCE_INITIALIZE(ShaderResources) + CORRADE_RESOURCE_INITIALIZE(GfxShaderResources) } namespace esp { @@ -42,11 +42,11 @@ TextureVisualizerShader::TextureVisualizerShader(Flags flags) : flags_(flags) { "Flag::DepthTexture and " "Flag::ObjectIdTexture are mutually exclusive.", ); - if (!Corrade::Utility::Resource::hasGroup("default-shaders")) { + if (!Corrade::Utility::Resource::hasGroup("gfx-shaders")) { importShaderResources(); } - const Corrade::Utility::Resource rs{"default-shaders"}; + const Corrade::Utility::Resource rs{"gfx-shaders"}; #ifdef MAGNUM_TARGET_WEBGL Mn::GL::Version glVersion = Mn::GL::Version::GLES300; diff --git a/src/esp/gfx/VarianceShadowMapShader.cpp b/src/esp/gfx/VarianceShadowMapShader.cpp index 5720b8bd73..567d899a2a 100644 --- a/src/esp/gfx/VarianceShadowMapShader.cpp +++ b/src/esp/gfx/VarianceShadowMapShader.cpp @@ -24,7 +24,7 @@ // compiled into static library, it must be explicitly initialized via this // macro, and should be called *outside* of any namespace. static void importShaderResources() { - CORRADE_RESOURCE_INITIALIZE(ShaderResources) + CORRADE_RESOURCE_INITIALIZE(GfxShaderResources) } namespace Mn = Magnum; @@ -34,7 +34,7 @@ namespace esp { namespace gfx { VarianceShadowMapShader::VarianceShadowMapShader() { - if (!Corrade::Utility::Resource::hasGroup("default-shaders")) { + if (!Corrade::Utility::Resource::hasGroup("gfx-shaders")) { importShaderResources(); } @@ -46,7 +46,7 @@ VarianceShadowMapShader::VarianceShadowMapShader() { // this is not the file name, but the group name in the config file // see Shaders.conf in the shaders folder - const Cr::Utility::Resource rs{"default-shaders"}; + const Cr::Utility::Resource rs{"gfx-shaders"}; Mn::GL::Shader vert{glVersion, Mn::GL::Shader::Type::Vertex}; Mn::GL::Shader frag{glVersion, Mn::GL::Shader::Type::Fragment}; diff --git a/src/esp/gfx_batch/CMakeLists.txt b/src/esp/gfx_batch/CMakeLists.txt index fbe8a645b2..f403f62aed 100644 --- a/src/esp/gfx_batch/CMakeLists.txt +++ b/src/esp/gfx_batch/CMakeLists.txt @@ -10,11 +10,22 @@ find_package( Shaders Trade ) +find_package(Corrade REQUIRED Utility) -set(gfx_batch_SOURCES Renderer.cpp Renderer.h RendererStandalone.cpp - RendererStandalone.h +set( + gfx_batch_SOURCES + DepthUnprojection.cpp + DepthUnprojection.h + Renderer.cpp + Renderer.h + RendererStandalone.cpp + RendererStandalone.h ) +set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) +corrade_add_resource(GfxBatchShaderResources ../../shaders/gfx_batch/Shaders.conf) +list(APPEND gfx_batch_SOURCES ${GfxBatchShaderResources}) + add_library( gfx_batch STATIC ${gfx_batch_SOURCES} @@ -24,8 +35,8 @@ add_library( target_include_directories(gfx_batch PUBLIC ${PROJECT_BINARY_DIR}) if(BUILD_WITH_CUDA) target_include_directories( - gfx_batch PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES} - ${CMAKE_CURRENT_LIST_DIR}/cuda_helpers + gfx_batch PUBLIC ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES} + ${CMAKE_CURRENT_LIST_DIR}/cuda_helpers ) target_link_libraries(gfx_batch PUBLIC ${CUDART_LIBRARY}) diff --git a/src/esp/gfx/DepthUnprojection.cpp b/src/esp/gfx_batch/DepthUnprojection.cpp similarity index 78% rename from src/esp/gfx/DepthUnprojection.cpp rename to src/esp/gfx_batch/DepthUnprojection.cpp index 91625ce94c..d8a19d1ade 100644 --- a/src/esp/gfx/DepthUnprojection.cpp +++ b/src/esp/gfx_batch/DepthUnprojection.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -17,22 +18,21 @@ namespace Cr = Corrade; namespace Mn = Magnum; static void importShaderResources() { - CORRADE_RESOURCE_INITIALIZE(ShaderResources) + CORRADE_RESOURCE_INITIALIZE(GfxBatchShaderResources) } namespace esp { -namespace gfx { +namespace gfx_batch { namespace { enum { DepthTextureUnit = 1 }; } DepthShader::DepthShader(Flags flags) : flags_{flags} { - if (!Corrade::Utility::Resource::hasGroup("default-shaders")) { + if (!Corrade::Utility::Resource::hasGroup("gfx-batch-shaders")) { importShaderResources(); } - - const Corrade::Utility::Resource rs{"default-shaders"}; + const Corrade::Utility::Resource rs{"gfx-batch-shaders"}; #ifdef MAGNUM_TARGET_WEBGL Mn::GL::Version glVersion = Mn::GL::Version::GLES300; @@ -108,24 +108,29 @@ Mn::Vector2 calculateDepthUnprojection(const Mn::Matrix4& projectionMatrix) { #if defined(CORRADE_TARGET_X86) && defined(__GNUC__) && __GNUC__ >= 6 __attribute__((target_clones("default", "sse4.2", "avx2"))) #endif -void unprojectDepth(const Mn::Vector2& unprojection, - Cr::Containers::ArrayView depth) { - for (Mn::Float& d : depth) { - d = unprojection[1] / (d + unprojection[0]); +void unprojectDepth( + const Mn::Vector2& unprojection, + const Cr::Containers::StridedArrayView2D& depth) { + for (Cr::Containers::StridedArrayView1D row : depth) { + for (Mn::Float& d : row) { + d = unprojection[1] / (d + unprojection[0]); + } } /* Change pixels on the far plane to be 0. Done in a separate loop to allow the optimizer to vectorize the above better. */ const Mn::Float farDepth = unprojection[1] / (1.0f + unprojection[0]); - for (Mn::Float& d : depth) { - /* We can afford using == for comparison as 1.0f has an exact - representation, the depth was cleared to exactly this value and the - calculation is done exactly the same way in both cases -- thus the - result should be bit-exact. */ - if (d == farDepth) - d = 0.0f; + for (Cr::Containers::StridedArrayView1D row : depth) { + for (Mn::Float& d : row) { + /* We can afford using == for comparison as 1.0f has an exact + representation, the depth was cleared to exactly this value and the + calculation is done exactly the same way in both cases -- thus the + result should be bit-exact. */ + if (d == farDepth) + d = 0.0f; + } } } -} // namespace gfx +} // namespace gfx_batch } // namespace esp diff --git a/src/esp/gfx/DepthUnprojection.h b/src/esp/gfx_batch/DepthUnprojection.h similarity index 96% rename from src/esp/gfx/DepthUnprojection.h rename to src/esp/gfx_batch/DepthUnprojection.h index 37ae5295de..ac03e6cf78 100644 --- a/src/esp/gfx/DepthUnprojection.h +++ b/src/esp/gfx_batch/DepthUnprojection.h @@ -9,7 +9,7 @@ #include namespace esp { -namespace gfx { +namespace gfx_batch { /** @brief Depth-only shader @@ -148,10 +148,11 @@ Additionally to applying that calculation, if the input depth is at the far plane (of value @cpp 1.0f @ce), it's set to @cpp 0.0f @ce on output as consumers expect zeros for things that are too far. */ -void unprojectDepth(const Magnum::Vector2& unprojection, - Corrade::Containers::ArrayView depth); +void unprojectDepth( + const Magnum::Vector2& unprojection, + const Corrade::Containers::StridedArrayView2D& depth); -} // namespace gfx +} // namespace gfx_batch } // namespace esp #endif // ESP_GFX_DEPTHUNPROJECTION_H_ diff --git a/src/esp/gfx_batch/Renderer.cpp b/src/esp/gfx_batch/Renderer.cpp index 0f6b9a41e5..d7f7af495f 100644 --- a/src/esp/gfx_batch/Renderer.cpp +++ b/src/esp/gfx_batch/Renderer.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include namespace Cr = Corrade; @@ -150,6 +152,9 @@ Mn::UnsignedInt drawBatchId( } struct Scene { + /* Camera unprojection. Updated from updateCamera(). */ + Mn::Vector2 cameraUnprojection; + /* Node parents and transformations. Appended to with add(). Some of these (but not all) are referenced from the transformationIds array below. */ Cr::Containers::Array parents; /* parents[i] < i, always */ @@ -245,8 +250,8 @@ struct Renderer::State { /* Updated from addFile() */ Mn::GL::Buffer materialUniform; - /* Updated from camera() */ - Cr::Containers::Array projections; + /* Combined view and projection matrices. Updated from updateCamera() */ + Cr::Containers::Array cameraMatrices; /* Updated from draw() every frame */ Mn::GL::Buffer projectionUniform; @@ -276,7 +281,7 @@ void Renderer::create(const RendererConfiguration& configurationWrapper) { state_->maxLightCount = configuration.maxLightCount; state_->ambientFactor = configuration.ambientFactor; const std::size_t sceneCount = configuration.tileCount.product(); - state_->projections = Cr::Containers::Array{sceneCount}; + state_->cameraMatrices = Cr::Containers::Array{sceneCount}; state_->scenes = Cr::Containers::Array{sceneCount}; /* Texture 0 is reserved as a white pixel */ @@ -622,7 +627,7 @@ bool Renderer::addFile(const Cr::Containers::StringView filename, /* Fill initial projection data for each view. Will be uploaded afresh every draw. */ - state_->projections = Cr::Containers::Array{ + state_->cameraMatrices = Cr::Containers::Array{ Cr::DefaultInit, std::size_t(state_->tileCount.product())}; // TODO (mutable) buffer storage @@ -745,7 +750,7 @@ bool Renderer::addFile(const Cr::Containers::StringView filename, "named templates" to be referenced from addNodeHierarchy(). */ if (!(flags & RendererFileFlag::Whole)) { /* Transformations of all objects in the scene. Objects that don't have - this field default to an indentity transform. */ + this field default to an identity transform. */ Cr::Containers::Array transformations{ std::size_t(scene->mappingBound())}; for (Cr::Containers::Pair transformation : @@ -1050,8 +1055,38 @@ void Renderer::clearLights(const Mn::UnsignedInt sceneId) { transformations every frame anyway */ } -Mn::Matrix4& Renderer::camera(const Mn::UnsignedInt scene) { - return state_->projections[scene].projectionMatrix; +Magnum::Matrix4 Renderer::camera(Magnum::UnsignedInt sceneId) const { + CORRADE_ASSERT(sceneId < state_->scenes.size(), + "Renderer::camera(): index" << sceneId << "out of range for" + << state_->scenes.size() + << "scenes", + {}); + + return state_->cameraMatrices[sceneId].projectionMatrix; +} + +Magnum::Vector2 Renderer::cameraDepthUnprojection( + Magnum::UnsignedInt sceneId) const { + CORRADE_ASSERT(sceneId < state_->scenes.size(), + "Renderer::cameraDepthUnprojection(): index" + << sceneId << "out of range for" << state_->scenes.size() + << "scenes", + {}); + + return state_->scenes[sceneId].cameraUnprojection; +} + +void Renderer::updateCamera(Magnum::UnsignedInt sceneId, + const Magnum::Matrix4& projection, + const Magnum::Matrix4& view) { + CORRADE_ASSERT(sceneId < state_->scenes.size(), + "Renderer::updateCamera(): index" + << sceneId << "out of range for" << state_->scenes.size() + << "scenes", ); + + state_->cameraMatrices[sceneId].projectionMatrix = projection * view; + state_->scenes[sceneId].cameraUnprojection = + calculateDepthUnprojection(projection); } Cr::Containers::StridedArrayView1D Renderer::transformations( @@ -1165,7 +1200,7 @@ void Renderer::draw(Mn::GL::AbstractFramebuffer& framebuffer) { /* Upload projection uniform, assuming it changes every frame. Do it early to minimize stalls. */ - state_->projectionUniform.setData(state_->projections); + state_->projectionUniform.setData(state_->cameraMatrices); /* Calculate absolute transformations */ for (std::size_t sceneId = 0; sceneId != state_->scenes.size(); ++sceneId) { diff --git a/src/esp/gfx_batch/Renderer.h b/src/esp/gfx_batch/Renderer.h index cbefa8f4c8..a25ce7a2b5 100644 --- a/src/esp/gfx_batch/Renderer.h +++ b/src/esp/gfx_batch/Renderer.h @@ -219,7 +219,7 @@ initial transformation and returns an ID of the node added into the scene. The ID can then be used to subsequently update its transformation via @ref transformations(), for example in respose to a physics simulation or an animation. Each scene also has an associated camera and its combined projection -and transformation matrix can be updated using @ref camera(). +and transformation matrix can be updated using @ref updateCamera(). Finally, the @ref draw() function renders the grid of scenes into a provided framebuffer. @@ -239,7 +239,7 @@ implemented in @ref shaders-usage-multidraw "Magnum shaders". added to a *draw list*. The draw list is @ref gfx_batch-Renderer-workflow-draw-list "further detailed below". - For each @ref draw() and each non-empty scene, the following is done: - - The @ref camera() transformation is uploaded to a uniform buffer. + - The transformation passed to @ref updateCamera() is uploaded to a uniform buffer. - The renderer calculates hierarchical transformations for all nodes based on the matrices supplied via @ref transformations(). Each item in the draw list is then assigned a corresponding calculated absolute transformation, @@ -485,7 +485,7 @@ class Renderer { /** * @brief Max light count * - * By default there's zero lights, i.e. flat-shaded renderering. + * By default there's zero lights, i.e. flat-shaded rendering. * @see @ref RendererConfiguration::setMaxLightCount() */ Magnum::UnsignedInt maxLightCount() const; @@ -640,13 +640,31 @@ class Renderer { void clearLights(Magnum::UnsignedInt sceneId); /** - * @brief Combined camera projection and transformation matrix - * @param sceneId Scene ID, expected to be less than @ref sceneCount() + * @brief Get the combined projection and view matrices of a camera + * (read-only) + * @param sceneId Scene ID, expected to be less than @ref sceneCount() + */ + Magnum::Matrix4 camera(Magnum::UnsignedInt sceneId) const; + + /** + * @brief Get the depth unprojection parameters of a camera (read-only) + * @param sceneId Scene ID, expected to be less than @ref sceneCount() + */ + Magnum::Vector2 cameraDepthUnprojection(Magnum::UnsignedInt sceneId) const; + + /** + * @brief Set the camera projection and view matrices + * @param sceneId Scene ID, expected to be less than @ref sceneCount() + * @param view View matrix of the camera (inverse transform) + * @param projection Projection matrix of the camera * + * Also computes the camera unprojection. * Modifications to the transformation are taken into account in the next * @ref draw(). */ - Magnum::Matrix4& camera(Magnum::UnsignedInt sceneId); + void updateCamera(Magnum::UnsignedInt sceneId, + const Magnum::Matrix4& projection, + const Magnum::Matrix4& view); /** * @brief Transformations of all nodes in the scene diff --git a/src/esp/gfx_batch/RendererStandalone.h b/src/esp/gfx_batch/RendererStandalone.h index 9b74417f9c..dead2a01f0 100644 --- a/src/esp/gfx_batch/RendererStandalone.h +++ b/src/esp/gfx_batch/RendererStandalone.h @@ -29,7 +29,7 @@ enum class RendererStandaloneFlag { * @m_class{m-note m-warning} * * @par - * **Not recommmended** to be enabled in end-user applications, as the log + * **Not recommended** to be enabled in end-user applications, as the log * contains vital information for debugging platform-specific issues. */ QuietLog = 1 << 0 @@ -189,7 +189,9 @@ class RendererStandalone : public Renderer { const Magnum::MutableImageView2D& image); /** - * @brief Retrieve the rendered depth output + * @brief Retrieve the raw rendered depth output. + * + * This returns the depth buffer as-is. To unproject, use @ref unprojectDepth(). * * Stalls the CPU until the GPU finishes the last @ref draw() and then * returns an image in @ref depthFramebufferFormat() and with size being @@ -198,7 +200,10 @@ class RendererStandalone : public Renderer { Magnum::Image2D depthImage(); /** - * @brief Retrieve the rendered depth output into a pre-allocated location + * @brief Retrieve the raw rendered depth output into a pre-allocated + * location. + * + * This returns the depth buffer as-is. To unproject, use @ref unprojectDepth(). * * Expects that @p rectangle is contained in a size defined * by @ref tileSize() multiplied by @ref tileCount(), that @p image diff --git a/src/esp/sensor/CMakeLists.txt b/src/esp/sensor/CMakeLists.txt index 1d78c1cf33..b85962d3de 100644 --- a/src/esp/sensor/CMakeLists.txt +++ b/src/esp/sensor/CMakeLists.txt @@ -42,7 +42,7 @@ add_library( target_link_libraries( sensor - PUBLIC core gfx scene sim + PUBLIC core gfx gfx_batch scene sim ) # ATTENTION developers !!!!!!!!!!!!!!!!!!!! diff --git a/src/esp/sensor/CameraSensor.cpp b/src/esp/sensor/CameraSensor.cpp index ceb073ea2b..bcd8dc12b6 100644 --- a/src/esp/sensor/CameraSensor.cpp +++ b/src/esp/sensor/CameraSensor.cpp @@ -10,7 +10,7 @@ #include #include "CameraSensor.h" -#include "esp/gfx/DepthUnprojection.h" +#include "esp/gfx_batch/DepthUnprojection.h" #include "esp/sim/Simulator.h" namespace esp { @@ -175,7 +175,7 @@ Corrade::Containers::Optional CameraSensor::depthUnprojection() const { // projectionMatrix_ is managed by implementation class and is set whenever // quantities change. - return {gfx::calculateDepthUnprojection(projectionMatrix_)}; + return {gfx_batch::calculateDepthUnprojection(projectionMatrix_)}; } // CameraSensor::depthUnprojection } // namespace sensor diff --git a/src/esp/sim/BatchReplayRenderer.cpp b/src/esp/sim/BatchReplayRenderer.cpp index 121a84b21b..c8101ae893 100644 --- a/src/esp/sim/BatchReplayRenderer.cpp +++ b/src/esp/sim/BatchReplayRenderer.cpp @@ -4,6 +4,7 @@ #include "BatchReplayRenderer.h" +#include #include "esp/sensor/CameraSensor.h" #include @@ -232,7 +233,8 @@ void BatchReplayRenderer::doSetSensorTransform( // TODO assumes there's just one sensor per env const std::string&, const Mn::Matrix4& transform) { - renderer_->camera(envIndex) = theOnlySensorProjection_ * transform.inverted(); + renderer_->updateCamera(envIndex, theOnlySensorProjection_, + transform.inverted()); } void BatchReplayRenderer::doSetSensorTransformsFromKeyframe( @@ -246,9 +248,9 @@ void BatchReplayRenderer::doSetSensorTransformsFromKeyframe( ESP_CHECK(found, "setSensorTransformsFromKeyframe: couldn't find user transform \"" << userName << "\" for environment " << envIndex << "."); - renderer_->camera(envIndex) = - theOnlySensorProjection_ * - Mn::Matrix4::from(rotation.toMatrix(), translation).inverted(); + renderer_->updateCamera( + envIndex, theOnlySensorProjection_, + Mn::Matrix4::from(rotation.toMatrix(), translation).inverted()); } void BatchReplayRenderer::doRender( @@ -257,7 +259,8 @@ void BatchReplayRenderer::doRender( CORRADE_ASSERT(standalone_, "BatchReplayRenderer::render(): can use this function only " "with a standalone renderer", ); - static_cast(*renderer_).draw(); + auto& standalone = static_cast(*renderer_); + standalone.draw(); // todo: integrate debugLineRender_->flushLines CORRADE_INTERNAL_ASSERT(!debugLineRender_); @@ -270,12 +273,17 @@ void BatchReplayRenderer::doRender( renderer_->tileSize()); if (colorImageViews.size() > 0) { - static_cast(*renderer_) - .colorImageInto(rectangle, colorImageViews[envIndex]); + standalone.colorImageInto(rectangle, colorImageViews[envIndex]); } if (depthImageViews.size() > 0) { - static_cast(*renderer_) - .depthImageInto(rectangle, depthImageViews[envIndex]); + Mn::MutableImageView2D depthBufferView{ + standalone.depthFramebufferFormat(), depthImageViews[envIndex].size(), + depthImageViews[envIndex].data()}; + standalone.depthImageInto(rectangle, depthBufferView); + + // TODO: Add GPU depth unprojection support. + gfx_batch::unprojectDepth(renderer_->cameraDepthUnprojection(envIndex), + depthBufferView.pixels()); } } } diff --git a/src/esp/sim/ClassicReplayRenderer.cpp b/src/esp/sim/ClassicReplayRenderer.cpp index a72e7b1c6e..a75168ffef 100644 --- a/src/esp/sim/ClassicReplayRenderer.cpp +++ b/src/esp/sim/ClassicReplayRenderer.cpp @@ -227,7 +227,7 @@ void ClassicReplayRenderer::doRender( } #ifdef ESP_BUILD_WITH_BACKGROUND_RENDERER - if (imageViews != nullptr) { + if (imageViews.size() > 0) { renderer_->enqueueAsyncDrawJob( visualSensor, sceneGraph, imageViews[envIndex], esp::gfx::RenderCamera::Flags{ diff --git a/src/shaders/Shaders.conf b/src/shaders/gfx/Shaders.conf similarity index 92% rename from src/shaders/Shaders.conf rename to src/shaders/gfx/Shaders.conf index 34579cda55..9e9528ba49 100644 --- a/src/shaders/Shaders.conf +++ b/src/shaders/gfx/Shaders.conf @@ -1,14 +1,8 @@ -group = default-shaders +group = gfx-shaders # To avoid a needless copy when the sources get added to GL::Shader nullTerminated=true -[file] -filename = depth.vert - -[file] -filename = depth.frag - [file] filename = ptex-default-gl410.vert diff --git a/src/shaders/bigTriangle.vert b/src/shaders/gfx/bigTriangle.vert similarity index 100% rename from src/shaders/bigTriangle.vert rename to src/shaders/gfx/bigTriangle.vert diff --git a/src/shaders/doubleSphereCamera.frag b/src/shaders/gfx/doubleSphereCamera.frag similarity index 100% rename from src/shaders/doubleSphereCamera.frag rename to src/shaders/gfx/doubleSphereCamera.frag diff --git a/src/shaders/equirectangular.frag b/src/shaders/gfx/equirectangular.frag similarity index 100% rename from src/shaders/equirectangular.frag rename to src/shaders/gfx/equirectangular.frag diff --git a/src/shaders/equirectangularToCubeMap.frag b/src/shaders/gfx/equirectangularToCubeMap.frag similarity index 100% rename from src/shaders/equirectangularToCubeMap.frag rename to src/shaders/gfx/equirectangularToCubeMap.frag diff --git a/src/shaders/gaussianFilter.frag b/src/shaders/gfx/gaussianFilter.frag similarity index 100% rename from src/shaders/gaussianFilter.frag rename to src/shaders/gfx/gaussianFilter.frag diff --git a/src/shaders/pbr.frag b/src/shaders/gfx/pbr.frag similarity index 100% rename from src/shaders/pbr.frag rename to src/shaders/gfx/pbr.frag diff --git a/src/shaders/pbr.vert b/src/shaders/gfx/pbr.vert similarity index 100% rename from src/shaders/pbr.vert rename to src/shaders/gfx/pbr.vert diff --git a/src/shaders/pbrBSDF.glsl b/src/shaders/gfx/pbrBSDF.glsl similarity index 100% rename from src/shaders/pbrBSDF.glsl rename to src/shaders/gfx/pbrBSDF.glsl diff --git a/src/shaders/pbrCommon.glsl b/src/shaders/gfx/pbrCommon.glsl similarity index 100% rename from src/shaders/pbrCommon.glsl rename to src/shaders/gfx/pbrCommon.glsl diff --git a/src/shaders/pbrIrradianceMap.frag b/src/shaders/gfx/pbrIrradianceMap.frag similarity index 100% rename from src/shaders/pbrIrradianceMap.frag rename to src/shaders/gfx/pbrIrradianceMap.frag diff --git a/src/shaders/pbrLighting.glsl b/src/shaders/gfx/pbrLighting.glsl similarity index 100% rename from src/shaders/pbrLighting.glsl rename to src/shaders/gfx/pbrLighting.glsl diff --git a/src/shaders/pbrMaterials.glsl b/src/shaders/gfx/pbrMaterials.glsl similarity index 100% rename from src/shaders/pbrMaterials.glsl rename to src/shaders/gfx/pbrMaterials.glsl diff --git a/src/shaders/pbrPrecomputedMap.vert b/src/shaders/gfx/pbrPrecomputedMap.vert similarity index 100% rename from src/shaders/pbrPrecomputedMap.vert rename to src/shaders/gfx/pbrPrecomputedMap.vert diff --git a/src/shaders/pbrPrefilteredMap.frag b/src/shaders/gfx/pbrPrefilteredMap.frag similarity index 100% rename from src/shaders/pbrPrefilteredMap.frag rename to src/shaders/gfx/pbrPrefilteredMap.frag diff --git a/src/shaders/pbrStructs.glsl b/src/shaders/gfx/pbrStructs.glsl similarity index 100% rename from src/shaders/pbrStructs.glsl rename to src/shaders/gfx/pbrStructs.glsl diff --git a/src/shaders/pbrUniforms.glsl b/src/shaders/gfx/pbrUniforms.glsl similarity index 100% rename from src/shaders/pbrUniforms.glsl rename to src/shaders/gfx/pbrUniforms.glsl diff --git a/src/shaders/ptex-default-gl410.frag b/src/shaders/gfx/ptex-default-gl410.frag similarity index 100% rename from src/shaders/ptex-default-gl410.frag rename to src/shaders/gfx/ptex-default-gl410.frag diff --git a/src/shaders/ptex-default-gl410.geom b/src/shaders/gfx/ptex-default-gl410.geom similarity index 100% rename from src/shaders/ptex-default-gl410.geom rename to src/shaders/gfx/ptex-default-gl410.geom diff --git a/src/shaders/ptex-default-gl410.vert b/src/shaders/gfx/ptex-default-gl410.vert similarity index 100% rename from src/shaders/ptex-default-gl410.vert rename to src/shaders/gfx/ptex-default-gl410.vert diff --git a/src/shaders/shadowsVSM.glsl b/src/shaders/gfx/shadowsVSM.glsl similarity index 100% rename from src/shaders/shadowsVSM.glsl rename to src/shaders/gfx/shadowsVSM.glsl diff --git a/src/shaders/textureVisualizer.frag b/src/shaders/gfx/textureVisualizer.frag similarity index 100% rename from src/shaders/textureVisualizer.frag rename to src/shaders/gfx/textureVisualizer.frag diff --git a/src/shaders/varianceShadowMap.frag b/src/shaders/gfx/varianceShadowMap.frag similarity index 100% rename from src/shaders/varianceShadowMap.frag rename to src/shaders/gfx/varianceShadowMap.frag diff --git a/src/shaders/varianceShadowMap.vert b/src/shaders/gfx/varianceShadowMap.vert similarity index 100% rename from src/shaders/varianceShadowMap.vert rename to src/shaders/gfx/varianceShadowMap.vert diff --git a/src/shaders/gfx_batch/Shaders.conf b/src/shaders/gfx_batch/Shaders.conf new file mode 100644 index 0000000000..d06448de91 --- /dev/null +++ b/src/shaders/gfx_batch/Shaders.conf @@ -0,0 +1,10 @@ +group = gfx-batch-shaders + +# To avoid a needless copy when the sources get added to GL::Shader +nullTerminated=true + +[file] +filename = depth.vert + +[file] +filename = depth.frag diff --git a/src/shaders/depth.frag b/src/shaders/gfx_batch/depth.frag similarity index 100% rename from src/shaders/depth.frag rename to src/shaders/gfx_batch/depth.frag diff --git a/src/shaders/depth.vert b/src/shaders/gfx_batch/depth.vert similarity index 100% rename from src/shaders/depth.vert rename to src/shaders/gfx_batch/depth.vert diff --git a/src/tests/BatchReplayRendererTest.cpp b/src/tests/BatchReplayRendererTest.cpp index d9a6f01bbf..c035cafe3e 100644 --- a/src/tests/BatchReplayRendererTest.cpp +++ b/src/tests/BatchReplayRendererTest.cpp @@ -72,18 +72,13 @@ Mn::MutableImageView2D getRGBView(int width, Mn::MutableImageView2D getDepthView(int width, int height, - std::vector& buffer, - bool classic) { + std::vector& buffer) { Mn::Vector2i size(width, height); constexpr int pixelSize = 4; buffer.resize(std::size_t(width * height * pixelSize)); - // BEWARE: Classic renderer requires R32F because the depth is unprojected. - // Batch renderer directly returns the depth buffer at the moment. - auto pixelFormat = - classic ? Mn::PixelFormat::R32F : Mn::PixelFormat::Depth32F; - auto view = Mn::MutableImageView2D(pixelFormat, size, buffer); + auto view = Mn::MutableImageView2D(Mn::PixelFormat::R32F, size, buffer); return view; } @@ -230,7 +225,6 @@ void BatchReplayRendererTest::testIntegration() { constexpr int numEnvs = 4; const std::string userPrefix = "sensor_"; const std::string screenshotPrefix = "ReplayBatchRendererTest_env"; - const std::string screenshotExtension = ".png"; std::vector serKeyframes; for (int envIndex = 0; envIndex < numEnvs; envIndex++) { @@ -287,8 +281,6 @@ void BatchReplayRendererTest::testIntegration() { { Cr::Containers::Pointer renderer = data.create(batchRendererConfig); - bool isClassicRenderer = dynamic_cast( - renderer.get()) != nullptr; // Check that the context is properly created CORRADE_VERIFY(Mn::GL::Context::hasCurrent()); @@ -305,10 +297,9 @@ void BatchReplayRendererTest::testIntegration() { renderer->sensorSize(envIndex).y(), colorBuffers[envIndex])); } if (data.testFlags & TestFlag::Depth) { - depthImageViews.emplace_back( - getDepthView(renderer->sensorSize(envIndex).x(), - renderer->sensorSize(envIndex).y(), - depthBuffers[envIndex], isClassicRenderer)); + depthImageViews.emplace_back(getDepthView( + renderer->sensorSize(envIndex).x(), + renderer->sensorSize(envIndex).y(), depthBuffers[envIndex])); } } @@ -324,7 +315,7 @@ void BatchReplayRendererTest::testIntegration() { // Test color output if (data.testFlags & TestFlag::Color) { std::string groundTruthImageFile = - screenshotPrefix + std::to_string(envIndex) + screenshotExtension; + screenshotPrefix + std::to_string(envIndex) + ".png"; CORRADE_COMPARE_WITH( Mn::ImageView2D{colorImageViews[envIndex]}, Cr::Utility::Path::join(screenshotDir, groundTruthImageFile), @@ -333,10 +324,12 @@ void BatchReplayRendererTest::testIntegration() { // Test depth output if (data.testFlags & TestFlag::Depth) { const auto depth = depthImageViews[envIndex]; - float pixelA = depth.pixels()[32][32]; - float pixelB = depth.pixels()[64][64]; - CORRADE_VERIFY(pixelA > 0.0f); - CORRADE_VERIFY(pixelA != pixelB); + std::string groundTruthImageFile = + screenshotPrefix + std::to_string(envIndex) + ".exr"; + CORRADE_COMPARE_WITH( + Mn::ImageView2D{depthImageViews[envIndex]}, + Cr::Utility::Path::join(screenshotDir, groundTruthImageFile), + (Mn::DebugTools::CompareImageToFile{maxThreshold, meanThreshold})); } } diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 4c5c06e49e..853dda7524 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -8,6 +8,10 @@ find_package( OPTIONAL_COMPONENTS GltfSceneConverter KtxImageConverter ) +if(BUILD_WITH_BACKGROUND_RENDERER) + find_package(MagnumPlugins REQUIRED OpenExrImporter OpenExrImageConverter) +endif() + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h ) @@ -38,6 +42,7 @@ corrade_add_test( LIBRARIES gfx_batch Magnum::DebugTools + Magnum::AnyImageConverter Magnum::AnySceneImporter MagnumPlugins::GltfImporter MagnumPlugins::KtxImporter @@ -65,7 +70,7 @@ corrade_add_test( DepthUnprojectionTest DepthUnprojectionTest.cpp LIBRARIES - gfx + gfx_batch Magnum::MeshTools Magnum::OpenGLTester Magnum::Trade @@ -96,6 +101,8 @@ if(BUILD_WITH_BACKGROUND_RENDERER) gfx gfx_batch sim + MagnumPlugins::OpenExrImageConverter + MagnumPlugins::OpenExrImporter ) target_include_directories( BatchReplayRendererTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR} diff --git a/src/tests/CullingTest.cpp b/src/tests/CullingTest.cpp index 2f2285cbb4..92304b3d76 100644 --- a/src/tests/CullingTest.cpp +++ b/src/tests/CullingTest.cpp @@ -17,6 +17,7 @@ #include "esp/gfx/RenderCamera.h" #include "esp/gfx/RenderTarget.h" #include "esp/gfx/WindowlessContext.h" +#include "esp/gfx_batch/DepthUnprojection.h" #include "esp/metadata/MetadataMediator.h" #include "esp/scene/SceneManager.h" @@ -191,7 +192,7 @@ void CullingTest::frustumCulling() { // create a render target Mn::Matrix4 projMtx = renderCamera.projectionMatrix(); esp::gfx::RenderTarget::uptr target = esp::gfx::RenderTarget::create_unique( - frameBufferSize, esp::gfx::calculateDepthUnprojection(projMtx)); + frameBufferSize, esp::gfx_batch::calculateDepthUnprojection(projMtx)); // ============== Test 1 ================== // draw all the invisibles reported by cull() diff --git a/src/tests/DepthUnprojectionTest.cpp b/src/tests/DepthUnprojectionTest.cpp index 2e8a17c132..d6b7e56544 100644 --- a/src/tests/DepthUnprojectionTest.cpp +++ b/src/tests/DepthUnprojectionTest.cpp @@ -19,7 +19,7 @@ #include #include -#include "esp/gfx/DepthUnprojection.h" +#include "esp/gfx_batch/DepthUnprojection.h" namespace Cr = Corrade; namespace Mn = Magnum; @@ -28,7 +28,7 @@ using Mn::Math::Literals::operator""_degf; using Mn::Math::Literals::operator""_rgbf; namespace { -using namespace esp::gfx; +using namespace esp::gfx_batch; struct DepthUnprojectionTest : Mn::GL::OpenGLTester { explicit DepthUnprojectionTest(); @@ -90,13 +90,15 @@ __attribute__((target_clones("default", "sse4.2", "avx2"))) #endif CORRADE_NEVER_INLINE void unprojectBaseline(const Mn::Matrix4& unprojection, - Cr::Containers::ArrayView depth) { - for (float& d : depth) { - if (d == 1.0f) { - d = 0.0f; - continue; + const Cr::Containers::StridedArrayView2D& depth) { + for (const Cr::Containers::StridedArrayView1D row : depth) { + for (float& d : row) { + if (d == 1.0f) { + d = 0.0f; + continue; + } + d = -unprojection.transformPoint(Mn::Vector3::zAxis(-d)).z(); } - d = -unprojection.transformPoint(Mn::Vector3::zAxis(-d)).z(); } } @@ -104,10 +106,13 @@ unprojectBaseline(const Mn::Matrix4& unprojection, __attribute__((target_clones("default", "sse4.2", "avx2"))) #endif CORRADE_NEVER_INLINE void -unprojectBaselineNoBranch(const Mn::Matrix4& unprojection, - Cr::Containers::ArrayView depth) { - for (float& d : depth) { - d = -unprojection.transformPoint(Mn::Vector3::zAxis(-d)).z(); +unprojectBaselineNoBranch( + const Mn::Matrix4& unprojection, + const Cr::Containers::StridedArrayView2D& depth) { + for (const Cr::Containers::StridedArrayView1D row : depth) { + for (float& d : row) { + d = -unprojection.transformPoint(Mn::Vector3::zAxis(-d)).z(); + } } } @@ -116,17 +121,21 @@ __attribute__((target_clones("default", "sse4.2", "avx2"))) #endif CORRADE_NEVER_INLINE void unprojectDepthNoBranch(const Mn::Vector2& unprojection, - Cr::Containers::ArrayView depth) { - for (float& d : depth) { - d = unprojection[1] / (d + unprojection[0]); + const Cr::Containers::StridedArrayView2D& depth) { + for (const Cr::Containers::StridedArrayView1D row : depth) { + for (float& d : row) { + d = unprojection[1] / (d + unprojection[0]); + } } } const struct { const char* name; - void (*unprojectorFull)(const Mn::Matrix4&, Cr::Containers::ArrayView); - void (*unprojectorOptimized)(const Mn::Vector2&, - Cr::Containers::ArrayView); + void (*unprojectorFull)(const Mn::Matrix4&, + const Cr::Containers::StridedArrayView2D&); + void (*unprojectorOptimized)( + const Mn::Vector2&, + const Cr::Containers::StridedArrayView2D&); DepthShader::Flags flags; } UnprojectBenchmarkData[]{ {"", unprojectBaseline, unprojectDepth, {}}, @@ -140,7 +149,7 @@ DepthUnprojectionTest::DepthUnprojectionTest() { &DepthUnprojectionTest::testGpuUnprojectExisting}, Cr::Containers::arraySize(TestData)); - addInstancedBenchmarks({&DepthUnprojectionTest::benchmarkBaseline}, 50, + addInstancedBenchmarks({&DepthUnprojectionTest::benchmarkBaseline}, 10, Cr::Containers::arraySize(UnprojectBenchmarkData)); addInstancedBenchmarks({&DepthUnprojectionTest::benchmarkCpu}, 50, @@ -166,7 +175,8 @@ void DepthUnprojectionTest::testCpu() { Cr::TestSuite::Compare::around(data.depth * 0.0006f)); float depth[] = {Mn::Math::lerpInverted(-1.0f, 1.0f, projected.z())}; - unprojectDepth(calculateDepthUnprojection(data.projection), depth); + unprojectDepth(calculateDepthUnprojection(data.projection), + Cr::Containers::stridedArrayView(depth)); CORRADE_COMPARE_WITH(depth[0], data.expected, Cr::TestSuite::Compare::around(data.depth * 0.0002f)); } @@ -279,7 +289,9 @@ void DepthUnprojectionTest::benchmarkBaseline() { for (std::size_t i = 0; i != depth.size(); ++i) depth[i] = float(2 * (i % 10000)) / float(10000) - 1.0f; - CORRADE_BENCHMARK(1) { data.unprojectorFull(unprojection, depth); } + CORRADE_BENCHMARK(1) { + data.unprojectorFull(unprojection, stridedArrayView(depth)); + } CORRADE_COMPARE_AS(Mn::Math::max(depth), 9.0f, Cr::TestSuite::Compare::Greater); @@ -297,7 +309,9 @@ void DepthUnprojectionTest::benchmarkCpu() { for (std::size_t i = 0; i != depth.size(); ++i) depth[i] = float(i % 10000) / float(10000); - CORRADE_BENCHMARK(1) { data.unprojectorOptimized(unprojection, depth); } + CORRADE_BENCHMARK(1) { + data.unprojectorOptimized(unprojection, stridedArrayView(depth)); + } CORRADE_COMPARE_AS(Mn::Math::max(depth), 9.0f, Cr::TestSuite::Compare::Greater); diff --git a/src/tests/GfxBatchRendererTest.cpp b/src/tests/GfxBatchRendererTest.cpp index 4c6da21555..c1c59b6d07 100644 --- a/src/tests/GfxBatchRendererTest.cpp +++ b/src/tests/GfxBatchRendererTest.cpp @@ -2,9 +2,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. -#include #include -#include #include #include #include @@ -15,8 +13,6 @@ #include #include #include -#include -#include #include #include #include @@ -33,6 +29,8 @@ #include #include +#include +#include "Corrade/TestSuite/Compare/Numeric.h" #include "esp/gfx_batch/RendererStandalone.h" #ifdef ESP_BUILD_WITH_CUDA @@ -71,6 +69,7 @@ struct GfxBatchRendererTest : Cr::TestSuite::Tester { void clearLights(); void imageInto(); + void depthUnprojection(); void cudaInterop(); }; @@ -297,6 +296,7 @@ GfxBatchRendererTest::GfxBatchRendererTest() { addTests({&GfxBatchRendererTest::clearLights, &GfxBatchRendererTest::imageInto, + &GfxBatchRendererTest::depthUnprojection, &GfxBatchRendererTest::cudaInterop}); // clang-format on } @@ -482,7 +482,7 @@ void GfxBatchRendererTest::generateTestData() { } meshes[11]; struct Transformation { Mn::UnsignedInt object; - Mn::Matrix4 trasformation; + Mn::Matrix4 transformation; } transformations[4]; } scene[]{{ {{0, -1}, {1, 0}, /* square and its child mesh */ @@ -540,7 +540,7 @@ void GfxBatchRendererTest::generateTestData() { Cr::Containers::stridedArrayView(scene->meshes).slice(&Scene::Mesh::meshViewMaterial)}, Mn::Trade::SceneFieldData{Mn::Trade::SceneField::Transformation, Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::object), - Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::trasformation)}, + Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::transformation)}, }})); // clang-format on @@ -700,7 +700,7 @@ void GfxBatchRendererTest::generateTestDataMultipleMeshes() { } meshes[7]; struct Transformation { Mn::UnsignedInt object; - Mn::Matrix4 trasformation; + Mn::Matrix4 transformation; } transformations[4]; } scene[]{{ {{0, -1}, {1, 0}, /* square and its child mesh */ @@ -740,7 +740,7 @@ void GfxBatchRendererTest::generateTestDataMultipleMeshes() { Cr::Containers::stridedArrayView(scene->meshes).slice(&Scene::Mesh::meshMaterial)}, Mn::Trade::SceneFieldData{Mn::Trade::SceneField::Transformation, Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::object), - Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::trasformation)}, + Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::transformation)}, }})); // clang-format on @@ -946,7 +946,7 @@ void GfxBatchRendererTest::generateTestDataMultipleTextures() { } meshes[7]; struct Transformation { Mn::UnsignedInt object; - Mn::Matrix4 trasformation; + Mn::Matrix4 transformation; } transformations[4]; } scene[]{{ {{0, -1}, {1, 0}, /* square and its child mesh */ @@ -992,7 +992,7 @@ void GfxBatchRendererTest::generateTestDataMultipleTextures() { Cr::Containers::stridedArrayView(scene->meshes).slice(&Scene::Mesh::meshViewMaterial)}, Mn::Trade::SceneFieldData{Mn::Trade::SceneField::Transformation, Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::object), - Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::trasformation)}, + Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::transformation)}, }})); // clang-format on @@ -1333,7 +1333,7 @@ void GfxBatchRendererTest::generateTestDataFourSquares() { } meshes[4]; struct Transformation { Mn::UnsignedInt object; - Mn::Matrix4 trasformation; + Mn::Matrix4 transformation; } transformations[4]; } scene[]{{ {{0, -1}, {1, 0}, {2, 0}, /* four squares */ @@ -1363,7 +1363,7 @@ void GfxBatchRendererTest::generateTestDataFourSquares() { Cr::Containers::stridedArrayView(scene->meshes).slice(&Scene::Mesh::meshMaterial)}, Mn::Trade::SceneFieldData{Mn::Trade::SceneField::Transformation, Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::object), - Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::trasformation)}, + Cr::Containers::stridedArrayView(scene->transformations).slice(&Scene::Transformation::transformation)}, }}; // clang-format on @@ -1385,9 +1385,9 @@ void GfxBatchRendererTest::generateTestDataFourSquares() { if (data.sceneDeepHierarchy) { scene->parents[2].parent = 1; scene->parents[4].parent = 3; - scene->transformations[1].trasformation = + scene->transformations[1].transformation = Mn::Matrix4::translation(Mn::Vector3::xAxis(1.0f / 0.4f)); - scene->transformations[3].trasformation = + scene->transformations[3].transformation = Mn::Matrix4::translation(Mn::Vector3::xAxis(1.0f / 0.4f)); } @@ -1540,10 +1540,11 @@ void GfxBatchRendererTest::singleMesh() { file.second(), file.third())); /* Undo the aspect ratio, move camera back */ - renderer.camera(0) = + renderer.updateCamera( + 0, Mn::Matrix4::orthographicProjection(2.0f * Mn::Vector2{4.0f / 3.0f, 1.0f}, - 0.1f, 10.0f) * - Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted(); + 0.1f, 10.0f), + Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted()); CORRADE_VERIFY(renderer.hasNodeHierarchy("square")); CORRADE_VERIFY(!renderer.hasNodeHierarchy("squares")); @@ -1618,10 +1619,11 @@ void GfxBatchRendererTest::meshHierarchy() { file.second(), file.third())); /* Undo the aspect ratio, move camera back */ - renderer.camera(0) = + renderer.updateCamera( + 0, Mn::Matrix4::orthographicProjection(2.0f * Mn::Vector2{4.0f / 3.0f, 1.0f}, - 0.1f, 10.0f) * - Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted(); + 0.1f, 10.0f), + Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted()); CORRADE_COMPARE(renderer.addNodeHierarchy(0, "four squares"), 0); @@ -1675,10 +1677,11 @@ void GfxBatchRendererTest::multipleMeshes() { Cr::Utility::Path::join({TEST_ASSETS, "scenes", file.first()}), file.second(), file.third())); - renderer.camera(0) = + renderer.updateCamera( + 0, Mn::Matrix4::orthographicProjection(2.0f * Mn::Vector2{4.0f / 3.0f, 1.0f}, - 0.1f, 10.0f) * - Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted(); + 0.1f, 10.0f), + Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted()); CORRADE_COMPARE(renderer.addNodeHierarchy(0, "square"), 0); renderer.transformations(0)[0] = @@ -1750,10 +1753,11 @@ void GfxBatchRendererTest::multipleScenes() { Cr::Utility::Path::join({TEST_ASSETS, "scenes", file.first()}), file.second(), file.third())); - renderer.camera(0) = + renderer.updateCamera( + 0, Mn::Matrix4::orthographicProjection(2.0f * Mn::Vector2{1.0f, 4.0f / 3.0f}, - 0.1f, 10.0f) * - Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted(); + 0.1f, 10.0f), + Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted()); /* Scene 0 has one multi-mesh, scene 1 has two single-meshes, scene 2 is unused and scene 3 has a single triangle */ @@ -1784,10 +1788,13 @@ void GfxBatchRendererTest::multipleScenes() { /* Each camera is shifted differently on Y, each added mesh is shifted differently on X to test the right transformation is used each time */ - renderer.camera(0) = Mn::Matrix4::translation({0.0f, 0.0f, 1.0f}).inverted(); + const auto identity = Mn::Matrix4{Mn::Math::IdentityInit}; + renderer.updateCamera( + 0, identity, Mn::Matrix4::translation({0.0f, 0.0f, 1.0f}).inverted()); renderer.transformations(0)[0] = Mn::Matrix4::translation({0.0f, 0.0f, 0.0f}); - renderer.camera(1) = Mn::Matrix4::translation({0.0f, 0.5f, 1.0f}).inverted(); + renderer.updateCamera( + 1, identity, Mn::Matrix4::translation({0.0f, 0.5f, 1.0f}).inverted()); renderer.transformations(1)[0] = Mn::Matrix4::translation({0.5f, 0.0f, 0.0f}) * Mn::Matrix4::scaling(Mn::Vector3{0.5f}); @@ -1795,7 +1802,8 @@ void GfxBatchRendererTest::multipleScenes() { Mn::Matrix4::translation({-0.5f, 1.0f, 0.0f}) * Mn::Matrix4::scaling(Mn::Vector3{0.5f}); - renderer.camera(3) = Mn::Matrix4::translation({0.0f, -0.5f, 1.0f}).inverted(); + renderer.updateCamera( + 3, identity, Mn::Matrix4::translation({0.0f, -0.5f, 1.0f}).inverted()); renderer.transformations(3)[0] = Mn::Matrix4::translation({0.5f, 0.0f, 0.0f}) * Mn::Matrix4::scaling(Mn::Vector3{0.5f}); @@ -1832,10 +1840,11 @@ void GfxBatchRendererTest::clearScene() { Cr::Utility::Path::join({TEST_ASSETS, "scenes", file.first()}), file.second(), file.third())); - renderer.camera(0) = + renderer.updateCamera( + 0, Mn::Matrix4::orthographicProjection(2.0f * Mn::Vector2{1.0f, 4.0f / 3.0f}, - 0.1f, 10.0f) * - Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted(); + 0.1f, 10.0f), + Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted()); /* Like in multipleScenes(), except in different order, there's more stuff added to scene 1 and it isn't transformed in any way */ @@ -1845,12 +1854,16 @@ void GfxBatchRendererTest::clearScene() { CORRADE_COMPARE(renderer.addNodeHierarchy(1, "triangle"), 4); CORRADE_COMPARE(renderer.addNodeHierarchy(0, "four squares"), 0); - renderer.camera(0) = Mn::Matrix4::translation({0.0f, 0.0f, 1.0f}).inverted(); + const auto identity = Mn::Matrix4{Mn::Math::IdentityInit}; + renderer.updateCamera( + 0, identity, Mn::Matrix4::translation({0.0f, 0.0f, 1.0f}).inverted()); renderer.transformations(0)[0] = Mn::Matrix4::translation({0.0f, 0.0f, 0.0f}); - renderer.camera(1) = Mn::Matrix4::translation({0.0f, 0.5f, 1.0f}).inverted(); + renderer.updateCamera( + 1, identity, Mn::Matrix4::translation({0.0f, 0.5f, 1.0f}).inverted()); - renderer.camera(3) = Mn::Matrix4::translation({0.0f, -0.5f, 1.0f}).inverted(); + renderer.updateCamera( + 3, identity, Mn::Matrix4::translation({0.0f, -0.5f, 1.0f}).inverted()); renderer.transformations(3)[0] = Mn::Matrix4::translation({0.5f, 0.0f, 0.0f}) * Mn::Matrix4::scaling(Mn::Vector3{0.5f}); @@ -1910,9 +1923,10 @@ void GfxBatchRendererTest::lights() { Cr::Utility::Path::join(TEST_ASSETS, "scenes/batch.gltf"))); /* Undo the aspect ratio, move camera back */ - renderer.camera(0) = - Mn::Matrix4::perspectiveProjection(60.0_degf, 4.0f / 3.0f, 0.1f, 10.0f) * - Mn::Matrix4::translation(Mn::Vector3::zAxis(5.0f)).inverted(); + renderer.updateCamera( + 0, + Mn::Matrix4::perspectiveProjection(60.0_degf, 4.0f / 3.0f, 0.1f, 10.0f), + Mn::Matrix4::translation(Mn::Vector3::zAxis(5.0f)).inverted()); /* Add meshes, transform them in place */ CORRADE_COMPARE(renderer.addNodeHierarchy(0, data.firstObject), 0); @@ -1993,9 +2007,10 @@ void GfxBatchRendererTest::clearLights() { Cr::Utility::Path::join(TEST_ASSETS, "scenes/batch.gltf"))); /* Undo the aspect ratio, move camera back */ - renderer.camera(0) = - Mn::Matrix4::perspectiveProjection(60.0_degf, 4.0f / 3.0f, 0.1f, 10.0f) * - Mn::Matrix4::translation(Mn::Vector3::zAxis(5.0f)).inverted(); + renderer.updateCamera( + 0, + Mn::Matrix4::perspectiveProjection(60.0_degf, 4.0f / 3.0f, 0.1f, 10.0f), + Mn::Matrix4::translation(Mn::Vector3::zAxis(5.0f)).inverted()); /* Add the same scene as in lights() */ CORRADE_COMPARE(renderer.addNodeHierarchy(0, "flat checkerboard sphere"), 0); @@ -2118,10 +2133,11 @@ void GfxBatchRendererTest::imageInto() { /* Mostly the same as singleMesh() */ CORRADE_VERIFY(renderer.addFile( Cr::Utility::Path::join(TEST_ASSETS, "scenes/batch.gltf"))); - renderer.camera(0) = + renderer.updateCamera( + 0, Mn::Matrix4::orthographicProjection(2.0f * Mn::Vector2{4.0f / 3.0f, 1.0f}, - 0.1f, 10.0f) * - Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted(); + 0.1f, 10.0f), + Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted()); renderer.addNodeHierarchy(0, "square", Mn::Matrix4::scaling(Mn::Vector3{0.8f})); renderer.draw(); @@ -2156,6 +2172,79 @@ void GfxBatchRendererTest::imageInto() { CORRADE_COMPARE(depth.pixels()[64][48], 0.0909091f); } +void GfxBatchRendererTest::depthUnprojection() { + constexpr Mn::Vector2i tileCount{2, 2}; + constexpr float near = 0.001f; + constexpr float far = 10.0f; + constexpr Mn::Vector2i tileSize(64, 64); + + // clang-format off + esp::gfx_batch::RendererStandalone renderer{ + esp::gfx_batch::RendererConfiguration{} + .setTileSizeCount(tileSize, tileCount), + esp::gfx_batch::RendererStandaloneConfiguration{} + .setFlags(esp::gfx_batch::RendererStandaloneFlag::QuietLog) + }; + // clang-format on + + CORRADE_VERIFY(renderer.addFile( + Cr::Utility::Path::join(TEST_ASSETS, "scenes/batch.gltf"))); + + // Place environment cameras at various distances from origin. + const auto& projection = + Mn::Matrix4::perspectiveProjection(60.0_degf, 1.0f, near, far); + renderer.updateCamera( + 0, projection, + Mn::Matrix4::translation(Mn::Vector3::zAxis(2.5f)).inverted()); + renderer.updateCamera( + 1, projection, + Mn::Matrix4::translation(Mn::Vector3::zAxis(5.0f)).inverted()); + renderer.updateCamera( + 2, projection, + Mn::Matrix4::translation(Mn::Vector3::zAxis(7.5f)).inverted()); + renderer.updateCamera( + 3, projection, + Mn::Matrix4::translation(Mn::Vector3::zAxis(20.0f)).inverted()); + + // Spawn a plane in each environment, at origin, facing the camera. + CORRADE_VERIFY(renderer.hasNodeHierarchy("square")); + for (int i = 0; i < tileCount.product(); ++i) { + CORRADE_COMPARE(renderer.addNodeHierarchy(i, "square"), 0); + } + + // Render. + renderer.draw(); + Mn::Image2D depth = renderer.depthImage(); + MAGNUM_VERIFY_NO_GL_ERROR(); + + // Unproject each scene. + for (int y = 0; y != tileCount.y(); ++y) { + for (int x = 0; x != tileCount.x(); ++x) { + const std::size_t sceneId = y * tileCount.x() + x; + const Mn::Containers::Size2D offset(x * tileSize.x(), y * tileSize.y()); + const Mn::Containers::Size2D size(tileSize.x(), tileSize.y()); + esp::gfx_batch::unprojectDepth( + renderer.cameraDepthUnprojection(sceneId), + depth.pixels().sliceSize(offset, size)); + } + } + + // Unprojected depth should read the distance between the cameras and the + // plane, in meters. For each environment, the center pixel is compared with + // the camera distance from origin. + // Target 0 is at 2.5 meters from the camera + CORRADE_COMPARE_WITH(depth.pixels()[32][32], 2.5f, + Corrade::TestSuite::Compare::around(0.01f)); + // Target 1 is at 5.0 meters from the camera + CORRADE_COMPARE_WITH(depth.pixels()[32][96], 5.0f, + Corrade::TestSuite::Compare::around(0.01f)); + // Target 2 is at 7.5 meters from the camera + CORRADE_COMPARE_WITH(depth.pixels()[96][32], 7.5f, + Corrade::TestSuite::Compare::around(0.01f)); + // Target 3 is beyond the far plane. Here, unprojected depth is set to 0. + CORRADE_COMPARE(depth.pixels()[96][96], 0.0f); +} + void GfxBatchRendererTest::cudaInterop() { #ifndef ESP_BUILD_WITH_CUDA CORRADE_SKIP("ESP_BUILD_WITH_CUDA is not enabled"); @@ -2186,10 +2275,11 @@ void GfxBatchRendererTest::cudaInterop() { /* Mostly the same as singleMesh() */ CORRADE_VERIFY(renderer.addFile( Cr::Utility::Path::join(TEST_ASSETS, "scenes/batch.gltf"))); - renderer.camera(0) = + renderer.updateCamera( + 0, Mn::Matrix4::orthographicProjection(2.0f * Mn::Vector2{4.0f / 3.0f, 1.0f}, - 0.1f, 10.0f) * - Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted(); + 0.1f, 10.0f), + Mn::Matrix4::translation(Mn::Vector3::zAxis(1.0f)).inverted()); renderer.addNodeHierarchy(0, "square"); renderer.transformations(0)[0] = Mn::Matrix4::scaling(Mn::Vector3{0.8f}); renderer.draw();