From fe5b2b6fb1387544023afc1ec8c9b0fe26ca6e7e Mon Sep 17 00:00:00 2001 From: Simon Tippe Date: Sun, 10 Dec 2023 23:28:10 +0100 Subject: [PATCH 1/3] Enabling better access to mesh data (#37) * Made vertex/index buffer host accessible * Improved mesh data component * Bunch of improvements and fixes * More fixes * Make graphics device thread safe for resource creation/deletion * Reduce driver overhead with multi vertex buffer binding * Further fixes * Replace header guards with pragma once * Updated model loader to make use of updated data component * Fixed inconsistencies in color spaces * New under water shader * Fixed a few issues * Fixed last remaining color conversion issues * Fixed another bug * Ocean improvements * Experimenting with ocean normals * Fixed many more ocean bugs * Underwater shader now with little artifacts * Improved hot shader reloading * Fixed a number of things * Fixed a few Vulkan library issues concerning CMake * Attempt to fix build pipeline issues * Try debug build pipeline issue * Another attempt at fixing pipeline issues * Updated workflows * Update build.yml * Update build.yml * Update build.yml * Update build.yml * More fixes * Quick fix * Should fix remaining issues * Better fog + applies to all parts of the scene * Fix ocean vertex shader issue --- .github/workflows/build.yml | 51 ++-- .github/workflows/release.yml | 14 +- CMakeLists.txt | 8 +- README.md | 2 +- data/shader/atmosphere.csh | 10 +- data/shader/bilateralBlur.csh | 18 +- data/shader/clouds/clouds.hsh | 5 +- data/shader/clouds/integrate.hsh | 9 +- data/shader/clouds/shadow.csh | 3 +- data/shader/common/normalreconstruction.hsh | 17 +- data/shader/common/stencil.hsh | 26 +- data/shader/deferred/geometry.fsh | 33 --- data/shader/deferred/geometry.vsh | 4 - data/shader/globals.hsh | 2 + data/shader/normalreconstruction.csh | 69 +++++ data/shader/ocean/butterfly.csh | 20 +- data/shader/ocean/caustics.csh | 2 +- data/shader/ocean/common.hsh | 123 +++++++++ data/shader/ocean/depth.fsh | 27 ++ data/shader/ocean/depth.vsh | 58 ++++ data/shader/ocean/h0.csh | 14 +- data/shader/ocean/ht.csh | 17 +- data/shader/ocean/inversion.csh | 8 +- data/shader/ocean/normal.csh | 44 +-- data/shader/ocean/ocean.fsh | 117 ++++---- data/shader/ocean/ocean.vsh | 69 +---- data/shader/ocean/sharedUniforms.hsh | 18 +- data/shader/ocean/shoreInteraction.hsh | 2 + data/shader/ocean/underwater.csh | 106 ++++++++ data/shader/taa.csh | 2 +- data/shader/terrain/terrain.tcsh | 4 +- data/shader/volumetric/fog.hsh | 8 + data/shader/volumetric/volumetric.csh | 119 ++++++-- data/shader/volumetric/volumetric.hsh | 57 ++++ data/shader/volumetric/volumetricResolve.csh | 83 ++---- libs/ImguiExtension/ImguiWrapper.h | 5 +- src/demo/App.cpp | 54 ++-- src/demo/App.h | 7 +- src/demo/CMakeLists.txt | 4 +- src/engine/App.h | 7 +- src/engine/CMakeLists.txt | 8 +- src/engine/Camera.h | 7 +- src/engine/Clock.h | 7 +- src/engine/Decal.h | 7 +- src/engine/Display.h | 7 +- src/engine/Engine.cpp | 2 +- src/engine/Engine.h | 7 +- src/engine/EngineInstance.h | 8 +- src/engine/Filter.h | 7 +- src/engine/Font.h | 7 +- src/engine/Log.h | 7 +- src/engine/Material.h | 11 +- src/engine/RenderList.cpp | 4 +- src/engine/RenderList.h | 7 +- src/engine/RenderTarget.cpp | 37 +++ src/engine/RenderTarget.h | 14 +- src/engine/System.h | 7 +- src/engine/Viewport.h | 7 +- src/engine/Window.h | 7 +- src/engine/actor/Actor.h | 8 +- src/engine/actor/AudioActor.h | 7 +- src/engine/actor/DecalActor.h | 7 +- src/engine/actor/MeshActor.h | 7 +- src/engine/actor/MovableMeshActor.h | 7 +- src/engine/actor/StaticMeshActor.h | 7 +- src/engine/actor/VegetationActor.h | 8 +- src/engine/audio/AudioData.h | 7 +- src/engine/audio/AudioManager.h | 8 +- src/engine/audio/AudioStream.h | 7 +- src/engine/buffer/Buffer.h | 7 +- src/engine/buffer/IndexBuffer.cpp | 5 +- src/engine/buffer/IndexBuffer.h | 12 +- src/engine/buffer/UniformBuffer.cpp | 8 +- src/engine/buffer/UniformBuffer.h | 16 +- src/engine/buffer/VertexArray.cpp | 18 +- src/engine/buffer/VertexArray.h | 7 +- src/engine/buffer/VertexBuffer.cpp | 6 +- src/engine/buffer/VertexBuffer.h | 12 +- src/engine/common/ColorConverter.h | 57 ++++ src/engine/common/Hash.h | 11 +- src/engine/common/Image.h | 7 +- src/engine/common/MathHelper.h | 2 + src/engine/common/MatrixDecomposition.h | 9 +- src/engine/common/NoiseGenerator.h | 7 +- src/engine/common/Packing.h | 7 +- src/engine/common/Path.h | 7 +- src/engine/common/Piecewise.h | 8 +- src/engine/common/RandomHelper.h | 7 +- src/engine/common/Ref.h | 7 +- src/engine/ecs/Entity.h | 7 +- src/engine/ecs/EntityManager.h | 7 +- src/engine/ecs/Pool.h | 7 +- src/engine/ecs/Pools.h | 7 +- src/engine/ecs/Storage.h | 7 +- src/engine/ecs/Subset.h | 7 +- src/engine/ecs/TypeIndex.h | 7 +- src/engine/events/AudioDeviceEvent.h | 7 +- src/engine/events/ControllerAxisEvent.h | 8 +- src/engine/events/ControllerButtonEvent.h | 7 +- src/engine/events/ControllerDeviceEvent.h | 8 +- src/engine/events/DropEvent.h | 8 +- src/engine/events/EventDelegate.h | 7 +- src/engine/events/EventManager.h | 7 +- src/engine/events/FrameEvent.h | 7 +- src/engine/events/KeyboardEvent.h | 8 +- src/engine/events/Keycodes.h | 8 +- src/engine/events/MouseButtonEvent.h | 7 +- src/engine/events/MouseMotionEvent.h | 7 +- src/engine/events/MouseWheelEvent.h | 7 +- src/engine/events/TextInputEvent.h | 7 +- src/engine/events/TouchEvent.h | 7 +- src/engine/events/WindowEvent.h | 7 +- src/engine/graphics/ASBuilder.h | 7 +- src/engine/graphics/BLAS.h | 7 +- src/engine/graphics/Barrier.h | 7 +- src/engine/graphics/Buffer.h | 7 +- src/engine/graphics/CommandList.cpp | 24 ++ src/engine/graphics/CommandList.h | 9 +- src/engine/graphics/Common.h | 7 +- src/engine/graphics/Descriptor.h | 7 +- src/engine/graphics/Extensions.h | 7 +- src/engine/graphics/Format.h | 7 +- src/engine/graphics/Framebuffer.h | 8 +- src/engine/graphics/GraphicsDevice.cpp | 194 ++++--------- src/engine/graphics/GraphicsDevice.h | 53 ++-- src/engine/graphics/Image.h | 7 +- src/engine/graphics/Initializers.h | 7 +- src/engine/graphics/Instance.h | 8 +- src/engine/graphics/MemoryManager.h | 7 +- src/engine/graphics/MemoryTransferManager.h | 7 +- src/engine/graphics/Pipeline.h | 7 +- src/engine/graphics/Profiler.h | 7 +- src/engine/graphics/QueryPool.h | 7 +- src/engine/graphics/Queue.h | 7 +- src/engine/graphics/RenderPass.h | 7 +- src/engine/graphics/Sampler.h | 7 +- src/engine/graphics/Shader.cpp | 38 ++- src/engine/graphics/Shader.h | 10 +- src/engine/graphics/ShaderCompiler.h | 7 +- src/engine/graphics/StructureChainBuilder.h | 7 +- src/engine/graphics/Surface.h | 7 +- src/engine/graphics/SwapChain.h | 10 +- src/engine/graphics/TLAS.h | 7 +- src/engine/input/Controller.h | 7 +- src/engine/input/Keyboard.h | 7 +- src/engine/input/Mouse.h | 7 +- src/engine/input/Touch.h | 7 +- src/engine/lighting/AO.h | 8 +- src/engine/lighting/Atmosphere.h | 8 +- src/engine/lighting/DirectionalLight.h | 7 +- src/engine/lighting/EnvironmentProbe.h | 7 +- src/engine/lighting/Fog.h | 15 +- src/engine/lighting/IrradianceVolume.h | 7 +- src/engine/lighting/Light.h | 8 +- src/engine/lighting/PointLight.h | 7 +- src/engine/lighting/Reflection.h | 8 +- src/engine/lighting/SSS.h | 7 +- src/engine/lighting/Shadow.cpp | 17 ++ src/engine/lighting/Shadow.h | 10 +- src/engine/lighting/Sky.h | 12 +- src/engine/lighting/Volumetric.h | 7 +- src/engine/lighting/VolumetricClouds.h | 15 +- src/engine/loader/AssetLoader.h | 7 +- src/engine/loader/ImageLoader.h | 7 +- src/engine/loader/MaterialLoader.h | 8 +- src/engine/loader/ModelLoader.cpp | 139 +++++----- src/engine/loader/ModelLoader.h | 11 +- src/engine/loader/ShaderLoader.cpp | 5 +- src/engine/loader/ShaderLoader.h | 10 +- src/engine/loader/TerrainLoader.h | 7 +- src/engine/mesh/DataComponent.h | 119 +++++++- src/engine/mesh/Impostor.h | 7 +- src/engine/mesh/Mesh.cpp | 56 ++-- src/engine/mesh/Mesh.h | 39 ++- src/engine/mesh/MeshData.cpp | 6 +- src/engine/mesh/MeshData.h | 11 +- src/engine/ocean/Ocean.h | 21 +- src/engine/ocean/OceanNode.h | 7 +- src/engine/ocean/OceanSimulation.cpp | 45 ++-- src/engine/ocean/OceanSimulation.h | 51 ++-- src/engine/ocean/OceanState.h | 8 +- src/engine/pipeline/PipelineConfig.cpp | 5 + src/engine/pipeline/PipelineConfig.h | 12 +- src/engine/pipeline/PipelineManager.cpp | 17 +- src/engine/pipeline/PipelineManager.h | 7 +- .../postprocessing/ChromaticAberration.h | 8 +- src/engine/postprocessing/FilmGrain.h | 8 +- src/engine/postprocessing/PostProcessing.h | 7 +- src/engine/postprocessing/Sharpen.h | 7 +- src/engine/postprocessing/TAA.h | 7 +- src/engine/postprocessing/Vignette.h | 7 +- src/engine/renderer/AORenderer.cpp | 2 +- src/engine/renderer/AORenderer.h | 7 +- src/engine/renderer/AtmosphereRenderer.h | 7 +- src/engine/renderer/BlurRenderer.h | 7 +- src/engine/renderer/DDGIRenderer.h | 7 +- src/engine/renderer/DecalRenderer.h | 7 +- src/engine/renderer/DirectLightRenderer.cpp | 2 +- src/engine/renderer/DirectLightRenderer.h | 7 +- src/engine/renderer/ExampleRenderer.cpp | 2 +- src/engine/renderer/ExampleRenderer.h | 7 +- .../renderer/GBufferDownscaleRenderer.h | 7 +- src/engine/renderer/ImpostorRenderer.h | 7 +- src/engine/renderer/ImpostorShadowRenderer.h | 7 +- src/engine/renderer/IndirectLightRenderer.h | 7 +- src/engine/renderer/MainRenderer.cpp | 33 ++- src/engine/renderer/MainRenderer.h | 9 +- src/engine/renderer/OceanRenderer.cpp | 254 ++++++++++++++++-- src/engine/renderer/OceanRenderer.h | 75 ++---- src/engine/renderer/OpaqueRenderer.cpp | 2 +- src/engine/renderer/OpaqueRenderer.h | 7 +- src/engine/renderer/PathTracingRenderer.h | 7 +- src/engine/renderer/PointLightRenderer.h | 7 +- src/engine/renderer/PostProcessRenderer.cpp | 2 +- src/engine/renderer/PostProcessRenderer.h | 7 +- src/engine/renderer/RTReflectionRenderer.h | 7 +- src/engine/renderer/RenderBatch.h | 7 +- src/engine/renderer/Renderer.h | 9 +- src/engine/renderer/SSSRenderer.h | 8 +- src/engine/renderer/ShadowRenderer.cpp | 48 ++-- src/engine/renderer/ShadowRenderer.h | 7 +- src/engine/renderer/SkyboxRenderer.h | 7 +- src/engine/renderer/TemporalAARenderer.h | 7 +- src/engine/renderer/TerrainRenderer.h | 7 +- src/engine/renderer/TerrainShadowRenderer.h | 7 +- src/engine/renderer/TextRenderer.h | 7 +- src/engine/renderer/TextureRenderer.h | 7 +- src/engine/renderer/VegetationRenderer.cpp | 2 +- src/engine/renderer/VegetationRenderer.h | 7 +- .../renderer/VolumetricCloudRenderer.cpp | 10 +- src/engine/renderer/VolumetricCloudRenderer.h | 11 +- src/engine/renderer/VolumetricRenderer.cpp | 37 ++- src/engine/renderer/VolumetricRenderer.h | 19 +- src/engine/renderer/helper/CommonStructures.h | 15 +- src/engine/renderer/helper/GeometryHelper.h | 7 +- src/engine/renderer/helper/HaltonSequence.h | 7 +- .../renderer/helper/RayTracingHelper.cpp | 3 +- src/engine/renderer/helper/RayTracingHelper.h | 7 +- src/engine/renderer/helper/VegetationHelper.h | 7 +- src/engine/resource/Resource.h | 9 +- src/engine/resource/ResourceManager.h | 61 +++-- src/engine/scene/RTData.cpp | 83 +++--- src/engine/scene/RTData.h | 7 +- src/engine/scene/RTStructures.h | 7 +- src/engine/scene/Scene.cpp | 2 +- src/engine/scene/Scene.h | 7 +- src/engine/scene/SceneNode.h | 7 +- src/engine/scene/SpacePartitioning.h | 8 +- src/engine/scene/Vegetation.h | 7 +- src/engine/terrain/Terrain.h | 7 +- src/engine/terrain/TerrainNode.h | 7 +- src/engine/terrain/TerrainStorage.h | 7 +- src/engine/terrain/TerrainStorageCell.h | 7 +- src/engine/texture/Cubemap.h | 7 +- src/engine/texture/Texture.h | 7 +- src/engine/texture/Texture2D.h | 7 +- src/engine/texture/Texture2DArray.h | 7 +- src/engine/texture/Texture3D.h | 7 +- src/engine/texture/TextureAtlas.h | 7 +- src/engine/tools/ImpostorTool.cpp | 2 +- src/engine/tools/ImpostorTool.h | 7 +- src/engine/tools/PerformanceCounter.h | 7 +- src/engine/tools/RayCasting.h | 7 +- src/engine/tools/TerrainTool.h | 7 +- vcpkg.json | 5 +- 265 files changed, 2342 insertions(+), 1908 deletions(-) create mode 100644 data/shader/normalreconstruction.csh create mode 100644 data/shader/ocean/common.hsh create mode 100644 data/shader/ocean/depth.fsh create mode 100644 data/shader/ocean/depth.vsh create mode 100644 data/shader/ocean/underwater.csh create mode 100644 data/shader/volumetric/volumetric.hsh create mode 100644 src/engine/common/ColorConverter.h diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3e9cb91e9..f4a78e7a7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,7 @@ name: Build pipeline on: + workflow_dispatch: push: branches: - '*' @@ -11,6 +12,7 @@ env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE_DEBUG: Debug BUILD_TYPE_RELEASE: Release + USE_CACHE: true jobs: windows-build: @@ -30,7 +32,16 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 93895b28ea7bc8cda10f156c5d336f3fc070f8b1 + committish: 8568364fbeb414db3fa5b5bfc436e57b4368c55f + cache: ${{ env.USE_CACHE }} + + # This doesn't work when the Visual Studio C++ CLI was set up first (maybe needs a setup with 2019 version) + - name: Prepare Vulkan SDK + uses: humbletim/setup-vulkan-sdk@v1.2.0 + with: + vulkan-query-version: 1.3.204.0 + vulkan-components: Vulkan-Headers, Vulkan-Loader + vulkan-use-cache: false - name: Setup Microsoft Visual C++ CLI uses: ilammy/msvc-dev-cmd@v1 @@ -41,17 +52,11 @@ jobs: # ninja version to download. Default: 1.10.0 version: 1.10.0 - - name: Prepare Vulkan SDK - uses: humbletim/setup-vulkan-sdk@v1.2.0 - with: - vulkan-query-version: 1.3.204.0 - vulkan-components: Vulkan-Headers - vulkan-use-cache: true - - name: Run scripts shell: pwsh # Add additional scripting steps here run: | + Get-ChildItem -Recurse D:/a/Atlas-Engine/Atlas-Engine/VULKAN_SDK cd ${{ github.workspace }} ${{ github.workspace }}/vcpkg/vcpkg install --clean-after-build --triplet=x64-windows Remove-Item –path vcpkg_installed –recurse @@ -83,7 +88,7 @@ jobs: !**/CMakeFiles linux-build: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 name: Build on Linux # Run both builds in parallel and don't cancel if one fails strategy: @@ -99,7 +104,8 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 93895b28ea7bc8cda10f156c5d336f3fc070f8b1 + committish: 8568364fbeb414db3fa5b5bfc436e57b4368c55f + cache: ${{ env.USE_CACHE }} - name: Setup Ninja uses: ashutoshvarma/setup-ninja@master @@ -111,8 +117,8 @@ jobs: uses: humbletim/setup-vulkan-sdk@v1.2.0 with: vulkan-query-version: 1.3.204.0 - vulkan-components: Vulkan-Headers - vulkan-use-cache: true + vulkan-components: Vulkan-Headers, Vulkan-Loader + vulkan-use-cache: false - name: Run scripts shell: bash @@ -168,7 +174,8 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 93895b28ea7bc8cda10f156c5d336f3fc070f8b1 + committish: 8568364fbeb414db3fa5b5bfc436e57b4368c55f + cache: ${{ env.USE_CACHE }} - name: Setup Ninja uses: ashutoshvarma/setup-ninja@master @@ -180,8 +187,8 @@ jobs: uses: humbletim/setup-vulkan-sdk@v1.2.0 with: vulkan-query-version: 1.3.204.0 - vulkan-components: Vulkan-Headers - vulkan-use-cache: true + vulkan-components: Vulkan-Headers, Vulkan-Loader + vulkan-use-cache: false - name: Run scripts shell: bash @@ -204,3 +211,17 @@ jobs: -DATLAS_DEMO=ON -G Ninja parallel: 16 build-type: ${{ matrix.build-type }} + + - name: Upload artifact + if: ${{ matrix.build-type == 'Release' }} + uses: actions/upload-artifact@v2 + with: + name: Atlas Engine Demo MacOS ${{ matrix.build-type }} + path: | + data + README.md + LICENSE.md + THIRDPARTY.md + ${{ github.workspace }}/bin/**/AtlasEngineDemo + ${{ github.workspace }}/bin/**/*.dylib* + !**/CMakeFiles diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f0f82aee7..6e1930147 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 93895b28ea7bc8cda10f156c5d336f3fc070f8b1 + committish: 8568364fbeb414db3fa5b5bfc436e57b4368c55f - name: Setup Microsoft Visual C++ CLI uses: ilammy/msvc-dev-cmd@v1 @@ -46,8 +46,8 @@ jobs: uses: humbletim/setup-vulkan-sdk@v1.2.0 with: vulkan-query-version: 1.3.204.0 - vulkan-components: Vulkan-Headers - vulkan-use-cache: true + vulkan-components: Vulkan-Headers, Vulkan-Loader + vulkan-use-cache: false - name: Run scripts shell: pwsh @@ -83,7 +83,7 @@ jobs: !**/CMakeFiles linux-build: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 name: Build on Linux # Run both builds in parallel and don't cancel if one fails strategy: @@ -99,7 +99,7 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 93895b28ea7bc8cda10f156c5d336f3fc070f8b1 + committish: 8568364fbeb414db3fa5b5bfc436e57b4368c55f - name: Setup Ninja uses: ashutoshvarma/setup-ninja@master @@ -111,8 +111,8 @@ jobs: uses: humbletim/setup-vulkan-sdk@v1.2.0 with: vulkan-query-version: 1.3.204.0 - vulkan-components: Vulkan-Headers - vulkan-use-cache: true + vulkan-components: Vulkan-Headers, Vulkan-Loader + vulkan-use-cache: false - name: Run scripts shell: bash diff --git a/CMakeLists.txt b/CMakeLists.txt index b3eab1469..04c3afb9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,8 +10,9 @@ if(COMMAND cmake_policy) cmake_policy(SET CMP0042 NEW) endif(COMMAND cmake_policy) -project(AtlasEngine VERSION 0.1.8) -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.24) + +project(AtlasEngine VERSION 0.1.10) # Only 64 bit is supported ################################################################### @@ -64,9 +65,10 @@ if (ANDROID) add_subdirectory(${HIDAPI_LOCATION}) endif() +find_package(Vulkan REQUIRED) find_package(Threads REQUIRED) find_package(volk CONFIG REQUIRED) -find_package(unofficial-vulkan-memory-allocator CONFIG REQUIRED) +find_package(VulkanMemoryAllocator CONFIG REQUIRED) find_package(glslang CONFIG REQUIRED) find_package(unofficial-spirv-reflect CONFIG REQUIRED) find_package(SPIRV-Tools-opt CONFIG REQUIRED) diff --git a/README.md b/README.md index 448bb7a8c..e9bc60d3a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [code-quality-image]: https://app.codacy.com/project/badge/Grade/0b8608dc5cb349a38b8d64c7fbcbcda6 [code-quality-url]: https://app.codacy.com/gh/tippesi/Atlas-Engine/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade -![GI scene](wiki/images/intel_sponza.gif)
*Realtime Sponza scene with software raytraced GI, AO and reflections (model from [Intel Graphics Research Sample Library](https://www.intel.com/content/www/us/en/developer/topic-technology/graphics-research/samples.html))*
+![GI scene](wiki/images/intel_sponza.gif)
*Realtime Sponza scene with raytraced GI, AO and reflections (model from [Intel Graphics Research Sample Library](https://www.intel.com/content/www/us/en/developer/topic-technology/graphics-research/samples.html))*
## Introduction This is a cross platform toy engine developed in my spare time that is available on Linux, Windows and MacOS. ## Requirements diff --git a/data/shader/atmosphere.csh b/data/shader/atmosphere.csh index a9358a25b..dc2c4d93d 100644 --- a/data/shader/atmosphere.csh +++ b/data/shader/atmosphere.csh @@ -37,8 +37,6 @@ vec2 resolution = vec2(imageSize(colorImage)); const float rayScaleHeight = 8.0e3; const float mieScaleHeight = 1.2e3; -vec3 planetCenter = -vec3(0.0, uniforms.planetRadius, 0.0); - void atmosphere(vec3 r, vec3 r0, vec3 pSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, out vec3 totalRlh, out vec3 totalMie); @@ -148,8 +146,8 @@ vec2 IntersectSphere(vec3 origin, vec3 direction, vec3 pos, float radius) { void CalculateRayLength(vec3 rayOrigin, vec3 rayDirection, out float minDist, out float maxDist) { - vec2 planetDist = IntersectSphere(rayOrigin, rayDirection, planetCenter, uniforms.planetRadius); - vec2 atmosDist = IntersectSphere(rayOrigin, rayDirection, planetCenter, uniforms.atmosphereRadius); + vec2 planetDist = IntersectSphere(rayOrigin, rayDirection, uniforms.planetCenter.xyz, uniforms.planetRadius); + vec2 atmosDist = IntersectSphere(rayOrigin, rayDirection, uniforms.planetCenter.xyz, uniforms.atmosphereRadius); // We're in the in the planet if (planetDist.x < 0.0 && planetDist.y >= 0.0) { @@ -194,7 +192,7 @@ out float opticalDepthRay) { vec3 pos = origin + sunDirection * (time + 0.5 * stepSize); - float height = distance(planetCenter, pos) - planetRadius; + float height = distance(uniforms.planetCenter.xyz, pos) - planetRadius; if (height < 0.0) return false; @@ -241,7 +239,7 @@ void atmosphere(vec3 r, vec3 r0, vec3 pSun, float rPlanet, float rAtmos, vec3 kR vec3 iPos = r0 + r * (iTime + iStepSize * 0.5); // Calculate the height of the sample. - float iHeight = distance(iPos, planetCenter) - rPlanet; + float iHeight = distance(iPos, uniforms.planetCenter.xyz) - rPlanet; // Calculate the optical depth of the Rayleigh and Mie scattering for this step. float odStepRlh = exp(-iHeight / rayScaleHeight) * iStepSize; diff --git a/data/shader/bilateralBlur.csh b/data/shader/bilateralBlur.csh index ad054ca55..dc3c241c1 100644 --- a/data/shader/bilateralBlur.csh +++ b/data/shader/bilateralBlur.csh @@ -9,7 +9,7 @@ layout (local_size_x = 256, local_size_y = 1) in; layout (local_size_x = 1, local_size_y = 256) in; #endif -#ifdef BLUR_RGB +#if defined(BLUR_RGB) || defined(BLUR_RGBA) layout(set = 3, binding = 0, rgba16f) writeonly uniform image2D outputImage; #else layout(set = 3, binding = 0, r16f) writeonly uniform image2D outputImage; @@ -33,8 +33,10 @@ layout(set = 3, binding = 4, std140) uniform WeightBuffer { const float normalPhi = 32.0; const float depthPhi = 0.5; -#ifdef BLUR_RGB +#if defined(BLUR_RGB) shared vec3 inputs[320]; +#elif defined(BLUR_RGBA) +shared vec4 inputs[320]; #else shared float inputs[320]; #endif @@ -67,8 +69,10 @@ void LoadGroupSharedData() { #else localOffset.y += int(i); #endif -#ifdef BLUR_RGB +#if defined(BLUR_RGB) vec3 localInput = texelFetch(inputTexture, localOffset, 0).rgb; +#elif defined(BLUR_RGBA) + vec4 localInput = texelFetch(inputTexture, localOffset, 0); #else float localInput = texelFetch(inputTexture, localOffset, 0).r; #endif @@ -101,8 +105,10 @@ void main() { // E.g. this is the x component for horizontal blurring sharedDataOffset += max3(ivec3(gl_LocalInvocationID)); -#ifdef BLUR_RGB +#if defined(BLUR_RGB) vec3 center, result; +#elif defined(BLUR_RGBA) + vec4 center, result; #else float center, result; #endif @@ -161,8 +167,10 @@ void main() { ivec2 pixel = offset + ivec2(gl_LocalInvocationID); -#ifdef BLUR_RGB +#if defined(BLUR_RGB) imageStore(outputImage, pixel, vec4(result / totalWeight, 0.0)); +#elif defined(BLUR_RGBA) + imageStore(outputImage, pixel, vec4(result / totalWeight)); #else imageStore(outputImage, pixel, vec4(result / totalWeight)); #endif diff --git a/data/shader/clouds/clouds.hsh b/data/shader/clouds/clouds.hsh index 06bd5e9db..2b2046eaf 100644 --- a/data/shader/clouds/clouds.hsh +++ b/data/shader/clouds/clouds.hsh @@ -39,12 +39,13 @@ layout(std140, set = 3, binding = 7) uniform CloudUniformBuffer { uint frameSeed; int sampleCount; - int shadowSampleCount; + int occlusionSampleCount; float darkEdgeFocus; float darkEdgeAmbient; vec4 extinctionCoefficients; + vec4 planetCenter; } cloudUniforms; const float multiscatterAttenuation = 0.5; @@ -58,7 +59,7 @@ const vec3 windDirectionDetail = normalize(vec3(0.1, -0.0, 0.1)); const vec2 windDirectionCoverage = vec2(0.4, 0.4); const float windSpeed = 0.001; -vec3 cloudPlanetCenter = -vec3(0.0, cloudUniforms.planetRadius, 0.0); +vec3 cloudPlanetCenter = cloudUniforms.planetCenter.xyz; #define MULTISCATTER_OCTAVES 2 diff --git a/data/shader/clouds/integrate.hsh b/data/shader/clouds/integrate.hsh index abfa0564c..f95205bd0 100644 --- a/data/shader/clouds/integrate.hsh +++ b/data/shader/clouds/integrate.hsh @@ -63,7 +63,7 @@ void CalculateRayLength(vec3 rayOrigin, vec3 rayDirection, out float minDist, ou vec4 GetExtinctionToLight(vec3 pos, int ditherIdx, vec4 noiseVector) { - const int lightSampleCount = cloudUniforms.shadowSampleCount; + const int lightSampleCount = cloudUniforms.occlusionSampleCount; vec3 rayDirection = -normalize(cloudUniforms.light.direction.xyz); @@ -72,9 +72,12 @@ vec4 GetExtinctionToLight(vec3 pos, int ditherIdx, vec4 noiseVector) { float rayLength = (outDist - inDist) * 0.5; +#ifdef STOCHASTIC_OCCLUSION_SAMPLING // Dither secondary rays float noiseOffset = noiseVector[ditherIdx % 4]; - noiseOffset = 0.5; +#else + float noiseOffset = 0.5; +#endif vec4 extinctionAccumulation = vec4(0.0); for (int i = 0; i < lightSampleCount; i++) { @@ -184,7 +187,7 @@ vec4 IntegrateVolumetricClouds(vec3 rayOrigin, vec3 rayDirection, float rayStart float lightDotView = dot(normalize(cloudUniforms.light.direction.xyz), normalize(rayDirection)); vec3 lightColor = cloudUniforms.light.color.rgb * cloudUniforms.light.intensity; - vec4 lightExtinction = GetExtinctionToLight(rayPos, noiseIdx++, noiseVector); + vec4 lightExtinction = GetExtinctionToLight(rayPos, 1, noiseVector); float phaseFunction = DualPhaseFunction(cloudUniforms.eccentricityFirstPhase, cloudUniforms.eccentricitySecondPhase, cloudUniforms.phaseAlpha, lightDotView); diff --git a/data/shader/clouds/shadow.csh b/data/shader/clouds/shadow.csh index 3ad4b4af1..cab4b75da 100644 --- a/data/shader/clouds/shadow.csh +++ b/data/shader/clouds/shadow.csh @@ -19,6 +19,7 @@ layout(std140, set = 3, binding = 8) uniform UniformBuffer { mat4 ipMatrix; vec4 lightDirection; + int shadowSampleFraction; } uniforms; vec2 ComputeVolumetricClouds(vec3 minDepthPos, vec3 maxDepthPos); @@ -51,7 +52,7 @@ vec2 ComputeVolumetricClouds(vec3 minDepthPos, vec3 maxDepthPos) { float rayLength = outDist - inDist; float rayStart = inDist; - int raySampleCount = cloudUniforms.sampleCount / 4; + int raySampleCount = cloudUniforms.sampleCount / uniforms.shadowSampleFraction; float stepLength = rayLength / float(raySampleCount); vec3 stepVector = rayDirection * stepLength; diff --git a/data/shader/common/normalreconstruction.hsh b/data/shader/common/normalreconstruction.hsh index 72f83a8de..418093e84 100644 --- a/data/shader/common/normalreconstruction.hsh +++ b/data/shader/common/normalreconstruction.hsh @@ -1,18 +1,12 @@ -#include -#include +#include -vec3 reconstructNormal(const in sampler2D depthTexture, vec2 texCoord) { +vec3 ReconstructNormal(ivec2 resolution, float depthCenter, float depthLeft, float depthRight, + float depthTop, float depthBottom, vec2 texCoord) { - vec2 texSize = vec2(textureSize(depthTexture, 0)); + vec2 texSize = vec2(resolution); vec2 invTexSize = 1.0 / texSize; ivec2 pixel = ivec2(texCoord * texSize); - float depthCenter = texelFetch(depthTexture, pixel, 0).r; - float depthLeft = sampleTex(depthTexture, pixel + ivec2(-1, 0), SAMPLE_CLAMP).r; - float depthRight = sampleTex(depthTexture, pixel + ivec2(1, 0), SAMPLE_CLAMP).r; - float depthTop = sampleTex(depthTexture, pixel + ivec2(0, -1), SAMPLE_CLAMP).r; - float depthBottom = sampleTex(depthTexture, pixel + ivec2(0, 1), SAMPLE_CLAMP).r; - vec3 posCenter = ConvertDepthToViewSpace(depthCenter, texCoord); vec3 posLeft = ConvertDepthToViewSpace(depthLeft, texCoord + vec2(-1.0, 0.0) * invTexSize); vec3 posRight = ConvertDepthToViewSpace(depthRight, texCoord + vec2(1.0, 0.0) * invTexSize); @@ -42,7 +36,6 @@ vec3 reconstructNormal(const in sampler2D depthTexture, vec2 texCoord) { } vec3 p0 = posCenter; - - return normalize(cross(p2 - p0, p1 - p0)); + return -normalize(cross(p2 - p0, p1 - p0)); } \ No newline at end of file diff --git a/data/shader/common/stencil.hsh b/data/shader/common/stencil.hsh index 291db6787..2b27900a2 100644 --- a/data/shader/common/stencil.hsh +++ b/data/shader/common/stencil.hsh @@ -1,14 +1,32 @@ struct StencilFeatures { bool responsivePixel; + bool waterPixel; + bool underWaterPixel; }; +StencilFeatures CreateStencilFeatures() { + + StencilFeatures features; + + features.responsivePixel = false; + features.waterPixel = false; + features.underWaterPixel = false; + + return features; + +} + uint EncodeStencilFeatures(StencilFeatures features) { const uint responsivePixel = (1 << 0); + const uint waterPixel = (1 << 1); + const uint underWaterPixel = (1 << 2); - uint data = 0; + uint data = 0u; - data |= features.responsivePixel ? responsivePixel : 0; + data |= features.responsivePixel ? responsivePixel : 0u; + data |= features.waterPixel ? waterPixel : 0u; + data |= features.underWaterPixel ? underWaterPixel : 0u; return data; @@ -17,10 +35,14 @@ uint EncodeStencilFeatures(StencilFeatures features) { StencilFeatures DecodeStencilFeatures(uint data) { const uint responsivePixel = (1 << 0); + const uint waterPixel = (1 << 1); + const uint underWaterPixel = (1 << 2); StencilFeatures features; features.responsivePixel = (data & responsivePixel) > 0u; + features.waterPixel = (data & waterPixel) > 0u; + features.underWaterPixel = (data & underWaterPixel) > 0u; return features; diff --git a/data/shader/deferred/geometry.fsh b/data/shader/deferred/geometry.fsh index c240482f2..be2e091d0 100644 --- a/data/shader/deferred/geometry.fsh +++ b/data/shader/deferred/geometry.fsh @@ -2,11 +2,7 @@ #include <../common/normalencode.hsh> #include <../globals.hsh> -#ifdef GENERATE_IMPOSTOR -layout (location = 0) out vec4 baseColorFS; -#else layout (location = 0) out vec3 baseColorFS; -#endif layout (location = 1) out vec2 normalFS; layout (location = 2) out vec2 geometryNormalFS; layout (location = 3) out vec3 roughnessMetalnessAoFS; @@ -52,13 +48,6 @@ layout(location=5) in vec4 vertexColorsVS; layout(location=6) in mat3 TBN; #endif -#ifdef GENERATE_IMPOSTOR -uniform vec3 baseColor; -uniform float roughness; -uniform float metalness; -uniform float ao; -#endif - layout(push_constant) uniform constants { uint vegetation; uint invertUVs; @@ -127,11 +116,7 @@ void main() { texCoords = ParallaxMapping(texCoords, viewDir); #endif -#ifdef GENERATE_IMPOSTOR - baseColorFS = vec4(1.0); -#else baseColorFS = vec3(1.0); -#endif #if (defined(OPACITY_MAP) || defined(VERTEX_COLORS)) float opacity = 1.0; @@ -147,16 +132,8 @@ void main() { #ifdef BASE_COLOR_MAP vec3 textureColor = texture(baseColorMap, texCoords).rgb; -#ifdef GENERATE_IMPOSTOR - baseColorFS *= vec4(textureColor.rgb, 1.0); -#else baseColorFS *= textureColor.rgb; #endif -#endif - -#ifdef GENERATE_IMPOSTOR - baseColorFS *= vec4(baseColor, 1.0); -#endif #ifdef VERTEX_COLORS baseColorFS *= vertexColorsVS.rgb; @@ -178,15 +155,9 @@ void main() { geometryNormalFS = EncodeNormal(geometryNormal); -#ifdef GENERATE_IMPOSTOR - float roughnessFactor = roughness; - float metalnessFactor = metalness; - float aoFactor = ao; -#else float roughnessFactor = 1.0; float metalnessFactor = 1.0; float aoFactor = 1.0; -#endif #ifdef ROUGHNESS_MAP roughnessFactor *= texture(roughnessMap, texCoords).r; @@ -201,10 +172,6 @@ void main() { roughnessMetalnessAoFS.b = aoFactor; #endif -#ifdef GENERATE_IMPOSTOR - roughnessMetalnessAoFS = vec3(roughnessFactor, - metalnessFactor, aoFactor); -#endif // Calculate velocity vec2 ndcL = ndcLastVS.xy / ndcLastVS.z; vec2 ndcC = ndcCurrentVS.xy / ndcCurrentVS.z; diff --git a/data/shader/deferred/geometry.vsh b/data/shader/deferred/geometry.vsh index dfe483e42..7af08e013 100644 --- a/data/shader/deferred/geometry.vsh +++ b/data/shader/deferred/geometry.vsh @@ -97,10 +97,6 @@ void main() { // Only after ndc calculation apply the clip correction gl_Position = gl_Position; -#ifdef GENERATE_IMPOSTOR - mvMatrix = globalData.mMatrix; -#endif - normalVS = mat3(mvMatrix) * vNormal; #if defined(NORMAL_MAP) || defined(HEIGHT_MAP) diff --git a/data/shader/globals.hsh b/data/shader/globals.hsh index e0c78900e..8385722e0 100644 --- a/data/shader/globals.hsh +++ b/data/shader/globals.hsh @@ -12,6 +12,8 @@ layout(std140, set = 0, binding = 0) uniform GlobalBuffer { vec4 cameraDirection; vec4 cameraUp; vec4 cameraRight; + vec4 planetCenter; + float planetRadius; float time; float deltaTime; uint frameCount; diff --git a/data/shader/normalreconstruction.csh b/data/shader/normalreconstruction.csh new file mode 100644 index 000000000..6726b3412 --- /dev/null +++ b/data/shader/normalreconstruction.csh @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include + +layout (local_size_x = 8, local_size_y = 8) in; + +layout(set = 3, binding = 0, rg16f) writeonly uniform image2D normalImage; + +layout(set = 3, binding = 1) uniform sampler2D depthTexture; + +const uint sharedDataSize = (gl_WorkGroupSize.x + 2) * (gl_WorkGroupSize.y + 2); +const ivec2 unflattenedSharedDataSize = ivec2(gl_WorkGroupSize) + ivec2(2); + +shared float depths[sharedDataSize]; + +void LoadGroupSharedData() { + + ivec2 resolution = imageSize(normalImage); + ivec2 workGroupOffset = ivec2(gl_WorkGroupID) * ivec2(gl_WorkGroupSize) - ivec2(1); + + uint workGroupSize = gl_WorkGroupSize.x * gl_WorkGroupSize.y; + for(uint i = gl_LocalInvocationIndex; i < sharedDataSize; i += workGroupSize) { + ivec2 localOffset = Unflatten2D(int(i), unflattenedSharedDataSize); + ivec2 texel = localOffset + workGroupOffset; + + texel = clamp(texel, ivec2(0), ivec2(resolution) - ivec2(1)); + + depths[i] = texelFetch(depthTexture, texel, 0).r; + } + + barrier(); + +} + +float GetPixelDepth(ivec2 pixelOffset) { + + ivec2 pixel = ivec2(gl_LocalInvocationID) + ivec2(1); + int sharedMemoryIdx = Flatten2D(pixel + pixelOffset, unflattenedSharedDataSize); + return depths[sharedMemoryIdx]; + +} + +void main() { + + LoadGroupSharedData(); + ivec2 pixel = ivec2(gl_GlobalInvocationID); + if (pixel.x > imageSize(normalImage).x || + pixel.y > imageSize(normalImage).y) + return; + + vec2 texCoord = (vec2(pixel) + 0.5) / vec2(imageSize(normalImage)); + + float depthCenter = GetPixelDepth(ivec2(0, 0)); + float depthLeft = GetPixelDepth(ivec2(-1, 0)); + float depthRight = GetPixelDepth(ivec2(1, 0)); + float depthTop = GetPixelDepth(ivec2(0, -1)); + float depthBottom = GetPixelDepth(ivec2(0, 1)); + + ivec2 resolution = ivec2(imageSize(normalImage)); + vec3 normal = ReconstructNormal(resolution, depthCenter, depthLeft, depthRight, + depthTop, depthBottom, texCoord); + + vec2 encoded = EncodeNormal(normal); + imageStore(normalImage, pixel, vec4(encoded, 0.0, 0.0)); + +} \ No newline at end of file diff --git a/data/shader/ocean/butterfly.csh b/data/shader/ocean/butterfly.csh index 14eeb16d2..cabcb3ab4 100644 --- a/data/shader/ocean/butterfly.csh +++ b/data/shader/ocean/butterfly.csh @@ -1,12 +1,12 @@ #include <../common/PI.hsh> #include <../common/complex.hsh> -layout (local_size_x = 8, local_size_y = 8) in; +layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; layout (set = 3, binding = 0, rg32f) readonly uniform image2D twiddleIndicesTexture; -layout (set = 3, binding = 1, rgba32f) readonly uniform image2D pingpong0; -layout (set = 3, binding = 2, rgba32f) writeonly uniform image2D pingpong1; +layout (set = 3, binding = 1, rgba32f) readonly uniform image2DArray pingpong0; +layout (set = 3, binding = 2, rgba32f) writeonly uniform image2DArray pingpong1; layout(push_constant) uniform constants { int stage; @@ -31,6 +31,7 @@ void main() { void horizontal() { + int spectrumIdx = int(gl_GlobalInvocationID.z); ivec2 coord = ivec2(gl_GlobalInvocationID.xy); float k = mod(float(coord.x) * PushConstants.preTwiddle, float(PushConstants.N)); @@ -39,20 +40,21 @@ void horizontal() { vec2 twiddle = imageLoad(twiddleIndicesTexture, ivec2(PushConstants.stage, coord.x)).rg; - vec4 s1 = imageLoad(pingpong0, ivec2(twiddle.x, coord.y)); - vec4 t1 = imageLoad(pingpong0, ivec2(twiddle.y, coord.y)); + vec4 s1 = imageLoad(pingpong0, ivec3(twiddle.x, coord.y, spectrumIdx)); + vec4 t1 = imageLoad(pingpong0, ivec3(twiddle.y, coord.y, spectrumIdx)); vec4 u1 = vec4(twiddleFactor.x, twiddleFactor.y, twiddleFactor.x, twiddleFactor.y); vec4 H1 = s1 + mul(u1, t1); - imageStore(pingpong1, coord, H1); + imageStore(pingpong1, ivec3(gl_GlobalInvocationID.xyz), H1); } void vertical() { + int spectrumIdx = int(gl_GlobalInvocationID.z); ivec2 coord = ivec2(gl_GlobalInvocationID.xy); float k = mod(float(coord.y) * PushConstants.preTwiddle, float(PushConstants.N)); @@ -61,15 +63,15 @@ void vertical() { vec2 twiddle = imageLoad(twiddleIndicesTexture, ivec2(PushConstants.stage, coord.y)).rg; - vec4 s1 = imageLoad(pingpong0, ivec2(coord.x, twiddle.x)); - vec4 t1 = imageLoad(pingpong0, ivec2(coord.x, twiddle.y)); + vec4 s1 = imageLoad(pingpong0, ivec3(coord.x, twiddle.x, spectrumIdx)); + vec4 t1 = imageLoad(pingpong0, ivec3(coord.x, twiddle.y, spectrumIdx)); vec4 u1 = vec4(twiddleFactor.x, twiddleFactor.y, twiddleFactor.x, twiddleFactor.y); vec4 H1 = s1 + mul(u1, t1); - imageStore(pingpong1, coord, H1); + imageStore(pingpong1, ivec3(gl_GlobalInvocationID.xyz), H1); } \ No newline at end of file diff --git a/data/shader/ocean/caustics.csh b/data/shader/ocean/caustics.csh index 84941231a..597eeeca2 100644 --- a/data/shader/ocean/caustics.csh +++ b/data/shader/ocean/caustics.csh @@ -1,6 +1,6 @@ layout (local_size_x = 8, local_size_y = 8) in; -#define SHADOW_FILTER_1x1 +#define SHADOW_FILTER_3x3 #include <../structures> #include <../shadow.hsh> diff --git a/data/shader/ocean/common.hsh b/data/shader/ocean/common.hsh new file mode 100644 index 000000000..9f5924922 --- /dev/null +++ b/data/shader/ocean/common.hsh @@ -0,0 +1,123 @@ +#include +#include + +layout(set = 3, binding = 0) uniform sampler2DArray displacementMap; +layout(set = 3, binding = 1) uniform sampler2DArray normalMap; +layout(set = 3, binding = 13) uniform sampler2D perlinNoiseMap; + +const float shoreStartScaling = 15.0; +const float shoreOffsetScaling = 5.0; +const float minShoreScaling = 0.3; + +const vec4 fadeDistances = vec4(10.0, 20.0, 40.0, 100.0); +vec4 spectrumScaling = Uniforms.spectrumWeights; + +vec3 GetOceanDisplacement(vec3 position, float dist, out float perlinScale, + out float shoreScale, out vec3 normalShoreWave) { + + vec2 texCoord = position.xz / Uniforms.tiling; + +#ifdef TERRAIN + float waterDepth = shoreStartScaling; + + vec2 terrainTex = (position.xz - vec2(Uniforms.terrainTranslation.xz)) + / Uniforms.terrainSideLength; + + if (terrainTex.x >= 0.0 && terrainTex.y >= 0.0 + && terrainTex.x <= 1.0 && terrainTex.y <= 1.0) { + waterDepth = position.y - textureLod(terrainHeight, terrainTex, 0.0).r + * Uniforms.terrainHeightScale + Uniforms.terrainTranslation.y; + } + + shoreScale = clamp((waterDepth - shoreOffsetScaling) / + (shoreStartScaling - shoreOffsetScaling), minShoreScaling, 1.0); +#else + shoreScale = 1.0; +#endif + + vec4 tilingFactors = Uniforms.spectrumTilingFactors; + vec4 fadeoutDistances = Uniforms.spectrumFadeoutDistances; + + vec3 displacement0 = vec3(0.0), displacement1 = vec3(0.0), displacement2 = vec3(0.0), displacement3 = vec3(0.0); + + vec4 fade = saturate((fadeoutDistances - vec4(dist)) / fadeDistances); + if (Uniforms.spectrumCount > 0 && fade[0] > 0.0) { + displacement0 = textureLod(displacementMap, vec3(texCoord / tilingFactors[0], 0.0), 0.0).grb * spectrumScaling.x; + displacement0 *= fade[0]; + } + if (Uniforms.spectrumCount > 1 && fade[1] > 0.0) { + displacement1 = textureLod(displacementMap, vec3(texCoord / tilingFactors[1] + vec2(0.25), 1.0), 0.0).grb * spectrumScaling.y; + displacement1 *= fade[1]; + } + if (Uniforms.spectrumCount > 2 && fade[2] > 0.0) { + displacement2 = textureLod(displacementMap, vec3(texCoord / tilingFactors[2] + vec2(0.5), 2.0), 0.0).grb * spectrumScaling.z; + displacement2 *= fade[2]; + } + if (Uniforms.spectrumCount > 3 && fade[3] > 0.0) { + displacement3 = textureLod(displacementMap, vec3(texCoord / tilingFactors[3] + vec2(0.75), 3.0), 0.0).grb * spectrumScaling.w; + displacement3 *= fade[3]; + } + + float weightSum = dot(vec4(1.0), spectrumScaling); + vec3 displacement = (displacement0 + displacement1 + displacement2 + displacement3) / weightSum; + displacement.y *= Uniforms.displacementScale * shoreScale; + displacement.x *= Uniforms.choppyScale * shoreScale; + displacement.z *= Uniforms.choppyScale * shoreScale; + + float perlin = textureLod(perlinNoiseMap, texCoord * 0.125 * 0.125, 0.0).r; + perlinScale = saturate(sqr(perlin)); + + displacement = displacement; + +#ifdef TERRAIN + vec3 dx = vec3(0.1, 0.0, 0.0) + CalculateGerstner(position + vec3(0.1, 0.0, 0.0)); + vec3 dz = vec3(0.0, 0.0, 0.1) + CalculateGerstner(position + vec3(0.0, 0.0, 0.1)); + vec3 centerOffset = CalculateGerstner(position); + + normalShoreWave = normalize(cross(dz - centerOffset, dx - centerOffset)); + displacement += centerOffset; +#endif + + return displacement; + +} + +void GetOceanGradientAndFold(vec2 texCoord, float dist, out float fold, out vec2 gradient) { + + texCoord /= Uniforms.tiling; + + vec4 tilingFactors = Uniforms.spectrumTilingFactors; + vec4 fadeoutDistances = Uniforms.spectrumFadeoutDistances; + + vec4 data0 = vec4(0.0), data1 = vec4(0.0), data2 = vec4(0.0), data3 = vec4(0.0); + + vec4 fade = saturate((fadeoutDistances - vec4(dist)) / fadeDistances); + if (Uniforms.spectrumCount > 0 && fade[0] > 0.0) { + data0 = texture(normalMap, vec3(texCoord / tilingFactors[0], 0.0)) * spectrumScaling.x; + data0 *= fade[0]; + } + if (Uniforms.spectrumCount > 1 && fade[1] > 0.0) { + data1 = texture(normalMap, vec3(texCoord / tilingFactors[1] + vec2(0.25), 1.0)) * spectrumScaling.y; + data1 *= fade[1]; + } + if (Uniforms.spectrumCount > 2 && fade[2] > 0.0) { + data2 = texture(normalMap, vec3(texCoord / tilingFactors[2] + vec2(0.5), 2.0)) * spectrumScaling.z; + data2 *= fade[2]; + } + if (Uniforms.spectrumCount > 3 && fade[3] > 0.0) { + data3 = texture(normalMap, vec3(texCoord / tilingFactors[3] + vec2(0.75), 3.0)) * spectrumScaling.w; + data3 *= fade[3]; + } + + float perlin = textureLod(perlinNoiseMap, texCoord * 0.125 * 0.125, 0.0).r; + float perlinScale = sqr(clamp(perlin, 0.0, 1.0)); + + float weightSum = dot(vec4(1.0), spectrumScaling); + fold = saturate(10.0 * ((data0.a + data1.a + data2.a + data3.a) / weightSum)); + gradient = ((data0.rg / tilingFactors[0]) + (data1.rg / tilingFactors[1]) + + (data2.rg / tilingFactors[2]) + (data3.rg / tilingFactors[3])) / weightSum; + + //fold *= perlinScale; + //gradient *= perlinScale; + +} \ No newline at end of file diff --git a/data/shader/ocean/depth.fsh b/data/shader/ocean/depth.fsh new file mode 100644 index 000000000..2ffd4a515 --- /dev/null +++ b/data/shader/ocean/depth.fsh @@ -0,0 +1,27 @@ +#include <../common/normalencode.hsh> +#include <../common/stencil.hsh> +#include <../globals.hsh> + +void main() { + + /* + StencilFeatures features = CreateStencilFeatures(); + + vec3 viewDir = normalize(fPosition - globalData.cameraLocation.xyz); + bool frontFacing = dot(normalize(fNormal), viewDir) < 0.0 ? true : false; + + if (!gl_FrontFacing && !frontFacing) { + features.underWaterPixel = true; + } + else { + features.underWaterPixel = false; + } + + features.waterPixel = true; + + // Remember that the ocean stencil has only 8 bits, need to be careful about the features + // and their bit order. + stencil = EncodeStencilFeatures(features); + */ + +} \ No newline at end of file diff --git a/data/shader/ocean/depth.vsh b/data/shader/ocean/depth.vsh new file mode 100644 index 000000000..180bbaf95 --- /dev/null +++ b/data/shader/ocean/depth.vsh @@ -0,0 +1,58 @@ +#include <../common/utility.hsh> +#include <../common/PI.hsh> + +#include +#include +#include + +layout(location=0) in vec3 vPosition; + +layout(location=1) out vec3 fPosition; + +vec3 stitch(vec3 position) { + + // Note: This only works because our grid has a constant size + // which cannot be changed. + position.xz *= 128.0; + + if (position.x == 0.0 && PushConstants.leftLoD > 1.0) { + position.z = floor(position.z / PushConstants.leftLoD) + * PushConstants.leftLoD; + } + else if (position.z == 0.0 && PushConstants.topLoD > 1.0) { + position.x = floor(position.x / PushConstants.topLoD) + * PushConstants.topLoD; + } + else if (position.x == 128.0 && PushConstants.rightLoD > 1.0) { + position.z = floor(position.z / PushConstants.rightLoD) + * PushConstants.rightLoD; + } + else if (position.z == 128.0 && PushConstants.bottomLoD > 1.0) { + position.x = floor(position.x / PushConstants.bottomLoD) + * PushConstants.bottomLoD; + } + + position.xz /= 128.0; + + return position; + +} + +void main() { + + fPosition = stitch(vPosition) * PushConstants.nodeSideLength + + vec3(PushConstants.nodeLocation.x, 0.0, PushConstants.nodeLocation.y) + + Uniforms.translation.xyz; + + float distanceToCamera = distance(fPosition.xyz, globalData.cameraLocation.xyz); + + float perlinScale, shoreScaling; + vec3 normalShoreWave; + fPosition += GetOceanDisplacement(fPosition, distanceToCamera, perlinScale, shoreScaling, normalShoreWave); + + vec3 position = vec3(globalData.vMatrix * vec4(fPosition, 1.0)); + vec4 fClipSpace = globalData.pMatrix * vec4(position, 1.0); + + gl_Position = fClipSpace; + +} \ No newline at end of file diff --git a/data/shader/ocean/h0.csh b/data/shader/ocean/h0.csh index 42de08c4c..950ed6b9a 100644 --- a/data/shader/ocean/h0.csh +++ b/data/shader/ocean/h0.csh @@ -1,8 +1,8 @@ #include <../common/PI.hsh> -layout (local_size_x = 16, local_size_y = 16) in; +layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; -layout (set = 3, binding = 0, rg32f) writeonly uniform image2D h0K; +layout (set = 3, binding = 0, rg32f) writeonly uniform image2DArray h0K; layout(set = 3, binding = 2) uniform sampler2D noise0; layout(set = 3, binding = 3) uniform sampler2D noise1; @@ -10,13 +10,13 @@ layout(set = 3, binding = 4) uniform sampler2D noise2; layout(set = 3, binding = 5) uniform sampler2D noise3; layout(push_constant) uniform constants { + ivec4 L; + vec2 w; int N; - int L; float A; float windspeed; float windDependency; float waveSurpression; - vec2 w; } PushConstants; const float g = 9.81; @@ -53,10 +53,12 @@ float phillips(vec2 k, float A, float l, vec2 w) { } void main() { + + int spectrumIdx = int(gl_GlobalInvocationID.z); vec2 coord = vec2(gl_GlobalInvocationID.xy) - float(PushConstants.N) / 2.0; - vec2 waveVector = 2.0 * PI / float(PushConstants.L) * coord; + vec2 waveVector = 2.0 * PI / float(PushConstants.L[spectrumIdx]) * coord; float l = (PushConstants.windspeed * PushConstants.windspeed) / g; @@ -69,6 +71,6 @@ void main() { h0k = 0.0; } - imageStore(h0K, ivec2(gl_GlobalInvocationID.xy), vec4(rnd.xy * h0k, 0.0, 0.0)); + imageStore(h0K, ivec3(gl_GlobalInvocationID.xyz), vec4(rnd.xy * h0k, 0.0, 0.0)); } \ No newline at end of file diff --git a/data/shader/ocean/ht.csh b/data/shader/ocean/ht.csh index 719587fba..5aa24c44f 100644 --- a/data/shader/ocean/ht.csh +++ b/data/shader/ocean/ht.csh @@ -1,14 +1,14 @@ #include <../common/PI.hsh> #include <../common/complex.hsh> -layout (local_size_x = 16, local_size_y = 16) in; +layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; -layout (set = 3, binding = 0, rgba32f) writeonly uniform image2D hTD; -layout (set = 3, binding = 1, rg32f) readonly uniform image2D h0K; +layout (set = 3, binding = 0, rgba32f) writeonly uniform image2DArray hTD; +layout (set = 3, binding = 1, rg32f) readonly uniform image2DArray h0K; layout(push_constant) uniform constants { + ivec4 L; int N; - int L; float time; } PushConstants; @@ -16,16 +16,17 @@ const float g = 9.81; void main() { + int spectrumIdx = int(gl_GlobalInvocationID.z); vec2 coord = vec2(gl_GlobalInvocationID.xy) - float(PushConstants.N) / 2.0; - vec2 waveVector = 2.0 * PI / float(PushConstants.L) * coord; + vec2 waveVector = 2.0 * PI / float(PushConstants.L[spectrumIdx]) * coord; float k = length(waveVector); float w = sqrt(g * k); ivec2 inverseCoords = (ivec2(imageSize(h0K))) - ivec2(gl_GlobalInvocationID); - vec2 h0_k = imageLoad(h0K, ivec2(gl_GlobalInvocationID)).rg; - vec2 h0_mk = imageLoad(h0K, ivec2(inverseCoords)).rg; + vec2 h0_k = imageLoad(h0K, ivec3(gl_GlobalInvocationID.xy, spectrumIdx)).rg; + vec2 h0_mk = imageLoad(h0K, ivec3(inverseCoords, spectrumIdx)).rg; float cos_wt = cos(w * PushConstants.time); float sin_wt = sin(w * PushConstants.time); @@ -46,6 +47,6 @@ void main() { vec2 dt_x = vec2(ht.y * nkt.x, -ht.x * nkt.x); vec2 idt_z = vec2(ht.x * nkt.y, ht.y * nkt.y); - imageStore(hTD, ivec2(gl_GlobalInvocationID.xy), vec4(ht, dt_x + idt_z)); + imageStore(hTD, ivec3(gl_GlobalInvocationID.xyz), vec4(ht, dt_x + idt_z)); } \ No newline at end of file diff --git a/data/shader/ocean/inversion.csh b/data/shader/ocean/inversion.csh index 6f5fae2e2..8145e4207 100644 --- a/data/shader/ocean/inversion.csh +++ b/data/shader/ocean/inversion.csh @@ -1,11 +1,11 @@ -layout (local_size_x = 16, local_size_y = 16) in; +layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; -layout (set = 3, binding = 0, rgba16f) writeonly uniform image2D displacementMap; -layout (set = 3, binding = 1, rgba32f) readonly uniform image2D pingpongMap; +layout (set = 3, binding = 0, rgba16f) writeonly uniform image2DArray displacementMap; +layout (set = 3, binding = 1, rgba32f) readonly uniform image2DArray pingpongMap; void main() { - ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + ivec3 coord = ivec3(gl_GlobalInvocationID.xyz); float perm = bool((coord.x + coord.y) & 1) ? -1.0 : 1.0; diff --git a/data/shader/ocean/normal.csh b/data/shader/ocean/normal.csh index f2021ab56..5ba97dc23 100644 --- a/data/shader/ocean/normal.csh +++ b/data/shader/ocean/normal.csh @@ -1,12 +1,15 @@ -layout (local_size_x = 16, local_size_y = 16) in; +#include <../common/utility.hsh> -layout (set = 3, binding = 0, rgba16f) readonly uniform image2D displacementMap; -layout (set = 3, binding = 1, rgba16f) writeonly uniform image2D normalMap; -layout (set = 3, binding = 2, rgba16f) readonly uniform image2D historyMap; +layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +layout (set = 3, binding = 0, rgba16f) readonly uniform image2DArray displacementMap; +layout (set = 3, binding = 1, rgba16f) writeonly uniform image2DArray normalMap; +// layout (set = 3, binding = 2, rgba16f) readonly uniform image2D historyMap; layout(push_constant) uniform constants { + ivec4 L; + vec4 tilingFactor; int N; - int L; float choppyScale; float displacementScale; float tiling; @@ -27,19 +30,20 @@ vec3 AdjustScale(vec3 point) { void main() { - ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + ivec3 coord = ivec3(gl_GlobalInvocationID.xyz); - vec2 fCoord = vec2(coord); + vec2 fCoord = vec2(coord) + vec2(0.5); float fN = float(PushConstants.N); - float tileSize = PushConstants.tiling / float(PushConstants.N); + float tilingFactor = PushConstants.tilingFactor[coord.z]; + float tileSize = (tilingFactor * PushConstants.tiling) / float(PushConstants.N); float invTileSize = 1.0 / tileSize; vec3 center = imageLoad(displacementMap, coord).grb; - vec3 left = imageLoad(displacementMap, ivec2(mod(fCoord.x - 1.0, fN), coord.y)).grb; - vec3 right = imageLoad(displacementMap, ivec2(mod(fCoord.x + 1.0, fN), coord.y)).grb; - vec3 top = imageLoad(displacementMap, ivec2(coord.x, mod(fCoord.y + 1.0, fN))).grb; - vec3 bottom = imageLoad(displacementMap, ivec2(coord.x, mod(fCoord.y - 1.0, fN))).grb; + vec3 left = imageLoad(displacementMap, ivec3(mod(fCoord.x - 1.0, fN), coord.y, coord.z)).grb; + vec3 right = imageLoad(displacementMap, ivec3(mod(fCoord.x + 1.0, fN), coord.y, coord.z)).grb; + vec3 top = imageLoad(displacementMap, ivec3(coord.x, mod(fCoord.y + 1.0, fN), coord.z)).grb; + vec3 bottom = imageLoad(displacementMap, ivec3(coord.x, mod(fCoord.y - 1.0, fN), coord.z)).grb; center = AdjustScale(center); left = AdjustScale(left); @@ -47,21 +51,23 @@ void main() { top = AdjustScale(top); bottom = AdjustScale(bottom); - float history = imageLoad(historyMap, coord).a; + // float history = imageLoad(historyMap, coord).a; // Calculate jacobian - vec2 Dx = (right.xz - left.xz) * invTileSize; - vec2 Dy = (top.xz - bottom.xz) * invTileSize; + vec2 Dx = (right.xz - left.xz); + vec2 Dy = (top.xz - bottom.xz); float J = (1.0 + Dx.x) * (1.0 + Dy.y) - Dx.y * Dy.x; - float fold = -clamp(J, -1.0, 1.0) + PushConstants.foamOffset; + float fold = max(0.0, -clamp(J, -1.0, 1.0) + PushConstants.foamOffset); /* float blend = fold > PushConstants.temporalThreshold ? 0.0 : 0.0; fold = mix(fold, history, blend); - */ - - vec2 gradient = vec2(left.y - right.y, bottom.y - top.y); + */ + + float width = right.x - left.x; + float height = bottom.z - top.z; + vec2 gradient = vec2(left.y - right.y, bottom.y - top.y) / (1.0 + abs(vec2(width, height))); imageStore(normalMap, coord, vec4(gradient, 0.0, fold)); diff --git a/data/shader/ocean/ocean.fsh b/data/shader/ocean/ocean.fsh index 728d48b0c..a4771b85f 100644 --- a/data/shader/ocean/ocean.fsh +++ b/data/shader/ocean/ocean.fsh @@ -1,9 +1,12 @@ -#define SHADOW_FILTER_1x1 +#define SHADOW_FILTER_3x3 +#include #include <../common/convert.hsh> #include <../common/utility.hsh> #include <../common/stencil.hsh> +#include <../common/normalencode.hsh> #include <../clouds/shadow.hsh> +#include <../volumetric/volumetric.hsh> #include <../structures> #include @@ -14,7 +17,6 @@ layout (location = 0) out vec3 color; layout (location = 1) out vec2 velocity; layout (location = 2) out uint stencil; -layout(set = 3, binding = 1) uniform sampler2D normalMap; layout (set = 3, binding = 2) uniform sampler2D foamTexture; layout (set = 3, binding = 3) uniform samplerCube skyEnvProbe; layout (set = 3, binding = 4) uniform sampler2D refractionTexture; @@ -22,14 +24,13 @@ layout (set = 3, binding = 5) uniform sampler2D depthTexture; layout (set = 3, binding = 7) uniform sampler2D volumetricTexture; layout (set = 3, binding = 8) uniform sampler2DArrayShadow cascadeMaps; layout (set = 3, binding = 10) uniform sampler2D rippleTexture; -layout(set = 3, binding = 13) uniform sampler2D perlinNoiseMap; -layout(set = 3, binding = 15) uniform sampler2D cloudMap; +layout(set = 3, binding = 15) uniform sampler2D cloudShadowMap; +layout(set = 3, binding = 16) uniform sampler2D volumetricCloudTexture; layout(location=0) in vec4 fClipSpace; layout(location=1) in vec3 fPosition; layout(location=2) in vec3 fModelCoord; layout(location=3) in vec3 fOriginalCoord; -layout(location=4) in vec2 fTexCoord; // layout(location=5) in float waterDepth; layout(location=6) in float shoreScaling; layout(location=7) in vec3 ndcCurrent; @@ -37,11 +38,6 @@ layout(location=8) in vec3 ndcLast; layout(location=9) in vec3 normalShoreWave; layout(location=10) in float perlinScale; -const vec3 waterBodyColor = pow(vec3(0.1, 1.0, 0.7), vec3(2.2)); -const vec3 deepWaterBodyColor = pow(vec3(0.1,0.15, 0.5), vec3(2.2)); -const vec3 scatterColor = pow(vec3(0.3,0.7,0.6), vec3(2.2)); -const vec2 waterColorIntensity = pow(vec2(0.1, 0.3), vec2(2.2)); - // Control water scattering at crests const float scatterIntensity = 1.5; const float scatterCrestScale = 0.2; @@ -61,32 +57,35 @@ void main() { Light light = LightUniforms.light; - // Retrieve precalculated normals and wave folding information - //vec3 fNormal = normalize(2.0 * texture(normalMap, fTexCoord).rgb - 1.0); - float fold = texture(normalMap, fTexCoord).a; + vec2 fTexCoord = fOriginalCoord.xz / Uniforms.tiling; - vec2 gradientDisplacement = texture(normalMap, fTexCoord).rg; + float fold; + vec2 gradientDisplacement; + GetOceanGradientAndFold(fOriginalCoord.xz, distance(fOriginalCoord.xyz, globalData.cameraLocation.xyz), + fold, gradientDisplacement); - vec2 gradient = perlinScale * gradientDisplacement; + vec2 gradient = gradientDisplacement; float tileSize = Uniforms.tiling / float(Uniforms.N); - vec3 fNormal = normalize(vec3(gradient.x, 2.0 * tileSize, gradient.y)); vec2 ndcCoord = 0.5 * (fClipSpace.xy / fClipSpace.w) + 0.5; float clipDepth = textureLod(depthTexture, ndcCoord, 0.0).r; vec3 depthPos = ConvertDepthToViewSpace(clipDepth, ndcCoord); + vec3 fNormal = normalize(vec3(gradient.x, 2.0 * tileSize, gradient.y)); - float shadowFactor = max(CalculateCascadedShadow(light.shadow, - cascadeMaps, fPosition, fNormal, 0.0), 0.01); + float shadowFactor = CalculateCascadedShadow(light.shadow, + cascadeMaps, fPosition, fNormal, 1.0); #ifdef CLOUD_SHADOWS - float cloudShadowFactor = CalculateCloudShadow(fPosition, cloudShadowUniforms.cloudShadow, cloudMap); + float cloudShadowFactor = CalculateCloudShadow(fPosition, cloudShadowUniforms.cloudShadow, cloudShadowMap); shadowFactor = min(shadowFactor, cloudShadowFactor); #endif shadowFactor = max(shadowFactor, 0.01); +#ifdef TERRAIN fNormal = mix(normalShoreWave, fNormal, shoreScaling); +#endif // Create TBN matrix for normal mapping vec3 norm = fNormal; @@ -99,12 +98,12 @@ void main() { // Normal mapping normal (offsets actual normal) vec3 rippleNormal = vec3(0.0, 1.0, 0.0); - if (Uniforms.hasRippleTexture > 0) { +#ifdef RIPPLE_TEXTURE rippleNormal = normalize(2.0 * texture(rippleTexture, 20.0 * fTexCoord - vec2(globalData.time * 0.2)).rgb - 1.0); rippleNormal += normalize(2.0 * texture(rippleTexture, 20.0 * fTexCoord * 0.5 + vec2(globalData.time * 0.05)).rgb - 1.0); // Won't work with rippleNormal = vec3(0.0, 1.0, 0.0). Might be worth an investigation norm = normalize(tbn * rippleNormal); - } +#endif // Scale ripples based on actual (not view) depth of water float rippleScaling = clamp(1.0 - shoreScaling, 0.05, 0.1); @@ -120,7 +119,7 @@ void main() { // Calculate reflection vector vec3 reflectionVec = normalize(reflect(eyeDir, norm)); - reflectionVec.y = max(0.0, reflectionVec.y); + //reflectionVec.y = max(0.0, reflectionVec.y); // Calculate sun spot float specularFactor = shadowFactor * fresnel * pow(max(dot(normalize(reflectionVec), @@ -131,16 +130,15 @@ void main() { float scatterFactor = scatterIntensity * max(0.0, waveHeight * scatterCrestScale + scatterCrestOffset); - scatterFactor *= shadowFactor * pow(max(0.0, dot(normalize(vec3(-light.direction.x, - 0.0, -light.direction.z)), eyeDir)), 2.0); + scatterFactor *= shadowFactor * pow(max(0.0, dot(-light.direction.xyz, eyeDir)), 8.0); - scatterFactor *= pow(max(0.0, 1.0 - nDotL), 8.0); + scatterFactor *= pow(clamp(1.0 - nDotL, 0.0, 1.1), 16.0); /* - scatterFactor += shadowFactor * waterColorIntensity.y + scatterFactor += shadowFactor * Uniforms.waterColorIntensity.y * max(0.0, waveHeight) * max(0.0, nDotE) * - max(0.0, 1.0 + eyeDir.y) * dot(-light.direction, -eyeDir); - */ + max(0.0, 1.0 + eyeDir.y) * dot(-light.direction.xyz, -eyeDir); + */ // Calculate water depth based on the viewer (ray from camera to ground) float waterViewDepth = max(0.0, fPosition.z - depthPos.z); @@ -155,8 +153,8 @@ void main() { vec3 reflectionColor = textureLod(skyEnvProbe, reflectionVec, 0).rgb; // Calculate water color - vec3 depthFog = mix(deepWaterBodyColor, waterBodyColor, min(1.0 , exp(-waterViewDepth / 10.0))); - float diffuseFactor = waterColorIntensity.x + waterColorIntensity.y * + vec3 depthFog = mix(Uniforms.deepWaterBodyColor.rgb, Uniforms.waterBodyColor.rgb, min(1.0 , exp(-waterViewDepth / 10.0))); + float diffuseFactor = Uniforms.waterColorIntensity.x + Uniforms.waterColorIntensity.y * max(0.0, nDotL) * shadowFactor; vec3 waterColor = diffuseFactor * light.intensity * light.color.rgb * depthFog; @@ -165,11 +163,14 @@ void main() { // Update refraction color based on water depth (exponential falloff) refractionColor = mix(waterColor, refractionColor, min(1.0 , exp(-waterViewDepth / 2.0))); - - vec2 shoreInteraction = CalculateShoreInteraction(fModelCoord); + + vec2 shoreInteraction = vec2(0.0); +#ifdef TERRAIN + shoreInteraction = CalculateShoreInteraction(fModelCoord); +#endif // Calculate foam based on folding of wave and fade it out near shores - float foam = 0.0; + float foam = fold; foam += shoreInteraction.x; foam = min(foam, 1.0); @@ -179,29 +180,41 @@ void main() { // Mix relection and refraction and add sun spot color = mix(refractionColor, reflectionColor, fresnel); color += specularIntensity * fresnel * specularFactor * light.color.rgb; - color += scatterColor * scatterFactor; + color += Uniforms.scatterColor.rgb * scatterFactor; - vec3 foamColor = vec3(texture(foamTexture, fOriginalCoord.xz / 8.0).r) * - nDotL * light.intensity; - - color = mix(color, vec3(mix(scatterColor * 0.1, vec3(1.0), - foamColor)), foam); + vec3 foamShadowFactor = mix(vec3(0.1), + vec3(0.5) * max(0.0, nDotL) * shadowFactor, 0.7); + + vec3 foamColor = vec3(1.0); +#ifdef FOAM_TEXTURE + foamColor = foamShadowFactor * light.intensity * light.color.rgb; +#endif + color = mix(color, foamColor, foam * texture(foamTexture, fOriginalCoord.xz / 8.0).r); - vec3 breakingColor = mix(vec3(0.1), - vec3(0.5) * max(0.0, nDotL) * shadowFactor, 0.7) * light.intensity * light.color.rgb; + vec3 breakingColor = foamShadowFactor * light.intensity * light.color.rgb; color = mix(color, breakingColor, shoreInteraction.y); - /* - if (dot(norm, -eyeDir) < 0.0) { - float waterViewDepth = max(0.0, -fPosition.z); - depthFog = mix(deepWaterBodyColor, waterBodyColor, min(1.0 , exp(-waterViewDepth / 10.0))); - color = mix(depthFog, textureLod(refractionTexture, ndcCoord + refractionDisturbance, 0).rgb, min(1.0 , exp(-waterViewDepth / 2.0))); - } - */ + StencilFeatures features = CreateStencilFeatures(); + + if (!gl_FrontFacing) { + fresnel = 0.02 + (1.0 - 0.02) * pow(1.0 - saturate(dot(fNormal, eyeDir)), 5.0); + disturbance = (mat3(globalData.vMatrix) * -vec3(norm.x, 0.0, norm.z)).xz; - //color = vec3(mod(fTexCoord, 1.0), 0.0); - //color = vec3(reflectionVec.y); - //color = vec3(perlinDisplacement); + refractionDisturbance = vec2(disturbance.x, -disturbance.y) * 0.1; + refractionDisturbance *= min(2.0, waterViewDepth); + color = textureLod(refractionTexture, ndcCoord + refractionDisturbance, 0).rgb * (1.0 - fresnel); + features.underWaterPixel = true; + } + else { + features.underWaterPixel = false; + vec4 volumetricFog = textureLod(volumetricTexture, ndcCoord, 0.0); + vec4 volumetricClouds = vec4(0.0, 0.0, 0.0, 1.0); +#ifdef CLOUDS + volumetricClouds = textureLod(volumetricCloudTexture, ndcCoord, 0.0); +#endif + color = ApplyVolumetrics(Uniforms.fog, color, volumetricFog, volumetricClouds, + eyeDir, globalData.planetCenter.xyz, Uniforms.innerCloudRadius); + } // Calculate velocity vec2 ndcL = ndcLast.xy / ndcLast.z; @@ -212,8 +225,8 @@ void main() { velocity = (ndcL - ndcC) * 0.5; - StencilFeatures features; features.responsivePixel = true; + features.waterPixel = true; stencil = EncodeStencilFeatures(features); } \ No newline at end of file diff --git a/data/shader/ocean/ocean.vsh b/data/shader/ocean/ocean.vsh index 0d4083955..6b970d443 100644 --- a/data/shader/ocean/ocean.vsh +++ b/data/shader/ocean/ocean.vsh @@ -1,29 +1,25 @@ #include <../common/utility.hsh> #include <../common/PI.hsh> + +#include #include #include layout(location=0) in vec3 vPosition; -layout(set = 3, binding = 0) uniform sampler2D displacementMap; -layout(set = 3, binding = 13) uniform sampler2D perlinNoiseMap; - layout(location=0) out vec4 fClipSpace; layout(location=1) out vec3 fPosition; layout(location=2) out vec3 fModelCoord; layout(location=3) out vec3 fOriginalCoord; -layout(location=4) out vec2 fTexCoord; // layout(location=5) out float waterDepth; layout(location=6) out float shoreScaling; layout(location=7) out vec3 ndcCurrent; layout(location=8) out vec3 ndcLast; +#ifdef TERRAIN layout(location=9) out vec3 normalShoreWave; +#endif layout(location=10) out float perlinScale; -const float shoreStartScaling = 15.0; -const float shoreOffsetScaling = 5.0; -const float minShoreScaling = 0.3; - vec3 stitch(vec3 position) { // Note: This only works because our grid has a constant size @@ -53,68 +49,23 @@ vec3 stitch(vec3 position) { } -const float fadeoutDistance = 100.0; -const float fadeoutFalloff = 0.2; - void main() { - float waterDepth = shoreStartScaling; - fPosition = stitch(vPosition) * PushConstants.nodeSideLength + vec3(PushConstants.nodeLocation.x, 0.0, PushConstants.nodeLocation.y) + Uniforms.translation.xyz; - - bool hasTerrain = Uniforms.terrainSideLength > 0.0; - - vec2 terrainTex = (vec2(fPosition.xz) - vec2(Uniforms.terrainTranslation.xz)) - / Uniforms.terrainSideLength; - - float shoreDistance = 0.0; - vec2 shoreGradient = vec2(0.0); - - if (hasTerrain && terrainTex.x >= 0.0 && terrainTex.y >= 0.0 - && terrainTex.x <= 1.0 && terrainTex.y <= 1.0) { - waterDepth = fPosition.y - textureLod(terrainHeight, terrainTex, 0.0).r - * Uniforms.terrainHeightScale + Uniforms.terrainTranslation.y; - shoreDistance = textureLod(terrainHeight, terrainTex, 0.0).g; - shoreGradient = normalize(2.0 * textureLod(terrainHeight, terrainTex, 0.0).ba - 1.0); - } - - float depthScaling = clamp((waterDepth - shoreOffsetScaling) / - (shoreStartScaling - shoreOffsetScaling), minShoreScaling, 1.0); - shoreScaling = hasTerrain ? depthScaling : 1.0; - - vec2 vTexCoord = vec2(fPosition.x, fPosition.z) / Uniforms.tiling; fOriginalCoord = fPosition; - - vec3 displacement = textureLod(displacementMap, vTexCoord, 0.0).grb; - displacement.y *= Uniforms.displacementScale * shoreScaling; - displacement.x *= Uniforms.choppyScale * shoreScaling; - displacement.z *= Uniforms.choppyScale * shoreScaling; - - float perlin = textureLod(perlinNoiseMap, vTexCoord * 0.125 * 0.5, 0.0).r; - - vec3 octaveFadeout = vec3(400.0, 200.0, 100.0); - - perlinScale = sqr(clamp(perlin, 0.0, 1.0)); - displacement = perlinScale * displacement; - - fPosition += displacement; - - vec3 dx = vec3(0.1, 0.0, 0.0) + CalculateGerstner(fPosition + vec3(0.1, 0.0, 0.0)); - vec3 dz = vec3(0.0, 0.0, 0.1) + CalculateGerstner(fPosition + vec3(0.0, 0.0, 0.1)); - vec3 centerOffset = CalculateGerstner(fPosition); - - normalShoreWave = normalize(cross(dz - centerOffset, dx - centerOffset)); - fPosition += centerOffset; - + +#ifndef TERRAIN + vec3 normalShoreWave; +#endif + float distanceToCamera = distance(fOriginalCoord.xyz, globalData.cameraLocation.xyz); + fPosition += GetOceanDisplacement(fPosition, distanceToCamera, perlinScale, shoreScaling, normalShoreWave); fModelCoord = fPosition; fPosition = vec3(globalData.vMatrix * vec4(fPosition, 1.0)); fClipSpace = globalData.pMatrix * vec4(fPosition, 1.0); - - fTexCoord = vTexCoord; ndcCurrent = vec3(fClipSpace.xy, fClipSpace.w); // For moving objects we need the last matrix diff --git a/data/shader/ocean/sharedUniforms.hsh b/data/shader/ocean/sharedUniforms.hsh index 70245db03..2ed613413 100644 --- a/data/shader/ocean/sharedUniforms.hsh +++ b/data/shader/ocean/sharedUniforms.hsh @@ -1,8 +1,22 @@ #include <../globals.hsh> #include <../structures> +#include <../volumetric/fog.hsh> layout (set = 3, binding = 11, std140) uniform UniformBuffer { + Fog fog; + + vec4 waterBodyColor; + vec4 deepWaterBodyColor; + vec4 scatterColor; + vec4 translation; + vec4 terrainTranslation; + + vec4 waterColorIntensity; + + vec4 spectrumTilingFactors; + vec4 spectrumWeights; + vec4 spectrumFadeoutDistances; float displacementScale; float choppyScale; @@ -19,10 +33,10 @@ layout (set = 3, binding = 11, std140) uniform UniformBuffer { float shoreWaveLength; float terrainSideLength; - vec4 terrainTranslation; - float terrainHeightScale; int N; + int spectrumCount; + float innerCloudRadius; } Uniforms; layout (set = 3, binding = 12, std140) uniform LightUniformBuffer { diff --git a/data/shader/ocean/shoreInteraction.hsh b/data/shader/ocean/shoreInteraction.hsh index 666212ce1..95a5f1c42 100644 --- a/data/shader/ocean/shoreInteraction.hsh +++ b/data/shader/ocean/shoreInteraction.hsh @@ -1,5 +1,7 @@ #include +#include <../common/utility.hsh> + layout(set = 3, binding = 9) uniform sampler2D terrainHeight; vec3 CalculateGerstner(vec3 position) { diff --git a/data/shader/ocean/underwater.csh b/data/shader/ocean/underwater.csh new file mode 100644 index 000000000..bfbd3d327 --- /dev/null +++ b/data/shader/ocean/underwater.csh @@ -0,0 +1,106 @@ +layout (local_size_x = 8, local_size_y = 8) in; + +#define SHADOW_FILTER_3x3 + +#include +#include + +#include <../structures> +#include <../shadow.hsh> +#include <../globals.hsh> +#include <../common/convert.hsh> +#include <../common/utility.hsh> +#include <../common/stencil.hsh> +#include <../common/normalencode.hsh> + +layout (set = 3, binding = 4) uniform sampler2D refractionTexture; +layout(set = 3, binding = 16) uniform sampler2D depthTexture; +layout(set = 3, binding = 17) uniform usampler2D stencilTexture; +layout(set = 3, binding = 18) uniform sampler2D oceanDepthTexture; +layout(set = 3, binding = 8) uniform sampler2DArrayShadow cascadeMaps; +layout(set = 3, binding = 20, rgba16f) uniform image2D outputImage; + + +void main() { + + ivec2 pixel = ivec2(gl_GlobalInvocationID); + if (pixel.x > imageSize(outputImage).x || + pixel.y > imageSize(outputImage).y) + return; + + Light light = LightUniforms.light; + + vec2 texCoord = (vec2(pixel) + 0.5) / vec2(imageSize(outputImage)); + + float depth = textureLod(depthTexture, texCoord, 0.0).r; + float oceanDepth = textureLod(oceanDepthTexture, texCoord, 0.0).r; + + if (depth == 1.0 && oceanDepth == 1.0) + return; + + vec3 viewSpacePos = ConvertDepthToViewSpace(depth, texCoord); + vec3 viewSpaceOceanPos = ConvertDepthToViewSpace(oceanDepth, texCoord); + vec3 pixelPos = vec3(globalData.ivMatrix * vec4(viewSpacePos, 1.0)); + vec3 oceanPos = vec3(globalData.ivMatrix * vec4(viewSpaceOceanPos, 1.0)); + vec3 nearPos = vec3(globalData.ivMatrix * vec4(ConvertDepthToViewSpace(0.0, texCoord), 1.0)); + + float distanceToCamera = length(viewSpacePos); + + int pixelsUnderWater = 0; + int waterPixels = 0; + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + StencilFeatures features = DecodeStencilFeatures(texelFetch(stencilTexture, pixel + ivec2(x, y), 0).r); + + waterPixels += features.waterPixel ? 1 : 0; + pixelsUnderWater += features.underWaterPixel ? 1 : 0; + } + } + + // Do majority vote, since pixel informtion gl_FrontFacing pixel information is inaccurate at a distance with shallow angle + bool underWaterPixel = float(pixelsUnderWater) > float(waterPixels) / 2.0 ? true : false; + + StencilFeatures features = DecodeStencilFeatures(texelFetch(stencilTexture, pixel, 0).r); + + float perlinScale, shoreScaling; + vec3 normalShoreWave; + vec3 displacement = GetOceanDisplacement(nearPos, 0.0, perlinScale, shoreScaling, normalShoreWave); + + vec3 viewVector = pixelPos - nearPos; + + float waterDepth = Uniforms.translation.y - nearPos.y + displacement.y; + if (waterDepth < 0.0 && depth < oceanDepth && oceanDepth < 1.0 || + oceanDepth == 1.0 && pixelPos.y > Uniforms.translation.y || + (!underWaterPixel && features.waterPixel)) { + return; + } + + texCoord = (texCoord + vec2(0.05)) * 0.9; + + texCoord.x += sin(texCoord.y * 2.0 * PI * 5.0 + 5.0 * globalData.time) * 0.001 / depth; + texCoord.y += cos(texCoord.x * 2.0 * PI * 2.0 + 2.0 * globalData.time) * 0.001 / depth; + + depth = textureLod(depthTexture, texCoord, 0.0).r; + viewSpacePos = ConvertDepthToViewSpace(depth, texCoord); + pixelPos = vec3(globalData.ivMatrix * vec4(viewSpacePos, 1.0)); + + vec3 refractionColor = textureLod(refractionTexture, texCoord, 0.0).rgb; + + float NDotL = dot(-light.direction.xyz, vec3(0.0, 1.0, 0.0)); + + viewVector = normalize(viewVector); + + float waterOffset = max(0.0, (nearPos.y - Uniforms.translation.y) / max(viewVector.y, 0.0001)); + float waterViewDepth = distance(pixelPos, nearPos); + + // Calculate water color + vec3 depthFog = mix(Uniforms.deepWaterBodyColor.rgb, Uniforms.waterBodyColor.rgb, min(1.0 , exp(-waterViewDepth / 20.0))); + float diffuseFactor = Uniforms.waterColorIntensity.x + Uniforms.waterColorIntensity.y * max(0.0, NDotL); + vec3 waterColor = diffuseFactor * light.intensity * light.color.rgb * depthFog; + + // Update refraction color based on water depth (exponential falloff) + refractionColor = mix(waterColor, refractionColor, min(1.0 , exp(-waterViewDepth / 5.0))); + + imageStore(outputImage, pixel, vec4(refractionColor, 0.0)); + +} \ No newline at end of file diff --git a/data/shader/taa.csh b/data/shader/taa.csh index 6b7e3b880..40947580c 100644 --- a/data/shader/taa.csh +++ b/data/shader/taa.csh @@ -343,7 +343,7 @@ void main() { ivec2 velocityPixel = clamp(pixel + offset, ivec2(0), ivec2(PushConstants.resolution) - ivec2(1)); vec2 velocity = texelFetch(velocityTexture, velocityPixel, 0).rg; - vec2 lastVelocity = texelFetch(lastVelocityTexture, velocityPixel, 0).rg; + vec2 lastVelocity = texelFetch(lastVelocityTexture, pixel, 0).rg; float velocityBlend = saturate(600.0 * max(length(velocity), length(lastVelocity))); diff --git a/data/shader/terrain/terrain.tcsh b/data/shader/terrain/terrain.tcsh index 0c14f43cb..49511f158 100644 --- a/data/shader/terrain/terrain.tcsh +++ b/data/shader/terrain/terrain.tcsh @@ -79,8 +79,8 @@ void main() { // Displacement is in normal direction // To fix culling we simply scale the bounding // box of the current tile by a small margin - maxVec += dir * .25; - minVec -= dir * .25; + maxVec += dir; + minVec -= dir; if (IsTileVisible(minVec, maxVec)) { diff --git a/data/shader/volumetric/fog.hsh b/data/shader/volumetric/fog.hsh index adc7dcf7c..5f808aaa7 100644 --- a/data/shader/volumetric/fog.hsh +++ b/data/shader/volumetric/fog.hsh @@ -24,6 +24,14 @@ float ComputeVolumetricFog(Fog fog, vec3 viewPos, vec3 worldPos) { return exp(-fog.density * fogIntegration); } +float GetVolumetricFogDensity(Fog fog, vec3 viewPos, vec3 worldPos) { + + viewPos.y -= fog.height; + + return exp(-fog.heightFalloff * viewPos.y);; + +} + // Henyey-Greenstein phase function https://www.astro.umd.edu/~jph/HG_note.pdf float ComputeScattering(float scatteringAnisotropy, float lightDotView) { // Range [-1;1] diff --git a/data/shader/volumetric/volumetric.csh b/data/shader/volumetric/volumetric.csh index 1149940da..6ffb22ae2 100644 --- a/data/shader/volumetric/volumetric.csh +++ b/data/shader/volumetric/volumetric.csh @@ -8,6 +8,7 @@ layout (local_size_x = 8, local_size_y = 8) in; #include <../structures> #include <../common/ign.hsh> #include <../common/convert.hsh> +#include <../common/stencil.hsh> #include <../common/utility.hsh> #include <../common/random.hsh> #include <../clouds/shadow.hsh> @@ -18,18 +19,22 @@ layout(set = 3, binding = 0, rgba16f) writeonly uniform image2D volumetricImage; layout(set = 3, binding = 1) uniform sampler2D depthTexture; layout(set = 3, binding = 2) uniform sampler2DArrayShadow cascadeMaps; layout(set = 3, binding = 3) uniform sampler2D cloudMap; +layout(set = 3, binding = 4) uniform sampler2D oceanDepthTexture; +layout(set = 3, binding = 5) uniform usampler2D oceanStencilTexture; +layout(set = 3, binding = 6) uniform sampler2D volumetricCloudTexture; -layout(std140, set = 3, binding = 4) uniform UniformBuffer { +layout(std140, set = 3, binding = 7) uniform UniformBuffer { int sampleCount; float intensity; - float seed; int fogEnabled; + float oceanHeight; + vec4 planetCenterAndRadius; Fog fog; Light light; CloudShadow cloudShadow; } uniforms; -vec3 ComputeVolumetric(vec3 fragPos, vec2 texCoords); +vec4 ComputeVolumetric(vec3 fragPos, float startDepth, vec2 texCoords); void main() { @@ -43,47 +48,70 @@ void main() { float depth = textureLod(depthTexture, texCoord, 0.0).r; vec3 pixelPos = ConvertDepthToViewSpace(depth, texCoord); - vec3 radiance = vec3(0.0); - radiance = ComputeVolumetric(pixelPos, texCoord); - imageStore(volumetricImage, pixel, vec4(radiance, 0.0)); + vec3 endPos = pixelPos; + + float startDepth = 0.0; +#ifdef OCEAN + StencilFeatures features = DecodeStencilFeatures(textureLod(oceanStencilTexture, texCoord, 0.0).r); + + float oceanDepth = textureLod(oceanDepthTexture, texCoord, 0.0).r; + vec3 oceanPos = ConvertDepthToViewSpace(oceanDepth, texCoord); + + // We could use stencil features here, but they are unrealiable. Next option is to just apply + // simpl fog to the refraction texture when under water + if (oceanDepth < depth) { + endPos = oceanPos; + } +#endif + + vec4 radiance = vec4(0.0); + radiance = ComputeVolumetric(endPos, startDepth, texCoord); + imageStore(volumetricImage, pixel, radiance); } -vec3 ComputeVolumetric(vec3 fragPos, vec2 texCoords) { +vec4 ComputeVolumetric(vec3 fragPos, float startDepth, vec2 texCoords) { vec2 resolution = vec2(imageSize(volumetricImage)); vec3 viewPosition = vec3(globalData.ivMatrix * vec4(fragPos, 1.0)); // We compute this in view space vec3 rayVector = fragPos; - float rayLength = length(rayVector); + float rayLength = max(length(rayVector) - startDepth, 0.0); vec3 rayDirection = rayVector / rayLength; float stepLength = rayLength / float(uniforms.sampleCount); vec3 stepVector = rayDirection * stepLength; vec3 foginess = vec3(0.0); + float extinction = 1.0; + float extinctionWithClouds = 1.0; - texCoords = (0.5 * texCoords + 0.5) * resolution; + vec2 interleavedTexCoords = (0.5 * texCoords + 0.5) * resolution; - float noiseOffset = GetInterleavedGradientNoise(texCoords); - vec3 currentPosition = stepVector * noiseOffset; + float noiseOffset = GetInterleavedGradientNoise(interleavedTexCoords); + vec3 currentPosition = stepVector * (noiseOffset + startDepth); int cascadeIndex = 0; int lastCascadeIndex = 0; mat4 cascadeMatrix = uniforms.light.shadow.cascades[0].cascadeSpace; +#ifdef CLOUDS + bool receivedCloudExtinction = false; + float cloudExtinction = min(textureLod(volumetricCloudTexture, texCoords, 0.0).a, 1.0); +#endif + for (int i = 0; i < uniforms.sampleCount; i++) { - float distance = -currentPosition.z; - + float dist = -currentPosition.z; + int cascadeIndex = 0; - - cascadeIndex = distance >= uniforms.light.shadow.cascades[0].distance ? 1 : cascadeIndex; - cascadeIndex = distance >= uniforms.light.shadow.cascades[1].distance ? 2 : cascadeIndex; - cascadeIndex = distance >= uniforms.light.shadow.cascades[2].distance ? 3 : cascadeIndex; - cascadeIndex = distance >= uniforms.light.shadow.cascades[3].distance ? 4 : cascadeIndex; - cascadeIndex = distance >= uniforms.light.shadow.cascades[4].distance ? 5 : cascadeIndex; - + + cascadeIndex = dist >= uniforms.light.shadow.cascades[0].distance ? 1 : cascadeIndex; + cascadeIndex = dist >= uniforms.light.shadow.cascades[1].distance ? 2 : cascadeIndex; + cascadeIndex = dist >= uniforms.light.shadow.cascades[2].distance ? 3 : cascadeIndex; + cascadeIndex = dist >= uniforms.light.shadow.cascades[3].distance ? 4 : cascadeIndex; + cascadeIndex = dist >= uniforms.light.shadow.cascades[4].distance ? 5 : cascadeIndex; + cascadeIndex = min(uniforms.light.shadow.cascadeCount - 1, cascadeIndex); if (lastCascadeIndex != cascadeIndex) { @@ -96,7 +124,7 @@ vec3 ComputeVolumetric(vec3 fragPos, vec2 texCoords) { } lastCascadeIndex = cascadeIndex; - + vec4 cascadeSpace = cascadeMatrix * vec4(currentPosition, 1.0); cascadeSpace.xyz /= cascadeSpace.w; @@ -104,28 +132,61 @@ vec3 ComputeVolumetric(vec3 fragPos, vec2 texCoords) { #ifdef AE_TEXTURE_SHADOW_LOD // This fixes issues that can occur at cascade borders - float shadowValue = textureLod(cascadeMaps, + float shadowValue = textureLod(cascadeMaps, vec4(cascadeSpace.xy, cascadeIndex, cascadeSpace.z), 0); #else - float shadowValue = texture(cascadeMaps, + float shadowValue = texture(cascadeMaps, vec4(cascadeSpace.xy, cascadeIndex, cascadeSpace.z)); #endif + + //shadowValue = distance > uniforms.light.shadow.distance ? 1.0 : shadowValue; + vec3 worldPosition = vec3(globalData.ivMatrix * vec4(currentPosition, 1.0)); + +#ifdef CLOUDS + vec3 planetCenter = uniforms.planetCenterAndRadius.xyz; + float cloudInnerRadius = uniforms.planetCenterAndRadius.w; + + float distToPlanetCenter = distance(worldPosition, planetCenter); +#endif + #ifdef CLOUD_SHADOWS float cloudShadowValue = CalculateCloudShadow(currentPosition, uniforms.cloudShadow, cloudMap); + cloudShadowValue = distToPlanetCenter < cloudInnerRadius ? cloudShadowValue : 1.0; shadowValue = min(shadowValue, cloudShadowValue); #endif - vec3 worldPosition = vec3(globalData.ivMatrix * vec4(currentPosition, 1.0)); - - float fogAmount = uniforms.fogEnabled > 0 ? (1.0 - saturate(ComputeVolumetricFog(uniforms.fog, viewPosition, worldPosition))) : 1.0; + float NdotL = dot(rayDirection, uniforms.light.direction.xyz); - float scattering = uniforms.fogEnabled > 0 ? uniforms.fog.scatteringAnisotropy : 0.0; - foginess += shadowValue * fogAmount * ComputeScattering(scattering, NdotL) * uniforms.light.color.rgb; + float density = uniforms.fog.density * GetVolumetricFogDensity(uniforms.fog, viewPosition, worldPosition); + + float clampedExtinction = max(density, 0.0000001); + float stepExtinction = exp(-density * stepLength); + + float phaseFunction = uniforms.fogEnabled > 0 ? + ComputeScattering(uniforms.fog.scatteringAnisotropy, NdotL) : 1.0; + vec3 stepScattering = density * (shadowValue * phaseFunction * uniforms.light.color.rgb + uniforms.fog.color.rgb); + + vec3 luminanceIntegral = (stepScattering - stepScattering * stepExtinction) / clampedExtinction; +#ifdef CLOUDS + foginess += luminanceIntegral * extinctionWithClouds; + + if (distToPlanetCenter > cloudInnerRadius && !receivedCloudExtinction) { + extinctionWithClouds *= uniforms.fogEnabled > 0 ? stepExtinction * cloudExtinction : 1.0; + receivedCloudExtinction = true; + } + else { + extinctionWithClouds *= uniforms.fogEnabled > 0 ? stepExtinction : 1.0; + } +#else + foginess += luminanceIntegral * extinction; +#endif + + extinction *= uniforms.fogEnabled > 0 ? stepExtinction : 1.0; currentPosition += stepVector; } - return foginess / float(uniforms.sampleCount) * uniforms.intensity; + return vec4(foginess * uniforms.intensity, extinction); } \ No newline at end of file diff --git a/data/shader/volumetric/volumetric.hsh b/data/shader/volumetric/volumetric.hsh new file mode 100644 index 000000000..d9e2897db --- /dev/null +++ b/data/shader/volumetric/volumetric.hsh @@ -0,0 +1,57 @@ +#include +#include <../globals.hsh> + +#define RAYMARCHED_FOG + +vec2 IntersectSphere(vec3 origin, vec3 direction, vec3 pos, float radius) { + + vec3 L = pos - origin; + float DT = dot(L, direction); + float r2 = radius * radius; + + float ct2 = dot(L, L) - DT * DT; + + if (ct2 > r2) + return vec2(-1.0); + + float AT = sqrt(r2 - ct2); + float BT = AT; + + float AO = DT - AT; + float BO = DT + BT; + + float minDist = min(AO, BO); + float maxDist = max(AO, BO); + + return vec2(minDist, maxDist); +} + +vec3 ApplyVolumetrics(Fog fog, vec3 inputColor, vec4 volumetricFog, vec4 volumetricClouds, + vec3 worldViewDirection, vec3 planetCenter, float innerCloudRadius) { + + vec2 intersectDists = IntersectSphere(globalData.cameraLocation.xyz, worldViewDirection, + planetCenter, innerCloudRadius); + +#ifdef RAYMARCHED_FOG + float fogExtinction = volumetricFog.a; +#else + volumetricFog.rgb = fog.color.rgb; + float fogExtinction = 1.0; +#endif + +#ifdef CLOUDS + float alpha = volumetricClouds.a; + fogExtinction = intersectDists.x < 0.0 ? fogExtinction : fogExtinction * alpha; + inputColor = alpha * inputColor; +#endif + + vec3 outputColor = fog.density > 0.0 ? inputColor * fogExtinction + volumetricFog.rgb : inputColor; + +#ifdef CLOUDS + fogExtinction = intersectDists.x < 0.0 ? fogExtinction : 1.0; + outputColor += volumetricClouds.rgb * fogExtinction; +#endif + + return outputColor; + +} \ No newline at end of file diff --git a/data/shader/volumetric/volumetricResolve.csh b/data/shader/volumetric/volumetricResolve.csh index 4066e8399..2d3e9b914 100644 --- a/data/shader/volumetric/volumetricResolve.csh +++ b/data/shader/volumetric/volumetricResolve.csh @@ -4,6 +4,7 @@ #include <../common/flatten.hsh> #include +#include layout (local_size_x = 8, local_size_y = 8) in; @@ -18,6 +19,7 @@ layout(set = 3, binding = 3) uniform sampler2D lowResVolumetricCloudsTexture; layout(set = 3, binding = 5) uniform UniformBuffer { Fog fog; + vec4 planetCenter; int downsampled2x; int cloudsEnabled; int fogEnabled; @@ -28,13 +30,13 @@ layout(set = 3, binding = 5) uniform UniformBuffer { // (localSize / 2 + 2)^2 shared float depths[36]; -shared vec3 volumetrics[36]; +shared vec4 volumetrics[36]; shared vec4 clouds[36]; const uint depthDataSize = (gl_WorkGroupSize.x / 2 + 2) * (gl_WorkGroupSize.y / 2 + 2); const ivec2 unflattenedDepthDataSize = ivec2(gl_WorkGroupSize) / 2 + 2; -vec3 planetCenter = -vec3(0.0, uniforms.planetRadius, 0.0); +vec3 planetCenter = uniforms.planetCenter.xyz; void LoadGroupSharedData() { @@ -46,7 +48,7 @@ void LoadGroupSharedData() { offset += workGroupOffset; offset = clamp(offset, ivec2(0), textureSize(lowResDepthTexture, 0)); depths[gl_LocalInvocationIndex] = texelFetch(lowResDepthTexture, offset, 0).r; - volumetrics[gl_LocalInvocationIndex] = texelFetch(lowResVolumetricTexture, offset, 0).rgb; + volumetrics[gl_LocalInvocationIndex] = texelFetch(lowResVolumetricTexture, offset, 0); #ifdef CLOUDS clouds[gl_LocalInvocationIndex] = texelFetch(lowResVolumetricCloudsTexture, offset, 0); #endif @@ -106,36 +108,13 @@ void Upsample2x(float referenceDepth, vec2 texCoord, out vec4 volumetric, out ve int idx = NearestDepth(referenceDepth, invocationDepths); int offset = Flatten2D(pixel + offsets[idx], unflattenedDepthDataSize); - volumetric = vec4(volumetrics[offset], 1.0); + volumetric = volumetrics[offset]; #ifdef CLOUDS vec4 bilinearCloudScattering = texture(lowResVolumetricCloudsTexture, texCoord); volumetricClouds = mix(clouds[offset], bilinearCloudScattering, minWeight); #endif } -vec2 IntersectSphere(vec3 origin, vec3 direction, vec3 pos, float radius) { - - vec3 L = pos - origin; - float DT = dot(L, direction); - float r2 = radius * radius; - - float ct2 = dot(L, L) - DT * DT; - - if (ct2 > r2) - return vec2(-1.0); - - float AT = sqrt(r2 - ct2); - float BT = AT; - - float AO = DT - AT; - float BO = DT + BT; - - float minDist = min(AO, BO); - float maxDist = max(AO, BO); - - return vec2(minDist, maxDist); -} - void main() { if (uniforms.downsampled2x > 0) LoadGroupSharedData(); @@ -149,59 +128,27 @@ void main() { float depth = texelFetch(depthTexture, pixel, 0).r; - vec4 volumetric; - vec4 cloudScattering; + vec4 volumetricFog; + vec4 volumetricClouds = vec4(0.0); if (uniforms.downsampled2x > 0) { - Upsample2x(depth, texCoord, volumetric, cloudScattering); + Upsample2x(depth, texCoord, volumetricFog, volumetricClouds); } else { - volumetric = vec4(textureLod(lowResVolumetricTexture, texCoord, 0).rgb, 0.0); + volumetricFog = textureLod(lowResVolumetricTexture, texCoord, 0); #ifdef CLOUDS - cloudScattering = texture(lowResVolumetricCloudsTexture, texCoord); + volumetricClouds = texture(lowResVolumetricCloudsTexture, texCoord); #endif } vec3 viewPosition = ConvertDepthToViewSpace(depth, texCoord); - float viewLength = length(viewPosition); vec3 worldDirection = normalize(vec3(globalData.ivMatrix * vec4(viewPosition, 0.0))); - vec2 intersectDists = IntersectSphere(globalData.cameraLocation.xyz, worldDirection, - planetCenter, uniforms.innerCloudRadius); - vec2 planetDists = IntersectSphere(globalData.cameraLocation.xyz, worldDirection, - planetCenter, uniforms.planetRadius); - - // x => first intersection with sphere, y => second intersection with sphere - float cloudFadeout = 1.0; - float cloudDist = intersectDists.y; - float planetDist = planetDists.x < 0.0 ? planetDists.y : planetDists.x; - if (depth == 1.0) { - float maxDist = max(cloudDist, planetDist); - // If we don't hit at all we don't want any fog - viewPosition *= intersectDists.y > 0.0 ? max(maxDist / viewLength, 1.0) : 0.0; - - cloudFadeout = intersectDists.x < 0.0 ? saturate((uniforms.cloudDistanceLimit - - cloudDist) / (uniforms.cloudDistanceLimit)) : cloudFadeout; - } - - vec3 worldPosition = vec3(globalData.ivMatrix * vec4(viewPosition, 1.0)); - - vec4 resolve = imageLoad(resolveImage, pixel); - - float fogAmount = uniforms.fogEnabled > 0 ? saturate(1.0 - ComputeVolumetricFog(uniforms.fog, globalData.cameraLocation.xyz, worldPosition)) : 0.0; + vec3 resolve = imageLoad(resolveImage, pixel).rgb; -#ifdef CLOUDS - if (uniforms.cloudsEnabled > 0) { - cloudScattering.rgb *= cloudFadeout; - cloudScattering.a = mix(1.0, cloudScattering.a, cloudFadeout); - - float alpha = cloudScattering.a; - fogAmount = intersectDists.x < 0.0 ? fogAmount : fogAmount * alpha; - resolve = alpha * resolve + cloudScattering; - } -#endif - resolve = uniforms.fogEnabled > 0 ? mix(resolve, uniforms.fog.color, fogAmount) + volumetric : resolve + volumetric; + resolve = ApplyVolumetrics(uniforms.fog, resolve, volumetricFog, volumetricClouds, + worldDirection, planetCenter, uniforms.innerCloudRadius); - imageStore(resolveImage, pixel, resolve); + imageStore(resolveImage, pixel, vec4(resolve, 1.0)); } \ No newline at end of file diff --git a/libs/ImguiExtension/ImguiWrapper.h b/libs/ImguiExtension/ImguiWrapper.h index 1173958f4..08cb6d77e 100644 --- a/libs/ImguiExtension/ImguiWrapper.h +++ b/libs/ImguiExtension/ImguiWrapper.h @@ -1,5 +1,4 @@ -#ifndef AE_IMGUIWRAPPER_H -#define AE_IMGUIWRAPPER_H +#pragma once // ImguiExtension includes #include @@ -55,5 +54,3 @@ class ImguiWrapper { std::unordered_map imageViewToDescriptorSetMap; }; - -#endif \ No newline at end of file diff --git a/src/demo/App.cpp b/src/demo/App.cpp index 5cdae5c4b..de8ed55a6 100644 --- a/src/demo/App.cpp +++ b/src/demo/App.cpp @@ -43,6 +43,8 @@ void App::LoadContent() { keyboardHandler.speed = cameraSpeed; } }); + + Atlas::PipelineManager::EnableHotReload(); directionalLight = std::make_shared(AE_MOVABLE_LIGHT); directionalLight->direction = glm::vec3(0.0f, -1.0f, 1.0f); @@ -66,8 +68,8 @@ void App::LoadContent() { scene->fog->scatteringAnisotropy = 0.0f; scene->sky.clouds = std::make_shared(); - scene->sky.clouds->minHeight = 100.0f; - scene->sky.clouds->maxHeight = 600.0f; + scene->sky.clouds->minHeight = 1400.0f; + scene->sky.clouds->maxHeight = 1700.0f; scene->sky.clouds->castShadow = false; scene->sky.atmosphere = std::make_shared(); @@ -491,10 +493,12 @@ void App::Render(float deltaTime) { if (ImGui::CollapsingHeader("Clouds")) { ImGui::Checkbox("Enable##Clouds", &clouds->enable); ImGui::Checkbox("Cast shadow##Clouds", &clouds->castShadow); + ImGui::Checkbox("Stochastic occlusion sampling##Clouds", &clouds->stochasticOcclusionSampling); ImGui::Checkbox("Debug##Clouds", &debugClouds); ImGui::Text("Quality"); ImGui::SliderInt("Sample count##Clouds", &clouds->sampleCount, 1, 128); - ImGui::SliderInt("Shadow sample count##Clouds", &clouds->shadowSampleCount, 1, 16); + ImGui::SliderInt("Shadow sample count##Clouds", &clouds->occlusionSampleCount, 1, 16); + ImGui::SliderInt("Shadow sample fraction count##Clouds", &clouds->shadowSampleFraction, 1, 4); ImGui::Text("Shape"); ImGui::SliderFloat("Density multiplier##Clouds", &clouds->densityMultiplier, 0.0f, 1.0f); ImGui::SliderFloat("Height stretch##Clouds", &clouds->heightStretch, 0.0f, 1.0f); @@ -503,7 +507,7 @@ void App::Render(float deltaTime) { } ImGui::Separator(); ImGui::Text("Dimensions"); - ImGui::SliderFloat("Min height##Clouds", &clouds->minHeight, 0.0f, 1000.0f); + ImGui::SliderFloat("Min height##Clouds", &clouds->minHeight, 0.0f, 2000.0f); ImGui::SliderFloat("Max height##Clouds", &clouds->maxHeight, 0.0f, 4000.0f); ImGui::SliderFloat("Distance limit##Clouds", &clouds->distanceLimit, 0.0f, 10000.0f); ImGui::Separator(); @@ -562,15 +566,11 @@ void App::Render(float deltaTime) { auto emissionPowerLabel = "Emission power##" + label; auto transmissionColorLabel = "Transmission color##" + label; - auto emissionPower = glm::max(material->emissiveColor.r, glm::max(material->emissiveColor.g, - glm::max(material->emissiveColor.b, 1.0f))); - material->emissiveColor /= emissionPower; ImGui::Checkbox(twoSidedLabel.c_str(), &material->twoSided); ImGui::ColorEdit3(baseColorLabel.c_str(), glm::value_ptr(material->baseColor)); ImGui::ColorEdit3(emissionColorLabel.c_str(), glm::value_ptr(material->emissiveColor)); - ImGui::SliderFloat(emissionPowerLabel.c_str(), &emissionPower, 1.0f, 10000.0f, + ImGui::SliderFloat(emissionPowerLabel.c_str(), &material->emissiveIntensity, 1.0f, 10000.0f, "%.3f", ImGuiSliderFlags_Logarithmic); - material->emissiveColor *= emissionPower; auto roughnessLabel = "Roughness##" + label; auto metallicLabel = "Metallic##" + label; @@ -777,7 +777,7 @@ bool App::LoadScene() { meshes.reserve(1); glm::mat4 transform = glm::scale(glm::mat4(1.0f), glm::vec3(10.0f)); - auto mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "cornell/CornellBox-Original.obj", ModelLoader::LoadMesh, false, transform, 2048 ); meshes.push_back(mesh); @@ -796,20 +796,22 @@ bool App::LoadScene() { meshes.reserve(1); glm::mat4 transform = glm::scale(glm::mat4(1.0f), glm::vec3(.05f)); - auto mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "sponza/sponza.obj", ModelLoader::LoadMesh, false, transform, 2048 ); meshes.push_back(mesh); transform = glm::scale(glm::mat4(1.0f), glm::vec3(1.f)); - mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( - "metallicwall.gltf", ModelLoader::LoadMesh, false, transform, 2048 + mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( + "metallicwall.gltf", ModelLoader::LoadMesh, Atlas::Mesh::MeshMobility::Movable, + false, transform, 2048 ); meshes.push_back(mesh); transform = glm::scale(glm::mat4(1.0f), glm::vec3(1.f)); - mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( - "chromesphere.gltf", ModelLoader::LoadMesh, false, transform, 2048 + mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( + "chromesphere.gltf", ModelLoader::LoadMesh, Atlas::Mesh::MeshMobility::Movable, + false, transform, 2048 ); meshes.push_back(mesh); @@ -830,7 +832,7 @@ bool App::LoadScene() { meshes.reserve(1); auto transform = glm::scale(glm::mat4(1.0f), glm::vec3(.015f)); - auto mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "bistro/mesh/exterior.obj", ModelLoader::LoadMesh, false, transform, 2048 ); meshes.push_back(mesh); @@ -850,7 +852,7 @@ bool App::LoadScene() { meshes.reserve(1); auto transform = glm::scale(glm::mat4(1.0f), glm::vec3(2.0f)); - auto mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "sanmiguel/san-miguel-low-poly.obj", ModelLoader::LoadMesh, false, transform, 2048 ); meshes.push_back(mesh); @@ -870,7 +872,7 @@ bool App::LoadScene() { else if (sceneSelection == MEDIEVAL) { meshes.reserve(1); - auto mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "medieval/scene.fbx", ModelLoader::LoadMesh, false, glm::mat4(1.0f), 2048 ); meshes.push_back(mesh); @@ -892,7 +894,7 @@ bool App::LoadScene() { meshes.reserve(1); auto transform = glm::rotate(glm::mat4(1.0f), -3.14f / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); - auto mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "pica pica/mesh/scene.gltf", ModelLoader::LoadMesh, false, transform, 2048 ); meshes.push_back(mesh); @@ -911,7 +913,7 @@ bool App::LoadScene() { else if (sceneSelection == SUBWAY) { meshes.reserve(1); - auto mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "subway/scene.gltf", ModelLoader::LoadMesh, false, glm::mat4(1.0f), 2048 ); meshes.push_back(mesh); @@ -931,7 +933,7 @@ bool App::LoadScene() { meshes.reserve(1); auto transform = glm::scale(glm::vec3(8.0f)); - auto mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "material demo/materials.obj", ModelLoader::LoadMesh, false, transform, 2048 ); meshes.push_back(mesh); @@ -974,19 +976,19 @@ bool App::LoadScene() { meshes.reserve(4); auto transform = glm::mat4(glm::scale(glm::mat4(1.0f), glm::vec3(4.0f))); - auto mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "newsponza/main/NewSponza_Main_Blender_glTF.gltf", ModelLoader::LoadMesh, false, transform, 2048 ); meshes.push_back(mesh); - mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "newsponza/candles/NewSponza_100sOfCandles_glTF_OmniLights.gltf", ModelLoader::LoadMesh, false, transform, 2048 ); meshes.push_back(mesh); - mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "newsponza/curtains/NewSponza_Curtains_glTF.gltf", ModelLoader::LoadMesh, false, transform, 2048 ); meshes.push_back(mesh); - mesh = Atlas::ResourceManager::GetResourceWithLoaderAsync( + mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( "newsponza/ivy/NewSponza_IvyGrowth_glTF.gltf", ModelLoader::LoadMesh, false, transform, 2048 ); meshes.push_back(mesh); @@ -1141,7 +1143,7 @@ void App::CheckLoadScene() { scene->irradianceVolume->SetRayCount(128, 32); } else if (sceneSelection == PICAPICA) { - for (auto& material : meshes.front()->data.materials) material.twoSided = false; + for (auto& material : meshes.front()->data.materials) material->twoSided = false; scene->irradianceVolume = std::make_shared( sceneAABB.Scale(1.0f), glm::ivec3(20)); diff --git a/src/demo/App.h b/src/demo/App.h index bba14d1db..835d753a6 100644 --- a/src/demo/App.h +++ b/src/demo/App.h @@ -1,5 +1,4 @@ -#ifndef AE_APP_H -#define AE_APP_H +#pragma once #include #include @@ -96,6 +95,4 @@ class App : public Atlas::EngineInstance { ImguiWrapper imguiWrapper; -}; - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/src/demo/CMakeLists.txt b/src/demo/CMakeLists.txt index c6821cd4e..32d42279b 100644 --- a/src/demo/CMakeLists.txt +++ b/src/demo/CMakeLists.txt @@ -1,4 +1,5 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.24) + project(AtlasEngineDemo) # Note: For this project, the root CMakeLists.txt turns @@ -33,6 +34,7 @@ endforeach() if (UNIX AND NOT APPLE AND NOT ANDROID) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'") endif() + # We use the exported main file from the AtlasEngine library to able to use # the app class. Alternatively, you can write a main function yourself. # To export the main file the ATLAS_EXPORT_MAIN option has to be turned on. diff --git a/src/engine/App.h b/src/engine/App.h index d871833ee..28ddf6b80 100644 --- a/src/engine/App.h +++ b/src/engine/App.h @@ -1,5 +1,4 @@ -#ifndef AE_APP_H -#define AE_APP_H +#pragma once #include #include @@ -22,6 +21,4 @@ class App : public Atlas::EngineInstance { virtual void Render(float deltaTime) final; -}; - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index 9c5c4e4ed..cbc11d5e4 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -3,7 +3,7 @@ if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) endif() project(AtlasEngine) -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.24) # Validate options ################################################################################ if (ATLAS_NO_APP AND ATLAS_EXPORT_MAIN) @@ -40,7 +40,7 @@ if(WIN32) set(ATLAS_ENGINE_COMPILE_DEFINITIONS ${ATLAS_ENGINE_COMPILE_DEFINITIONS} AE_OS_WINDOWS) set(ATLAS_ENGINE_LIBS assimp::assimp SDL2::SDL2 SDL2::SDL2main SPIRV-Tools-opt - volk::volk volk::volk_headers unofficial::vulkan-memory-allocator::vulkan-memory-allocator + volk::volk volk::volk_headers GPUOpen::VulkanMemoryAllocator unofficial::spirv-reflect::spirv-reflect glslang::SPIRV) endif() @@ -53,7 +53,7 @@ if(APPLE) set(ATLAS_ENGINE_LIBS assimp::assimp SDL2::SDL2 SDL2::SDL2main SDL2::SDL2-static volk::volk volk::volk_headers SPIRV-Tools-opt - unofficial::vulkan-memory-allocator::vulkan-memory-allocator + GPUOpen::VulkanMemoryAllocator Vulkan::Vulkan unofficial::spirv-reflect::spirv-reflect glslang::SPIRV) endif() @@ -76,7 +76,7 @@ if(UNIX AND NOT APPLE AND NOT ANDROID) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) set(ATLAS_ENGINE_LIBS assimp::assimp SDL2::SDL2 SDL2::SDL2main SDL2::SDL2-static - volk::volk volk::volk_headers unofficial::vulkan-memory-allocator::vulkan-memory-allocator + volk::volk volk::volk_headers GPUOpen::VulkanMemoryAllocator unofficial::spirv-reflect::spirv-reflect SPIRV-Tools-opt glslang::SPIRV) endif() diff --git a/src/engine/Camera.h b/src/engine/Camera.h index 2eeb3b6ca..fb07b10b3 100644 --- a/src/engine/Camera.h +++ b/src/engine/Camera.h @@ -1,5 +1,4 @@ -#ifndef AE_CAMERA_H -#define AE_CAMERA_H +#pragma once #include "System.h" #include "volume/Frustum.h" @@ -139,6 +138,4 @@ namespace Atlas { }; -} - -#endif +} \ No newline at end of file diff --git a/src/engine/Clock.h b/src/engine/Clock.h index 4a5721b68..a59bf66ad 100644 --- a/src/engine/Clock.h +++ b/src/engine/Clock.h @@ -1,5 +1,4 @@ -#ifndef AE_TIME_H -#define AE_TIME_H +#pragma once #include "System.h" @@ -63,6 +62,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/Decal.h b/src/engine/Decal.h index 9d806cf80..22636b259 100644 --- a/src/engine/Decal.h +++ b/src/engine/Decal.h @@ -1,5 +1,4 @@ -#ifndef AE_DECAL_H -#define AE_DECAL_H +#pragma once #include "System.h" #include "texture/Texture2D.h" @@ -22,6 +21,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/Display.h b/src/engine/Display.h index 3ddb1cfd2..e05f95846 100644 --- a/src/engine/Display.h +++ b/src/engine/Display.h @@ -1,5 +1,4 @@ -#ifndef AE_DISPLAY_H -#define AE_DISPLAY_H +#pragma once #include "System.h" @@ -45,6 +44,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index f6ca28620..8e3323643 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -40,7 +40,7 @@ namespace Atlas { #ifdef AE_BUILDTYPE_RELEASE Graphics::Instance::DefaultInstance = new Graphics::Instance("AtlasEngineInstance", false); #else - Graphics::Instance::DefaultInstance = new Graphics::Instance("AtlasEngineInstance", true); + Graphics::Instance::DefaultInstance = new Graphics::Instance("AtlasEngineInstance", false); #endif // Initialize window surface DefaultWindow->CreateSurface(); diff --git a/src/engine/Engine.h b/src/engine/Engine.h index a04a63bfb..b31108c8f 100644 --- a/src/engine/Engine.h +++ b/src/engine/Engine.h @@ -1,5 +1,4 @@ -#ifndef AE_ENGINE_H -#define AE_ENGINE_H +#pragma once #include "System.h" #include "Window.h" @@ -63,6 +62,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/EngineInstance.h b/src/engine/EngineInstance.h index 3cf33d73a..d25f5ff9d 100644 --- a/src/engine/EngineInstance.h +++ b/src/engine/EngineInstance.h @@ -1,5 +1,4 @@ -#ifndef AE_ENGINEINSTANCE_H -#define AE_ENGINEINSTANCE_H +#pragma once #include "Engine.h" @@ -104,7 +103,4 @@ namespace Atlas { }; -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/Filter.h b/src/engine/Filter.h index 93d40c2d7..ca02f5e3c 100644 --- a/src/engine/Filter.h +++ b/src/engine/Filter.h @@ -1,5 +1,4 @@ -#ifndef AE_KERNEL_H -#define AE_KERNEL_H +#pragma once #include "System.h" #include @@ -91,6 +90,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/Font.h b/src/engine/Font.h index fd6ad4226..50004db10 100644 --- a/src/engine/Font.h +++ b/src/engine/Font.h @@ -1,5 +1,4 @@ -#ifndef AE_FONT_H -#define AE_FONT_H +#pragma once #include "System.h" #include "texture/Texture2DArray.h" @@ -112,6 +111,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/Log.h b/src/engine/Log.h index 2c2ac7fde..dd6778c45 100644 --- a/src/engine/Log.h +++ b/src/engine/Log.h @@ -1,5 +1,4 @@ -#ifndef AE_LOG_H -#define AE_LOG_H +#pragma once #include "System.h" @@ -73,6 +72,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/Material.h b/src/engine/Material.h index a187c4eca..ce518e57d 100644 --- a/src/engine/Material.h +++ b/src/engine/Material.h @@ -1,5 +1,4 @@ -#ifndef AE_MATERIAL_H -#define AE_MATERIAL_H +#pragma once #include "System.h" #include "texture/Texture2D.h" @@ -37,9 +36,11 @@ namespace Atlas { Ref displacementMap = nullptr; vec3 baseColor = vec3(1.0f); - vec3 emissiveColor = vec3(0.0f); vec3 transmissiveColor = vec3(0.0f); + vec3 emissiveColor = vec3(0.0f); + float emissiveIntensity = 1.0f; + float opacity = 1.0f; float roughness = 1.0f; @@ -70,6 +71,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/RenderList.cpp b/src/engine/RenderList.cpp index a8f2b9624..d99610ecb 100644 --- a/src/engine/RenderList.cpp +++ b/src/engine/RenderList.cpp @@ -188,7 +188,7 @@ namespace Atlas { if (!currentMatricesBuffer || currentMatricesBuffer->size < sizeof(mat3x4) * currentActorMatrices.size()) { auto newSize = currentMatricesBuffer != nullptr ? currentMatricesBuffer->size * 2 : sizeof(mat3x4) * currentActorMatrices.size(); - newSize = std::max(newSize, size_t(1)); + newSize = std::max(std::max(newSize, size_t(1)), sizeof(mat3x4) * currentActorMatrices.size()); auto bufferDesc = Graphics::BufferDesc { .usageFlags = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, .domain = Graphics::BufferDomain::Host, @@ -201,7 +201,7 @@ namespace Atlas { if (!impostorMatricesBuffer || impostorMatricesBuffer->size < sizeof(mat3x4) * impostorMatrices.size()) { auto newSize = impostorMatricesBuffer != nullptr ? impostorMatricesBuffer->size * 2 : sizeof(mat3x4) * impostorMatrices.size(); - newSize = std::max(newSize, size_t(1)); + newSize = std::max(std::max(newSize, size_t(1)), sizeof(mat3x4) * impostorMatrices.size()); auto bufferDesc = Graphics::BufferDesc { .usageFlags = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, .domain = Graphics::BufferDomain::Host, diff --git a/src/engine/RenderList.h b/src/engine/RenderList.h index 0450b2412..a7214de9d 100644 --- a/src/engine/RenderList.h +++ b/src/engine/RenderList.h @@ -1,5 +1,4 @@ -#ifndef AE_RENDERLIST_H -#define AE_RENDERLIST_H +#pragma once #include "System.h" #include "actor/MeshActor.h" @@ -70,6 +69,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/RenderTarget.cpp b/src/engine/RenderTarget.cpp index 166f07a78..37737ba15 100644 --- a/src/engine/RenderTarget.cpp +++ b/src/engine/RenderTarget.cpp @@ -25,6 +25,11 @@ namespace Atlas { postProcessTexture = Texture::Texture2D(width, height, VK_FORMAT_R8G8B8A8_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + oceanDepthTexture = Texture::Texture2D(width, height, VK_FORMAT_D32_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest); + oceanStencilTexture = Texture::Texture2D(width, height, VK_FORMAT_R8_UINT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + { Graphics::RenderPassColorAttachment colorAttachments[] = { {.imageFormat = targetData.baseColorTexture->format}, @@ -82,6 +87,28 @@ namespace Atlas { }; lightingRenderPass = graphicsDevice->CreateRenderPass(lightingRenderPassDesc); } + { + Graphics::RenderPassColorAttachment colorAttachments[] = { + {.imageFormat = oceanStencilTexture.format} + }; + for (auto &attachment: colorAttachments) { + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + Graphics::RenderPassDepthAttachment depthAttachment = { + .imageFormat = oceanDepthTexture.format, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + }; + + auto oceanRenderPassDesc = Graphics::RenderPassDesc { + .colorAttachments = {colorAttachments[0]}, + .depthAttachment = depthAttachment + }; + oceanRenderPass = graphicsDevice->CreateRenderPass(oceanRenderPassDesc); + } CreateFrameBuffers(); @@ -338,6 +365,16 @@ namespace Atlas { }; lightingFrameBufferWithStencil = graphicsDevice->CreateFrameBuffer(lightingFrameBufferDesc); + auto oceanDepthOnlyFrameBufferDesc = Graphics::FrameBufferDesc{ + .renderPass = oceanRenderPass, + .colorAttachments = { + {oceanStencilTexture.image, 0, true}, + }, + .depthAttachment = {oceanDepthTexture.image, 0, true}, + .extent = {uint32_t(width), uint32_t(height)} + }; + oceanDepthOnlyFrameBuffer = graphicsDevice->CreateFrameBuffer(oceanDepthOnlyFrameBufferDesc); + } } \ No newline at end of file diff --git a/src/engine/RenderTarget.h b/src/engine/RenderTarget.h index d68b11a76..aeb027f97 100644 --- a/src/engine/RenderTarget.h +++ b/src/engine/RenderTarget.h @@ -1,5 +1,4 @@ -#ifndef AE_RENDERTARGET_H -#define AE_RENDERTARGET_H +#pragma once #include "System.h" #include "texture/Texture2D.h" @@ -168,6 +167,9 @@ namespace Atlas { Ref lightingFrameBuffer; Ref lightingFrameBufferWithStencil; + Ref oceanRenderPass; + Ref oceanDepthOnlyFrameBuffer; + Texture::Texture2D postProcessTexture; Texture::Texture2D aoTexture; @@ -178,6 +180,9 @@ namespace Atlas { Texture::Texture2D sssTexture; + Texture::Texture2D oceanDepthTexture; + Texture::Texture2D oceanStencilTexture; + Texture::Texture2D volumetricTexture; Texture::Texture2D swapVolumetricTexture; @@ -216,7 +221,4 @@ namespace Atlas { }; -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/System.h b/src/engine/System.h index 659aff0ed..9376d4ed4 100644 --- a/src/engine/System.h +++ b/src/engine/System.h @@ -1,5 +1,4 @@ -#ifndef AE_SYSTEM_H -#define AE_SYSTEM_H +#pragma once #define _CRT_SECURE_NO_WARNINGS @@ -63,6 +62,4 @@ namespace Atlas { typedef short float16; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/Viewport.h b/src/engine/Viewport.h index 8e84caa5d..86ea4f255 100644 --- a/src/engine/Viewport.h +++ b/src/engine/Viewport.h @@ -1,5 +1,4 @@ -#ifndef AE_VIEWPORT_H -#define AE_VIEWPORT_H +#pragma once #include "System.h" #include "Camera.h" @@ -57,6 +56,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/Window.h b/src/engine/Window.h index 3f84f4b91..69a7b2401 100644 --- a/src/engine/Window.h +++ b/src/engine/Window.h @@ -1,5 +1,4 @@ -#ifndef WINDOW_H -#define WINDOW_H +#pragma once #include "System.h" #include "Viewport.h" @@ -213,6 +212,4 @@ namespace Atlas { }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/actor/Actor.h b/src/engine/actor/Actor.h index 2b1fe7bb2..793441d20 100644 --- a/src/engine/actor/Actor.h +++ b/src/engine/actor/Actor.h @@ -1,5 +1,4 @@ -#ifndef AE_ACTOR_H -#define AE_ACTOR_H +#pragma once #include "../System.h" #include "../Camera.h" @@ -64,7 +63,4 @@ namespace Atlas { } -} - - -#endif +} \ No newline at end of file diff --git a/src/engine/actor/AudioActor.h b/src/engine/actor/AudioActor.h index 528a7394c..4505b5272 100644 --- a/src/engine/actor/AudioActor.h +++ b/src/engine/actor/AudioActor.h @@ -1,5 +1,4 @@ -#ifndef AE_AUDIOACTOR_H -#define AE_AUDIOACTOR_H +#pragma once #include "../System.h" #include "../audio/AudioStream.h" @@ -45,6 +44,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/actor/DecalActor.h b/src/engine/actor/DecalActor.h index b6819fe62..d4edacae1 100644 --- a/src/engine/actor/DecalActor.h +++ b/src/engine/actor/DecalActor.h @@ -1,5 +1,4 @@ -#ifndef AE_DECALACTOR_H -#define AE_DECALACTOR_H +#pragma once #include "Actor.h" #include "../Decal.h" @@ -26,6 +25,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/actor/MeshActor.h b/src/engine/actor/MeshActor.h index efd87cc56..8db8551df 100644 --- a/src/engine/actor/MeshActor.h +++ b/src/engine/actor/MeshActor.h @@ -1,5 +1,4 @@ -#ifndef AE_MESHACTOR_H -#define AE_MESHACTOR_H +#pragma once #include "System.h" #include "Actor.h" @@ -27,6 +26,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/actor/MovableMeshActor.h b/src/engine/actor/MovableMeshActor.h index f294b023a..bbe9cfd27 100644 --- a/src/engine/actor/MovableMeshActor.h +++ b/src/engine/actor/MovableMeshActor.h @@ -1,5 +1,4 @@ -#ifndef AE_MOVABLEMESHACTOR_H -#define AE_MOVABLEMESHACTOR_H +#pragma once #include "MeshActor.h" @@ -19,6 +18,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/actor/StaticMeshActor.h b/src/engine/actor/StaticMeshActor.h index 206ac5700..a03497380 100644 --- a/src/engine/actor/StaticMeshActor.h +++ b/src/engine/actor/StaticMeshActor.h @@ -1,5 +1,4 @@ -#ifndef AE_STATICMESHACTOR_H -#define AE_STATICMESHACTOR_H +#pragma once #include "MeshActor.h" @@ -20,6 +19,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/actor/VegetationActor.h b/src/engine/actor/VegetationActor.h index afbbfb15c..19b061e96 100644 --- a/src/engine/actor/VegetationActor.h +++ b/src/engine/actor/VegetationActor.h @@ -1,5 +1,4 @@ -#ifndef AE_VEGETATIONACTOR_H -#define AE_VEGETATIONACTOR_H +#pragma once #include "../System.h" #include "../Camera.h" @@ -30,7 +29,4 @@ namespace Atlas { } -} - - -#endif +} \ No newline at end of file diff --git a/src/engine/audio/AudioData.h b/src/engine/audio/AudioData.h index 54066b40f..2447520e0 100644 --- a/src/engine/audio/AudioData.h +++ b/src/engine/audio/AudioData.h @@ -1,5 +1,4 @@ -#ifndef AE_AUDIODATA_H -#define AE_AUDIODATA_H +#pragma once #include "../System.h" @@ -41,6 +40,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/audio/AudioManager.h b/src/engine/audio/AudioManager.h index 3d738aa1e..698c308d0 100644 --- a/src/engine/audio/AudioManager.h +++ b/src/engine/audio/AudioManager.h @@ -1,5 +1,4 @@ -#ifndef AE_AUDIOMANAGER_H -#define AE_AUDIOMANAGER_H +#pragma once #include "../System.h" @@ -48,7 +47,4 @@ namespace Atlas { } -} - - -#endif +} \ No newline at end of file diff --git a/src/engine/audio/AudioStream.h b/src/engine/audio/AudioStream.h index 52fe0e776..618c1abac 100644 --- a/src/engine/audio/AudioStream.h +++ b/src/engine/audio/AudioStream.h @@ -1,5 +1,4 @@ -#ifndef AE_AUDIOSTREAM_H -#define AE_AUDIOSTREAM_H +#pragma once #include "../System.h" @@ -62,6 +61,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/buffer/Buffer.h b/src/engine/buffer/Buffer.h index 1040cdacc..7c87a83d3 100644 --- a/src/engine/buffer/Buffer.h +++ b/src/engine/buffer/Buffer.h @@ -1,5 +1,4 @@ -#ifndef AE_BUFFER_H -#define AE_BUFFER_H +#pragma once #include "../System.h" @@ -162,6 +161,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/buffer/IndexBuffer.cpp b/src/engine/buffer/IndexBuffer.cpp index ef7e44465..3b36f5706 100644 --- a/src/engine/buffer/IndexBuffer.cpp +++ b/src/engine/buffer/IndexBuffer.cpp @@ -6,7 +6,8 @@ namespace Atlas { namespace Buffer { - IndexBuffer::IndexBuffer(VkIndexType type, size_t elementCount, void* data) : type(type) { + IndexBuffer::IndexBuffer(VkIndexType type, size_t elementCount, void* data, bool hostAccess) + : type(type), hostAccessible(hostAccess) { switch(type) { case VK_INDEX_TYPE_UINT16: elementSize = 2; break; @@ -51,7 +52,7 @@ namespace Atlas { Graphics::BufferDesc desc { .usageFlags = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - .domain = Graphics::BufferDomain::Device, + .domain = hostAccessible ? Graphics::BufferDomain::Host : Graphics::BufferDomain::Device, .data = data, .size = sizeInBytes }; diff --git a/src/engine/buffer/IndexBuffer.h b/src/engine/buffer/IndexBuffer.h index 0f8b2acef..61aec4ea3 100644 --- a/src/engine/buffer/IndexBuffer.h +++ b/src/engine/buffer/IndexBuffer.h @@ -1,5 +1,4 @@ -#ifndef AE_INDEXBUFFER_H -#define AE_INDEXBUFFER_H +#pragma once #include "../System.h" #include "Buffer.h" @@ -23,7 +22,8 @@ namespace Atlas { * @param elementCount The number of elements in the vertex buffer will be filled with * @param data Optional parameter for directly filling the buffer with data */ - IndexBuffer(VkIndexType type, size_t elementCount, void* data = nullptr); + IndexBuffer(VkIndexType type, size_t elementCount, void* data = nullptr, + bool hostAccess = false); /** * Sets the size of the buffer @@ -46,6 +46,8 @@ namespace Atlas { size_t elementCount = 0; size_t elementSize = 0; + bool hostAccessible = false; + private: void Reallocate(void* data); @@ -53,6 +55,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/buffer/UniformBuffer.cpp b/src/engine/buffer/UniformBuffer.cpp index f9193b5ed..2815c358d 100644 --- a/src/engine/buffer/UniformBuffer.cpp +++ b/src/engine/buffer/UniformBuffer.cpp @@ -37,6 +37,12 @@ namespace Atlas { buffer->SetData(data, offset * Graphics::Buffer::GetAlignedSize(elementSize), elementSize); } + + void UniformBuffer::SetData(void *data, size_t offset, size_t length) { + + buffer->SetData(data, offset, length); + + } Ref UniformBuffer::Get() { @@ -85,4 +91,4 @@ namespace Atlas { } -} \ No newline at end of file +} diff --git a/src/engine/buffer/UniformBuffer.h b/src/engine/buffer/UniformBuffer.h index 9cf618a9f..8eef27d15 100644 --- a/src/engine/buffer/UniformBuffer.h +++ b/src/engine/buffer/UniformBuffer.h @@ -1,5 +1,4 @@ -#ifndef AE_UNIFORMBUFFER_H -#define AE_UNIFORMBUFFER_H +#pragma once #include "../System.h" @@ -46,6 +45,15 @@ namespace Atlas { * @param offset The offset in the buffer in elements (not bytes). */ void SetData(void* data, size_t offset); + + /** + * Sets the data of a buffer if it isn't mapped. + * @param data A pointer to the data. + * @param offset The offset in the buffer in bytes. + * @param length The length in bytes + * @note This method exists in case we need only partial upload of an element (in case of an array) + */ + void SetData(void* data, size_t offset, size_t length); /** * Returns an owning pointer to a graphics multi buffer @@ -92,6 +100,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/buffer/VertexArray.cpp b/src/engine/buffer/VertexArray.cpp index d8745e262..5af0f609f 100644 --- a/src/engine/buffer/VertexArray.cpp +++ b/src/engine/buffer/VertexArray.cpp @@ -62,8 +62,24 @@ namespace Atlas { commandList->BindIndexBuffer(indexComponent.buffer, indexComponent.type); } + // Bind in batches to reduce driver binding overhead + uint32_t bindingOffset = 0; + std::vector> buffers; for (auto& [attribArray, vertexComponent] : vertexComponents) { - commandList->BindVertexBuffer(vertexComponent.vertexBuffer.buffer, attribArray); + if (attribArray != bindingOffset + 1 && buffers.size() > 0) { + uint32_t bindingCount = uint32_t(buffers.size()); + commandList->BindVertexBuffers(buffers, bindingOffset, bindingCount); + + buffers.clear(); + bindingOffset = attribArray; + } + + buffers.push_back(vertexComponent.vertexBuffer.buffer); + } + + if (buffers.size()) { + uint32_t bindingCount = uint32_t(buffers.size()); + commandList->BindVertexBuffers(buffers, bindingOffset, bindingCount); } } diff --git a/src/engine/buffer/VertexArray.h b/src/engine/buffer/VertexArray.h index 3771fb905..b0428ef76 100644 --- a/src/engine/buffer/VertexArray.h +++ b/src/engine/buffer/VertexArray.h @@ -1,5 +1,4 @@ -#ifndef AE_VERTEXARRAY_H -#define AE_VERTEXARRAY_H +#pragma once #include "../System.h" #include "IndexBuffer.h" @@ -76,6 +75,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/buffer/VertexBuffer.cpp b/src/engine/buffer/VertexBuffer.cpp index c425a83c6..efb7faf8e 100644 --- a/src/engine/buffer/VertexBuffer.cpp +++ b/src/engine/buffer/VertexBuffer.cpp @@ -8,8 +8,8 @@ namespace Atlas { namespace Buffer { - VertexBuffer::VertexBuffer(VkFormat format, size_t elementCount, void* data) - : format(format), elementSize(Graphics::GetFormatSize(format)) { + VertexBuffer::VertexBuffer(VkFormat format, size_t elementCount, void* data, bool hostAccess) + : format(format), elementSize(Graphics::GetFormatSize(format)), hostAccessible(hostAccess) { if (elementCount) { SetSize(elementCount, data); @@ -48,7 +48,7 @@ namespace Atlas { Graphics::BufferDesc desc { .usageFlags = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - .domain = Graphics::BufferDomain::Device, + .domain = hostAccessible ? Graphics::BufferDomain::Host : Graphics::BufferDomain::Device, .data = data, .size = sizeInBytes }; diff --git a/src/engine/buffer/VertexBuffer.h b/src/engine/buffer/VertexBuffer.h index ad78f3cdd..dd0dcf28a 100644 --- a/src/engine/buffer/VertexBuffer.h +++ b/src/engine/buffer/VertexBuffer.h @@ -1,5 +1,4 @@ -#ifndef AE_VERTEXBUFFER_H -#define AE_VERTEXBUFFER_H +#pragma once #include "../System.h" #include "Buffer.h" @@ -26,7 +25,8 @@ namespace Atlas { * @param elementCount The number of elements in the vertex buffer will be filled with * @param data Optional parameter for directly filling the buffer with data */ - VertexBuffer(VkFormat format, size_t elementCount, void* data = nullptr); + VertexBuffer(VkFormat format, size_t elementCount, void* data = nullptr, + bool hostAccess = false); /** * Sets the size of the buffer @@ -49,6 +49,8 @@ namespace Atlas { size_t elementCount = 0; size_t elementSize = 0; + bool hostAccessible = false; + private: void Reallocate(void* data); @@ -56,6 +58,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/common/ColorConverter.h b/src/engine/common/ColorConverter.h new file mode 100644 index 000000000..db03a5c38 --- /dev/null +++ b/src/engine/common/ColorConverter.h @@ -0,0 +1,57 @@ +#pragma once + +#include "../System.h" + +namespace Atlas { + + namespace Common { + + namespace ColorConverter { + + static inline vec2 ConvertSRGBToLinear(vec2 color) { + + const float gamma = 2.2f; + return glm::pow(color, vec2(gamma)); + + } + + static inline vec3 ConvertSRGBToLinear(vec3 color) { + + const float gamma = 2.2f; + return glm::pow(color, vec3(gamma)); + + } + + static inline vec4 ConvertSRGBToLinear(vec4 color) { + + const float gamma = 2.2f; + return vec4(glm::pow(vec3(color), vec3(gamma)), color.a); + + } + + static inline vec2 ConvertLinearToSRGB(vec2 color) { + + const float gamma = 1.0f / 2.2f; + return glm::pow(color, vec2(gamma)); + + } + + static inline vec3 ConvertLinearToSRGB(vec3 color) { + + const float gamma = 1.0f / 2.2f; + return glm::pow(color, vec3(gamma)); + + } + + static inline vec4 ConvertLinearToSRGB(vec4 color) { + + const float gamma = 1.0f / 2.2f; + return vec4(glm::pow(vec3(color), vec3(gamma)), color.a); + + } + + } + + } + +} \ No newline at end of file diff --git a/src/engine/common/Hash.h b/src/engine/common/Hash.h index b8eb48ad9..bb62edcb5 100644 --- a/src/engine/common/Hash.h +++ b/src/engine/common/Hash.h @@ -1,17 +1,16 @@ -#ifndef AE_HASH_H -#define AE_HASH_H +#pragma once #include #include namespace Atlas { + using Hash = size_t; + template - inline void HashCombine(size_t& hash, const T& v) { + inline void HashCombine(Hash& hash, const T& v) { std::hash hasher; hash ^= hasher(v) + 0x9e3779b9 + (hash << 6) + (hash >> 2); } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/common/Image.h b/src/engine/common/Image.h index 1248130a3..2cc4c6add 100644 --- a/src/engine/common/Image.h +++ b/src/engine/common/Image.h @@ -1,5 +1,4 @@ -#ifndef AE_IMAGE_H -#define AE_IMAGE_H +#pragma once #include "../System.h" #include "../Filter.h" @@ -853,6 +852,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/common/MathHelper.h b/src/engine/common/MathHelper.h index 4c5ce7ee0..a24d172c7 100644 --- a/src/engine/common/MathHelper.h +++ b/src/engine/common/MathHelper.h @@ -1,3 +1,5 @@ +#pragma once + #include #include diff --git a/src/engine/common/MatrixDecomposition.h b/src/engine/common/MatrixDecomposition.h index bf39e4089..414c41c1f 100644 --- a/src/engine/common/MatrixDecomposition.h +++ b/src/engine/common/MatrixDecomposition.h @@ -1,5 +1,4 @@ -#ifndef AE_MATRIXDECOMPOSITION_H -#define AE_MATRIXDECOMPOSITION_H +#pragma once #include "../System.h" @@ -26,8 +25,4 @@ namespace Atlas { } -} - - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/common/NoiseGenerator.h b/src/engine/common/NoiseGenerator.h index 77c40b927..adaa4557d 100644 --- a/src/engine/common/NoiseGenerator.h +++ b/src/engine/common/NoiseGenerator.h @@ -1,5 +1,4 @@ -#ifndef AE_NOISEGENERATOR_H -#define AE_NOISEGENERATOR_H +#pragma once #include "../System.h" #include "../texture/Texture2D.h" @@ -82,6 +81,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/common/Packing.h b/src/engine/common/Packing.h index 3b240dde7..7167e9be5 100644 --- a/src/engine/common/Packing.h +++ b/src/engine/common/Packing.h @@ -1,5 +1,4 @@ -#ifndef AE_PACKING_H -#define AE_PACKING_H +#pragma once #include "../System.h" @@ -61,6 +60,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/common/Path.h b/src/engine/common/Path.h index ccef5f833..f880418ca 100644 --- a/src/engine/common/Path.h +++ b/src/engine/common/Path.h @@ -1,5 +1,4 @@ -#ifndef AE_PATH_H -#define AE_PATH_H +#pragma once #include "../System.h" @@ -73,6 +72,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/common/Piecewise.h b/src/engine/common/Piecewise.h index 809bf195e..a12ee43ac 100644 --- a/src/engine/common/Piecewise.h +++ b/src/engine/common/Piecewise.h @@ -1,5 +1,4 @@ -#ifndef AE_PIECEWISE_H -#define AE_PIECEWISE_H +#pragma once #include "../System.h" @@ -78,7 +77,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/common/RandomHelper.h b/src/engine/common/RandomHelper.h index e124858d1..22bff77aa 100644 --- a/src/engine/common/RandomHelper.h +++ b/src/engine/common/RandomHelper.h @@ -1,5 +1,4 @@ -#ifndef AE_RANDOM_H -#define AE_RANDOM_H +#pragma once #include @@ -29,6 +28,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/common/Ref.h b/src/engine/common/Ref.h index eda51bfd6..c8ac26632 100644 --- a/src/engine/common/Ref.h +++ b/src/engine/common/Ref.h @@ -1,5 +1,4 @@ -#ifndef AE_REF_H -#define AE_REF_H +#pragma once #include @@ -29,6 +28,4 @@ namespace Atlas { return std::make_shared(*t); } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/ecs/Entity.h b/src/engine/ecs/Entity.h index d34a81e0a..f458869f9 100644 --- a/src/engine/ecs/Entity.h +++ b/src/engine/ecs/Entity.h @@ -1,5 +1,4 @@ -#ifndef AE_ECSENTITY_H -#define AE_ECSENTITY_H +#pragma once #include @@ -34,6 +33,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/ecs/EntityManager.h b/src/engine/ecs/EntityManager.h index 21b48e25d..080d4a0db 100644 --- a/src/engine/ecs/EntityManager.h +++ b/src/engine/ecs/EntityManager.h @@ -1,5 +1,4 @@ -#ifndef AE_ECSENTITYMANAGER_H -#define AE_ECSENTITYMANAGER_H +#pragma once #include "Entity.h" #include "Pools.h" @@ -173,6 +172,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/ecs/Pool.h b/src/engine/ecs/Pool.h index 940a389fa..a0ea5b845 100644 --- a/src/engine/ecs/Pool.h +++ b/src/engine/ecs/Pool.h @@ -1,5 +1,4 @@ -#ifndef AE_ECSPOOL_H -#define AE_ECSPOOL_H +#pragma once #include "Storage.h" @@ -59,6 +58,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/ecs/Pools.h b/src/engine/ecs/Pools.h index 8585b0271..504e79a2a 100644 --- a/src/engine/ecs/Pools.h +++ b/src/engine/ecs/Pools.h @@ -1,5 +1,4 @@ -#ifndef AE_ECSPOOLS_H -#define AE_ECSPOOLS_H +#pragma once #include "Pool.h" #include "TypeIndex.h" @@ -56,6 +55,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/ecs/Storage.h b/src/engine/ecs/Storage.h index fd6e1ba0d..c62117aae 100644 --- a/src/engine/ecs/Storage.h +++ b/src/engine/ecs/Storage.h @@ -1,5 +1,4 @@ -#ifndef AE_ECSSTORAGE_H -#define AE_ECSSTORAGE_H +#pragma once #include "../System.h" #include "Entity.h" @@ -96,6 +95,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/ecs/Subset.h b/src/engine/ecs/Subset.h index fdb62fd27..fb8703bea 100644 --- a/src/engine/ecs/Subset.h +++ b/src/engine/ecs/Subset.h @@ -1,5 +1,4 @@ -#ifndef AE_ECSSUBSET_H -#define AE_ECSSUBSET_H +#pragma once #include "Pool.h" @@ -149,6 +148,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/ecs/TypeIndex.h b/src/engine/ecs/TypeIndex.h index dd74dbf8c..de6ff5dc4 100644 --- a/src/engine/ecs/TypeIndex.h +++ b/src/engine/ecs/TypeIndex.h @@ -1,5 +1,4 @@ -#ifndef AE_ECSTYPEINDEX_H -#define AE_ECSTYPEINDEX_H +#pragma once #include @@ -38,6 +37,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/AudioDeviceEvent.h b/src/engine/events/AudioDeviceEvent.h index 61d3bf4fa..daae255fd 100644 --- a/src/engine/events/AudioDeviceEvent.h +++ b/src/engine/events/AudioDeviceEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_AUDIODEVICEEVENT_H -#define AE_AUDIODEVICEEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -38,6 +37,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/events/ControllerAxisEvent.h b/src/engine/events/ControllerAxisEvent.h index 170407a5d..be1bf82b1 100644 --- a/src/engine/events/ControllerAxisEvent.h +++ b/src/engine/events/ControllerAxisEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_CONTROLLERAXISEVENT_H -#define AE_CONTROLLERAXISEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -48,7 +47,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/ControllerButtonEvent.h b/src/engine/events/ControllerButtonEvent.h index aae778e2a..6a9a35258 100644 --- a/src/engine/events/ControllerButtonEvent.h +++ b/src/engine/events/ControllerButtonEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_CONTROLLERBUTTONEVENT_H -#define AE_CONTROLLERBUTTONEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -57,6 +56,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/ControllerDeviceEvent.h b/src/engine/events/ControllerDeviceEvent.h index 46a135a2b..71ef5c81b 100644 --- a/src/engine/events/ControllerDeviceEvent.h +++ b/src/engine/events/ControllerDeviceEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_CONTROLLERDEVICEEVENT_H -#define AE_CONTROLLERDEVICEEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -39,7 +38,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/DropEvent.h b/src/engine/events/DropEvent.h index 719caa562..431ae262c 100644 --- a/src/engine/events/DropEvent.h +++ b/src/engine/events/DropEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_DROPEVENT_H -#define AE_DROPEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -49,7 +48,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/EventDelegate.h b/src/engine/events/EventDelegate.h index dccd56fab..316e93690 100644 --- a/src/engine/events/EventDelegate.h +++ b/src/engine/events/EventDelegate.h @@ -1,5 +1,4 @@ -#ifndef AE_EVENTDELEGATE_H -#define AE_EVENTDELEGATE_H +#pragma once #include "../System.h" @@ -103,6 +102,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/EventManager.h b/src/engine/events/EventManager.h index eacbaf031..5615392f4 100644 --- a/src/engine/events/EventManager.h +++ b/src/engine/events/EventManager.h @@ -1,5 +1,4 @@ -#ifndef AE_EVENTHANDLER_H -#define AE_EVENTHANDLER_H +#pragma once #define AE_BUTTON_RELEASED 0 #define AE_BUTTON_PRESSED 1 @@ -68,6 +67,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/FrameEvent.h b/src/engine/events/FrameEvent.h index e25672e73..063b4d370 100644 --- a/src/engine/events/FrameEvent.h +++ b/src/engine/events/FrameEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_CLOCKEVENT_H -#define AE_CLOCKEVENT_H +#pragma once #include "../System.h" @@ -30,6 +29,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/KeyboardEvent.h b/src/engine/events/KeyboardEvent.h index 296599719..a40968f69 100644 --- a/src/engine/events/KeyboardEvent.h +++ b/src/engine/events/KeyboardEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_KEYBOARDEVENT_H -#define AE_KEYBOARDEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -60,7 +59,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/Keycodes.h b/src/engine/events/Keycodes.h index 2cefc0150..db1888d0d 100644 --- a/src/engine/events/Keycodes.h +++ b/src/engine/events/Keycodes.h @@ -1,5 +1,4 @@ -#ifndef AE_KEYCODES_H -#define AE_KEYCODES_H +#pragma once #include "../System.h" @@ -266,7 +265,4 @@ enum { AE_KEY_AUDIOREWIND = SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOREWIND), AE_KEY_AUDIOFASTFORWARD = SCANCODE_TO_KEYCODE(SDL_SCANCODE_AUDIOFASTFORWARD) -}; - - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/src/engine/events/MouseButtonEvent.h b/src/engine/events/MouseButtonEvent.h index cad35d4c4..65ae6b5ee 100644 --- a/src/engine/events/MouseButtonEvent.h +++ b/src/engine/events/MouseButtonEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_MOUSEBUTTONEVENT_H -#define AE_MOUSEBUTTONEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -65,6 +64,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/MouseMotionEvent.h b/src/engine/events/MouseMotionEvent.h index a1a7e558c..f51979327 100644 --- a/src/engine/events/MouseMotionEvent.h +++ b/src/engine/events/MouseMotionEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_MOUSEMOTIONEVENT_H -#define AE_MOUSEMOTIONEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -53,6 +52,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/MouseWheelEvent.h b/src/engine/events/MouseWheelEvent.h index a3bdcd6ee..7dd257762 100644 --- a/src/engine/events/MouseWheelEvent.h +++ b/src/engine/events/MouseWheelEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_MOUSEWHEELEVENT_H -#define AE_MOUSEWHEELEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -43,6 +42,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/TextInputEvent.h b/src/engine/events/TextInputEvent.h index 75c0730c3..3aa88c37f 100644 --- a/src/engine/events/TextInputEvent.h +++ b/src/engine/events/TextInputEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_TEXTINPUTEVENT_H -#define AE_TEXTINPUTEVENT_H +#pragma once #include "../System.h" @@ -43,6 +42,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/TouchEvent.h b/src/engine/events/TouchEvent.h index 48d70205b..a17b19308 100644 --- a/src/engine/events/TouchEvent.h +++ b/src/engine/events/TouchEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_TOUCHEVENT_H -#define AE_TOUCHEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -73,6 +72,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/events/WindowEvent.h b/src/engine/events/WindowEvent.h index 8f0bbd70e..41fdfece2 100644 --- a/src/engine/events/WindowEvent.h +++ b/src/engine/events/WindowEvent.h @@ -1,5 +1,4 @@ -#ifndef AE_WINDOWEVENT_H -#define AE_WINDOWEVENT_H +#pragma once #include "../System.h" #include "EventDelegate.h" @@ -53,6 +52,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/ASBuilder.h b/src/engine/graphics/ASBuilder.h index 862e9a4f3..fc0154894 100644 --- a/src/engine/graphics/ASBuilder.h +++ b/src/engine/graphics/ASBuilder.h @@ -1,5 +1,4 @@ -#ifndef GRAPHICSASBUILDER_H -#define GRAPHICSASBUILDER_H +#pragma once #include "Common.h" @@ -41,6 +40,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/BLAS.h b/src/engine/graphics/BLAS.h index dcd256c55..873f24e52 100644 --- a/src/engine/graphics/BLAS.h +++ b/src/engine/graphics/BLAS.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSBLAS_H -#define AE_GRAPHICSBLAS_H +#pragma once #include "Common.h" #include "Buffer.h" @@ -54,6 +53,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/Barrier.h b/src/engine/graphics/Barrier.h index a04caeb08..d832ad06d 100644 --- a/src/engine/graphics/Barrier.h +++ b/src/engine/graphics/Barrier.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSBARRIER_H -#define AE_GRAPHICSBARRIER_H +#pragma once #include "Common.h" @@ -53,6 +52,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/Buffer.h b/src/engine/graphics/Buffer.h index 8749cadc6..2e99bf45d 100644 --- a/src/engine/graphics/Buffer.h +++ b/src/engine/graphics/Buffer.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSBUFFER_H -#define AE_GRAPHICSBUFFER_H +#pragma once #include "Common.h" @@ -116,6 +115,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/CommandList.cpp b/src/engine/graphics/CommandList.cpp index 8eff62f3f..98a96f0ef 100644 --- a/src/engine/graphics/CommandList.cpp +++ b/src/engine/graphics/CommandList.cpp @@ -379,6 +379,25 @@ namespace Atlas { } + void CommandList::BindVertexBuffers(std::vector> &buffers, uint32_t bindingOffset, + uint32_t bindingCount) { + + assert(pipelineInUse && "No pipeline is bound"); + if (!pipelineInUse) return; + + std::vector offsets; + std::vector bindBuffers; + for (const auto& buffer : buffers) { + if (!buffer->buffer) return; + assert(buffer->size > 0 && "Invalid buffer size"); + bindBuffers.push_back(buffer->buffer); + offsets.push_back(0); + } + + vkCmdBindVertexBuffers(commandBuffer, bindingOffset, bindingCount, bindBuffers.data(), offsets.data()); + + } + void CommandList::BindBuffer(const Ref& buffer, uint32_t set, uint32_t binding) { assert(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); @@ -901,6 +920,7 @@ namespace Atlas { bufferInfo.range = binding.size ? std::min(binding.size, uint32_t(buffer->size)) : VK_WHOLE_SIZE; auto& setWrite = setWrites[bindingCounter++]; + setWrite = {}; setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; setWrite.pNext = nullptr; setWrite.dstBinding = j; @@ -929,6 +949,7 @@ namespace Atlas { bufferInfo.range = binding.size ? std::min(binding.size, uint32_t(buffer->size)) : VK_WHOLE_SIZE; auto& setWrite = setWrites[bindingCounter++]; + setWrite = {}; setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; setWrite.pNext = nullptr; setWrite.dstBinding = j; @@ -959,6 +980,7 @@ namespace Atlas { imageInfo.imageLayout = image->layout; auto& setWrite = setWrites[bindingCounter++]; + setWrite = {}; setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; setWrite.pNext = nullptr; setWrite.dstBinding = j; @@ -988,6 +1010,7 @@ namespace Atlas { imageInfo.imageLayout = image->layout; auto& setWrite = setWrites[bindingCounter++]; + setWrite = {}; setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; setWrite.pNext = nullptr; setWrite.dstBinding = j; @@ -1018,6 +1041,7 @@ namespace Atlas { tlasInfo.pAccelerationStructures = &tlas->accelerationStructure; auto& setWrite = setWrites[bindingCounter++]; + setWrite = {}; setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; setWrite.dstBinding = j; setWrite.dstArrayElement = binding.arrayElement; diff --git a/src/engine/graphics/CommandList.h b/src/engine/graphics/CommandList.h index ecd807c52..ff6c8b558 100644 --- a/src/engine/graphics/CommandList.h +++ b/src/engine/graphics/CommandList.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSCOMMANDLIST_H -#define AE_GRAPHICSCOMMANDLIST_H +#pragma once #include "Common.h" #include "Queue.h" @@ -78,6 +77,8 @@ namespace Atlas { void BindVertexBuffer(const Ref& buffer, uint32_t binding); + void BindVertexBuffers(std::vector>& buffers, uint32_t bindingOffset, uint32_t bindingCount); + void BindBuffer(const Ref& buffer, uint32_t set, uint32_t binding); void BindBufferOffset(const Ref& buffer, size_t offset, uint32_t set, uint32_t binding); @@ -256,6 +257,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/Common.h b/src/engine/graphics/Common.h index 1518dd4f9..5be42e92a 100644 --- a/src/engine/graphics/Common.h +++ b/src/engine/graphics/Common.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSCOMMON_H -#define AE_GRAPHICSCOMMON_H +#pragma once #include "Initializers.h" #include "../Log.h" @@ -74,6 +73,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/Descriptor.h b/src/engine/graphics/Descriptor.h index 4d79589e0..1c4700e5c 100644 --- a/src/engine/graphics/Descriptor.h +++ b/src/engine/graphics/Descriptor.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSDESCRIPTOR_H -#define AE_GRAPHICSDESCRIPTOR_H +#pragma once #include "Common.h" @@ -48,6 +47,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/Extensions.h b/src/engine/graphics/Extensions.h index 1d18c12a4..656ba82e1 100644 --- a/src/engine/graphics/Extensions.h +++ b/src/engine/graphics/Extensions.h @@ -1,5 +1,4 @@ -#ifndef AE_EXTENSIONS_H -#define AE_EXTENSIONS_H +#pragma once #include "System.h" @@ -32,6 +31,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/Format.h b/src/engine/graphics/Format.h index f4fdfe0a5..8f36c4645 100644 --- a/src/engine/graphics/Format.h +++ b/src/engine/graphics/Format.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSFORMAT_H -#define AE_GRAPHICSFORMAT_H +#pragma once #include "Common.h" @@ -278,6 +277,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/Framebuffer.h b/src/engine/graphics/Framebuffer.h index 3f2f341a3..2a925a7f6 100644 --- a/src/engine/graphics/Framebuffer.h +++ b/src/engine/graphics/Framebuffer.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSFRAMEBUFFER_H -#define AE_GRAPHICSFRAMEBUFFER_H +#pragma once #include "Common.h" #include "RenderPass.h" @@ -72,7 +71,4 @@ namespace Atlas { } -} - - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/GraphicsDevice.cpp b/src/engine/graphics/GraphicsDevice.cpp index b50df8bfe..b2a7e58cb 100644 --- a/src/engine/graphics/GraphicsDevice.cpp +++ b/src/engine/graphics/GraphicsDevice.cpp @@ -89,62 +89,64 @@ namespace Atlas { // so delete all of the memoryManager content before cleaning the rest memoryManager->DestroyAllImmediate(); - for (auto& tlasRef : tlases) { + // We assume everything else is terminated by now, so this is the only thread still alive + // In that case we don't lock all the mutexes + for (auto& tlasRef : tlases.data) { assert(tlasRef.use_count() == 1 && "TLAS wasn't deallocated or allocated wrongly"); tlasRef.reset(); } - for (auto& blasRef : blases) { + for (auto& blasRef : blases.data) { assert(blasRef.use_count() == 1 && "BLAS wasn't deallocated or allocated wrongly"); blasRef.reset(); } - for (auto& pipelineRef : pipelines) { + for (auto& pipelineRef : pipelines.data) { assert(pipelineRef.use_count() == 1 && "Pipeline wasn't deallocated or allocated wrongly"); pipelineRef.reset(); } - for (auto& frameBufferRef : frameBuffers) { + for (auto& frameBufferRef : frameBuffers.data) { assert(frameBufferRef.use_count() == 1 && "Frame buffer wasn't deallocated or allocated wrongly"); frameBufferRef.reset(); } - for (auto& renderPassRef : renderPasses) { + for (auto& renderPassRef : renderPasses.data) { assert(renderPassRef.use_count() == 1 && "Render pass wasn't deallocated or allocated wrongly"); renderPassRef.reset(); } - for (auto& shaderRef : shaders) { + for (auto& shaderRef : shaders.data) { assert(shaderRef.use_count() == 1 && "Shader wasn't deallocated or allocated wrongly"); shaderRef.reset(); } - for (auto& bufferRef : buffers) { + for (auto& bufferRef : buffers.data) { assert(bufferRef.use_count() == 1 && "Buffer wasn't deallocated or allocated wrongly"); bufferRef.reset(); } - for (auto& multiBufferRef : multiBuffers) { + for (auto& multiBufferRef : multiBuffers.data) { assert(multiBufferRef.use_count() == 1 && "Multi buffer wasn't deallocated or allocated wrongly"); multiBufferRef.reset(); } - for (auto& imageRef : images) { + for (auto& imageRef : images.data) { assert(imageRef.use_count() == 1 && "Image wasn't deallocated or allocated wrongly"); imageRef.reset(); } - for (auto& samplerRef : samplers) { + for (auto& samplerRef : samplers.data) { assert(samplerRef.use_count() == 1 && "Sampler wasn't deallocated or allocated wrongly"); samplerRef.reset(); } - for (auto& poolRef : descriptorPools) { + for (auto& poolRef : descriptorPools.data) { assert(poolRef.use_count() == 1 && "Descriptor pool wasn't deallocated or allocated wrongly"); poolRef.reset(); } - for (auto& poolRef : queryPools) { + for (auto& poolRef : queryPools.data) { assert(poolRef.use_count() == 1 && "Query pool wasn't deallocated or allocated wrongly"); poolRef.reset(); } @@ -199,7 +201,8 @@ namespace Atlas { auto renderPass = std::make_shared(this, desc); - renderPasses.push_back(renderPass); + std::lock_guard guard(renderPasses.mutex); + renderPasses.data.push_back(renderPass); return renderPass; @@ -209,7 +212,8 @@ namespace Atlas { auto frameBuffer = std::make_shared(this, desc); - frameBuffers.push_back(frameBuffer); + std::lock_guard guard(frameBuffers.mutex); + frameBuffers.data.push_back(frameBuffer); return frameBuffer; @@ -219,7 +223,8 @@ namespace Atlas { auto shader = std::make_shared(this, desc); - shaders.push_back(shader); + std::lock_guard guard(shaders.mutex); + shaders.data.push_back(shader); return shader; @@ -229,7 +234,8 @@ namespace Atlas { auto pipeline = std::make_shared(this, desc); - pipelines.push_back(pipeline); + std::lock_guard guard(pipelines.mutex); + pipelines.data.push_back(pipeline); return pipeline; @@ -239,7 +245,8 @@ namespace Atlas { auto pipeline = std::make_shared(this, desc); - pipelines.push_back(pipeline); + std::lock_guard guard(pipelines.mutex); + pipelines.data.push_back(pipeline); return pipeline; @@ -249,7 +256,8 @@ namespace Atlas { auto buffer = std::make_shared(this, desc); - buffers.push_back(buffer); + std::lock_guard guard(buffers.mutex); + buffers.data.push_back(buffer); return buffer; @@ -259,7 +267,8 @@ namespace Atlas { auto multiBuffer = std::make_shared(this, desc); - multiBuffers.push_back(multiBuffer); + std::lock_guard guard(multiBuffers.mutex); + multiBuffers.data.push_back(multiBuffer); return multiBuffer; @@ -269,7 +278,8 @@ namespace Atlas { auto image = std::make_shared(this, desc); - images.push_back(image); + std::lock_guard guard(images.mutex); + images.data.push_back(image); return image; @@ -279,7 +289,8 @@ namespace Atlas { auto sampler = std::make_shared(this, desc); - samplers.push_back(sampler); + std::lock_guard guard(samplers.mutex); + samplers.data.push_back(sampler); return sampler; @@ -289,7 +300,8 @@ namespace Atlas { auto pool = std::make_shared(this); - descriptorPools.push_back(pool); + std::lock_guard guard(descriptorPools.mutex); + descriptorPools.data.push_back(pool); return pool; @@ -299,7 +311,8 @@ namespace Atlas { auto pool = std::make_shared(this, desc); - queryPools.push_back(pool); + std::lock_guard guard(queryPools.mutex); + queryPools.data.push_back(pool); return pool; @@ -309,7 +322,8 @@ namespace Atlas { auto blas = std::make_shared(this, desc); - blases.push_back(blas); + std::lock_guard guard(blases.mutex); + blases.data.push_back(blas); return blas; @@ -319,7 +333,8 @@ namespace Atlas { auto tlas = std::make_shared(this, desc); - tlases.push_back(tlas); + std::lock_guard guard(tlases.mutex); + tlases.data.push_back(tlas); return tlas; @@ -482,8 +497,12 @@ namespace Atlas { // Update frame index of all objects in need memoryManager->UpdateFrameIndex(frameIndex); - for (auto& multiBuffer : multiBuffers) { - multiBuffer->UpdateFrameIndex(frameIndex); + + { + std::lock_guard guard(multiBuffers.mutex); + for (auto& multiBuffer : multiBuffers.data) { + multiBuffer->UpdateFrameIndex(frameIndex); + } } auto nextFrame = GetFrameData(); @@ -1020,115 +1039,18 @@ namespace Atlas { void GraphicsDevice::DestroyUnusedGraphicObjects() { - for (size_t i = 0; i < renderPasses.size(); i++) { - auto& renderPassRef = renderPasses[i]; - if (renderPassRef.use_count() == 1) { - renderPassRef.swap(renderPasses.back()); - memoryManager->DestroyAllocation(renderPasses.back()); - renderPasses.pop_back(); - i--; - } - } - - for (size_t i = 0; i < pipelines.size(); i++) { - auto& pipelineRef = pipelines[i]; - if (pipelineRef.use_count() == 1) { - pipelineRef.swap(pipelines.back()); - memoryManager->DestroyAllocation(pipelines.back()); - pipelines.pop_back(); - i--; - } - } - - for (size_t i = 0; i < shaders.size(); i++) { - auto& shaderRef = shaders[i]; - if (shaderRef.use_count() == 1) { - shaderRef.swap(shaders.back()); - memoryManager->DestroyAllocation(shaders.back()); - shaders.pop_back(); - i--; - } - } - - for (size_t i = 0; i < buffers.size(); i++) { - auto& bufferRef = buffers[i]; - if (bufferRef.use_count() == 1) { - bufferRef.swap(buffers.back()); - memoryManager->DestroyAllocation(buffers.back()); - buffers.pop_back(); - i--; - } - } - - for (size_t i = 0; i < multiBuffers.size(); i++) { - auto& multiBufferRef = multiBuffers[i]; - if (multiBufferRef.use_count() == 1) { - multiBufferRef.swap(multiBuffers.back()); - memoryManager->DestroyAllocation(multiBuffers.back()); - multiBuffers.pop_back(); - i--; - } - } - - for (size_t i = 0; i < images.size(); i++) { - auto& imageRef = images[i]; - if (imageRef.use_count() == 1) { - imageRef.swap(images.back()); - memoryManager->DestroyAllocation(images.back()); - images.pop_back(); - i--; - } - } - - for (size_t i = 0; i < samplers.size(); i++) { - auto& samplerRef = samplers[i]; - if (samplerRef.use_count() == 1) { - samplerRef.swap(samplers.back()); - memoryManager->DestroyAllocation(samplers.back()); - samplers.pop_back(); - i--; - } - } - - for (size_t i = 0; i < descriptorPools.size(); i++) { - auto& poolRef = descriptorPools[i]; - if (poolRef.use_count() == 1) { - poolRef.swap(descriptorPools.back()); - memoryManager->DestroyAllocation(descriptorPools.back()); - descriptorPools.pop_back(); - i--; - } - } - - for (size_t i = 0; i < queryPools.size(); i++) { - auto& poolRef = queryPools[i]; - if (poolRef.use_count() == 1) { - poolRef.swap(queryPools.back()); - memoryManager->DestroyAllocation(queryPools.back()); - queryPools.pop_back(); - i--; - } - } - - for (size_t i = 0; i < blases.size(); i++) { - auto& blasRef = blases[i]; - if (blasRef.use_count() == 1) { - blasRef.swap(blases.back()); - memoryManager->DestroyAllocation(blases.back()); - blases.pop_back(); - i--; - } - } - - for (size_t i = 0; i < tlases.size(); i++) { - auto& tlasRef = tlases[i]; - if (tlasRef.use_count() == 1) { - tlasRef.swap(tlases.back()); - memoryManager->DestroyAllocation(tlases.back()); - tlases.pop_back(); - i--; - } - } + DeleteOutdatedResources(renderPasses); + DeleteOutdatedResources(frameBuffers); + DeleteOutdatedResources(shaders); + DeleteOutdatedResources(pipelines); + DeleteOutdatedResources(buffers); + DeleteOutdatedResources(multiBuffers); + DeleteOutdatedResources(images); + DeleteOutdatedResources(samplers); + DeleteOutdatedResources(descriptorPools); + DeleteOutdatedResources(queryPools); + DeleteOutdatedResources(blases); + DeleteOutdatedResources(tlases); } diff --git a/src/engine/graphics/GraphicsDevice.h b/src/engine/graphics/GraphicsDevice.h index 9e4949d08..3c4e12a23 100644 --- a/src/engine/graphics/GraphicsDevice.h +++ b/src/engine/graphics/GraphicsDevice.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSDEVICE_H -#define AE_GRAPHICSDEVICE_H +#pragma once #include "Common.h" #include "Surface.h" @@ -185,6 +184,12 @@ namespace Atlas { queueFamilies[QueueType::PresentationQueue].has_value() && queueFamilies[QueueType::TransferQueue].has_value(); } + }; + + template + struct Resources { + std::vector> data; + std::mutex mutex; }; QueueRef SubmitAllCommandLists(); @@ -232,20 +237,36 @@ namespace Atlas { QueueRef FindAndLockQueue(uint32_t familyIndex); + template + void DeleteOutdatedResources(Resources& resources) { + std::lock_guard guard(resources.mutex); + + auto& data = resources.data; + for (size_t i = 0; i < data.size(); i++) { + auto& ref = data[i]; + if (ref.use_count() == 1) { + ref.swap(data.back()); + memoryManager->DestroyAllocation(data.back()); + data.pop_back(); + i--; + } + } + } + QueueFamilyIndices queueFamilyIndices; - std::vector> renderPasses; - std::vector> frameBuffers; - std::vector> shaders; - std::vector> pipelines; - std::vector> buffers; - std::vector> multiBuffers; - std::vector> images; - std::vector> samplers; - std::vector> descriptorPools; - std::vector> queryPools; - std::vector> blases; - std::vector> tlases; + Resources renderPasses; + Resources frameBuffers; + Resources shaders; + Resources pipelines; + Resources buffers; + Resources multiBuffers; + Resources images; + Resources samplers; + Resources descriptorPools; + Resources queryPools; + Resources blases; + Resources tlases; std::mutex commandListsMutex; std::vector commandLists; @@ -259,6 +280,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/Image.h b/src/engine/graphics/Image.h index 3db00e94f..f36be3e53 100644 --- a/src/engine/graphics/Image.h +++ b/src/engine/graphics/Image.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSTEXTURE_H -#define AE_GRAPHICSTEXTURE_H +#pragma once #include "Common.h" @@ -92,6 +91,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/Initializers.h b/src/engine/graphics/Initializers.h index 92baa2089..4cce6c0e1 100644 --- a/src/engine/graphics/Initializers.h +++ b/src/engine/graphics/Initializers.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSINITIALIZERS_H -#define AE_GRAPHICSINITIALIZERS_H +#pragma once #include @@ -87,6 +86,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/Instance.h b/src/engine/graphics/Instance.h index 7cbbd3d9c..14db7ee81 100644 --- a/src/engine/graphics/Instance.h +++ b/src/engine/graphics/Instance.h @@ -1,6 +1,4 @@ - -#ifndef AE_GRAPHICINSTANCE_H -#define AE_GRAPHICINSTANCE_H +#pragma once #include #include @@ -87,6 +85,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/MemoryManager.h b/src/engine/graphics/MemoryManager.h index e69832b91..e0372ac76 100644 --- a/src/engine/graphics/MemoryManager.h +++ b/src/engine/graphics/MemoryManager.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSMEMORYMANAGER_H -#define AE_GRAPHICSMEMORYMANAGER_H +#pragma once #include "Common.h" #include "RenderPass.h" @@ -131,6 +130,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/MemoryTransferManager.h b/src/engine/graphics/MemoryTransferManager.h index 2fef6b52c..d3509f6ed 100644 --- a/src/engine/graphics/MemoryTransferManager.h +++ b/src/engine/graphics/MemoryTransferManager.h @@ -1,5 +1,4 @@ -#ifndef AE_MEMORYUPLOADMANAGER_H -#define AE_MEMORYUPLOADMANAGER_H +#pragma once #include "Common.h" #include "Queue.h" @@ -56,6 +55,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/Pipeline.h b/src/engine/graphics/Pipeline.h index 379605810..bbea87ec1 100644 --- a/src/engine/graphics/Pipeline.h +++ b/src/engine/graphics/Pipeline.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSPIPELINE_H -#define AE_GRAPHICSPIPELINE_H +#pragma once #include "Common.h" #include "Shader.h" @@ -79,6 +78,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/Profiler.h b/src/engine/graphics/Profiler.h index c7e82f2b9..6a6dbfef5 100644 --- a/src/engine/graphics/Profiler.h +++ b/src/engine/graphics/Profiler.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSPROFILER_H -#define AE_GRAPHICSPROFILER_H +#pragma once #include "Common.h" #include "CommandList.h" @@ -187,6 +186,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/QueryPool.h b/src/engine/graphics/QueryPool.h index b212c7367..b8e7e21c7 100644 --- a/src/engine/graphics/QueryPool.h +++ b/src/engine/graphics/QueryPool.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSQUERYPOOL_H -#define AE_GRAPHICSQUERYPOOL_H +#pragma once #include "Common.h" @@ -36,6 +35,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/Queue.h b/src/engine/graphics/Queue.h index 86cf010bb..205fa8d28 100644 --- a/src/engine/graphics/Queue.h +++ b/src/engine/graphics/Queue.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICQUEUE_H -#define AE_GRAPHICQUEUE_H +#pragma once #include "Common.h" @@ -107,6 +106,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/RenderPass.h b/src/engine/graphics/RenderPass.h index dab2c102a..7cf087b9a 100644 --- a/src/engine/graphics/RenderPass.h +++ b/src/engine/graphics/RenderPass.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSRENDERPASS_H -#define AE_GRAPHICSRENDERPASS_H +#pragma once #include "Common.h" #include "Image.h" @@ -94,6 +93,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/Sampler.h b/src/engine/graphics/Sampler.h index d6443fdc7..e2b407115 100644 --- a/src/engine/graphics/Sampler.h +++ b/src/engine/graphics/Sampler.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSSAMPLER_H -#define AE_GRAPHICSSAMPLER_H +#pragma once #include "Common.h" @@ -41,6 +40,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/Shader.cpp b/src/engine/graphics/Shader.cpp index efe0eb2a5..621cf7ab3 100644 --- a/src/engine/graphics/Shader.cpp +++ b/src/engine/graphics/Shader.cpp @@ -79,7 +79,7 @@ namespace Atlas { Ref Shader::GetVariant(std::vector macros) { - std::sort(macros.begin(), macros.end());; + std::sort(macros.begin(), macros.end()); { std::lock_guard lock(variantMutex); @@ -91,6 +91,20 @@ namespace Atlas { auto variant = std::make_shared(device, shaderStageFiles, macros); + // Rollback in case there is a history and shader variant didn't compile + if (!variant->isComplete && historyShaderStageFiles.size()) { + Log::Warning("Rolling back shader due to compilation failure"); + + { + std::lock_guard lock(variantMutex); + shaderStageFiles = historyShaderStageFiles; + historyShaderStageFiles.clear(); + shaderVariants.clear(); + } + + variant = std::make_shared(device, shaderStageFiles, macros); + } + // In the meanwhile another process could have created this variant, check again { std::lock_guard lock(variantMutex); @@ -108,22 +122,35 @@ namespace Atlas { std::lock_guard lock(variantMutex); + std::filesystem::file_time_type maxLastModified = lastReload; + bool reload = false; for (auto& shaderStage : shaderStageFiles) { - if (Loader::ShaderLoader::CheckForReload(shaderStage.filename, shaderStage.lastModified)) { + std::filesystem::file_time_type lastModified; + if (Loader::ShaderLoader::CheckForReload(shaderStage.filename, + shaderStage.lastModified, lastModified)) { + maxLastModified = std::max(maxLastModified, lastModified); reload = true; } for (auto& includePath : shaderStage.includes) { - auto lastModified = lastModifiedMap.find(includePath); + auto lastModifiedIt = lastModifiedMap.find(includePath); - if (lastModified == lastModifiedMap.end()) continue; + if (lastModifiedIt == lastModifiedMap.end()) continue; - if (lastModified->second > shaderStage.lastModified) + if (lastModifiedIt->second > shaderStage.lastModified) reload = true; + + maxLastModified = std::max(maxLastModified, lastModifiedIt->second); } } + // This avoids consecutive reloads after a rollback + if (maxLastModified <= lastReload) + reload = false; + else + lastReload = maxLastModified; + std::vector newShaderStageFiles; if (reload) { for (auto& shaderStage : shaderStageFiles) { @@ -131,6 +158,7 @@ namespace Atlas { Loader::ShaderLoader::LoadFile(shaderStage.filename, shaderStage.shaderStage)); } // Reload means clearing all existing data + historyShaderStageFiles = shaderStageFiles; shaderStageFiles = newShaderStageFiles; shaderVariants.clear(); } diff --git a/src/engine/graphics/Shader.h b/src/engine/graphics/Shader.h index a31b473df..78e25ae00 100644 --- a/src/engine/graphics/Shader.h +++ b/src/engine/graphics/Shader.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSSHADER_H -#define AE_GRAPHICSSHADER_H +#pragma once #include "Common.h" #include @@ -132,6 +131,9 @@ namespace Atlas { GraphicsDevice* device = nullptr; std::vector shaderStageFiles; + std::vector historyShaderStageFiles; + + std::filesystem::file_time_type lastReload = std::filesystem::file_time_type::min(); std::mutex variantMutex; std::vector> shaderVariants; @@ -140,6 +142,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/ShaderCompiler.h b/src/engine/graphics/ShaderCompiler.h index bb97e4750..3df4c622f 100644 --- a/src/engine/graphics/ShaderCompiler.h +++ b/src/engine/graphics/ShaderCompiler.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSSHADERCOMPILER_H -#define AE_GRAPHICSSHADERCOMPILER_H +#pragma once #include "Common.h" #include "Shader.h" @@ -25,6 +24,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/StructureChainBuilder.h b/src/engine/graphics/StructureChainBuilder.h index aaecd1490..8f8574bab 100644 --- a/src/engine/graphics/StructureChainBuilder.h +++ b/src/engine/graphics/StructureChainBuilder.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSSTRUCTURECHAINBUILDER_H -#define AE_GRAPHICSSTRUCTURECHAINBUILDER_H +#pragma once #include "Common.h" @@ -36,6 +35,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/graphics/Surface.h b/src/engine/graphics/Surface.h index f7647c458..8b94b0444 100644 --- a/src/engine/graphics/Surface.h +++ b/src/engine/graphics/Surface.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSSURFACE_H -#define AE_GRAPHICSSURFACE_H +#pragma once #include #include @@ -30,6 +29,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/SwapChain.h b/src/engine/graphics/SwapChain.h index 4d0b8d0a7..9f6176b70 100644 --- a/src/engine/graphics/SwapChain.h +++ b/src/engine/graphics/SwapChain.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSSWAPCHAIN_H -#define AE_GRAPHICSSWAPCHAIN_H +#pragma once #include "Common.h" #include "Surface.h" @@ -45,7 +44,7 @@ namespace Atlas { SwapChain(const SwapChainSupportDetails& supportDetails, VkSurfaceKHR surface, GraphicsDevice* Device, int32_t desiredWidth, int32_t desiredHeight, ColorSpace preferredColorSpace = SRGB_NONLINEAR, - VkPresentModeKHR desiredMode = VK_PRESENT_MODE_FIFO_KHR, + VkPresentModeKHR desiredMode = VK_PRESENT_MODE_IMMEDIATE_KHR, SwapChain* oldSwapchain = nullptr); ~SwapChain(); @@ -94,7 +93,4 @@ namespace Atlas { } -} - - -#endif +} \ No newline at end of file diff --git a/src/engine/graphics/TLAS.h b/src/engine/graphics/TLAS.h index 08fd817c2..1411ebc35 100644 --- a/src/engine/graphics/TLAS.h +++ b/src/engine/graphics/TLAS.h @@ -1,5 +1,4 @@ -#ifndef AE_GRAPHICSTLAS_H -#define AE_GRAPHICSTLAS_H +#pragma once #include "Common.h" #include "Buffer.h" @@ -41,6 +40,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/input/Controller.h b/src/engine/input/Controller.h index 91321fd4c..9f301420c 100644 --- a/src/engine/input/Controller.h +++ b/src/engine/input/Controller.h @@ -1,5 +1,4 @@ -#ifndef AE_CONTROLLER_H -#define AE_CONTROLLER_H +#pragma once #include "../System.h" #include "../Camera.h" @@ -61,6 +60,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/input/Keyboard.h b/src/engine/input/Keyboard.h index 2617ffa44..946dcc315 100644 --- a/src/engine/input/Keyboard.h +++ b/src/engine/input/Keyboard.h @@ -1,5 +1,4 @@ -#ifndef AE_KEYBOARD_H -#define AE_KEYBOARD_H +#pragma once #include "../System.h" #include "../Camera.h" @@ -45,6 +44,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/input/Mouse.h b/src/engine/input/Mouse.h index fb4b1eba6..510dd83a1 100644 --- a/src/engine/input/Mouse.h +++ b/src/engine/input/Mouse.h @@ -1,5 +1,4 @@ -#ifndef AE_MOUSE_H -#define AE_MOUSE_H +#pragma once #include "../System.h" #include "../Camera.h" @@ -58,6 +57,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/input/Touch.h b/src/engine/input/Touch.h index ab49e04cd..21bde248d 100644 --- a/src/engine/input/Touch.h +++ b/src/engine/input/Touch.h @@ -1,5 +1,4 @@ -#ifndef AE_TOUCH_H -#define AE_TOUCH_H +#pragma once #include "../System.h" #include "../events/EventManager.h" @@ -54,6 +53,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/AO.h b/src/engine/lighting/AO.h index ed72c5dd4..3ea6a2fb0 100644 --- a/src/engine/lighting/AO.h +++ b/src/engine/lighting/AO.h @@ -1,5 +1,4 @@ -#ifndef AE_AO_H -#define AE_AO_H +#pragma once #include "../System.h" #include "../RenderTarget.h" @@ -32,7 +31,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/Atmosphere.h b/src/engine/lighting/Atmosphere.h index 2852decbc..8dd8a3451 100644 --- a/src/engine/lighting/Atmosphere.h +++ b/src/engine/lighting/Atmosphere.h @@ -1,5 +1,4 @@ -#ifndef AE_ATMOSPHERE_H -#define AE_ATMOSPHERE_H +#pragma once #include "../System.h" @@ -21,7 +20,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/DirectionalLight.h b/src/engine/lighting/DirectionalLight.h index 93f5ab2f6..a7a772e8b 100644 --- a/src/engine/lighting/DirectionalLight.h +++ b/src/engine/lighting/DirectionalLight.h @@ -1,5 +1,4 @@ -#ifndef AE_DIRECTIONALLIGHT_H -#define AE_DIRECTIONALLIGHT_H +#pragma once #include "../System.h" #include "Light.h" @@ -44,6 +43,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/EnvironmentProbe.h b/src/engine/lighting/EnvironmentProbe.h index 51f3d858a..0e636cc92 100644 --- a/src/engine/lighting/EnvironmentProbe.h +++ b/src/engine/lighting/EnvironmentProbe.h @@ -1,5 +1,4 @@ -#ifndef AE_ENVIRONMENTPROBE_H -#define AE_ENVIRONMENTPROBE_H +#pragma once #include "../System.h" @@ -43,6 +42,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/Fog.h b/src/engine/lighting/Fog.h index 0574cec20..96cd50bdd 100644 --- a/src/engine/lighting/Fog.h +++ b/src/engine/lighting/Fog.h @@ -1,5 +1,4 @@ -#ifndef AE_FOG_H -#define AE_FOG_H +#pragma once #include "../System.h" @@ -14,18 +13,20 @@ namespace Atlas { bool enable = true; - vec3 color = vec3(0.5, 0.6, 0.7); + vec3 color = vec3(0.73, 0.79, 0.85) * 0.2f; float density = 0.05f; float height = 0.0f; float heightFalloff = 0.005f; - float scatteringAnisotropy = 0.0f; + float scatteringAnisotropy = -0.6f; + + bool rayMarching = true; + int32_t rayMarchStepCount = 10; + }; } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/IrradianceVolume.h b/src/engine/lighting/IrradianceVolume.h index 04d114c91..d540cdd5f 100644 --- a/src/engine/lighting/IrradianceVolume.h +++ b/src/engine/lighting/IrradianceVolume.h @@ -1,5 +1,4 @@ -#ifndef AE_IRRADIANCEVOLUME_H -#define AE_IRRADIANCEVOLUME_H +#pragma once #include "../System.h" @@ -108,6 +107,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/lighting/Light.h b/src/engine/lighting/Light.h index a5d447433..5e79183ca 100644 --- a/src/engine/lighting/Light.h +++ b/src/engine/lighting/Light.h @@ -1,5 +1,4 @@ -#ifndef AE_ILIGHT_H -#define AE_ILIGHT_H +#pragma once #include "../System.h" #include "Shadow.h" @@ -51,7 +50,4 @@ namespace Atlas { } -} - - -#endif +} \ No newline at end of file diff --git a/src/engine/lighting/PointLight.h b/src/engine/lighting/PointLight.h index 064cee641..337f92336 100644 --- a/src/engine/lighting/PointLight.h +++ b/src/engine/lighting/PointLight.h @@ -1,5 +1,4 @@ -#ifndef AE_POINTLIGHT_H -#define AE_POINTLIGHT_H +#pragma once #include "../System.h" #include "Light.h" @@ -35,6 +34,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/lighting/Reflection.h b/src/engine/lighting/Reflection.h index cfdf2e0b7..d3d80f815 100644 --- a/src/engine/lighting/Reflection.h +++ b/src/engine/lighting/Reflection.h @@ -1,5 +1,4 @@ -#ifndef AE_REFLECTION_H -#define AE_REFLECTION_H +#pragma once #include "../System.h" #include "../RenderTarget.h" @@ -36,7 +35,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/SSS.h b/src/engine/lighting/SSS.h index 8af028ab5..1b773b3b8 100644 --- a/src/engine/lighting/SSS.h +++ b/src/engine/lighting/SSS.h @@ -1,5 +1,4 @@ -#ifndef AE_SSS_H -#define AE_SSS_H +#pragma once #include "../System.h" @@ -22,6 +21,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/Shadow.cpp b/src/engine/lighting/Shadow.cpp index 3c7ac34cd..3e7a3074b 100644 --- a/src/engine/lighting/Shadow.cpp +++ b/src/engine/lighting/Shadow.cpp @@ -49,6 +49,23 @@ namespace Atlas { } + void Shadow::SetResolution(int32_t resolution) { + + this->resolution = resolution; + + if (useCubemap) { + cubemap = Texture::Cubemap(resolution, resolution, VK_FORMAT_D16_UNORM, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + } + else { + maps = Texture::Texture2DArray(resolution, resolution, componentCount, VK_FORMAT_D16_UNORM, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + } + + Update(); + + } + void Shadow::Update() { update = true; diff --git a/src/engine/lighting/Shadow.h b/src/engine/lighting/Shadow.h index 5d2eaf674..8de4ed2f3 100644 --- a/src/engine/lighting/Shadow.h +++ b/src/engine/lighting/Shadow.h @@ -1,5 +1,4 @@ -#ifndef AE_SHADOW_H -#define AE_SHADOW_H +#pragma once #include "../System.h" #include "../Camera.h" @@ -32,6 +31,8 @@ namespace Atlas { Shadow(float distance, float bias, int32_t resolution, bool useCubemap = false); + void SetResolution(int32_t resolution); + void Update(); float distance = 300.0f; @@ -59,7 +60,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/Sky.h b/src/engine/lighting/Sky.h index e90a3fc62..ecb3655d8 100644 --- a/src/engine/lighting/Sky.h +++ b/src/engine/lighting/Sky.h @@ -1,5 +1,4 @@ -#ifndef AE_SKY_H -#define AE_SKY_H +#pragma once #include "../System.h" @@ -20,10 +19,10 @@ namespace Atlas { EnvironmentProbe* GetProbe(); vec3 planetCenter = vec3(0.0f, -650000.0f, 0.0f); - float planetRadius = 650000.0f; + float planetRadius = 649000.0f; Ref sun = nullptr; - Ref atmosphere = nullptr; + Ref atmosphere = CreateRef(); Ref clouds = nullptr; Ref probe = nullptr; @@ -32,7 +31,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/Volumetric.h b/src/engine/lighting/Volumetric.h index 9f175e09e..1bccdcbbc 100644 --- a/src/engine/lighting/Volumetric.h +++ b/src/engine/lighting/Volumetric.h @@ -1,5 +1,4 @@ -#ifndef AE_VOLUMETRIC_H -#define AE_VOLUMETRIC_H +#pragma once #include "../System.h" #include "../RenderTarget.h" @@ -21,6 +20,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/lighting/VolumetricClouds.h b/src/engine/lighting/VolumetricClouds.h index ab9817209..b3e1e2eda 100644 --- a/src/engine/lighting/VolumetricClouds.h +++ b/src/engine/lighting/VolumetricClouds.h @@ -1,5 +1,4 @@ -#ifndef AE_VOLUMETRICCLOUDS_H -#define AE_VOLUMETRICCLOUDS_H +#pragma once #include "../System.h" #include "../Camera.h" @@ -35,10 +34,11 @@ namespace Atlas { }; int32_t sampleCount = 64; - int32_t shadowSampleCount = 5; + int32_t occlusionSampleCount = 5; + int32_t shadowSampleFraction = 4; - float minHeight = 100.0f; - float maxHeight = 600.0f; + float minHeight = 1400.0f; + float maxHeight = 1700.0f; float distanceLimit = 8000.0f; float coverageScale = 0.25f; @@ -61,11 +61,10 @@ namespace Atlas { bool needsNoiseUpdate = true; bool enable = true; bool castShadow = true; + bool stochasticOcclusionSampling = true; }; } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/loader/AssetLoader.h b/src/engine/loader/AssetLoader.h index 6f8b0bcbf..6ce8f1596 100644 --- a/src/engine/loader/AssetLoader.h +++ b/src/engine/loader/AssetLoader.h @@ -1,5 +1,4 @@ -#ifndef AE_ASSETLOADER_H -#define AE_ASSETLOADER_H +#pragma once #include "../System.h" @@ -148,6 +147,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/loader/ImageLoader.h b/src/engine/loader/ImageLoader.h index 2318e2fce..e74399a96 100644 --- a/src/engine/loader/ImageLoader.h +++ b/src/engine/loader/ImageLoader.h @@ -1,5 +1,4 @@ -#ifndef AE_IMAGELOADER_H -#define AE_IMAGELOADER_H +#pragma once #include "../System.h" #include "../common/Image.h" @@ -217,6 +216,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/loader/MaterialLoader.h b/src/engine/loader/MaterialLoader.h index 601420a05..332e6a93e 100644 --- a/src/engine/loader/MaterialLoader.h +++ b/src/engine/loader/MaterialLoader.h @@ -1,5 +1,4 @@ -#ifndef AE_MATERIALLOADER_H -#define AE_MATERIALLOADER_H +#pragma once #include "../System.h" #include "../Material.h" @@ -32,7 +31,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/loader/ModelLoader.cpp b/src/engine/loader/ModelLoader.cpp index 2f43d68a8..0a11e30d9 100644 --- a/src/engine/loader/ModelLoader.cpp +++ b/src/engine/loader/ModelLoader.cpp @@ -18,7 +18,14 @@ namespace Atlas { Ref ModelLoader::LoadMesh(const std::string& filename, bool forceTangents, mat4 transform, int32_t maxTextureResolution) { - Mesh::MeshData meshData; + return LoadMesh(filename, Mesh::MeshMobility::Stationary, forceTangents, + transform, maxTextureResolution); + + } + + Ref ModelLoader::LoadMesh(const std::string& filename, + Mesh::MeshMobility mobility, bool forceTangents, + mat4 transform, int32_t maxTextureResolution) { auto directoryPath = GetDirectoryPath(filename); @@ -96,6 +103,10 @@ namespace Atlas { hasTangents = true; } + auto mesh = CreateRef(); + mesh->mobility = mobility; + auto& meshData = mesh->data; + if (vertexCount > 65535) { meshData.indices.SetType(Mesh::ComponentFormat::UnsignedInt); } @@ -104,9 +115,9 @@ namespace Atlas { } meshData.vertices.SetType(Mesh::ComponentFormat::Float); - meshData.normals.SetType(Mesh::ComponentFormat::PackedFloat); + meshData.normals.SetType(Mesh::ComponentFormat::PackedNormal); meshData.texCoords.SetType(Mesh::ComponentFormat::HalfFloat); - meshData.tangents.SetType(Mesh::ComponentFormat::PackedFloat); + meshData.tangents.SetType(Mesh::ComponentFormat::PackedNormal); meshData.colors.SetType(Mesh::ComponentFormat::PackedColor); meshData.SetIndexCount(indexCount); @@ -125,13 +136,21 @@ namespace Atlas { uint32_t usedVertices = 0; uint32_t loadedVertices = 0; - std::vector indices(indexCount); + auto& indices = meshData.indices; + + auto& vertices = meshData.vertices; + auto& texCoords = meshData.texCoords; + auto& normals = meshData.normals; + auto& tangents = meshData.tangents; + auto& colors = meshData.colors; - std::vector vertices(vertexCount); - std::vector texCoords(hasTexCoords ? vertexCount : 0); - std::vector normals(vertexCount); - std::vector tangents(hasTangents ? vertexCount : 0); - std::vector colors(hasVertexColors ? vertexCount : 0); + indices.SetElementCount(indexCount); + + vertices.SetElementCount(vertexCount); + texCoords.SetElementCount(hasTexCoords ? vertexCount : 0); + normals.SetElementCount(vertexCount); + tangents.SetElementCount(hasTangents ? vertexCount : 0); + colors.SetElementCount(hasVertexColors ? vertexCount : 0); auto graphicsDevice = Graphics::GraphicsDevice::DefaultDevice; auto rgbSupport = graphicsDevice->CheckFormatSupport(VK_FORMAT_R8G8B8_UNORM, @@ -165,19 +184,20 @@ namespace Atlas { } meshData.subData = std::vector(scene->mNumMaterials); - meshData.materials = std::vector(scene->mNumMaterials); for (uint32_t i = 0; i < scene->mNumMaterials; i++) { - auto& material = meshData.materials[i]; + auto material = CreateRef(); + meshData.materials.push_back(material); + auto& images = materialImages[i]; auto& subData = meshData.subData[i]; - LoadMaterial(scene->mMaterials[i], images, material); + LoadMaterial(scene->mMaterials[i], images, *material); - material.vertexColors = hasVertexColors; + material->vertexColors = hasVertexColors; - subData.material = &material; + subData.material = material; subData.materialIdx = i; subData.indicesOffset = usedFaces * 3; @@ -255,38 +275,12 @@ namespace Atlas { materialImages.clear(); meshData.aabb = Volume::AABB(min, max); - - meshData.indices.Set(indices); - indices.clear(); - indices.shrink_to_fit(); - - meshData.vertices.Set(vertices); - vertices.clear(); - vertices.shrink_to_fit(); - - meshData.normals.Set(normals); - normals.clear(); - normals.shrink_to_fit(); - - if (hasTexCoords) { - meshData.texCoords.Set(texCoords); - texCoords.clear(); - texCoords.shrink_to_fit(); - } - if (hasTangents) { - meshData.tangents.Set(tangents); - tangents.clear(); - tangents.shrink_to_fit(); - } - if (hasVertexColors) { - meshData.colors.Set(colors); - colors.clear(); - colors.shrink_to_fit(); - } - meshData.filename = filename; - return CreateRef(meshData); + mesh->name = meshData.filename; + mesh->UpdateData(); + + return mesh; } @@ -363,7 +357,8 @@ namespace Atlas { bool hasVertexColors = false; bool hasTexCoords = assimpMesh->mNumUVComponents[0] > 0; - Mesh::MeshData meshData; + auto mesh = CreateRef(); + auto& meshData = mesh->data; hasTangents |= forceTangents; if (assimpMaterial->GetTextureCount(aiTextureType_NORMALS) > 0) @@ -380,34 +375,36 @@ namespace Atlas { hasVertexColors = assimpMesh->HasVertexColors(0); - meshData.vertices.SetType(Mesh::ComponentFormat::Float); - meshData.normals.SetType(Mesh::ComponentFormat::PackedFloat); - meshData.texCoords.SetType(Mesh::ComponentFormat::HalfFloat); - meshData.tangents.SetType(Mesh::ComponentFormat::PackedFloat); - meshData.colors.SetType(Mesh::ComponentFormat::PackedColor); - meshData.SetIndexCount(indexCount); meshData.SetVertexCount(vertexCount); - std::vector indices(indexCount); + auto& indices = meshData.indices; - std::vector vertices(vertexCount); - std::vector texCoords(hasTexCoords ? vertexCount : 0); - std::vector normals(vertexCount); - std::vector tangents(hasTangents ? vertexCount : 0); - std::vector colors(hasVertexColors ? vertexCount : 0); + auto& vertices = meshData.vertices; + auto& texCoords = meshData.texCoords; + auto& normals = meshData.normals; + auto& tangents = meshData.tangents; + auto& colors = meshData.colors; - meshData.materials = std::vector(1); + indices.SetElementCount(indexCount); + + vertices.SetElementCount(vertexCount); + texCoords.SetElementCount(hasTexCoords ? vertexCount : 0); + normals.SetElementCount(vertexCount); + tangents.SetElementCount(hasTangents ? vertexCount : 0); + colors.SetElementCount(hasVertexColors ? vertexCount : 0); + + auto material = CreateRef(); + meshData.materials.push_back(material); auto min = vec3(std::numeric_limits::max()); auto max = vec3(-std::numeric_limits::max()); auto& images = materialImages[materialIdx]; - auto& material = meshData.materials.front(); - LoadMaterial(assimpMaterial, images, material); + LoadMaterial(assimpMaterial, images, *material); - material.vertexColors = hasVertexColors; + material->vertexColors = hasVertexColors; for (uint32_t j = 0; j < assimpMesh->mNumVertices; j++) { @@ -461,31 +458,19 @@ namespace Atlas { meshData.aabb = Volume::AABB(min, max); - meshData.indices.Set(indices); - meshData.vertices.Set(vertices); - meshData.normals.Set(normals); - - if (hasTexCoords) - meshData.texCoords.Set(texCoords); - if (hasTangents) - meshData.tangents.Set(tangents); - if (hasVertexColors) - meshData.colors.Set(colors); - meshData.subData.push_back({ .indicesOffset = 0, .indicesCount = indexCount, - .material = &material, + .material = material, .materialIdx = 0, .aabb = meshData.aabb }); meshData.filename = std::string(assimpMesh->mName.C_Str()); - - auto mesh = CreateRef(meshData); mesh->name = meshData.filename; + mesh->UpdateData(); auto handle = ResourceManager::AddResource(filename + "_" + mesh->name, mesh); meshMap[assimpMesh] = handle; @@ -551,7 +536,9 @@ namespace Atlas { material.name = std::string(name.C_Str()); material.baseColor = vec3(diffuse.r, diffuse.g, diffuse.b); - material.emissiveColor = vec3(emissive.r, emissive.g, emissive.b); + + material.emissiveIntensity = glm::max(glm::max(emissive.r, glm::max(emissive.g, emissive.b)), 1.0f); + material.emissiveColor = vec3(emissive.r, emissive.g, emissive.b) / material.emissiveIntensity; material.displacementScale = 0.01f; material.normalScale = 0.5f; diff --git a/src/engine/loader/ModelLoader.h b/src/engine/loader/ModelLoader.h index e2cdd5a3e..e3a21c461 100644 --- a/src/engine/loader/ModelLoader.h +++ b/src/engine/loader/ModelLoader.h @@ -1,5 +1,4 @@ -#ifndef AE_MODELLOADER_H -#define AE_MODELLOADER_H +#pragma once #include "../System.h" #include "../mesh/MeshData.h" @@ -21,6 +20,10 @@ namespace Atlas { bool forceTangents = false, mat4 transform = mat4(1.0f), int32_t maxTextureResolution = 4096); + static Ref LoadMesh(const std::string& filename, + Mesh::MeshMobility mobility, bool forceTangents = false, + mat4 transform = mat4(1.0f), int32_t maxTextureResolution = 4096); + static Ref LoadScene(const std::string& filename, bool forceTangents = false, mat4 transform = mat4(1.0f), int32_t maxTextureResolution = 4096); @@ -86,6 +89,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/loader/ShaderLoader.cpp b/src/engine/loader/ShaderLoader.cpp index 7fbe4fed7..8d116fc83 100644 --- a/src/engine/loader/ShaderLoader.cpp +++ b/src/engine/loader/ShaderLoader.cpp @@ -33,12 +33,13 @@ namespace Atlas { } - bool ShaderLoader::CheckForReload(const std::string& filename, const std::filesystem::file_time_type fileTime) { + bool ShaderLoader::CheckForReload(const std::string& filename, const std::filesystem::file_time_type fileTime, + std::filesystem::file_time_type& pathLastModified) { auto path = sourceDirectory.length() != 0 ? sourceDirectory + "/" : ""; path += filename; - auto pathLastModified = GetModifiedTime(Loader::AssetLoader::GetFullPath(path), fileTime); + pathLastModified = GetModifiedTime(Loader::AssetLoader::GetFullPath(path), fileTime); return pathLastModified > fileTime; } diff --git a/src/engine/loader/ShaderLoader.h b/src/engine/loader/ShaderLoader.h index ace921066..4ad421219 100644 --- a/src/engine/loader/ShaderLoader.h +++ b/src/engine/loader/ShaderLoader.h @@ -1,5 +1,4 @@ -#ifndef AE_SHADERLOADER_H -#define AE_SHADERLOADER_H +#pragma once #include "../System.h" #include "../graphics/Shader.h" @@ -16,7 +15,8 @@ namespace Atlas { public: static Graphics::ShaderStageFile LoadFile(const std::string& filename, VkShaderStageFlagBits shaderStage); - static bool CheckForReload(const std::string& filename, const std::filesystem::file_time_type fileTime); + static bool CheckForReload(const std::string& filename, const std::filesystem::file_time_type fileTime, + std::filesystem::file_time_type& pathLastModified); static void SetSourceDirectory(const std::string& sourceDirectory); @@ -41,6 +41,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/loader/TerrainLoader.h b/src/engine/loader/TerrainLoader.h index 97b6d6978..943bbe5ab 100644 --- a/src/engine/loader/TerrainLoader.h +++ b/src/engine/loader/TerrainLoader.h @@ -1,5 +1,4 @@ -#ifndef AE_TERRAINLOADER_H -#define AE_TERRAINLOADER_H +#pragma once #include "../System.h" #include "../terrain/Terrain.h" @@ -47,6 +46,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/mesh/DataComponent.h b/src/engine/mesh/DataComponent.h index ee3dc9368..782996523 100644 --- a/src/engine/mesh/DataComponent.h +++ b/src/engine/mesh/DataComponent.h @@ -1,5 +1,4 @@ -#ifndef AE_DATACOMPONENT_H -#define AE_DATACOMPONENT_H +#pragma once #include "../System.h" #include "../common/Packing.h" @@ -21,7 +20,7 @@ namespace Atlas { UnsignedShort = 1, Float = 2, HalfFloat = 3, - PackedFloat = 4, + PackedNormal = 4, PackedColor = 5 }; @@ -45,8 +44,9 @@ namespace Atlas { /** * Constructs a DataComponent object. * @param format The type of the component S should be converted into. See {@link DataComponent.h} for more. + * @param elementCount The amount of elements the data component should hold */ - DataComponent(ComponentFormat format); + DataComponent(ComponentFormat format, size_t elementCount = 0); /** * Resets the type of the component. @@ -68,10 +68,17 @@ namespace Atlas { */ VkFormat GetFormat() const; + /** + * Pushes back a single element and increases the size by one + * @param value + */ + void PushBack(const T& value); + /** * Sets the data for the component and converts it into data of component type. * @param values An vector of values of type S. * @note The length of the should be exactly the same as the size set by {@link SetSize}. + * This also adjusts the element count to the size of the values vector */ void Set(std::vector& values); @@ -88,12 +95,24 @@ namespace Atlas { */ size_t GetStride(); + /** + * + * @param elementCount + */ + void SetElementCount(size_t elementCount); + /** * Returns the size of one converted element. * @return The size in bytes */ size_t GetElementSize(); + /** + * + * @return + */ + size_t GetElementCount() const; + /** * Converts the data and returns a byte array. * @return Returns the converted data as a byte vector. @@ -123,6 +142,32 @@ namespace Atlas { */ bool ContainsData(); + /** + * + * @param idx + * @return + */ + T& operator[](size_t idx); + + /** + * + * @param idx + * @return + */ + const T& operator[](std::size_t idx) const; + + /** + * + * @return + */ + typename std::vector::const_iterator begin() const; + + /** + * + * @return + */ + typename std::vector::const_iterator end() const; + private: void ConvertData(); @@ -134,8 +179,9 @@ namespace Atlas { }; template - DataComponent::DataComponent(ComponentFormat format) : format(format) { + DataComponent::DataComponent(ComponentFormat format, size_t elementCount) : format(format) { + data.resize(elementCount); } @@ -187,11 +233,22 @@ namespace Atlas { switch(format) { case ComponentFormat::Float: return VK_FORMAT_R32G32B32A32_SFLOAT; case ComponentFormat::HalfFloat: return VK_FORMAT_R16G16B16A16_SFLOAT; - case ComponentFormat::PackedFloat: return VK_FORMAT_A2B10G10R10_SNORM_PACK32; + case ComponentFormat::PackedNormal: return VK_FORMAT_A2B10G10R10_SNORM_PACK32; case ComponentFormat::PackedColor: return VK_FORMAT_R8G8B8A8_UNORM; default: assert(0 && "Invalid combination of formats"); } } + + assert(0 && "Invalid combination of formats"); + return VK_FORMAT_R32G32B32A32_SFLOAT; + + } + + template + void DataComponent::PushBack(const T& value) { + + this->data.push_back(value); + } template @@ -216,6 +273,13 @@ namespace Atlas { } + template + void DataComponent::SetElementCount(size_t elementCount) { + + data.resize(elementCount); + + } + template size_t DataComponent::GetElementSize() { @@ -225,7 +289,7 @@ namespace Atlas { case ComponentFormat::UnsignedShort: size = sizeof(uint16_t) * GetStride(); break; case ComponentFormat::Float: size = sizeof(float) * GetStride(); break; case ComponentFormat::HalfFloat: size = sizeof(float16) * GetStride(); break; - case ComponentFormat::PackedFloat: size = sizeof(uint32_t); break; + case ComponentFormat::PackedNormal: size = sizeof(uint32_t); break; case ComponentFormat::PackedColor: size = sizeof(uint32_t); break; } @@ -233,6 +297,13 @@ namespace Atlas { } + template + size_t DataComponent::GetElementCount() const { + + return data.size(); + + } + template std::vector& DataComponent::GetConverted() { @@ -307,7 +378,7 @@ namespace Atlas { std::memcpy(converted.data(), convertedData.data(), sizeInBytes); } } - else if (format == ComponentFormat::PackedFloat) { + else if (format == ComponentFormat::PackedNormal) { if constexpr(std::is_same_v) { std::vector convertedData(data.size()); std::transform(data.begin(), data.end(), convertedData.begin(), @@ -326,8 +397,34 @@ namespace Atlas { } - } + template + T& DataComponent::operator[](size_t idx) { -} + return data[idx]; + + } + + template + const T& DataComponent::operator[](std::size_t idx) const { + + return data[idx]; + + } + + template + typename std::vector::const_iterator DataComponent::begin() const { + + return data.begin(); + + } + + template + typename std::vector::const_iterator DataComponent::end() const { + + return data.end(); + + } + + } -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/mesh/Impostor.h b/src/engine/mesh/Impostor.h index dd6903788..627a132ed 100644 --- a/src/engine/mesh/Impostor.h +++ b/src/engine/mesh/Impostor.h @@ -1,5 +1,4 @@ -#ifndef AE_IMPOSTOR_H -#define AE_IMPOSTOR_H +#pragma once #include "../System.h" #include "../volume/AABB.h" @@ -71,6 +70,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/mesh/Mesh.cpp b/src/engine/mesh/Mesh.cpp index bfbe56630..0e886d1a7 100644 --- a/src/engine/mesh/Mesh.cpp +++ b/src/engine/mesh/Mesh.cpp @@ -8,13 +8,19 @@ namespace Atlas { namespace Mesh { - Mesh::Mesh(MeshData& meshData, - MeshMobility mobility) : data(meshData), mobility(mobility) { + Mesh::Mesh(MeshData& meshData, MeshMobility mobility, MeshUsage usage) + : data(meshData), mobility(mobility), usage(usage) { UpdateData(); } + Mesh::Mesh(MeshMobility mobility, MeshUsage usage) + : mobility(mobility), usage(usage) { + + + } + void Mesh::SetTransform(mat4 matrix) { data.SetTransform(matrix); @@ -25,37 +31,51 @@ namespace Atlas { void Mesh::UpdateData() { + bool hostAccessible = usage & MeshUsageBits::HostAccessBit; + if (data.indices.ContainsData()) { auto type = data.indices.GetElementSize() == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32; - indexBuffer = Buffer::IndexBuffer(type, data.GetIndexCount(), data.indices.GetConvertedVoid()); - vertexArray.AddIndexComponent(indexBuffer); + indexBuffer = Buffer::IndexBuffer(type, data.GetIndexCount(), + data.indices.GetConvertedVoid(), hostAccessible); } if (data.vertices.ContainsData()) { vertexBuffer = Buffer::VertexBuffer(data.vertices.GetFormat(), data.GetVertexCount(), - data.vertices.GetConvertedVoid()); - vertexArray.AddComponent(0, vertexBuffer); + data.vertices.GetConvertedVoid(), hostAccessible); } if (data.normals.ContainsData()) { - Buffer::VertexBuffer buffer(data.normals.GetFormat(), data.GetVertexCount(), - data.normals.GetConvertedVoid()); - vertexArray.AddComponent(1, buffer); + normalBuffer = Buffer::VertexBuffer(data.normals.GetFormat(), data.GetVertexCount(), + data.normals.GetConvertedVoid(), hostAccessible); } if (data.texCoords.ContainsData()) { - Buffer::VertexBuffer buffer(data.texCoords.GetFormat(), data.GetVertexCount(), - data.texCoords.GetConvertedVoid()); - vertexArray.AddComponent(2, buffer); + texCoordBuffer = Buffer::VertexBuffer(data.texCoords.GetFormat(), data.GetVertexCount(), + data.texCoords.GetConvertedVoid(), hostAccessible); } if (data.tangents.ContainsData()) { - Buffer::VertexBuffer buffer(data.tangents.GetFormat(), data.GetVertexCount(), - data.tangents.GetConvertedVoid()); - vertexArray.AddComponent(3, buffer); + tangentBuffer = Buffer::VertexBuffer(data.tangents.GetFormat(), data.GetVertexCount(), + data.tangents.GetConvertedVoid(), hostAccessible); } if (data.colors.ContainsData()) { - Buffer::VertexBuffer buffer(data.colors.GetFormat(), data.GetVertexCount(), - data.colors.GetConvertedVoid()); - vertexArray.AddComponent(4, buffer); + colorBuffer = Buffer::VertexBuffer(data.colors.GetFormat(), data.GetVertexCount(), + data.colors.GetConvertedVoid(), hostAccessible); } + UpdateVertexArray(); + + } + + void Mesh::UpdateVertexArray() { + + vertexArray.AddIndexComponent(indexBuffer); + vertexArray.AddComponent(0, vertexBuffer); + if (data.normals.ContainsData()) + vertexArray.AddComponent(1, normalBuffer); + if (data.texCoords.ContainsData()) + vertexArray.AddComponent(2, texCoordBuffer); + if (data.tangents.ContainsData()) + vertexArray.AddComponent(3, tangentBuffer); + if (data.colors.ContainsData()) + vertexArray.AddComponent(4, colorBuffer); + } } diff --git a/src/engine/mesh/Mesh.h b/src/engine/mesh/Mesh.h index 1dfccc845..feb1f8483 100644 --- a/src/engine/mesh/Mesh.h +++ b/src/engine/mesh/Mesh.h @@ -1,5 +1,4 @@ -#ifndef AE_MESH_H -#define AE_MESH_H +#pragma once #include "../System.h" #include "../Material.h" @@ -21,28 +20,54 @@ namespace Atlas { Movable }; + typedef uint32_t MeshUsage; + + typedef enum MeshUsageBits { + MultiBufferedBit = (1 << 0), + HostAccessBit = (1 << 1), + } MeshUsageBits; + class Mesh { public: Mesh() = default; - explicit Mesh(MeshData& meshData, - MeshMobility mobility = MeshMobility::Stationary); + explicit Mesh(MeshData& meshData, MeshMobility mobility = MeshMobility::Stationary, + MeshUsage usage = 0); + + explicit Mesh(MeshMobility mobility, MeshUsage usage = 0); + /** + * Transforms the underlying mesh data and updates the buffer data afterwards + * @param transform + */ void SetTransform(mat4 transform); + /** + * Fully updates the buffer data with data available through the MeshData member + */ void UpdateData(); - bool CheckForLoad(); + /** + * Updates the vertex array based on the state of the vertex buffers. + * @note This is useful when running your own data pipeline + */ + void UpdateVertexArray(); std::string name = ""; MeshData data; MeshMobility mobility = MeshMobility::Stationary; + MeshUsage usage = 0; Buffer::VertexArray vertexArray; + Buffer::IndexBuffer indexBuffer; Buffer::VertexBuffer vertexBuffer; + Buffer::VertexBuffer normalBuffer; + Buffer::VertexBuffer texCoordBuffer; + Buffer::VertexBuffer tangentBuffer; + Buffer::VertexBuffer colorBuffer; Ref blas = nullptr; @@ -69,6 +94,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/mesh/MeshData.cpp b/src/engine/mesh/MeshData.cpp index 4b95e07da..a11b95489 100644 --- a/src/engine/mesh/MeshData.cpp +++ b/src/engine/mesh/MeshData.cpp @@ -18,8 +18,8 @@ namespace Atlas { vertices = DataComponent(ComponentFormat::Float); texCoords = DataComponent(ComponentFormat::HalfFloat); - normals = DataComponent(ComponentFormat::PackedFloat); - tangents = DataComponent(ComponentFormat::PackedFloat); + normals = DataComponent(ComponentFormat::PackedNormal); + tangents = DataComponent(ComponentFormat::PackedNormal); colors = DataComponent(ComponentFormat::PackedColor); } @@ -353,7 +353,7 @@ namespace Atlas { for (size_t i = 0; i < that.subData.size(); i++) { materials[i] = that.materials[i]; subData[i] = that.subData[i]; - subData[i].material = &materials[i]; + subData[i].material = materials[i]; } } diff --git a/src/engine/mesh/MeshData.h b/src/engine/mesh/MeshData.h index 3e1f2a8b1..6d769087b 100644 --- a/src/engine/mesh/MeshData.h +++ b/src/engine/mesh/MeshData.h @@ -1,5 +1,4 @@ -#ifndef AE_MESHDATA_H -#define AE_MESHDATA_H +#pragma once #include "../System.h" #include "../volume/AABB.h" @@ -24,7 +23,7 @@ namespace Atlas { uint32_t indicesOffset; uint32_t indicesCount; - Material* material; + Ref material; int32_t materialIdx; Volume::AABB aabb; @@ -98,7 +97,7 @@ namespace Atlas { DataComponent tangents; DataComponent colors; - std::vector materials; + std::vector> materials; std::vector subData; int32_t primitiveType = 0; @@ -121,6 +120,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/ocean/Ocean.h b/src/engine/ocean/Ocean.h index 15a9ac6a7..f175854f8 100644 --- a/src/engine/ocean/Ocean.h +++ b/src/engine/ocean/Ocean.h @@ -1,5 +1,4 @@ -#ifndef AE_OCEAN_H -#define AE_OCEAN_H +#pragma once #include "../System.h" #include "../Camera.h" @@ -38,12 +37,15 @@ namespace Atlas { Texture::Texture2D rippleTexture; Texture::Texture2D foamTexture; - bool enable = true; - vec3 translation = vec3(0.0f); - float displacementScale = 4.0f; - float choppynessScale = 3.0f; + vec3 waterBodyColor = vec3(0.1f, 1.0f, 0.7f); + vec3 deepWaterBodyColor = vec3(0.1f, 0.15f, 0.5f); + vec3 scatterColor = vec3(0.3f, 0.7f, 0.6f); + vec2 waterColorIntensity = vec2(0.1f, 0.2f); + + float displacementScale = 0.11f; + float choppynessScale = 0.21f; float tiling = 64.0f; @@ -56,6 +58,8 @@ namespace Atlas { float shoreWaveLength = 5.0f; bool wireframe = false; + bool enable = true; + bool underwaterShader = true; private: void SortNodes(std::vector& nodes, vec3 cameraLocation); @@ -72,7 +76,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/ocean/OceanNode.h b/src/engine/ocean/OceanNode.h index a63d6e902..263c9a77e 100644 --- a/src/engine/ocean/OceanNode.h +++ b/src/engine/ocean/OceanNode.h @@ -1,5 +1,4 @@ -#ifndef AE_OCEANNODE_H -#define AE_OCEANNODE_H +#pragma once #include "../System.h" #include "../Camera.h" @@ -50,6 +49,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/ocean/OceanSimulation.cpp b/src/engine/ocean/OceanSimulation.cpp index a9578f783..bb9d71449 100644 --- a/src/engine/ocean/OceanSimulation.cpp +++ b/src/engine/ocean/OceanSimulation.cpp @@ -12,7 +12,7 @@ namespace Atlas { namespace Ocean { - OceanSimulation::OceanSimulation(int32_t N, int32_t L) : N(N), L(L) { + OceanSimulation::OceanSimulation(int32_t N, int32_t L, int32_t C) : N(N), L(L), C(C) { noise0 = Texture::Texture2D(N, N, VK_FORMAT_R8_UNORM); noise1 = Texture::Texture2D(N, N, VK_FORMAT_R8_UNORM); @@ -34,20 +34,20 @@ namespace Atlas { noise2.SetData(image2.GetData()); noise3.SetData(image3.GetData()); - displacementMap = Texture::Texture2D(N, N, VK_FORMAT_R16G16B16A16_SFLOAT, + displacementMap = Texture::Texture2DArray(N, N, C, VK_FORMAT_R16G16B16A16_SFLOAT, Texture::Wrapping::Repeat, Texture::Filtering::Linear); - normalMap = Texture::Texture2D(N, N, VK_FORMAT_R16G16B16A16_SFLOAT, + normalMap = Texture::Texture2DArray(N, N, C, VK_FORMAT_R16G16B16A16_SFLOAT, Texture::Wrapping::Repeat, Texture::Filtering::Anisotropic); perlinNoiseMap = Texture::Texture2D(N * 2, N * 2, VK_FORMAT_R32G32B32A32_SFLOAT, Texture::Wrapping::Repeat, Texture::Filtering::Linear); displacementMapPrev = Texture::Texture2D(N, N, VK_FORMAT_R16G16B16A16_SFLOAT, Texture::Wrapping::Repeat, Texture::Filtering::Linear); - h0K = Texture::Texture2D(N, N, VK_FORMAT_R32G32_SFLOAT); + h0K = Texture::Texture2DArray(N, N, C, VK_FORMAT_R32G32_SFLOAT); twiddleIndices = Texture::Texture2D((int32_t)log2((float)N), N, VK_FORMAT_R32G32_SFLOAT); - hTD = Texture::Texture2D(N, N, VK_FORMAT_R32G32B32A32_SFLOAT); - hTDPingpong = Texture::Texture2D(N, N, VK_FORMAT_R32G32B32A32_SFLOAT); + hTD = Texture::Texture2DArray(N, N, C, VK_FORMAT_R32G32B32A32_SFLOAT); + hTDPingpong = Texture::Texture2DArray(N, N, C, VK_FORMAT_R32G32B32A32_SFLOAT); h0Config = PipelineConfig("ocean/h0.csh"); htConfig = PipelineConfig("ocean/ht.csh"); @@ -79,13 +79,13 @@ namespace Atlas { void OceanSimulation::ComputeSpectrum(Graphics::CommandList* commandList) { struct alignas(16) PushConstants { + ivec4 L; + vec2 w = vec2(1.0f); int N = 1; - int L = 1; float A = 1.0f; float windSpeed = 1.0f; float windDependency = 1.0f; float waveSurpression = 1.0f; - vec2 w = vec2(1.0f); }; Graphics::Profiler::BeginQuery("Compute ocean spectrum"); @@ -93,14 +93,14 @@ namespace Atlas { auto pipeline = PipelineManager::GetPipeline(h0Config); commandList->BindPipeline(pipeline); - PushConstants constants { + PushConstants constants{ + .L = ivec4(L, L * 2, L * 4, L * 8), + .w = windDirection, .N = N, - .L = L, .A = waveAmplitude, .windSpeed = windSpeed, .windDependency = windDependency, .waveSurpression = waveSurpression, - .w = windDirection }; commandList->PushConstants("constants", &constants); @@ -114,7 +114,7 @@ namespace Atlas { commandList->ImageMemoryBarrier(h0K.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT); - commandList->Dispatch(N / 16, N / 16, 1); + commandList->Dispatch(N / 16, N / 16, C); commandList->ImageMemoryBarrier(h0K.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); @@ -168,8 +168,8 @@ namespace Atlas { { struct alignas(16) PushConstants { + ivec4 L; int N = 1; - int L = 1; float time = 1.0f; }; @@ -179,8 +179,8 @@ namespace Atlas { commandList->BindPipeline(pipeline); PushConstants constants = { + .L = ivec4(L, L * 2, L * 4, L * 8), .N = N, - .L = L, .time = simulationSpeed * time }; commandList->PushConstants("constants", &constants); @@ -197,7 +197,7 @@ namespace Atlas { commandList->BindImage(hTD.image, 3, 0); commandList->BindImage(h0K.image, 3, 1); - commandList->Dispatch(N / 16, N / 16, 1); + commandList->Dispatch(N / 16, N / 16, C); Graphics::Profiler::EndQuery(); } @@ -263,7 +263,7 @@ namespace Atlas { }; commandList->PushConstants("constants", &constants); - commandList->Dispatch(N / 8, N / 8, 1); + commandList->Dispatch(N / 8, N / 8, C); pingpong = (pingpong + 1) % 2; } @@ -297,15 +297,16 @@ namespace Atlas { commandList->BindImage(displacementMap.image, 3, 0); commandList->BindImage(image, 3, 1); - commandList->Dispatch(N / 16, N / 16, 1); + commandList->Dispatch(N / 16, N / 16, C); Graphics::Profiler::EndQuery(); } { struct alignas(16) PushConstants { + ivec4 L; + vec4 tilingFactors; int N = 1; - int L = 1; float choppyScale = 1.0f; float displacementScale = 1.0f; float tiling = 1.0f; @@ -322,8 +323,9 @@ namespace Atlas { commandList->BindPipeline(pipeline); PushConstants constants = { + .L = ivec4(L, L * 2, L * 4, L * 8), + .tilingFactors = spectrumTilingFactors, .N = N, - .L = L, .choppyScale = choppinessScale, .displacementScale = displacementScale, .tiling = tiling, @@ -347,7 +349,7 @@ namespace Atlas { commandList->BindImage(displacementMap.image, 3, 0); commandList->BindImage(normalMap.image, 3, 1); - commandList->Dispatch(N / 16, N / 16, 1); + commandList->Dispatch(N / 16, N / 16, C); Graphics::Profiler::EndQuery(); } @@ -363,7 +365,8 @@ namespace Atlas { commandList->ImageMemoryBarrier(displacementMap.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); Graphics::Profiler::EndQuery(); Graphics::Profiler::EndQuery(); diff --git a/src/engine/ocean/OceanSimulation.h b/src/engine/ocean/OceanSimulation.h index e878a6724..89020a97c 100644 --- a/src/engine/ocean/OceanSimulation.h +++ b/src/engine/ocean/OceanSimulation.h @@ -1,9 +1,9 @@ -#ifndef AE_OCEANSIMULATION_H -#define AE_OCEANSIMULATION_H +#pragma once #include "../System.h" #include "../common/NoiseGenerator.h" #include "../texture/Texture2D.h" +#include "../texture/Texture2DArray.h" #include "../pipeline/PipelineConfig.h" #include "../graphics/CommandList.h" @@ -18,7 +18,7 @@ namespace Atlas { public: OceanSimulation() = default; - OceanSimulation(int32_t N, int32_t L); + OceanSimulation(int32_t N, int32_t L, int32_t C = 4); void Update(float deltaTime); @@ -26,8 +26,8 @@ namespace Atlas { void Compute(Graphics::CommandList* commandList); - Texture::Texture2D displacementMap; - Texture::Texture2D normalMap; + Texture::Texture2DArray displacementMap; + Texture::Texture2DArray normalMap; Texture::Texture2D perlinNoiseMap; Texture::Texture2D twiddleIndices; @@ -36,11 +36,16 @@ namespace Atlas { int32_t N; int32_t L; + int32_t C; float choppinessScale = 3.0f; float displacementScale = 4.0f; float tiling = 64.0f; + vec4 spectrumTilingFactors = vec4(1.0f, 3.0f, 6.0f, 9.0f); + vec4 spectrumWeights = vec4(1.0f, 0.5f, 0.25f, 0.125f); + vec4 spectrumFadeoutDistances = vec4(200.0f, 400.0f, 1000.0f, 4000.0f); + float waveAmplitude = 1.0f; float waveSurpression = 0.001f; @@ -51,7 +56,7 @@ namespace Atlas { float foamTemporalWeight = 0.985f; float foamTemporalThreshold = 0.6f; - float foamOffset = 0.85f; + float foamOffset = 0.0f; float foamScale = 1.5f; float foamSize = 4.0f; @@ -87,39 +92,13 @@ namespace Atlas { Texture::Texture2D noise2; Texture::Texture2D noise3; - Texture::Texture2D h0K; - - Texture::Texture2D hTD; - Texture::Texture2D hTDPingpong; - - /* - OldShader::Uniform* htNUniform; - OldShader::Uniform* htLUniform; - OldShader::Uniform* htTimeUniform; - - OldShader::Uniform* butterflyStageUniform; - OldShader::Uniform* butterflyPingpongUniform; - OldShader::Uniform* butterflyNUniform; - OldShader::Uniform* butterflyPreTwiddleUniform; + Texture::Texture2DArray h0K; - OldShader::Uniform* inversionNUniform; - OldShader::Uniform* inversionPingpongUniform; - - OldShader::Uniform* normalNUniform; - OldShader::Uniform* normalLUniform; - OldShader::Uniform* normalChoppyScaleUniform; - OldShader::Uniform* normalDisplacementScaleUniform; - OldShader::Uniform* normalTilingUniform; - OldShader::Uniform* normalFoamTemporalWeightUniform; - OldShader::Uniform* normalFoamTemporalThresholdUniform; - OldShader::Uniform* normalFoamOffsetUniform; - */ + Texture::Texture2DArray hTD; + Texture::Texture2DArray hTDPingpong; }; } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/ocean/OceanState.h b/src/engine/ocean/OceanState.h index 621b2d751..0d481dd2c 100644 --- a/src/engine/ocean/OceanState.h +++ b/src/engine/ocean/OceanState.h @@ -1,5 +1,4 @@ -#ifndef AE_OCEANSTATE_H -#define AE_OCEANSTATE_H +#pragma once #include "../System.h" @@ -15,7 +14,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/pipeline/PipelineConfig.cpp b/src/engine/pipeline/PipelineConfig.cpp index 7f233afa2..3e1e9bf24 100644 --- a/src/engine/pipeline/PipelineConfig.cpp +++ b/src/engine/pipeline/PipelineConfig.cpp @@ -121,6 +121,11 @@ namespace Atlas { HashCombine(variantHash, graphicsPipelineDesc.colorBlendAttachment.blendEnable); HashCombine(variantHash, graphicsPipelineDesc.rasterizer.polygonMode); HashCombine(variantHash, graphicsPipelineDesc.rasterizer.cullMode); + + for (int32_t i = 0; i < graphicsPipelineDesc.vertexInputInfo.vertexAttributeDescriptionCount; i++) { + auto& vertexAttributeDesc = graphicsPipelineDesc.vertexInputInfo.pVertexAttributeDescriptions[i]; + HashCombine(variantHash, vertexAttributeDesc.format); + } } } diff --git a/src/engine/pipeline/PipelineConfig.h b/src/engine/pipeline/PipelineConfig.h index 9c4b56879..95d0a8f23 100644 --- a/src/engine/pipeline/PipelineConfig.h +++ b/src/engine/pipeline/PipelineConfig.h @@ -1,7 +1,7 @@ -#ifndef AE_PIPELINECONFIG_H -#define AE_PIPELINECONFIG_H +#pragma once #include "../graphics/Pipeline.h" +#include "../common/Hash.h" #include #include @@ -39,8 +39,8 @@ namespace Atlas { bool IsValid() const; - size_t shaderHash = 0; - size_t variantHash = 0; + Hash shaderHash = 0; + Hash variantHash = 0; private: void CalculateShaderHash(); @@ -54,6 +54,4 @@ namespace Atlas { Graphics::GraphicsPipelineDesc graphicsPipelineDesc; }; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/pipeline/PipelineManager.cpp b/src/engine/pipeline/PipelineManager.cpp index 1638a87f2..e17cfe22d 100644 --- a/src/engine/pipeline/PipelineManager.cpp +++ b/src/engine/pipeline/PipelineManager.cpp @@ -38,7 +38,10 @@ namespace Atlas { if (lastModifiedMap.find(includePath) != lastModifiedMap.end()) continue; - lastModifiedMap[includePath] = std::filesystem::last_write_time(includePath); + std::error_code errorCode; + auto lastModified = std::filesystem::last_write_time(includePath, errorCode); + if (!errorCode) + lastModifiedMap[includePath] = lastModified; } } } @@ -54,6 +57,18 @@ namespace Atlas { } + void PipelineManager::EnableHotReload() { + + hotReload = true; + + } + + void PipelineManager::DisableHotReload() { + + hotReload = false; + + } + void PipelineManager::AddPipeline(PipelineConfig &config) { GetOrCreatePipeline(config); diff --git a/src/engine/pipeline/PipelineManager.h b/src/engine/pipeline/PipelineManager.h index bbf2c3119..80529905c 100644 --- a/src/engine/pipeline/PipelineManager.h +++ b/src/engine/pipeline/PipelineManager.h @@ -1,5 +1,4 @@ -#ifndef AE_PIPELINEMANAGER_H -#define AE_PIPELINEMANAGER_H +#pragma once #include "PipelineConfig.h" @@ -49,6 +48,4 @@ namespace Atlas { }; -} - -#endif +} \ No newline at end of file diff --git a/src/engine/postprocessing/ChromaticAberration.h b/src/engine/postprocessing/ChromaticAberration.h index 3bb9a34a2..25ba02045 100644 --- a/src/engine/postprocessing/ChromaticAberration.h +++ b/src/engine/postprocessing/ChromaticAberration.h @@ -1,5 +1,4 @@ -#ifndef AE_CHROMATICABBERATION_H -#define AE_CHROMATICABBERATION_H +#pragma once #include "../System.h" @@ -27,7 +26,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/postprocessing/FilmGrain.h b/src/engine/postprocessing/FilmGrain.h index 0f65c2e67..dd6d6fa71 100644 --- a/src/engine/postprocessing/FilmGrain.h +++ b/src/engine/postprocessing/FilmGrain.h @@ -1,5 +1,4 @@ -#ifndef AE_FILMGRAIN_H -#define AE_FILMGRAIN_H +#pragma once #include "../System.h" @@ -21,7 +20,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/postprocessing/PostProcessing.h b/src/engine/postprocessing/PostProcessing.h index 248b21bb0..70619b3e8 100644 --- a/src/engine/postprocessing/PostProcessing.h +++ b/src/engine/postprocessing/PostProcessing.h @@ -1,5 +1,4 @@ -#ifndef AE_POSTPROCESSING_H -#define AE_POSTPROCESSING_H +#pragma once #include "../System.h" @@ -31,6 +30,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/postprocessing/Sharpen.h b/src/engine/postprocessing/Sharpen.h index cdcfb4342..70c1b87a8 100644 --- a/src/engine/postprocessing/Sharpen.h +++ b/src/engine/postprocessing/Sharpen.h @@ -1,5 +1,4 @@ -#ifndef AE_SHARPEN_H -#define AE_SHARPEN_H +#pragma once #include "../System.h" @@ -21,6 +20,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/postprocessing/TAA.h b/src/engine/postprocessing/TAA.h index e34e4a265..e17610845 100644 --- a/src/engine/postprocessing/TAA.h +++ b/src/engine/postprocessing/TAA.h @@ -1,5 +1,4 @@ -#ifndef AE_TAA_H -#define AE_TAA_H +#pragma once #include "../System.h" @@ -22,6 +21,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/postprocessing/Vignette.h b/src/engine/postprocessing/Vignette.h index 8f3429e49..d4b3e6ae3 100644 --- a/src/engine/postprocessing/Vignette.h +++ b/src/engine/postprocessing/Vignette.h @@ -1,5 +1,4 @@ -#ifndef AE_VIGNETTE_H -#define AE_VIGNETTE_H +#pragma once #include "../System.h" @@ -32,6 +31,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/AORenderer.cpp b/src/engine/renderer/AORenderer.cpp index 48d2bfb96..4d636000f 100644 --- a/src/engine/renderer/AORenderer.cpp +++ b/src/engine/renderer/AORenderer.cpp @@ -126,7 +126,7 @@ namespace Atlas { .frameCount = frameCount++ }; ssUniformBuffer.SetData(&uniforms, 0); - ssSamplesUniformBuffer.SetData(&ao->samples[0], 0); + ssSamplesUniformBuffer.SetData(&ao->samples[0], 0, ao->samples.size() * sizeof(vec4)); commandList->BindImage(target->aoTexture.image, 3, 0); diff --git a/src/engine/renderer/AORenderer.h b/src/engine/renderer/AORenderer.h index c299cd815..2d35a5e58 100644 --- a/src/engine/renderer/AORenderer.h +++ b/src/engine/renderer/AORenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_RTAORENDERER_H -#define AE_RTAORENDERER_H +#pragma once #include "../System.h" @@ -54,6 +53,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/renderer/AtmosphereRenderer.h b/src/engine/renderer/AtmosphereRenderer.h index 44bac9ff4..9d715bc06 100644 --- a/src/engine/renderer/AtmosphereRenderer.h +++ b/src/engine/renderer/AtmosphereRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_ATMOSPHERERENDERER_H -#define AE_ATMOSPHERERENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -49,6 +48,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/renderer/BlurRenderer.h b/src/engine/renderer/BlurRenderer.h index 3ab6d08c7..d46feadb8 100644 --- a/src/engine/renderer/BlurRenderer.h +++ b/src/engine/renderer/BlurRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_BLURRENDERER_H -#define AE_BLURRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -35,6 +34,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/renderer/DDGIRenderer.h b/src/engine/renderer/DDGIRenderer.h index 201a487be..c4efd2c48 100644 --- a/src/engine/renderer/DDGIRenderer.h +++ b/src/engine/renderer/DDGIRenderer.h @@ -1,5 +1,4 @@ -#ifndef IRRADIANCEVOLUMERENDERER_H -#define IRRADIANCEVOLUMERENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -78,6 +77,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/DecalRenderer.h b/src/engine/renderer/DecalRenderer.h index fb3704e7a..9c1a20045 100644 --- a/src/engine/renderer/DecalRenderer.h +++ b/src/engine/renderer/DecalRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_DECALRENDERER_H -#define AE_DECALRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -44,6 +43,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/renderer/DirectLightRenderer.cpp b/src/engine/renderer/DirectLightRenderer.cpp index 08b9e44a5..88a4239ec 100644 --- a/src/engine/renderer/DirectLightRenderer.cpp +++ b/src/engine/renderer/DirectLightRenderer.cpp @@ -45,7 +45,7 @@ namespace Atlas { auto& lightUniform = uniforms.light; lightUniform.location = vec4(0.0f); lightUniform.direction = vec4(direction, 0.0f); - lightUniform.color = vec4(light->color, 0.0f); + lightUniform.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(light->color), 0.0f); lightUniform.intensity = light->intensity; lightUniform.scatteringFactor = 1.0f; lightUniform.radius = 1.0f; diff --git a/src/engine/renderer/DirectLightRenderer.h b/src/engine/renderer/DirectLightRenderer.h index f36b0e043..07a460107 100644 --- a/src/engine/renderer/DirectLightRenderer.h +++ b/src/engine/renderer/DirectLightRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_DIRECTLIGHTRENDERER_H -#define AE_DIRECTLIGHTRENDERER_H +#pragma once #include "Renderer.h" @@ -32,6 +31,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/renderer/ExampleRenderer.cpp b/src/engine/renderer/ExampleRenderer.cpp index 8241cd93c..56ae339fc 100644 --- a/src/engine/renderer/ExampleRenderer.cpp +++ b/src/engine/renderer/ExampleRenderer.cpp @@ -116,7 +116,7 @@ namespace Atlas { mainFrameBuffer = device->CreateFrameBuffer(frameBufferDesc); } { - auto mesh = Atlas::ResourceManager::GetResourceWithLoader( + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoader( "sponza/sponza.obj", Atlas::Loader::ModelLoader::LoadMesh, false, glm::mat4(1.0f), 2048 ); diff --git a/src/engine/renderer/ExampleRenderer.h b/src/engine/renderer/ExampleRenderer.h index 9c49721dc..b55655cfa 100644 --- a/src/engine/renderer/ExampleRenderer.h +++ b/src/engine/renderer/ExampleRenderer.h @@ -1,5 +1,4 @@ -#ifndef ATLASENGINE_VULKANTESTRENDERER_H -#define ATLASENGINE_VULKANTESTRENDERER_H +#pragma once #include "Renderer.h" @@ -53,6 +52,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/renderer/GBufferDownscaleRenderer.h b/src/engine/renderer/GBufferDownscaleRenderer.h index f711fdb13..0fa66fce7 100644 --- a/src/engine/renderer/GBufferDownscaleRenderer.h +++ b/src/engine/renderer/GBufferDownscaleRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_GBUFFERDOWNSCALERENDERER_H -#define AE_GBUFFERDOWNSCALERENDERER_H +#pragma once #include "Renderer.h" @@ -31,6 +30,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/renderer/ImpostorRenderer.h b/src/engine/renderer/ImpostorRenderer.h index 82dedaa87..27c0ea75c 100644 --- a/src/engine/renderer/ImpostorRenderer.h +++ b/src/engine/renderer/ImpostorRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_IMPOSTORRENDERER_H -#define AE_IMPOSTORRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -37,6 +36,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/ImpostorShadowRenderer.h b/src/engine/renderer/ImpostorShadowRenderer.h index 93a339eda..e5a8809f5 100644 --- a/src/engine/renderer/ImpostorShadowRenderer.h +++ b/src/engine/renderer/ImpostorShadowRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_IMPOSTORSHADOWRENDERER_H -#define AE_IMPOSTORSHADOWRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -29,6 +28,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/IndirectLightRenderer.h b/src/engine/renderer/IndirectLightRenderer.h index 0d432093c..5734e5b4d 100644 --- a/src/engine/renderer/IndirectLightRenderer.h +++ b/src/engine/renderer/IndirectLightRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_INDIRECTLIGHTRENDERER_H -#define AE_INDIRECTLIGHTRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -35,6 +34,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/MainRenderer.cpp b/src/engine/renderer/MainRenderer.cpp index 2aaf75a02..2a36504bd 100644 --- a/src/engine/renderer/MainRenderer.cpp +++ b/src/engine/renderer/MainRenderer.cpp @@ -117,6 +117,9 @@ namespace Atlas { if (scene->vegetation) vegetationRenderer.helper.PrepareInstanceBuffer(*scene->vegetation, camera, commandList); + if (scene->ocean && scene->ocean->enable) + scene->ocean->simulation.Compute(commandList); + if (scene->sky.probe) { if (scene->sky.probe->update) { FilterProbe(scene->sky.probe.get(), commandList); @@ -201,6 +204,8 @@ namespace Atlas { Graphics::Profiler::EndQuery(); } + oceanRenderer.RenderDepthOnly(viewport, target, camera, scene, commandList); + auto targetData = target->GetData(FULL_RES); commandList->BindImage(targetData->baseColorTexture->image, targetData->baseColorTexture->sampler, 1, 2); @@ -230,7 +235,7 @@ namespace Atlas { {target->historyAoLengthTexture.image, layout, access}, {target->historyReflectionTexture.image, layout, access}, {target->historyReflectionMomentsTexture.image, layout, access}, - {target->historyVolumetricCloudsTexture.image, layout, access}, + {target->historyVolumetricCloudsTexture.image, layout, access} }; commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); } @@ -252,6 +257,8 @@ namespace Atlas { {rtData->materialIdxTexture->image, layout, access}, {rtData->stencilTexture->image, layout, access}, {rtData->velocityTexture->image, layout, access}, + {target->oceanStencilTexture.image, layout, access}, + {target->oceanDepthTexture.image, layout, access} }; commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); @@ -295,9 +302,8 @@ namespace Atlas { Graphics::Profiler::EndQuery(); } - oceanRenderer.Render(viewport, target, camera, scene, commandList); - - downscaleRenderer.Downscale(target, commandList); + // This was needed after the ocean renderer, if we ever want to have alpha transparency we need it again + // downscaleRenderer.Downscale(target, commandList); { volumetricCloudRenderer.Render(viewport, target, camera, scene, commandList); @@ -305,6 +311,8 @@ namespace Atlas { volumetricRenderer.Render(viewport, target, camera, scene, commandList); } + oceanRenderer.Render(viewport, target, camera, scene, commandList); + { taaRenderer.Render(viewport, target, camera, scene, commandList); @@ -792,6 +800,8 @@ namespace Atlas { .cameraDirection = vec4(camera->direction, 0.0f), .cameraUp = vec4(camera->up, 0.0f), .cameraRight = vec4(camera->right, 0.0f), + .planetCenter = vec4(scene->sky.planetCenter, 0.0f), + .planetRadius = scene->sky.planetRadius, .time = Clock::Get(), .deltaTime = Clock::GetDelta(), .frameCount = frameCount @@ -835,7 +845,7 @@ namespace Atlas { auto meshes = scene->GetMeshes(); for (auto& mesh : meshes) { - if (!mesh->impostor) continue; + if (!mesh.IsLoaded() || !mesh->impostor) continue; auto impostor = mesh->impostor; Mesh::Impostor::ImpostorInfo impostorInfo = { @@ -869,14 +879,11 @@ namespace Atlas { for (auto material : sceneMaterials) { PackedMaterial packed; - auto emissiveIntensity = glm::max(glm::max(material->emissiveColor.r, - material->emissiveColor.g), material->emissiveColor.b); - - packed.baseColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(material->baseColor, 0.0f)); - packed.emissiveColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(material->emissiveColor / emissiveIntensity, 0.0f)); - packed.transmissionColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(material->transmissiveColor, 0.0f)); + packed.baseColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(material->baseColor), 0.0f)); + packed.emissiveColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(material->emissiveColor), 0.0f)); + packed.transmissionColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(material->transmissiveColor), 0.0f)); - packed.emissiveIntensityTiling = glm::packHalf2x16(vec2(emissiveIntensity, material->tiling)); + packed.emissiveIntensityTiling = glm::packHalf2x16(vec2(material->emissiveIntensity, material->tiling)); vec4 data0, data1, data2; @@ -928,7 +935,7 @@ namespace Atlas { packed.baseColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(1.0f)); packed.emissiveColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(0.0f)); - packed.transmissionColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(impostor->transmissiveColor, 1.0f)); + packed.transmissionColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(impostor->transmissiveColor), 1.0f)); vec4 data0, data1, data2; diff --git a/src/engine/renderer/MainRenderer.h b/src/engine/renderer/MainRenderer.h index 7d8a65e59..bc4ad2857 100644 --- a/src/engine/renderer/MainRenderer.h +++ b/src/engine/renderer/MainRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_MAINRENDERER_H -#define AE_MAINRENDERER_H +#pragma once #include "../System.h" #include "../graphics/GraphicsDevice.h" @@ -162,6 +161,8 @@ namespace Atlas { vec4 cameraDirection; vec4 cameraUp; vec4 cameraRight; + vec4 planetCenter; + float planetRadius; float time; float deltaTime; uint32_t frameCount; @@ -244,6 +245,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/OceanRenderer.cpp b/src/engine/renderer/OceanRenderer.cpp index d20b752a6..ad806dcbe 100644 --- a/src/engine/renderer/OceanRenderer.cpp +++ b/src/engine/renderer/OceanRenderer.cpp @@ -14,8 +14,10 @@ namespace Atlas { Helper::GeometryHelper::GenerateGridVertexArray(vertexArray, 129, 1.0f / 128.0f, false); causticPipelineConfig = PipelineConfig("ocean/caustics.csh"); + underWaterPipelineConfig = PipelineConfig("ocean/underwater.csh"); uniformBuffer = Buffer::UniformBuffer(sizeof(Uniforms)); + depthUniformBuffer = Buffer::UniformBuffer(sizeof(Uniforms)); lightUniformBuffer = Buffer::UniformBuffer(sizeof(Light)); cloudShadowUniformBuffer = Buffer::UniformBuffer(sizeof(CloudShadow)); @@ -44,8 +46,7 @@ namespace Atlas { auto ocean = scene->ocean; auto clouds = scene->sky.clouds; - - ocean->simulation.Compute(commandList); + auto fog = scene->fog; auto sun = scene->sky.sun.get(); if (!sun) { @@ -63,7 +64,7 @@ namespace Atlas { Light lightUniform; lightUniform.direction = vec4(sun->direction, 0.0); - lightUniform.color = vec4(sun->color, 0.0); + lightUniform.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(sun->color), 0.0); lightUniform.intensity = sun->intensity; if (sun->GetVolumetric()) { @@ -78,20 +79,28 @@ namespace Atlas { shadowUniform.distance = distance; shadowUniform.bias = shadow->bias; shadowUniform.cascadeCount = shadow->componentCount; + shadowUniform.cascadeBlendDistance = shadow->cascadeBlendDistance; shadowUniform.resolution = vec2(shadow->resolution); commandList->BindImage(shadow->maps.image, shadowSampler, 3, 8); - for (int32_t i = 0; i < sun->GetShadow()->componentCount; i++) { - auto& cascade = shadow->components[i]; - auto& cascadeUniform = shadowUniform.cascades[i]; - auto frustum = Volume::Frustum(cascade.frustumMatrix); - auto corners = frustum.GetCorners(); - auto texelSize = glm::max(abs(corners[0].x - corners[1].x), - abs(corners[1].y - corners[3].y)) / (float)sun->GetShadow()->resolution; - cascadeUniform.distance = cascade.farDistance; - cascadeUniform.cascadeSpace = cascade.projectionMatrix * cascade.viewMatrix * camera->invViewMatrix; - cascadeUniform.texelSize = texelSize; + auto componentCount = shadow->componentCount; + for (int32_t i = 0; i < MAX_SHADOW_CASCADE_COUNT + 1; i++) { + if (i < componentCount) { + auto cascade = &shadow->components[i]; + auto frustum = Volume::Frustum(cascade->frustumMatrix); + auto corners = frustum.GetCorners(); + auto texelSize = glm::max(abs(corners[0].x - corners[1].x), + abs(corners[1].y - corners[3].y)) / (float)sun->GetShadow()->resolution; + shadowUniform.cascades[i].distance = cascade->farDistance; + shadowUniform.cascades[i].cascadeSpace = cascade->projectionMatrix * + cascade->viewMatrix * camera->invViewMatrix; + shadowUniform.cascades[i].texelSize = texelSize; + } + else { + auto cascade = &shadow->components[componentCount - 1]; + shadowUniform.cascades[i].distance = cascade->farDistance; + } } } else { @@ -101,6 +110,8 @@ namespace Atlas { lightUniformBuffer.SetData(&lightUniform, 0); lightUniformBuffer.Bind(commandList, 3, 12); + bool cloudsEnabled = clouds && clouds->enable; + bool cloudShadowsEnabled = clouds && clouds->enable && clouds->castShadow; CloudShadow cloudShadowUniform; @@ -141,7 +152,7 @@ namespace Atlas { commandList->BindImage(depthImage, nearestSampler, 3, 0); commandList->BindImage(lightingImage, 3, 1); - commandList->ImageMemoryBarrier(lightingImage, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT); + commandList->ImageMemoryBarrier(lightingImage, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); commandList->PushConstants("constants", &ocean->translation.y); @@ -182,8 +193,8 @@ namespace Atlas { commandList->CopyImage(depthImage, depthTexture.image); imageBarriers = { - {colorImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, - {depthImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {colorImage, VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT}, + {depthImage, VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT}, {refractionTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {depthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; @@ -197,8 +208,12 @@ namespace Atlas { commandList->BeginRenderPass(target->lightingFrameBufferWithStencil->renderPass, target->lightingFrameBufferWithStencil); - auto config = GeneratePipelineConfig(target, ocean->wireframe); + auto config = GeneratePipelineConfig(target, false, ocean->wireframe); + if (cloudsEnabled) config.AddMacro("CLOUDS"); if (cloudShadowsEnabled) config.AddMacro("CLOUD_SHADOWS"); + if (ocean->rippleTexture.IsValid()) config.AddMacro("RIPPLE_TEXTURE"); + if (ocean->foamTexture.IsValid()) config.AddMacro("FOAM_TEXTURE"); + if (scene->terrain && scene->terrain->shoreLine.IsValid()) config.AddMacro("TERRAIN"); auto pipeline = PipelineManager::GetPipeline(config); @@ -207,7 +222,17 @@ namespace Atlas { vertexArray.Bind(commandList); Uniforms uniforms = { + .waterBodyColor = vec4(Common::ColorConverter::ConvertSRGBToLinear(ocean->waterBodyColor), 1.0f), + .deepWaterBodyColor = vec4(Common::ColorConverter::ConvertSRGBToLinear(ocean->deepWaterBodyColor), 1.0f), + .scatterColor = vec4(Common::ColorConverter::ConvertSRGBToLinear(ocean->scatterColor), 1.0f), + .translation = vec4(ocean->translation, 1.0f), + + .waterColorIntensity = vec4(Common::ColorConverter::ConvertSRGBToLinear(ocean->waterColorIntensity), 0.0f, 1.0f), + + .spectrumTilingFactors = ocean->simulation.spectrumTilingFactors, + .spectrumWeights = ocean->simulation.spectrumWeights, + .spectrumFadeoutDistances = ocean->simulation.spectrumFadeoutDistances, .displacementScale = ocean->displacementScale, .choppyScale = ocean->choppynessScale, @@ -224,6 +249,7 @@ namespace Atlas { .shoreWaveLength = ocean->shoreWaveLength, .terrainSideLength = -1.0f, .N = ocean->simulation.N, + .spectrumCount = ocean->simulation.C, }; ocean->simulation.displacementMap.Bind(commandList, 3, 0); @@ -235,6 +261,11 @@ namespace Atlas { scene->sky.GetProbe()->cubemap.Bind(commandList, 3, 3); } + if (cloudsEnabled) { + target->volumetricCloudsTexture.Bind(commandList, 3, 16); + uniforms.innerCloudRadius = scene->sky.planetRadius + clouds->minHeight; + } + refractionTexture.Bind(commandList, 3, 4); depthTexture.Bind(commandList, 3, 5); @@ -251,6 +282,17 @@ namespace Atlas { } } + if (fog && fog->enable) { + target->volumetricTexture.Bind(commandList, 3, 7); + + auto& fogUniform = uniforms.fog; + fogUniform.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(fog->color), 1.0f); + fogUniform.density = fog->density; + fogUniform.heightFalloff = fog->heightFalloff; + fogUniform.height = fog->height; + fogUniform.scatteringAnisotropy = glm::clamp(fog->scatteringAnisotropy, -0.999f, 0.999f); + } + if (ocean->rippleTexture.IsValid()) { ocean->rippleTexture.Bind(commandList, 3, 10); } @@ -300,6 +342,59 @@ namespace Atlas { commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); } + if (ocean->underwaterShader) { + Graphics::Profiler::EndAndBeginQuery("Underwater"); + + auto& colorImage = target->lightingFrameBuffer->GetColorImage(0); + + std::vector imageBarriers; + std::vector bufferBarriers; + + imageBarriers = { + {colorImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, + {refractionTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, + }; + commandList->PipelineBarrier(imageBarriers, bufferBarriers, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + + commandList->CopyImage(colorImage, refractionTexture.image); + + imageBarriers = { + {colorImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {refractionTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + }; + commandList->PipelineBarrier(imageBarriers, bufferBarriers, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + const int32_t groupSize = 8; + auto res = ivec2(target->GetWidth(), target->GetHeight()); + + ivec2 groupCount = res / groupSize; + groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); + groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); + + underWaterPipelineConfig.ManageMacro("TERRAIN", scene->terrain && scene->terrain->shoreLine.IsValid()); + auto pipeline = PipelineManager::GetPipeline(underWaterPipelineConfig); + + commandList->BindPipeline(pipeline); + + auto lightingImage = target->lightingFrameBuffer->GetColorImage(0); + auto stencilImage = target->lightingFrameBuffer->GetColorImage(2); + auto depthImage = target->lightingFrameBuffer->GetDepthImage(); + + refractionTexture.Bind(commandList, 3, 4); + commandList->BindImage(depthImage, nearestSampler, 3, 16); + commandList->BindImage(stencilImage, nearestSampler, 3, 17); + commandList->BindImage(target->oceanDepthTexture.image, nearestSampler, 3, 18); + commandList->BindImage(lightingImage, 3, 20); + + commandList->ImageMemoryBarrier(lightingImage, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + commandList->ImageMemoryBarrier(lightingImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); + } + Graphics::Profiler::EndQuery(); } @@ -308,15 +403,132 @@ namespace Atlas { } - PipelineConfig OceanRenderer::GeneratePipelineConfig(RenderTarget* target, bool wireframe) { + void OceanRenderer::RenderDepthOnly(Viewport* viewport, RenderTarget* target, Camera* camera, + Scene::Scene* scene, Graphics::CommandList* commandList) { + + if (!scene->ocean || !scene->ocean->enable) + return; + + Graphics::Profiler::BeginQuery("Ocean depth"); + + auto ocean = scene->ocean; + + Graphics::Profiler::BeginQuery("Rendering"); + + commandList->BeginRenderPass(target->oceanDepthOnlyFrameBuffer->renderPass, + target->oceanDepthOnlyFrameBuffer); + + auto config = GeneratePipelineConfig(target, true, ocean->wireframe); + if (scene->terrain && scene->terrain->shoreLine.IsValid()) config.AddMacro("TERRAIN"); + + auto pipeline = PipelineManager::GetPipeline(config); + + commandList->BindPipeline(pipeline); + + vertexArray.Bind(commandList); + + Uniforms uniforms = { + .waterBodyColor = vec4(Common::ColorConverter::ConvertSRGBToLinear(ocean->waterBodyColor), 1.0f), + .deepWaterBodyColor = vec4(Common::ColorConverter::ConvertSRGBToLinear(ocean->deepWaterBodyColor), 1.0f), + .scatterColor = vec4(Common::ColorConverter::ConvertSRGBToLinear(ocean->scatterColor), 1.0f), + + .translation = vec4(ocean->translation, 1.0f), + + .waterColorIntensity = vec4(Common::ColorConverter::ConvertSRGBToLinear(ocean->waterColorIntensity), 0.0f, 1.0f), + + .spectrumTilingFactors = ocean->simulation.spectrumTilingFactors, + .spectrumWeights = ocean->simulation.spectrumWeights, + .spectrumFadeoutDistances = ocean->simulation.spectrumFadeoutDistances, + + .displacementScale = ocean->displacementScale, + .choppyScale = ocean->choppynessScale, + .tiling = ocean->tiling, + .hasRippleTexture = ocean->rippleTexture.IsValid() ? 1 : 0, + + .shoreWaveDistanceOffset = ocean->shoreWaveDistanceOffset, + .shoreWaveDistanceScale = ocean->shoreWaveDistanceScale, + .shoreWaveAmplitude = ocean->shoreWaveAmplitude, + .shoreWaveSteepness = ocean->shoreWaveSteepness, + + .shoreWavePower = ocean->shoreWavePower, + .shoreWaveSpeed = ocean->shoreWaveSpeed, + .shoreWaveLength = ocean->shoreWaveLength, + .terrainSideLength = -1.0f, + .N = ocean->simulation.N, + .spectrumCount = ocean->simulation.C, + }; + + ocean->simulation.displacementMap.Bind(commandList, 3, 0); + ocean->simulation.normalMap.Bind(commandList, 3, 1); + + ocean->foamTexture.Bind(commandList, 3, 2); + + if (scene->sky.GetProbe()) { + scene->sky.GetProbe()->cubemap.Bind(commandList, 3, 3); + } + + refractionTexture.Bind(commandList, 3, 4); + depthTexture.Bind(commandList, 3, 5); + + if (scene->terrain) { + if (scene->terrain->shoreLine.IsValid()) { + auto terrain = scene->terrain; + + uniforms.terrainTranslation = vec4(terrain->translation, 1.0f); + uniforms.terrainSideLength = scene->terrain->sideLength; + uniforms.terrainHeightScale = scene->terrain->heightScale; + + scene->terrain->shoreLine.Bind(commandList, 3, 9); + + } + } + + if (ocean->rippleTexture.IsValid()) { + ocean->rippleTexture.Bind(commandList, 3, 10); + } + + depthUniformBuffer.SetData(&uniforms, 0); + depthUniformBuffer.Bind(commandList, 3, 11); + + ocean->simulation.perlinNoiseMap.Bind(commandList, 3, 13); + + auto renderList = ocean->GetRenderList(); + + for (auto node : renderList) { + + PushConstants constants = { + .nodeSideLength = node->sideLength, + + .leftLoD = node->leftLoDStitch, + .topLoD = node->topLoDStitch, + .rightLoD = node->rightLoDStitch, + .bottomLoD = node->bottomLoDStitch, + + .nodeLocation = node->location, + }; + + commandList->PushConstants("constants", &constants); + + commandList->DrawIndexed(vertexArray.GetIndexComponent().elementCount); + + } + + commandList->EndRenderPass(); + + Graphics::Profiler::EndQuery(); + Graphics::Profiler::EndQuery(); + + } + + PipelineConfig OceanRenderer::GeneratePipelineConfig(RenderTarget* target, bool depthOnly, bool wireframe) { const auto shaderConfig = ShaderConfig { - {"ocean/ocean.vsh", VK_SHADER_STAGE_VERTEX_BIT}, - {"ocean/ocean.fsh", VK_SHADER_STAGE_FRAGMENT_BIT}, + {depthOnly ? "ocean/depth.vsh" : "ocean/ocean.vsh", VK_SHADER_STAGE_VERTEX_BIT}, + {depthOnly ? "ocean/depth.fsh" : "ocean/ocean.fsh", VK_SHADER_STAGE_FRAGMENT_BIT}, }; auto pipelineDesc = Graphics::GraphicsPipelineDesc { - .frameBuffer = target->lightingFrameBufferWithStencil, + .frameBuffer = depthOnly ? target->oceanDepthOnlyFrameBuffer : target->lightingFrameBufferWithStencil, .vertexInputInfo = vertexArray.GetVertexInputState(), }; diff --git a/src/engine/renderer/OceanRenderer.h b/src/engine/renderer/OceanRenderer.h index 6205ddf6b..27c5ca54d 100644 --- a/src/engine/renderer/OceanRenderer.h +++ b/src/engine/renderer/OceanRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_OCEANRENDERER_H -#define AE_OCEANRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -20,9 +19,25 @@ namespace Atlas { void Render(Viewport* viewport, RenderTarget* target, Camera* camera, Scene::Scene* scene, Graphics::CommandList* commandList); + void RenderDepthOnly(Viewport* viewport, RenderTarget* target, Camera* camera, + Scene::Scene* scene, Graphics::CommandList* commandList); + private: struct alignas(16) Uniforms { + Fog fog; + + vec4 waterBodyColor; + vec4 deepWaterBodyColor; + vec4 scatterColor; + vec4 translation; + vec4 terrainTranslation; + + vec4 waterColorIntensity; + + vec4 spectrumTilingFactors; + vec4 spectrumWeights; + vec4 spectrumFadeoutDistances; float displacementScale; float choppyScale; @@ -39,53 +54,10 @@ namespace Atlas { float shoreWaveLength; float terrainSideLength; - vec4 terrainTranslation; - float terrainHeightScale; int32_t N; - }; - - struct alignas(16) Cascade { - float distance; - float texelSize; - float aligment0; - float aligment1; - mat4 cascadeSpace; - }; - - struct alignas(16) Shadow { - float distance; - float bias; - - float cascadeBlendDistance; - - int cascadeCount; - - float aligment0; - float aligment1; - - vec2 resolution; - - mat4 cloudViewMatrix; - mat4 cloudProjectionMatrix; - - Cascade cascades[6]; - }; - - struct alignas(16) Light { - vec4 location; - vec4 direction; - - vec4 color; - float intensity; - - float scatteringFactor; - - float radius; - - float alignment; - - Shadow shadow; + int32_t spectrumCount; + float innerCloudRadius; }; struct alignas(16) PushConstants { @@ -102,9 +74,10 @@ namespace Atlas { vec2 nodeLocation; }; - PipelineConfig GeneratePipelineConfig(RenderTarget* target, bool wireframe); + PipelineConfig GeneratePipelineConfig(RenderTarget* target, bool depthOnly, bool wireframe); PipelineConfig causticPipelineConfig; + PipelineConfig underWaterPipelineConfig; Buffer::VertexArray vertexArray; @@ -112,6 +85,7 @@ namespace Atlas { Texture::Texture2D depthTexture; Buffer::UniformBuffer uniformBuffer; + Buffer::UniformBuffer depthUniformBuffer; Buffer::UniformBuffer lightUniformBuffer; Buffer::UniformBuffer cloudShadowUniformBuffer; @@ -122,7 +96,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/OpaqueRenderer.cpp b/src/engine/renderer/OpaqueRenderer.cpp index 67a3846c7..45dca425c 100644 --- a/src/engine/renderer/OpaqueRenderer.cpp +++ b/src/engine/renderer/OpaqueRenderer.cpp @@ -85,7 +85,7 @@ namespace Atlas { .invertUVs = mesh->invertUVs ? 1u : 0u, .twoSided = material->twoSided ? 1u : 0u, .staticMesh = mesh->mobility == Mesh::MeshMobility::Stationary ? 1u : 0u, - .materialIdx = uint32_t(materialMap[material]), + .materialIdx = uint32_t(materialMap[material.get()]), .normalScale = material->normalScale, .displacementScale = material->displacementScale }; diff --git a/src/engine/renderer/OpaqueRenderer.h b/src/engine/renderer/OpaqueRenderer.h index 07df645b3..9cf992365 100644 --- a/src/engine/renderer/OpaqueRenderer.h +++ b/src/engine/renderer/OpaqueRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_GEOMETRYRENDERER_H -#define AE_GEOMETRYRENDERER_H +#pragma once #include "../System.h" #include "../RenderList.h" @@ -43,6 +42,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/PathTracingRenderer.h b/src/engine/renderer/PathTracingRenderer.h index a7a088e30..2589cf057 100644 --- a/src/engine/renderer/PathTracingRenderer.h +++ b/src/engine/renderer/PathTracingRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_GPURAYTRACINGRENDERER_H -#define AE_GPURAYTRACINGRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -114,6 +113,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/PointLightRenderer.h b/src/engine/renderer/PointLightRenderer.h index 8abc475cd..c3fe022b9 100644 --- a/src/engine/renderer/PointLightRenderer.h +++ b/src/engine/renderer/PointLightRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_POINTLIGHTRENDERER_H -#define AE_POINTLIGHTRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -40,6 +39,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/PostProcessRenderer.cpp b/src/engine/renderer/PostProcessRenderer.cpp index c812c8baa..b62a62fb3 100644 --- a/src/engine/renderer/PostProcessRenderer.cpp +++ b/src/engine/renderer/PostProcessRenderer.cpp @@ -140,7 +140,7 @@ namespace Atlas { uniforms.vignetteOffset = vignette.offset; uniforms.vignettePower = vignette.power; uniforms.vignetteStrength = vignette.strength; - uniforms.vignetteColor = vec4(vignette.color, 0.0f); + uniforms.vignetteColor = vec4(Common::ColorConverter::ConvertSRGBToLinear(vignette.color), 0.0f); } if (filmGrain.enable) { diff --git a/src/engine/renderer/PostProcessRenderer.h b/src/engine/renderer/PostProcessRenderer.h index bd1b7d26e..5e5fd117a 100644 --- a/src/engine/renderer/PostProcessRenderer.h +++ b/src/engine/renderer/PostProcessRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_POSTPROCESSRENDERER_H -#define AE_POSTPROCESSRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -47,6 +46,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/RTReflectionRenderer.h b/src/engine/renderer/RTReflectionRenderer.h index b4b0cec82..6613eb611 100644 --- a/src/engine/renderer/RTReflectionRenderer.h +++ b/src/engine/renderer/RTReflectionRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_RTRRENDERER_H -#define AE_RTRRENDERER_H +#pragma once #include "../System.h" @@ -56,6 +55,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/RenderBatch.h b/src/engine/renderer/RenderBatch.h index 0dde3db07..1e2d8a19c 100644 --- a/src/engine/renderer/RenderBatch.h +++ b/src/engine/renderer/RenderBatch.h @@ -1,5 +1,4 @@ -#ifndef AE_RENDERBATCH_H -#define AE_RENDERBATCH_H +#pragma once #include "System.h" #include "buffer/VertexArray.h" @@ -65,6 +64,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/Renderer.h b/src/engine/renderer/Renderer.h index ba4137a9e..361ba22ce 100644 --- a/src/engine/renderer/Renderer.h +++ b/src/engine/renderer/Renderer.h @@ -1,5 +1,4 @@ -#ifndef AE_RENDERER_H -#define AE_RENDERER_H +#pragma once #include "../System.h" #include "../RenderTarget.h" @@ -12,6 +11,8 @@ #include "../graphics/Profiler.h" #include "../graphics/GraphicsDevice.h" +#include "../common/ColorConverter.h" + #include "helper/CommonStructures.h" namespace Atlas { @@ -36,6 +37,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/SSSRenderer.h b/src/engine/renderer/SSSRenderer.h index e7280a307..9adc16734 100644 --- a/src/engine/renderer/SSSRenderer.h +++ b/src/engine/renderer/SSSRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_SSSRENDERER_H -#define AE_SSSRENDERER_H +#pragma once #include "Renderer.h" @@ -34,7 +33,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/ShadowRenderer.cpp b/src/engine/renderer/ShadowRenderer.cpp index d5ef08da3..f27e4ace7 100644 --- a/src/engine/renderer/ShadowRenderer.cpp +++ b/src/engine/renderer/ShadowRenderer.cpp @@ -163,30 +163,34 @@ namespace Atlas { Ref ShadowRenderer::GetOrCreateFrameBuffer(Lighting::Light* light) { auto shadow = light->GetShadow(); + if (lightMap.contains(light)) { - return lightMap[light]; - } - else { - Graphics::RenderPassDepthAttachment attachment = { - .imageFormat = shadow->useCubemap ? shadow->cubemap.format : - shadow->maps.format, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - }; - Graphics::RenderPassDesc renderPassDesc = { - .depthAttachment = { attachment } - }; - auto renderPass = device->CreateRenderPass(renderPassDesc); - - Graphics::FrameBufferDesc frameBufferDesc = { - .renderPass = renderPass, - .depthAttachment = { shadow->useCubemap ? shadow->cubemap.image : shadow->maps.image, 0, true}, - .extent = { uint32_t(shadow->resolution), uint32_t(shadow->resolution) } - }; - return device->CreateFrameBuffer(frameBufferDesc); + auto frameBuffer = lightMap[light]; + if (frameBuffer->extent.width == shadow->resolution || + frameBuffer->extent.height == shadow->resolution) { + return frameBuffer; + } } + Graphics::RenderPassDepthAttachment attachment = { + .imageFormat = shadow->useCubemap ? shadow->cubemap.format : + shadow->maps.format, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + }; + Graphics::RenderPassDesc renderPassDesc = { + .depthAttachment = { attachment } + }; + auto renderPass = device->CreateRenderPass(renderPassDesc); + + Graphics::FrameBufferDesc frameBufferDesc = { + .renderPass = renderPass, + .depthAttachment = { shadow->useCubemap ? shadow->cubemap.image : shadow->maps.image, 0, true}, + .extent = { uint32_t(shadow->resolution), uint32_t(shadow->resolution) } + }; + return device->CreateFrameBuffer(frameBufferDesc); + } PipelineConfig ShadowRenderer::GetPipelineConfigForSubData(Mesh::MeshSubData *subData, @@ -203,7 +207,7 @@ namespace Atlas { .vertexInputInfo = mesh->vertexArray.GetVertexInputState(), }; - if (!material->twoSided && mesh->cullBackFaces) { + if (material->twoSided || !mesh->cullBackFaces) { pipelineDesc.rasterizer.cullMode = VK_CULL_MODE_NONE; } diff --git a/src/engine/renderer/ShadowRenderer.h b/src/engine/renderer/ShadowRenderer.h index 504f2a6bc..afeac978f 100644 --- a/src/engine/renderer/ShadowRenderer.h +++ b/src/engine/renderer/ShadowRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_SHADOWRENDERER_H -#define AE_SHADOWRENDERER_H +#pragma once #include "../System.h" #include "../RenderList.h" @@ -45,6 +44,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/SkyboxRenderer.h b/src/engine/renderer/SkyboxRenderer.h index 882047100..7600e3a9a 100644 --- a/src/engine/renderer/SkyboxRenderer.h +++ b/src/engine/renderer/SkyboxRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_SKYBOXRENDERER_H -#define AE_SKYBOXRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -28,6 +27,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/TemporalAARenderer.h b/src/engine/renderer/TemporalAARenderer.h index 38e5a0fb0..ca6997712 100644 --- a/src/engine/renderer/TemporalAARenderer.h +++ b/src/engine/renderer/TemporalAARenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_TEMPORALAARENDERER_H -#define AE_TEMPORALAARENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -33,6 +32,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/TerrainRenderer.h b/src/engine/renderer/TerrainRenderer.h index 42dcaee12..e095d2af2 100644 --- a/src/engine/renderer/TerrainRenderer.h +++ b/src/engine/renderer/TerrainRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_TERRAINRENDERER_H -#define AE_TERRAINRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -78,6 +77,4 @@ namespace Atlas { } -} - -#endif +} \ No newline at end of file diff --git a/src/engine/renderer/TerrainShadowRenderer.h b/src/engine/renderer/TerrainShadowRenderer.h index aa0818a0b..30a8f27cc 100644 --- a/src/engine/renderer/TerrainShadowRenderer.h +++ b/src/engine/renderer/TerrainShadowRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_TERRAINSHADOWRENDERER_H -#define AE_TERRAINSHADOWRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -52,6 +51,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/TextRenderer.h b/src/engine/renderer/TextRenderer.h index 1386809b0..f405692e3 100644 --- a/src/engine/renderer/TextRenderer.h +++ b/src/engine/renderer/TextRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_TEXTRENDERER_H -#define AE_TEXTRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -70,6 +69,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/TextureRenderer.h b/src/engine/renderer/TextureRenderer.h index cef792139..83bc6a8aa 100644 --- a/src/engine/renderer/TextureRenderer.h +++ b/src/engine/renderer/TextureRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_TEXTURERENDERER_H -#define AE_TEXTURERENDERER_H +#pragma once #include "Renderer.h" @@ -72,6 +71,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/VegetationRenderer.cpp b/src/engine/renderer/VegetationRenderer.cpp index a7f393833..05d8607a0 100644 --- a/src/engine/renderer/VegetationRenderer.cpp +++ b/src/engine/renderer/VegetationRenderer.cpp @@ -78,7 +78,7 @@ namespace Atlas { auto pushConstants = PushConstants { .invertUVs = mesh->invertUVs ? 1u : 0u, .twoSided = material->twoSided ? 1u : 0u, - .materialIdx = uint32_t(materialMap[material]), + .materialIdx = uint32_t(materialMap[material.get()]), .normalScale = material->normalScale, .displacementScale = material->displacementScale }; diff --git a/src/engine/renderer/VegetationRenderer.h b/src/engine/renderer/VegetationRenderer.h index 8dec5d9cd..03c3b98b6 100644 --- a/src/engine/renderer/VegetationRenderer.h +++ b/src/engine/renderer/VegetationRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_VEGETATIONRENDERER_H -#define AE_VEGETATIONRENDERER_H +#pragma once #include "../System.h" #include "Renderer.h" @@ -31,6 +30,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/VolumetricCloudRenderer.cpp b/src/engine/renderer/VolumetricCloudRenderer.cpp index 278579b8a..830763a77 100644 --- a/src/engine/renderer/VolumetricCloudRenderer.cpp +++ b/src/engine/renderer/VolumetricCloudRenderer.cpp @@ -72,6 +72,8 @@ namespace Atlas { auto uniforms = GetUniformStructure(camera, scene); volumetricUniformBuffer.SetData(&uniforms, 0); + integratePipelineConfig.ManageMacro("STOCHASTIC_OCCLUSION_SAMPLING", clouds->stochasticOcclusionSampling); + auto pipeline = PipelineManager::GetPipeline(integratePipelineConfig); commandList->BindPipeline(pipeline); @@ -189,6 +191,7 @@ namespace Atlas { shadowUniforms.ipMatrix = glm::inverse(shadowUniforms.ipMatrix); shadowUniforms.ivMatrix = glm::inverse(shadowUniforms.ivMatrix); shadowUniforms.lightDirection = vec4(normalize(sun->direction), 0.0f); + shadowUniforms.shadowSampleFraction = clouds->shadowSampleFraction; shadowUniformBuffer.SetData(&shadowUniforms, 0); auto uniforms = GetUniformStructure(camera, scene); @@ -311,17 +314,18 @@ namespace Atlas { .frameSeed = frameCount, .sampleCount = clouds->sampleCount, - .shadowSampleCount = clouds->shadowSampleCount, + .occlusionSampleCount = clouds->occlusionSampleCount, .darkEdgeDirect = clouds->darkEdgeFocus, .darkEdgeDetail = clouds->darkEdgeAmbient, - .extinctionCoefficients = clouds->scattering.extinctionCoefficients + .extinctionCoefficients = clouds->scattering.extinctionCoefficients, + .planetCenter = vec4(scene->sky.planetCenter, 1.0f) }; if (sun) { uniforms.light.direction = vec4(sun->direction, 0.0f); - uniforms.light.color = vec4(sun->color, 1.0f); + uniforms.light.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(sun->color), 1.0f); uniforms.light.intensity = sun->intensity; } else { diff --git a/src/engine/renderer/VolumetricCloudRenderer.h b/src/engine/renderer/VolumetricCloudRenderer.h index 570600799..1964c909c 100644 --- a/src/engine/renderer/VolumetricCloudRenderer.h +++ b/src/engine/renderer/VolumetricCloudRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_VOLUMETRICCLOUDRENDERER_H -#define AE_VOLUMETRICCLOUDRENDERER_H +#pragma once #include "Renderer.h" @@ -56,18 +55,20 @@ namespace Atlas { uint32_t frameSeed; int32_t sampleCount; - int32_t shadowSampleCount; + int32_t occlusionSampleCount; float darkEdgeDirect; float darkEdgeDetail; vec4 extinctionCoefficients; + vec4 planetCenter; }; struct alignas(16) CloudShadowUniforms { mat4 ivMatrix; mat4 ipMatrix; vec4 lightDirection; + int32_t shadowSampleFraction; }; void GenerateShapeTexture(Graphics::CommandList* commandList, @@ -100,6 +101,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/VolumetricRenderer.cpp b/src/engine/renderer/VolumetricRenderer.cpp index 36c8421a0..0a27cdc95 100644 --- a/src/engine/renderer/VolumetricRenderer.cpp +++ b/src/engine/renderer/VolumetricRenderer.cpp @@ -16,9 +16,9 @@ namespace Atlas { volumetricPipelineConfig = PipelineConfig("volumetric/volumetric.csh"); horizontalBlurPipelineConfig = PipelineConfig("bilateralBlur.csh", - {"HORIZONTAL", "BLUR_RGB", "DEPTH_WEIGHT"}); + {"HORIZONTAL", "BLUR_RGBA", "DEPTH_WEIGHT"}); verticalBlurPipelineConfig = PipelineConfig("bilateralBlur.csh", - {"VERTICAL", "BLUR_RGB", "DEPTH_WEIGHT"}); + {"VERTICAL", "BLUR_RGBA", "DEPTH_WEIGHT"}); resolvePipelineConfig = PipelineConfig("volumetric/volumetricResolve.csh"); @@ -78,10 +78,9 @@ namespace Atlas { VolumetricUniforms uniforms; uniforms.sampleCount = volumetric->sampleCount; uniforms.intensity = volumetric->intensity * light->intensity; - uniforms.seed = Common::Random::SampleFastUniformFloat(); uniforms.light.direction = vec4(direction, 0.0); - uniforms.light.color = vec4(light->color, 0.0); + uniforms.light.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(light->color), 0.0); uniforms.light.shadow.cascadeCount = shadow->componentCount; commandList->BindImage(shadow->maps.image, shadowSampler, 3, 2); @@ -101,6 +100,17 @@ namespace Atlas { } } + auto ocean = scene->ocean; + bool oceanEnabled = ocean && ocean->enable; + + if (oceanEnabled) { + // This is the full res depth buffer.. + uniforms.oceanHeight = ocean->translation.y; + target->oceanDepthTexture.Bind(commandList, 3, 4); + // Needs barrier + target->oceanStencilTexture.Bind(commandList, 3, 5); + } + auto fog = scene->fog; bool fogEnabled = fog && fog->enable; @@ -108,7 +118,7 @@ namespace Atlas { if (fogEnabled) { auto& fogUniform = uniforms.fog; - fogUniform.color = vec4(fog->color, 1.0f); + fogUniform.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(fog->color), 1.0f); fogUniform.density = fog->density; fogUniform.heightFalloff = fog->heightFalloff; fogUniform.height = fog->height; @@ -116,7 +126,15 @@ namespace Atlas { } auto clouds = scene->sky.clouds; - bool cloudShadowsEnabled = clouds && clouds->enable && clouds->castShadow; + bool cloudsEnabled = clouds && clouds->enable; + bool cloudShadowsEnabled = cloudsEnabled && clouds->castShadow; + + if (cloudsEnabled) { + target->volumetricCloudsTexture.Bind(commandList, 3, 6); + + float cloudInnerRadius = scene->sky.planetRadius + clouds->minHeight; + uniforms.planetCenterAndRadius = vec4(scene->sky.planetCenter, cloudInnerRadius); + } if (cloudShadowsEnabled) { auto& cloudShadowUniform = uniforms.cloudShadow; @@ -133,9 +151,11 @@ namespace Atlas { } volumetricUniformBuffer.SetData(&uniforms, 0); - commandList->BindBuffer(volumetricUniformBuffer.Get(), 3, 4); + commandList->BindBuffer(volumetricUniformBuffer.Get(), 3, 7); + volumetricPipelineConfig.ManageMacro("CLOUDS", cloudsEnabled); volumetricPipelineConfig.ManageMacro("CLOUD_SHADOWS", cloudShadowsEnabled); + volumetricPipelineConfig.ManageMacro("OCEAN", oceanEnabled); auto volumetricPipeline = PipelineManager::GetPipeline(volumetricPipelineConfig); commandList->BindPipeline(volumetricPipeline); @@ -249,7 +269,7 @@ namespace Atlas { if (fogEnabled) { auto& fogUniform = uniforms.fog; - fogUniform.color = vec4(fog->color, 1.0f); + fogUniform.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(fog->color), 1.0f); fogUniform.density = fog->density; fogUniform.heightFalloff = fog->heightFalloff; fogUniform.height = fog->height; @@ -260,6 +280,7 @@ namespace Atlas { uniforms.innerCloudRadius = scene->sky.planetRadius + clouds->minHeight; uniforms.planetRadius = scene->sky.planetRadius; uniforms.cloudDistanceLimit = clouds->distanceLimit; + uniforms.planetCenter = vec4(scene->sky.planetCenter, 1.0f); commandList->BindImage(target->volumetricCloudsTexture.image, target->volumetricCloudsTexture.sampler, 3, 3); } diff --git a/src/engine/renderer/VolumetricRenderer.h b/src/engine/renderer/VolumetricRenderer.h index df4026bf6..ce31fe4f6 100644 --- a/src/engine/renderer/VolumetricRenderer.h +++ b/src/engine/renderer/VolumetricRenderer.h @@ -1,5 +1,4 @@ -#ifndef AE_VOLUMETRICRENDERER_H -#define AE_VOLUMETRICRENDERER_H +#pragma once #include "../System.h" #include "../Filter.h" @@ -20,19 +19,12 @@ namespace Atlas { Scene::Scene* scene, Graphics::CommandList* commandList); private: - struct alignas(16) Fog { - float density; - float heightFalloff; - float height; - float scatteringAnisotropy; - vec4 color; - }; - struct alignas(16) VolumetricUniforms { int sampleCount; float intensity; - float seed; int fogEnabled; + float oceanHeight; + vec4 planetCenterAndRadius; Fog fog; Light light; CloudShadow cloudShadow; @@ -44,6 +36,7 @@ namespace Atlas { struct alignas(16) ResolveUniforms { Fog fog; + vec4 planetCenter; int downsampled2x; int cloudsEnabled; int fogEnabled; @@ -71,6 +64,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/helper/CommonStructures.h b/src/engine/renderer/helper/CommonStructures.h index f660e0257..408c9505b 100644 --- a/src/engine/renderer/helper/CommonStructures.h +++ b/src/engine/renderer/helper/CommonStructures.h @@ -1,5 +1,4 @@ -#ifndef AE_COMMONSTRUCTURES_H -#define AE_COMMONSTRUCTURES_H +#pragma once #include "../../System.h" @@ -55,8 +54,14 @@ namespace Atlas { mat4 ipMatrix; }; - } + struct alignas(16) Fog { + float density; + float heightFalloff; + float height; + float scatteringAnisotropy; + vec4 color; + }; -} + } -#endif +} \ No newline at end of file diff --git a/src/engine/renderer/helper/GeometryHelper.h b/src/engine/renderer/helper/GeometryHelper.h index 6bb46a836..13591fb6a 100644 --- a/src/engine/renderer/helper/GeometryHelper.h +++ b/src/engine/renderer/helper/GeometryHelper.h @@ -1,5 +1,4 @@ -#ifndef AE_GEOMETRYHELPER_H -#define AE_GEOMETRYHELPER_H +#pragma once #include "../../System.h" #include "buffer/VertexArray.h" @@ -37,6 +36,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/helper/HaltonSequence.h b/src/engine/renderer/helper/HaltonSequence.h index ad6a93708..2f2588936 100644 --- a/src/engine/renderer/helper/HaltonSequence.h +++ b/src/engine/renderer/helper/HaltonSequence.h @@ -1,5 +1,4 @@ -#ifndef AE_HALTONSEQUENCE_H -#define AE_HALTONSEQUENCE_H +#pragma once #include "../../System.h" @@ -27,6 +26,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/helper/RayTracingHelper.cpp b/src/engine/renderer/helper/RayTracingHelper.cpp index 55237d370..654cbac5f 100644 --- a/src/engine/renderer/helper/RayTracingHelper.cpp +++ b/src/engine/renderer/helper/RayTracingHelper.cpp @@ -1,4 +1,5 @@ #include "RayTracingHelper.h" +#include "../../common/ColorConverter.h" #include "../../common/RandomHelper.h" #include "../../common/Piecewise.h" #include "../../volume/BVH.h" @@ -517,7 +518,7 @@ namespace Atlas { for (auto light : lightSources) { - auto radiance = light->color * light->intensity; + auto radiance = Common::ColorConverter::ConvertSRGBToLinear(light->color) * light->intensity; auto brightness = dot(radiance, vec3(0.3333f)); vec3 P = vec3(0.0f); diff --git a/src/engine/renderer/helper/RayTracingHelper.h b/src/engine/renderer/helper/RayTracingHelper.h index eb4a8a1b7..da73109d0 100644 --- a/src/engine/renderer/helper/RayTracingHelper.h +++ b/src/engine/renderer/helper/RayTracingHelper.h @@ -1,5 +1,4 @@ -#ifndef RAYTRACINGHELPER_H -#define RAYTRACINGHELPER_H +#pragma once #include "../../System.h" #include "../../scene/Scene.h" @@ -98,6 +97,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/renderer/helper/VegetationHelper.h b/src/engine/renderer/helper/VegetationHelper.h index 6183f52c8..1c3ca3f72 100644 --- a/src/engine/renderer/helper/VegetationHelper.h +++ b/src/engine/renderer/helper/VegetationHelper.h @@ -1,5 +1,4 @@ -#ifndef AE_VEGETATIONHELPER_H -#define AE_VEGETATIONHELPER_H +#pragma once #include "../../System.h" #include "../../scene/Vegetation.h" @@ -75,6 +74,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/resource/Resource.h b/src/engine/resource/Resource.h index f3a8841a9..683c37315 100644 --- a/src/engine/resource/Resource.h +++ b/src/engine/resource/Resource.h @@ -1,5 +1,4 @@ -#ifndef AE_RESOURCE_H -#define AE_RESOURCE_H +#pragma once #include "System.h" #include "../common/Hash.h" @@ -90,7 +89,7 @@ namespace Atlas { data = nullptr; } - size_t ID = 0; + Hash ID = 0; ResourceOrigin origin = System; @@ -183,6 +182,4 @@ namespace Atlas { std::function>&)> function; }; -} - -#endif +} \ No newline at end of file diff --git a/src/engine/resource/ResourceManager.h b/src/engine/resource/ResourceManager.h index 2009574c3..f2c815c8b 100644 --- a/src/engine/resource/ResourceManager.h +++ b/src/engine/resource/ResourceManager.h @@ -1,8 +1,8 @@ -#ifndef AE_RESOURCEMANAGER_H -#define AE_RESOURCEMANAGER_H +#pragma once #include "System.h" #include "Resource.h" +#include "Log.h" #include "events/EventManager.h" #include @@ -16,15 +16,30 @@ namespace Atlas { class ResourceManager { public: + static ResourceHandle GetResource(const std::string& path) { + + CheckInitialization(); + + std::lock_guard lock(mutex); + if (resources.contains(path)) { + auto& resource = resources[path]; + resource->framesToDeletion = RESOURCE_RETENTION_FRAME_COUNT; + return ResourceHandle(resource); + } + + return ResourceHandle(); + + } + template - static ResourceHandle GetResource(const std::string& path, Args&&... args) { + static ResourceHandle GetOrLoadResource(const std::string& path, Args&&... args) { - return GetResource(path, System, std::forward(args)...); + return GetOrLoadResource(path, System, std::forward(args)...); } template - static ResourceHandle GetResource(const std::string& path, ResourceOrigin origin, Args&&... args) { + static ResourceHandle GetOrLoadResource(const std::string& path, ResourceOrigin origin, Args&&... args) { static_assert(std::is_constructible() || std::is_constructible(), @@ -44,31 +59,31 @@ namespace Atlas { } template - static ResourceHandle GetResourceWithLoader(const std::string& path, + static ResourceHandle GetOrLoadResourceWithLoader(const std::string& path, Ref (*loaderFunction)(const std::string&, Args...), Args... args) { - return GetResourceWithLoader(path, System, std::function(loaderFunction), std::forward(args)...); + return GetOrLoadResourceWithLoader(path, System, std::function(loaderFunction), std::forward(args)...); } template - static ResourceHandle GetResourceWithLoader(const std::string& path, ResourceOrigin origin, + static ResourceHandle GetOrLoadResourceWithLoader(const std::string& path, ResourceOrigin origin, Ref (*loaderFunction)(const std::string&, Args...), Args... args) { - return GetResourceWithLoader(path, origin, std::function(loaderFunction), std::forward(args)...); + return GetOrLoadResourceWithLoader(path, origin, std::function(loaderFunction), std::forward(args)...); } template - static ResourceHandle GetResourceWithLoader(const std::string& path, + static ResourceHandle GetOrLoadResourceWithLoader(const std::string& path, std::function(const std::string&, Args...)> loaderFunction, Args&&... args) { - return GetResourceWithLoader(path, System, loaderFunction, std::forward(args)...); + return GetOrLoadResourceWithLoader(path, System, loaderFunction, std::forward(args)...); } template - static ResourceHandle GetResourceWithLoader(const std::string& path, ResourceOrigin origin, + static ResourceHandle GetOrLoadResourceWithLoader(const std::string& path, ResourceOrigin origin, std::function(const std::string&, Args...)> loaderFunction, Args&&... args) { CheckInitialization(); @@ -85,14 +100,14 @@ namespace Atlas { } template - static ResourceHandle GetResourceAsync(const std::string& path, Args&&... args) { + static ResourceHandle GetOrLoadResourceAsync(const std::string& path, Args&&... args) { - return GetResourceAsync(path, System, std::forward(args)...); + return GetOrLoadResourceAsync(path, System, std::forward(args)...); } template - static ResourceHandle GetResourceAsync(const std::string& path, ResourceOrigin origin, Args&&... args) { + static ResourceHandle GetOrLoadResourceAsync(const std::string& path, ResourceOrigin origin, Args&&... args) { static_assert(std::is_constructible() || std::is_constructible(), @@ -114,23 +129,23 @@ namespace Atlas { } template - static ResourceHandle GetResourceWithLoaderAsync(const std::string& path, + static ResourceHandle GetOrLoadResourceWithLoaderAsync(const std::string& path, Ref (*loaderFunction)(const std::string&, Args...), Args... args) { - return GetResourceWithLoaderAsync(path, System, std::function(loaderFunction), std::forward(args)...); + return GetOrLoadResourceWithLoaderAsync(path, System, std::function(loaderFunction), std::forward(args)...); } template - static ResourceHandle GetResourceWithLoaderAsync(const std::string& path, ResourceOrigin origin, + static ResourceHandle GetOrLoadResourceWithLoaderAsync(const std::string& path, ResourceOrigin origin, Ref (*loaderFunction)(const std::string&, Args...), Args... args) { - return GetResourceWithLoaderAsync(path, origin, std::function(loaderFunction), std::forward(args)...); + return GetOrLoadResourceWithLoaderAsync(path, origin, std::function(loaderFunction), std::forward(args)...); } template - static ResourceHandle GetResourceWithLoaderAsync(const std::string& path, + static ResourceHandle GetOrLoadResourceWithLoaderAsync(const std::string& path, std::function(const std::string&, Args...)> loaderFunction, Args... args) { return GetResourceWithLoaderAsync(path, System, loaderFunction, std::forward(args)...); @@ -138,7 +153,7 @@ namespace Atlas { } template - static ResourceHandle GetResourceWithLoaderAsync(const std::string& path, ResourceOrigin origin, + static ResourceHandle GetOrLoadResourceWithLoaderAsync(const std::string& path, ResourceOrigin origin, std::function(const std::string&, Args...)> loaderFunction, Args... args) { CheckInitialization(); @@ -366,6 +381,4 @@ namespace Atlas { template std::atomic_int ResourceManager::subscriberCount = 0; -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/scene/RTData.cpp b/src/engine/scene/RTData.cpp index 4cc951d6c..804beffe5 100644 --- a/src/engine/scene/RTData.cpp +++ b/src/engine/scene/RTData.cpp @@ -4,6 +4,7 @@ #include "../mesh/MeshData.h" #include "../volume/BVH.h" #include "../graphics/ASBuilder.h" +#include "../common/ColorConverter.h" namespace Atlas { @@ -133,61 +134,62 @@ namespace Atlas { continue; for (auto& material : mesh->data.materials) { - if (!materialAccess.contains(&material)) + if (!materialAccess.contains(material.get())) continue; - auto materialIdx = materialAccess[&material]; + auto materialIdx = materialAccess[material.get()]; GPUMaterial gpuMaterial; - gpuMaterial.baseColor = material.baseColor; - gpuMaterial.emissiveColor = material.emissiveColor; + gpuMaterial.baseColor = Common::ColorConverter::ConvertSRGBToLinear(material->baseColor); + gpuMaterial.emissiveColor = Common::ColorConverter::ConvertSRGBToLinear(material->emissiveColor) + * material->emissiveIntensity; - gpuMaterial.opacity = material.opacity; + gpuMaterial.opacity = material->opacity; - gpuMaterial.roughness = material.roughness; - gpuMaterial.metalness = material.metalness; - gpuMaterial.ao = material.ao; + gpuMaterial.roughness = material->roughness; + gpuMaterial.metalness = material->metalness; + gpuMaterial.ao = material->ao; - gpuMaterial.reflectance = material.reflectance; + gpuMaterial.reflectance = material->reflectance; - gpuMaterial.normalScale = material.normalScale; + gpuMaterial.normalScale = material->normalScale; gpuMaterial.invertUVs = mesh->invertUVs ? 1 : 0; - gpuMaterial.twoSided = material.twoSided ? 1 : 0; + gpuMaterial.twoSided = material->twoSided ? 1 : 0; - if (material.HasBaseColorMap()) { - auto& slices = baseColorTextureAtlas.slices[material.baseColorMap.get()]; + if (material->HasBaseColorMap()) { + auto& slices = baseColorTextureAtlas.slices[material->baseColorMap.get()]; gpuMaterial.baseColorTexture = CreateGPUTextureStruct(slices); } - if (material.HasOpacityMap()) { - auto& slices = opacityTextureAtlas.slices[material.opacityMap.get()]; + if (material->HasOpacityMap()) { + auto& slices = opacityTextureAtlas.slices[material->opacityMap.get()]; gpuMaterial.opacityTexture = CreateGPUTextureStruct(slices); } - if (material.HasNormalMap()) { - auto& slices = normalTextureAtlas.slices[material.normalMap.get()]; + if (material->HasNormalMap()) { + auto& slices = normalTextureAtlas.slices[material->normalMap.get()]; gpuMaterial.normalTexture = CreateGPUTextureStruct(slices); } - if (material.HasRoughnessMap()) { - auto& slices = roughnessTextureAtlas.slices[material.roughnessMap.get()]; + if (material->HasRoughnessMap()) { + auto& slices = roughnessTextureAtlas.slices[material->roughnessMap.get()]; gpuMaterial.roughnessTexture = CreateGPUTextureStruct(slices); } - if (material.HasMetalnessMap()) { - auto& slices = metalnessTextureAtlas.slices[material.metalnessMap.get()]; + if (material->HasMetalnessMap()) { + auto& slices = metalnessTextureAtlas.slices[material->metalnessMap.get()]; gpuMaterial.metalnessTexture = CreateGPUTextureStruct(slices); } - if (material.HasAoMap()) { - auto& slices = aoTextureAtlas.slices[material.aoMap.get()]; + if (material->HasAoMap()) { + auto& slices = aoTextureAtlas.slices[material->aoMap.get()]; gpuMaterial.aoTexture = CreateGPUTextureStruct(slices); } @@ -217,21 +219,21 @@ namespace Atlas { continue; for (auto& material : mesh->data.materials) { - if (!materialAccess.contains(&material)) + if (!materialAccess.contains(material.get())) continue; - if (material.HasBaseColorMap()) - baseColorTextures.push_back(material.baseColorMap); - if (material.HasOpacityMap()) - opacityTextures.push_back(material.opacityMap); - if (material.HasNormalMap()) - normalTextures.push_back(material.normalMap); - if (material.HasRoughnessMap()) - roughnessTextures.push_back(material.roughnessMap); - if (material.HasMetalnessMap()) - metalnessTextures.push_back(material.metalnessMap); - if (material.HasAoMap()) - aoTextures.push_back(material.aoMap); + if (material->HasBaseColorMap()) + baseColorTextures.push_back(material->baseColorMap); + if (material->HasOpacityMap()) + opacityTextures.push_back(material->opacityMap); + if (material->HasNormalMap()) + normalTextures.push_back(material->normalMap); + if (material->HasRoughnessMap()) + roughnessTextures.push_back(material->roughnessMap); + if (material->HasMetalnessMap()) + metalnessTextures.push_back(material->metalnessMap); + if (material->HasAoMap()) + aoTextures.push_back(material->aoMap); } } @@ -347,7 +349,7 @@ namespace Atlas { } for (auto& material : mesh->data.materials) { - materialAccess[&material] = materialCount++; + materialAccess[material.get()] = materialCount++; } MeshInfo meshInfo = { @@ -414,7 +416,7 @@ namespace Atlas { } for (auto& material : mesh->data.materials) { - materialAccess[&material] = materialCount++; + materialAccess[material.get()] = materialCount++; } std::vector geometryRegions; @@ -547,7 +549,8 @@ namespace Atlas { auto idx = reinterpret_cast(triangle.d0.w); auto& material = materials[idx]; - auto radiance = material.emissiveColor; + auto radiance = Common::ColorConverter::ConvertSRGBToLinear(material->emissiveColor) + * material->emissiveIntensity; auto brightness = dot(radiance, vec3(0.3333f)); if (brightness > 0.0f) { @@ -580,7 +583,7 @@ namespace Atlas { GPULight light; light.P = vec4(P, 1.0f); light.N = vec4(N, 0.0f); - light.color = vec4(radiance, 0.0f); + light.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(radiance), 0.0f); light.data = vec4(cd, weight, area, 0.0f); meshInfo.triangleLights.push_back(light); diff --git a/src/engine/scene/RTData.h b/src/engine/scene/RTData.h index a7fba358f..d9663e3e5 100644 --- a/src/engine/scene/RTData.h +++ b/src/engine/scene/RTData.h @@ -1,5 +1,4 @@ -#ifndef AE_RTDATA_H -#define AE_RTDATA_H +#pragma once #include "../System.h" @@ -107,6 +106,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/scene/RTStructures.h b/src/engine/scene/RTStructures.h index afe15d489..36c7dc089 100644 --- a/src/engine/scene/RTStructures.h +++ b/src/engine/scene/RTStructures.h @@ -1,5 +1,4 @@ -#ifndef AE_RTSTRUCTURES_H -#define AE_RTSTRUCTURES_H +#pragma once #include @@ -101,6 +100,4 @@ namespace Atlas { vec4 data; }; -} - -#endif +} \ No newline at end of file diff --git a/src/engine/scene/Scene.cpp b/src/engine/scene/Scene.cpp index 3a35de7d8..a8905f387 100644 --- a/src/engine/scene/Scene.cpp +++ b/src/engine/scene/Scene.cpp @@ -117,7 +117,7 @@ namespace Atlas { if (!mesh.IsLoaded()) continue; for (auto& material : mesh->data.materials) { - materials.push_back(&material); + materials.push_back(material.get()); } } diff --git a/src/engine/scene/Scene.h b/src/engine/scene/Scene.h index 432977e14..1c4519e5c 100644 --- a/src/engine/scene/Scene.h +++ b/src/engine/scene/Scene.h @@ -1,5 +1,4 @@ -#ifndef AE_SCENE_H -#define AE_SCENE_H +#pragma once #include "../System.h" #include "../actor/MeshActor.h" @@ -152,6 +151,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/scene/SceneNode.h b/src/engine/scene/SceneNode.h index ecab2a078..f978899ac 100644 --- a/src/engine/scene/SceneNode.h +++ b/src/engine/scene/SceneNode.h @@ -1,5 +1,4 @@ -#ifndef AE_SCENENODE_H -#define AE_SCENENODE_H +#pragma once #include "../System.h" #include "../actor/MovableMeshActor.h" @@ -201,6 +200,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/scene/SpacePartitioning.h b/src/engine/scene/SpacePartitioning.h index 534f3f304..38085299b 100644 --- a/src/engine/scene/SpacePartitioning.h +++ b/src/engine/scene/SpacePartitioning.h @@ -1,5 +1,4 @@ -#ifndef AE_SPACEPARTITIONING_H -#define AE_SPACEPARTITIONING_H +#pragma once #include "../System.h" #include "../RenderList.h" @@ -79,7 +78,4 @@ namespace Atlas { } -} - - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/scene/Vegetation.h b/src/engine/scene/Vegetation.h index d336cb858..be30c069e 100644 --- a/src/engine/scene/Vegetation.h +++ b/src/engine/scene/Vegetation.h @@ -1,5 +1,4 @@ -#ifndef AE_VEGETATION_H -#define AE_VEGETATION_H +#pragma once #include "../System.h" #include "mesh/Mesh.h" @@ -44,6 +43,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/terrain/Terrain.h b/src/engine/terrain/Terrain.h index 133561993..7c49dc8d6 100644 --- a/src/engine/terrain/Terrain.h +++ b/src/engine/terrain/Terrain.h @@ -1,5 +1,4 @@ -#ifndef AE_TERRAIN_H -#define AE_TERRAIN_H +#pragma once #include "../System.h" #include "TerrainNode.h" @@ -195,6 +194,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/terrain/TerrainNode.h b/src/engine/terrain/TerrainNode.h index fa4c311cf..769e12bd5 100644 --- a/src/engine/terrain/TerrainNode.h +++ b/src/engine/terrain/TerrainNode.h @@ -1,5 +1,4 @@ -#ifndef AE_TERRAINNODE_H -#define AE_TERRAINNODE_H +#pragma once #include "../System.h" #include "TerrainStorage.h" @@ -56,6 +55,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/terrain/TerrainStorage.h b/src/engine/terrain/TerrainStorage.h index 6118f5d0e..28ea4b52e 100644 --- a/src/engine/terrain/TerrainStorage.h +++ b/src/engine/terrain/TerrainStorage.h @@ -1,5 +1,4 @@ -#ifndef AE_TERRAINSTORAGE_H -#define AE_TERRAINSTORAGE_H +#pragma once #include "../System.h" #include "TerrainStorageCell.h" @@ -118,6 +117,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/terrain/TerrainStorageCell.h b/src/engine/terrain/TerrainStorageCell.h index ef66bec11..b0cc88984 100644 --- a/src/engine/terrain/TerrainStorageCell.h +++ b/src/engine/terrain/TerrainStorageCell.h @@ -1,5 +1,4 @@ -#ifndef AE_TERRAINSTORAGECELL_H -#define AE_TERRAINSTORAGECELL_H +#pragma once #include "../System.h" #include "../Material.h" @@ -43,6 +42,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/texture/Cubemap.h b/src/engine/texture/Cubemap.h index c94ecf397..5f4867910 100644 --- a/src/engine/texture/Cubemap.h +++ b/src/engine/texture/Cubemap.h @@ -1,5 +1,4 @@ -#ifndef AE_CUBEMAP_H -#define AE_CUBEMAP_H +#pragma once #include "System.h" #include "Texture.h" @@ -85,6 +84,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/texture/Texture.h b/src/engine/texture/Texture.h index c8bb444e4..d81d8987f 100644 --- a/src/engine/texture/Texture.h +++ b/src/engine/texture/Texture.h @@ -1,5 +1,4 @@ -#ifndef AE_VULKANTEXTURE_H -#define AE_VULKANTEXTURE_H +#pragma once #include "../System.h" @@ -157,6 +156,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/texture/Texture2D.h b/src/engine/texture/Texture2D.h index c0487e23a..9bd6c7550 100644 --- a/src/engine/texture/Texture2D.h +++ b/src/engine/texture/Texture2D.h @@ -1,5 +1,4 @@ -#ifndef AE_TEXTURE2D_H -#define AE_TEXTURE2D_H +#pragma once #include "../System.h" #include "Texture.h" @@ -150,6 +149,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/texture/Texture2DArray.h b/src/engine/texture/Texture2DArray.h index 2461feff9..e582fb7c6 100644 --- a/src/engine/texture/Texture2DArray.h +++ b/src/engine/texture/Texture2DArray.h @@ -1,5 +1,4 @@ -#ifndef AE_TEXTURE2DARRAY_H -#define AE_TEXTURE2DARRAY_H +#pragma once #include "../System.h" #include "Texture.h" @@ -114,6 +113,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/texture/Texture3D.h b/src/engine/texture/Texture3D.h index 781003967..62eb820cd 100644 --- a/src/engine/texture/Texture3D.h +++ b/src/engine/texture/Texture3D.h @@ -1,5 +1,4 @@ -#ifndef AE_TEXTURE3D_H -#define AE_TEXTURE3D_H +#pragma once #include "Texture.h" @@ -40,6 +39,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/texture/TextureAtlas.h b/src/engine/texture/TextureAtlas.h index a722742cf..311723718 100644 --- a/src/engine/texture/TextureAtlas.h +++ b/src/engine/texture/TextureAtlas.h @@ -1,5 +1,4 @@ -#ifndef AE_TEXTUREATLAS_H -#define AE_TEXTUREATLAS_H +#pragma once #include "../System.h" #include "Texture2D.h" @@ -88,6 +87,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/tools/ImpostorTool.cpp b/src/engine/tools/ImpostorTool.cpp index 002e1c7d4..b2983df59 100644 --- a/src/engine/tools/ImpostorTool.cpp +++ b/src/engine/tools/ImpostorTool.cpp @@ -81,7 +81,7 @@ namespace Atlas { // Approximate transmission for (auto& material : mesh->data.materials) { - impostor->transmissiveColor += material.transmissiveColor / (float)mesh->data.materials.size(); + impostor->transmissiveColor += material->transmissiveColor / (float)mesh->data.materials.size(); } impostor->baseColorTexture.GenerateMipmap(); diff --git a/src/engine/tools/ImpostorTool.h b/src/engine/tools/ImpostorTool.h index 213b72381..41739b3c7 100644 --- a/src/engine/tools/ImpostorTool.h +++ b/src/engine/tools/ImpostorTool.h @@ -1,5 +1,4 @@ -#ifndef AE_IMPOSTORTOOL_H -#define AE_IMPOSTORTOOL_H +#pragma once #include "../System.h" #include "../mesh/Impostor.h" @@ -29,6 +28,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/tools/PerformanceCounter.h b/src/engine/tools/PerformanceCounter.h index 74df28944..dcaed6807 100644 --- a/src/engine/tools/PerformanceCounter.h +++ b/src/engine/tools/PerformanceCounter.h @@ -1,5 +1,4 @@ -#ifndef AE_PERFORMANCECOUNTER_H -#define AE_PERFORMANCECOUNTER_H +#pragma once #include "../System.h" @@ -58,6 +57,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/tools/RayCasting.h b/src/engine/tools/RayCasting.h index 8c827f41f..90ad8782f 100644 --- a/src/engine/tools/RayCasting.h +++ b/src/engine/tools/RayCasting.h @@ -1,5 +1,4 @@ -#ifndef AE_RAYCASTING_H -#define AE_RAYCASTING_H +#pragma once #include "../System.h" #include "../Viewport.h" @@ -49,6 +48,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/engine/tools/TerrainTool.h b/src/engine/tools/TerrainTool.h index eacf4a988..f6e7c6fe1 100644 --- a/src/engine/tools/TerrainTool.h +++ b/src/engine/tools/TerrainTool.h @@ -1,5 +1,4 @@ -#ifndef AE_TERRAINTOOL_H -#define AE_TERRAINTOOL_H +#pragma once #include "../System.h" #include "../terrain/Terrain.h" @@ -80,6 +79,4 @@ namespace Atlas { } -} - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index 3363ccef1..654bd56e5 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", "name": "atlas-engine", - "version-string": "0.1.8", + "version-string": "0.1.10", "port-version": 0, "builtin-baseline": "59b58fc56de40eaa279ae0208debcf5773a2db77", "homepage": "https://github.com/tippesi/Atlas-Engine", @@ -37,6 +37,7 @@ { "name": "assimp", "version": "5.2.5" }, { "name": "spirv-tools", "version": "2022.1#0" }, { "name": "glslang", "version": "11.11.0#1" }, - { "name": "sdl2", "version": "2.24.2" } + { "name": "sdl2", "version": "2.28.2" }, + { "name": "vulkan-memory-allocator", "version": "3.0.1#3" } ] } From 9893770cc69f529e403959bb3c98ea5953365ef6 Mon Sep 17 00:00:00 2001 From: Simon Tippe Date: Sun, 7 Jan 2024 20:55:46 +0100 Subject: [PATCH 2/3] Realtime path tracing (#38) * Initial implementation of real time path tracing * Added reprojection to path tracer using motion vectors * Fixed some MacOS issues * Added sky probe generation to path tracer * Bug fixes and path tracing denoising improvements * Further improvements to temporal denoiser * Begin work on bindless resources * More descriptor changes * More work on new descriptor management, also some bug fixes * User defined global descriptor injection works now * Bindless textures are now working * Fixes and cleanups * Some more changes to get everything working on Mac (textures are missing) * Work on fully bindless ray tracing continues * Fixed the rest of the issues * Mac works now * Fixed rest of MoltenVK/Metal isses * Ray tracing should now handle new meshes without a rebuild * Fixed a few things to make software pathtracing work in Voxel app * Some smaller improvements * Experimenting with better TLAS * Fixed a few things * Try fix build pipeline * Fixed a few more things * Try to fix build pipeline * Fix more stuff * More build pipeline changes * More build pipeline fixes * Should reduce pipeline build times * Fix MacOS binding issue * Hardware raytracing + bindless works now * Few more fixes * Update build.yml * Update build.yml * Update build.yml * Update build.yml * Make engine be able to work headlessly * Attempt to fix build pipeline * Try to get more info related to pipeline failure * More build pipeline related changes * A few more fixes * Added test project * Fix CMake issue * Should be cleaned up a bit more * Update build.yml * Update build.yml * Update build.yml * More changes * Fix build * Try again * Try again * Try to enable validation layers * Update build.yml * Update build.yml * Find out more * Log more stuff * More attempts * More fixes * More fixes * Update build.yml * Update build.yml * Update build.yml * Test directly setting env in main * Last try * Disabling validation layer if required ones are not found * Improved automated pipeline tests * Added SpirV caching on ShaderCompiler level * Should fix some issues * Fixed a bunch of things * Revert some previous BVH changes * Some small BVH changes * Some more fixes * Smaller changes * Fixed window not closing issue * Should have fixed a bunch of validation errors * More code style changes * More fixes * Build fixes * Extended tests * Added a new scene and new tests * Some SSGI tests * Did more changes to SSGI * Fixed a few things * Fixed a few things * Small fix for ocean * Fixed some issues * More tests, small fixes * Fix release pipeline * Fix one SSGI issue * Fixed a validation issue regarding shader rollback * Update thirdparty file * Some small improvements --- .github/workflows/build.yml | 229 ++++++- .github/workflows/release.yml | 27 +- .gitignore | 1 + CMakeLists.txt | 21 + LICENSE.md | 2 +- README.md | 3 + THIRDPARTY.md | 31 + data/chromesphere.bin | Bin 0 -> 130560 bytes data/chromesphere.gltf | 85 +-- data/shader/ao/rtao.csh | 7 +- data/shader/ao/ssao.csh | 2 +- data/shader/ao/temporal.csh | 6 +- data/shader/atmosphere.csh | 8 +- data/shader/brdf/importanceSample.hsh | 2 +- data/shader/brdf/preintegrateDFG.csh | 2 +- data/shader/clouds/clouds.hsh | 6 +- data/shader/clouds/integrate.csh | 4 +- data/shader/common/convert.hsh | 4 +- data/shader/common/ign.hsh | 2 +- data/shader/common/material.hsh | 5 +- data/shader/common/utility.hsh | 4 + data/shader/ddgi/probeDebug.fsh | 4 +- data/shader/ddgi/probeDebug.vsh | 6 +- data/shader/ddgi/rayHit.csh | 21 +- data/shader/deferred/deferred.hsh | 20 +- data/shader/deferred/geometry.fsh | 4 +- data/shader/deferred/geometry.vsh | 14 +- data/shader/deferred/indirect.csh | 70 ++- data/shader/deferred/sss.csh | 6 +- data/shader/globals.hsh | 19 +- data/shader/impostor/impostor.fsh | 8 +- data/shader/impostor/impostor.vsh | 16 +- data/shader/ocean/caustics.csh | 6 +- data/shader/ocean/depth.fsh | 2 +- data/shader/ocean/depth.vsh | 6 +- data/shader/ocean/ocean.fsh | 18 +- data/shader/ocean/ocean.vsh | 12 +- data/shader/ocean/shoreInteraction.hsh | 6 +- data/shader/ocean/underwater.csh | 12 +- data/shader/pathtracer/rayGen.csh | 27 +- data/shader/pathtracer/rayHit.csh | 92 ++- data/shader/pathtracer/temporal.csh | 411 +++++++++++++ data/shader/postprocessing.fsh | 5 +- data/shader/raytracer/buffers.hsh | 38 +- data/shader/raytracer/bvh.hsh | 321 +++++----- data/shader/raytracer/common.hsh | 3 +- data/shader/raytracer/direct.hsh | 4 +- data/shader/raytracer/intersections.hsh | 8 +- data/shader/raytracer/structures.hsh | 46 +- data/shader/raytracer/surface.hsh | 80 ++- data/shader/raytracer/texture.hsh | 138 ++--- data/shader/raytracer/traceClosest.csh | 2 + data/shader/reflection/rtreflection.csh | 16 +- data/shader/reflection/temporal.csh | 168 +++--- data/shader/shadowMapping.vsh | 4 +- data/shader/skybox.csh | 14 +- data/shader/ssgi/ssgi.csh | 171 ++++++ data/shader/ssgi/temporal.csh | 292 +++++++++ data/shader/taa.csh | 16 +- data/shader/terrain/terrain.fsh | 8 +- data/shader/terrain/terrain.tcsh | 8 +- data/shader/terrain/terrain.tesh | 6 +- data/shader/terrain/terrain.vsh | 4 +- data/shader/vegetation/common.hsh | 6 +- data/shader/vegetation/instanceCulling.csh | 2 +- data/shader/vegetation/vegetation.fsh | 4 +- data/shader/vegetation/vegetation.vsh | 10 +- data/shader/volumetric/volumetric.csh | 4 +- data/shader/volumetric/volumetric.hsh | 2 +- data/shader/volumetric/volumetricResolve.csh | 8 +- libs/ImguiExtension/ImguiWrapper.cpp | 4 +- libs/ImguiExtension/ImguiWrapper.h | 4 +- src/demo/App.cpp | 186 +++++- src/demo/App.h | 5 +- src/engine/CMakeLists.txt | 18 +- src/engine/Engine.cpp | 35 +- src/engine/Engine.h | 5 + src/engine/EngineInstance.cpp | 19 +- src/engine/EngineInstance.h | 11 +- src/engine/Main.cpp | 14 +- src/engine/RenderTarget.cpp | 33 +- src/engine/RenderTarget.h | 17 + src/engine/System.h | 2 + src/engine/Window.cpp | 20 +- src/engine/Window.h | 10 +- src/engine/common/Image.h | 2 +- src/engine/graphics/CommandList.cpp | 566 ++++++++++++------ src/engine/graphics/CommandList.h | 57 +- src/engine/graphics/Common.h | 14 +- .../{Descriptor.cpp => DescriptorPool.cpp} | 55 +- .../{Descriptor.h => DescriptorPool.h} | 18 +- src/engine/graphics/DescriptorSetLayout.cpp | 143 +++++ src/engine/graphics/DescriptorSetLayout.h | 76 +++ src/engine/graphics/Framebuffer.cpp | 22 +- src/engine/graphics/GraphicsDevice.cpp | 149 +++-- src/engine/graphics/GraphicsDevice.h | 58 +- src/engine/graphics/Image.cpp | 3 + src/engine/graphics/Image.h | 2 + src/engine/graphics/Instance.cpp | 116 ++-- src/engine/graphics/Instance.h | 18 +- src/engine/graphics/MemoryManager.cpp | 8 + src/engine/graphics/MemoryManager.h | 8 +- src/engine/graphics/MemoryTransferManager.cpp | 9 +- src/engine/graphics/Pipeline.cpp | 27 +- src/engine/graphics/Profiler.cpp | 28 +- src/engine/graphics/Profiler.h | 5 +- src/engine/graphics/RenderPass.cpp | 2 +- src/engine/graphics/Shader.cpp | 155 +++-- src/engine/graphics/Shader.h | 14 +- src/engine/graphics/ShaderCompiler.cpp | 103 +++- src/engine/graphics/ShaderCompiler.h | 25 + src/engine/graphics/Surface.cpp | 38 +- src/engine/graphics/Surface.h | 9 +- src/engine/lighting/SSGI.cpp | 12 + src/engine/lighting/SSGI.h | 32 + src/engine/loader/AssetLoader.cpp | 16 + src/engine/loader/AssetLoader.h | 8 + src/engine/loader/ModelLoader.cpp | 45 +- src/engine/loader/ModelLoader.h | 9 + src/engine/loader/ShaderLoader.cpp | 24 +- src/engine/loader/ShaderLoader.h | 3 - src/engine/mesh/DataComponent.h | 16 +- src/engine/mesh/Mesh.cpp | 67 +++ src/engine/mesh/Mesh.h | 24 + src/engine/mesh/MeshData.cpp | 18 +- src/engine/mesh/MeshData.h | 11 +- src/engine/ocean/Ocean.h | 4 +- src/engine/ocean/OceanSimulation.h | 6 +- src/engine/pipeline/PipelineManager.cpp | 79 ++- src/engine/pipeline/PipelineManager.h | 8 +- src/engine/postprocessing/PostProcessing.h | 1 + src/engine/renderer/AORenderer.cpp | 1 - src/engine/renderer/ExampleRenderer.cpp | 8 +- src/engine/renderer/ExampleRenderer.h | 2 +- .../renderer/GBufferDownscaleRenderer.h | 3 - src/engine/renderer/GIRenderer.cpp | 296 +++++++++ src/engine/renderer/GIRenderer.h | 57 ++ src/engine/renderer/IndirectLightRenderer.cpp | 19 +- src/engine/renderer/MainRenderer.cpp | 240 ++++++-- src/engine/renderer/MainRenderer.h | 10 + src/engine/renderer/OceanRenderer.cpp | 18 +- src/engine/renderer/OceanRenderer.h | 1 + src/engine/renderer/PathTracingRenderer.cpp | 179 +++++- src/engine/renderer/PathTracingRenderer.h | 97 ++- src/engine/renderer/PostProcessRenderer.cpp | 94 +++ src/engine/renderer/PostProcessRenderer.h | 5 + src/engine/renderer/RTReflectionRenderer.cpp | 1 - src/engine/renderer/TemporalAARenderer.cpp | 78 ++- src/engine/renderer/TemporalAARenderer.h | 10 +- .../renderer/helper/RayTracingHelper.cpp | 70 +-- src/engine/resource/ResourceManager.h | 2 + src/engine/scene/RTData.cpp | 430 ++++--------- src/engine/scene/RTData.h | 39 +- src/engine/scene/RTStructures.h | 25 +- src/engine/scene/Scene.cpp | 57 +- src/engine/scene/Scene.h | 14 +- src/engine/texture/Texture.cpp | 2 +- src/engine/texture/Texture.h | 6 +- src/engine/texture/Texture2D.h | 27 +- src/engine/texture/Texture2DArray.cpp | 2 +- src/engine/texture/Texture2DArray.h | 2 +- src/engine/texture/TextureAtlas.cpp | 9 +- src/engine/texture/TextureAtlas.h | 7 +- src/engine/tools/ImpostorTool.cpp | 2 + src/engine/volume/BVH.cpp | 109 ++-- src/engine/volume/BVH.h | 8 +- src/tests/App.cpp | 401 +++++++++++++ src/tests/App.h | 107 ++++ src/tests/CMakeLists.txt | 50 ++ src/tests/Main.cpp | 117 ++++ vcpkg.json | 8 +- 171 files changed, 5853 insertions(+), 1875 deletions(-) create mode 100644 data/chromesphere.bin create mode 100644 data/shader/pathtracer/temporal.csh create mode 100644 data/shader/ssgi/ssgi.csh create mode 100644 data/shader/ssgi/temporal.csh rename src/engine/graphics/{Descriptor.cpp => DescriptorPool.cpp} (59%) rename src/engine/graphics/{Descriptor.h => DescriptorPool.h} (51%) create mode 100644 src/engine/graphics/DescriptorSetLayout.cpp create mode 100644 src/engine/graphics/DescriptorSetLayout.h create mode 100644 src/engine/lighting/SSGI.cpp create mode 100644 src/engine/lighting/SSGI.h create mode 100644 src/engine/renderer/GIRenderer.cpp create mode 100644 src/engine/renderer/GIRenderer.h create mode 100644 src/tests/App.cpp create mode 100644 src/tests/App.h create mode 100644 src/tests/CMakeLists.txt create mode 100644 src/tests/Main.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f4a78e7a7..5f528a570 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,6 +13,7 @@ env: BUILD_TYPE_DEBUG: Debug BUILD_TYPE_RELEASE: Release USE_CACHE: true + RUN_TESTS: true jobs: windows-build: @@ -26,22 +27,35 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - name: Restore swiftshader repository cache + uses: actions/cache/restore@v3 + id: swiftshadercache-restore + with: + path: ${{ github.workspace }}/swiftshader/build/Windows + key: ${{ runner.os }} + + - name: Checkout swiftshader repository + uses: actions/checkout@v4 + if: steps.swiftshadercache-restore.outputs.cache-hit != 'true' + with: + repository: google/swiftshader + path: swiftshader - name: Setup Vcpkg uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 8568364fbeb414db3fa5b5bfc436e57b4368c55f + committish: 6c937c32233bdf295ab2140dbce97fd00084a5f3 cache: ${{ env.USE_CACHE }} # This doesn't work when the Visual Studio C++ CLI was set up first (maybe needs a setup with 2019 version) - - name: Prepare Vulkan SDK - uses: humbletim/setup-vulkan-sdk@v1.2.0 + - name: Install Vulkan SDK + uses: humbletim/install-vulkan-sdk@v1.1.1 with: - vulkan-query-version: 1.3.204.0 - vulkan-components: Vulkan-Headers, Vulkan-Loader - vulkan-use-cache: false + version: 1.3.261.1 + cache: true - name: Setup Microsoft Visual C++ CLI uses: ilammy/msvc-dev-cmd@v1 @@ -61,6 +75,25 @@ jobs: ${{ github.workspace }}/vcpkg/vcpkg install --clean-after-build --triplet=x64-windows Remove-Item –path vcpkg_installed –recurse + - name: Build swiftshader + uses: ashutoshvarma/action-cmake-build@master + if: steps.swiftshadercache-restore.outputs.cache-hit != 'true' + with: + build-dir: ${{ github.workspace }}/swiftshader/build + source-dir: ${{ github.workspace }}/swiftshader + cc: "cl" + cxx: "cl" + configure-options: -G Ninja -DSWIFTSHADER_BUILD_TESTS=OFF -DSWIFTSHADER_ENABLE_ASTC=OFF + parallel: 16 + build-type: MinSizeRel + + - name: Save swiftshader respository cache + id: swiftshadercache-save + uses: actions/cache/save@v3 + with: + path: ${{ github.workspace }}/swiftshader/build/Windows + key: ${{ steps.swiftshadercache-restore.outputs.cache-primary-key }} + - name: Build ${{ matrix.build-type }} configuration with CMake uses: ashutoshvarma/action-cmake-build@master with: @@ -69,10 +102,39 @@ jobs: cc: "cl" cxx: "cl" configure-options: -DCMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake' - -DATLAS_DEMO=ON -G Ninja + -DATLAS_DEMO=ON -DATLAS_TESTS=OFF -G Ninja parallel: 16 build-type: ${{ matrix.build-type }} + - name: Build ${{ matrix.build-type }} test configuration with CMake + uses: ashutoshvarma/action-cmake-build@master + if: ${{ env.RUN_TESTS }} == true + with: + build-dir: ${{ github.workspace }}/tests/${{ matrix.build-type }} + source-dir: ${{ github.workspace }} + cc: "cl" + cxx: "cl" + configure-options: -DCMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake' + -DATLAS_TESTS=ON -DATLAS_BINDLESS=OFF -DATLAS_HEADLESS=ON -G Ninja + parallel: 16 + build-type: ${{ matrix.build-type }} + + - name: Run tests + shell: pwsh + if: ${{ env.RUN_TESTS }} == true + # Add additional scripting steps here + run: | + Copy-Item -Path "${{ github.workspace }}\swiftshader\build\Windows\*.*" -Destination "${{ github.workspace }}/bin/tests/${{ matrix.build-type }}" + Copy-Item -Path "${{ github.workspace }}\VULKAN_SDK\Bin\VkLayer_khronos_validation.*" -Destination "${{ github.workspace }}/bin/tests/${{ matrix.build-type }}" + cd ${{ github.workspace }}/bin/tests/${{ matrix.build-type }} + set VK_LOADER_LAYERS_ENABLE=*validation + set VK_ADD_LAYER_PATH=${{ github.workspace }}\VULKAN_SDK\Bin\ + .\AtlasEngineTests.exe + env: + VK_ICD_FILENAMES: ${{ github.workspace }}/bin/${{ matrix.build-type }}/vk_swiftshader_icd.json + VK_ADD_LAYER_PATH: ${{ github.workspace }}\VULKAN_SDK\Bin\;${{ github.workspace }}/bin/tests/${{ matrix.build-type }} + VK_LOADER_LAYERS_ENABLE: '*validation' + - name: Upload artifact if: ${{ matrix.build-type == 'Release' }} uses: actions/upload-artifact@v2 @@ -85,6 +147,7 @@ jobs: THIRDPARTY.md ${{ github.workspace }}/bin/**/AtlasEngineDemo.exe ${{ github.workspace }}/bin/**/*.dll + !${{ github.workspace }}/bin/tests !**/CMakeFiles linux-build: @@ -98,13 +161,27 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - name: Restore swiftshader repository cache + uses: actions/cache/restore@v3 + id: swiftshadercache-restore + with: + path: ${{ github.workspace }}/swiftshader/build/Linux + key: ${{ runner.os }} + + - name: Checkout swiftshader repository + uses: actions/checkout@v4 + if: steps.swiftshadercache-restore.outputs.cache-hit != 'true' + with: + repository: google/swiftshader + path: swiftshader - name: Setup Vcpkg uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 8568364fbeb414db3fa5b5bfc436e57b4368c55f + committish: 6c937c32233bdf295ab2140dbce97fd00084a5f3 cache: ${{ env.USE_CACHE }} - name: Setup Ninja @@ -113,13 +190,12 @@ jobs: # ninja version to download. Default: 1.10.0 version: 1.10.0 - - name: Prepare Vulkan SDK - uses: humbletim/setup-vulkan-sdk@v1.2.0 + - name: Install Vulkan SDK + uses: humbletim/install-vulkan-sdk@v1.1.1 with: - vulkan-query-version: 1.3.204.0 - vulkan-components: Vulkan-Headers, Vulkan-Loader - vulkan-use-cache: false - + version: 1.3.261.1 + cache: true + - name: Run scripts shell: bash # Add additional scripting steps here @@ -130,6 +206,25 @@ jobs: ${{ github.workspace }}/vcpkg/vcpkg install --clean-after-build --triplet=x64-linux rm -r vcpkg_installed + - name: Build swiftshader + uses: ashutoshvarma/action-cmake-build@master + if: steps.swiftshadercache-restore.outputs.cache-hit != 'true' + with: + build-dir: ${{ github.workspace }}/swiftshader/build + source-dir: ${{ github.workspace }}/swiftshader + cc: "gcc" + cxx: "g++" + configure-options: -G Ninja -DSWIFTSHADER_BUILD_TESTS=OFF -DSWIFTSHADER_ENABLE_ASTC=OFF + parallel: 16 + build-type: MinSizeRel + + - name: Save swiftshader respository cache + id: swiftshadercache-save + uses: actions/cache/save@v3 + with: + path: ${{ github.workspace }}/swiftshader/build/Linux + key: ${{ steps.swiftshadercache-restore.outputs.cache-primary-key }} + # https://github.com/marketplace/actions/setup-ninja - name: Build ${{ matrix.build-type }} configuration with CMake uses: ashutoshvarma/action-cmake-build@master @@ -139,10 +234,36 @@ jobs: cc: "gcc" cxx: "g++" configure-options: -DCMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake' - -DATLAS_DEMO=ON -DATLAS_DEMO=ON -G Ninja + -DATLAS_DEMO=ON -DATLAS_TESTS=OFF -G Ninja + parallel: 16 + build-type: ${{ matrix.build-type }} + + - name: Build ${{ matrix.build-type }} test configuration with CMake + uses: ashutoshvarma/action-cmake-build@master + if: ${{ env.RUN_TESTS }} == true + with: + build-dir: ${{ github.workspace }}/tests/${{ matrix.build-type }} + source-dir: ${{ github.workspace }} + cc: "gcc" + cxx: "g++" + configure-options: -DCMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake' + -DATLAS_TESTS=ON -DATLAS_BINDLESS=OFF -DATLAS_HEADLESS=ON -G Ninja parallel: 16 build-type: ${{ matrix.build-type }} + - name: Run tests + shell: bash + if: ${{ env.RUN_TESTS }} == true + # Add additional scripting steps here + run: | + cd ${{ github.workspace }}/bin/tests/${{ matrix.build-type }} + ./AtlasEngineTests + env: + VK_ICD_FILENAMES: ${{ github.workspace }}/swiftshader/build/Linux/vk_swiftshader_icd.json + VK_ADD_LAYER_PATH: ${{ github.workspace }}/VULKAN_SDK/lib/vulkan/layers/:${{ github.workspace }}/VULKAN_SDK/etc/vulkan/explicit_layer.d/ + LD_LIBRARY_PATH: ${{ github.workspace }}/VULKAN_SDK/lib/ + VK_LOADER_LAYERS_ENABLE: '*validation' + - name: Upload artifact if: ${{ matrix.build-type == 'Release' }} uses: actions/upload-artifact@v2 @@ -155,6 +276,7 @@ jobs: THIRDPARTY.md ${{ github.workspace }}/bin/**/AtlasEngineDemo ${{ github.workspace }}/bin/**/*.so* + !${{ github.workspace }}/bin/tests !**/CMakeFiles macos-build: @@ -170,11 +292,25 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - name: Restore swiftshader repository cache + uses: actions/cache/restore@v3 + id: swiftshadercache-restore + with: + path: ${{ github.workspace }}/swiftshader/build/Darwin + key: ${{ runner.os }} + + - name: Checkout swiftshader repository + uses: actions/checkout@v4 + if: steps.swiftshadercache-restore.outputs.cache-hit != 'true' + with: + repository: google/swiftshader + path: swiftshader + - name: Setup Vcpkg uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 8568364fbeb414db3fa5b5bfc436e57b4368c55f + committish: 6c937c32233bdf295ab2140dbce97fd00084a5f3 cache: ${{ env.USE_CACHE }} - name: Setup Ninja @@ -183,12 +319,11 @@ jobs: # ninja version to download. Default: 1.10.0 version: 1.10.0 - - name: Prepare Vulkan SDK - uses: humbletim/setup-vulkan-sdk@v1.2.0 + - name: Install Vulkan SDK + uses: humbletim/install-vulkan-sdk@v1.1.1 with: - vulkan-query-version: 1.3.204.0 - vulkan-components: Vulkan-Headers, Vulkan-Loader - vulkan-use-cache: false + version: 1.3.261.1 + cache: true - name: Run scripts shell: bash @@ -199,6 +334,25 @@ jobs: ${{ github.workspace }}/vcpkg/vcpkg install --clean-after-build --triplet=x64-osx rm -r vcpkg_installed + - name: Build swiftshader + uses: ashutoshvarma/action-cmake-build@master + if: steps.swiftshadercache-restore.outputs.cache-hit != 'true' + with: + build-dir: ${{ github.workspace }}/swiftshader/build + source-dir: ${{ github.workspace }}/swiftshader + cc: "clang" + cxx: "clang++" + configure-options: -G Ninja -DSWIFTSHADER_BUILD_TESTS=OFF -DSWIFTSHADER_ENABLE_ASTC=OFF + parallel: 16 + build-type: MinSizeRel + + - name: Save swiftshader respository cache + id: swiftshadercache-save + uses: actions/cache/save@v3 + with: + path: ${{ github.workspace }}/swiftshader/build/Darwin + key: ${{ steps.swiftshadercache-restore.outputs.cache-primary-key }} + # https://github.com/marketplace/actions/setup-ninja - name: Build ${{ matrix.build-type }} configuration with CMake uses: ashutoshvarma/action-cmake-build@master @@ -208,10 +362,36 @@ jobs: cc: "clang" cxx: "clang++" configure-options: -DCMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake' - -DATLAS_DEMO=ON -G Ninja + -DATLAS_DEMO=ON -DATLAS_TESTS=OFF -G Ninja parallel: 16 build-type: ${{ matrix.build-type }} + - name: Build ${{ matrix.build-type }} test configuration with CMake + uses: ashutoshvarma/action-cmake-build@master + if: ${{ env.RUN_TESTS }} == true + with: + build-dir: ${{ github.workspace }}/tests/${{ matrix.build-type }} + source-dir: ${{ github.workspace }} + cc: "clang" + cxx: "clang++" + configure-options: -DCMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake' + -DATLAS_TESTS=ON -DATLAS_BINDLESS=OFF -DATLAS_HEADLESS=ON -G Ninja + parallel: 16 + build-type: ${{ matrix.build-type }} + + - name: Run tests + shell: bash + if: ${{ env.RUN_TESTS }} == true + # Add additional scripting steps here + run: | + cd ${{ github.workspace }}/bin/tests/${{ matrix.build-type }} + ./AtlasEngineTests + env: + VK_ICD_FILENAMES: ${{ github.workspace }}/swiftshader/build/Darwin/vk_swiftshader_icd.json + VK_ADD_LAYER_PATH: ${{ github.workspace }}/VULKAN_SDK/share/vulkan/explicit_layer.d/ + DYLD_LIBRARY_PATH: ${{ github.workspace }}/VULKAN_SDK/lib/ + VK_LOADER_LAYERS_ENABLE: '*validation' + - name: Upload artifact if: ${{ matrix.build-type == 'Release' }} uses: actions/upload-artifact@v2 @@ -224,4 +404,5 @@ jobs: THIRDPARTY.md ${{ github.workspace }}/bin/**/AtlasEngineDemo ${{ github.workspace }}/bin/**/*.dylib* + !${{ github.workspace }}/bin/tests !**/CMakeFiles diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6e1930147..3261dd719 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 8568364fbeb414db3fa5b5bfc436e57b4368c55f + committish: 6c937c32233bdf295ab2140dbce97fd00084a5f3 - name: Setup Microsoft Visual C++ CLI uses: ilammy/msvc-dev-cmd@v1 @@ -42,12 +42,12 @@ jobs: # ninja version to download. Default: 1.10.0 version: 1.10.0 - - name: Prepare Vulkan SDK - uses: humbletim/setup-vulkan-sdk@v1.2.0 + # This doesn't work when the Visual Studio C++ CLI was set up first (maybe needs a setup with 2019 version) + - name: Install Vulkan SDK + uses: humbletim/install-vulkan-sdk@v1.1.1 with: - vulkan-query-version: 1.3.204.0 - vulkan-components: Vulkan-Headers, Vulkan-Loader - vulkan-use-cache: false + version: 1.3.261.1 + cache: true - name: Run scripts shell: pwsh @@ -65,7 +65,7 @@ jobs: cc: "cl" cxx: "cl" configure-options: -G Ninja -DCMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake' - -DASSIMP_BUILD_TESTS=OFF -DASSIMP_BUILD_ASSIMP_TOOLS=OFF -DATLAS_DEMO=ON + -DASSIMP_BUILD_TESTS=OFF -DASSIMP_BUILD_ASSIMP_TOOLS=OFF -DATLAS_DEMO=ON -DATLAS_TESTS=OFF parallel: 16 build-type: ${{ matrix.build-type }} @@ -99,7 +99,7 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 8568364fbeb414db3fa5b5bfc436e57b4368c55f + committish: 6c937c32233bdf295ab2140dbce97fd00084a5f3 - name: Setup Ninja uses: ashutoshvarma/setup-ninja@master @@ -107,12 +107,11 @@ jobs: # ninja version to download. Default: 1.10.0 version: 1.10.0 - - name: Prepare Vulkan SDK - uses: humbletim/setup-vulkan-sdk@v1.2.0 + - name: Install Vulkan SDK + uses: humbletim/install-vulkan-sdk@v1.1.1 with: - vulkan-query-version: 1.3.204.0 - vulkan-components: Vulkan-Headers, Vulkan-Loader - vulkan-use-cache: false + version: 1.3.261.1 + cache: true - name: Run scripts shell: bash @@ -132,7 +131,7 @@ jobs: cc: "gcc" cxx: "g++" configure-options: -G Ninja -DCMAKE_TOOLCHAIN_FILE='${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake' - -DASSIMP_BUILD_TESTS=OFF -DASSIMP_BUILD_ASSIMP_TOOLS=OFF -DATLAS_DEMO=ON + -DASSIMP_BUILD_TESTS=OFF -DASSIMP_BUILD_ASSIMP_TOOLS=OFF -DATLAS_DEMO=ON -DATLAS_TESTS=OFF parallel: 16 build-type: ${{ matrix.build-type }} diff --git a/.gitignore b/.gitignore index eab8f391f..417c649a3 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ data/dump.txt data/subway data/material demo data/emissivesphere.gltf +data/.cache diff --git a/CMakeLists.txt b/CMakeLists.txt index 04c3afb9d..9b010dca6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,10 +28,22 @@ option(ATLAS_NO_APP "Disables the engines main function" OFF) option(ATLAS_DEMO "Build demo executable" OFF) option(ATLAS_IMGUI "Activate ImGui integration" OFF) option(ATLAS_ASSIMP "Activate Assimp integration" ON) +option(ATLAS_HEADLESS "Activate support for running the engine in headless mode" OFF) +option(ATLAS_BINDLESS "Activate support for running the engine with bindless resources turned on" ON) + +if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + option(ATLAS_TESTS "Activate support for running the engine with bindless resources turned on" ON) +else() + option(ATLAS_TESTS "Activate support for running the engine with bindless resources turned on" OFF) +endif() + if (ATLAS_DEMO) set (ATLAS_IMGUI ON) endif() +if (ATLAS_TESTS) + set (ATLAS_IMGUI ON) +endif() set(CMAKE_CXX_STANDARD 20) set(CMAKE_BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bin/${CMAKE_BUILD_TYPE}) @@ -53,6 +65,7 @@ endif() # Set dependencies location ####################################################################### set (ATLAS_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/engine) set (DEMO_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/demo) +set (TESTS_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/tests) set (IMGUI_EXTENSION_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libs/ImguiExtension) # Add dependencies ################################################################################ @@ -73,6 +86,10 @@ find_package(glslang CONFIG REQUIRED) find_package(unofficial-spirv-reflect CONFIG REQUIRED) find_package(SPIRV-Tools-opt CONFIG REQUIRED) +if (ATLAS_TESTS) +find_package(GTest CONFIG REQUIRED) +endif() + add_subdirectory(${ATLAS_LOCATION}) if (ATLAS_IMGUI) @@ -82,3 +99,7 @@ endif() if (ATLAS_DEMO) add_subdirectory(${DEMO_LOCATION}) endif() + +if (ATLAS_TESTS) + add_subdirectory(${TESTS_LOCATION}) +endif() diff --git a/LICENSE.md b/LICENSE.md index 38fd469dc..9acbbb332 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Simon Tippe +Copyright (c) 2024 Simon Tippe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index e9bc60d3a..031cec409 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,9 @@ asset directory is correct. initialize the engine yourself - **ATLAS_IMGUI** Enables the [ImGui](https://github.com/ocornut/imgui) integration. Is enabled by default if the demo project is build. - **ATLAS_ASSIMP** Enables the [Assimp](https://github.com/assimp/assimp) integration. Is enabled by default. +- **ATLAS_HEADLESS** Enables the headless support, which means no window needs to be created. Is disabled by default. +- **ATLAS_BINDLESS** Enables support for bindless resources. Might be problematic on MacOS. Enabled by default. +- **ATLAS_TESTS** Generates the testing project and allows to target it. ## Documentation If you want more information have a look into the [Documentation](https://tippesi.github.io/Atlas-Engine-Doc/index.html). ## License diff --git a/THIRDPARTY.md b/THIRDPARTY.md index 52e81ff83..3a9b13d2a 100644 --- a/THIRDPARTY.md +++ b/THIRDPARTY.md @@ -1,3 +1,34 @@ +GTest +-------------------------------------------------------------------------------- + Copyright 2008, Google Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + SDL2 -------------------------------------------------------------------------------- diff --git a/data/chromesphere.bin b/data/chromesphere.bin new file mode 100644 index 0000000000000000000000000000000000000000..445902a123b0a37a87c814c42662c32c11121d25 GIT binary patch literal 130560 zcmb?^3EYpx`aUH(M++@Pix6#6w#xF%NGYdHlBGh0N=On?=xE-zT2+x5JE&!@*bzdj$`*ZX|tJIg&Y_uO;OJ@-_p!v7|C{*P!w z@G+*X{r1NX-ItT9+YQA}TyV9k^3#W|>EdJUhT@%m9Oy@&Tvm2Nv6SUE6iXZZhGLXu z&|Xv4-VMSi%Y)CRjL((DtaJEnAuM$!uR@&*Vf2edADOZ~+91qPh_1E#AJK;318QC5 z=Ki>uAKvaT8-t%3G|V;czMFq=_MCh+eh~M#?(eK^QncOl4hdQh|eg_kSg(oHkOCOmSEV4a~!6MsT3>Mk$Vz9_| z7lTE%yBI98-Nj&$?Jfp?{ZJ1-80!Usv5ZjZTs zB-VH46N3-A@L4wr`Udlf!M!I>a2G)TVLmZ<-oD?Y1EG&GpBUU|r-klu$occ2&&S~H zU;N&2eSZe}d<@>VQC&X+a(*22`54@MuYLSI==&R?&&S}Nbvpa2P@lHY=VR~!e+=qV z#s30*J_f(P(+Pe*^jBZ#^D$WVZaxOf-p$8g*}M4|EPFQ>gJtjLVzBJpTnv`ITN;Bo zX4+!>oY-ki8iSuG8_&N@8tGzikFxpI&ZjO0?}hn(FXsC#zyI!HaGO8nkB`Ci{*;G4 z1~>duKKmFfdjTJVYnJU(nziriWAKI8H_d@u-C|L1AA?=lK5oGFU40Dh{O7*X$KbHH zoqbVrAA_f2-~A}&DfZny2KVSc%nsfCG4m_zHZl10k4D;=lP8$Yu-nAo?%$8L*KM?k zc^P(_7<}oNvuxjU>daaVyG;yUdEj9C?~OKbzrt=4gCFeK2lkB#t{v<)F?iUihuP<-t`!VFI_&^ z#bC~b`!FvK+`X=g!C`Lxa>cc24BqAB{^m{0_s=>FvN3qhS38+6(XJ~dkF+s3J#||0 z3iy2X@8fI?o`kv1f4~3!PaA_#hZOa3qyDH*r7?Koz#mt}pGsr&kwbsEvi_OofV=!d9$9iEP+qqs;8bh{wtX~Fl zp6geoF=V^PdTJo&O$>%?_gvp0=efRD8bh{w$ax1jPkp1(7_!|%&O69?>OYmnknJA& zn1h_BK2~XrG2<|PT-o@kG(NP$`wrvT4aRtm!PT~{=2wHyoL@0`r*rCf&iBVL-(xUj zj)(j)7ycoCDvhUpvbX;g^Wr1O!x%iS>%JcH*&GJ>9D^a-OZh;~`xp$_?jh$5va%2Mp(9QJY4pDT^aWP3~O&0kn=tUL$;UnaL9QdgJoafW3cQ~d<+izChX&U494EACI4X`=VNf# zOJX0#`jqyVmBF&NC4a)6cZ0F)wb@@`55B<|HhByCyn%h5=R1|gu*qATH(A){O$>%j z-s1er!ai?eFl_P`_IV5YyotfE$y=NcTAmNaVA$jh&MPg?D`PNh@&@OphUcd-7&dtW zza3NNw^L~hn|ufO5n0&hZ48D@K81bW!ai?fFl_P;epVLtc^iXalXvj@vh@3k!LZ3= zO|N2MpQj&Mr7>*s9`<=dzql9-UmXuWD+50(`t4L2!*|HT&&tBjihe|u#_(nG@Uyb; zv!Y*9r7?V~Jp8OI{H$ih;7|wnS<&ySRG-RV;R*fF0)I9bOCQlMF7($1WB5{f_*HWJ z_!u0u?g#ikHPS<3G0`S!SJyO>#2{y@UeluQQgAd#>HUx*p&1Q_}jP`3?G}4{sVs- z7lWyT!QaNf--iAYmB!QmS0{lVzBSP6VlaG2lB-`l+m_u6s5FL8OY+E5Lu}bSg-T=i z*cjZKuqXa;Z=%xpkn@i)xW{4pLa&R#iw6xexL0ByfL<4aJJq_#;GT=+J(m~^UrGzV zN=rYz7z|%Z3%|-zJ~0@+loo!KrF>#Ad?_vbDogpqVE9rR_*ItjiNWxtH1Mk|gop!h{W6SQ#R2su~+Tp&8ExRvM zX$;?KkNYyV?7mE;F?^>z?#tM+`!bcr@SXO!FJsH@%TyXmztev{^!o;5>390ihkoB+ zEd5UZ`OxnhjHTb{KOg#igR%5G{pUl!Z!ngAr~iED_YKC<@ARJ!{l39i*7P(6%lb}# z(XhU6FqZY5{;Oeq-(W23JN;?H`o6(f)^s0(Wli@nSk`nOgXitNhuQy%bM488eV@kQ z8l7)5+m1fduDQCZi@}Xg>tl8<<#WD^!8g@kjeB*wBv0{EESke7?2)68Ut;IE=w(jQZM4N4w^L&oTJ7OXry?^}**cZG8;h zd*chzc98RvYjtrk_{3e7E@%Y#JazI+7lRM)*3vPbA{A0LC8 zH|*saLe4*YaT6bdyPJ7#I_h)pm^wZNR~hr0qntnQoN7J>_kXX3sRsG;=)}W)4DM8` z!Gc+khiCuv59q6v$20mgbsHVj(EfKwCm(~~IPgwa1^FDhppB2gHHLoT9z?sges>oi zgQv{j+E)diYh6*>$KZSW9PRdloTt4m23PHRkJ}#d`SV6o)Bg+nMWd-n$fq)Rw~bc1 z#gMC?x7x+U;D>+s&>aam-*NIKEn~3M!NlOx4%pnk1$p@E5u;rUp0{aj|19M5SvSo{ z|1a>Un`R^-pUPlqi;2OlyEgGJK#nzNJk`bEY4i8=*F&y0I=-ok!59C%xvv2^f8bNs zw2Z-_4o#15YGQCp@aMTd_+w)5DYf_a-$9?hzNU9E`1E%=`kNsSXSW{iV(=D=I{S~H zhi}n(TKa#46Uk?TacIk&*27H<7T%f|yw9KdyotewmFeL_mtSXM@Ns4Pg1aWqG%;9o zW)p)aLZ9ygIe#wVW5wVj%JvsKcl*l3;9-C2y(R{?D$~Oo4c*wr;3j4Jf-6SUvQfBf zpE9FgJ=-J#i;ikzu;|P-1_vGfp^KZ?7##K#E1T_OW3cRTYz!9N(#GKG(0d>HL+`aQ z_;u*v8~>q)+ZcQ=_65ub`vMz-cP!hRjQ;5#HUa&zl%5X9Ok&i|yLR;M%aOzw(D&-Nazp_7@_b&riP0#Ncq&&}?}% z6NAO(Y-6z4z-VA_g*r+t1*TN{Ie?RxIl4Q&h# zw)A6`SFpS|`moB{8 zya4-r3_k71E_TEv%gpIGFNnb(T{94RPxVKKz>Xb*A920yT|>4s$HJ~2gWE4V*iIXA zt$7A^{urFV5B5s%=PjH&#Ne+7_O_GSZtAAP{v3l>;ob#onN?Q79v*}L@q@J~`1~X6 z^D+3N`Fq;$Fb;P|ysj9$YnN^8iI`t4ziZ=TaJ8em*c&#!z_o=-=p@YZ(PUB|b&8Fuv;{NNjnY`ceQnmvbh@-evavzy!dArBqS9b)jL249#HHbIOf zoP)&Rg9rGeJM!r}eu$63Z{Iqh!;NUy$?0exgBR{{RC*l8VZXsX25KJ^>CEeU@kYhXFHNeNiuKdsAvrj;~V(`^l404q7ll^EP zgIlb;(LDq?zr(!Kd<_10`CK;@{F%76r;ow6j34S8*BFO8;T~8F?uB#MzL;Mxb?oe8@c0@xyJ?W~XG3np;L#^9 za?hf_PQrYT!D~By>c*~lX?9P{uNd49F)w$9+*pKh7=wFu*~Z@mdAJ6Aj=|qNy`%39 z`TW>f`}i2#<%ZsVFXVGC&QoIW_d`zeBhjt{aQ+p8U;DJb-xcFbiGk616L&(Aq)9~*=FV*Q$ecCG5z*~Z{gp>OoWID8TLL}9GUTcE!d ztnFz}jlg4|N3FvAs*gKYG5AO5tCJzeu19@haE${#bsN>#ZO+km4X`ozMd+DVLjLSN z?=%~OkAglw6>@$%|CKL5_)-RGQvIL9&g zY3!S>0H1I0qiqb{{-Vv@2N;LFQJ)xWnjDq(!2D_f{>0#mq0ip~IX`Or5F3MMWB;`n z{WUTjZDVk6>~r^?Rc%hcs^e`8?gcwQO~{Sj$R`GG4Ex5vAP-^RurYXd*jtWpNGS* zDF(N|InhLn!yn=I6@#C``Pa8SmYDy+pDhOe+^vg!6mow5D{9*qyb9+jo44E4%{ix< zjlpl?-0GLP+s&Ok^}O26~jgGy=~bKh)fhd`82*8iT7J@TsZV?AAxR!(JDI z&)@HMv&E%DWCeCgU`fU%~dbD+SP$QJO&@K@<#LI zkZawx;ByS#@}kYnFBe|zdSV>L;AXc@Sa3J^GZ6DD27mwbj<)Y6Pr2E!8^_?2#{6bZ z?YYFgfc}cXUoM|({(Z?ZcP-{c41V~MZe}L(*#UAR2G?kER5B3l+6VG52DcvI(_Y~7 zagfh3xa!ee{6xt4{cvs`gCECzhZ&I1y>M@)Lw_+7-{cdi-eM?%hD1wWY>eES=Xd@IPq{oxM~gIoV#{S%PSTV2w` z$KX3$Z$A_HjDp`z3~tnJi2v=unr;aEqhjz@&u)%!c$s?{vG~WG5E+MyZBMa$MGJ01fI3xIKKnhwe5{FT?`(5{Hgxe zYEQY^wY#_&yiu8)Z-f0;4F0T4K0l28R}7w4wq9`G9fRM7Tph9Jt&d!WeOwHtY_IlK z>)pS@{yheBt!dos))sfe-V%cw|0xem45mz;@rQi2F_>$_eB@Ib_MaGhN7?$dFYIhF z__H!O=CNOn!Hvq~>gJ2zG*P%r&aZ)8t5F2zTC)~%{y^COV(`{w>);Epug2g}f65;d zgCBuB-0=^2XksvB@@UBC6n4KD%ryf3y<9JB4Cb2E`;Ya@#$c|Y$6-DF40iPxEVddO zgXhBD^6x+FEj9*U1e?z+{0Mrraj8W;6|`ZOohHW z0OwyZn6{B0Ay<3g+%pE#R^y@1*8zWG@ENcp{f7F~g+D+HJ|1?h`@kRiOT^%oW%h(V zI3J9`e}{deKjh)wnC~%|HkRp-&)Z{u#o#Ys|Cxb&uE98r!6(2Tbqd-Adz6jA-<8?N z=qD3{S4=tE9s#?}4};Hd8G|?fa-8i0`CJeFQU6Ccm3%fBfBw)odoASr4)C*z!3g_f z51L%P=|PBZ6NAg@(1QMuF?edV(e``TH{dU7VsM|E$JvV@pQpj!F9yRWvy{)&Km0Q* zjqgCazD2v*!4EbD!>8Adg&Z^RXN$qtjvHa0g>$YJ zhVaY%Kfo5JG$vakI|JhV#V-_QQdEbG?NUj(~)3`UF# z^Y6|(`t4xnkHJGvUt(6Aez@NU_V5_o)yy-unzsH?*ym&LYX{zGemthBxEjva&doOix?>7#DGE$r$sxPHT4rdHbCFT#D#7(As|^~X!T6;4ETH>?DH}Bl(xN-i5Q1JBYtKK?zv@y z4%0BdF2MOk46e5E1qtFG`j2prJ_grsytG3d^w&qY{~m+i_~60?^Dr-l!>%5K-@S2I zx&(6LCD{36@W+=vmpaJ9ZE@}pgGanq!+it!j7z>g2Jd*;JhxH(I{pj9H;%#I^?tzp z2kn}Sc*`;Pu2EmRnHYz6BA*!i#vZr2Rp_tX5Jx%&zkqm^Q!u}_fDe8Qe);pQ{r@1x z_Cvyok8*G5GYCHuKdW=U+oUG5Dv=oB4OZp9JlS z!5vTQ<0$7>U46cb!Br6l_%O)%&%y5){P%fP{XFpbRmAy^!B3pn*pIpi?%5rb#dU+o^kyr_Y98Dj8n(C21D zZu}kj#NgvzYV9XO9)65=#o*_*Ji3GLbl@sVQi z8D-9@q8fK z6@w{rW<&l|Ips1FgNH)S|FodJ|LvwFCI&CTI`|>@(+P2?V(@m58?_)ecELD|!J9zN zcY>U6ihhs5TtlA#pKBnFSqy#_`o`rLhfQ&g5rd~fkGc(V{sF`di^0nw=UYL}{|LDe zgZp4z-Uj`(E9QF){toNkW0)5sFu!8(3h0L~KyDn4aTtSthTgjX^6+8sIR@_pJ$xPH z^Dl@O7lQ}Z-rs(Ue6B;>gBUyv_frr@+CPGL1u+;g!|k)+&-Q=B53e-dYEfstFXO*$ zH7$+7GjUJqD$K8~VHb_TrysDn{V&GPTEvZy!CRwU@1b4$!wwmPul;*-`y1r*{sXJF ziNT9Et!*9TSS#4)WAOePtu#+!o-W6G0WtV2jKga&4r%X=!H=Tf@t&0L4*PrzehK_} z9Q^qZ=O8io4ZJ6H5Z0Z~>K@!C2G{F)kJ%1#buQvU#NeTj8>=BV?tpzh1|Nj^J`3}` z9nMo?@P(LPizuIQZWV(EVjRxGIP8dXy%^jQe5SrJ3g?wE_`NJWa|Iwmun;6^^ z_MeSmx0wsSofv!u>`}DaRK-2k82n}J1|1&2JUthFUorTa$-~l>==X==9~FZiXxBTL ziuwK-;(o{AmtlYY8S?op#I=sWOW(V20qr(z;6E0F7q)&TL0n>gGyMHx@OiM$Zw0%} ze8g9e!C&J1g7*2Y@H>scn>StBVMolb$8g^>27d$l{85mrci{eM3_cC#Ahgf7#(mfr zJOSrKc=yE5f`4xeZh-TzshD3l|FSW7dYOIxS=`f%!EiTjXxsr>4DNP-nZ-r*FCk@%_5h54*2`2i5OsxBmS$cNy}T zVY^z};|6yj(jV96BwORIQ{BbL=ZWol+E4d6)(u|srfb#uBwKUIP`}`sMUHjw-=5+} zq1*|JuT2Ff7hc%cpZW0b?)s-%q=FME*Gsux%JovNmvX(d*Gqf7wAV>{y|mXMAA|ZE z)*tyBw9BwvDA%C9hV4at4ESKk2h`JmKZg84J|28CZ4AF<+e4knlTc^!D%6=g40ZNWXAhoR@ZXg2Ki~v? zWYJ%S{k6w;r`WfEdt7&If@C1Jv}NNk-~?ke!ML?$<2K;r z;Z-x;hiCoGZ*kqhnQ_(ZqZ`}_Z`b!Th?vqP2Sk-~T(Nx&5|U1NZX%P5g}7 z);{q5^={X1I{Nuj>RSD6!vzD}rqhn}Gk>aS^|udvSj*ja_mTd`Ia}E$mVcGLwYH=G z=+Y+kwZ`M!eXDx=7yVa8e|yWscJ7OI1N^`KwcO}$yPW+>>hXPg&5zBaV{T3Fz;E}T zR2}anAL*VyZHOOn#r;NqJHOAC?)ugv{B7q?GWy$w`<{`0itk5leYg3b;TGw@Aw&GC z-p`p+AK1Z7zV&S1VoX<~zil)B`SiS_#`<2LG&TC$aUWi|;FxY>{k{iuGJ99MrG3lW z&i0qBJKqf5x^63 z+du7wn@5kSV&B17x@7VlM$-&BYMprpWAe;FYcgq;Zd=p7{7M_YecO*rubsECTOv)* zEAKG-{aV*{TH40%`N%?}X`9zFN4n&se9XxZu2=u*k4yXZ`xl{*YEv(EBnyc<>t||SGqap9%}Q-{m=F7%rD1m>EHV7 zNV|4U7vrb==stesSUX~u@utp{dj9^WJK0Sxx!N@U=|ik>$J)GdYra}Lsn>5af7*vV z?5c%_CVTZ+=Z;%(q8;(slgUY_&ldkX+%EX%&q>SCD_wHN0Gn5C`|hu%hkjYz|N2cY zd%>A=(yf}Waqn(Az)ov?eCik1_CNgI%{Ex`=7ODjz3qP4?^K&tuB@$@HC<)zqFGJd z5zj4kYpa}M^U9U+;AA|w+dFo)FJU|!aQme@3o! zE5|l;FD*IT-g0w2%B3p$TfvF`R&dfB@1ZqEJU*_WQXeDrF`AFm$7nuMA0zxR!rM%q z!cU`l3V)3BS7!cdp3+}N`pankBL7p7^J$KpcR6xC6*=!j&O4FwsmO3Aay}Iq?lg_a zd8cVahC7k-PUO7PG$Q9SX+(xQk@HUEywfxy=QC+UhC7k-PGq>#G$QA{rV$zLMb0~s z;ZD;;RjvHVSQGDkZ0}TX?)P7wXzIU&&emg-*{R^; z6`cEh@0!*9_gKf5eDYW-IH~%|r*6CU1N`~hHctiTem|w=V)rU^k2kB$P6a2Y95>C? zfL^!X<9nxqbH9)M_b4|Bdd@x1&rAg;pS+p41bW^4<6ESHbH9temkOpnzutZ5(7iu) zld#qty-{cTY1h}>)UT`ifqNJ`;ky^yQ%jb*pRPNad!_QdWS;*2*cn@Rk2QVQ(ar5Q z+il|a$C_9hI{ccGH}W&PzwOr6vR2F5;jtBNE!O?TU$@AVyJG5YzVh%DvWqfHmE3_OWSd+xn`I z+tkm`K51XS0_(*|la?DT>-C44_%XNC^Mj%PYq>{%)6MVPcb#j#&E011jYs;UN3C>s zLO=gxN*6y5Z9MaWNk+?>GrYZTiu&9D{a?#Eyd8`^G9LbynJ-mOj!@!ajO3fy4LRFf975Y6b5_fe ze$4FCWiOW5r^{ZZ$2pnHss2{ywf;75 z9hdQ=_MBA4kCXAEGC!5^<21dDXQydoJUbcBPUeo&G%~+3X=HvmnO{!km(w&d-@T@h z`R--DJDKlZ)981RKVIaI7y08w?q$;Gcaeu)p-MMGkQ5PL&mM z#*OCaw@26)f0>o+Ic7 ze+Af!e=LZcGB-o(`yS!Fyl^rvoaQ6* zBGr6kUT`fgx7*7+O>^d{%bBNXy$;)(=9tT8ro$}cxq+R>(9R=g4R-d;rvF9G~Sn}A)TTGw-jj)>kOZ9d#<9jqS ztC2?Ye8169(-HH1@@FIL!L=`JzxS`Rk~!7>X)m2!t;1)J?{6M>VXWPL^sUJyA2v@e z#dj@B^L*yqhURq4_l7T)(x*tDGX1ZkZcUdQeL&Lg`}3Ip6UaYh{#q8&BuJAojh2Nx z9r8?>C(3fDzhnJThZOB~Y%kK6mxVenK%E`ytYx8%4sCR7Biio3Pe*=gp5T>3UpV#! zc$k8Rjy%+|z;g$lJMvu4$TE4#85#Su+;*q=%UWOuKfb5k7Uf$jBLcOhfzBid$UsTKn1NCpESYTzs6{zHgGrv+DSkcDvrq+zb6%TFtZe z<;J$Xu8(^dw%i&IZ)=-DPJVOFq4u=}b!@vTkEU-vdPFAAN9H!M=dW&_9`|Y|t9gDq zv$fr^`s{S0A6wZzf2(f4KtJ|>`dHg|tLk>}>gLInu+yGZYg>B)##JNOZ#Dl#4>z&j z^*kW?3HDpf|MhAgnQePDGfm$=(f%;=V>6-p>|~$e1MCO4RkiIdJp1`dH1bs z{dxPFxt|@LDeH?KPneT2cmDDB0rsaYoca62KIY`Mr`rABdee->yzBIBFRNvJKlTUn z6Xwp?sz+za%A0p`#%HxG=z9kHal`$%mIYbuvA!6tFOcCLYmng@1ljIkk2SEz8abmh zSPMMX6jNGL{yeX=kkuaRo#A>18Sb$r8m@_u?H+5E;hLpoVJ+}j;|$k0Ef;dwWBpCI z{z8U(td)jqC1kb78l7;B*0QkXdaSt#*IX?Zvf4ujNT~xrhI_2t3D<7OY7dPQ3RvpHw<5ZS!hH0Y zk16LP=8nhQahyAtQyz0R<($>BFb5sxV9GhDWns>G$b^J40dv`7E~lK!nByKYB%ut^ zvNV6l3J+P4P*y;Oc*u~1G6b^4LtYumE6pGB%|pH!$~Vm)vdcqu8OkomG!L0(DAOS8 zJmjaL{M0-lZ$0F#p}f^RAxk}Esi7>*n|GS0_z6(nmgfoC?s2|mc)lk2c=b1seCRt7 zd=!6c$(8)UJWgX=H zD6GreLxpwO%eu_-)8KdH^5(VZ2^yzHC2?3=u%k$qeyjqKyR?Bn>`uxDiYu&-oUVPBa^gSv%1EbAQhs%%Tx zSF(-eX=I;Eo`ijL=C`uXC2zw%H2`I_PRnwA9{zQq}t;Tf5h1zWnsS)S!t9&GOxXKj{e zZLqgnoc9@?_i0(M;ai;98J^i`S+J#BoGn_OEyDJ0arS3<_6M81#rdP*`6F!g7H5Tq zXN6j>mIYh7!C9x}S?8tquP~FIu5F)M*Meu12IrxM=b^CGTbx}Qo?U9WT9)`{TG-wV zd?PJ=BXu7qGMQ(c<>hKwu(2ob`7!kQf$cqkFOa1#kc=hzPa617TKZ4QxXSpu!UrjZ z50aq|5^V1&e3LAFlVFqY0N)`)-yzuQ6ZjNa`V{4r3mdzGPn4lg6m0JfzEYOHQn1OV z@MSXeWrD3fg^!b^k5gW`u(3P%Xc_uw!S?RpyJhLS1)IEsZx#GkOFmZ)K3A4LS9#?w ze}A2O8troBK35h#SM==(e#P|p@$lKR^x1lNDQ933~r@xi{a?)S9KH)Ncyo?{74TW*bv!O6J{mP_~^^0fIVcq1}bXY4h-(@|`q>=U1%X-SQ^00Pi zzRUXVG>xq9PS$rX>pRc%gTBGD{-AI0d_U+L^bH8QjT3!?z63$H$)pkeCzD3>A1C?` zeH(&)MSq5%k2%rD=pz^OG5SuFr_VdbfiGnWUwT{SOJ9D^0zMlZ;J0t-w=Z`t=&PT= z&)?F|AHE+6eE2PW_~8@s1bq7~ef#yd@F7WXkHGRCf&LagCkFQpEbkq_7scRCf#sb7 z_^2efYhZcTKz|FLmIU`2EblewZ{Y)Da1X-r9t3=24DL8s-f@7>OoF=)mU^207Ctrx z_be>$S?F)!lS5y6%X=B{)iJnpVR`2QK0FETVp!hA(BH!6$Kc+F<-HI6EqsIw?ul65 z6M^rL!5t9GJ0QQ+XkdPTe);4UmiIo&e+wTZgL^2J_fYh=@L4jrw_@Qt+aleA@ik^$2%E9mz0u?bl9d>v!-hqUmMv}Jyh z0VnXKwD7C6Wqy?b(+@M0MgPoz=?`h)Cuz(4Bm++1OKIU(Y0La71I{fMzL5rglD5oG zGT;Qhloo!Kw#=_G;M{WI8)@JtY0La115V&eY2a6hHz`Ygl>yTaGn7UD%z)_+Y2YVm z%lsq*PT)&v;8$tO{3-*cA7&_v{+R*i-g}ZcNWaSR`IuKO{G1)`yjb3OfseGqU6_(S z1Hb4LcVsN@$ml(+Jox}WXNNm4mUmv@Bkgb(#_}!<{GuK1$XMQy(X!xc?Qo~Y@=lGG z1z%`~J1>@ZUf?6`a2Lk%E)4vl9q!0j-jUI=;A`!0r^fP5jg|#pXpcKDmUmv@Bkgb( z#_}!<{Gxf^%JPnkmIYsHhdVWvcWSgO_&Iyrd9l3n0v~CQyD*k_Vc-|-aYx4Tj*ONC zUu%auHI{d3v@G~Jd)#@kyz>GdX^*=wmUm&`7wvIJ#`2DgmIYsHk2^J%cWSgO(bF<| zwcOX^-I?;TL^rcqmh?rM)9)^)-+2!-tPAvk5B*Nx_|Wh4nGgL=U-~e{Qt5Xm{Z9Y( z(C_q(5B*Mm_|Wh4r7tf_`klV_&&(C_qz5B*MG z`Y^_v^n0c(=?f?QPJj5&@ARb)W6Vjvdo4@)J)@sXztfjK^t+RO_ga?B-%LNs8tr6# zrw?aX-|5>K${iro&LaKeW#CcSflAb z9CQQvB!@MczRF>J&*)$%w$vTzx{rpLD$I3zWvWHtbT`RRuwGP^+TGpYHuQMloFvza`c#6}q z^70?C{Yq19Vn4g?)&5S_-}I7mtJ#B3=xN{CzqL~xATR%Yo_@q6H}tbhe|XesSp!af z+sxeMSbO$>JDirKV@%tkV@%81ed2VpwqH+sV$&5)%gU?g*B5MKcb(bAwtTI>Qypn! zlP31e;l_3cpH)Z7tLI6>e>RhcA7*DiFxP2W>8`cyd+iRiQ_(IhE3clHKk%%%;odIL z`M-Bs*6l}pZ4PLApdI?|N~dM%+)8{5r)*~_Uen%CaTdp5BBs<*Q5px?Eu{y*$(hrG0#9fxtKW#zT^ zme;G>%{FOe=Yl_4R<};|ZMRqVu&Z`i0sp5z%gSr-yS@Kwo=3YjomkInSu-xIV%xm3 z8*GDHdM!(3i}F@wiz?HgKFp8Qwai?TbL(}$<=iAqNjrTsn9zS;-&(qVE@7C2FyK0b~ zJ$)YUU534Y%PaSg?`}-@xW1o#dxxK#*5T|YzkQ-Q0Ruar+4z& zmQ!EaG}PhPCo(N#P1FnjCsTYGKGKW_d$eP{mzZU2WFd2LHx zx$oZbfO}v@7yE9rO}w_{@4H>?HiJCh`IT+Gwk5CJJbn1_C*E+qAkRm(+{7yn^UB@% zqJMN~->$j+!!iA-S;(G+1q-y^2(D*+uxpU z0{J<1xb@1DymIfZc9Z)b)`j0kHucJrW^;$Q`OEgOJM?Pfl_z=SCX+sJ-=STbzq74Z zo($h=p_`BO<&%Fl^~#gHa`W~JUFvP**MUFpAHI#(zQ`+AZOh7AwJoboD|V-hPOG+V z<*l6YdF5?hxoUe?-m2|gd7D?Ro&_jx^(;Vnn^&%$O(<{mY(jaPS8m?EOwUTRPxH!M zQSD#uD(L6D1!wF$pHE)uxo-r+b@t65-C&+x}se5oOOZVVXRx0@< zIr*5Jd@}Z;Di3VpU+x3>d4Ci4HtyH%1l@brQPWc0%S%~lp55T*n``(l8y#C}i{>LZ z(R>7F>M!*%`dg`w(cemaGWM(6-)-Q#LY~)n`7C$+dG-8e(3v-Wut~dPfl+_@*U@B^Xie)`H;8Jmfe#TW(UHWRhabZg$h_k;YbgBTifUu@%7 zLgt_T*uAOdDQ!el$AMy*7Ix4Q`+t{PpN{qGExT}x0!wsJ~MVoIG4}ZY=tL`i4^#pu@8%GoUxOO zPHse>&pme&T{L4S7aiDWS)v0QEi3Q4=;Vy+74`+W=Xav}W$f*ua~iF)=$uCDocCRH zV5@Z&9oT4{^S+BtZe@Rwd!8t|R>uA>I-b?Gi;ict?Rnot=d{{((K)TQJ@32dz*gHX zI+9*Rz7m4|uXMaQ$sL(%c9@-Xka=$ux0 zC_1NA9_D=)9oQ-lMF+OZ!@TdJlV^ADQ zuhqWD`!0I8)xHot+-hIseV2WK)xMB@fz`gq`!4$wt9>E+6svua_g(f)nX`V`H)YQ2 z#Xg_$qY&G;5&L{1ws9l&`9$pM89xfKMH~IC*rJWt)iY@{e@!EH{zPo!M(q5F*uyja z7-IW1S_iTH8nK6G(r6ttjn+YI(MIg^M$>3LHI1AXBw`yka$b;#9XsP6BDPkmZ4q0m z6+5=kG};zTqiqq}uMxYt(KOl?O`~lQTeJ~7ztJ?>UQHwC4vE;tjhs6qVt>x~y@>tU zDo@0=X~q7WNuxZ`G|CgPwOX-<8%?7;(KN~vvHjw{T)BPTXd2~-rcs`VE!xO=fzdR| zH%%kw7m3)$jhtU3VmHqC1&Q6*DsRPZobe~uG|F4CEnDTS*s(MI?V3h;E4FT{ycN58 z#@}AkC~w8~Zj`rT=g;{6YZ~RPoI4oht(-e#?jvX#<-eSRByvuXxwD{tQ+fV-^G`q7 zUVX?c^QpheX2JRpcc0~*xn=3vsAXvzHUH;p?`JOnfBx~zPQK5m4)&kt4l^wtY~;0FJO>D6X}h#6^_4yI zpH1y4=<97(?2+-6)v}bITGrC~h#Py~UalkT%oiSVq#f|+W9g!|xAnMh7-Gw0_@;4J zl!wnZ=wzoJb3oGXtfv0Ork(5u|K2NoXF}5quQY$f5~kfBt4) zI(U{@iZcuKZPv2#`bF>J={-K&#|*J|y!Hj|a@w*uKbgMJyMcPIQ1ev&Yo5ygynBwy zbIo6QuKDNPyHuWQS;})QEAJkt@?6VOo@-g^)2{bY)u&y{%In|u>&AEZ5`BH<+B3ZR z@T=c`-aS6`)vwj~lC*8tQ_b{cWBfTg4oSa;%wIQdMCLA`mX&u;@UR{J=I+KEA73AD zCj5CfDwC()P0&2G{!brT&z&|rO^&HL&iAXoty_z^R`uyIy#E+t-FU5Y-u=h+JqEa$ z=M6Kj_aEV>H9Fpnt$mNVc8j5zJhjf6r}Ck1lZmc(SIF~I`ZIo7xT}@PQ}1qQp2~-< z-x=zzg`B_jsWJY{{|eNt_E-u=+)&%4bn>9H{JXxBXhrnp{^`S&zE z!)yN9cFj}y`Nj`Vy1L_sndc{*>_2&Lf$LQ39y7Ap37I^#?V6|Z^N=0hbAI|fbI7jU z85b_xLCfT+cVskA<>xs!-R9cOc`O}=vA5&uDX#Os_j0FUY%8zy4sYH)TsM2MyYs%i z((^G6tIt^Mwt~!mZ}XvE^H&}!-?XfMclgv*!#F(b)c*eSI%{0R-HbW7`3adkm4}+A z_RCKXRrBX{Kh^98{v3YSMt*8MDMPZeF;fm&sG_ zG-;mNFM079l;?WqT6wPbrt{)ODF3xA<-eAd7r#RJuVrce+P@kfL*r>^ObqP{&0qUh zV{~YI56x5iLi5!A&5JjpdFov@%~Sig&d>e*CI6UZn!hr|Y3v2P$EPtC^6uqbzwAu^ zFX$Kxm!6f03vuc0L;U`b`P1=TV?N}StLb-WJj8!>?>uwVxQ0&C>s>{yr`|Vw_QJ9L z&NI@4XOtT6qTY^U{C}b6-;M7Y%ObD-n!a_NG5${I^HODc;4C6HtJnYZKK}DT=3O-{(Z>ydef(+Tdq3K_wRK;a{1_Ksm2M>w&%tD z(BF1@`#e9k=fZ^b(cktOIo1!}p<^jVWgZ_ijq*ouqHPzPC?E2^3x6_qU4=iH`?Ifq zf1ck9Iy~E=yt?MlalXa7C^%7m=6x4F8|9(!*(eY5zRNf? z%0n54MtPX`UFKKjZnDg;%zbC&r}9v6qOooSCmL5LhtG-TFa4DpA1tR|68)|6T;@e4 z?wO_$xnY$5A~%fkU*h59epmjBJT%IGk;A#)wO>R&XYQfPy=f!sg_XO{R@RHm-FaCr zGI#c6{mT4S)-S8SmG#TY`jtr|>rp0+#sm<3BNK~2)@8;*ERQE9>$254$hvH0UCyM@ zdTJW2gXmFK^eC%oL@&yu5q;H)zM6^EAbJa9GnU6&)B20vVnuJUqPJL0uWivZ+7{6> zt>~Fn(`b7&jkZPfc`N#SZhJM2?02l}cQP?CwCxg4EW-!U53T5jR`kP6dgY0xSDuJI zZ$+QCnnrn|>6It4Z?dv)vYJMDqG^;TvahtVugv9}rqNg;vR}@`Fi{>#ytNDuMW45# z&u3z*DnFI~%0vCF?7uRxUX`EvTTQRLm3?j|o~@=)-ijT-DsROOkcqFWX_U7TE6FNv z#lDe=_p52dUSY-Fl1VRdlQOYu#Qwv0k|8cxCf<(Ne=@Om#MZ>X~hni+g`1+{#NY9R_w-^I_qz>&iY%i&l|DNTWznz zlrWl)oL?BtN6s&d*n1gkG{li(ywDH}F2e`0_gb;{=JHM3uIa@-Z^b@uwe9*_ZM*(f z&OwZvgJjyPzty(uZ{?iG$T<<;%2{t5FwIBK?2P6k=Wa&q^BLZXecmc>#Xg^j!>?(S zpK_j(;isIZWZnf(9xDI!?EvMcoLeylayYj#%0o@BJk;OHxgKK_m%p2k$2ZMK&MS@P zBj=TTlW4v1-?T5}{51DH2#s-vb+bG^p2n`kI%{C(G_-T(#ii6VSfdSWqzP@LSoh0g zS39pwr3I0ol6i0##%XBp*vkykEs!UShtj%QxV z6X@^;=UI;DS;~{Va`WPaLT_Avb2`U!I_-?1sUIUIdCpy!7e`}D`~@W*iU$H+`m2=lO3TfQD-PYMJT^FVArIdzN8hcy za<%Q)GkW-_IQpnye;3{|@YrK|_?bBRndHTX*S2Gy<>6E0=u@O^$G*(NFUZj^D6d>? zJN6(RzFUsITiSN)Sv-8M9DT0x%GI_*SM>00bM$Rfoo2m4 zHi59Naql12m)!4Kf05x<)}72dDIyQK*9-3~WZEe6G}B&@3Ax|3jUp>(s|oU%wurDU zxGdyLK{VppP4lJ zU3n<;)F`h+KIDE^9*W$ctuL&tw26iFCHK4XP-ItzS0XcWzsovkMTT2h2Q%*niTp9j zTaiDB@>b*zZ4p8KhJ*=m+y}R|j8zpudo|OcUXhqWa=-rj!ZrCzKb1+XQRQ6l>7dj?5Q*F63Skl=St;oAj)1o)AqdYVq3_x zS8NMBTMRZUp2-HAL*~2KI(YsYY-l{=4tA$Zd-J}F?Ipt#vAysOukij$?t6=}cjkFn z`P+`Ncjoz8uo3a>GT2En-(@e)bJAe5;+b;T%k#`M*sOBDi)|spL$NLJj62xScwQfD z4w>&_>&Wm{Y#sC^2sS_3^l5c>~(ErLytJ|V##mH96As7zmoJ&L|4!Ny2mmtY^ud>8vzrjNuv#<%3cJ480` z-B|cR+3?mge5JyB&8cAeK3VvxmHh7T=GS`PcZOe6`5Voe9)3_3{(%Yn0~7dGS@^=y zuWy3n2vsyNIt3L<8tzGIr(V2oV1I+uc2K| z^OtrxsV9Ad%iq+7ubPF=u!GMq-x#&w{ciZSS@`-o`1))sHpIKEi}AHVYV&EflJ@dl0K8#GcbeFa0g z^wqR@o5t~N8ch$sS&MgP9N(SM^umWszrgp{;%yqow`ugZ@Kv^Wcc#RD{cTRaB$7`m z`M8{XTuwfGk0!K>Z_NZgIL%Yq<)m&I{tKV^)=c0t-y95l&g3tA&h#&0O<2UK^Ndr6 zSQ8fS8F{>C~BL2BY{Byn|WAXlx$NNW&Wo;2J-6LK)-<`2|x5(q&B9GW025|*F z;|d}kg+XjR&)9m1C1Mbd-6I~m)3Oj-+ai9uNBnli+%7K*F)Iw>?|H`G`*p?h2JbX^ zywjA)6R|JM*(Yq~@vf8CJhlFaCgh}bU%appbZ%sZ_!;*;~e z36Hq+j3r)PXT&Noh`Z<+cMwa%KS@&Pe44B``d#wSD!4TE>AJl?U& zZynE#}Pvrw*?-<1G^o-ky_%jBvG(BT!B36$<9Da{D{7&1B`0WO9`#s|J zJ8e5+)ELCu^o+O3yQ;J!&_1tuYTGqWZ98JHB#3Y68Q&6dKMZ0#dd7IvJhknbr}7iA zK@!A)^o#?E*dPhs3-fp{ER!c!ugF3JMLO zx6?CjCt|0hc;C(AeYZ@Wh{2NJJvfi|;JoIk{eqbD3-BI|$9psm@uu42tr?HEW*FNx z!TU8H@7JW-7kFzR!Fv`S?^$H}0x{=Pyhr2l9t~sIrg&?{xcqhVar6mQLVyfwoZu;qDbUud4% z7l=9U@E(oFdo=#rY7N}W_c!q~YTHbn+83Iq_Ag@2JG@8Z@g5Ch(L22B=kcyzCQrn= zN%0P#$2)*t^VI&`;jtBNEoA%RuUqgE3*h&$mBciJ=VG-6gbyi4fuE+J#! zJG^n@@x~3~+q)CquJ7^o4P#R~#KHEAgRQ?sED?t|;hu5A^|y$};_&8<$D2Ehx9{*4 zkH=d)9`PtV-emN6laX&rdA!x=@m8Zp{0fhF+E{D0^jhbFTjCH$-7}85)*-K4#K-V> zv(n?uN{84Q9&cHCyk+STPs1bLxM#d^ZF^q1h(+TN_uez^y|x9hdK}{Ld&c3{wjjQc z!&^%pZ!PhS28TD8Jl0P%>&?ir6=+k$v3 z9`WiuCZ88(to7hI+gi>JTTyBd)t=TzBPRUb%=tcsIfG-2~+c;y5|HY31>z72mUPco;t@aJGk(7E1o3J--WTwEUqE@1S1w|q zc)aE6@s=y!IP-Yp)#Hsm5bO@4(}p)zKf*1MJz0bca;46<6ET4 zTfA+N;tev)$0>ZfB*oig9&eNJ?Fol>nmpfWQr;qlmBYJEp6@y-Z}ZAUOeBwYqdeb@ zQr;qFl*2nyp6^U4Z}ZAU%pZ?;tUTYbQr;rAlE=GOp6^~MZ}ZAUEE@r8m+ z$jx8+E0O+UtgtXn5*a7C`D>kJela#znAeOw6=WS_90fUMM2=Usk%-)2EWRMG7^5$&1-b229*Uf2EWWV5F!o(ob9399 z;o+Y;31jGmb)2#0f=*)c+N(SieS7Yk3R#?!ZtmsjU?H2Sm z#w827pUvZ&$oWL%JYysVInOsJgPhOhoA!mQ%Z!m2)@8mU8P?@ozG+{G{=?XYLH{wL z|Cl_!XBFq56|?4=;4eh7xZu|dbmX_ zXp1-OJl?QljA)Cu?L6MLW1MG;INqLdyb)8{;@xMDcc1w-vBlfX9&b1E&0>ppqCMV; zW~^_ESl^zpzV)|=v1}2?+cS>0{uVKrE#7YSc)OV~s4d=!_IM|nanvo|=JR-)kFmim z-t6;uv(F(Wwnf}^&$#Pa=e%+eQ`#bCxM$38tpj35Tf_tRus>JPIv{4X#T(G2?|9$Y z;=O5)_oh?C61Rx)?ioKp>ws9_7V+CXD*4On8eo4L#m8bcm^L z5hvd>PQJE1uUyGLk#;dA_WIgoa@xh%<`yyM+1?4-7Qu;>l?qO@EX1NWi0$tg+h5y) znDZ8K`%C$2Tk^`qI|T-BM0>$#?w>-j4KmJJKO0zrni(p6?nc5A({EzFAI9WgAAd{^fB2Sx!J7`H z@u@r!oJd)z;6%&9TMq_rdV0L+$u}Pj-um=->(k*)2ZMJgO8IMFbN+!$iwc zK5JRR=S29-w=>qqXOqKczVTu3mTPGq;q4EDH(ov7cufT-QdTNB(Xzz;!?%Wm{l|(; z$%_4lv5|v)&x-wrv8jVSD)U>habR_+Vt*53D`!i!=2YbF1 z`!nCR4t7Y!s4s7e*ddL!MeLA9?2wr>+7?ZtZ4tY%6}vHGqzC(aroEa*+amUP#sd%M z9l7n*G;)5y*yG_G$IAJI6?-q=CJ44!BQ|8d3lMDYeET5S^NrYhGij73nnrmd_IV@r zd828RCz?iiBIh7R&OsQ{KAaO}_@-%;Cvr|?<(w#&Z<bI*Z{=Li z$hn@;G|F2|qr8>#N+ai$x%}5Oa(>FU0K)mHmGe`3UGsJpDxKYD{-6^5>aLn4`c(6F z@S~Yv?`%H5M62F?rMdgzUiPmjf5UWvR^NMm!oMrh6GwI{rTZ)TM;{a2KHWxBtjQk>pRkRTRy~Aq>b;cY52V&?KkpFo2(peE7F<=*ERfJkxsjEnBD#JQMMvox2BQd z_loq>yN1|pzB|`eq;Fr@-0*uv`tN0(OWy5I}Ez<;JMN*6&sFnv+V@x=5+ zX(Nntwznv~2lI~Ui_&B3zFW$_C_QuJvQqtv(#w`TT}oe+QjU>7MJeY2(-);nG2Y3a zqLky5>5I~R=Wkk)4}or5<;l`~ulRTPK5p42CHWKR<=_|PPQ|~&_eEm=fi79Ia=m|t@AqDLWNAIB-}jgj{j0w}zw5Bldi0;py-M`2{%$$HOKCm2 zrulIt`d5FCS=PD4&n|WQmgryo?bdWE_18vKPAt*C`ny_{j-~OiU`_uL{j0wZt(%m_ z+vsH{m*`*p&2^gNxhUnj%lTE5avf#*qLlg)(-)=W3)2^+>?gLjDCM|e`l9sQWlc)s zvLdZsW#`iOinR6o-Ad!KB0Zw+j-~Gv=@)nHSsIrW>BQ#sOW!Nf8CNzhjmwJk&CYd8 z-z(DoBU_ZlWkuSc?>43H73mIrTbIUVMLKX~?b7#(^o`E@mBwX7df$~>l)hJ_oUi16 zQOfzp@lcd5EeK6Wd#qa@;U|QF=JW71I}`%P~$^|Du%r&Gbbn z_cKgilyV(p`l6KU8rxfxa-CxOqLg}i$04n4QA)jJk5O$)`biN=J$>tc8e5c7Pygkz z18q@CJ^kYwy4a$Wde72Zk0|LsMJV<3`S%`ei_({{{?9}DqVzcE7pzB7`Z4quwznu9 z0sV;lEJ}Za{>A<(N~yrj@Owo{I~~VcMT+t}@Owo{J(b^!QtEjuw2jz&!Uw3bJnLQ<^G)QElRmRCqIi)?$6m@MJe}b91le)_vajMMJe|?JdY|$ zU&a1~>5I}&u^(ajqI4siYcYLMS|9ow(-)-!p^q_rQTi|FS4>}&eg^%V>5I~>p>H#N zQ926xGt(EPi<{3cr7udihJNu!eTq?>vzF2qr}H~7|EHQ4qtqjrz9^+$$n-@i^*p98 zN_j5J^hGJxMW!!Gxy~_tQOb3T>5Ee8JxpJe&V)Y0^hIeK=qF5HlrF^iHq#fSl;cca zl+rH7^hGJkIXSIYf7|E@^suduX9?ULVu($an&-z!quDHiNqr(~~C%Kbe5u1IMInbCIp zl08Hz_w)R_BBh;W;(@7;UpXc8dDeXA_Jfca-o}-ledH!9I(oQs_*B&K%ky7sG z`FBN1JJeu&r~Q-kul}Z;>pYZ8eSq|@{^q$Q+eP~)>0kZLb4&7#_D|Bk`kUvL>=)WU zN&o6^o?9+Mztav%`d5GR+>+yp_D|Bk`kVWFrVEt&^!5E6zL)F>s7IisefoO-4&P~~ zU^@flK7DAB?Utizf;XCa_9EX8& zzrMb|!*}j8$-h9kPhH>N;d^PH3H}9I+6S-a@9>@bO!6;K?z7kTclgeAm;4Kq`}p&JHh(?4&S*>lYfD79bVtx;XC)4tpJBIUk_{HsX0zvcIelnKVQm!XVUzC#XOkb3e?@V8ma{e%VQTi*^Yw7_- zDf^Y_i&FM0(-)=eSI)bll>N%|MJeS4(-)=mx24=GN-1YKe~VJCV@zL^QZ92o7Nx8= z(-);&Cz-w|rQGNIDoV*0rY}m#7p5;txo$IkQObSb1k9JBlzteTUqvbT&h$ko`Ofr3 zDfJGfFG_d9ddl%%l-_{;W%{C&{mS%3Df^Y_i&FM0(-)=GvzWdpr5%U!y(p!g%K2NA za-YETMJe@K&c~vZ^=A5_l=~2-FG{I5bAA=2T>q(x+j+V)~-AHSF!B^|v7X5cOyJqI44MV@zL^-V1v}Y5o?Z)xaO7 zFG^>FKTKbgwuHTo>5I}2aUQ|>UX(Tl-#NdE(*J<}rTJTsUIhL#eNkE;_C}^JN@1T& zIR1;$i_ou3UzDy}b5|*SQ92F%%lwPdJ0z*YbAA=2w7)ZbQA)lreNoy6c3!40N@;)Rd?`xF zccw2&dC!t|@S>D_XZ}Sg?=>-fQF<=U5jg&fQl4ipeNoDOW%{CY6#AF@{i2lp%KVE` zo+~hYQOffN`acwtB@e+>!M!N@@3D{fko8gZ?>1X(!Z&?JY{@qCRYIQA+zb+gp^ zoBSzC_dxy0pQ7}3)Svt*N@Byg=ly(iKFG_h1!TJ=Xv~w~44WZ-@(-)z% z>#;sXX${z0Sf8Sl`LchDQs&Eg7p3&uVEv2Iv9SNJ{zWP4!SP&_E<%0S-lB9I>cjRH zrSxlIdyCR9VZUN~i&FBD^Svk~AIYDhl=UWmic;2_{3%Llmn46RQrhXrpQ4m@4W=(j zY4>4$ic;D^nE!@Q@`ve*P}-GPpQ4m@7S^XIWxnj+qLlfv-bE?xT&#alO1ls1UzD;Q z9M455>%sOGrK|_rTa?mH$MzPbv|F*gMJeme`CgQ=-sDeF%6gMOMJek|{uHIOOOiiD zDeZLRPf<#{2GbX%wEM6=Md`(`hcN%5l=iXGd?`q2S7LpN(r;jIVSS2H+7H>kMJerc ztbb8Tdsk`v7o@cNu>M6U?W!ElMJer`Y;RFYJ0sg$l+sSe_79?pq=X+5~ zJ2LrGlukzd$)BRM73xp^6s6n4K1u!*r5nRuNB$J0^p9lvqLguwSf8TwaM(kbe^E-i z82M9_@*V=~QqF^t_xe!!eL`LzO5f}G^NgYN^Xm1X^z-V^KZerJtJjCp z&#U(nO21FY>qF`9FD^tpI_D1Fc3^`Z2=k6$mL^tpR|D1Gi;A4;FQ*N4*gL|z|Czc0h+=Y z^Xk`cDE+*8eJK6BdOxA`J(t&q(%%#3*Lx`axe>p9L+S5{@cK~tb1Z&6hSK}?`cV3N zF1$XJz6bH^E0jJLuMefq#p^@q@AdHdQ2O4-ua{8z+`T@OK6kGVrO(~#L+S55@%m8u zUd+G$q4e|W^`Z3h>ep{5{k(d8DE+*8KcV#Z$asAy{k^(=y@%4DXY}hgl>T03uMg$B zKj$m`dJLua?e(Gb=QF)Nl>U6EUtgiT_9q{&52Zi<>Gh%X`$)V#l>T00zg|M=bNBjC z`rN%flsISe!V`FeqR0h4W*w~uMcIHv zd!PM!4`rG^=aBvS4W&P??De7C_UGKPUyq?&@W;Q`hjPT9^U+=(%A$Yz5Wl`cneIXef*N4*Q?)9PcxqE#m{k@Q0A4qF>! zCGRJczGv}%Lg}CD?`sdGf3Ei(O5byN|Dp7~kM|!+@5BG@Mku`xKi*J!AAY={^gW#) zZzz3l<;NRJ@7v$sA4>1r=Mzfr+vgKX@7w1SO5aQRd_w7aI-gG{eXrs5q4d3v_Y+Fr zgZStFFZB6%eF%N8eZ2or zdLRCM4yE_u#~VuT!;d$VzNho!4W;j`{CGp@ef#wuO7Gj}6H4#f=Mzfr+vgKX-%I*@ zLg{-tpHC?L|NUMcO8>dS>qF`9Rq_5q=|AWA=ZDhYE9ajdO7Gj}A4>l@-1`Zo??Jqu zP)7YTUhgNAew_Y&3#I>D>ivh(e{T2wL+L-qdjFxE_a`6kKa@TfzaB&BKezkwhSK*M ze!QXdJ&qr5D1DB8yrJ~Hm>+K_eUAR`TPS^x>GKKYpMAIgpHC=#ukD{7O5cNMFs02#<9U|SPct6P$OhO zO|%(B0IQM2(E-J^aa>RXE0hQa_stJ7ArGp$Ce(2le& z?Ld3dIQq4(&q|(Aabs8BAN!L3AW-L7UOxv^^b0$J2JS4IN9n z(lK-@?Ls@z$+S0}L}$}pv&~@}MeLxP<=j0xV zNKeuo^feE3X8`+(GN5Z`$c21BCjFKM&lQiHPxlC4)?BFIz!7h;dBptg(o|0Vb4tY(=ut(%0X~JHR?_>~rM=p|7 z>|c_SttTtUb&{SXV`*7tmXsxDS=m~Wg>5Bk$X$|~rC~W)e)bnj%?h%NtN<&@GO$dn zINMB$vE5`7c}&W(9IP~}#ImvMtTM~XDzfS<56j1DvcjwetIGA$2u#&7btH4^a_N*K$&pNWItOM)Hs<3LTJFCsQvEHl}tHb)T{iF{&PWF-a zWH4*Q2C`wS0c*&HvleV98^fBh=4>o$%SN+_tPN|&CbQ0L5}VFCu`X;T>&a%YxvU54 z#pbi4WF9+9j*zbek^$^D{)YRp{_H#chkeB#@DTPl`-n%e_xL3q!A7#zcszTBALDUs z0(*+5vM2a1p2DWF`*=3Hhp*#VY!17LPm>!s88}5&ljo9$(5*gCeKH6jODOVWhwAxGH;cATALo7fh1nr&le*?G2u?P3?% z9(I{sW&79xcAd2#H(7hqiX0+$*&%kHJ!D7NG4`09U{Bd|c8Z;0FWEWvn!RNg*d_L! zU11;DXLgOZ@oF5G$LDKtFR~sd0DVYJvY6xs^+`fr zi!>$4c>~g#Wa7<8M^c2hA>BzG-ih=jy?75&n~3Z9s!;%RtNo{XpE z^GSL>oh&5HNoJmsXXQEgUpzI>$U=V($w!l^q%*0@i}Cur5ih|@^2WRjZ^~Qna=bim z$t&^JydAH?tMc}|2Jgtb@LIe!@5;xL?tCa2PkNHxydLk%2k-{GAs@(_@WK2a-i$Zr zLwPGcoR8vdcw0W2ci?0B1m1~v<`a20KABJBJ$O$(oew87_&_p}^e4-4AO0U+fcx?O zd?6mhm*81=2>+YU#>4nLJOz*7Bl%Q3hEK=i@Hjr6kH?evL_7jd;ZylYJd=;cL+~s< zoBxdmlYek#@Ha_^s*o%o6eF1~|b;f2U0o|hCLG0<&( zfZyZ~`F_5SKj6ptJ^q{@OXR>=aSRb%WWni4CY%wZBLnbXBrWKL6N-MgD^4zY;PyC^=!9G1BBBj$gzJjt zxDM_u8sh#qkw{76i=-qu>4{T_1R|M8BjSp9BCSYFQj7Q`A?b`Wi=-l>$RQGoBqFEy zOJo=ML`soLW<5v=>!GThT>S5|u?) z5s`EjhB(py_Y$>54>3^G6g9*E(LnSQ|A=~`z8ET+i6LT?Xd;@5(V~qQDJF0z}E}~$DKMRM`;JM-_o+G~CA2>N)EM|&DVwspBW{DNz6aH7c!=G^i zyjIK?Ys3aIPb?6d#1gStY!gexas&sDRzn7;q7x76fLBGUOG#~vIKgB$R z2}!BtWMt)3GzLYGk>ofOMUF?qP&64sjzCxONR$Lz!&lKcoE)4- zG37OM62+Gn&>{4fJcah4Jn{(IhN{Z_XanjjccSYkmb`%D$P@SyzK9aY*fNn!D&xv{ zGMPMuQ^-U33_gug%Y-tm%peoXBr>B+E;Gw)GNnu+*N0vbYWO-B!4U&UparBQYi3*@$a=0vrM#;h`7aAkS%G_w8%!9I^ zNpiBxiq_z4C_Y$+hoBWWG3bY;%Au$Snk@&SPH4I8joPB^vMXwi&dGMDA$lWQpnuR5 z`7fR(7vYt70GcJI%Q1#&T7Bl`G|ca*tdq_sPR@y<8^`$<6YJJSDfvE%Jn%fltZ_cqVR- z&dZ(hqP!}1%U$w{+%K=m+w!11AaBW|@{W8ckIQ57fjlE0$rtjhJT0HeN%*-Og(u^d z=&igU-^)+(lDsTG%j@#1{3&nB8}f&|D}PI@?#p`;smGEirJl+sQm9u_s)*{fd?}q8 zgCnS+cr0#=D0(La`UOA8kMcMCA`$umzsc|NEBqzD!}ky>px!~MKEjufE2dsRqh7%$ z&?>DS!N}?{ybq(ONa`*ej_<&%U>MGT2jTP}8;+?m<2*RN%83i()G9wNiSw$WxIC_^ zO5v)wv#NkI;^=BHj;i|LfjB#kt756xDyfR0qN&8HFHWes;odkePN@>86e^>Nr{bve zDw#^7vZ*90k;aba5>yjRZ;C#PgO}(P~BB6)m8OZHB>d#S2a+*)!(X~s-p(0W@?}sshX%p zYPf2nhN|(Zm1?2Js3v%{s*fAvD)=Vsq^`rOu!Cx+F2f$`B0LYfsV?d)?59q{ldzBK zrH;cP>L@%62dM$-ARM9g!@Y2r`bX`C-)~m#Ljf#sC;@4=4TCO&!J!+|1taho@ zYKJQzz6qwN@Qd@$eB99mmEW(HXT}ol;lSR<&7OQhU_}bxZA5JJk(! zSY1;O)Iqgh-BTyk9raWlS4Y)j6$3v~5pXp89lcWL)eH4WomHpR2X$4wQ$N&Yby0m& zx78N~^-XnM{Z&34IWi(!0=Mv>27uS#^0`NoUtNbrqdk zSJO3gUY%dp(gk%LT~8O*MRfyRTsP89bV*%WH`BXO3%v#HMgOAKx}0vSJLn3!qVA|G z>(08HuBxl+?z*P#sr%^Kx~}f0>+1n}kZ!0O>mjvXLT^Q*^g6U1twD8Q z8(j}pgY9&CT^)APHDLwVMR(N|VGmsymV&)>Z(SPp)n#E3*k2FS#o%CF9Oi?6>!G>; z9Igw(9B`x_t#iTkDEFT~O&d`TGzcXG`Op}h9Th>7bY4^%&DVucMYLX*K-JN4T@KYn zPjyw46OGe@(FENG{f+XYDSE7)rf2B!dZM1C`=U9z8|sgWqJ?_0UZj`ksd~Czu4n3h z^-4Wk&(*7RceGY_L_JX{v{BE~oAp+`KrhzY^-{f4@7Bxo3cXkVr}yiFdbM7o59zjIy-luQt1NyGMuMg=X`k`)x z9_yy44XTEo>0|n(eyvaFQ~Iqwqu=X~`kcO?KkG~StNyO9=xh3?zM+3>Xm05{8k>8X z8g3rwM_QUDNSpeo8LERegC}|`SPh=*7kVvtrPqVy;EjH#{{;=%*+JifiTKU1ohBlz(50Z4`x8wz*FeVL-+&liMUWxl93*)Fd+nO*)g` z6gO$iU#6Hzh>DmvC_efD%a{zNtf^=+n#`uM$!;o{8YY{`VQQLurn;$T@|e7)zA0+z znx>|(DP)?M*r<_-hGHU!TA322wP|lkn$o7DDQ`NMuBM!+V7i-Xrkm+ws+g*#uc>2t zn?a_Qsci=u(9GNi*FkgB(%b}X%?)r7v@z|>WzgAN z0%t%c)5V+xz07HF4D>KP&2ccm90dnJKhxhF1Vhb!up9hshL~NzqMaZfFi68&FczS2 zj1llRm}C(A3g(#~;5}GpK7p6un0W&pgD2)0kZ`QI0Y{rF@EXK$vKeJ2ni*z<8EIyk zOK`e52QR>%V7{4P=9(pDoEdMHnrUXS`Oi!-Q_V_q2L5YKz*F!uSa0T-wPuT%WoDbL zW|7%!c9{icq1kO#n4RW;S!R}-gXS3AZw|pD@GUrQ)|jK_j9Fz?o3mzd}*em9$f%cC1ZJ51fZX0fQL29?b9q=I-2_Ko^a0q;09-6=5 z3o{t@gU`%!(;vPwePIvy#=JE>;TO{#c7mVGXVV$}G96(X_{01(Z6UF(VKay5SFxk;857y4uXxKwi}?dtKnMM z0!FgNMzFE0vckr&t6(&{9R3U2!FV>ZjboG9h}PL8Hl|HvQ`_h^s!e5=!4!5OTnxLw z^ftatYqQ(9Hnz=bliJKSuT5+d+T8Xpo6{DyDQ$9F&@O=a?QA#?_JYN223ypYx9Mye zTh?Z?rEOK4#b&aVZ5~_E*0#B94qMX}vej)vTfpYC_3bQJ*G`2q-~iavmavU&TU*Q) zv8`=6+tPNnWo#+i(N?kTZBJXtR4}v+t)U*z3tz&o~>gC*(q?K9S}I>zxUip%4P(MS zwwH|#huEkvG8}9N*obf(bTBd)3nRe#FbaqUhubJH4jf}+!9;L|O#oBC|7*$K85oMD}SQuu74Zs825Yzz=?R{GdJhQbyHSpX%wN=0yTNP9QZ|!Sa z4t%oZK`HRrezYaP4_gux0YB|`TL>Um7~})kL6-;Qfq8)jdEs_oVH~g*NVfwV1`*so za1z9EN5BP;)|~>^Kv8!d+yRZ_kXCQSPD!2z&C!BWC9CO4)aSTRs5E6I@#B$EX zaEY98$|Z17T|AeA}+NnhihD zuCB}Da=F^>1E}dbM?mpsVF-xc=@H=7vL5c0~)$fU^uAn>bYT{x%&qU22EWPHwd(K z13+KU+O=|hKxfwrbO#+>2iFbsbX`D4(A{-&9l!wB4zvdSTp!m8JOM2L0gpg_umM<5 z6by0&Kxr_{6$2H)Bv%Gh2TNQfP#5fSH9%u)aeS*R6L8+(x&>Epm(9R<{UjceB6}P!sHS%iLafz^!oqx`S?&JM50R zHEyjt?l!oS?u^^yHoLQKn>+6=xgBn&yX@wGt8NOI2kL{HZjZa|?zw$#zq{`axrgqF zJK~PIr|yJ%?q0c5?zDUD&bhblgS+4^x{vOP`|Q5CYwo)H?xumCZXB2anu6xwmTL*x zfIIH4YYQH@_Mj7Z2c<0``{@{}v2!? #include <../raytracer/structures.hsh> #include <../raytracer/common.hsh> @@ -63,8 +65,8 @@ void main() { ivec2 offset = offsets[offsetIdx]; vec2 recontructTexCoord = (2.0 * vec2(pixel) + offset + vec2(0.5)) / (2.0 * vec2(resolution)); - vec3 worldPos = vec3(globalData.ivMatrix * vec4(ConvertDepthToViewSpace(depth, recontructTexCoord), 1.0)); - vec3 worldNorm = normalize(vec3(globalData.ivMatrix * vec4(DecodeNormal(textureLod(normalTexture, texCoord, 0).rg), 0.0))); + vec3 worldPos = vec3(globalData[0].ivMatrix * vec4(ConvertDepthToViewSpace(depth, recontructTexCoord), 1.0)); + vec3 worldNorm = normalize(vec3(globalData[0].ivMatrix * vec4(DecodeNormal(textureLod(normalTexture, texCoord, 0).rg), 0.0))); float ao = 0.0; @@ -86,7 +88,6 @@ void main() { BRDFSample brdfSample = SampleDiffuseBRDF(surface, blueNoiseVec); ray.direction = brdfSample.L; - ray.inverseDirection = 1.0 / ray.direction; ray.origin = worldPos + ray.direction * EPSILON + worldNorm * EPSILON; ray.hitID = -1; diff --git a/data/shader/ao/ssao.csh b/data/shader/ao/ssao.csh index ca0fdb581..890d52d2f 100644 --- a/data/shader/ao/ssao.csh +++ b/data/shader/ao/ssao.csh @@ -61,7 +61,7 @@ void main() { // project sample position (to sample texture) (to get position on screen/texture) vec4 offset = vec4(ssaoSample, 1.0); - offset = globalData.pMatrix * offset; // from view to clip-space + offset = globalData[0].pMatrix * offset; // from view to clip-space offset.xyz /= offset.w; // perspective divide offset.xyz = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0 diff --git a/data/shader/ao/temporal.csh b/data/shader/ao/temporal.csh index 9f91d64e3..b2075e244 100644 --- a/data/shader/ao/temporal.csh +++ b/data/shader/ao/temporal.csh @@ -166,7 +166,6 @@ bool SampleHistory(ivec2 pixel, vec2 historyPixel, out float history, out float float weights[4] = { (1 - x) * (1 - y), x * (1 - y), (1 - x) * y, x * y }; - uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; vec3 normal = DecodeNormal(texelFetch(normalTexture, pixel, 0).rg); float depth = texelFetch(depthTexture, pixel, 0).r; @@ -178,9 +177,6 @@ bool SampleHistory(ivec2 pixel, vec2 historyPixel, out float history, out float ivec2 offsetPixel = ivec2(historyPixel) + pixelOffsets[i]; float confidence = 1.0; - uint historyMaterialIdx = texelFetch(historyMaterialIdxTexture, offsetPixel, 0).r; - confidence *= historyMaterialIdx != materialIdx ? 0.0 : 1.0; - vec3 historyNormal = DecodeNormal(texelFetch(historyNormalTexture, offsetPixel, 0).rg); confidence *= pow(abs(dot(historyNormal, normal)), 2.0); @@ -188,7 +184,7 @@ bool SampleHistory(ivec2 pixel, vec2 historyPixel, out float history, out float float historyLinearDepth = ConvertDepthToViewSpaceDepth(historyDepth); confidence *= min(1.0 , exp(-abs(linearDepth - historyLinearDepth))); - if (confidence > 0.1) { + if (confidence > 0.2) { totalWeight += weights[i]; history += texelFetch(historyTexture, offsetPixel, 0).r * weights[i]; historyLength += texelFetch(historyLengthTexture, offsetPixel, 0).r * weights[i]; diff --git a/data/shader/atmosphere.csh b/data/shader/atmosphere.csh index dc2c4d93d..acba66193 100644 --- a/data/shader/atmosphere.csh +++ b/data/shader/atmosphere.csh @@ -102,14 +102,14 @@ void main() { #ifndef ENVIRONMENT_PROBE // Calculate velocity - vec3 ndcCurrent = (globalData.pMatrix * vec4(viewPos, 1.0)).xyw; - vec3 ndcLast = (globalData.pvMatrixLast * vec4(worldPos, 1.0)).xyw; + vec3 ndcCurrent = (globalData[0].pMatrix * vec4(viewPos, 1.0)).xyw; + vec3 ndcLast = (globalData[0].pvMatrixLast * vec4(worldPos, 1.0)).xyw; vec2 ndcL = ndcLast.xy / ndcLast.z; vec2 ndcC = ndcCurrent.xy / ndcCurrent.z; - ndcL -= globalData.jitterLast; - ndcC -= globalData.jitterCurrent; + ndcL -= globalData[0].jitterLast; + ndcC -= globalData[0].jitterCurrent; vec2 velocity = (ndcL - ndcC) * 0.5; diff --git a/data/shader/brdf/importanceSample.hsh b/data/shader/brdf/importanceSample.hsh index 729c5345c..128539e55 100644 --- a/data/shader/brdf/importanceSample.hsh +++ b/data/shader/brdf/importanceSample.hsh @@ -80,7 +80,7 @@ void ImportanceSampleGGXVNDF(vec2 Xi, vec3 N, vec3 V, float roughness, } void ImportanceSampleCosDir(vec3 N, vec2 rand, out vec3 L, -out float NdotL, out float pdf) { + out float NdotL, out float pdf) { float theta = sqrt(rand.x); float phi = 2.0 * PI * rand.y; diff --git a/data/shader/brdf/preintegrateDFG.csh b/data/shader/brdf/preintegrateDFG.csh index 190572f7e..d76c2c2d6 100644 --- a/data/shader/brdf/preintegrateDFG.csh +++ b/data/shader/brdf/preintegrateDFG.csh @@ -5,7 +5,7 @@ layout (local_size_x = 8, local_size_y = 8) in; -layout (set = 0, binding = 0, rgba16f) writeonly uniform image2D textureOut; +layout (set = 3, binding = 0, rgba16f) writeonly uniform image2D textureOut; const uint sampleCount = 4096u; diff --git a/data/shader/clouds/clouds.hsh b/data/shader/clouds/clouds.hsh index 2b2046eaf..fcf1fa9c6 100644 --- a/data/shader/clouds/clouds.hsh +++ b/data/shader/clouds/clouds.hsh @@ -54,9 +54,9 @@ const float multiscatterEccentricityAttenuation = 0.5; const float epsilon = 0.001; const float logEpsilon = -log(epsilon); -const vec3 windDirection = vec3(0.4, -0.4, 0.4); -const vec3 windDirectionDetail = normalize(vec3(0.1, -0.0, 0.1)); -const vec2 windDirectionCoverage = vec2(0.4, 0.4); +const vec3 windDirection = vec3(-0.4, -.4, -0.4) * 2.0; +const vec3 windDirectionDetail = normalize(vec3(0.1, -0.8, -0.1)); +const vec2 windDirectionCoverage = vec2(0.2, 0.2) * 1.0; const float windSpeed = 0.001; vec3 cloudPlanetCenter = cloudUniforms.planetCenter.xyz; diff --git a/data/shader/clouds/integrate.csh b/data/shader/clouds/integrate.csh index 5db8d23e0..a496b01f9 100644 --- a/data/shader/clouds/integrate.csh +++ b/data/shader/clouds/integrate.csh @@ -48,8 +48,8 @@ void main() { vec4 ComputeVolumetricClouds(vec3 fragPos, float depth) { - vec3 rayDirection = normalize(vec3(globalData.ivMatrix * vec4(fragPos, 0.0))); - vec3 rayOrigin = globalData.cameraLocation.xyz; + vec3 rayDirection = normalize(vec3(globalData[0].ivMatrix * vec4(fragPos, 0.0))); + vec3 rayOrigin = globalData[0].cameraLocation.xyz; float inDist, outDist; CalculateRayLength(rayOrigin, rayDirection, inDist, outDist); diff --git a/data/shader/common/convert.hsh b/data/shader/common/convert.hsh index c8ce9c652..c9944c1ed 100644 --- a/data/shader/common/convert.hsh +++ b/data/shader/common/convert.hsh @@ -2,14 +2,14 @@ vec3 ConvertDepthToViewSpace(float depth, vec2 texcoords) { - vec4 viewSpace = globalData.ipMatrix * vec4(texcoords * 2.0 - 1.0, depth, 1.0); + vec4 viewSpace = globalData[0].ipMatrix * vec4(texcoords * 2.0 - 1.0, depth, 1.0); return viewSpace.xyz / viewSpace.w; } float ConvertDepthToViewSpaceDepth(float depth) { - vec2 viewSpace = (globalData.ipMatrix * vec4(0.0, 0.0, depth, 1.0)).zw; + vec2 viewSpace = (globalData[0].ipMatrix * vec4(0.0, 0.0, depth, 1.0)).zw; return viewSpace.x / viewSpace.y; } diff --git a/data/shader/common/ign.hsh b/data/shader/common/ign.hsh index 1cd603c63..c56a93d6f 100644 --- a/data/shader/common/ign.hsh +++ b/data/shader/common/ign.hsh @@ -2,7 +2,7 @@ // https://blog.demofox.org/2022/01/01/interleaved-gradient-noise-a-different-kind-of-low-discrepancy-sequence/ float GetInterleavedGradientNoise(vec2 screenPos) { - uint frame = globalData.frameCount % 64u; + uint frame = globalData[0].frameCount % 64u; float x = float(screenPos.x) + 5.588238 * float(frame); float y = float(screenPos.y) + 5.588238 * float(frame); diff --git a/data/shader/common/material.hsh b/data/shader/common/material.hsh index 941bdfac9..62f258cf8 100644 --- a/data/shader/common/material.hsh +++ b/data/shader/common/material.hsh @@ -10,6 +10,7 @@ #define FEATURE_VERTEX_COLORS (1 << 8) struct Material { + uint ID; vec3 baseColor; vec3 emissiveColor; @@ -42,7 +43,7 @@ struct PackedMaterial { vec4 data1; }; -layout (std430, set = 0, binding = 2) buffer PackedMaterials { +layout (std430, set = 1, binding = 14) buffer PackedMaterials { PackedMaterial packedMaterials[]; }; @@ -52,6 +53,8 @@ Material UnpackMaterial(uint idx) { Material material; + material.ID = idx; + vec2 emissiveIntensityTiling = unpackHalf2x16(floatBitsToUint(compressed.data0.w)); material.baseColor = vec3(unpackColorVector(floatBitsToInt(compressed.data0.x))); diff --git a/data/shader/common/utility.hsh b/data/shader/common/utility.hsh index 06cac1f7f..0d629b20a 100644 --- a/data/shader/common/utility.hsh +++ b/data/shader/common/utility.hsh @@ -125,4 +125,8 @@ vec2 Spherical(vec3 cartesian) { bool isnan3(vec3 value) { return isnan(value.x) || isnan(value.y) || isnan(value.z); +} + +bool isinf3(vec3 value) { + return isinf(value.x) || isinf(value.y) || isinf(value.z); } \ No newline at end of file diff --git a/data/shader/ddgi/probeDebug.fsh b/data/shader/ddgi/probeDebug.fsh index 51ec9811e..e35470c64 100644 --- a/data/shader/ddgi/probeDebug.fsh +++ b/data/shader/ddgi/probeDebug.fsh @@ -50,8 +50,8 @@ void main() { vec2 ndcL = ndcLastVS.xy / ndcLastVS.z; vec2 ndcC = ndcCurrentVS.xy / ndcCurrentVS.z; - ndcL -= globalData.jitterLast; - ndcC -= globalData.jitterCurrent; + ndcL -= globalData[0].jitterLast; + ndcC -= globalData[0].jitterCurrent; velocityFS = (ndcL - ndcC) * 0.5; diff --git a/data/shader/ddgi/probeDebug.vsh b/data/shader/ddgi/probeDebug.vsh index c41598d7c..9fc3390b2 100644 --- a/data/shader/ddgi/probeDebug.vsh +++ b/data/shader/ddgi/probeDebug.vsh @@ -24,19 +24,19 @@ void main() { vec3 probeOffset = GetProbeOffset(gl_InstanceIndex); vec3 probePosition = GetProbePosition(probeCoord); - mat4 mvMatrix = globalData.vMatrix; + mat4 mvMatrix = globalData[0].vMatrix; // Move any animation code to their own compute shaders vec3 position = probeOffset + probePosition + vPosition * scale; vec4 positionToCamera = mvMatrix * vec4(position, 1.0); - gl_Position = globalData.pMatrix * positionToCamera; + gl_Position = globalData[0].pMatrix * positionToCamera; // Needed for velocity buffer calculation ndcCurrentVS = vec3(gl_Position.xy, gl_Position.w); // For moving objects we need the last frames matrix vec3 lastPosition = position; - vec4 last = globalData.pvMatrixLast * vec4(lastPosition, 1.0); + vec4 last = globalData[0].pvMatrixLast * vec4(lastPosition, 1.0); ndcLastVS = vec3(last.xy, last.w); worldSpaceNormal = normalize(vPosition); diff --git a/data/shader/ddgi/rayHit.csh b/data/shader/ddgi/rayHit.csh index 09a0aa064..7ae246e87 100644 --- a/data/shader/ddgi/rayHit.csh +++ b/data/shader/ddgi/rayHit.csh @@ -70,9 +70,11 @@ vec3 EvaluateHit(inout Ray ray) { } // Unpack the compressed triangle and extract surface parameters - Triangle tri = UnpackTriangle(triangles[ray.hitID]); - bool backfaceHit; - Surface surface = GetSurfaceParameters(tri, ray, false, backfaceHit, 4); + Instance instance = GetInstance(ray); + Triangle tri = GetTriangle(ray, instance); + + bool backfaceHit; + Surface surface = GetSurfaceParameters(instance, tri, ray, false, backfaceHit, 4); // Indicates backface hit if (backfaceHit) { @@ -115,12 +117,18 @@ vec3 EvaluateDirectLight(inout Surface surface) { // Check for visibilty. This is important to get an // estimate of the solid angle of the light from point P // on the surface. + if (light.type == uint(DIRECTIONAL_LIGHT)) { #ifdef USE_SHADOW_MAP - radiance *= CalculateShadowWorldSpace(Uniforms.shadow, cascadeMaps, surface.P, - surface.geometryNormal, saturate(dot(surface.L, surface.geometryNormal))); + float shadowFactor = CalculateShadowWorldSpace(Uniforms.shadow, cascadeMaps, surface.P, + surface.geometryNormal, saturate(dot(surface.L, surface.geometryNormal))); + radiance *= shadowFactor; #else - radiance *= CheckVisibility(surface, lightDistance) ? 1.0 : 0.0; + radiance *= CheckVisibility(surface, lightDistance) ? 1.0 : 0.0; #endif + } + else { + radiance *= CheckVisibility(surface, lightDistance) ? 1.0 : 0.0; + } return reflectance * radiance * surface.NdotL / lightPdf; @@ -132,7 +140,6 @@ bool CheckVisibility(Surface surface, float lightDistance) { Ray ray; ray.direction = surface.L; ray.origin = surface.P + surface.N * EPSILON; - ray.inverseDirection = 1.0 / ray.direction; return HitAny(ray, 0.0, lightDistance - 2.0 * EPSILON) == false; } else { diff --git a/data/shader/deferred/deferred.hsh b/data/shader/deferred/deferred.hsh index db8b0d754..d639798ee 100644 --- a/data/shader/deferred/deferred.hsh +++ b/data/shader/deferred/deferred.hsh @@ -3,16 +3,16 @@ #include <../common/normalencode.hsh> #include <../brdf/surface.hsh> -layout(set = 1, binding = 2) uniform sampler2D baseColorTexture; -layout(set = 1, binding = 3) uniform sampler2D normalTexture; -layout(set = 1, binding = 4) uniform sampler2D geometryNormalTexture; -layout(set = 1, binding = 5) uniform sampler2D roughnessMetalnessAoTexture; -layout(set = 1, binding = 6) uniform usampler2D materialIdxTexture; -layout(set = 1, binding = 7) uniform sampler2D depthTexture; -layout(set = 1, binding = 8) uniform sampler2D volumetricTexture; - -layout(set = 1, binding = 9) uniform samplerCube specularProbe; -layout(set = 1, binding = 10) uniform samplerCube diffuseProbe; +layout(set = 1, binding = 3) uniform sampler2D baseColorTexture; +layout(set = 1, binding = 4) uniform sampler2D normalTexture; +layout(set = 1, binding = 5) uniform sampler2D geometryNormalTexture; +layout(set = 1, binding = 6) uniform sampler2D roughnessMetalnessAoTexture; +layout(set = 1, binding = 7) uniform usampler2D materialIdxTexture; +layout(set = 1, binding = 8) uniform sampler2D depthTexture; +layout(set = 1, binding = 9) uniform sampler2D volumetricTexture; + +layout(set = 1, binding = 10) uniform samplerCube specularProbe; +layout(set = 1, binding = 11) uniform samplerCube diffuseProbe; Material GetMaterial(vec2 texCoord) { uint materialIdx = textureLod(materialIdxTexture, texCoord, 0).r; diff --git a/data/shader/deferred/geometry.fsh b/data/shader/deferred/geometry.fsh index be2e091d0..d733f5e5a 100644 --- a/data/shader/deferred/geometry.fsh +++ b/data/shader/deferred/geometry.fsh @@ -176,8 +176,8 @@ void main() { vec2 ndcL = ndcLastVS.xy / ndcLastVS.z; vec2 ndcC = ndcCurrentVS.xy / ndcCurrentVS.z; - ndcL -= globalData.jitterLast; - ndcC -= globalData.jitterCurrent; + ndcL -= globalData[0].jitterLast; + ndcC -= globalData[0].jitterCurrent; velocityFS = (ndcL - ndcC) * 0.5; diff --git a/data/shader/deferred/geometry.vsh b/data/shader/deferred/geometry.vsh index 7af08e013..330d01091 100644 --- a/data/shader/deferred/geometry.vsh +++ b/data/shader/deferred/geometry.vsh @@ -25,11 +25,11 @@ layout(location=3) in vec4 vTangent; layout(location=4) in vec4 vVertexColors; #endif -layout(std430, set = 1, binding = 0) buffer CurrentMatrices { +layout(std430, set = 1, binding = 1) buffer CurrentMatrices { mat3x4 currentMatrices[]; }; -layout(std430, set = 1, binding = 1) buffer LastMatrices { +layout(std430, set = 1, binding = 2) buffer LastMatrices { mat3x4 lastMatrices[]; }; @@ -71,27 +71,27 @@ void main() { texCoordVS = PushConstants.invertUVs > 0 ? vec2(vTexCoord.x, 1.0 - vTexCoord.y) : vTexCoord; #endif - mat4 mvMatrix = globalData.vMatrix * mMatrix; + mat4 mvMatrix = globalData[0].vMatrix * mMatrix; vec3 position = vPosition; vec3 lastPosition = vPosition; if (PushConstants.vegetation > 0) { - position = WindAnimation(vPosition, globalData.time, mMatrix[3].xyz); - lastPosition = WindAnimation(vPosition, globalData.time - globalData.deltaTime, mMatrix[3].xyz); + position = WindAnimation(vPosition, globalData[0].time, mMatrix[3].xyz); + lastPosition = WindAnimation(vPosition, globalData[0].time - globalData[0].deltaTime, mMatrix[3].xyz); } vec4 positionToCamera = mvMatrix * vec4(position, 1.0); positionVS = positionToCamera.xyz; - gl_Position = globalData.pMatrix * positionToCamera; + gl_Position = globalData[0].pMatrix * positionToCamera; // Needed for velocity buffer calculation ndcCurrentVS = vec3(gl_Position.xy, gl_Position.w); // For moving objects we need the last frames matrix - vec4 last = globalData.pvMatrixLast * mMatrixLast * vec4(lastPosition, 1.0); + vec4 last = globalData[0].pvMatrixLast * mMatrixLast * vec4(lastPosition, 1.0); ndcLastVS = vec3(last.xy, last.w); // Only after ndc calculation apply the clip correction diff --git a/data/shader/deferred/indirect.csh b/data/shader/deferred/indirect.csh index efe0336d9..25b02874b 100644 --- a/data/shader/deferred/indirect.csh +++ b/data/shader/deferred/indirect.csh @@ -12,9 +12,10 @@ layout (local_size_x = 8, local_size_y = 8) in; layout(set = 3, binding = 0, rgba16f) uniform image2D image; layout(set = 3, binding = 1) uniform sampler2D aoTexture; layout(set = 3, binding = 2) uniform sampler2D reflectionTexture; -layout(set = 3, binding = 3) uniform sampler2D lowResDepthTexture; +layout(set = 3, binding = 3) uniform sampler2D giTexture; +layout(set = 3, binding = 4) uniform sampler2D lowResDepthTexture; -layout(set = 3, binding = 4) uniform UniformBuffer { +layout(set = 3, binding = 5) uniform UniformBuffer { int aoEnabled; int aoDownsampled2x; int reflectionEnabled; @@ -26,6 +27,7 @@ layout(set = 3, binding = 4) uniform UniformBuffer { shared float depths[36]; shared float aos[36]; shared vec3 reflections[36]; +shared vec4 gi[36]; const uint depthDataSize = (gl_WorkGroupSize.x / 2 + 2) * (gl_WorkGroupSize.y / 2 + 2); const ivec2 unflattenedDepthDataSize = ivec2(gl_WorkGroupSize) / 2 + 2; @@ -39,9 +41,16 @@ void LoadGroupSharedData() { ivec2 offset = Unflatten2D(int(gl_LocalInvocationIndex), unflattenedDepthDataSize); offset += workGroupOffset; offset = clamp(offset, ivec2(0), textureSize(lowResDepthTexture, 0)); - depths[gl_LocalInvocationIndex] = texelFetch(lowResDepthTexture, offset, 0).r; + depths[gl_LocalInvocationIndex] = ConvertDepthToViewSpaceDepth(texelFetch(lowResDepthTexture, offset, 0).r); +#ifdef AO aos[gl_LocalInvocationIndex] = texelFetch(aoTexture, offset, 0).r; +#endif +#ifdef REFLECTION reflections[gl_LocalInvocationIndex] = texelFetch(reflectionTexture, offset, 0).rgb; +#endif +#ifdef SSGI + gi[gl_LocalInvocationIndex] = texelFetch(giTexture, offset, 0); +#endif } barrier(); @@ -101,12 +110,14 @@ vec3 UpsampleReflection2x(float referenceDepth, vec2 texCoords) { float minWeight = 1.0; + referenceDepth = ConvertDepthToViewSpaceDepth(referenceDepth); + for (uint i = 0; i < 9; i++) { int sharedMemoryOffset = Flatten2D(pixel + offsets[i], unflattenedDepthDataSize); float depth = depths[sharedMemoryOffset]; float depthDiff = abs(referenceDepth - depth); - float depthWeight = min(exp(-depthDiff * 32.0), 1.0); + float depthWeight = min(exp(-depthDiff * 4.0), 1.0); minWeight = min(minWeight, depthWeight); invocationDepths[i] = depth; @@ -120,6 +131,35 @@ vec3 UpsampleReflection2x(float referenceDepth, vec2 texCoords) { } +vec4 UpsampleGi2x(float referenceDepth, vec2 texCoords) { + + ivec2 pixel = ivec2(gl_LocalInvocationID) / 2 + ivec2(1); + + float invocationDepths[9]; + + float minWeight = 1.0; + + referenceDepth = ConvertDepthToViewSpaceDepth(referenceDepth); + + for (uint i = 0; i < 9; i++) { + int sharedMemoryOffset = Flatten2D(pixel + offsets[i], unflattenedDepthDataSize); + float depth = depths[sharedMemoryOffset]; + + float depthDiff = abs(referenceDepth - depth); + float depthWeight = min(exp(-depthDiff * 4.0), 1.0); + minWeight = min(minWeight, depthWeight); + + invocationDepths[i] = depth; + } + + int idx = NearestDepth(referenceDepth, invocationDepths); + int offset = Flatten2D(pixel + offsets[idx], unflattenedDepthDataSize); + + vec4 bilinearGi = textureLod(giTexture, texCoords, 0); + return mix(gi[offset], bilinearGi, minWeight); + +} + void main() { if (Uniforms.aoDownsampled2x > 0) LoadGroupSharedData(); @@ -142,10 +182,10 @@ void main() { // We don't have any light direction, that's why we use vec3(0.0, -1.0, 0.0) as a placeholder Surface surface = GetSurface(texCoord, depth, vec3(0.0, -1.0, 0.0), geometryNormal); - vec3 worldView = normalize(vec3(globalData.ivMatrix * vec4(surface.P, 0.0))); - vec3 worldPosition = vec3(globalData.ivMatrix * vec4(surface.P, 1.0)); - vec3 worldNormal = normalize(vec3(globalData.ivMatrix * vec4(surface.N, 0.0))); - vec3 geometryWorldNormal = normalize(vec3(globalData.ivMatrix * vec4(geometryNormal, 0.0))); + vec3 worldView = normalize(vec3(globalData[0].ivMatrix * vec4(surface.P, 0.0))); + vec3 worldPosition = vec3(globalData[0].ivMatrix * vec4(surface.P, 1.0)); + vec3 worldNormal = normalize(vec3(globalData[0].ivMatrix * vec4(surface.N, 0.0))); + vec3 geometryWorldNormal = normalize(vec3(globalData[0].ivMatrix * vec4(geometryNormal, 0.0))); // Indirect diffuse BRDF #ifdef DDGI @@ -158,10 +198,11 @@ void main() { #else vec3 prefilteredDiffuse = textureLod(diffuseProbe, worldNormal, 0).rgb; vec3 indirectDiffuse = prefilteredDiffuse * EvaluateIndirectDiffuseBRDF(surface); + #endif // Indirect specular BRDF - vec3 R = normalize(mat3(globalData.ivMatrix) * reflect(-surface.V, surface.N)); + vec3 R = normalize(mat3(globalData[0].ivMatrix) * reflect(-surface.V, surface.N)); float mipLevel = surface.material.roughness * float(Uniforms.specularProbeMipLevels - 1); vec3 prefilteredSpecular = textureLod(specularProbe, R, mipLevel).rgb; // We multiply by local sky visibility because the reflection probe only includes the sky @@ -186,12 +227,19 @@ void main() { #ifdef AO float occlusionFactor = Uniforms.aoEnabled > 0 ? Uniforms.aoDownsampled2x > 0 ? UpsampleAo2x(depth) : texture(aoTexture, texCoord).r : 1.0; - indirect *= pow(occlusionFactor, Uniforms.aoStrength); +#ifdef SSGI + vec4 ssgi = UpsampleGi2x(depth, texCoord); + occlusionFactor = ssgi.a; +#endif + indirect *= vec3(pow(occlusionFactor, Uniforms.aoStrength)); +#ifdef SSGI + indirect += EvaluateIndirectDiffuseBRDF(surface) * ssgi.rgb; +#endif #endif } vec3 direct = imageLoad(image, pixel).rgb; - imageStore(image, pixel, vec4(direct + indirect, 0.0)); + imageStore(image, pixel, vec4(vec3(direct + indirect), 0.0)); } \ No newline at end of file diff --git a/data/shader/deferred/sss.csh b/data/shader/deferred/sss.csh index 308a8bbc7..6beea62db 100644 --- a/data/shader/deferred/sss.csh +++ b/data/shader/deferred/sss.csh @@ -23,7 +23,7 @@ layout(push_constant) uniform constants { // https://blog.demofox.org/2022/01/01/interleaved-gradient-noise-a-different-kind-of-low-discrepancy-sequence/ float GetInterleavedGradientNoise(vec2 screenPos) { - uint frame = globalData.frameCount % 64u; + uint frame = globalData[0].frameCount % 64u; float x = float(screenPos.x) + 5.588238 * float(frame); float y = float(screenPos.y) + 5.588238 * float(frame); @@ -41,7 +41,7 @@ float EdgeFadeOut(vec2 screenPos, float fadeDist) { vec2 PosToUV(vec3 pos) { - vec4 clipSpace = globalData.pMatrix * vec4(pos, 1.0); + vec4 clipSpace = globalData[0].pMatrix * vec4(pos, 1.0); clipSpace.xyz /= clipSpace.w; return clipSpace.xy * 0.5 + 0.5; @@ -91,7 +91,7 @@ void main() { // Step the ray rayPos += rayDir * stepLength; - vec4 offset = globalData.pMatrix * vec4(rayPos, 1.0); + vec4 offset = globalData[0].pMatrix * vec4(rayPos, 1.0); offset.xyz /= offset.w; vec2 uvPos = offset.xy * 0.5 + 0.5; diff --git a/data/shader/globals.hsh b/data/shader/globals.hsh index 8385722e0..a39d5594e 100644 --- a/data/shader/globals.hsh +++ b/data/shader/globals.hsh @@ -1,4 +1,12 @@ -layout(std140, set = 0, binding = 0) uniform GlobalBuffer { +#ifdef AE_BINDLESS +#define UNIFORM_COUNT 8192 +#define TEXTURE_COUNT 16384 +#else +#define UNIFORM_COUNT 1 +#define TEXTURE_COUNT 1 +#endif + +layout(set = 0, binding = 3, std140) uniform GlobalBuffer { vec4 frustumPlanes[6]; mat4 vMatrix; mat4 pMatrix; @@ -17,6 +25,11 @@ layout(std140, set = 0, binding = 0) uniform GlobalBuffer { float time; float deltaTime; uint frameCount; -} globalData; +} globalData[UNIFORM_COUNT]; + +layout(set = 0, binding = 4) uniform texture2D bindlessTextures[TEXTURE_COUNT]; + +layout(set = 1, binding = 12) uniform sampler2D dfgTexture; -layout(set = 0, binding = 1) uniform sampler2D dfgTexture; \ No newline at end of file +// Binding 2 is taken by materials buffer in common/material.hsh +layout(set = 1, binding = 13) uniform sampler bindlessSampler; diff --git a/data/shader/impostor/impostor.fsh b/data/shader/impostor/impostor.fsh index c45fbceab..7129cb9b1 100644 --- a/data/shader/impostor/impostor.fsh +++ b/data/shader/impostor/impostor.fsh @@ -85,7 +85,7 @@ void main() { vec3 geometryNormal = 2.0 * texture(normalMap, vec3(texCoordVS, float(indexVS)), uniforms.mipBias).rgb - 1.0; #endif - geometryNormal = normalize(vec3(globalData.vMatrix * vec4(geometryNormal, 0.0))); + geometryNormal = normalize(vec3(globalData[0].vMatrix * vec4(geometryNormal, 0.0))); // We want the normal always two face the camera for two sided materials geometryNormal *= -dot(geometryNormal, positionVS); geometryNormal = normalize(geometryNormal); @@ -109,8 +109,8 @@ void main() { vec2 ndcL = ndcLastVS.xy / ndcLastVS.z; vec2 ndcC = ndcCurrentVS.xy / ndcCurrentVS.z; - ndcL -= globalData.jitterLast; - ndcC -= globalData.jitterCurrent; + ndcL -= globalData[0].jitterLast; + ndcC -= globalData[0].jitterCurrent; velocityFS = (ndcL - ndcC) * 0.5; @@ -128,7 +128,7 @@ void main() { #else float depthOffset = texture(depthMap, vec3(texCoordVS, float(indexVS)), uniforms.mipBias).r; #endif - vec3 modelPosition = modelPositionVS + depthOffset * -globalData.cameraDirection.xyz; + vec3 modelPosition = modelPositionVS + depthOffset * -globalData[0].cameraDirection.xyz; vec4 modelPositionFS = instanceMatrix * vec4(modelPosition.xyz, 1.0); float modelDepth = modelPositionFS.z / modelPositionFS.w; gl_FragDepth = modelDepth; diff --git a/data/shader/impostor/impostor.vsh b/data/shader/impostor/impostor.vsh index bbdc3bceb..99ec1a7c5 100644 --- a/data/shader/impostor/impostor.vsh +++ b/data/shader/impostor/impostor.vsh @@ -10,7 +10,7 @@ struct ViewPlane { vec4 up; }; -layout(std430, set = 1, binding = 2) buffer Matrices { +layout(std430, set = 1, binding = 3) buffer Matrices { mat3x4 matrices[]; }; @@ -78,7 +78,7 @@ void main() { texCoordVS = 0.5 * vPosition + 0.5; vec3 pos = vec3(mMatrix * vec4(uniforms.center.xyz, 1.0)); - vec3 dir = normalize(globalData.cameraLocation.xyz - pos); + vec3 dir = normalize(globalData[0].cameraLocation.xyz - pos); float frames = float(uniforms.views); @@ -140,23 +140,23 @@ void main() { right = viewPlane.right; #endif - // up = globalData.cameraUp; - // right = globalData.cameraRight; + // up = globalData[0].cameraUp; + // right = globalData[0].cameraRight; vec4 modelPosition = vec4((normalize(up.xyz) * position.y + normalize(right.xyz) * position.x) + uniforms.center.xyz, 1.0); - positionVS = vec3(globalData.vMatrix * mMatrix * modelPosition); + positionVS = vec3(globalData[0].vMatrix * mMatrix * modelPosition); #ifdef PIXEL_DEPTH_OFFSET - instanceMatrix = globalData.pMatrix * globalData.vMatrix * mMatrix; + instanceMatrix = globalData[0].pMatrix * globalData[0].vMatrix * mMatrix; modelPositionVS = modelPosition.xyz; #endif - gl_Position = globalData.pMatrix * vec4(positionVS, 1.0); + gl_Position = globalData[0].pMatrix * vec4(positionVS, 1.0); ndcCurrentVS = vec3(gl_Position.xy, gl_Position.w); // For moving objects we need the last matrix - vec4 last = globalData.pvMatrixLast * mMatrix * modelPosition; + vec4 last = globalData[0].pvMatrixLast * mMatrix * modelPosition; ndcLastVS = vec3(last.xy, last.w); } diff --git a/data/shader/ocean/caustics.csh b/data/shader/ocean/caustics.csh index 597eeeca2..7811ac31b 100644 --- a/data/shader/ocean/caustics.csh +++ b/data/shader/ocean/caustics.csh @@ -23,7 +23,7 @@ layout(push_constant) uniform constants { // Modified from Shadertoy: https://www.shadertoy.com/view/XtKfRG float caustics(vec3 pos) { mat3 m = mat3(-2,-1,2, 3,-2,1, 1,2,2); - vec3 a = vec3(pos.xz * 0.5, globalData.time / 4.0) * m; + vec3 a = vec3(pos.xz * 0.5, globalData[0].time / 4.0) * m; vec3 b = a * m * 0.4; vec3 c = b * m * 0.3; return pow( @@ -49,7 +49,7 @@ void main() { float depth = textureLod(depthTexture, texCoord, 0.0).r; vec3 viewSpacePos = ConvertDepthToViewSpace(depth, texCoord); - vec3 pixelPos = vec3(globalData.ivMatrix * vec4(viewSpacePos, 1.0)); + vec3 pixelPos = vec3(globalData[0].ivMatrix * vec4(viewSpacePos, 1.0)); float distanceToCamera = length(viewSpacePos); float fadeout = min(1.0, 1.0 - (distanceToCamera - fadeoutDistance) / (fadeoutFalloff * fadeoutDistance)); @@ -60,7 +60,7 @@ void main() { float shadowFactor = max(CalculateCascadedShadow(light.shadow, cascadeMaps, viewSpacePos, vec3(0.0, 1.0, 0.0), 0.0), 0.0); - vec3 pos = vec3(pixelPos.x, globalData.time * 0.5, pixelPos.z); + vec3 pos = vec3(pixelPos.x, globalData[0].time * 0.5, pixelPos.z); pos *= 2.0; vec3 o = vec3(1.0, 0.0, 1.0)*0.02; diff --git a/data/shader/ocean/depth.fsh b/data/shader/ocean/depth.fsh index 2ffd4a515..dd3339024 100644 --- a/data/shader/ocean/depth.fsh +++ b/data/shader/ocean/depth.fsh @@ -7,7 +7,7 @@ void main() { /* StencilFeatures features = CreateStencilFeatures(); - vec3 viewDir = normalize(fPosition - globalData.cameraLocation.xyz); + vec3 viewDir = normalize(fPosition - globalData[0].cameraLocation.xyz); bool frontFacing = dot(normalize(fNormal), viewDir) < 0.0 ? true : false; if (!gl_FrontFacing && !frontFacing) { diff --git a/data/shader/ocean/depth.vsh b/data/shader/ocean/depth.vsh index 180bbaf95..571998bb7 100644 --- a/data/shader/ocean/depth.vsh +++ b/data/shader/ocean/depth.vsh @@ -44,14 +44,14 @@ void main() { vec3(PushConstants.nodeLocation.x, 0.0, PushConstants.nodeLocation.y) + Uniforms.translation.xyz; - float distanceToCamera = distance(fPosition.xyz, globalData.cameraLocation.xyz); + float distanceToCamera = distance(fPosition.xyz, globalData[0].cameraLocation.xyz); float perlinScale, shoreScaling; vec3 normalShoreWave; fPosition += GetOceanDisplacement(fPosition, distanceToCamera, perlinScale, shoreScaling, normalShoreWave); - vec3 position = vec3(globalData.vMatrix * vec4(fPosition, 1.0)); - vec4 fClipSpace = globalData.pMatrix * vec4(position, 1.0); + vec3 position = vec3(globalData[0].vMatrix * vec4(fPosition, 1.0)); + vec4 fClipSpace = globalData[0].pMatrix * vec4(position, 1.0); gl_Position = fClipSpace; diff --git a/data/shader/ocean/ocean.fsh b/data/shader/ocean/ocean.fsh index a4771b85f..fa8c60e05 100644 --- a/data/shader/ocean/ocean.fsh +++ b/data/shader/ocean/ocean.fsh @@ -61,7 +61,7 @@ void main() { float fold; vec2 gradientDisplacement; - GetOceanGradientAndFold(fOriginalCoord.xz, distance(fOriginalCoord.xyz, globalData.cameraLocation.xyz), + GetOceanGradientAndFold(fOriginalCoord.xz, distance(fOriginalCoord.xyz, globalData[0].cameraLocation.xyz), fold, gradientDisplacement); vec2 gradient = gradientDisplacement; @@ -99,8 +99,8 @@ void main() { vec3 rippleNormal = vec3(0.0, 1.0, 0.0); #ifdef RIPPLE_TEXTURE - rippleNormal = normalize(2.0 * texture(rippleTexture, 20.0 * fTexCoord - vec2(globalData.time * 0.2)).rgb - 1.0); - rippleNormal += normalize(2.0 * texture(rippleTexture, 20.0 * fTexCoord * 0.5 + vec2(globalData.time * 0.05)).rgb - 1.0); + rippleNormal = normalize(2.0 * texture(rippleTexture, 20.0 * fTexCoord - vec2(globalData[0].time * 0.2)).rgb - 1.0); + rippleNormal += normalize(2.0 * texture(rippleTexture, 20.0 * fTexCoord * 0.5 + vec2(globalData[0].time * 0.05)).rgb - 1.0); // Won't work with rippleNormal = vec3(0.0, 1.0, 0.0). Might be worth an investigation norm = normalize(tbn * rippleNormal); #endif @@ -109,7 +109,7 @@ void main() { float rippleScaling = clamp(1.0 - shoreScaling, 0.05, 0.1); norm = normalize(mix(fNormal, norm, rippleScaling)); - vec3 eyeDir = normalize(fModelCoord - globalData.cameraLocation.xyz); + vec3 eyeDir = normalize(fModelCoord - globalData[0].cameraLocation.xyz); float nDotL = dot(norm, -light.direction.xyz); float nDotE = saturate(dot(norm, -eyeDir)); @@ -143,7 +143,7 @@ void main() { // Calculate water depth based on the viewer (ray from camera to ground) float waterViewDepth = max(0.0, fPosition.z - depthPos.z); - vec2 disturbance = (mat3(globalData.vMatrix) * vec3(norm.x, 0.0, norm.z)).xz; + vec2 disturbance = (mat3(globalData[0].vMatrix) * vec3(norm.x, 0.0, norm.z)).xz; vec2 refractionDisturbance = vec2(-disturbance.x, disturbance.y) * 0.02; refractionDisturbance *= min(2.0, waterViewDepth); @@ -198,7 +198,7 @@ void main() { if (!gl_FrontFacing) { fresnel = 0.02 + (1.0 - 0.02) * pow(1.0 - saturate(dot(fNormal, eyeDir)), 5.0); - disturbance = (mat3(globalData.vMatrix) * -vec3(norm.x, 0.0, norm.z)).xz; + disturbance = (mat3(globalData[0].vMatrix) * -vec3(norm.x, 0.0, norm.z)).xz; refractionDisturbance = vec2(disturbance.x, -disturbance.y) * 0.1; refractionDisturbance *= min(2.0, waterViewDepth); @@ -213,15 +213,15 @@ void main() { volumetricClouds = textureLod(volumetricCloudTexture, ndcCoord, 0.0); #endif color = ApplyVolumetrics(Uniforms.fog, color, volumetricFog, volumetricClouds, - eyeDir, globalData.planetCenter.xyz, Uniforms.innerCloudRadius); + eyeDir, globalData[0].planetCenter.xyz, Uniforms.innerCloudRadius); } // Calculate velocity vec2 ndcL = ndcLast.xy / ndcLast.z; vec2 ndcC = ndcCurrent.xy / ndcCurrent.z; - ndcL -= globalData.jitterLast; - ndcC -= globalData.jitterCurrent; + ndcL -= globalData[0].jitterLast; + ndcC -= globalData[0].jitterCurrent; velocity = (ndcL - ndcC) * 0.5; diff --git a/data/shader/ocean/ocean.vsh b/data/shader/ocean/ocean.vsh index 6b970d443..610081308 100644 --- a/data/shader/ocean/ocean.vsh +++ b/data/shader/ocean/ocean.vsh @@ -18,7 +18,6 @@ layout(location=8) out vec3 ndcLast; #ifdef TERRAIN layout(location=9) out vec3 normalShoreWave; #endif -layout(location=10) out float perlinScale; vec3 stitch(vec3 position) { @@ -59,17 +58,18 @@ void main() { #ifndef TERRAIN vec3 normalShoreWave; -#endif - float distanceToCamera = distance(fOriginalCoord.xyz, globalData.cameraLocation.xyz); +#endif + float perlinScale; + float distanceToCamera = distance(fOriginalCoord.xyz, globalData[0].cameraLocation.xyz); fPosition += GetOceanDisplacement(fPosition, distanceToCamera, perlinScale, shoreScaling, normalShoreWave); fModelCoord = fPosition; - fPosition = vec3(globalData.vMatrix * vec4(fPosition, 1.0)); - fClipSpace = globalData.pMatrix * vec4(fPosition, 1.0); + fPosition = vec3(globalData[0].vMatrix * vec4(fPosition, 1.0)); + fClipSpace = globalData[0].pMatrix * vec4(fPosition, 1.0); ndcCurrent = vec3(fClipSpace.xy, fClipSpace.w); // For moving objects we need the last matrix - vec4 last = globalData.pvMatrixLast * vec4(fModelCoord, 1.0); + vec4 last = globalData[0].pvMatrixLast * vec4(fModelCoord, 1.0); ndcLast = vec3(last.xy, last.w); gl_Position = fClipSpace; diff --git a/data/shader/ocean/shoreInteraction.hsh b/data/shader/ocean/shoreInteraction.hsh index 95a5f1c42..f9999cf72 100644 --- a/data/shader/ocean/shoreInteraction.hsh +++ b/data/shader/ocean/shoreInteraction.hsh @@ -22,11 +22,11 @@ vec3 CalculateGerstner(vec3 position) { float w = 1.0 / waveLength; float phi = speed * w; - float rad = w * shoreDistance + phi * globalData.time; + float rad = w * shoreDistance + phi * globalData[0].time; float modulation = saturate(sin(position.x / 20.0) + cos(position.z / 10.0)); - float distanceScale = saturate(1.0 - (distance(globalData.cameraLocation.xyz, position) + float distanceScale = saturate(1.0 - (distance(globalData[0].cameraLocation.xyz, position) - Uniforms.shoreWaveDistanceOffset) / Uniforms.shoreWaveDistanceScale); float amplitude = 1.0 * Uniforms.shoreWaveAmplitude * scale * modulation * distanceScale; @@ -57,7 +57,7 @@ vec2 CalculateShoreInteraction(vec3 position) { float w = 1.0 / waveLength; float phi = speed * w; - float rad = w * shoreDistance + phi * globalData.time; + float rad = w * shoreDistance + phi * globalData[0].time; float modulation = saturate(sin(position.x / 20.0) + cos(position.z / 10.0)); diff --git a/data/shader/ocean/underwater.csh b/data/shader/ocean/underwater.csh index bfbd3d327..8fea519c7 100644 --- a/data/shader/ocean/underwater.csh +++ b/data/shader/ocean/underwater.csh @@ -40,9 +40,9 @@ void main() { vec3 viewSpacePos = ConvertDepthToViewSpace(depth, texCoord); vec3 viewSpaceOceanPos = ConvertDepthToViewSpace(oceanDepth, texCoord); - vec3 pixelPos = vec3(globalData.ivMatrix * vec4(viewSpacePos, 1.0)); - vec3 oceanPos = vec3(globalData.ivMatrix * vec4(viewSpaceOceanPos, 1.0)); - vec3 nearPos = vec3(globalData.ivMatrix * vec4(ConvertDepthToViewSpace(0.0, texCoord), 1.0)); + vec3 pixelPos = vec3(globalData[0].ivMatrix * vec4(viewSpacePos, 1.0)); + vec3 oceanPos = vec3(globalData[0].ivMatrix * vec4(viewSpaceOceanPos, 1.0)); + vec3 nearPos = vec3(globalData[0].ivMatrix * vec4(ConvertDepthToViewSpace(0.0, texCoord), 1.0)); float distanceToCamera = length(viewSpacePos); @@ -77,12 +77,12 @@ void main() { texCoord = (texCoord + vec2(0.05)) * 0.9; - texCoord.x += sin(texCoord.y * 2.0 * PI * 5.0 + 5.0 * globalData.time) * 0.001 / depth; - texCoord.y += cos(texCoord.x * 2.0 * PI * 2.0 + 2.0 * globalData.time) * 0.001 / depth; + texCoord.x += sin(texCoord.y * 2.0 * PI * 5.0 + 5.0 * globalData[0].time) * 0.001 / depth; + texCoord.y += cos(texCoord.x * 2.0 * PI * 2.0 + 2.0 * globalData[0].time) * 0.001 / depth; depth = textureLod(depthTexture, texCoord, 0.0).r; viewSpacePos = ConvertDepthToViewSpace(depth, texCoord); - pixelPos = vec3(globalData.ivMatrix * vec4(viewSpacePos, 1.0)); + pixelPos = vec3(globalData[0].ivMatrix * vec4(viewSpacePos, 1.0)); vec3 refractionColor = textureLod(refractionTexture, texCoord, 0.0).rgb; diff --git a/data/shader/pathtracer/rayGen.csh b/data/shader/pathtracer/rayGen.csh index 0ad0ad141..5e828a734 100644 --- a/data/shader/pathtracer/rayGen.csh +++ b/data/shader/pathtracer/rayGen.csh @@ -8,6 +8,10 @@ layout (local_size_x = 8, local_size_y = 8) in; +#ifdef REALTIME +layout (set = 3, binding = 1, r32ui) writeonly uniform uimage2DArray frameAccumImage; +#endif + layout(set = 3, binding = 4) uniform UniformBuffer { vec4 origin; vec4 right; @@ -28,17 +32,21 @@ void main() { // Apply a subpixel jitter to get supersampling float jitterX = random(vec2(float(Uniforms.sampleCount), 0.0)); float jitterY = random(vec2(float(Uniforms.sampleCount), 1.0)); - +#ifndef REALTIME vec2 coord = (vec2(pixel) + vec2(jitterX, jitterY)) / vec2(float(Uniforms.resolution.x), float(Uniforms.resolution.y)); +#else + vec2 coord = globalData[0].jitterCurrent * 0.25 + (vec2(pixel) + vec2(0.5)) / + vec2(float(Uniforms.resolution.x), float(Uniforms.resolution.y)); +#endif Ray ray; - ray.ID = Flatten2D(pixel, Uniforms.resolution); + ray.ID = Flatten2D(pixel, Uniforms.resolution) * int(gl_NumWorkGroups.z) + int(gl_WorkGroupID.z); ray.direction = normalize(Uniforms.origin.xyz + Uniforms.right.xyz * coord.x - + Uniforms.bottom.xyz * coord.y - globalData.cameraLocation.xyz); - ray.origin = globalData.cameraLocation.xyz; + + Uniforms.bottom.xyz * coord.y - globalData[0].cameraLocation.xyz); + ray.origin = globalData[0].cameraLocation.xyz; ray.hitID = 0; @@ -69,7 +77,16 @@ void main() { index = Flatten2D(localID.yx, overlappingPixels.yx) + offset; } - WriteRay(ray, uint(index)); + WriteRay(ray, uint(index) * gl_NumWorkGroups.z + gl_WorkGroupID.z); + +#ifdef REALTIME + if (gl_WorkGroupID.z == 0u) { + imageStore(frameAccumImage, ivec3(pixel, 0), uvec4(0u)); + imageStore(frameAccumImage, ivec3(pixel, 1), uvec4(0u)); + imageStore(frameAccumImage, ivec3(pixel, 2), uvec4(0u)); + } +#endif + } } \ No newline at end of file diff --git a/data/shader/pathtracer/rayHit.csh b/data/shader/pathtracer/rayHit.csh index f0a84a668..77d6ed90c 100644 --- a/data/shader/pathtracer/rayHit.csh +++ b/data/shader/pathtracer/rayHit.csh @@ -4,6 +4,7 @@ #include <../common/random.hsh> #include <../common/utility.hsh> +#include <../common/normalencode.hsh> #include <../common/flatten.hsh> #include <../common/PI.hsh> @@ -14,7 +15,16 @@ layout (local_size_x = 32) in; +#ifdef REALTIME +layout (set = 3, binding = 1, r32ui) uniform uimage2DArray frameAccumImage; +layout (set = 3, binding = 5, rg16f) writeonly uniform image2D velocityImage; +layout (set = 3, binding = 6, r32f) writeonly uniform image2D depthImage; +layout (set = 3, binding = 7, rg16f) writeonly uniform image2D normalImage; +layout (set = 3, binding = 8, r16ui) writeonly uniform uimage2D materialIdxImage; +layout (set = 3, binding = 9, rgba8) writeonly uniform image2D albedoImage; +#else layout (set = 3, binding = 1, rgba8) writeonly uniform image2D outputImage; +#endif /* Except for image variables qualified with the format qualifiers r32f, r32i, and r32ui, @@ -31,11 +41,13 @@ layout(set = 3, binding = 4) uniform UniformBuffer { int bounceCount; float seed; float exposure; + int samplesPerFrame; + float maxRadiance; } Uniforms; const float gamma = 1.0 / 2.2; -void EvaluateBounce(inout Ray ray, inout RayPayload payload); +Surface EvaluateBounce(inout Ray ray, inout RayPayload payload); vec3 EvaluateDirectLight(inout Surface surface); void EvaluateIndirectLight(inout Surface surface, inout Ray ray, inout RayPayload payload); float CheckVisibility(Surface surface, float lightDistance); @@ -53,27 +65,72 @@ void main() { if (Uniforms.bounceCount > 0) payload = ReadRayPayload(); - ivec2 pixel = Unflatten2D(ray.ID, Uniforms.resolution); - - EvaluateBounce(ray, payload); + ivec2 pixel = Unflatten2D(ray.ID / Uniforms.samplesPerFrame, Uniforms.resolution); + Surface surface = EvaluateBounce(ray, payload); + vec4 accumColor = vec4(0.0); float energy = dot(payload.throughput, vec3(1.0)); + +#ifdef REALTIME + // Write out material information and velocity into a g-buffer + if (ray.ID % Uniforms.samplesPerFrame == 0 && Uniforms.bounceCount == 0) { + Instance instance = bvhInstances[ray.hitInstanceID]; + mat4 lastMatrix = mat4(transpose(instanceLastMatrices[ray.hitInstanceID])); + + vec3 pos = ray.hitID >= 0 ? surface.P : ray.origin + ray.direction * ray.hitDistance; + + // As a reminder: The current inverse matrix is also transposed + vec3 lastPos = vec3(lastMatrix * vec4(vec4(pos, 1.0) * instance.inverseMatrix, 1.0)); + + vec4 viewSpacePos = globalData[0].vMatrix * vec4(pos, 1.0); + vec4 projPositionCurrent = globalData[0].pMatrix * viewSpacePos; + vec4 projPositionLast = globalData[0].pvMatrixLast * vec4(lastPos, 1.0); + + vec2 ndcCurrent = projPositionCurrent.xy / projPositionCurrent.w; + vec2 ndcLast = projPositionLast.xy / projPositionLast.w; + + vec2 velocity = (ndcLast - ndcCurrent) * 0.5; + imageStore(velocityImage, pixel, vec4(velocity, 0.0, 0.0)); + + imageStore(depthImage, pixel, vec4(viewSpacePos.z, 0.0, 0.0, 0.0)); + imageStore(materialIdxImage, pixel, uvec4(surface.material.ID, 0.0, 0.0, 0.0)); + imageStore(normalImage, pixel, vec4(EncodeNormal(surface.geometryNormal), 0.0, 0.0)); + } +#endif - if (energy == 0 || Uniforms.bounceCount == Uniforms.maxBounces) { + if (energy == 0 || Uniforms.bounceCount == Uniforms.maxBounces) { +#ifndef REALTIME if (Uniforms.sampleCount > 0) accumColor = imageLoad(inAccumImage, pixel); - - accumColor += vec4(payload.radiance, 1.0); + + accumColor += vec4(payload.radiance, 1.0); imageStore(outAccumImage, pixel, accumColor); - + vec3 color = accumColor.rgb * Uniforms.exposure / float(Uniforms.sampleCount + 1); color = vec3(1.0) - exp(-color); //color = color / (vec3(1.0) + color); imageStore(outputImage, pixel, vec4(pow(color, vec3(gamma)), 1.0)); +#else + if (Uniforms.samplesPerFrame == 1) { + imageStore(frameAccumImage, ivec3(pixel, 0), uvec4(floatBitsToUint(payload.radiance.r))); + imageStore(frameAccumImage, ivec3(pixel, 1), uvec4(floatBitsToUint(payload.radiance.g))); + imageStore(frameAccumImage, ivec3(pixel, 2), uvec4(floatBitsToUint(payload.radiance.b))); + } + else { + uint maxValuePerSample = 0xFFFFFFFF / uint(Uniforms.samplesPerFrame); + + vec3 normalizedRadiance = clamp(payload.radiance / Uniforms.maxRadiance, vec3(0.0), vec3(1.0)); + uvec3 quantizedRadiance = clamp(uvec3(normalizedRadiance * float(maxValuePerSample)), uvec3(0u), uvec3(maxValuePerSample)); + + imageAtomicAdd(frameAccumImage, ivec3(pixel, 0), quantizedRadiance.r); + imageAtomicAdd(frameAccumImage, ivec3(pixel, 1), quantizedRadiance.g); + imageAtomicAdd(frameAccumImage, ivec3(pixel, 2), quantizedRadiance.b); + } +#endif } else { WriteRay(ray, payload); @@ -82,7 +139,9 @@ void main() { } -void EvaluateBounce(inout Ray ray, inout RayPayload payload) { +Surface EvaluateBounce(inout Ray ray, inout RayPayload payload) { + + Surface surface; // If we didn't find a triangle along the ray, // we add the contribution of the environment map @@ -91,12 +150,13 @@ void EvaluateBounce(inout Ray ray, inout RayPayload payload) { payload.radiance += min(SampleEnvironmentMap(ray.direction).rgb * payload.throughput, vec3(10.0)); payload.throughput = vec3(0.0); - return; + return surface; } // Unpack the compressed triangle and extract surface parameters - Triangle tri = UnpackTriangle(triangles[ray.hitID]); - Surface surface = GetSurfaceParameters(tri, ray, true, 0); + Instance instance = GetInstance(ray); + Triangle tri = GetTriangle(ray, instance); + surface = GetSurfaceParameters(instance, tri, ray, true, 0); // If we hit an emissive surface we need to terminate the ray if (dot(surface.material.emissiveColor, vec3(1.0)) > 0.0 && @@ -116,7 +176,7 @@ void EvaluateBounce(inout Ray ray, inout RayPayload payload) { // will be heavily biased or not there entirely. Better lobe // selection etc. helps if (Uniforms.bounceCount > 0) { - const float radianceLimit = 25.0; + const float radianceLimit = 10.0; float radianceMax = max(max(radiance.r, max(radiance.g, radiance.b)), radianceLimit); radiance *= radianceLimit / radianceMax; @@ -124,7 +184,7 @@ void EvaluateBounce(inout Ray ray, inout RayPayload payload) { payload.radiance += radiance; EvaluateIndirectLight(surface, ray, payload); - return; + return surface; } @@ -224,12 +284,11 @@ void EvaluateIndirectLight(inout Surface surface, inout Ray ray, inout RayPayloa } ray.direction = normalize(brdfSample.L); - ray.inverseDirection = 1.0 / ray.direction; payload.throughput *= mat.ao; // Russain roulette, terminate rays with a chance of one percent float probability = clamp(max(payload.throughput.r, - max(payload.throughput.g, payload.throughput.b)), 0.01, 0.99); + max(payload.throughput.g, payload.throughput.b)), 0.1, 0.99); if (random(raySeed, curSeed) > probability) { payload.throughput = vec3(0.0); @@ -251,7 +310,6 @@ float CheckVisibility(Surface surface, float lightDistance) { Ray ray; ray.direction = surface.L; ray.origin = surface.P + surface.N * EPSILON; - ray.inverseDirection = 1.0 / ray.direction; return HitAnyTransparency(ray, 0.0, lightDistance - 2.0 * EPSILON); } else { diff --git a/data/shader/pathtracer/temporal.csh b/data/shader/pathtracer/temporal.csh new file mode 100644 index 000000000..0010a34cc --- /dev/null +++ b/data/shader/pathtracer/temporal.csh @@ -0,0 +1,411 @@ +#include <../common/utility.hsh> +#include <../common/convert.hsh> +#include <../common/PI.hsh> +#include <../common/stencil.hsh> +#include <../common/flatten.hsh> +#include <../common/material.hsh> +#include <../common/random.hsh> +#include <../common/normalencode.hsh> + +layout (local_size_x = 16, local_size_y = 16) in; + +layout (set = 3, binding = 0, rgba8) writeonly uniform image2D resolveImage; +layout (set = 3, binding = 1, r32ui) readonly uniform uimage2DArray frameAccumImage; +layout (set = 3, binding = 2) uniform sampler2D inAccumImage; +layout (set = 3, binding = 3, rgba32f) writeonly uniform image2D outAccumImage; +layout (set = 3, binding = 4) uniform sampler2D velocityTexture; + +layout (set = 3, binding = 5) uniform sampler2D depthTexture; +layout (set = 3, binding = 6) uniform sampler2D normalTexture; +layout (set = 3, binding = 7) uniform usampler2D materialIdxTexture; + +layout(set = 3, binding = 9) uniform sampler2D historyDepthTexture; +layout(set = 3, binding = 10) uniform sampler2D historyNormalTexture; +layout(set = 3, binding = 11) uniform usampler2D historyMaterialIdxTexture; + +layout(push_constant) uniform constants { + float historyClipMax; + float currentClipFactor; + float maxHistoryLength; + float exposure; + int samplesPerFrame; + float maxRadiance; +} pushConstants; + +vec2 invResolution = 1.0 / vec2(imageSize(resolveImage)); +vec2 resolution = vec2(imageSize(resolveImage)); + +const int kernelRadius = 5; + +const uint sharedDataSize = (gl_WorkGroupSize.x + 2 * kernelRadius) * (gl_WorkGroupSize.y + 2 * kernelRadius); +const ivec2 unflattenedSharedDataSize = ivec2(gl_WorkGroupSize) + 2 * kernelRadius; + +shared vec4 sharedRadianceDepth[sharedDataSize]; + +const ivec2 offsets[9] = ivec2[9]( + ivec2(-1, -1), + ivec2(0, -1), + ivec2(1, -1), + ivec2(-1, 0), + ivec2(0, 0), + ivec2(1, 0), + ivec2(-1, 1), + ivec2(0, 1), + ivec2(1, 1) +); + +const ivec2 pixelOffsets[4] = ivec2[4]( + ivec2(0, 0), + ivec2(1, 0), + ivec2(0, 1), + ivec2(1, 1) +); + +float Luma(vec3 color) { + + const vec3 luma = vec3(0.299, 0.587, 0.114); + return dot(color, luma); + +} + +vec3 FetchTexel(ivec2 texel) { + + vec3 color; + + if (pushConstants.samplesPerFrame == 1) { + color.r = uintBitsToFloat(imageLoad(frameAccumImage, ivec3(texel, 0)).r); + color.g = uintBitsToFloat(imageLoad(frameAccumImage, ivec3(texel, 1)).r); + color.b = uintBitsToFloat(imageLoad(frameAccumImage, ivec3(texel, 2)).r); + } + else { + uvec3 frameAccumData; + frameAccumData.r = imageLoad(frameAccumImage, ivec3(texel, 0)).r; + frameAccumData.g = imageLoad(frameAccumImage, ivec3(texel, 1)).r; + frameAccumData.b = imageLoad(frameAccumImage, ivec3(texel, 2)).r; + + uint maxValuePerSample = 0xFFFFFFFF / uint(pushConstants.samplesPerFrame); + color = vec3(vec3(frameAccumData) / float(maxValuePerSample)) / + float(pushConstants.samplesPerFrame) * pushConstants.maxRadiance; + } + + return max(color, 0.0); + +} + +void LoadGroupSharedData() { + + ivec2 workGroupOffset = ivec2(gl_WorkGroupID) * ivec2(gl_WorkGroupSize) - ivec2(kernelRadius); + + uint workGroupSize = gl_WorkGroupSize.x * gl_WorkGroupSize.y; + for(uint i = gl_LocalInvocationIndex; i < sharedDataSize; i += workGroupSize) { + ivec2 localOffset = Unflatten2D(int(i), unflattenedSharedDataSize); + ivec2 texel = localOffset + workGroupOffset; + + texel = clamp(texel, ivec2(0), ivec2(resolution) - ivec2(1)); + + sharedRadianceDepth[i].rgb = FetchTexel(texel); + sharedRadianceDepth[i].a = texelFetch(depthTexture, texel, 0).r; + } + + barrier(); + +} + +int GetSharedMemoryIndex(ivec2 pixelOffset) { + + ivec2 pixel = ivec2(gl_LocalInvocationID) + ivec2(kernelRadius); + return Flatten2D(pixel + pixelOffset, unflattenedSharedDataSize); + +} + +vec3 FetchCurrentRadiance(int sharedMemoryIdx) { + + return sharedRadianceDepth[sharedMemoryIdx].rgb; + +} + +float FetchDepth(int sharedMemoryIdx) { + + return sharedRadianceDepth[sharedMemoryIdx].a; + +} + +ivec2 FindNearest3x3(ivec2 pixel) { + + ivec2 offset = ivec2(0); + float depth = 1.0; + + for (int i = 0; i < 9; i++) { + float currDepth = texelFetch(depthTexture, pixel + offsets[i], 0).r; + if (currDepth < depth) { + depth = currDepth; + offset = offsets[i]; + } + } + + return offset; + +} + +float ClipBoundingBox(vec3 boxMin, vec3 boxMax, vec3 history, vec3 current) { + + vec3 origin = history; + vec3 dir = current - history; + + // Make sure dir isn't zero + dir.x = abs(dir.x) < (1.0 / 32767.0) ? (1.0 / 32767.0) : dir.x; + dir.y = abs(dir.y) < (1.0 / 32767.0) ? (1.0 / 32767.0) : dir.y; + dir.z = abs(dir.z) < (1.0 / 32767.0) ? (1.0 / 32767.0) : dir.z; + + vec3 invDir = 1.0 / dir; + + vec3 t0 = (boxMin - origin) * invDir; + vec3 t1 = (boxMax - origin) * invDir; + + vec3 intersect = min(t0, t1); + return max(intersect.x, max(intersect.y, intersect.z)); + +} + +float IsHistoryPixelValid(ivec2 pixel, float linearDepth, uint materialIdx, vec3 normal) { + + float confidence = 1.0; + + vec3 historyNormal = DecodeNormal(texelFetch(historyNormalTexture, pixel, 0).rg); + confidence *= pow(abs(dot(historyNormal, normal)), 16.0); + + uint historyMaterialIdx = texelFetch(historyMaterialIdxTexture, pixel, 0).r; + confidence *= historyMaterialIdx != materialIdx ? 0.0 : 1.0; + + float depthPhi = max(1.0, abs(0.25 * linearDepth)); + float historyDepth = texelFetch(historyDepthTexture, pixel, 0).r; + float historyLinearDepth = historyDepth; + confidence *= min(1.0 , exp(-abs(linearDepth - historyLinearDepth))); + + return confidence > 0.1 ? 1.0 : 0.0; + +} + +bool SampleHistory(ivec2 pixel, vec2 historyPixel, out vec4 history, out vec4 historyMoments) { + + history = vec4(0.0); + historyMoments = vec4(0.0); + + float totalWeight = 0.0; + float x = fract(historyPixel.x); + float y = fract(historyPixel.y); + + float weights[4] = { (1 - x) * (1 - y), x * (1 - y), (1 - x) * y, x * y }; + + uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; + vec3 normal = DecodeNormal(texelFetch(normalTexture, pixel, 0).rg); + float depth = texelFetch(depthTexture, pixel, 0).r; + + float linearDepth = depth; + + // Calculate confidence over 2x2 bilinear neighborhood + for (int i = 0; i < 4; i++) { + ivec2 offsetPixel = ivec2(historyPixel) + pixelOffsets[i]; + + if (IsHistoryPixelValid(offsetPixel, linearDepth, materialIdx, normal) > 0.0) { + totalWeight += weights[i]; + history += texelFetch(inAccumImage, offsetPixel, 0) * weights[i]; + //historyMoments += texelFetch(historyMomentsTexture, offsetPixel, 0) * weights[i]; + } + } + + if (totalWeight > 0.0) { + history /= totalWeight; + //historyMoments /= totalWeight; + return true; + } + + for (int i = 0; i < 9; i++) { + ivec2 offsetPixel = ivec2(historyPixel) + offsets[i]; + + if (IsHistoryPixelValid(offsetPixel, linearDepth, materialIdx, normal) > 0.0) { + totalWeight += 1.0; + history += texelFetch(inAccumImage, offsetPixel, 0); + //historyMoments += texelFetch(historyMomentsTexture, offsetPixel, 0) * weights[i]; + } + } + + if (totalWeight > 0.0) { + history /= totalWeight; + //historyMoments /= totalWeight; + return true; + } + + history = vec4(0.0); + historyMoments = vec4(0.0); + + return false; + +} + +vec4 GetCatmullRomSample(ivec2 pixel, inout float weight, float linearDepth, uint materialIdx, vec3 normal) { + + pixel = clamp(pixel, ivec2(0), ivec2(imageSize(resolveImage) - 1)); + + weight *= IsHistoryPixelValid(pixel, linearDepth, materialIdx, normal); + + return texelFetch(inAccumImage, pixel, 0) * weight; + +} + +bool SampleCatmullRom(ivec2 pixel, vec2 uv, out vec4 history) { + + // http://advances.realtimerendering.com/s2016/Filmic%20SMAA%20v7.pptx + // Credit: Jorge Jimenez (SIGGRAPH 2016) + // Ignores the 4 corners of the 4x4 grid + // Learn more: http://vec3.ca/bicubic-filtering-in-fewer-taps/ + + uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; + vec3 normal = DecodeNormal(texelFetch(normalTexture, pixel, 0).rg); + float depth = texelFetch(depthTexture, pixel, 0).r; + + vec2 position = uv * resolution; + + vec2 center = floor(position - 0.5) + 0.5; + vec2 f = position - center; + vec2 f2 = f * f; + vec2 f3 = f2 * f; + + vec2 w0 = f2 - 0.5 * (f3 + f); + vec2 w1 = 1.5 * f3 - 2.5 * f2 + 1.0; + vec2 w3 = 0.5 * (f3 - f2); + vec2 w2 = 1.0 - w0 - w1 - w3; + + ivec2 uv0 = ivec2(center - 1.0); + ivec2 uv1 = ivec2(center); + ivec2 uv2 = ivec2(center + 1.0); + ivec2 uv3 = ivec2(center + 2.0); + + ivec2 uvs[4] = { uv0, uv1, uv2, uv3 }; + vec2 weights[4] = { w0, w1, w2, w3 }; + + history = vec4(0.0); + + float totalWeight = 0.0; + + for (int x = 0; x <= 3; x++) { + for (int y = 0; y <= 3; y++) { + + float weight = weights[x].x * weights[y].y; + ivec2 uv = ivec2(uvs[x].x, uvs[y].y); + + history += GetCatmullRomSample(uv, weight, depth, materialIdx, normal); + totalWeight += weight; + + } + } + + if (totalWeight > 0.5) { + history /= totalWeight; + + return true; + } + + return false; +} + +void ComputeVarianceMinMax(out vec3 mean, out vec3 std) { + + vec3 m1 = vec3(0.0); + vec3 m2 = vec3(0.0); + // This could be varied using the temporal variance estimation + // By using a wide neighborhood for variance estimation (8x8) we introduce block artifacts + // These are similiar to video compression artifacts, the spatial filter mostly clears them up + const int radius = kernelRadius; + ivec2 pixel = ivec2(gl_GlobalInvocationID); + + float depth = texelFetch(depthTexture, pixel, 0).r; + float linearDepth = depth; + + float totalWeight = 0.0; + + for (int i = -radius; i <= radius; i++) { + for (int j = -radius; j <= radius; j++) { + int sharedMemoryIdx = GetSharedMemoryIndex(ivec2(i, j)); + + vec3 sampleRadiance = FetchCurrentRadiance(sharedMemoryIdx); + float sampleLinearDepth = FetchDepth(sharedMemoryIdx); + + float depthPhi = max(1.0, abs(0.025 * linearDepth)); + float weight = min(1.0 , exp(-abs(linearDepth - sampleLinearDepth) / depthPhi)); + + m1 += sampleRadiance * weight; + m2 += sampleRadiance * sampleRadiance * weight; + + totalWeight += weight; + } + } + + mean = m1 / totalWeight; + std = sqrt(max((m2 / totalWeight) - (mean * mean), 0.0)); +} + +void main() { + + LoadGroupSharedData(); + + ivec2 pixel = ivec2(gl_GlobalInvocationID); + if (pixel.x > imageSize(resolveImage).x || + pixel.y > imageSize(resolveImage).y) + return; + + uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; + + ivec2 offset = FindNearest3x3(pixel); + + vec3 mean, std; + ComputeVarianceMinMax(mean, std); + + vec3 currentRadiance = FetchTexel(pixel); + + vec2 velocity = texelFetch(velocityTexture, pixel, 0).rg; + + vec2 historyUV = (vec2(pixel) + vec2(0.5)) * invResolution + velocity; + vec2 historyPixel = vec2(pixel) + velocity * resolution; + + bool valid = true; + vec4 history; + vec4 historyMoments; + valid = SampleHistory(pixel, historyPixel, history, historyMoments); + float historyLength = history.a; + vec3 historyRadiance = history.rgb; + + bool success = SampleCatmullRom(pixel, historyUV, history); + historyRadiance = success && valid ? history.rgb : historyRadiance; + + vec3 historyNeighbourhoodMin = mean - std; + vec3 historyNeighbourhoodMax = mean + std; + + vec3 currentNeighbourhoodMin = mean - pushConstants.currentClipFactor * std; + vec3 currentNeighbourhoodMax = mean + pushConstants.currentClipFactor * std; + + // In case of clipping we might also reject the sample. TODO: Investigate + float clipBlend = ClipBoundingBox(historyNeighbourhoodMin, historyNeighbourhoodMax, + historyRadiance, currentRadiance); + float adjClipBlend = clamp(clipBlend, 0.0, pushConstants.historyClipMax); + currentRadiance = clamp(currentRadiance, currentNeighbourhoodMin, currentNeighbourhoodMax); + //historyRadiance = mix(historyRadiance, currentRadiance, adjClipBlend); + + float temporalWeight = (pushConstants.maxHistoryLength - 1.0) / pushConstants.maxHistoryLength; + float factor = mix(0.0, temporalWeight, 1.0 - adjClipBlend); + factor = (historyUV.x < 0.0 || historyUV.y < 0.0 || historyUV.x > 1.0 + || historyUV.y > 1.0) ? 0.0 : factor; + + if (factor < 0.1 || !valid) { + historyLength = 0.0; + } + + factor = min(factor, historyLength / (historyLength + 1.0)); + + vec3 resolve = mix(currentRadiance, historyRadiance, factor); + + imageStore(outAccumImage, pixel, vec4(resolve, historyLength + 1.0)); + //imageStore(outAccumImage, pixel, vec4(vec3(valid ? 1.0 : 0.0), historyLength + 1.0)); + //imageStore(outAccumImage, pixel, vec4(vec3(historyLength / 10.0), historyLength + 1.0)); + +} \ No newline at end of file diff --git a/data/shader/postprocessing.fsh b/data/shader/postprocessing.fsh index 765151f72..db5cdde61 100644 --- a/data/shader/postprocessing.fsh +++ b/data/shader/postprocessing.fsh @@ -15,6 +15,7 @@ layout(set = 3, binding = 4) uniform UniformBuffer { float exposure; float whitePoint; float saturation; + float contrast; float filmGrainStrength; int bloomPasses; float aberrationStrength; @@ -125,7 +126,7 @@ void main() { color *= Uniforms.exposure; #ifdef FILM_GRAIN - color = color + color * Uniforms.filmGrainStrength * (2.0 * random(vec3(texCoord * 1000.0, globalData.time)) - 1.0); + color = color + color * Uniforms.filmGrainStrength * (2.0 * random(vec3(texCoord * 1000.0, globalData[0].time)) - 1.0); color = max(color, vec3(0.0)); #endif @@ -160,6 +161,8 @@ void main() { color = clamp(saturate(color, Uniforms.saturation), vec3(0.0), vec3(1.0)); + color = ((color - 0.5) * max(Uniforms.contrast, 0.0)) + 0.5; + #ifdef VIGNETTE float vignetteFactor = max(1.0 - max(pow(length(fPosition) - Uniforms.vignetteOffset, Uniforms.vignettePower), 0.0) * Uniforms.vignetteStrength, 0.0); diff --git a/data/shader/raytracer/buffers.hsh b/data/shader/raytracer/buffers.hsh index 140f956b6..13c47287c 100644 --- a/data/shader/raytracer/buffers.hsh +++ b/data/shader/raytracer/buffers.hsh @@ -1,5 +1,11 @@ #include +#ifdef AE_BINDLESS +#define MESH_COUNT 8192 +#else +#define MESH_COUNT 1 +#endif + layout(std430, set = 2, binding = 13) buffer ReadAtomic { uint readAtomic[]; }; @@ -30,4 +36,34 @@ layout(std430, set = 2, binding = 18) buffer RayBinOffsets { layout(std430, set = 2, binding = 21) buffer Instances { Instance bvhInstances[]; -}; \ No newline at end of file +}; + +layout(std430, set = 2, binding = 27) buffer InstanceLastMatrices { + mat3x4 instanceLastMatrices[]; +}; + +layout (std430, set = 0, binding = 1) buffer Triangles { + PackedTriangle data[]; +} triangles[MESH_COUNT]; + +#ifndef AE_HARDWARE_RAYTRACING + +layout (std430, set = 0, binding = 2) buffer BVHTriangles { + PackedBVHTriangle data[]; +}bvhTriangles[MESH_COUNT]; + +layout(std430, set = 0, binding = 0) buffer BlasNodes { + PackedBVHNode data[]; +}blasNodes[MESH_COUNT]; + +layout(std430, set = 2, binding = 22) buffer TlasNodes { + PackedBVHNode tlasNodes[]; +}; + +#else + +layout(std430, set = 0, binding = 2) buffer GeometryTriangleOffsets { + uint data[]; +}geometryTriangleOffsets[MESH_COUNT]; + +#endif \ No newline at end of file diff --git a/data/shader/raytracer/bvh.hsh b/data/shader/raytracer/bvh.hsh index 64256b0e3..f49758ff2 100644 --- a/data/shader/raytracer/bvh.hsh +++ b/data/shader/raytracer/bvh.hsh @@ -1,12 +1,10 @@ +#extension GL_EXT_nonuniform_qualifier : require + #ifdef AE_HARDWARE_RAYTRACING #extension GL_EXT_ray_tracing : enable #extension GL_EXT_ray_query : enable layout(set = 2, binding = 23) uniform accelerationStructureEXT topLevelAS; - -layout(std430, set = 2, binding = 22) buffer GeometryTriangleOffsets { - uint geometryTriangleOffsets[]; -}; #endif #include @@ -18,55 +16,19 @@ layout(std430, set = 2, binding = 22) buffer GeometryTriangleOffsets { #define STACK_SIZE 32 #define TLAS_INVALID (STACK_SIZE + 2) -struct PackedBVHNode { - vec4 data0; - vec4 data1; - vec4 data2; - vec4 data3; -}; - -struct BVHNode { - AABB leftAABB; - AABB rightAABB; - int leftPtr; - int rightPtr; -}; - -struct PackedBVHTriangle { - vec4 v0; - vec4 v1; - vec4 v2; -}; - -layout (std430, set = 2, binding = 8) buffer Triangles { - PackedTriangle triangles[]; -}; - -layout (std430, set = 2, binding = 9) buffer BVHTriangles { - PackedBVHTriangle bvhTriangles[]; -}; - #ifndef AE_HARDWARE_RAYTRACING -layout(std430, set = 2, binding = 10) buffer BlasNodes { - PackedBVHNode blasNodes[]; -}; - -layout(std430, set = 2, binding = 22) buffer TlasNodes { - PackedBVHNode tlasNodes[]; -}; - BVHNode UnpackNode(PackedBVHNode compressed) { BVHNode node; node.leftAABB.min = vec3(compressed.data0.x, - compressed.data0.y, compressed.data0.z); + compressed.data0.y, compressed.data0.z); node.leftAABB.max = vec3(compressed.data0.w, - compressed.data1.x, compressed.data1.y); + compressed.data1.x, compressed.data1.y); node.rightAABB.min = vec3(compressed.data1.z, - compressed.data1.w, compressed.data2.x); + compressed.data1.w, compressed.data2.x); node.rightAABB.max = vec3(compressed.data2.y, - compressed.data2.z, compressed.data2.w); + compressed.data2.z, compressed.data2.w); node.leftPtr = floatBitsToInt(compressed.data3.x); node.rightPtr = floatBitsToInt(compressed.data3.y); @@ -74,25 +36,28 @@ BVHNode UnpackNode(PackedBVHNode compressed) { return node; } +// Use local stack if there isn't much register pressure +#ifndef LOCAL_STACK shared int stack[STACK_SIZE][32]; +#endif + +void CheckLeafClosest(inout Ray ray, int meshPtr, int nodePtr, float tmin, float tmax) { -void CheckLeafClosest(inout Ray ray, int nodePtr, float tmin, float tmax) { - int triPtr = ~nodePtr; bool endOfNode = false; - + vec3 sol, v0, v1, v2, n; - + while (!endOfNode) { - v0 = bvhTriangles[triPtr].v0.xyz; - v1 = bvhTriangles[triPtr].v1.xyz; - v2 = bvhTriangles[triPtr].v2.xyz; - endOfNode = bvhTriangles[triPtr].v0.w > 0.0; + v0 = bvhTriangles[nonuniformEXT(meshPtr)].data[triPtr].v0.xyz; + v1 = bvhTriangles[nonuniformEXT(meshPtr)].data[triPtr].v1.xyz; + v2 = bvhTriangles[nonuniformEXT(meshPtr)].data[triPtr].v2.xyz; + endOfNode = bvhTriangles[nonuniformEXT(meshPtr)].data[triPtr].v0.w > 0.0; float d = 0.0; -#ifdef BACKFACE_CULLING + #ifdef BACKFACE_CULLING n = cross(v0 - v1, v0 - v2); d = dot(n, ray.direction); -#endif + #endif bool intersect = IntersectTriangle(ray, v0, v1, v2, sol); if (intersect && sol.x > tmin && sol.x < tmax && d <= 0.0) { if (sol.x < ray.hitDistance) { @@ -103,29 +68,29 @@ void CheckLeafClosest(inout Ray ray, int nodePtr, float tmin, float tmax) { } triPtr++; } - + } -bool CheckLeaf(inout Ray ray, int nodePtr, float tmin, float tmax) { - +bool CheckLeaf(inout Ray ray, int meshPtr, int nodePtr, float tmin, float tmax) { + int triPtr = ~nodePtr; bool endOfNode = false; - + vec3 sol, v0, v1, v2, n; bool hit = false; - + while (!endOfNode && !hit) { - v0 = bvhTriangles[triPtr].v0.xyz; - v1 = bvhTriangles[triPtr].v1.xyz; - v2 = bvhTriangles[triPtr].v2.xyz; - endOfNode = bvhTriangles[triPtr].v0.w > 0.0; + v0 = bvhTriangles[nonuniformEXT(meshPtr)].data[triPtr].v0.xyz; + v1 = bvhTriangles[nonuniformEXT(meshPtr)].data[triPtr].v1.xyz; + v2 = bvhTriangles[nonuniformEXT(meshPtr)].data[triPtr].v2.xyz; + endOfNode = bvhTriangles[nonuniformEXT(meshPtr)].data[triPtr].v0.w > 0.0; float d = 0.0; -#ifdef BACKFACE_CULLING + #ifdef BACKFACE_CULLING n = cross(v0 - v1, v0 - v2); d = dot(n, ray.direction); -#endif + #endif bool intersect = IntersectTriangle(ray, v0, v1, v2, sol); - if (intersect && sol.x > tmin && sol.x < tmax && d <= 0.0) { + if (intersect && sol.x > tmin && sol.x < tmax && d <= 0.0) { hit = true; ray.hitDistance = sol.x; ray.hitID = triPtr; @@ -135,30 +100,31 @@ bool CheckLeaf(inout Ray ray, int nodePtr, float tmin, float tmax) { } return hit; - + } -void CheckLeafClosestTransparency(inout Ray ray, int nodePtr, float tmin, float tmax) { +void CheckLeafClosestTransparency(inout Ray ray, int meshPtr, int materialOffset, +int nodePtr, float tmin, float tmax) { int triPtr = ~nodePtr; bool endOfNode = false; - - vec3 sol, v0, v1, v2, n; - + + vec3 sol, v0, v1, v2, n; + while (!endOfNode) { - Triangle tri = UnpackTriangle(triangles[triPtr]); + Triangle tri = UnpackTriangle(triangles[nonuniformEXT(meshPtr)].data[triPtr]); v0 = tri.v0.xyz; v1 = tri.v1.xyz; v2 = tri.v2.xyz; endOfNode = tri.endOfNode; float d = 0.0; -#ifdef BACKFACE_CULLING + #ifdef BACKFACE_CULLING n = cross(v0 - v1, v0 - v2); d = dot(n, ray.direction); -#endif + #endif bool intersect = IntersectTriangle(ray, v0, v1, v2, sol); if (intersect && sol.x > tmin && sol.x < tmax && d <= 0.0 && sol.x < ray.hitDistance) { - float opacity = GetOpacity(tri, sol.yz, 0); + float opacity = tri.opacity < 0.0 ? GetOpacity(tri, sol.yz, materialOffset, 0) : tri.opacity; if (opacity > 0.0) { ray.hitDistance = sol.x; ray.hitID = triPtr; @@ -170,30 +136,31 @@ void CheckLeafClosestTransparency(inout Ray ray, int nodePtr, float tmin, float } -float CheckLeafTransparency(inout Ray ray, int nodePtr, float tmin, float tmax, float transparency) { +float CheckLeafTransparency(inout Ray ray, int meshPtr, int materialOffset, int nodePtr, +float tmin, float tmax, float transparency) { int triPtr = ~nodePtr; bool endOfNode = false; - + vec3 sol, v0, v1, v2, n; - + while (!endOfNode) { - Triangle tri = UnpackTriangle(triangles[triPtr]); + Triangle tri = UnpackTriangle(triangles[nonuniformEXT(meshPtr)].data[triPtr]); v0 = tri.v0.xyz; v1 = tri.v1.xyz; v2 = tri.v2.xyz; endOfNode = tri.endOfNode; float d = 0.0; -#ifdef BACKFACE_CULLING + #ifdef BACKFACE_CULLING n = cross(v0 - v1, v0 - v2); d = dot(n, ray.direction); -#endif + #endif bool intersect = IntersectTriangle(ray, v0, v1, v2, sol); if (intersect && sol.x > tmin && sol.x < tmax && d <= 0.0) { ray.hitDistance = sol.x; ray.hitID = triPtr; ray.hitInstanceID = ray.currentInstanceID; - transparency *= (1.0 - GetOpacity(tri, sol.yz, 0)); + transparency *= (1.0 - (tri.opacity < 0.0 ? GetOpacity(tri, sol.yz, materialOffset, 0) : tri.opacity)); } triPtr++; } @@ -202,27 +169,29 @@ float CheckLeafTransparency(inout Ray ray, int nodePtr, float tmin, float tmax, } -void CheckInstance(inout Ray ray, inout int nodePtr) { +void CheckInstance(inout Ray ray, inout int meshPtr, inout int materialOffset, inout int nodePtr) { int instancePtr = ~nodePtr; - Instance instance = bvhInstances[instancePtr]; // We don't normalize the direction in case there is a scale in the // matrix. In that case the normalization leads to wrong scales in closest hit distance ray.origin = vec4(ray.origin, 1.0) * instance.inverseMatrix; ray.direction = vec4(ray.direction, 0.0) * instance.inverseMatrix; - ray.inverseDirection = 1.0 / ray.direction; ray.currentInstanceID = instancePtr; - nodePtr = instance.blasOffset; + meshPtr = instance.meshOffset; + materialOffset = instance.materialOffset; + nodePtr = 0; } void HitClosest(inout Ray ray, float tMin, float tMax) { - + uint stackPtr = 1u; int nodePtr = 0; + int meshPtr = 0; + int materialOffset = 0; uint threadID = gl_LocalInvocationIndex; vec3 originalRayOrigin = ray.origin; @@ -231,22 +200,24 @@ void HitClosest(inout Ray ray, float tMin, float tMax) { ray.hitDistance = tMax; if (isnan3(ray.direction)) - return; + return; + + #ifdef LOCAL_STACK + int stack[STACK_SIZE][1]; + threadID = 0; + #endif uint tlasIndex = TLAS_INVALID; - + while (stackPtr != 0u) { if (stackPtr < tlasIndex) { - if (tlasIndex != TLAS_INVALID) { - ray.origin = originalRayOrigin; - ray.direction = originalRayDirection; - ray.inverseDirection = 1.0 / ray.direction; - } + ray.origin = originalRayOrigin; + ray.direction = originalRayDirection; tlasIndex = TLAS_INVALID; if (nodePtr < 0) { tlasIndex = stackPtr; - CheckInstance(ray, nodePtr); + CheckInstance(ray, meshPtr, materialOffset, nodePtr); } else { BVHNode node = UnpackNode(tlasNodes[nodePtr]); @@ -269,11 +240,11 @@ void HitClosest(inout Ray ray, float tMin, float tMax) { } else { if(nodePtr < 0) { - CheckLeafClosest(ray, nodePtr, tMin, ray.hitDistance); + CheckLeafClosest(ray, meshPtr, nodePtr, tMin, ray.hitDistance); nodePtr = stack[--stackPtr][threadID]; } else { - BVHNode node = UnpackNode(blasNodes[nodePtr]); + BVHNode node = UnpackNode(blasNodes[nonuniformEXT(meshPtr)].data[nodePtr]); float hitL = 0.0, hitR = 0.0; bool intersectL = IntersectAABB(ray, @@ -295,14 +266,15 @@ void HitClosest(inout Ray ray, float tMin, float tMax) { ray.origin = originalRayOrigin; ray.direction = originalRayDirection; - ray.inverseDirection = 1.0 / ray.direction; - + } void HitClosestTransparency(inout Ray ray, float tMin, float tMax) { - + uint stackPtr = 1u; int nodePtr = 0; + int meshPtr = 0; + int materialOffset = 0; uint threadID = gl_LocalInvocationIndex; vec3 originalRayOrigin = ray.origin; @@ -311,22 +283,24 @@ void HitClosestTransparency(inout Ray ray, float tMin, float tMax) { ray.hitDistance = tMax; if (isnan3(ray.direction)) - return; + return; + + #ifdef LOCAL_STACK + int stack[STACK_SIZE][1]; + threadID = 0; + #endif uint tlasIndex = TLAS_INVALID; - + while (stackPtr != 0u) { if (stackPtr < tlasIndex) { - if (tlasIndex != TLAS_INVALID) { - ray.origin = originalRayOrigin; - ray.direction = originalRayDirection; - ray.inverseDirection = 1.0 / ray.direction; - } + ray.origin = originalRayOrigin; + ray.direction = originalRayDirection; tlasIndex = TLAS_INVALID; if (nodePtr < 0) { tlasIndex = stackPtr; - CheckInstance(ray, nodePtr); + CheckInstance(ray, meshPtr, materialOffset, nodePtr); } else { BVHNode node = UnpackNode(tlasNodes[nodePtr]); @@ -349,11 +323,11 @@ void HitClosestTransparency(inout Ray ray, float tMin, float tMax) { } else { if(nodePtr < 0) { - CheckLeafClosestTransparency(ray, nodePtr, tMin, ray.hitDistance); + CheckLeafClosestTransparency(ray, meshPtr, materialOffset, nodePtr, tMin, ray.hitDistance); nodePtr = stack[--stackPtr][threadID]; } else { - BVHNode node = UnpackNode(blasNodes[nodePtr]); + BVHNode node = UnpackNode(blasNodes[nonuniformEXT(meshPtr)].data[nodePtr]); float hitL = 0.0, hitR = 0.0; bool intersectL = IntersectAABB(ray, @@ -375,24 +349,30 @@ void HitClosestTransparency(inout Ray ray, float tMin, float tMax) { ray.origin = originalRayOrigin; ray.direction = originalRayDirection; - ray.inverseDirection = 1.0 / ray.direction; - + } bool HitAny(inout Ray ray, float tMin, float tMax) { if (isnan3(ray.direction)) - return false; - + return false; + bool hit = false; uint stackPtr = 1u; int nodePtr = 0; + int meshPtr = 0; + int materialOffset = 0; uint threadID = gl_LocalInvocationIndex; vec3 originalRayOrigin = ray.origin; vec3 originalRayDirection = ray.direction; + #ifdef LOCAL_STACK + int stack[STACK_SIZE][1]; + threadID = 0; + #endif + uint tlasIndex = TLAS_INVALID; while (stackPtr != 0u && !hit) { @@ -400,13 +380,12 @@ bool HitAny(inout Ray ray, float tMin, float tMax) { if (tlasIndex != TLAS_INVALID) { ray.origin = originalRayOrigin; ray.direction = originalRayDirection; - ray.inverseDirection = 1.0 / ray.direction; } tlasIndex = TLAS_INVALID; if (nodePtr < 0) { tlasIndex = stackPtr; - CheckInstance(ray, nodePtr); + CheckInstance(ray, meshPtr, materialOffset, nodePtr); } else { BVHNode node = UnpackNode(tlasNodes[nodePtr]); @@ -416,34 +395,34 @@ bool HitAny(inout Ray ray, float tMin, float tMax) { bool intersectR = IntersectAABB(ray, node.rightAABB, tMin, tMax); - nodePtr = intersectR ? node.rightPtr : node.leftPtr; + nodePtr = intersectL ? node.leftPtr : node.rightPtr; nodePtr = !intersectR && !intersectL ? stack[--stackPtr][threadID] : nodePtr; if (intersectR && intersectL) { - stack[stackPtr++][threadID] = node.leftPtr; + stack[stackPtr++][threadID] = node.rightPtr; } } } else { if(nodePtr < 0) { - if (CheckLeaf(ray, nodePtr, tMin, tMax)) { + if (CheckLeaf(ray, meshPtr, nodePtr, tMin, tMax)) { hit = true; } nodePtr = stack[--stackPtr][threadID]; } else { - BVHNode node = UnpackNode(blasNodes[nodePtr]); + BVHNode node = UnpackNode(blasNodes[nonuniformEXT(meshPtr)].data[nodePtr]); bool intersectL = IntersectAABB(ray, node.leftAABB, tMin, tMax); bool intersectR = IntersectAABB(ray, node.rightAABB, tMin, tMax); - nodePtr = intersectR ? node.rightPtr : node.leftPtr; + nodePtr = intersectL ? node.leftPtr : node.rightPtr; nodePtr = !intersectR && !intersectL ? stack[--stackPtr][threadID] : nodePtr; if (intersectR && intersectL) { - stack[stackPtr++][threadID] = node.leftPtr; + stack[stackPtr++][threadID] = node.rightPtr; } } } @@ -451,78 +430,81 @@ bool HitAny(inout Ray ray, float tMin, float tMax) { ray.origin = originalRayOrigin; ray.direction = originalRayDirection; - ray.inverseDirection = 1.0 / ray.direction; return hit; - + } float HitAnyTransparency(inout Ray ray, float tMin, float tMax) { if (isnan3(ray.direction)) - return 1.0; - + return 1.0; + float transparency = 1.0; uint stackPtr = 1u; int nodePtr = 0; + int meshPtr = 0; + int materialOffset = 0; uint threadID = gl_LocalInvocationIndex; vec3 originalRayOrigin = ray.origin; vec3 originalRayDirection = ray.direction; + #ifdef LOCAL_STACK + int stack[STACK_SIZE][1]; + threadID = 0; + #endif + uint tlasIndex = TLAS_INVALID; while (stackPtr != 0u && transparency > 0.0) { if (stackPtr < tlasIndex) { - if (tlasIndex != TLAS_INVALID) { - ray.origin = originalRayOrigin; - ray.direction = originalRayDirection; - ray.inverseDirection = 1.0 / ray.direction; - } + ray.origin = originalRayOrigin; + ray.direction = originalRayDirection; tlasIndex = TLAS_INVALID; if (nodePtr < 0) { tlasIndex = stackPtr; - CheckInstance(ray, nodePtr); + CheckInstance(ray, meshPtr, materialOffset, nodePtr); } else { BVHNode node = UnpackNode(tlasNodes[nodePtr]); bool intersectL = IntersectAABB(ray, - node.leftAABB, tMin, tMax); + node.leftAABB, tMin, tMax); bool intersectR = IntersectAABB(ray, - node.rightAABB, tMin, tMax); + node.rightAABB, tMin, tMax); - nodePtr = intersectR ? node.rightPtr : node.leftPtr; + nodePtr = intersectL ? node.leftPtr : node.rightPtr; nodePtr = !intersectR && !intersectL ? stack[--stackPtr][threadID] : nodePtr; if (intersectR && intersectL) { - stack[stackPtr++][threadID] = node.leftPtr; + stack[stackPtr++][threadID] = node.rightPtr; } } } else { if(nodePtr < 0) { - transparency *= CheckLeafTransparency(ray, nodePtr, tMin, tMax, transparency); + transparency *= CheckLeafTransparency(ray, meshPtr, materialOffset, nodePtr, tMin, tMax, transparency); if (transparency < 0.000001) { transparency = 0.0; } nodePtr = stack[--stackPtr][threadID]; } else { - BVHNode node = UnpackNode(blasNodes[nodePtr]); + BVHNode node = UnpackNode(blasNodes[nonuniformEXT(meshPtr)].data[nodePtr]); bool intersectL = IntersectAABB(ray, node.leftAABB, tMin, tMax); bool intersectR = IntersectAABB(ray, node.rightAABB, tMin, tMax); - nodePtr = intersectR ? node.rightPtr : node.leftPtr; + nodePtr = intersectL ? node.leftPtr : node.rightPtr; nodePtr = !intersectR && !intersectL ? stack[--stackPtr][threadID] : nodePtr; if (intersectR && intersectL) { - stack[stackPtr++][threadID] = node.leftPtr; + stack[stackPtr++][threadID] = node.rightPtr; } } } @@ -530,7 +512,6 @@ float HitAnyTransparency(inout Ray ray, float tMin, float tMax) { ray.origin = originalRayOrigin; ray.direction = originalRayDirection; - ray.inverseDirection = 1.0 / ray.direction; return transparency; @@ -543,8 +524,8 @@ void HitClosest(inout Ray ray, float tMin, float tMax) { ray.hitDistance = tMax; rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, topLevelAS, gl_RayFlagsNoneEXT, 0xFF, - ray.origin, tMin, ray.direction, tMax); + rayQueryInitializeEXT(rayQuery, topLevelAS, gl_RayFlagsNoneEXT, 0xFF, + ray.origin, tMin, ray.direction, tMax); // Start traversal: return false if traversal is complete while(rayQueryProceedEXT(rayQuery)) { @@ -558,21 +539,21 @@ void HitClosest(inout Ray ray, float tMin, float tMax) { ray.hitDistance = rayQueryGetIntersectionTEXT(rayQuery, true); ray.hitInstanceID = rayQueryGetIntersectionInstanceIdEXT(rayQuery, true); - int geometryOffset = rayQueryGetIntersectionGeometryIndexEXT(rayQuery, true); - int idx = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, true) + geometryOffset; + int geometryOffset = rayQueryGetIntersectionGeometryIndexEXT(rayQuery, false); + int meshIdx = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, false); - int triangleOffset = int(geometryTriangleOffsets[idx]); + int triangleOffset = int(geometryTriangleOffsets[nonuniformEXT(meshIdx)].data[geometryOffset]); ray.hitID = rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true) + triangleOffset; } } bool HitAny(inout Ray ray, float tMin, float tMax) { - + ray.hitDistance = tMax; rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, topLevelAS, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, - ray.origin, tMin, ray.direction, tMax); + rayQueryInitializeEXT(rayQuery, topLevelAS, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, + ray.origin, tMin, ray.direction, tMax); // Start traversal: return false if traversal is complete while(rayQueryProceedEXT(rayQuery)) { @@ -596,8 +577,8 @@ void HitClosestTransparency(inout Ray ray, float tMin, float tMax) { ray.hitDistance = tMax; rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, topLevelAS, gl_RayFlagsNoneEXT, 0xFF, - ray.origin, tMin, ray.direction, tMax); + rayQueryInitializeEXT(rayQuery, topLevelAS, gl_RayFlagsNoneEXT, 0xFF, + ray.origin, tMin, ray.direction, tMax); // Start traversal: return false if traversal is complete bool proceed = true; @@ -605,21 +586,24 @@ void HitClosestTransparency(inout Ray ray, float tMin, float tMax) { if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCandidateIntersectionTriangleEXT) { int geometryOffset = rayQueryGetIntersectionGeometryIndexEXT(rayQuery, false); - int idx = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, false) + geometryOffset; + int meshIdx = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, false); - int triangleOffset = int(geometryTriangleOffsets[idx]); + int triangleOffset = int(geometryTriangleOffsets[nonuniformEXT(meshIdx)].data[geometryOffset]); int hitID = rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, false) + triangleOffset; + int hitInstanceID = rayQueryGetIntersectionInstanceIdEXT(rayQuery, false); + int materialOffset = bvhInstances[hitInstanceID].materialOffset; + vec2 barrycentric = rayQueryGetIntersectionBarycentricsEXT(rayQuery, false); - Triangle tri = UnpackTriangle(triangles[hitID]); + Triangle tri = UnpackTriangle(triangles[nonuniformEXT(meshIdx)].data[hitID]); - if (GetOpacity(tri, barrycentric, 0) > 0.0) { + if (GetOpacity(tri, barrycentric, materialOffset, 0) > 0.0) { rayQueryConfirmIntersectionEXT(rayQuery); } } - + } if (rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT) { @@ -627,37 +611,40 @@ void HitClosestTransparency(inout Ray ray, float tMin, float tMax) { ray.hitInstanceID = rayQueryGetIntersectionInstanceIdEXT(rayQuery, true); int geometryOffset = rayQueryGetIntersectionGeometryIndexEXT(rayQuery, true); - int idx = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, true) + geometryOffset; + int meshIdx = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, true); - int triangleOffset = int(geometryTriangleOffsets[idx]); + int triangleOffset = int(geometryTriangleOffsets[nonuniformEXT(meshIdx)].data[geometryOffset]); ray.hitID = rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true) + triangleOffset; } } float HitAnyTransparency(inout Ray ray, float tMin, float tMax) { - + float transparency = 1.0; ray.hitDistance = tMax; rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, topLevelAS, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, - ray.origin, tMin, ray.direction, tMax); + rayQueryInitializeEXT(rayQuery, topLevelAS, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, + ray.origin, tMin, ray.direction, tMax); while(rayQueryProceedEXT(rayQuery)) { if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCandidateIntersectionTriangleEXT) { - int geometryOffset = rayQueryGetIntersectionGeometryIndexEXT(rayQuery, false); - int idx = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, false) + geometryOffset; + int geometryOffset = rayQueryGetIntersectionGeometryIndexEXT(rayQuery, false); + int meshIdx = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, false); - int triangleOffset = int(geometryTriangleOffsets[idx]); + int triangleOffset = int(geometryTriangleOffsets[nonuniformEXT(meshIdx)].data[geometryOffset]); int hitID = rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, false) + triangleOffset; + int hitInstanceID = rayQueryGetIntersectionInstanceIdEXT(rayQuery, false); + int materialOffset = bvhInstances[hitInstanceID].materialOffset; + vec2 barrycentric = rayQueryGetIntersectionBarycentricsEXT(rayQuery, false); - Triangle tri = UnpackTriangle(triangles[hitID]); - transparency *= (1.0 - GetOpacity(tri, barrycentric, 0)); + Triangle tri = UnpackTriangle(triangles[nonuniformEXT(meshIdx)].data[hitID]); + transparency *= (1.0 - GetOpacity(tri, barrycentric, materialOffset, 0)); if (transparency < 0.001) { rayQueryTerminateEXT(rayQuery); @@ -665,7 +652,7 @@ float HitAnyTransparency(inout Ray ray, float tMin, float tMax) { } } - + } if (rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT) { diff --git a/data/shader/raytracer/common.hsh b/data/shader/raytracer/common.hsh index 8de01147e..c5f82175f 100644 --- a/data/shader/raytracer/common.hsh +++ b/data/shader/raytracer/common.hsh @@ -35,6 +35,7 @@ Triangle UnpackTriangle(PackedTriangle triangle) { tri.materialIndex = floatBitsToInt(triangle.d0.w); tri.endOfNode = triangle.d1.z > 0.0 ? true : false; + tri.opacity = triangle.d2.w; return tri; } @@ -49,8 +50,6 @@ Ray UnpackRay(PackedRay compressed) { ray.hitID = floatBitsToInt(compressed.hit.y); ray.hitDistance = compressed.hit.x; ray.hitInstanceID = floatBitsToInt(compressed.hit.z); - - ray.inverseDirection = 1.0 / ray.direction; return ray; } diff --git a/data/shader/raytracer/direct.hsh b/data/shader/raytracer/direct.hsh index f407788d7..ec3e32a43 100644 --- a/data/shader/raytracer/direct.hsh +++ b/data/shader/raytracer/direct.hsh @@ -1,3 +1,5 @@ +#extension GL_EXT_nonuniform_qualifier : require + #include #include #include @@ -58,7 +60,7 @@ void SampleLight(Light light, inout Surface surface, float seed0, float seed1, if (light.type == uint(TRIANGLE_LIGHT)) { Instance instance = bvhInstances[light.instanceIdx]; - Triangle tri = UnpackTriangle(triangles[light.triangleIdx + instance.triangleOffset]); + Triangle tri = UnpackTriangle(triangles[nonuniformEXT(instance.meshOffset)].data[light.triangleIdx]); TransformTriangle(tri, instance); lightSample = SampleTriangleLight(light, tri, surface, r0, r1); } diff --git a/data/shader/raytracer/intersections.hsh b/data/shader/raytracer/intersections.hsh index c35cd52ea..c5c6e5b61 100644 --- a/data/shader/raytracer/intersections.hsh +++ b/data/shader/raytracer/intersections.hsh @@ -2,8 +2,8 @@ bool IntersectAABB(Ray ray, AABB aabb, float tmin, float tmax) { - vec3 t0 = (aabb.min - ray.origin) * ray.inverseDirection; - vec3 t1 = (aabb.max - ray.origin) * ray.inverseDirection; + vec3 t0 = (aabb.min - ray.origin) / ray.direction; + vec3 t1 = (aabb.max - ray.origin) / ray.direction; vec3 tsmall = min(t0, t1); vec3 tbig = max(t0, t1); @@ -17,8 +17,8 @@ bool IntersectAABB(Ray ray, AABB aabb, float tmin, float tmax) { bool IntersectAABB(Ray ray, AABB aabb, float tmin, float tmax, out float dist) { - vec3 t0 = (aabb.min - ray.origin) * ray.inverseDirection; - vec3 t1 = (aabb.max - ray.origin) * ray.inverseDirection; + vec3 t0 = (aabb.min - ray.origin) / ray.direction; + vec3 t1 = (aabb.max - ray.origin) / ray.direction; vec3 tsmall = min(t0, t1); vec3 tbig = max(t0, t1); diff --git a/data/shader/raytracer/structures.hsh b/data/shader/raytracer/structures.hsh index d8fcb2fb6..7124bcc5a 100644 --- a/data/shader/raytracer/structures.hsh +++ b/data/shader/raytracer/structures.hsh @@ -21,14 +21,12 @@ struct Ray { vec3 origin; vec3 direction; - vec3 inverseDirection; int hitID; float hitDistance; int hitInstanceID; int currentInstanceID; - }; struct RayPayload { @@ -77,6 +75,7 @@ struct Triangle { int materialIndex; bool endOfNode; + float opacity; }; @@ -103,7 +102,9 @@ struct Texture { }; struct RaytraceMaterial { - + + int ID; + float baseR; float baseG; float baseB; @@ -124,13 +125,14 @@ struct RaytraceMaterial { int invertUVs; int twoSided; + int useVertexColors; - Texture baseColorTexture; - Texture opacityTexture; - Texture normalTexture; - Texture roughnessTexture; - Texture metalnessTexture; - Texture aoTexture; + int baseColorTexture; + int opacityTexture; + int normalTexture; + int roughnessTexture; + int metalnessTexture; + int aoTexture; }; @@ -166,13 +168,33 @@ struct LightSample { struct Instance { mat3x4 inverseMatrix; - int blasOffset; - int triangleOffset; + int meshOffset; + int materialOffset; - int padding0; + int nextInstance; int padding1; }; +struct PackedBVHNode { + vec4 data0; + vec4 data1; + vec4 data2; + vec4 data3; +}; + +struct BVHNode { + AABB leftAABB; + AABB rightAABB; + int leftPtr; + int rightPtr; +}; + +struct PackedBVHTriangle { + vec4 v0; + vec4 v1; + vec4 v2; +}; + layout(push_constant) uniform constants { int lightCount; uint rayBufferOffset; diff --git a/data/shader/raytracer/surface.hsh b/data/shader/raytracer/surface.hsh index ac9d8557d..80931e7d6 100644 --- a/data/shader/raytracer/surface.hsh +++ b/data/shader/raytracer/surface.hsh @@ -1,3 +1,5 @@ +#extension GL_EXT_nonuniform_qualifier : require + #include #include #include @@ -5,9 +7,23 @@ #include <../brdf/surface.hsh> -Material GetTriangleMaterial(Triangle tri, out RaytraceMaterial rayMat) { +Instance GetInstance(Ray ray) { + + return bvhInstances[ray.hitInstanceID]; + +} + +Triangle GetTriangle(Ray ray, Instance instance) { + + return UnpackTriangle(triangles[nonuniformEXT(instance.meshOffset)].data[ray.hitID]); + +} + +Material GetTriangleMaterial(Triangle tri, int materialOffset, out RaytraceMaterial rayMat) { Material mat; - rayMat = materials[tri.materialIndex]; + rayMat = materials[tri.materialIndex + materialOffset]; + + mat.ID = rayMat.ID; mat.baseColor = vec3(rayMat.baseR, rayMat.baseG, rayMat.baseB); mat.emissiveColor = vec3(rayMat.emissR, rayMat.emissG, rayMat.emissB); @@ -20,7 +36,7 @@ Material GetTriangleMaterial(Triangle tri, out RaytraceMaterial rayMat) { mat.ao = rayMat.ao; mat.reflectance = rayMat.reflectance; - + mat.normalScale = rayMat.normalScale; mat.displacementScale = 0.0; @@ -44,14 +60,14 @@ void TransformTriangle(inout Triangle tri, Instance instance) { } -Surface GetSurfaceParameters(Triangle tri, Ray ray, bool useNormalMaps, out bool backfaceHit, int textureLevel) { +Surface GetSurfaceParameters(Instance instance, Triangle tri, Ray ray, + bool useNormalMaps, out bool backfaceHit, int textureLevel) { + + TransformTriangle(tri, instance); Surface surface; RaytraceMaterial rayMat; - Material mat = GetTriangleMaterial(tri, rayMat); - - Instance instance = bvhInstances[ray.hitInstanceID]; - TransformTriangle(tri, instance); + Material mat = GetTriangleMaterial(tri, instance.materialOffset, rayMat); // The ray doesn't provide us with the barrycentric coordinates // so we intersect again. @@ -64,15 +80,15 @@ Surface GetSurfaceParameters(Triangle tri, Ray ray, bool useNormalMaps, out bool float s = barrycentric.x; float t = barrycentric.y; float r = 1.0 - s - t; - + // Interpolate normal by using barrycentric coordinates vec3 position = ray.origin + dist * ray.direction; vec2 texCoord = r * tri.uv0 + s * tri.uv1 + t * tri.uv2; vec3 normal = normalize(r * tri.n0 + s * tri.n1 + t * tri.n2); vec4 vertexColor = r * tri.color0 + s * tri.color1 + t * tri.color2; - + texCoord = rayMat.invertUVs > 0 ? vec2(texCoord.x, 1.0 - texCoord.y) : texCoord; - + // Produces some problems in the bottom left corner of the Sponza scene, // but fixes the cube. Should work in theory. vec3 triangleNormal = normalize(cross(tri.v0 - tri.v1, tri.v0 - tri.v2)); @@ -81,24 +97,24 @@ Surface GetSurfaceParameters(Triangle tri, Ray ray, bool useNormalMaps, out bool triangleNormal *= flipNormal ? -1.0 : 1.0; vec3 geometryNormal = triangleNormal; - + int level = textureLevel; - // mat.opacity *= vertexColor.a; - mat.baseColor *= vertexColor.rgb; - mat.baseColor *= SampleBaseColorBilinear(GetTextureLevel(rayMat.baseColorTexture, level), texCoord); - mat.opacity *= SampleOpacityBilinear(GetTextureLevel(rayMat.opacityTexture, level), texCoord); - - mat.roughness *= SampleRoughnessBilinear(GetTextureLevel(rayMat.roughnessTexture, level), texCoord); - mat.metalness *= SampleMetalnessBilinear(GetTextureLevel(rayMat.metalnessTexture, level), texCoord); - mat.ao *= SampleAoBilinear(GetTextureLevel(rayMat.aoTexture, level), texCoord); - + //mat.opacity *= vertexColor.a; + mat.baseColor *= rayMat.useVertexColors > 0 ? vertexColor.rgb : vec3(1.0); + mat.baseColor *= SampleBaseColorBilinear(rayMat.baseColorTexture, float(level), texCoord); + mat.opacity *= SampleOpacityBilinear(rayMat.opacityTexture, float(level), texCoord); + + mat.roughness *= SampleRoughnessBilinear(rayMat.roughnessTexture, float(level), texCoord); + mat.metalness *= SampleMetalnessBilinear(rayMat.metalnessTexture, float(level), texCoord); + mat.ao *= SampleAoBilinear(rayMat.aoTexture, float(level), texCoord); + // Account for changing texture coord direction vec3 bitangent = rayMat.invertUVs > 0 ? tri.bt : -tri.bt; mat3 TBN = mat3(tri.t, bitangent, normal); - + // Sample normal map - if (rayMat.normalTexture.valid >= 0 && useNormalMaps) { - vec3 texNormal = 2.0 * SampleNormalBilinear(GetTextureLevel(rayMat.normalTexture, level), texCoord) - 1.0; + if (rayMat.normalTexture >= 0 && useNormalMaps) { + vec3 texNormal = 2.0 * SampleNormalBilinear(rayMat.normalTexture, float(level), texCoord) - 1.0; texNormal = TBN * texNormal; // Make sure we don't divide by zero // The normalize function crashes the program in case of vec3(0.0) @@ -107,7 +123,7 @@ Surface GetSurfaceParameters(Triangle tri, Ray ray, bool useNormalMaps, out bool } normal = dot(normal, triangleNormal) < 0.0 ? -normal : normal; - + surface.P = position; surface.V = -ray.direction; surface.N = normalize(normal); @@ -116,29 +132,29 @@ Surface GetSurfaceParameters(Triangle tri, Ray ray, bool useNormalMaps, out bool surface.geometryNormal = geometryNormal; surface.F0 = mix(vec3(0.04), surface.material.baseColor, - surface.material.metalness); + surface.material.metalness); surface.F90 = 1.0; return surface; } -Surface GetSurfaceParameters(Triangle tri, Ray ray, bool useNormalMaps, int textureLevel) { +Surface GetSurfaceParameters(Instance instance, Triangle tri, Ray ray, bool useNormalMaps, int textureLevel) { bool normalFlipped; - return GetSurfaceParameters(tri, ray, useNormalMaps, normalFlipped, textureLevel); + return GetSurfaceParameters(instance, tri, ray, useNormalMaps, normalFlipped, textureLevel); } -float GetOpacity(Triangle tri, vec2 barrycentric, int textureLevel) { +float GetOpacity(Triangle tri, vec2 barrycentric, int materialOffset, int textureLevel) { float s = barrycentric.x; float t = barrycentric.y; float r = 1.0 - s - t; - RaytraceMaterial rayMat = materials[tri.materialIndex]; + RaytraceMaterial rayMat = materials[tri.materialIndex + materialOffset]; float vertexAlpha = r * tri.color0.a + s * tri.color1.a + t * tri.color2.a; - vec2 texCoord = r * tri.uv0 + s * tri.uv1 + t * tri.uv2; + vec2 texCoord = r * tri.uv0 + s * tri.uv1 + t * tri.uv2; texCoord = rayMat.invertUVs > 0 ? vec2(texCoord.x, 1.0 - texCoord.y) : texCoord; - return SampleOpacityBilinear(GetTextureLevel(rayMat.opacityTexture, textureLevel), texCoord) + return SampleOpacityBilinear(rayMat.opacityTexture, float(textureLevel), texCoord) * rayMat.opacity; } \ No newline at end of file diff --git a/data/shader/raytracer/texture.hsh b/data/shader/raytracer/texture.hsh index 0aee94927..16e7b7ffd 100644 --- a/data/shader/raytracer/texture.hsh +++ b/data/shader/raytracer/texture.hsh @@ -1,125 +1,63 @@ +#extension GL_EXT_nonuniform_qualifier : require + #include +#include <../globals.hsh> #include <../common/sample.hsh> -layout (set = 2, binding = 0) uniform sampler2DArray baseColorTextures; -layout (set = 2, binding = 1) uniform sampler2DArray opacityTextures; -layout (set = 2, binding = 2) uniform sampler2DArray normalTextures; -layout (set = 2, binding = 3) uniform sampler2DArray roughnessTextures; -layout (set = 2, binding = 4) uniform sampler2DArray metalnessTextures; -layout (set = 2, binding = 5) uniform sampler2DArray aoTextures; layout (set = 2, binding = 6) uniform samplerCube environmentMap; -vec2 GetTexCoord(vec2 texCoord, TextureLevel level, vec2 size) { - - return (vec2(mod(texCoord.x, float(level.width) - 0.5), - mod(texCoord.y, float(level.height) - 0.5)) + - vec2(level.x, level.y)) / size; - -} - -TextureLevel GetTextureLevel(Texture texture, int level) { - if (level == 0) return texture.level0; - else if (level == 1) return texture.level1; - else if (level == 2) return texture.level2; - else if (level == 3) return texture.level3; - else if (level == 4) return texture.level4; - else return texture.level4; -} +vec3 SampleBaseColorBilinear(int textureId, float mip, vec2 texCoord) { -vec3 SampleBaseColorBilinear(TextureLevel level, vec2 texCoord) { - - if (level.valid < 0) + if (textureId < 0) return vec3(1.0); - - texCoord *= vec2(level.width - 1, level.height - 1); - texCoord += vec2(0.5); - - vec2 size = vec2(textureSize(baseColorTextures, 0)); - texCoord = GetTexCoord(texCoord, level, size); - - return textureLod(baseColorTextures, - vec3(texCoord, float(level.layer)), 0).rgb; - + + return textureLod(sampler2D(bindlessTextures[nonuniformEXT(textureId)], bindlessSampler), texCoord, mip).rgb; + } -float SampleOpacityBilinear(TextureLevel level, vec2 texCoord) { - - if (level.valid < 0) +float SampleOpacityBilinear(int textureId, float mip, vec2 texCoord) { + + if (textureId < 0) return 1.0; - - texCoord *= vec2(level.width - 1, level.height - 1); - texCoord += vec2(0.5); - - vec2 size = vec2(textureSize(opacityTextures, 0)); - texCoord = GetTexCoord(texCoord, level, size); - - return textureLod(opacityTextures, - vec3(texCoord, float(level.layer)), 0).r; - + + return textureLod(sampler2D(bindlessTextures[nonuniformEXT(textureId)], bindlessSampler), texCoord, mip).r; + } -vec3 SampleNormalBilinear(TextureLevel level, vec2 texCoord) { - - if (level.valid < 0) +vec3 SampleNormalBilinear(int textureId, float mip, vec2 texCoord) { + + if (textureId < 0) return vec3(1.0); - - texCoord *= vec2(level.width - 1, level.height - 1); - texCoord += vec2(0.5); - - vec2 size = vec2(textureSize(normalTextures, 0)); - texCoord = GetTexCoord(texCoord, level, size); - - return textureLod(normalTextures, - vec3(texCoord, float(level.layer)), 0).rgb; - + + return textureLod(sampler2D(bindlessTextures[nonuniformEXT(textureId)], bindlessSampler), texCoord, mip).rgb; + } -float SampleRoughnessBilinear(TextureLevel level, vec2 texCoord) { - - if (level.valid < 0) +float SampleRoughnessBilinear(int textureId, float mip, vec2 texCoord) { + + if (textureId < 0) return 1.0; - - texCoord *= vec2(level.width - 1, level.height - 1); - texCoord += vec2(0.5); - - vec2 size = vec2(textureSize(roughnessTextures, 0)); - texCoord = GetTexCoord(texCoord, level, size); - - return textureLod(roughnessTextures, - vec3(texCoord, float(level.layer)), 0).r; - + + return textureLod(sampler2D(bindlessTextures[nonuniformEXT(textureId)], bindlessSampler), texCoord, mip).r; + } -float SampleMetalnessBilinear(TextureLevel level, vec2 texCoord) { - - if (level.valid < 0) +float SampleMetalnessBilinear(int textureId, float mip, vec2 texCoord) { + + if (textureId < 0) return 1.0; - - texCoord *= vec2(level.width - 1, level.height - 1); - texCoord += vec2(0.5); - - vec2 size = vec2(textureSize(metalnessTextures, 0)); - texCoord = GetTexCoord(texCoord, level, size); - - return textureLod(metalnessTextures, - vec3(texCoord, float(level.layer)), 0).r; - + + return textureLod(sampler2D(bindlessTextures[nonuniformEXT(textureId)], bindlessSampler), texCoord, mip).r; + } -float SampleAoBilinear(TextureLevel level, vec2 texCoord) { - - if (level.valid < 0) +float SampleAoBilinear(int textureId, float mip, vec2 texCoord) { + + if (textureId < 0) return 1.0; - - texCoord *= vec2(level.width - 1, level.height - 1); - texCoord += vec2(0.5); - - vec2 size = vec2(textureSize(aoTextures, 0)); - texCoord = GetTexCoord(texCoord, level, size); - - return textureLod(aoTextures, - vec3(texCoord, float(level.layer)), 0).r; - + + return textureLod(sampler2D(bindlessTextures[nonuniformEXT(textureId)], bindlessSampler), texCoord, mip).r; + } vec4 SampleEnvironmentMap(vec3 v) { diff --git a/data/shader/raytracer/traceClosest.csh b/data/shader/raytracer/traceClosest.csh index 7388a1d63..5d3fd430c 100644 --- a/data/shader/raytracer/traceClosest.csh +++ b/data/shader/raytracer/traceClosest.csh @@ -1,3 +1,5 @@ +#define LOCAL_STACK + #include #include #include diff --git a/data/shader/reflection/rtreflection.csh b/data/shader/reflection/rtreflection.csh index 205b4da56..799965467 100644 --- a/data/shader/reflection/rtreflection.csh +++ b/data/shader/reflection/rtreflection.csh @@ -72,9 +72,9 @@ void main() { vec2 recontructTexCoord = (2.0 * vec2(pixel) + offset + vec2(0.5)) / (2.0 * vec2(resolution)); vec3 viewPos = ConvertDepthToViewSpace(depth, recontructTexCoord); - vec3 worldPos = vec3(globalData.ivMatrix * vec4(viewPos, 1.0)); - vec3 viewVec = vec3(globalData.ivMatrix * vec4(viewPos, 0.0)); - vec3 worldNorm = normalize(vec3(globalData.ivMatrix * vec4(DecodeNormal(textureLod(normalTexture, texCoord, 0).rg), 0.0))); + vec3 worldPos = vec3(globalData[0].ivMatrix * vec4(viewPos, 1.0)); + vec3 viewVec = vec3(globalData[0].ivMatrix * vec4(viewPos, 0.0)); + vec3 worldNorm = normalize(vec3(globalData[0].ivMatrix * vec4(DecodeNormal(textureLod(normalTexture, texCoord, 0).rg), 0.0))); int sampleIdx = int(uniforms.frameSeed); vec2 blueNoiseVec = vec2( @@ -119,7 +119,6 @@ void main() { continue; } - ray.inverseDirection = 1.0 / ray.direction; ray.origin = worldPos + ray.direction * EPSILON + worldNorm * EPSILON; ray.hitID = -1; @@ -168,9 +167,11 @@ vec3 EvaluateHit(inout Ray ray) { } // Unpack the compressed triangle and extract surface parameters - Triangle tri = UnpackTriangle(triangles[ray.hitID]); - bool backfaceHit; - Surface surface = GetSurfaceParameters(tri, ray, false, backfaceHit, uniforms.textureLevel); + Instance instance = GetInstance(ray); + Triangle tri = GetTriangle(ray, instance); + + bool backfaceHit; + Surface surface = GetSurfaceParameters(instance, tri, ray, false, backfaceHit, uniforms.textureLevel); radiance += surface.material.emissiveColor; @@ -230,7 +231,6 @@ float CheckVisibility(Surface surface, float lightDistance) { Ray ray; ray.direction = surface.L; ray.origin = surface.P + surface.N * EPSILON; - ray.inverseDirection = 1.0 / ray.direction; return HitAnyTransparency(ray, 0.0, lightDistance - 2.0 * EPSILON); } else { diff --git a/data/shader/reflection/temporal.csh b/data/shader/reflection/temporal.csh index 80926fe00..5480747f3 100644 --- a/data/shader/reflection/temporal.csh +++ b/data/shader/reflection/temporal.csh @@ -149,7 +149,79 @@ float ClipBoundingBox(vec3 boxMin, vec3 boxMax, vec3 history, vec3 current) { } -vec4 SampleCatmullRom(vec2 uv) { +bool SampleHistory(ivec2 pixel, vec2 historyPixel, out vec4 history, out vec4 historyMoments) { + + history = vec4(0.0); + historyMoments = vec4(0.0); + + float totalWeight = 0.0; + float x = fract(historyPixel.x); + float y = fract(historyPixel.y); + + float weights[4] = { (1 - x) * (1 - y), x * (1 - y), (1 - x) * y, x * y }; + + vec3 normal = DecodeNormal(texelFetch(normalTexture, pixel, 0).rg); + float depth = texelFetch(depthTexture, pixel, 0).r; + + float linearDepth = ConvertDepthToViewSpaceDepth(depth); + + // Calculate confidence over 2x2 bilinear neighborhood + for (int i = 0; i < 4; i++) { + ivec2 offsetPixel = ivec2(historyPixel) + pixelOffsets[i]; + float confidence = 1.0; + + vec3 historyNormal = DecodeNormal(texelFetch(historyNormalTexture, offsetPixel, 0).rg); + confidence *= pow(abs(dot(historyNormal, normal)), 16.0); + + float historyDepth = texelFetch(historyDepthTexture, offsetPixel, 0).r; + float historyLinearDepth = ConvertDepthToViewSpaceDepth(historyDepth); + confidence *= min(1.0 , exp(-abs(linearDepth - historyLinearDepth))); + + if (confidence > 0.2) { + totalWeight += weights[i]; + history += texelFetch(historyTexture, offsetPixel, 0) * weights[i]; + historyMoments += texelFetch(historyMomentsTexture, offsetPixel, 0) * weights[i]; + } + } + + if (totalWeight > 0.0) { + history /= totalWeight; + historyMoments /= totalWeight; + return true; + } + + for (int i = 0; i < 9; i++) { + ivec2 offsetPixel = ivec2(historyPixel) + offsets[i]; + float confidence = 1.0; + + vec3 historyNormal = DecodeNormal(texelFetch(historyNormalTexture, offsetPixel, 0).rg); + confidence *= pow(abs(dot(historyNormal, normal)), 16.0); + + float historyDepth = texelFetch(historyDepthTexture, offsetPixel, 0).r; + float historyLinearDepth = ConvertDepthToViewSpaceDepth(historyDepth); + confidence *= min(1.0 , exp(-abs(linearDepth - historyLinearDepth))); + + if (confidence > 0.2) { + totalWeight += 1.0; + history += texelFetch(historyTexture, offsetPixel, 0); + historyMoments += texelFetch(historyMomentsTexture, offsetPixel, 0); + } + } + + if (totalWeight > 0.0) { + history /= totalWeight; + historyMoments /= totalWeight; + return true; + } + + history = vec4(0.0); + historyMoments = vec4(0.0); + + return false; + +} + +void SampleCatmullRom(ivec2 pixel, vec2 uv, out vec4 history) { // http://advances.realtimerendering.com/s2016/Filmic%20SMAA%20v7.pptx // Credit: Jorge Jimenez (SIGGRAPH 2016) @@ -173,32 +245,39 @@ vec4 SampleCatmullRom(vec2 uv) { vec2 tc12 = (center + w2 / w12) * invResolution; vec2 tc3 = (center + 2.0) * invResolution; - vec2 uv0 = clamp(vec2(tc12.x, tc0.y), vec2(0.0), vec2(1.0)); - vec2 uv1 = clamp(vec2(tc0.x, tc12.y), vec2(0.0), vec2(1.0)); - vec2 uv2 = clamp(vec2(tc12.x, tc12.y), vec2(0.0), vec2(1.0)); - vec2 uv3 = clamp(vec2(tc3.x, tc12.y), vec2(0.0), vec2(1.0)); - vec2 uv4 = clamp(vec2(tc12.x, tc3.y), vec2(0.0), vec2(1.0)); + vec2 uv0 = clamp(vec2(tc12.x, tc0.y), vec2(0.0), vec2(1.0)) * resolution - vec2(0.5); + vec2 uv1 = clamp(vec2(tc0.x, tc12.y), vec2(0.0), vec2(1.0)) * resolution - vec2(0.5); + vec2 uv2 = clamp(vec2(tc12.x, tc12.y), vec2(0.0), vec2(1.0)) * resolution - vec2(0.5); + vec2 uv3 = clamp(vec2(tc3.x, tc12.y), vec2(0.0), vec2(1.0)) * resolution - vec2(0.5); + vec2 uv4 = clamp(vec2(tc12.x, tc3.y), vec2(0.0), vec2(1.0)) * resolution - vec2(0.5); float weight0 = w12.x * w0.y; float weight1 = w0.x * w12.y; float weight2 = w12.x * w12.y; float weight3 = w3.x * w12.y; float weight4 = w12.x * w3.y; + + vec4 sample0, sample1, sample2, sample3, sample4; + vec4 moments0, moments1, moments2, moments3, moments4; + + SampleHistory(pixel, uv0, sample0, moments0); + SampleHistory(pixel, uv1, sample1, moments1); + SampleHistory(pixel, uv2, sample2, moments2); + SampleHistory(pixel, uv3, sample3, moments3); + SampleHistory(pixel, uv4, sample4, moments4); - vec4 sample0 = texture(historyTexture, uv0) * weight0; - vec4 sample1 = texture(historyTexture, uv1) * weight1; - vec4 sample2 = texture(historyTexture, uv2) * weight2; - vec4 sample3 = texture(historyTexture, uv3) * weight3; - vec4 sample4 = texture(historyTexture, uv4) * weight4; + sample0 *= weight0; + sample1 *= weight1; + sample2 *= weight2; + sample3 *= weight3; + sample4 *= weight4; float totalWeight = weight0 + weight1 + weight2 + weight3 + weight4; - vec4 totalSample = sample0 + sample1 + + history = sample0 + sample1 + sample2 + sample3 + sample4; - return totalSample / totalWeight; - } void ComputeVarianceMinMax(out vec3 mean, out vec3 std) { @@ -239,58 +318,6 @@ void ComputeVarianceMinMax(out vec3 mean, out vec3 std) { std = sqrt(max((m2 / totalWeight) - (mean * mean), 0.0)); } -bool SampleHistory(ivec2 pixel, vec2 historyPixel, out vec4 history, out vec4 historyMoments) { - - history = vec4(0.0); - historyMoments = vec4(0.0); - - float totalWeight = 0.0; - float x = fract(historyPixel.x); - float y = fract(historyPixel.y); - - float weights[4] = { (1 - x) * (1 - y), x * (1 - y), (1 - x) * y, x * y }; - - uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; - vec3 normal = DecodeNormal(texelFetch(normalTexture, pixel, 0).rg); - float depth = texelFetch(depthTexture, pixel, 0).r; - - float linearDepth = ConvertDepthToViewSpaceDepth(depth); - - // Calculate confidence over 2x2 bilinear neighborhood - for (int i = 0; i < 4; i++) { - ivec2 offsetPixel = ivec2(historyPixel) + pixelOffsets[i]; - float confidence = 1.0; - - uint historyMaterialIdx = texelFetch(historyMaterialIdxTexture, offsetPixel, 0).r; - confidence *= historyMaterialIdx != materialIdx ? 0.0 : 1.0; - - vec3 historyNormal = DecodeNormal(texelFetch(historyNormalTexture, offsetPixel, 0).rg); - confidence *= pow(abs(dot(historyNormal, normal)), 2.0); - - float historyDepth = texelFetch(historyDepthTexture, offsetPixel, 0).r; - float historyLinearDepth = ConvertDepthToViewSpaceDepth(historyDepth); - confidence *= min(1.0 , exp(-abs(linearDepth - historyLinearDepth))); - - if (confidence > 0.1) { - totalWeight += weights[i]; - history += texelFetch(historyTexture, offsetPixel, 0) * weights[i]; - historyMoments += texelFetch(historyMomentsTexture, offsetPixel, 0) * weights[i]; - } - } - - if (totalWeight > 0.0) { - history /= totalWeight; - historyMoments /= totalWeight; - return true; - } - - history = vec4(0.0); - historyMoments = vec4(0.0); - - return false; - -} - void main() { LoadGroupSharedData(); @@ -314,6 +341,8 @@ void main() { vec4 historyMoments; valid = SampleHistory(pixel, historyPixel, history, historyMoments); + SampleCatmullRom(pixel, uv, history); + vec3 historyColor = history.rgb; vec3 currentColor = texelFetch(currentTexture, pixel, 0).rgb; @@ -332,7 +361,7 @@ void main() { historyColor, currentColor); float adjClipBlend = clamp(clipBlend, 0.0, pushConstants.historyClipMax); currentColor = clamp(currentColor, currentNeighbourhoodMin, currentNeighbourhoodMax); - historyColor = mix(historyColor, currentColor, adjClipBlend); + //historyColor = mix(historyColor, currentColor, adjClipBlend); uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; Material material = UnpackMaterial(materialIdx); @@ -340,7 +369,8 @@ void main() { float roughness = material.roughness; roughness *= material.roughnessMap ? texelFetch(roughnessMetallicAoTexture, pixel, 0).r : 1.0; - float factor = clamp(16.0 * log(roughness + 1.0), 0.001, pushConstants.temporalWeight); + float temporalWeight = mix(pushConstants.temporalWeight, 0.5, adjClipBlend); + float factor = clamp(16.0 * log(roughness + 1.0), 0.001, temporalWeight); factor = (uv.x < 0.0 || uv.y < 0.0 || uv.x > 1.0 || uv.y > 1.0) ? 0.0 : factor; @@ -364,6 +394,6 @@ void main() { variance = roughness <= 0.02 ? 0.0 : variance; imageStore(momentsImage, pixel, vec4(momentsResolve, historyLength + 1.0, 0.0)); - imageStore(resolveImage, pixel, vec4(vec3(resolve), variance)); + imageStore(resolveImage, pixel, vec4(resolve, variance)); } \ No newline at end of file diff --git a/data/shader/shadowMapping.vsh b/data/shader/shadowMapping.vsh index 987f7185e..ce77c4a07 100644 --- a/data/shader/shadowMapping.vsh +++ b/data/shader/shadowMapping.vsh @@ -10,7 +10,7 @@ layout(location=2) in vec2 vTexCoord; layout(location=0) out vec2 texCoordVS; #endif -layout(std430, set = 1, binding = 0) buffer Matrices { +layout(std430, set = 1, binding = 1) buffer Matrices { mat3x4 matrices[]; }; @@ -33,7 +33,7 @@ void main() { if (PushConstants.vegetation > 0) { - position = WindAnimation(vPosition, globalData.time, mMatrix[3].xyz); + position = WindAnimation(vPosition, globalData[0].time, mMatrix[3].xyz); } diff --git a/data/shader/skybox.csh b/data/shader/skybox.csh index 148ca41ce..60ff86042 100644 --- a/data/shader/skybox.csh +++ b/data/shader/skybox.csh @@ -30,23 +30,23 @@ void main() { // Don't use the global inverse matrices here, since we also render the cubemap with this shader vec3 viewPos = ConvertDepthToViewSpace(depth, texCoord); - vec3 worldPos = vec3(globalData.ivMatrix * vec4(viewPos, 1.0)); + vec3 worldPos = vec3(globalData[0].ivMatrix * vec4(viewPos, 1.0)); - vec3 cubemapCoord = normalize(worldPos - globalData.cameraLocation.xyz); + vec3 cubemapCoord = normalize(worldPos - globalData[0].cameraLocation.xyz); vec3 color = textureLod(skyCubemap, cubemapCoord, 0).rgb; - vec3 cameraLocationDiff = globalData.cameraLocation.xyz - pushConstants.cameraLocationLast.xyz; + vec3 cameraLocationDiff = globalData[0].cameraLocation.xyz - pushConstants.cameraLocationLast.xyz; // Calculate velocity - vec3 ndcCurrent = (globalData.pMatrix * vec4(viewPos, 1.0)).xyw; - vec3 ndcLast = (globalData.pvMatrixLast * vec4(worldPos - cameraLocationDiff, 1.0)).xyw; + vec3 ndcCurrent = (globalData[0].pMatrix * vec4(viewPos, 1.0)).xyw; + vec3 ndcLast = (globalData[0].pvMatrixLast * vec4(worldPos - cameraLocationDiff, 1.0)).xyw; vec2 ndcL = ndcLast.xy / ndcLast.z; vec2 ndcC = ndcCurrent.xy / ndcCurrent.z; - ndcL -= globalData.jitterLast; - ndcC -= globalData.jitterCurrent; + ndcL -= globalData[0].jitterLast; + ndcC -= globalData[0].jitterCurrent; vec2 velocity = (ndcL - ndcC) * 0.5; diff --git a/data/shader/ssgi/ssgi.csh b/data/shader/ssgi/ssgi.csh new file mode 100644 index 000000000..7825ac8a6 --- /dev/null +++ b/data/shader/ssgi/ssgi.csh @@ -0,0 +1,171 @@ +#include <../globals.hsh> +#include <../raytracer/lights.hsh> +#include <../raytracer/tracing.hsh> +#include <../raytracer/direct.hsh> + +#include <../common/random.hsh> +#include <../common/utility.hsh> +#include <../common/flatten.hsh> +#include <../common/convert.hsh> +#include <../common/normalencode.hsh> +#include <../common/PI.hsh> +#include <../common/bluenoise.hsh> + +#include <../brdf/brdfEval.hsh> +#include <../brdf/brdfSample.hsh> +#include <../brdf/importanceSample.hsh> +#include <../brdf/surface.hsh> + +#include <../ddgi/ddgi.hsh> +#include <../shadow.hsh> + +layout (local_size_x = 8, local_size_y = 4) in; + +layout(set = 3, binding = 0, rgba16f) writeonly uniform image2D giImage; + +layout(set = 3, binding = 1) uniform sampler2D normalTexture; +layout(set = 3, binding = 2) uniform sampler2D depthTexture; +layout(set = 3, binding = 3) uniform sampler2D roughnessMetallicAoTexture; +layout(set = 3, binding = 4) uniform isampler2D offsetTexture; +layout(set = 3, binding = 5) uniform usampler2D materialIdxTexture; +layout(set = 3, binding = 6) uniform sampler2D directLightTexture; + +layout(set = 3, binding = 7) uniform sampler2D scramblingRankingTexture; +layout(set = 3, binding = 8) uniform sampler2D sobolSequenceTexture; + +const ivec2 offsets[4] = ivec2[4]( +ivec2(0, 0), +ivec2(1, 0), +ivec2(0, 1), +ivec2(1, 1) +); + +layout(std140, set = 3, binding = 9) uniform UniformBuffer { + float radianceLimit; + uint frameSeed; + float radius; + uint rayCount; + uint sampleCount; +} uniforms; + +float Luma(vec3 color) { + + const vec3 luma = vec3(0.299, 0.587, 0.114); + return dot(color, luma); + +} + +void main() { + + ivec2 resolution = ivec2(imageSize(giImage)); + + if (int(gl_GlobalInvocationID.x) < resolution.x && + int(gl_GlobalInvocationID.y) < resolution.y) { + + ivec2 pixel = ivec2(gl_GlobalInvocationID.xy); + + vec2 texCoord = (vec2(pixel) + vec2(0.5)) / vec2(resolution); + + int offsetIdx = texelFetch(offsetTexture, pixel, 0).r; + ivec2 pixelOffset = offsets[offsetIdx]; + + float depth = texelFetch(depthTexture, pixel, 0).r; + + vec2 recontructTexCoord = (2.0 * vec2(pixel) + pixelOffset + vec2(0.5)) / (2.0 * vec2(resolution)); + vec3 viewPos = ConvertDepthToViewSpace(depth, texCoord); + vec3 worldPos = vec3(globalData[0].ivMatrix * vec4(viewPos, 1.0)); + vec3 viewVec = vec3(globalData[0].ivMatrix * vec4(viewPos, 0.0)); + vec3 worldNorm = normalize(vec3(globalData[0].ivMatrix * vec4(DecodeNormal(textureLod(normalTexture, texCoord, 0).rg), 0.0))); + + uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; + Material material = UnpackMaterial(materialIdx); + + float roughness = texelFetch(roughnessMetallicAoTexture, pixel, 0).r; + material.roughness *= material.roughnessMap ? roughness : 1.0; + + vec3 irradiance = vec3(0.0); + float hits = 0.0; + + if (depth < 1.0) { + + float alpha = sqr(material.roughness); + + vec3 V = normalize(-viewVec); + vec3 N = worldNorm; + + Surface surface = CreateSurface(V, N, vec3(1.0), material); + + for (uint j = 0; j < uniforms.rayCount; j++) { + int sampleIdx = int(uniforms.frameSeed * uniforms.rayCount + j); + vec3 blueNoiseVec = vec3( + SampleBlueNoise(pixel, sampleIdx, 0, scramblingRankingTexture, sobolSequenceTexture), + SampleBlueNoise(pixel, sampleIdx, 1, scramblingRankingTexture, sobolSequenceTexture), + SampleBlueNoise(pixel, sampleIdx, 2, scramblingRankingTexture, sobolSequenceTexture) + ); + + Ray ray; + + float pdf = 1.0; + BRDFSample brdfSample = SampleDiffuseBRDF(surface, blueNoiseVec.xy); + + ray.hitID = -1; + ray.hitDistance = 0.0; + + // We could also use ray tracing here + float rayLength = uniforms.radius; + + float stepSize = rayLength / float(uniforms.sampleCount); + + ray.direction = brdfSample.L; + ray.origin = worldPos + surface.N * 0.1 + ray.direction * blueNoiseVec.z * stepSize; + + bool hit = false; + for (uint i = 0; i < uniforms.sampleCount; i++) { + + vec3 rayPos = vec3(globalData[0].vMatrix * vec4(ray.origin + float(i) * ray.direction * stepSize, 1.0)); + + vec4 offset = globalData[0].pMatrix * vec4(rayPos, 1.0); + offset.xyz /= offset.w; + vec2 uvPos = offset.xy * 0.5 + 0.5; + + if (uvPos.x < 0.0 || uvPos.x > 1.0 || uvPos.y < 0.0 || uvPos.y > 1.0) + continue; + + ivec2 stepPixel = ivec2(uvPos * vec2(resolution)); + float stepDepth = texelFetch(depthTexture, stepPixel, 0).r; + + float stepLinearDepth = -ConvertDepthToViewSpaceDepth(stepDepth); + float rayDepth = -rayPos.z; + + float depthDelta = rayDepth - stepLinearDepth; + vec3 worldNorm = normalize(vec3(globalData[0].ivMatrix * vec4(DecodeNormal(texelFetch(normalTexture, stepPixel, 0).rg), 0.0))); + + if (depthDelta > 0.0 && depthDelta < rayLength) { + float NdotL = dot(worldNorm, -ray.direction); + if (NdotL >= 0.0) { + vec3 rayIrradiance = texelFetch(directLightTexture, stepPixel * 2 + pixelOffset, 0).rgb; + irradiance += rayIrradiance; + } + hit = true; + + break; + } + + } + + hits += hit ? 1.0 : 0.0; + } + + irradiance /= float(uniforms.rayCount); + + float irradianceMax = max(max(max(irradiance.r, + max(irradiance.g, irradiance.b)), uniforms.radianceLimit), 0.01); + irradiance *= (uniforms.radianceLimit / irradianceMax); + } + + float ao = max(1.0 - (hits / float(uniforms.rayCount)), 0.0); + + imageStore(giImage, pixel, vec4(irradiance, ao)); + } + +} \ No newline at end of file diff --git a/data/shader/ssgi/temporal.csh b/data/shader/ssgi/temporal.csh new file mode 100644 index 000000000..b9a839a4b --- /dev/null +++ b/data/shader/ssgi/temporal.csh @@ -0,0 +1,292 @@ +#include <../common/utility.hsh> +#include <../common/convert.hsh> +#include <../common/PI.hsh> +#include <../common/stencil.hsh> +#include <../common/flatten.hsh> +#include <../common/random.hsh> +#include <../common/normalencode.hsh> + +layout (local_size_x = 8, local_size_y = 8) in; + +layout(set = 3, binding = 0, rgba16f) writeonly uniform image2D resolveImage; +layout(set = 3, binding = 1, r16f) writeonly uniform image2D historyLengthImage; + +layout(set = 3, binding = 2) uniform sampler2D currentTexture; +layout(set = 3, binding = 3) uniform sampler2D velocityTexture; +layout(set = 3, binding = 4) uniform sampler2D depthTexture; +layout(set = 3, binding = 5) uniform sampler2D roughnessMetallicAoTexture; +layout(set = 3, binding = 6) uniform sampler2D normalTexture; +layout(set = 3, binding = 7) uniform usampler2D materialIdxTexture; + +layout(set = 3, binding = 8) uniform sampler2D historyTexture; +layout(set = 3, binding = 9) uniform sampler2D historyLengthTexture; +layout(set = 3, binding = 10) uniform sampler2D historyDepthTexture; +layout(set = 3, binding = 11) uniform sampler2D historyNormalTexture; +layout(set = 3, binding = 12) uniform usampler2D historyMaterialIdxTexture; + +vec2 invResolution = 1.0 / vec2(imageSize(resolveImage)); +vec2 resolution = vec2(imageSize(resolveImage)); + +const int kernelRadius = 1; + +const uint sharedDataSize = (gl_WorkGroupSize.x + 2 * kernelRadius) * (gl_WorkGroupSize.y + 2 * kernelRadius); +const ivec2 unflattenedSharedDataSize = ivec2(gl_WorkGroupSize) + 2 * kernelRadius; + +shared vec4 sharedGiAo[sharedDataSize]; +shared float sharedDepth[sharedDataSize]; + +const ivec2 offsets[9] = ivec2[9]( + ivec2(-1, -1), + ivec2(0, -1), + ivec2(1, -1), + ivec2(-1, 0), + ivec2(0, 0), + ivec2(1, 0), + ivec2(-1, 1), + ivec2(0, 1), + ivec2(1, 1) +); + +const ivec2 pixelOffsets[4] = ivec2[4]( + ivec2(0, 0), + ivec2(1, 0), + ivec2(0, 1), + ivec2(1, 1) +); + +vec4 FetchTexel(ivec2 texel) { + + vec4 value = max(texelFetch(currentTexture, texel, 0), 0); + return value; + +} + +void LoadGroupSharedData() { + + ivec2 workGroupOffset = ivec2(gl_WorkGroupID) * ivec2(gl_WorkGroupSize) - ivec2(kernelRadius); + + uint workGroupSize = gl_WorkGroupSize.x * gl_WorkGroupSize.y; + for(uint i = gl_LocalInvocationIndex; i < sharedDataSize; i += workGroupSize) { + ivec2 localOffset = Unflatten2D(int(i), unflattenedSharedDataSize); + ivec2 texel = localOffset + workGroupOffset; + + texel = clamp(texel, ivec2(0), ivec2(resolution) - ivec2(1)); + + sharedGiAo[i] = FetchTexel(texel); + sharedDepth[i] = ConvertDepthToViewSpaceDepth(texelFetch(depthTexture, texel, 0).r); + } + + barrier(); + +} + +int GetSharedMemoryIndex(ivec2 pixelOffset) { + + ivec2 pixel = ivec2(gl_LocalInvocationID) + ivec2(kernelRadius); + return Flatten2D(pixel + pixelOffset, unflattenedSharedDataSize); + +} + +vec3 FetchCurrentGi(int sharedMemoryIdx) { + + return sharedGiAo[sharedMemoryIdx].rgb; + +} + +float FetchCurrentAo(int sharedMemoryIdx) { + + return sharedGiAo[sharedMemoryIdx].a; + +} + +float FetchDepth(int sharedMemoryIdx) { + + return sharedDepth[sharedMemoryIdx].r; + +} + +ivec2 FindNearest3x3(ivec2 pixel) { + + ivec2 offset = ivec2(0); + float depth = 1.0; + + for (int i = 0; i < 9; i++) { + float currDepth = texelFetch(depthTexture, pixel + offsets[i], 0).r; + if (currDepth < depth) { + depth = currDepth; + offset = offsets[i]; + } + } + + return offset; + +} + +void ComputeVarianceMinMax(out vec3 mean, out vec3 std) { + + vec3 m1 = vec3(0.0); + vec3 m2 = vec3(0.0); + // This could be varied using the temporal variance estimation + // By using a wide neighborhood for variance estimation (8x8) we introduce block artifacts + // These are similiar to video compression artifacts, the spatial filter mostly clears them up + const int radius = kernelRadius; + ivec2 pixel = ivec2(gl_GlobalInvocationID); + + uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; + + float depth = texelFetch(depthTexture, pixel, 0).r; + float linearDepth = ConvertDepthToViewSpaceDepth(depth); + + float totalWeight = 0.0; + + for (int i = -radius; i <= radius; i++) { + for (int j = -radius; j <= radius; j++) { + int sharedMemoryIdx = GetSharedMemoryIndex(ivec2(i, j)); + + vec3 sampleGi = FetchCurrentGi(sharedMemoryIdx); + float sampleLinearDepth = FetchDepth(sharedMemoryIdx); + + float depthPhi = max(1.0, abs(0.025 * linearDepth)); + float weight = min(1.0 , exp(-abs(linearDepth - sampleLinearDepth))); + + m1 += sampleGi * weight; + m2 += sampleGi * sampleGi * weight; + + totalWeight += weight; + } + } + + mean = m1 / totalWeight; + std = sqrt(max((m2 / totalWeight) - (mean * mean), 0.0)); + +} + +bool SampleHistory(ivec2 pixel, vec2 historyPixel, out vec4 history, out float historyLength) { + + bool valid = true; + + history = vec4(0.0); + historyLength = 0.0; + + float totalWeight = 0.0; + float x = fract(historyPixel.x); + float y = fract(historyPixel.y); + + float weights[4] = { (1 - x) * (1 - y), x * (1 - y), (1 - x) * y, x * y }; + + vec3 normal = DecodeNormal(texelFetch(normalTexture, pixel, 0).rg); + float depth = texelFetch(depthTexture, pixel, 0).r; + + float linearDepth = ConvertDepthToViewSpaceDepth(depth); + + // Calculate confidence over 2x2 bilinear neighborhood + // Note that 3x3 neighborhoud could help on edges + for (int i = 0; i < 4; i++) { + ivec2 offsetPixel = ivec2(historyPixel) + pixelOffsets[i]; + float confidence = 1.0; + + vec3 historyNormal = DecodeNormal(texelFetch(historyNormalTexture, offsetPixel, 0).rg); + confidence *= pow(abs(dot(historyNormal, normal)), 16.0); + + float historyDepth = texelFetch(historyDepthTexture, offsetPixel, 0).r; + float historyLinearDepth = ConvertDepthToViewSpaceDepth(historyDepth); + confidence *= min(1.0 , exp(-abs(linearDepth - historyLinearDepth))); + + if (confidence > 0.2) { + totalWeight += weights[i]; + history += texelFetch(historyTexture, offsetPixel, 0) * weights[i]; + historyLength += texelFetch(historyLengthTexture, offsetPixel, 0).r * weights[i]; + } + } + + if (totalWeight > 0.0) { + history /= totalWeight; + historyLength /= totalWeight; + return true; + } + + for (int i = 0; i < 9; i++) { + ivec2 offsetPixel = ivec2(historyPixel) + offsets[i]; + float confidence = 1.0; + + vec3 historyNormal = DecodeNormal(texelFetch(historyNormalTexture, offsetPixel, 0).rg); + confidence *= pow(abs(dot(historyNormal, normal)), 16.0); + + float historyDepth = texelFetch(historyDepthTexture, offsetPixel, 0).r; + float historyLinearDepth = ConvertDepthToViewSpaceDepth(historyDepth); + confidence *= min(1.0 , exp(-abs(linearDepth - historyLinearDepth))); + + if (confidence > 0.2) { + totalWeight += 1.0; + history += texelFetch(historyTexture, offsetPixel, 0); + historyLength += texelFetch(historyLengthTexture, offsetPixel, 0).r; + } + } + + if (totalWeight > 0.0) { + history /= totalWeight; + historyLength /= totalWeight; + return true; + } + + history = vec4(0.0); + historyLength = 0.0; + + return false; + +} + +void main() { + + LoadGroupSharedData(); + + ivec2 pixel = ivec2(gl_GlobalInvocationID); + if (pixel.x > imageSize(resolveImage).x || + pixel.y > imageSize(resolveImage).y) + return; + + vec3 mean, std; + ComputeVarianceMinMax(mean, std); + + const float historyClipFactor = 1.0; + vec3 historyNeighbourhoodMin = mean - historyClipFactor * std; + vec3 historyNeighbourhoodMax = mean + historyClipFactor * std; + + const float currentClipFactor = 4.0; + vec3 currentNeighbourhoodMin = mean - currentClipFactor * std; + vec3 currentNeighbourhoodMax = mean + currentClipFactor * std; + + ivec2 velocityPixel = pixel; + vec2 velocity = texelFetch(velocityTexture, velocityPixel, 0).rg; + + vec2 uv = (vec2(pixel) + vec2(0.5)) * invResolution + velocity; + vec2 historyPixel = vec2(pixel) + velocity * resolution; + + bool valid = true; + vec4 history; + float historyLength; + valid = SampleHistory(pixel, historyPixel, history, historyLength); + + vec4 historyValue = history; + vec4 currentValue = texelFetch(currentTexture, pixel, 0); + + // In case of clipping we might also reject the sample. TODO: Investigate + currentValue.rgb = clamp(currentValue.rgb, currentNeighbourhoodMin, currentNeighbourhoodMax); + //historyValue = clamp(historyValue, historyNeighbourhoodMin, historyNeighbourhoodMax); + + float factor = 0.95; + factor = (uv.x < 0.0 || uv.y < 0.0 || uv.x > 1.0 + || uv.y > 1.0) ? 0.0 : factor; + + if (factor == 0.0 || !valid) { + historyLength = 0.0; + } + + factor = min(factor, historyLength / (historyLength + 1.0)); + + vec4 resolve = mix(currentValue, historyValue, factor); + + imageStore(resolveImage, pixel, vec4(resolve)); + imageStore(historyLengthImage, pixel, vec4(historyLength + 1.0, 0.0, 0.0, 0.0)); + +} \ No newline at end of file diff --git a/data/shader/taa.csh b/data/shader/taa.csh index 40947580c..ba7ca084d 100644 --- a/data/shader/taa.csh +++ b/data/shader/taa.csh @@ -12,7 +12,10 @@ layout(set = 3, binding = 2) uniform sampler2D currentTexture; layout(set = 3, binding = 3) uniform sampler2D velocityTexture; layout(set = 3, binding = 4) uniform sampler2D depthTexture; layout(set = 3, binding = 5) uniform sampler2D lastVelocityTexture; + +#ifndef PATHTRACE layout(set = 3, binding = 6) uniform usampler2D stencilTexture; +#endif layout(push_constant) uniform constants { vec2 resolution; @@ -24,11 +27,14 @@ const float minVelocityBlend = 0.05; const float maxVelocityBlend = 0.5; #define TAA_YCOCG -//#define TAA_CLIP // Use clip instead of clamping for better ghosting prevention, introduces more flickering #define TAA_BICUBIC // Nearly always use the bicubic sampling for better quality and sharpness under movement #define TAA_TONE // Somehow introduces more flickering as well //#define TAA_DENOISE +#ifdef PATHTRACE +#define TAA_CLIP +#endif + const ivec2 offsets[9] = ivec2[9]( ivec2(-1, -1), ivec2(0, -1), @@ -362,10 +368,12 @@ void main() { float range = 1.4; float localAntiFlicker = mix(0.6, 5.0, 1.0 - velocityBlend); +#ifndef PATHTRACE #ifdef TAA_DENOISE range += localAntiFlicker; #else range += mix(0.0, localAntiFlicker, historyContrast); +#endif #endif localNeighbourhoodMin = average - range * (average - localNeighbourhoodMin); @@ -381,7 +389,11 @@ void main() { historyColor = clamp(historyColor, localNeighbourhoodMin, localNeighbourhoodMax); #endif +#ifndef PATHTRACE + float blendFactor = mix(minVelocityBlend, maxVelocityBlend, velocityBlend); +#else float blendFactor = mix(minVelocityBlend, maxVelocityBlend, velocityBlend); +#endif #ifdef TAA_YCOCG historyColor = YCoCgToRGB(historyColor); @@ -393,8 +405,10 @@ void main() { currentColor.rgb = InverseTonemap(currentColor.rgb); #endif +#ifndef PATHTRACE StencilFeatures features = DecodeStencilFeatures(texelFetch(stencilTexture, pixel, 0).r); blendFactor = features.responsivePixel ? 0.5 : blendFactor; +#endif // Check if we sampled outside the viewport area blendFactor = (uv.x < 0.0 || uv.y < 0.0 || uv.x > 1.0 diff --git a/data/shader/terrain/terrain.fsh b/data/shader/terrain/terrain.fsh index d3cd97b17..81a65d0a6 100644 --- a/data/shader/terrain/terrain.fsh +++ b/data/shader/terrain/terrain.fsh @@ -195,7 +195,7 @@ void main() { + 0.5 * PushConstants.normalTexelSize; vec3 norm = 2.0 * texture(normalMap, tex).rgb - 1.0; - geometryNormalFS = EncodeNormal(normalize(mat3(globalData.vMatrix) * norm)); + geometryNormalFS = EncodeNormal(normalize(mat3(globalData[0].vMatrix) * norm)); #ifdef MATERIAL_MAPPING // Normal mapping only for near tiles @@ -220,15 +220,15 @@ void main() { vec3 normal = norm; #endif - normalFS = EncodeNormal(normalize(mat3(globalData.vMatrix) * normal)); + normalFS = EncodeNormal(normalize(mat3(globalData[0].vMatrix) * normal)); roughnessMetalnessAoFS = vec3(roughness, metalness, ao); // Calculate velocity vec2 ndcL = ndcLast.xy / ndcLast.z; vec2 ndcC = ndcCurrent.xy / ndcCurrent.z; - ndcL -= globalData.jitterLast; - ndcC -= globalData.jitterCurrent; + ndcL -= globalData[0].jitterLast; + ndcC -= globalData[0].jitterCurrent; velocityFS = (ndcL - ndcC) * 0.5; diff --git a/data/shader/terrain/terrain.tcsh b/data/shader/terrain/terrain.tcsh index 49511f158..adf8bcf4e 100644 --- a/data/shader/terrain/terrain.tcsh +++ b/data/shader/terrain/terrain.tcsh @@ -90,10 +90,10 @@ void main() { vec3 midCD = vec3(gl_in[2].gl_Position + gl_in[3].gl_Position) / 2.0; vec3 midDA = vec3(gl_in[3].gl_Position + gl_in[0].gl_Position) / 2.0; - float distanceAB = distance(globalData.cameraLocation.xyz, midAB); - float distanceBC = distance(globalData.cameraLocation.xyz, midBC); - float distanceCD = distance(globalData.cameraLocation.xyz, midCD); - float distanceDA = distance(globalData.cameraLocation.xyz, midDA); + float distanceAB = distance(globalData[0].cameraLocation.xyz, midAB); + float distanceBC = distance(globalData[0].cameraLocation.xyz, midBC); + float distanceCD = distance(globalData[0].cameraLocation.xyz, midCD); + float distanceDA = distance(globalData[0].cameraLocation.xyz, midDA); gl_TessLevelOuter[AB] = mix(1.0, Uniforms.maxTessellationLevel, GetTessLevel(distanceAB)); gl_TessLevelOuter[BC] = mix(1.0, Uniforms.maxTessellationLevel, GetTessLevel(distanceBC)); diff --git a/data/shader/terrain/terrain.tesh b/data/shader/terrain/terrain.tesh index ab38d7500..9d46288c6 100644 --- a/data/shader/terrain/terrain.tesh +++ b/data/shader/terrain/terrain.tesh @@ -97,7 +97,7 @@ void main(){ texCoords /= PushConstants.nodeSideLength; #ifndef DISTANCE - float vertexDistance = distance(position.xyz, globalData.cameraLocation.xyz); + float vertexDistance = distance(position.xyz, globalData[0].cameraLocation.xyz); float weight = clamp((Uniforms.displacementDistance - vertexDistance), 0.0, 1.0); vec2 tex = vec2(PushConstants.normalTexelSize) + texCoords * @@ -130,11 +130,11 @@ void main(){ } #endif - gl_Position = globalData.pMatrix * globalData.vMatrix * position; + gl_Position = globalData[0].pMatrix * globalData[0].vMatrix * position; ndcCurrent = vec3(gl_Position.xy, gl_Position.w); - vec4 last = globalData.pvMatrixLast * position; + vec4 last = globalData[0].pvMatrixLast * position; ndcLast = vec3(last.xy, last.w); } \ No newline at end of file diff --git a/data/shader/terrain/terrain.vsh b/data/shader/terrain/terrain.vsh index be9968a6e..0d3e50b9a 100644 --- a/data/shader/terrain/terrain.vsh +++ b/data/shader/terrain/terrain.vsh @@ -98,11 +98,11 @@ void main() { #ifndef DISTANCE gl_Position = worldPosition; #else - gl_Position = globalData.pMatrix * globalData.vMatrix * worldPosition; + gl_Position = globalData[0].pMatrix * globalData[0].vMatrix * worldPosition; ndcCurrent = vec3(gl_Position.xy, gl_Position.w); - vec4 last = globalData.pvMatrixLast * worldPosition; + vec4 last = globalData[0].pvMatrixLast * worldPosition; ndcLast = vec3(last.xy, last.w); #endif diff --git a/data/shader/vegetation/common.hsh b/data/shader/vegetation/common.hsh index 566f258da..1ab0ab684 100644 --- a/data/shader/vegetation/common.hsh +++ b/data/shader/vegetation/common.hsh @@ -7,8 +7,8 @@ bool IsInstanceVisible(Instance instance, MeshInformation meshInfo) { vec3 max = meshInfo.aabbMin.xyz + instance.position.xyz; for (int i = 0; i < 6; i++) { - vec3 normal = globalData.frustumPlanes[i].xyz; - float dist = globalData.frustumPlanes[i].w; + vec3 normal = globalData[0].frustumPlanes[i].xyz; + float dist = globalData[0].frustumPlanes[i].w; vec3 s; s.x = normal.x >= 0.0 ? min.x : max.x; @@ -26,7 +26,7 @@ uint GetBin(Instance instance, MeshInformation meshInfo) { const float binScale = 32.0; - float dist = distance(globalData.cameraLocation.xyz, instance.position.xyz); + float dist = distance(globalData[0].cameraLocation.xyz, instance.position.xyz); // return uint(log2((dist * binScale) + 1.0)); return uint(sqrt(dist * binScale)); //return uint(sqrt(sqrt(dist * 64000.0))); diff --git a/data/shader/vegetation/instanceCulling.csh b/data/shader/vegetation/instanceCulling.csh index 26744f58c..0268df8e0 100644 --- a/data/shader/vegetation/instanceCulling.csh +++ b/data/shader/vegetation/instanceCulling.csh @@ -22,7 +22,7 @@ void main() { bool cull = false; Instance instance = instanceData[idx]; - float dist = distance(instance.position.xyz, globalData.cameraLocation.xyz); + float dist = distance(instance.position.xyz, globalData[0].cameraLocation.xyz); uint binIdx = min(GetBin(instance, meshInfo), PushConstants.binCount - 1u) + binOffset; cull = !IsInstanceVisible(instance, meshInfo) || dist > 100.0; diff --git a/data/shader/vegetation/vegetation.fsh b/data/shader/vegetation/vegetation.fsh index a869fd03b..acde74c62 100644 --- a/data/shader/vegetation/vegetation.fsh +++ b/data/shader/vegetation/vegetation.fsh @@ -108,8 +108,8 @@ void main() { vec2 ndcL = ndcLastVS.xy / ndcLastVS.z; vec2 ndcC = ndcCurrentVS.xy / ndcCurrentVS.z; - ndcL -= globalData.jitterLast; - ndcC -= globalData.jitterCurrent; + ndcL -= globalData[0].jitterLast; + ndcC -= globalData[0].jitterCurrent; velocityFS = (ndcL - ndcC) * 0.5; diff --git a/data/shader/vegetation/vegetation.vsh b/data/shader/vegetation/vegetation.vsh index 4182b383c..8b0e5d45b 100644 --- a/data/shader/vegetation/vegetation.vsh +++ b/data/shader/vegetation/vegetation.vsh @@ -49,24 +49,24 @@ void main() { Instance instance = instanceData[gl_InstanceIndex]; texCoordVS = pushConstants.invertUVs > 0 ? vec2(vTexCoord.x, 1.0 - vTexCoord.y) : vTexCoord; - mat4 mvMatrix = globalData.vMatrix; + mat4 mvMatrix = globalData[0].vMatrix; vec3 position = instance.position.xyz + vPosition; - position = instance.position.xyz + WindAnimation(vPosition, globalData.time, instance.position.xyz); - vec3 lastPosition = instance.position.xyz + WindAnimation(vPosition, globalData.time - globalData.deltaTime, instance.position.xyz); + position = instance.position.xyz + WindAnimation(vPosition, globalData[0].time, instance.position.xyz); + vec3 lastPosition = instance.position.xyz + WindAnimation(vPosition, globalData[0].time - globalData[0].deltaTime, instance.position.xyz); vec4 positionToCamera = mvMatrix * vec4(position, 1.0); #ifdef NORMAL_MAP positionVS = positionToCamera.xyz; #endif - gl_Position = globalData.pMatrix * positionToCamera; + gl_Position = globalData[0].pMatrix * positionToCamera; // Needed for velocity buffer calculation ndcCurrentVS = vec3(gl_Position.xy, gl_Position.w); // For moving objects we need the last frames matrix - vec4 last = globalData.pvMatrixLast * vec4(lastPosition, 1.0); + vec4 last = globalData[0].pvMatrixLast * vec4(lastPosition, 1.0); ndcLastVS = vec3(last.xy, last.w); normalVS = mat3(mvMatrix) * vNormal; diff --git a/data/shader/volumetric/volumetric.csh b/data/shader/volumetric/volumetric.csh index 6ffb22ae2..b80089c06 100644 --- a/data/shader/volumetric/volumetric.csh +++ b/data/shader/volumetric/volumetric.csh @@ -73,7 +73,7 @@ void main() { vec4 ComputeVolumetric(vec3 fragPos, float startDepth, vec2 texCoords) { vec2 resolution = vec2(imageSize(volumetricImage)); - vec3 viewPosition = vec3(globalData.ivMatrix * vec4(fragPos, 1.0)); + vec3 viewPosition = vec3(globalData[0].ivMatrix * vec4(fragPos, 1.0)); // We compute this in view space vec3 rayVector = fragPos; @@ -140,7 +140,7 @@ vec4 ComputeVolumetric(vec3 fragPos, float startDepth, vec2 texCoords) { #endif //shadowValue = distance > uniforms.light.shadow.distance ? 1.0 : shadowValue; - vec3 worldPosition = vec3(globalData.ivMatrix * vec4(currentPosition, 1.0)); + vec3 worldPosition = vec3(globalData[0].ivMatrix * vec4(currentPosition, 1.0)); #ifdef CLOUDS vec3 planetCenter = uniforms.planetCenterAndRadius.xyz; diff --git a/data/shader/volumetric/volumetric.hsh b/data/shader/volumetric/volumetric.hsh index d9e2897db..32a5f7243 100644 --- a/data/shader/volumetric/volumetric.hsh +++ b/data/shader/volumetric/volumetric.hsh @@ -29,7 +29,7 @@ vec2 IntersectSphere(vec3 origin, vec3 direction, vec3 pos, float radius) { vec3 ApplyVolumetrics(Fog fog, vec3 inputColor, vec4 volumetricFog, vec4 volumetricClouds, vec3 worldViewDirection, vec3 planetCenter, float innerCloudRadius) { - vec2 intersectDists = IntersectSphere(globalData.cameraLocation.xyz, worldViewDirection, + vec2 intersectDists = IntersectSphere(globalData[0].cameraLocation.xyz, worldViewDirection, planetCenter, innerCloudRadius); #ifdef RAYMARCHED_FOG diff --git a/data/shader/volumetric/volumetricResolve.csh b/data/shader/volumetric/volumetricResolve.csh index 2d3e9b914..590aee507 100644 --- a/data/shader/volumetric/volumetricResolve.csh +++ b/data/shader/volumetric/volumetricResolve.csh @@ -93,13 +93,15 @@ void Upsample2x(float referenceDepth, vec2 texCoord, out vec4 volumetric, out ve float minWeight = 1.0; + referenceDepth = ConvertDepthToViewSpaceDepth(referenceDepth); + for (uint i = 0; i < 9; i++) { int sharedMemoryOffset = Flatten2D(pixel + offsets[i], unflattenedDepthDataSize); - float depth = depths[sharedMemoryOffset]; + float depth = ConvertDepthToViewSpaceDepth(depths[sharedMemoryOffset]); float depthDiff = abs(referenceDepth - depth); - float depthWeight = min(exp(-depthDiff * 32.0), 1.0); + float depthWeight = min(exp(-depthDiff), 1.0); minWeight = min(minWeight, depthWeight); invocationDepths[i] = depth; @@ -142,7 +144,7 @@ void main() { vec3 viewPosition = ConvertDepthToViewSpace(depth, texCoord); - vec3 worldDirection = normalize(vec3(globalData.ivMatrix * vec4(viewPosition, 0.0))); + vec3 worldDirection = normalize(vec3(globalData[0].ivMatrix * vec4(viewPosition, 0.0))); vec3 resolve = imageLoad(resolveImage, pixel).rgb; diff --git a/libs/ImguiExtension/ImguiWrapper.cpp b/libs/ImguiExtension/ImguiWrapper.cpp index a0215d727..295d3891d 100644 --- a/libs/ImguiExtension/ImguiWrapper.cpp +++ b/libs/ImguiExtension/ImguiWrapper.cpp @@ -63,6 +63,8 @@ void ImguiWrapper::Unload() { // Unsubscribe from all events ImGui_ImplVulkan_Shutdown(); + initialized = false; + } void ImguiWrapper::Update(Atlas::Window* window, float deltaTime) { @@ -121,8 +123,6 @@ void ImguiWrapper::Render(bool clearSwapChain) { void ImguiWrapper::RecreateImGuiResources() { - static bool initialized = false; - if (initialized) { ImGui_ImplVulkan_Shutdown(); } diff --git a/libs/ImguiExtension/ImguiWrapper.h b/libs/ImguiExtension/ImguiWrapper.h index 08cb6d77e..3ad1e7288 100644 --- a/libs/ImguiExtension/ImguiWrapper.h +++ b/libs/ImguiExtension/ImguiWrapper.h @@ -6,7 +6,7 @@ // Atlas engine includes #include -#include +#include class ImguiWrapper { @@ -53,4 +53,6 @@ class ImguiWrapper { std::unordered_map imageViewToDescriptorSetMap; + bool initialized = false; + }; diff --git a/src/demo/App.cpp b/src/demo/App.cpp index de8ed55a6..4aa1a184e 100644 --- a/src/demo/App.cpp +++ b/src/demo/App.cpp @@ -27,6 +27,7 @@ void App::LoadContent() { mouseHandler = Atlas::Input::MouseHandler(&camera, 1.5f, 6.0f); keyboardHandler = Atlas::Input::KeyboardHandler(&camera, 7.0f, 6.0f); + controllerHandler = Atlas::Input::ControllerHandler(&camera, 1.5f, 5.0f, 10.0f, 5000.0f); Atlas::Events::EventManager::KeyboardEventDelegate.Subscribe( [this](Atlas::Events::KeyboardEvent event) { @@ -46,7 +47,7 @@ void App::LoadContent() { Atlas::PipelineManager::EnableHotReload(); - directionalLight = std::make_shared(AE_MOVABLE_LIGHT); + directionalLight = Atlas::CreateRef(AE_MOVABLE_LIGHT); directionalLight->direction = glm::vec3(0.0f, -1.0f, 1.0f); directionalLight->color = glm::vec3(255, 236, 209) / 255.0f; glm::mat4 orthoProjection = glm::ortho(-100.0f, 100.0f, -70.0f, 120.0f, -120.0f, 120.0f); @@ -55,30 +56,26 @@ void App::LoadContent() { scene->sky.sun = directionalLight; - scene->ao = std::make_shared(16); + scene->ao = Atlas::CreateRef(16); scene->ao->rt = true; - scene->reflection = std::make_shared(1); + scene->reflection = Atlas::CreateRef(1); scene->reflection->useShadowMap = true; - scene->fog = std::make_shared(); + scene->fog = Atlas::CreateRef(); scene->fog->enable = true; scene->fog->density = 0.0002f; scene->fog->heightFalloff = 0.0284f; scene->fog->height = 0.0f; - scene->fog->scatteringAnisotropy = 0.0f; - scene->sky.clouds = std::make_shared(); - scene->sky.clouds->minHeight = 1400.0f; - scene->sky.clouds->maxHeight = 1700.0f; - scene->sky.clouds->castShadow = false; - - scene->sky.atmosphere = std::make_shared(); + scene->sky.atmosphere = Atlas::CreateRef(); scene->postProcessing.taa = Atlas::PostProcessing::TAA(0.99f); scene->postProcessing.sharpen.enable = true; scene->postProcessing.sharpen.factor = 0.15f; - scene->sss = std::make_shared(); + scene->sss = Atlas::CreateRef(); + + scene->ssgi = Atlas::CreateRef(); LoadScene(); @@ -114,8 +111,13 @@ void App::Update(float deltaTime) { mouseHandler.lock = false; } - mouseHandler.Update(&camera, deltaTime); - keyboardHandler.Update(&camera, deltaTime); + if (controllerHandler.IsControllerAvailable()) { + controllerHandler.Update(&camera, deltaTime); + } + else { + mouseHandler.Update(&camera, deltaTime); + keyboardHandler.Update(&camera, deltaTime); + } if (rotateCamera) { camera.rotation.y += rotateCameraSpeed * cos(Atlas::Clock::Get()); @@ -157,15 +159,18 @@ void App::Render(float deltaTime) { static bool debugReflection = false; static bool debugClouds = false; static bool debugSSS = false; + static bool debugSSGI = false; static bool debugMotion = false; static bool slowMode = false; static float cloudDepthDebug = 0.0f; +#ifndef AE_HEADLESS auto windowFlags = window.GetFlags(); if (windowFlags & AE_WINDOW_HIDDEN || windowFlags & AE_WINDOW_MINIMIZED || !(windowFlags & AE_WINDOW_SHOWN)) { return; } +#endif if (!loadingComplete) { DisplayLoadingScreen(deltaTime); @@ -181,9 +186,9 @@ void App::Render(float deltaTime) { else { mainRenderer->RenderScene(&viewport, &renderTarget, &camera, scene.get()); - auto debug = debugAo || debugReflection || debugClouds || debugSSS || debugMotion; + auto debug = debugAo || debugReflection || debugClouds || debugSSS || debugSSGI || debugMotion; - if (debug) { + if (debug && graphicsDevice->swapChain->isComplete) { auto commandList = graphicsDevice->GetCommandList(Atlas::Graphics::GraphicsQueue); commandList->BeginCommands(); commandList->BeginRenderPass(graphicsDevice->swapChain, true); @@ -204,6 +209,10 @@ void App::Render(float deltaTime) { mainRenderer->textureRenderer.RenderTexture2D(commandList, &viewport, &renderTarget.sssTexture, 0.0f, 0.0f, float(viewport.width), float(viewport.height), 0.0, 1.0f, false, true); } + else if (debugSSGI) { + mainRenderer->textureRenderer.RenderTexture2D(commandList, &viewport, &renderTarget.giTexture, + 0.0f, 0.0f, float(viewport.width), float(viewport.height), 0.0, 1.0f, false, true); + } else if (debugMotion) { mainRenderer->textureRenderer.RenderTexture2D(commandList, &viewport, renderTarget.GetData(Atlas::FULL_RES)->velocityTexture.get(), @@ -231,6 +240,7 @@ void App::Render(float deltaTime) { const auto& reflection = scene->reflection; const auto& clouds = scene->sky.clouds; const auto& sss = scene->sss; + const auto& ssgi = scene->ssgi; auto& postProcessing = scene->postProcessing; bool openSceneNotFoundPopup = false; @@ -269,7 +279,8 @@ void App::Render(float deltaTime) { { const char* items[] = { "Cornell box", "Sponza", "San Miguel", "New Sponza", "Bistro", "Medieval", "Pica Pica", - "Subway", "Materials", "Forest"}; + "Subway", "Materials", "Forest", "Emerald square", + "Flying world"}; int currentItem = static_cast(sceneSelection); ImGui::Combo("Select scene", ¤tItem, items, IM_ARRAYSIZE(items)); @@ -285,10 +296,6 @@ void App::Render(float deltaTime) { } } - ImGui::Checkbox("Pathtrace", &pathTrace); - - if (pathTrace) ImGui::SliderInt("Pathtrace bounces", &mainRenderer->pathTracingRenderer.bounces, 0, 100); - if (ImGui::CollapsingHeader("General")) { static bool fullscreenMode = false; static bool vsyncMode = false; @@ -346,7 +353,19 @@ void App::Render(float deltaTime) { } } - + if (ImGui::CollapsingHeader("Pathtracing")) { + bool pathTraceEnabled = pathTrace; + ImGui::Checkbox("Enable##Pathtrace", &pathTrace); + ImGui::SliderInt("Bounces##Pathtrace", &mainRenderer->pathTracingRenderer.bounces, 0, 100); + ImGui::Checkbox("Sample emissives##Pathtrace", &mainRenderer->pathTracingRenderer.sampleEmissives); + ImGui::Text("Realtime"); + ImGui::Checkbox("Realtime##Pathtrace", &mainRenderer->pathTracingRenderer.realTime); + ImGui::SliderInt("Samples per frame##Pathtrace", &mainRenderer->pathTracingRenderer.realTimeSamplesPerFrame, 1, 100); + ImGui::Text("Realtime denoiser"); + ImGui::SliderInt("Max accumulated frames##Pathtrace", &mainRenderer->pathTracingRenderer.historyLengthMax, 1, 256); + ImGui::SliderFloat("Current clip##Pathtrace", &mainRenderer->pathTracingRenderer.currentClipFactor, 0.1f, 4.0f); + ImGui::SliderFloat("Max history clip##Pathtrace", &mainRenderer->pathTracingRenderer.historyClipMax, 0.0f, 1.0f); + } if (ImGui::CollapsingHeader("DDGI")) { ImGui::Text("Probe count: %s", vecToString(volume->probeCount).c_str()); ImGui::Text("Cell size: %s", vecToString(volume->cellSize).c_str()); @@ -427,7 +446,27 @@ void App::Render(float deltaTime) { ImGui::Text("Volumetric"); ImGui::SliderFloat("Intensity##Volumetric", &light->GetVolumetric()->intensity, 0.0f, 1.0f); ImGui::Text("Shadow"); - ImGui::SliderFloat("Bias##Shadow", &light->GetShadow()->bias, 0.0f, 2.0f); + auto shadow = light->GetShadow(); + const char* gridResItems[] = { "512x512", "1024x1024", "2048x2048", "4096x4096", "8192x8192" }; + int currentItem = 0; + if (shadow->resolution == 512) currentItem = 0; + if (shadow->resolution == 1024) currentItem = 1; + if (shadow->resolution == 2048) currentItem = 2; + if (shadow->resolution == 4096) currentItem = 3; + if (shadow->resolution == 8192) currentItem = 4; + auto prevItem = currentItem; + ImGui::Combo("Resolution##Shadow", ¤tItem, gridResItems, IM_ARRAYSIZE(gridResItems)); + + if (currentItem != prevItem) { + switch (currentItem) { + case 0: shadow->SetResolution(512); break; + case 1: shadow->SetResolution(1024); break; + case 2: shadow->SetResolution(2048); break; + case 3: shadow->SetResolution(4096); break; + case 4: shadow->SetResolution(8192); break; + } + } + ImGui::SliderFloat("Bias##Shadow", &shadow->bias, 0.0f, 2.0f); } if (ImGui::CollapsingHeader("Screen-space shadows (preview)")) { ImGui::Checkbox("Debug##SSS", &debugSSS); @@ -436,6 +475,17 @@ void App::Render(float deltaTime) { ImGui::SliderFloat("Max length##SSS", &sss->maxLength, 0.01f, 1.0f); ImGui::SliderFloat("Thickness##SSS", &sss->thickness, 0.001f, 1.0f, "%.3f", ImGuiSliderFlags_Logarithmic); } + if (ImGui::CollapsingHeader("SSGI")) { + ImGui::Checkbox("Debug##SSGI", &debugSSGI); + ImGui::Checkbox("Enable##SSGI", &ssgi->enable); + ImGui::Checkbox("Enable ambient occlusion##SSGI", &ssgi->enableAo); + ImGui::SliderInt("Ray count##SSGI", &ssgi->rayCount, 1, 8); + ImGui::SliderInt("Sample count##SSGI", &ssgi->sampleCount, 1, 16); + ImGui::SliderFloat("Radius##SSGI", &ssgi->radius, 0.0f, 10.0f); + ImGui::SliderFloat("Ao strength##SSGI", &ssgi->aoStrength, 0.0f, 10.0f); + ImGui::SliderFloat("Irradiance limit##SSGI", &ssgi->irradianceLimit, 0.0f, 10.0f); + //ImGui::SliderInt("Sample count##Ao", &ao->s, 0.0f, 20.0f, "%.3f", 2.0f); + } if (ImGui::CollapsingHeader("Ambient Occlusion")) { ImGui::Checkbox("Debug##Ao", &debugAo); ImGui::Checkbox("Enable ambient occlusion##Ao", &ao->enable); @@ -543,6 +593,7 @@ void App::Render(float deltaTime) { ImGui::Text("Image effects"); ImGui::Checkbox("Filmic tonemapping", &postProcessing.filmicTonemapping); ImGui::SliderFloat("Saturation##Postprocessing", &postProcessing.saturation, 0.0f, 2.0f); + ImGui::SliderFloat("Contrast##Postprocessing", &postProcessing.contrast, 0.0f, 2.0f); ImGui::SliderFloat("White point##Postprocessing", &postProcessing.whitePoint, 0.0f, 100.0f, "%.3f", 2.0f); ImGui::Separator(); ImGui::Text("Chromatic aberration"); @@ -594,6 +645,9 @@ void App::Render(float deltaTime) { ImGui::Text("Use F11 to hide/unhide the UI"); } if (ImGui::CollapsingHeader("Profiler")) { + bool enabled = Atlas::Graphics::Profiler::enable; + ImGui::Checkbox("Enable##Profiler", &enabled); + Atlas::Graphics::Profiler::enable = enabled; const char* items[] = { "Chronologically", "Max time", "Min time" }; static int item = 0; @@ -681,6 +735,12 @@ void App::Render(float deltaTime) { ImGui::Render(); +#ifdef AE_HEADLESS + Atlas::Log::Message("Frame rendererd"); + renderTarget.hdrTexture.Save("prepost"); + renderTarget.postProcessTexture.Save("result"); +#endif + if (!recreateSwapchain) { imguiWrapper.Render(); } @@ -697,7 +757,6 @@ void App::Render(float deltaTime) { firstFrame = false; } - } void App::DisplayLoadingScreen(float deltaTime) { @@ -751,6 +810,8 @@ bool App::IsSceneAvailable(SceneSelection selection) { case SUBWAY: return Atlas::Loader::AssetLoader::FileExists("subway/scene.gltf"); case MATERIALS: return Atlas::Loader::AssetLoader::FileExists("material demo/materials.obj"); case FOREST: return Atlas::Loader::AssetLoader::FileExists("forest/forest.gltf"); + case EMERALDSQUARE: return Atlas::Loader::AssetLoader::FileExists("emeraldsquare/square.gltf"); + case FLYINGWORLD: return Atlas::Loader::AssetLoader::FileExists("flying world/scene.gltf"); case NEWSPONZA: return Atlas::Loader::AssetLoader::FileExists("newsponza/main/NewSponza_Main_Blender_glTF.gltf") && Atlas::Loader::AssetLoader::FileExists("newsponza/candles/NewSponza_100sOfCandles_glTF_OmniLights.gltf") && Atlas::Loader::AssetLoader::FileExists("newsponza/curtains/NewSponza_Curtains_glTF.gltf") && @@ -767,6 +828,11 @@ bool App::LoadScene() { Atlas::Texture::Cubemap sky; directionalLight->direction = glm::vec3(0.0f, -1.0f, 1.0f); + scene->sky.clouds = Atlas::CreateRef(); + scene->sky.clouds->minHeight = 1400.0f; + scene->sky.clouds->maxHeight = 1700.0f; + scene->sky.clouds->castShadow = false; + scene->sky.probe = nullptr; scene->sky.clouds->enable = true; scene->sss->enable = true; @@ -972,6 +1038,50 @@ bool App::LoadScene() { scene->fog->enable = false; } + else if (sceneSelection == EMERALDSQUARE) { + auto otherScene = Atlas::Loader::ModelLoader::LoadScene("emeraldsquare/square.gltf", false, glm::mat4(1.0f), 1024); + otherScene->Update(&camera, 1.0f); + + CopyActors(otherScene); + + // Other scene related settings apart from the mesh + directionalLight->intensity = 10.0f; + directionalLight->GetVolumetric()->intensity = 0.08f; + + // Setup camera + camera.location = glm::vec3(30.0f, 25.0f, 0.0f); + camera.rotation = glm::vec2(-3.14f / 2.0f, 0.0f); + camera.exposure = 1.0f; + + scene->fog->enable = false; + } + else if (sceneSelection == FLYINGWORLD) { + meshes.reserve(1); + + auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( + "flying world/scene.gltf", ModelLoader::LoadMesh, false, glm::mat4(0.01f), 2048 + ); + meshes.push_back(mesh); + + // Metalness is set to 0.9f + //for (auto& material : mesh.data.materials) material.metalness = 0.0f; + + // Other scene related settings apart from the mesh + directionalLight->intensity = 50.0f; + directionalLight->GetVolumetric()->intensity = 0.08f; + + // Setup camera + camera.location = glm::vec3(30.0f, 25.0f, 0.0f); + camera.rotation = glm::vec2(-3.14f / 2.0f, 0.0f); + camera.exposure = 1.0f; + + scene->sky.clouds->minHeight = 700.0f; + scene->sky.clouds->maxHeight = 1000.0f; + scene->sky.clouds->densityMultiplier = 0.65f; + scene->sky.clouds->heightStretch = 1.0f; + + scene->fog->enable = true; + } else if (sceneSelection == NEWSPONZA) { meshes.reserve(4); @@ -1007,7 +1117,7 @@ bool App::LoadScene() { // scene.sky.probe = std::make_shared(sky); - if (sceneSelection != FOREST) { + if (sceneSelection != FOREST && sceneSelection != EMERALDSQUARE) { auto meshCount = 0; for (auto &mesh: meshes) { if (meshCount == 10) { @@ -1078,16 +1188,24 @@ void App::CheckLoadScene() { mesh->data.colors.Clear(); } } + else if (sceneSelection == PICAPICA) { + for (const auto& material : meshes.front()->data.materials) + material->vertexColors = false; + } + else if (sceneSelection == EMERALDSQUARE) { + for (const auto& mesh : meshes) + for (const auto& material : mesh->data.materials) + material->metalness = 0.0f; + } static std::future future; auto buildRTStructure = [&]() { auto sceneMeshes = scene->GetMeshes(); - for (auto& mesh : sceneMeshes) { - mesh->data.BuildBVH(); + for (const auto& mesh : sceneMeshes) { + mesh->BuildBVH(); } - scene->BuildRTStructures(); }; if (!future.valid()) { @@ -1169,13 +1287,21 @@ void App::CheckLoadScene() { sceneAABB.Scale(1.05f), glm::ivec3(20)); scene->irradianceVolume->SetRayCount(128, 32); } + else if (sceneSelection == EMERALDSQUARE) { + scene->irradianceVolume = std::make_shared( + sceneAABB.Scale(1.05f), glm::ivec3(20)); + scene->irradianceVolume->SetRayCount(128, 32); + } + else if (sceneSelection == FLYINGWORLD) { + scene->irradianceVolume = std::make_shared( + sceneAABB.Scale(1.05f), glm::ivec3(20)); + scene->irradianceVolume->SetRayCount(128, 32); + } scene->irradianceVolume->useShadowMap = true; Atlas::Clock::ResetAverage(); - auto device = Atlas::Graphics::GraphicsDevice::DefaultDevice; - loadingComplete = true; } diff --git a/src/demo/App.h b/src/demo/App.h index 835d753a6..29c8a8caf 100644 --- a/src/demo/App.h +++ b/src/demo/App.h @@ -39,7 +39,9 @@ class App : public Atlas::EngineInstance { PICAPICA, SUBWAY, MATERIALS, - FOREST + FOREST, + EMERALDSQUARE, + FLYINGWORLD }; void DisplayLoadingScreen(float deltaTime); @@ -73,6 +75,7 @@ class App : public Atlas::EngineInstance { Atlas::Input::MouseHandler mouseHandler; Atlas::Input::KeyboardHandler keyboardHandler; + Atlas::Input::ControllerHandler controllerHandler; Ref loadingTexture; diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index cbc11d5e4..8857b2ebf 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -51,9 +51,11 @@ endif() if(APPLE) set(ATLAS_ENGINE_COMPILE_DEFINITIONS ${ATLAS_ENGINE_COMPILE_DEFINITIONS} AE_OS_MACOS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_MACOS_MVK -DVK_EXAMPLE_XCODE_GENERATED") + set(ATLAS_ENGINE_LIBS assimp::assimp SDL2::SDL2 SDL2::SDL2main SDL2::SDL2-static - volk::volk volk::volk_headers SPIRV-Tools-opt - GPUOpen::VulkanMemoryAllocator Vulkan::Vulkan + volk::volk SPIRV-Tools-opt + GPUOpen::VulkanMemoryAllocator Vulkan::Vulkan Vulkan::Headers unofficial::spirv-reflect::spirv-reflect glslang::SPIRV) endif() @@ -132,15 +134,27 @@ endif() find_path(GLM_INCLUDE_DIRS "glm/common.hpp") find_path(STB_INCLUDE_DIRS "stb_c_lexer.h") +find_path(VULKAN_HEADERS_INCLUDE_DIRS "vk_video/vulkan_video_codec_h264std.h") + +if (ATLAS_HEADLESS) +set(ATLAS_ENGINE_COMPILE_DEFINITIONS ${ATLAS_ENGINE_COMPILE_DEFINITIONS} AE_HEADLESS) +endif() + +if (ATLAS_BINDLESS) +set(ATLAS_ENGINE_COMPILE_DEFINITIONS ${ATLAS_ENGINE_COMPILE_DEFINITIONS} AE_BINDLESS) +endif() # Include directories and definitions ############################################################# target_compile_definitions(${PROJECT_NAME} PUBLIC ${ATLAS_ENGINE_COMPILE_DEFINITIONS}) target_include_directories(${PROJECT_NAME} PUBLIC + ${VULKAN_HEADERS_INCLUDE_DIRS} ${STB_INCLUDE_DIRS} ${GLM_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libs) + +message(${VULKAN_HEADERS_INCLUDE_DIRS}) # Link libraries ################################################################################## target_link_libraries(${PROJECT_NAME} LINK_PUBLIC ${ATLAS_ENGINE_LIBS} LINK_PRIVATE ${ATLAS_ENGINE_SYSTEM_LIBS}) diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 8e3323643..ea14b118c 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -31,23 +31,38 @@ namespace Atlas { Graphics::ShaderCompiler::Init(); - // First need to get a window to retrieve the title +#ifndef AE_HEADLESS DefaultWindow = new Window("Default window", AE_WINDOWPOSITION_UNDEFINED, - AE_WINDOWPOSITION_UNDEFINED, 100, 100, - SDL_WINDOW_VULKAN | AE_WINDOW_HIDDEN, false); + AE_WINDOWPOSITION_UNDEFINED, 100, 100, AE_WINDOW_HIDDEN); +#endif // Then create graphics instance + auto instanceDesc = Graphics::InstanceDesc{ + .instanceName = "AtlasEngineInstance", #ifdef AE_BUILDTYPE_RELEASE - Graphics::Instance::DefaultInstance = new Graphics::Instance("AtlasEngineInstance", false); + .enableValidationLayers = false, #else - Graphics::Instance::DefaultInstance = new Graphics::Instance("AtlasEngineInstance", false); + .enableValidationLayers = true, #endif + .validationLayerSeverity = config.validationLayerSeverity + }; + + Graphics::Instance::DefaultInstance = new Graphics::Instance(instanceDesc); + Graphics::Surface* surface; // Initialize window surface - DefaultWindow->CreateSurface(); +#ifndef AE_HEADLESS + surface = Graphics::Instance::DefaultInstance->CreateSurface(DefaultWindow->GetSDLWindow()); +#else + surface = Graphics::Instance::DefaultInstance->CreateHeadlessSurface(); +#endif // Initialize device - Graphics::Instance::DefaultInstance->InitializeGraphicsDevice(DefaultWindow->surface); + Graphics::Instance::DefaultInstance->InitializeGraphicsDevice(surface); Graphics::GraphicsDevice::DefaultDevice = Graphics::Instance::DefaultInstance->GetGraphicsDevice(); +#ifndef AE_HEADLESS + delete Engine::DefaultWindow; +#endif + Graphics::Extensions::Process(); // Do the setup for all the classes that need static setup @@ -59,12 +74,6 @@ namespace Atlas { Clock::Update(); - // Only then create engine instance. This makes sure that the engine instance already - // has access to all graphics functionality and all other functionality on construction - auto engineInstance = GetEngineInstance(); - EngineInstance::instance = engineInstance; - - } void Engine::Shutdown() { diff --git a/src/engine/Engine.h b/src/engine/Engine.h index b31108c8f..50bae3354 100644 --- a/src/engine/Engine.h +++ b/src/engine/Engine.h @@ -29,6 +29,11 @@ namespace Atlas { * @note This path must be relative to the asset directory */ std::string shaderDirectory = "shader"; + + /** + * Configures which messages of the validation layers to log + */ + Log::Severity validationLayerSeverity = Log::SEVERITY_LOW; }; class Engine { diff --git a/src/engine/EngineInstance.cpp b/src/engine/EngineInstance.cpp index c5c34c002..c5ca49a85 100644 --- a/src/engine/EngineInstance.cpp +++ b/src/engine/EngineInstance.cpp @@ -1,24 +1,23 @@ #include "EngineInstance.h" +#include "graphics/Instance.h" namespace Atlas { - EngineInstance* EngineInstance::instance; - EngineInstance::EngineInstance(const std::string& instanceName, int32_t windowWidth, int32_t windowHeight, int32_t flags, bool createMainRenderer) : window(instanceName, AE_WINDOWPOSITION_UNDEFINED, AE_WINDOWPOSITION_UNDEFINED, windowWidth, windowHeight, flags) { graphicsDevice = Graphics::GraphicsDevice::DefaultDevice; + // If we're in headless mode, we already have a valid surface +#ifndef AE_HEADLESS // Only create swap chain after the engine instance window was created and // it's surface was assigned to the default graphics device - graphicsDevice->surface = window.surface; + auto graphicsInstance = Graphics::Instance::DefaultInstance; + graphicsDevice->surface = graphicsInstance->CreateSurface(window.GetSDLWindow()); +#endif graphicsDevice->CreateSwapChain(); - // Clean up old invisible default window and assign this one - delete Engine::DefaultWindow; - Engine::DefaultWindow = &window; - if(createMainRenderer) { mainRenderer = std::make_unique(); mainRenderer->Init(graphicsDevice); @@ -56,10 +55,4 @@ namespace Atlas { } - EngineInstance* EngineInstance::GetInstance() { - - return instance; - - } - } \ No newline at end of file diff --git a/src/engine/EngineInstance.h b/src/engine/EngineInstance.h index d25f5ff9d..9f1da8d69 100644 --- a/src/engine/EngineInstance.h +++ b/src/engine/EngineInstance.h @@ -66,7 +66,7 @@ namespace Atlas { const static EngineConfig engineConfig; /** - * The main window to which the context is attached to + * The main window */ Window window; @@ -76,8 +76,6 @@ namespace Atlas { */ std::vector args; - static EngineInstance* GetInstance(); - protected: /** * Returns the size of the screen. @@ -92,12 +90,9 @@ namespace Atlas { Graphics::GraphicsDevice* graphicsDevice = nullptr; - Scope mainRenderer = nullptr; + Graphics::Surface* surface = nullptr; - /** - * The main instance, needs to be implemented by user - */ - static EngineInstance* instance; + Scope mainRenderer = nullptr; std::vector displays; diff --git a/src/engine/Main.cpp b/src/engine/Main.cpp index a17e8c60d..15a29abc7 100644 --- a/src/engine/Main.cpp +++ b/src/engine/Main.cpp @@ -9,8 +9,11 @@ #ifdef AE_OS_WINDOWS #include +#include #endif +extern Atlas::EngineInstance* GetEngineInstance(); + int main(int argc, char* argv[]) { // Automatically change working directory to load @@ -24,6 +27,13 @@ int main(int argc, char* argv[]) { #endif } +#if defined(AE_OS_MACOS) && defined(AE_BINDLESS) + setenv("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "2", 1); + setenv("MVK_DEBUG", "0", 1); +#endif + + // SetEnvironmentVariable("VK_ICD_FILENAMES", "C:\\Users\\tippe\\Documents\\Repos\\swiftshader\\build\\Windows\\vk_swiftshader_icd.json"); + Atlas::Engine::Init(Atlas::EngineInstance::engineConfig); auto graphicsInstance = Atlas::Graphics::Instance::DefaultInstance; @@ -34,7 +44,9 @@ int main(int argc, char* argv[]) { return 0; } - auto engineInstance = Atlas::EngineInstance::GetInstance(); + // Only then create engine instance. This makes sure that the engine instance already + // has access to all graphics functionality and all other functionality on construction + auto engineInstance = GetEngineInstance(); if (!engineInstance) { Atlas::Log::Warning("Shutdown of application"); Atlas::Engine::Shutdown(); diff --git a/src/engine/RenderTarget.cpp b/src/engine/RenderTarget.cpp index 37737ba15..08644c3e1 100644 --- a/src/engine/RenderTarget.cpp +++ b/src/engine/RenderTarget.cpp @@ -93,13 +93,13 @@ namespace Atlas { }; for (auto &attachment: colorAttachments) { attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } Graphics::RenderPassDepthAttachment depthAttachment = { .imageFormat = oceanDepthTexture.format, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; @@ -112,6 +112,7 @@ namespace Atlas { CreateFrameBuffers(); + SetGIResolution(HALF_RES); SetAOResolution(HALF_RES); SetVolumetricResolution(HALF_RES); SetReflectionResolution(HALF_RES); @@ -135,7 +136,10 @@ namespace Atlas { hdrTexture.Resize(width, height); postProcessTexture.Resize(width, height); sssTexture.Resize(width, height); + oceanDepthTexture.Resize(width, height); + oceanStencilTexture.Resize(width, height); + SetGIResolution(giResolution); SetAOResolution(aoResolution); SetVolumetricResolution(volumetricResolution); SetReflectionResolution(reflectionResolution); @@ -190,6 +194,31 @@ namespace Atlas { } + void RenderTarget::SetGIResolution(RenderResolution resolution) { + + auto res = GetRelativeResolution(resolution); + giResolution = resolution; + + giTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + swapGiTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + historyGiTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + + giLengthTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + historyGiLengthTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + + } + + RenderResolution RenderTarget::GetGIResolution() { + + return giResolution; + + } + void RenderTarget::SetAOResolution(RenderResolution resolution) { auto res = GetRelativeResolution(resolution); diff --git a/src/engine/RenderTarget.h b/src/engine/RenderTarget.h index aeb027f97..ed3b68bee 100644 --- a/src/engine/RenderTarget.h +++ b/src/engine/RenderTarget.h @@ -116,6 +116,16 @@ namespace Atlas { */ ivec2 GetRelativeResolution(RenderResolution resolution); + /* + * Sets the render resolution for screen space global illumination + */ + void SetGIResolution(RenderResolution resolution); + + /* + * Gets the render resolution for screen space global illumination + */ + RenderResolution GetGIResolution(); + /* * Sets the render resolution for screen space ambient occlusion */ @@ -172,6 +182,12 @@ namespace Atlas { Texture::Texture2D postProcessTexture; + Texture::Texture2D giTexture; + Texture::Texture2D swapGiTexture; + Texture::Texture2D historyGiTexture; + Texture::Texture2D giLengthTexture; + Texture::Texture2D historyGiLengthTexture; + Texture::Texture2D aoTexture; Texture::Texture2D swapAoTexture; Texture::Texture2D historyAoTexture; @@ -212,6 +228,7 @@ namespace Atlas { int32_t width = 0; int32_t height = 0; + RenderResolution giResolution; RenderResolution aoResolution; RenderResolution volumetricResolution; RenderResolution reflectionResolution; diff --git a/src/engine/System.h b/src/engine/System.h index 9376d4ed4..3aa0e3dbe 100644 --- a/src/engine/System.h +++ b/src/engine/System.h @@ -30,6 +30,8 @@ #endif +#define AE_ASSERT assert + // SDL #ifdef AE_NO_APP #define SDL_MAIN_HANDLED diff --git a/src/engine/Window.cpp b/src/engine/Window.cpp index fef68e840..b2cab3c91 100644 --- a/src/engine/Window.cpp +++ b/src/engine/Window.cpp @@ -6,9 +6,10 @@ namespace Atlas { Window::Window(const std::string& title, int32_t x, int32_t y, int32_t width, int32_t height, - int32_t flags, bool createSurface) : title(title), x(x == AE_WINDOWPOSITION_UNDEFINED ? 0 : x), + int32_t flags) : title(title), x(x == AE_WINDOWPOSITION_UNDEFINED ? 0 : x), y(y == AE_WINDOWPOSITION_UNDEFINED ? 0 : y), width(width), height(height) { +#ifndef AE_HEADLESS sdlWindow = SDL_CreateWindow(title.c_str(), x, y, width, height, flags | SDL_WINDOW_VULKAN); if (!sdlWindow) { auto error = SDL_GetError(); @@ -16,9 +17,8 @@ namespace Atlas { return; } - if (createSurface) CreateSurface(); - ID = SDL_GetWindowID(sdlWindow); +#endif auto windowEventHandler = std::bind(&Window::WindowEventHandler, this, std::placeholders::_1); windowEventSubcriberID = Events::EventManager::WindowEventDelegate.Subscribe(windowEventHandler); @@ -199,20 +199,6 @@ namespace Atlas { } - bool Window::CreateSurface() { - - bool success = false; - auto graphicsInstance = Graphics::Instance::DefaultInstance; - surface = graphicsInstance->CreateSurface(sdlWindow); - if (!surface) { - Log::Error("Error initializing window surface"); - return false; - } - - return true; - - } - SDL_Window* Window::GetSDLWindow() { return sdlWindow; diff --git a/src/engine/Window.h b/src/engine/Window.h index 69a7b2401..fb30d8081 100644 --- a/src/engine/Window.h +++ b/src/engine/Window.h @@ -40,10 +40,9 @@ namespace Atlas { * @param width The width of the window in pixels * @param height The height of the window in pixels * @param flags Window flags. See {@link Window.h} for more. - * @param createSurface Whether or not to immediately create a surface */ Window(const std::string& title, int32_t x, int32_t y, int32_t width, int32_t height, - int32_t flags = AE_WINDOW_FULLSCREEN, bool createSurface = true); + int32_t flags = AE_WINDOW_FULLSCREEN); ~Window(); @@ -156,11 +155,6 @@ namespace Atlas { */ void SetDisplayMode(const DisplayMode& mode); - /** - * Creates a surface for the window to render to. - */ - bool CreateSurface(); - /** * Returns the pointer to the SDL window structure */ @@ -168,8 +162,6 @@ namespace Atlas { const std::string title; - Graphics::Surface* surface; - Events::EventDelegate windowEventDelegate; Events::EventDelegate keyboardEventDelegate; Events::EventDelegate mouseButtonEventDelegate; diff --git a/src/engine/common/Image.h b/src/engine/common/Image.h index 2cc4c6add..7139718be 100644 --- a/src/engine/common/Image.h +++ b/src/engine/common/Image.h @@ -427,7 +427,7 @@ namespace Atlas { template void Image::SetData(int32_t x, int32_t y, S data) { - assert(sizeof(S) / 4 == size_t(channels) && "Data can't be fitted into channels"); + AE_ASSERT(sizeof(S) / 4 == size_t(channels) && "Data can't be fitted into channels"); if constexpr (std::is_integral_v && (std::is_same_v || std::is_same_v || std::is_same_v)) { diff --git a/src/engine/graphics/CommandList.cpp b/src/engine/graphics/CommandList.cpp index 98a96f0ef..784daef6d 100644 --- a/src/engine/graphics/CommandList.cpp +++ b/src/engine/graphics/CommandList.cpp @@ -33,6 +33,8 @@ namespace Atlas { descriptorPool = new DescriptorPool(device); + CreatePlaceholders(device); + isComplete = true; } @@ -69,6 +71,9 @@ namespace Atlas { VK_CHECK(vkBeginCommandBuffer(commandBuffer, &cmdBeginInfo)) + // This is only effective for the first time it's called + ChangePlaceholderLayouts(); + } void CommandList::EndCommands() { @@ -246,6 +251,25 @@ namespace Atlas { void CommandList::BindPipeline(const Ref& pipeline) { + // Reset previous descriptor data such that a new descriptor + // set must be provided to the new pipeline only if descriptor set changes + if (pipelineInUse != nullptr) { + for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) { + bool changed = (descriptorBindingData.layouts[i].get() != pipeline->shader->sets[i].layout.get() + && pipeline->shader->sets[i].bindingCount > 0); + descriptorBindingData.changed[i] |= changed; + + if (pipeline->shader->sets[i].bindingCount > 0) { + descriptorBindingData.layouts[i] = pipeline->shader->sets[i].layout; + } + } + } + else { + for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) + descriptorBindingData.changed[i] = true; + } + + pipelineInUse = pipeline; vkCmdBindPipeline(commandBuffer, pipeline->bindPoint, pipeline->pipeline); @@ -257,16 +281,11 @@ namespace Atlas { SetScissor(0, 0, extent.width, extent.height); } - // Reset previous descriptor data such that a new descriptor - // set must be provided to the new pipeline - for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) - descriptorBindingData.changed[i] = true; - } void CommandList::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse) return; VkViewport viewport = {}; @@ -283,7 +302,7 @@ namespace Atlas { void CommandList::SetScissor(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse) return; VkRect2D scissor = {}; @@ -347,7 +366,7 @@ namespace Atlas { void CommandList::PushConstants(const std::string& pushConstantRangeName, void *data) { - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse) return; auto pushConstantRange = pipelineInUse->shader->GetPushConstantRange(pushConstantRangeName); @@ -360,9 +379,9 @@ namespace Atlas { void CommandList::BindIndexBuffer(const Ref& buffer, VkIndexType type) { - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse || !buffer->buffer) return; - assert(buffer->size > 0 && "Invalid buffer size"); + AE_ASSERT(buffer->size > 0 && "Invalid buffer size"); vkCmdBindIndexBuffer(commandBuffer, buffer->buffer, 0, type); @@ -370,9 +389,9 @@ namespace Atlas { void CommandList::BindVertexBuffer(const Ref& buffer, uint32_t binding) { - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse || !buffer->buffer) return; - assert(buffer->size > 0 && "Invalid buffer size"); + AE_ASSERT(buffer->size > 0 && "Invalid buffer size"); VkDeviceSize offset = 0; vkCmdBindVertexBuffers(commandBuffer, binding, 1, &buffer->buffer, &offset); @@ -382,14 +401,14 @@ namespace Atlas { void CommandList::BindVertexBuffers(std::vector> &buffers, uint32_t bindingOffset, uint32_t bindingCount) { - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse) return; std::vector offsets; std::vector bindBuffers; for (const auto& buffer : buffers) { if (!buffer->buffer) return; - assert(buffer->size > 0 && "Invalid buffer size"); + AE_ASSERT(buffer->size > 0 && "Invalid buffer size"); bindBuffers.push_back(buffer->buffer); offsets.push_back(0); } @@ -400,161 +419,175 @@ namespace Atlas { void CommandList::BindBuffer(const Ref& buffer, uint32_t set, uint32_t binding) { - assert(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); - assert(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); - assert(buffer->size > 0 && "Invalid buffer size"); - - // Only uniform buffers support dynamic binding for now - if (buffer->usageFlags & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) { - if (descriptorBindingData.dynamicBuffers[set][binding].first == buffer.get()) - return; - - // Since the buffer is partially owned by the device, we can safely get the pointer for this frame - descriptorBindingData.dynamicBuffers[set][binding] = {buffer.get(), 0}; - descriptorBindingData.buffers[set][binding] = nullptr; - descriptorBindingData.sampledImages[set][binding] = {nullptr, nullptr}; - descriptorBindingData.images[set][binding] = { nullptr, 0u }; - descriptorBindingData.tlases[set][binding] = nullptr; - descriptorBindingData.changed[set] = true; - } - else { - if (descriptorBindingData.buffers[set][binding] == buffer.get()) - return; - - // Since the buffer is partially owned by the device, we can safely get the pointer for this frame - descriptorBindingData.buffers[set][binding] = buffer.get(); - descriptorBindingData.dynamicBuffers[set][binding] = {nullptr, 0}; - descriptorBindingData.sampledImages[set][binding] = {nullptr, nullptr}; - descriptorBindingData.images[set][binding] = { nullptr, 0u }; - descriptorBindingData.tlases[set][binding] = nullptr; - descriptorBindingData.changed[set] = true; - } + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); + AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); + AE_ASSERT(buffer->size > 0 && "Invalid buffer size"); + + if (descriptorBindingData.buffers[set][binding].first == buffer.get()) + return; + + descriptorBindingData.ResetBinding(set, binding); + + // Since the buffer is partially owned by the device, we can safely get the pointer for this frame + descriptorBindingData.buffers[set][binding] = { buffer.get(), 0u }; + descriptorBindingData.changed[set] = true; } void CommandList::BindBufferOffset(const Ref &buffer, size_t offset, uint32_t set, uint32_t binding) { - assert(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); - assert(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); - assert(buffer->size > 0 && "Invalid buffer size"); - assert(buffer->usageFlags & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT && + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); + AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); + AE_ASSERT(buffer->size > 0 && "Invalid buffer size"); + AE_ASSERT(buffer->usageFlags & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT && "Only uniform buffers support dynamic bindings"); - // Only indicate a change of the buffer has changed, not just the offset - descriptorBindingData.changed[set] |= - descriptorBindingData.dynamicBuffers[set][binding].first != buffer.get(); + if (descriptorBindingData.buffers[set][binding].first == buffer.get()) + return; + + descriptorBindingData.ResetBinding(set, binding); + // Since the buffer is partially owned by the device, we can safely get the pointer for this frame - descriptorBindingData.dynamicBuffers[set][binding] = {buffer.get(), uint32_t(offset)}; - descriptorBindingData.buffers[set][binding] = nullptr; - descriptorBindingData.sampledImages[set][binding] = {nullptr, nullptr}; - descriptorBindingData.images[set][binding] = { nullptr, 0u }; - descriptorBindingData.tlases[set][binding] = nullptr; + descriptorBindingData.buffers[set][binding] = { buffer.get(), uint32_t(offset) }; + descriptorBindingData.changed[set] = true; } - void CommandList::BindBuffer(const Ref& buffer, uint32_t set, uint32_t binding) { + void CommandList::BindBuffers(const std::vector> &buffers, uint32_t set, uint32_t binding) { - assert(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); - assert(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); - assert(buffer->size > 0 && "Invalid buffer size"); - - // Only uniform buffers support dynamic binding for now - if (buffer->GetCurrent()->usageFlags & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) { - if (descriptorBindingData.dynamicBuffers[set][binding].first == buffer->GetCurrent()) - return; - - // Since the buffer is partially owned by the device, we can safely get the pointer for this frame - descriptorBindingData.dynamicBuffers[set][binding] = {buffer->GetCurrent(), 0}; - descriptorBindingData.buffers[set][binding] = nullptr; - descriptorBindingData.sampledImages[set][binding] = {nullptr, nullptr}; - descriptorBindingData.images[set][binding] = { nullptr, 0u }; - descriptorBindingData.tlases[set][binding] = nullptr; - descriptorBindingData.changed[set] = true; - } - else { - if (descriptorBindingData.buffers[set][binding] == buffer->GetCurrent()) - return; - - // Since the buffer is partially owned by the device, we can safely get the pointer for this frame - descriptorBindingData.buffers[set][binding] = buffer->GetCurrent(); - descriptorBindingData.dynamicBuffers[set][binding] = {nullptr, 0}; - descriptorBindingData.sampledImages[set][binding] = {nullptr, nullptr}; - descriptorBindingData.images[set][binding] = { nullptr, 0u }; - descriptorBindingData.tlases[set][binding] = nullptr; - descriptorBindingData.changed[set] = true; + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); + AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); + + if (!buffers.size()) return; + + std::vector buffersPtr; + for (auto& buffer : buffers) { + AE_ASSERT(buffer->size > 0 && "Invalid buffer size"); + AE_ASSERT(buffer->usageFlags & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT && + "Only storage buffers support array bindings"); + + buffersPtr.push_back(buffer.get()); } + + descriptorBindingData.ResetBinding(set, binding); + + // Since the buffer is partially owned by the device, we can safely get the pointer for this frame + descriptorBindingData.buffersArray[set][binding] = { buffersPtr }; + descriptorBindingData.changed[set] = true; + + } + + void CommandList::BindBuffer(const Ref& buffer, uint32_t set, uint32_t binding) { + + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); + AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); + AE_ASSERT(buffer->size > 0 && "Invalid buffer size"); + + if (descriptorBindingData.buffers[set][binding].first == buffer->GetCurrent()) + return; + + descriptorBindingData.ResetBinding(set, binding); + + // Since the buffer is partially owned by the device, we can safely get the pointer for this frame + descriptorBindingData.buffers[set][binding] = { buffer->GetCurrent(), 0 }; + descriptorBindingData.changed[set] = true; } void CommandList::BindBufferOffset(const Ref &buffer, size_t offset, uint32_t set, uint32_t binding) { - assert(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); - assert(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); - assert(buffer->size > 0 && "Invalid buffer size"); - assert(buffer->GetCurrent()->usageFlags & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT && + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); + AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); + AE_ASSERT(buffer->size > 0 && "Invalid buffer size"); + AE_ASSERT(buffer->GetCurrent()->usageFlags & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT && "Only uniform buffers support dynamic bindings"); + descriptorBindingData.ResetBinding(set, binding); + // Only indicate a change of the buffer has changed, not just the offset descriptorBindingData.changed[set] |= - descriptorBindingData.dynamicBuffers[set][binding].first != buffer->GetCurrent(); + descriptorBindingData.buffers[set][binding].first != buffer->GetCurrent(); // Since the buffer is partially owned by the device, we can safely get the pointer for this frame - descriptorBindingData.dynamicBuffers[set][binding] = {buffer->GetCurrent(), uint32_t(offset)}; - descriptorBindingData.buffers[set][binding] = nullptr; - descriptorBindingData.sampledImages[set][binding] = {nullptr, nullptr}; - descriptorBindingData.images[set][binding] = { nullptr, 0u }; - descriptorBindingData.tlases[set][binding] = nullptr; + descriptorBindingData.buffers[set][binding] = {buffer->GetCurrent(), uint32_t(offset)}; } void CommandList::BindImage(const Ref& image, uint32_t set, uint32_t binding, uint32_t mipLevel) { - assert(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); - assert(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); - assert(mipLevel < image->mipLevels && "Invalid mip level selected"); + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); + AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); + AE_ASSERT(mipLevel < image->mipLevels && "Invalid mip level selected"); if (descriptorBindingData.images[set][binding].first == image.get() && descriptorBindingData.images[set][binding].second == mipLevel) return; + descriptorBindingData.ResetBinding(set, binding); + descriptorBindingData.images[set][binding] = { image.get(), mipLevel }; - descriptorBindingData.buffers[set][binding] = nullptr; - descriptorBindingData.dynamicBuffers[set][binding] = {nullptr, 0}; - descriptorBindingData.sampledImages[set][binding] = { nullptr, nullptr }; - descriptorBindingData.tlases[set][binding] = nullptr; descriptorBindingData.changed[set] = true; } void CommandList::BindImage(const Ref& image, const Ref& sampler, uint32_t set, uint32_t binding) { - assert(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); - assert(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); + AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); if (descriptorBindingData.sampledImages[set][binding].first == image.get() && descriptorBindingData.sampledImages[set][binding].second == sampler.get()) return; + descriptorBindingData.ResetBinding(set, binding); + descriptorBindingData.sampledImages[set][binding] = { image.get(), sampler.get() }; - descriptorBindingData.buffers[set][binding] = nullptr; - descriptorBindingData.dynamicBuffers[set][binding] = {nullptr, 0}; - descriptorBindingData.images[set][binding] = { nullptr, 0u }; - descriptorBindingData.tlases[set][binding] = nullptr; + descriptorBindingData.changed[set] = true; + + } + + void CommandList::BindSampledImages(const std::vector>& images, uint32_t set, uint32_t binding) { + + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); + AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); + + if (!images.size()) return; + + std::vector imagesPtr; + for (auto& image : images) imagesPtr.push_back(image.get()); + + descriptorBindingData.ResetBinding(set, binding); + + // We don't do any checks here if the same things were bound already + descriptorBindingData.sampledImagesArray[set][binding] = { imagesPtr }; + descriptorBindingData.changed[set] = true; + + } + + void CommandList::BindSampler(const Ref& sampler, uint32_t set, uint32_t binding) { + + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); + AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); + + if (descriptorBindingData.samplers[set][binding] == sampler.get()) + return; + + descriptorBindingData.ResetBinding(set, binding); + + descriptorBindingData.samplers[set][binding] = sampler.get(); descriptorBindingData.changed[set] = true; } void CommandList::BindTLAS(const Ref &tlas, uint32_t set, uint32_t binding) { - assert(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); - assert(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); + AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); if (descriptorBindingData.tlases[set][binding] == tlas.get()) return; + descriptorBindingData.ResetBinding(set, binding); + descriptorBindingData.tlases[set][binding] = tlas.get(); - descriptorBindingData.buffers[set][binding] = nullptr; - descriptorBindingData.dynamicBuffers[set][binding] = {nullptr, 0u}; - descriptorBindingData.images[set][binding] = { nullptr, 0u }; - descriptorBindingData.sampledImages[set][binding] = { nullptr, nullptr }; descriptorBindingData.changed[set] = true; } @@ -700,10 +733,10 @@ namespace Atlas { void CommandList::DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) { - assert((swapChainInUse || renderPassInUse) && "No render pass is in use"); - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT((swapChainInUse || renderPassInUse) && "No render pass is in use"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse) return; - assert(indexCount && instanceCount && "Index or instance count should not be zero"); + AE_ASSERT(indexCount && instanceCount && "Index or instance count should not be zero"); BindDescriptorSets(); @@ -714,8 +747,8 @@ namespace Atlas { void CommandList::DrawIndexedIndirect(const Ref &buffer, size_t offset, uint32_t drawCount, uint32_t stride) { - assert((swapChainInUse || renderPassInUse) && "No render pass is in use"); - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT((swapChainInUse || renderPassInUse) && "No render pass is in use"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse) return; BindDescriptorSets(); @@ -727,9 +760,9 @@ namespace Atlas { void CommandList::Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) { - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse) return; - assert(vertexCount && instanceCount && "Index or instance count should not be zero"); + AE_ASSERT(vertexCount && instanceCount && "Index or instance count should not be zero"); BindDescriptorSets(); @@ -739,10 +772,10 @@ namespace Atlas { void CommandList::Dispatch(uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) { - assert(!swapChainInUse && !renderPassInUse && "No render pass should be in use for compute commands"); - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT(!swapChainInUse && !renderPassInUse && "No render pass should be in use for compute commands"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse) return; - assert(groupCountX && groupCountY && groupCountZ && "Group counts have to be larger equal to one"); + AE_ASSERT(groupCountX && groupCountY && groupCountZ && "Group counts have to be larger equal to one"); BindDescriptorSets(); @@ -752,8 +785,8 @@ namespace Atlas { void CommandList::DispatchIndirect(const Ref &buffer, uint32_t offset) { - assert(!swapChainInUse && !renderPassInUse && "No render pass should be in use for compute commands"); - assert(pipelineInUse && "No pipeline is bound"); + AE_ASSERT(!swapChainInUse && !renderPassInUse && "No render pass should be in use for compute commands"); + AE_ASSERT(pipelineInUse && "No pipeline is bound"); if (!pipelineInUse) return; BindDescriptorSets(); @@ -864,10 +897,10 @@ namespace Atlas { void CommandList::BindDescriptorSets() { - VkWriteDescriptorSet setWrites[BINDINGS_PER_DESCRIPTOR_SET]; - VkDescriptorBufferInfo bufferInfos[BINDINGS_PER_DESCRIPTOR_SET]; - VkDescriptorImageInfo imageInfos[BINDINGS_PER_DESCRIPTOR_SET]; - VkWriteDescriptorSetAccelerationStructureKHR tlasInfos[BINDINGS_PER_DESCRIPTOR_SET]; + auto& setWrites = descriptorBindingData.setWrites; + auto& bufferInfos = descriptorBindingData.bufferInfos; + auto& imageInfos = descriptorBindingData.imageInfos; + auto& tlasInfos = descriptorBindingData.tlasInfos; uint32_t dynamicOffsets[BINDINGS_PER_DESCRIPTOR_SET]; @@ -879,17 +912,17 @@ namespace Atlas { // We need to collect the dynamic offsets everytime we bind a descriptor set // This also means if just the offset changed, we don't need to update the set for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { - if (!descriptorBindingData.dynamicBuffers[i][j].first) continue; + if (!descriptorBindingData.buffers[i][j].first) continue; const auto& binding = shader->sets[i].bindings[j]; // This probably is an old binding, which isn't used by this shader if (!binding.valid) continue; // Check that the descriptor types match up - const auto descriptorType = binding.layoutBinding.descriptorType; + const auto descriptorType = binding.binding.descriptorType; if (descriptorType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC && descriptorType != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) continue; - auto [_, offset] = descriptorBindingData.dynamicBuffers[i][j]; + auto [_, offset] = descriptorBindingData.buffers[i][j]; dynamicOffsets[dynamicOffsetCounter++] = offset; } @@ -897,56 +930,33 @@ namespace Atlas { // We could run into issue if (descriptorBindingData.changed[i] && shader->sets[i].bindingCount > 0) { uint32_t bindingCounter = 0; + uint32_t imageInfoCounter = 0; + uint32_t bufferInfoCounter = 0; descriptorBindingData.changed[i] = false; descriptorBindingData.sets[i] = descriptorPool->GetCachedSet(shader->sets[i].layout); - // DYNAMIC BUFFER - for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { - if (!descriptorBindingData.dynamicBuffers[i][j].first) continue; - const auto& binding = shader->sets[i].bindings[j]; - // This probably is an old binding, which isn't used by this shader - if (!binding.valid) continue; - // Check that the descriptor types match up - auto descriptorType = binding.layoutBinding.descriptorType; - if (descriptorType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC && - descriptorType != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) continue; - - auto [buffer, offset] = descriptorBindingData.dynamicBuffers[i][j]; - - auto& bufferInfo = bufferInfos[bindingCounter]; - bufferInfo.offset = 0; - bufferInfo.buffer = buffer->buffer; - bufferInfo.range = binding.size ? std::min(binding.size, uint32_t(buffer->size)) : VK_WHOLE_SIZE; - - auto& setWrite = setWrites[bindingCounter++]; - setWrite = {}; - setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - setWrite.pNext = nullptr; - setWrite.dstBinding = j; - setWrite.dstArrayElement = binding.arrayElement; - setWrite.dstSet = descriptorBindingData.sets[i]; - setWrite.descriptorCount = 1; - setWrite.descriptorType = descriptorType; - setWrite.pBufferInfo = &bufferInfo; - } - // BUFFER for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { - if (!descriptorBindingData.buffers[i][j]) continue; - const auto& binding = shader->sets[i].bindings[j]; + if (!descriptorBindingData.buffers[i][j].first) continue; + const auto& shaderBinding = shader->sets[i].bindings[j]; // This probably is an old binding, which isn't used by this shader - if (!binding.valid) continue; + if (!shaderBinding.valid) continue; + + const auto& binding = shaderBinding.binding; // Check that the descriptor types match up - auto descriptorType = binding.layoutBinding.descriptorType; - if (descriptorType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) continue; + auto descriptorType = binding.descriptorType; + if (descriptorType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER && + descriptorType != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER && + descriptorType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC && + descriptorType != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) continue; - auto buffer = descriptorBindingData.buffers[i][j]; + auto [buffer, _] = descriptorBindingData.buffers[i][j]; - auto& bufferInfo = bufferInfos[bindingCounter]; + auto& bufferInfo = bufferInfos[bufferInfoCounter++]; bufferInfo.offset = 0; bufferInfo.buffer = buffer->buffer; - bufferInfo.range = binding.size ? std::min(binding.size, uint32_t(buffer->size)) : VK_WHOLE_SIZE; + bufferInfo.range = binding.size ? std::min(binding.size, uint64_t(buffer->size)) : VK_WHOLE_SIZE; auto& setWrite = setWrites[bindingCounter++]; setWrite = {}; @@ -954,7 +964,7 @@ namespace Atlas { setWrite.pNext = nullptr; setWrite.dstBinding = j; setWrite.dstArrayElement = binding.arrayElement; - setWrite.dstSet = descriptorBindingData.sets[i]; + setWrite.dstSet = descriptorBindingData.sets[i]->set; setWrite.descriptorCount = 1; setWrite.descriptorType = descriptorType; setWrite.pBufferInfo = &bufferInfo; @@ -964,17 +974,19 @@ namespace Atlas { for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { if (!descriptorBindingData.sampledImages[i][j].first || !descriptorBindingData.sampledImages[i][j].second) continue; - const auto& binding = shader->sets[i].bindings[j]; + const auto& shaderBinding = shader->sets[i].bindings[j]; // This probably is an old binding, which isn't used by this shader - if (!binding.valid) continue; + if (!shaderBinding.valid) continue; + + const auto& binding = shaderBinding.binding; // Check that the descriptor types match up - const auto descriptorType = binding.layoutBinding.descriptorType; + const auto descriptorType = binding.descriptorType; if (descriptorType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) continue; auto [image, sampler] = descriptorBindingData.sampledImages[i][j]; - auto& imageInfo = imageInfos[bindingCounter]; + auto& imageInfo = imageInfos[imageInfoCounter++]; imageInfo.sampler = sampler->sampler; imageInfo.imageView = image->view; imageInfo.imageLayout = image->layout; @@ -985,7 +997,7 @@ namespace Atlas { setWrite.pNext = nullptr; setWrite.dstBinding = j; setWrite.dstArrayElement = binding.arrayElement; - setWrite.dstSet = descriptorBindingData.sets[i]; + setWrite.dstSet = descriptorBindingData.sets[i]->set; setWrite.descriptorCount = 1; setWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; setWrite.pImageInfo = &imageInfo; @@ -994,17 +1006,19 @@ namespace Atlas { // STORAGE IMAGES OR IMAGES SEPARATED FROM SAMPLER for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { if (!descriptorBindingData.images[i][j].first) continue; - const auto& binding = shader->sets[i].bindings[j]; + const auto& shaderBinding = shader->sets[i].bindings[j]; // This probably is an old binding, which isn't used by this shader - if (!binding.valid) continue; + if (!shaderBinding.valid) continue; + + const auto& binding = shaderBinding.binding; // Check that the descriptor types match up - const auto descriptorType = binding.layoutBinding.descriptorType; + const auto descriptorType = binding.descriptorType; if (descriptorType != VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) continue; auto [image, mipLevel] = descriptorBindingData.images[i][j]; - auto& imageInfo = imageInfos[bindingCounter]; + auto& imageInfo = imageInfos[imageInfoCounter++]; imageInfo.sampler = VK_NULL_HANDLE; imageInfo.imageView = mipLevel == 0 ? image->view : image->mipMapViews[mipLevel]; imageInfo.imageLayout = image->layout; @@ -1015,20 +1029,53 @@ namespace Atlas { setWrite.pNext = nullptr; setWrite.dstBinding = j; setWrite.dstArrayElement = binding.arrayElement; - setWrite.dstSet = descriptorBindingData.sets[i]; + setWrite.dstSet = descriptorBindingData.sets[i]->set; setWrite.descriptorCount = 1; setWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; setWrite.pImageInfo = &imageInfo; } + // SAMPLERS + for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { + if (!descriptorBindingData.samplers[i][j]) continue; + const auto& shaderBinding = shader->sets[i].bindings[j]; + // This probably is an old binding, which isn't used by this shader + if (!shaderBinding.valid) continue; + + const auto& binding = shaderBinding.binding; + // Check that the descriptor types match up + const auto descriptorType = binding.descriptorType; + if (descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER) + continue; + + auto sampler = descriptorBindingData.samplers[i][j]; + + auto& imageInfo = imageInfos[imageInfoCounter++]; + imageInfo.sampler = sampler->sampler; + imageInfo.imageView = VK_NULL_HANDLE; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + auto& setWrite = setWrites[bindingCounter++]; + setWrite = {}; + setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + setWrite.dstBinding = j; + setWrite.dstArrayElement = binding.arrayElement; + setWrite.dstSet = descriptorBindingData.sets[i]->set; + setWrite.descriptorCount = 1; + setWrite.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + setWrite.pImageInfo = &imageInfo; + } + // TOP-LEVEL ACCELERATION STRUCTURES for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { if (!descriptorBindingData.tlases[i][j]) continue; - const auto& binding = shader->sets[i].bindings[j]; + const auto& shaderBinding = shader->sets[i].bindings[j]; // This probably is an old binding, which isn't used by this shader - if (!binding.valid) continue; + if (!shaderBinding.valid) continue; + + const auto& binding = shaderBinding.binding; // Check that the descriptor types match up - const auto descriptorType = binding.layoutBinding.descriptorType; + const auto descriptorType = binding.descriptorType; if (descriptorType != VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR) continue; @@ -1045,19 +1092,133 @@ namespace Atlas { setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; setWrite.dstBinding = j; setWrite.dstArrayElement = binding.arrayElement; - setWrite.dstSet = descriptorBindingData.sets[i]; + setWrite.dstSet = descriptorBindingData.sets[i]->set; setWrite.descriptorCount = 1; setWrite.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; setWrite.pNext = &tlasInfo; } - vkUpdateDescriptorSets(device, bindingCounter, setWrites, 0, nullptr); + // SAMPLED IMAGE ARRAYS + for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { + auto descriptorArraySize = descriptorBindingData.sets[i]->sampledImageArraySize[j]; + + if (descriptorBindingData.sampledImagesArray[i][j].empty() && !descriptorArraySize) continue; + const auto& shaderBinding = shader->sets[i].bindings[j]; + // This probably is an old binding, which isn't used by this shader + if (!shaderBinding.valid) continue; + + const auto& binding = shaderBinding.binding; + // Check that the descriptor types match up + const auto descriptorType = binding.descriptorType; + if (descriptorType != VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) + continue; + + auto sampledImageArraySize = uint32_t(descriptorBindingData.sampledImagesArray[i][j].size()); + AE_ASSERT(size_t(imageInfoCounter + sampledImageArraySize) < imageInfos.size() && + "Too much data written into buffer infos for descriptor set update"); + + if (size_t(imageInfoCounter) + sampledImageArraySize >= imageInfos.size() || + (sampledImageArraySize == 0 && descriptorArraySize == 0)) + continue; + + auto& setWrite = setWrites[bindingCounter++]; + setWrite = {}; + setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + setWrite.pNext = nullptr; + setWrite.dstBinding = j; + setWrite.dstArrayElement = 0; + setWrite.dstSet = descriptorBindingData.sets[i]->set; + setWrite.descriptorCount = std::max(sampledImageArraySize, descriptorArraySize); + setWrite.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + setWrite.pImageInfo = &imageInfos[imageInfoCounter]; + + for (uint32_t k = 0; k < sampledImageArraySize; k++) { + auto image = descriptorBindingData.sampledImagesArray[i][j][k]; + + auto& imageInfo = imageInfos[imageInfoCounter++]; + imageInfo.sampler = VK_NULL_HANDLE; + imageInfo.imageView = image->view; + imageInfo.imageLayout = image->layout; + } + + // We need to overwrite old descriptor binding data. If we don't it might + // lead to crashes since resource in descriptor set are deleted already + if (sampledImageArraySize < descriptorBindingData.sets[i]->sampledImageArraySize[j]) { + for (uint32_t k = sampledImageArraySize; k < descriptorArraySize; k++) { + auto& imageInfo = imageInfos[imageInfoCounter++]; + imageInfo.sampler = VK_NULL_HANDLE; + imageInfo.imageView = placeholderImage->view; + imageInfo.imageLayout = placeholderImage->layout; + } + } + + descriptorBindingData.sets[i]->sampledImageArraySize[j] = sampledImageArraySize; + } + + // BUFFER ARRAYS + for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { + auto descriptorArraySize = descriptorBindingData.sets[i]->bufferArraySize[j]; + + if (descriptorBindingData.buffersArray[i][j].empty() && !descriptorArraySize) continue; + const auto& shaderBinding = shader->sets[i].bindings[j]; + // This probably is an old binding, which isn't used by this shader + if (!shaderBinding.valid) continue; + + const auto& binding = shaderBinding.binding; + // Check that the descriptor types match up + const auto descriptorType = binding.descriptorType; + if (descriptorType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) + continue; + + auto bufferArraySize = uint32_t(descriptorBindingData.buffersArray[i][j].size()); + AE_ASSERT(size_t(bufferInfoCounter + bufferArraySize) < bufferInfos.size() && + "Too much data written into buffer infos for descriptor set update"); + + if (size_t(bufferInfoCounter) + bufferArraySize >= bufferInfos.size() || + (bufferArraySize == 0 && descriptorArraySize == 0)) + continue; + + auto& setWrite = setWrites[bindingCounter++]; + setWrite = {}; + setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + setWrite.pNext = nullptr; + setWrite.dstBinding = j; + setWrite.dstArrayElement = 0; + setWrite.dstSet = descriptorBindingData.sets[i]->set; + setWrite.descriptorCount = std::max(bufferArraySize, descriptorArraySize);; + setWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + setWrite.pBufferInfo = &bufferInfos[bufferInfoCounter]; + + for (uint32_t k = 0; k < bufferArraySize; k++) { + auto buffer = descriptorBindingData.buffersArray[i][j][k]; + + auto& bufferInfo = bufferInfos[bufferInfoCounter++]; + bufferInfo.offset = 0; + bufferInfo.buffer = buffer->buffer; + bufferInfo.range = VK_WHOLE_SIZE; + } + + // We need to overwrite old descriptor binding data. If we don't it might + // lead to crashes since resource in descriptor set are deleted already + if (bufferArraySize < descriptorBindingData.sets[i]->bufferArraySize[j]) { + for (uint32_t k = bufferArraySize; k < descriptorArraySize; k++) { + auto& bufferInfo = bufferInfos[bufferInfoCounter++]; + bufferInfo.offset = 0; + bufferInfo.buffer = placeholderBuffer->buffer; + bufferInfo.range = VK_WHOLE_SIZE; + } + } + + descriptorBindingData.sets[i]->bufferArraySize[j] = bufferArraySize; + } + + vkUpdateDescriptorSets(device, bindingCounter, setWrites.data(), 0, nullptr); } if (descriptorBindingData.sets[i] != nullptr && shader->sets[i].bindingCount > 0) { vkCmdBindDescriptorSets(commandBuffer, pipelineInUse->bindPoint, - pipelineInUse->layout, i, 1, &descriptorBindingData.sets[i], + pipelineInUse->layout, i, 1, &descriptorBindingData.sets[i]->set, dynamicOffsetCounter, dynamicOffsets); } } @@ -1082,7 +1243,7 @@ namespace Atlas { extent = frameBufferInUse->extent; } else { - assert(0 && "No valid render pass found"); + AE_ASSERT(0 && "No valid render pass found"); } return extent; @@ -1096,7 +1257,7 @@ namespace Atlas { return semaphore.semaphore; } - assert(0 && "Queue not found in available semaphores"); + AE_ASSERT(0 && "Queue not found in available semaphores"); return semaphores.front().semaphore; @@ -1113,6 +1274,33 @@ namespace Atlas { } + void CommandList::CreatePlaceholders(GraphicsDevice* device) { + + auto bufferDesc = Graphics::BufferDesc { + .usageFlags = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + .size = sizeof(int) + }; + placeholderBuffer = device->CreateBuffer(bufferDesc); + + auto imageDesc = Graphics::ImageDesc { + .usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, + .width = 1, + .height = 1, + .format = VK_FORMAT_R8G8B8A8_UNORM, + }; + placeholderImage = device->CreateImage(imageDesc); + + } + + void CommandList::ChangePlaceholderLayouts() { + + // Only ever do this once after command list creation + if (placeholderImage->layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + ImageTransition(placeholderImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); + } + + } + } } \ No newline at end of file diff --git a/src/engine/graphics/CommandList.h b/src/engine/graphics/CommandList.h index ff6c8b558..67b6fd210 100644 --- a/src/engine/graphics/CommandList.h +++ b/src/engine/graphics/CommandList.h @@ -8,7 +8,7 @@ #include "RenderPass.h" #include "Pipeline.h" #include "Buffer.h" -#include "Descriptor.h" +#include "DescriptorPool.h" #include "Sampler.h" #include "QueryPool.h" @@ -83,6 +83,8 @@ namespace Atlas { void BindBufferOffset(const Ref& buffer, size_t offset, uint32_t set, uint32_t binding); + void BindBuffers(const std::vector>& buffers, uint32_t set, uint32_t binding); + void BindBuffer(const Ref& buffer, uint32_t set, uint32_t binding); void BindBufferOffset(const Ref& buffer, size_t offset, uint32_t set, uint32_t binding); @@ -91,6 +93,10 @@ namespace Atlas { void BindImage(const Ref& image, const Ref& sampler, uint32_t set, uint32_t binding); + void BindSampledImages(const std::vector>& images, uint32_t set, uint32_t binding); + + void BindSampler(const Ref& sampler, uint32_t set, uint32_t binding); + void BindTLAS(const Ref& tlas, uint32_t set, uint32_t binding); void ResetBindings(); @@ -184,30 +190,45 @@ namespace Atlas { private: struct DescriptorBindingData { - Buffer* buffers[DESCRIPTOR_SET_COUNT][BINDINGS_PER_DESCRIPTOR_SET]; - std::pair dynamicBuffers[DESCRIPTOR_SET_COUNT][BINDINGS_PER_DESCRIPTOR_SET]; + std::pair buffers[DESCRIPTOR_SET_COUNT][BINDINGS_PER_DESCRIPTOR_SET]; + std::vector buffersArray[DESCRIPTOR_SET_COUNT][BINDINGS_PER_DESCRIPTOR_SET]; std::pair images[DESCRIPTOR_SET_COUNT][BINDINGS_PER_DESCRIPTOR_SET]; std::pair sampledImages[DESCRIPTOR_SET_COUNT][BINDINGS_PER_DESCRIPTOR_SET]; + std::vector sampledImagesArray[DESCRIPTOR_SET_COUNT][BINDINGS_PER_DESCRIPTOR_SET]; + Sampler* samplers[DESCRIPTOR_SET_COUNT][BINDINGS_PER_DESCRIPTOR_SET]; TLAS* tlases[DESCRIPTOR_SET_COUNT][BINDINGS_PER_DESCRIPTOR_SET]; - VkDescriptorSet sets[DESCRIPTOR_SET_COUNT]; + Ref sets[DESCRIPTOR_SET_COUNT]; + Ref layouts[DESCRIPTOR_SET_COUNT]; bool changed[DESCRIPTOR_SET_COUNT]; + std::vector setWrites; + std::vector bufferInfos; + std::vector imageInfos; + std::vector tlasInfos; + DescriptorBindingData() { Reset(); for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) { sets[i] = nullptr; changed[i] = true; } + + setWrites.resize(BINDINGS_PER_DESCRIPTOR_SET); + tlasInfos.resize(BINDINGS_PER_DESCRIPTOR_SET); + bufferInfos.resize(BINDINGS_PER_DESCRIPTOR_SET * 1024); + imageInfos.resize(BINDINGS_PER_DESCRIPTOR_SET * 1024); } void Reset() { for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) { for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { - buffers[i][j] = nullptr; - dynamicBuffers[i][j] = { nullptr, 0u }; + buffers[i][j] = { nullptr, 0u }; images[i][j] = { nullptr, 0u }; sampledImages[i][j] = { nullptr, nullptr }; + sampledImagesArray[i][j] = {}; + buffersArray[i][j] = {}; + samplers[i][j] = nullptr; tlases[i][j] = nullptr; } sets[i] = nullptr; @@ -217,15 +238,28 @@ namespace Atlas { void Reset(uint32_t set) { for (uint32_t j = 0; j < BINDINGS_PER_DESCRIPTOR_SET; j++) { - buffers[set][j] = nullptr; - dynamicBuffers[set][j] = { nullptr, 0u }; + buffers[set][j] = { nullptr, 0u }; images[set][j] = { nullptr, 0u }; sampledImages[set][j] = { nullptr, nullptr }; + sampledImagesArray[set][j] = {}; + buffersArray[set][j] = {}; + samplers[set][j] = nullptr; tlases[set][j] = nullptr; } sets[set] = nullptr; changed[set] = true; } + + void ResetBinding(uint32_t set, uint32_t binding) { + buffers[set][binding] = { nullptr, 0u }; + images[set][binding] = { nullptr, 0u }; + sampledImages[set][binding] = { nullptr, nullptr }; + sampledImagesArray[set][binding] = {}; + buffersArray[set][binding] = {}; + samplers[set][binding] = nullptr; + tlases[set][binding] = nullptr; + } + }descriptorBindingData; struct Semaphore { @@ -243,10 +277,17 @@ namespace Atlas { const std::vector GetSemaphores() const; + void CreatePlaceholders(GraphicsDevice* device); + + void ChangePlaceholderLayouts(); + VkDevice device; MemoryManager* memoryManager = nullptr; DescriptorPool* descriptorPool = nullptr; + Ref placeholderBuffer = nullptr; + Ref placeholderImage = nullptr; + std::atomic_bool isLocked = true; std::atomic_bool isSubmitted = true; std::atomic_bool wasSwapChainAccessed = false; diff --git a/src/engine/graphics/Common.h b/src/engine/graphics/Common.h index 5be42e92a..2e87154a7 100644 --- a/src/engine/graphics/Common.h +++ b/src/engine/graphics/Common.h @@ -10,7 +10,7 @@ #include #define FRAME_DATA_COUNT 2 -#define DESCRIPTOR_POOL_SIZE 128 +#define DESCRIPTOR_POOL_SIZE 128u #define DESCRIPTOR_SET_COUNT 4 #define BINDINGS_PER_DESCRIPTOR_SET 32 #define MAX_COLOR_ATTACHMENTS 8 @@ -21,7 +21,17 @@ { \ Atlas::Log::Error("Detected Vulkan error: " + \ Atlas::Graphics::VkResultToString(err)); \ - assert(err == VK_SUCCESS); \ + AE_ASSERT(err == VK_SUCCESS); \ + } \ + } + +#define VK_CHECK_MESSAGE(x,y) { \ + VkResult err = x; \ + if (err) \ + { \ + Atlas::Log::Error("Detected Vulkan error: " + \ + Atlas::Graphics::VkResultToString(err)); \ + AE_ASSERT(err == VK_SUCCESS && y); \ } \ } diff --git a/src/engine/graphics/Descriptor.cpp b/src/engine/graphics/DescriptorPool.cpp similarity index 59% rename from src/engine/graphics/Descriptor.cpp rename to src/engine/graphics/DescriptorPool.cpp index 451e006d2..9c97e4d60 100644 --- a/src/engine/graphics/Descriptor.cpp +++ b/src/engine/graphics/DescriptorPool.cpp @@ -1,4 +1,4 @@ -#include "Descriptor.h" +#include "DescriptorPool.h" #include "GraphicsDevice.h" namespace Atlas { @@ -7,7 +7,8 @@ namespace Atlas { DescriptorPool::DescriptorPool(GraphicsDevice* device) : device(device) { - pools.push_back(InitPool()); + DescriptorSetSize size = {}; + pools.push_back(InitPool(size)); } @@ -19,7 +20,7 @@ namespace Atlas { } - VkDescriptorSet DescriptorPool::GetCachedSet(VkDescriptorSetLayout layout) { + Ref DescriptorPool::GetCachedSet(const Ref& layout) { // This approach might lead to memory issues. Need to release // the cached descriptors at some point @@ -38,25 +39,41 @@ namespace Atlas { } - VkDescriptorSet DescriptorPool::Allocate(VkDescriptorSetLayout layout) { + Ref DescriptorPool::Allocate(const Ref& layout) { VkDescriptorSetAllocateInfo allocInfo = {}; allocInfo.pNext = nullptr; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.descriptorPool = pools[poolIdx]; allocInfo.descriptorSetCount = 1; - allocInfo.pSetLayouts = &layout; + allocInfo.pSetLayouts = &layout->layout; + + /* + VkDescriptorSetVariableDescriptorCountAllocateInfo countInfo = {}; + std::vector bindlessDescriptorCount; + if (layout->bindless) { + for (auto& binding : layout->bindings) { + if (!binding.bindless) continue; + bindlessDescriptorCount.push_back(binding.descriptorCount); + } + + countInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO; + countInfo.descriptorSetCount = uint32_t(bindlessDescriptorCount.size()); + countInfo.pDescriptorCounts = bindlessDescriptorCount.data(); + allocInfo.pNext = &countInfo; + } + */ - VkDescriptorSet set; - auto result = vkAllocateDescriptorSets(device->device, &allocInfo, &set); + Ref set = CreateRef(); + auto result = vkAllocateDescriptorSets(device->device, &allocInfo, &set->set); // Handle the pool out of memory error by allocating a new pool if (result == VK_ERROR_OUT_OF_POOL_MEMORY) { poolIdx++; if (poolIdx == pools.size()) { - pools.push_back(InitPool()); + pools.push_back(InitPool(layout->size)); } allocInfo.descriptorPool = pools[poolIdx]; - VK_CHECK(vkAllocateDescriptorSets(device->device, &allocInfo, &set)) + VK_CHECK(vkAllocateDescriptorSets(device->device, &allocInfo, &set->set)) } else { VK_CHECK(result); } @@ -77,7 +94,7 @@ namespace Atlas { void DescriptorPool::ResetAllocationCounters() { - for (auto& [layout, allocations] : layoutAllocationsMap) { + for (auto& [_, allocations] : layoutAllocationsMap) { allocations.counter = 0; } @@ -89,20 +106,22 @@ namespace Atlas { } - VkDescriptorPool DescriptorPool::InitPool() { + VkDescriptorPool DescriptorPool::InitPool(const DescriptorSetSize& size) { std::vector sizes = { - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, DESCRIPTOR_POOL_SIZE }, - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, DESCRIPTOR_POOL_SIZE }, - { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, DESCRIPTOR_POOL_SIZE }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, DESCRIPTOR_POOL_SIZE }, - { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, DESCRIPTOR_POOL_SIZE }, - { VK_DESCRIPTOR_TYPE_SAMPLER, DESCRIPTOR_POOL_SIZE }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, std::max(DESCRIPTOR_POOL_SIZE, size.dynamicUniformBufferCount) }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, std::max(DESCRIPTOR_POOL_SIZE, size.uniformBufferCount) }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, std::max(DESCRIPTOR_POOL_SIZE, size.combinedImageSamplerCount) }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, std::max(DESCRIPTOR_POOL_SIZE, size.sampledImageCount) }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, std::max(DESCRIPTOR_POOL_SIZE, size.dynamicStorageBufferCount) }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, std::max(DESCRIPTOR_POOL_SIZE, size.storageBufferCount) }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, std::max(DESCRIPTOR_POOL_SIZE, size.storageImageCount) }, + { VK_DESCRIPTOR_TYPE_SAMPLER, std::max(DESCRIPTOR_POOL_SIZE, size.samplerCount) }, }; VkDescriptorPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.flags = 0; + poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT; poolInfo.maxSets = uint32_t(sizes.size()) * DESCRIPTOR_POOL_SIZE; poolInfo.poolSizeCount = uint32_t(sizes.size()); poolInfo.pPoolSizes = sizes.data(); diff --git a/src/engine/graphics/Descriptor.h b/src/engine/graphics/DescriptorPool.h similarity index 51% rename from src/engine/graphics/Descriptor.h rename to src/engine/graphics/DescriptorPool.h index 1c4700e5c..19ff727e5 100644 --- a/src/engine/graphics/Descriptor.h +++ b/src/engine/graphics/DescriptorPool.h @@ -1,6 +1,7 @@ #pragma once #include "Common.h" +#include "DescriptorSetLayout.h" #include #include @@ -12,6 +13,13 @@ namespace Atlas { class GraphicsDevice; class MemoryManager; + struct DescriptorSet { + VkDescriptorSet set; + + uint32_t sampledImageArraySize[BINDINGS_PER_DESCRIPTOR_SET] = {}; + uint32_t bufferArraySize[BINDINGS_PER_DESCRIPTOR_SET] = {}; + }; + class DescriptorPool { public: @@ -23,23 +31,23 @@ namespace Atlas { void ResetAllocationCounters(); - VkDescriptorSet GetCachedSet(VkDescriptorSetLayout layout); + Ref GetCachedSet(const Ref& layout); - VkDescriptorSet Allocate(VkDescriptorSetLayout layout); + Ref Allocate(const Ref& layout); VkDescriptorPool GetNativePool(); private: struct LayoutAllocations { - std::vector sets; + std::vector> sets; size_t counter = 0; }; - VkDescriptorPool InitPool(); + VkDescriptorPool InitPool(const DescriptorSetSize& size); GraphicsDevice* device; std::vector pools; - std::unordered_map layoutAllocationsMap; + std::unordered_map, LayoutAllocations> layoutAllocationsMap; uint32_t poolIdx = 0; diff --git a/src/engine/graphics/DescriptorSetLayout.cpp b/src/engine/graphics/DescriptorSetLayout.cpp new file mode 100644 index 000000000..c18031f1a --- /dev/null +++ b/src/engine/graphics/DescriptorSetLayout.cpp @@ -0,0 +1,143 @@ +#include "DescriptorSetLayout.h" + +#include "GraphicsDevice.h" + +namespace Atlas { + + namespace Graphics { + + DescriptorSetLayout::DescriptorSetLayout(GraphicsDevice* device, const DescriptorSetLayoutDesc& desc) : + device(device) { + + bindings.resize(desc.bindingCount); + layoutBindings.resize(desc.bindingCount); + layoutBindingFlags.resize(desc.bindingCount); + + bool bindlessAllowed = true; + bool bindlessNeeded = false; + + for (uint32_t i = 0; i < desc.bindingCount; i++) { + VkDescriptorSetLayoutBinding layoutBinding = {}; + layoutBinding.binding = desc.bindings[i].bindingIdx; + layoutBinding.descriptorCount = desc.bindings[i].descriptorCount; + layoutBinding.descriptorType = desc.bindings[i].descriptorType; + layoutBinding.stageFlags = desc.bindings[i].stageFlags; + + switch (layoutBinding.descriptorType) { + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + size.dynamicUniformBufferCount += layoutBinding.descriptorCount; break; + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + size.uniformBufferCount += layoutBinding.descriptorCount; break; + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + size.dynamicStorageBufferCount += layoutBinding.descriptorCount; break; + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + size.storageBufferCount += layoutBinding.descriptorCount; break; + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + size.combinedImageSamplerCount += layoutBinding.descriptorCount; break; + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + size.sampledImageCount += layoutBinding.descriptorCount; break; + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + size.storageImageCount += layoutBinding.descriptorCount; break; + case VK_DESCRIPTOR_TYPE_SAMPLER: + size.samplerCount += layoutBinding.descriptorCount; break; + default: break; + } + + bindlessAllowed &= (layoutBinding.descriptorType != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) && + (layoutBinding.descriptorType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC); + bindlessNeeded |= desc.bindings[i].bindless; + + bindings[i] = desc.bindings[i]; + layoutBindings[i] = layoutBinding; + } + + VkDescriptorSetLayoutCreateInfo setInfo = {}; + setInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + setInfo.pNext = nullptr; + setInfo.bindingCount = desc.bindingCount; + setInfo.flags = bindlessAllowed && bindlessNeeded ? + VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT : 0; + setInfo.pBindings = desc.bindingCount ? layoutBindings.data() : VK_NULL_HANDLE; + + VkDescriptorSetLayoutBindingFlagsCreateInfo extendedInfo = {}; + if (bindlessAllowed && bindlessNeeded) { + bindless = true; + + for (uint32_t i = 0; i < desc.bindingCount; i++) { + VkDescriptorBindingFlags bindingFlags = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; + + if (desc.bindings[i].bindless) { + //bindingFlags |= VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT; + } + + layoutBindingFlags[i] = bindingFlags; + } + + extendedInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO; + extendedInfo.bindingCount = desc.bindingCount; + extendedInfo.pBindingFlags = desc.bindingCount ? layoutBindingFlags.data() : VK_NULL_HANDLE; + + setInfo.pNext = &extendedInfo; + } + + VK_CHECK(vkCreateDescriptorSetLayout(device->device, &setInfo, nullptr, &layout)) + + isComplete = true; + + } + + DescriptorSetLayout::~DescriptorSetLayout() { + + vkDestroyDescriptorSetLayout(device->device, layout, nullptr); + + } + + bool DescriptorSetLayout::IsCompatible(const Ref& that) const { + + if (that->layoutBindings.size() > layoutBindings.size()) + return false; + + for (size_t i = 0; i < that->layoutBindings.size(); i++) { + + bool found = false; + const auto& otherBinding = that->bindings[i]; + + for (size_t j = 0; j < layoutBindings.size(); j++) { + const auto& binding = bindings[j]; + // Only check identical bindings + if (binding.bindingIdx != otherBinding.bindingIdx) + continue; + + if ((binding.descriptorCount != otherBinding.descriptorCount && !binding.bindless) || + binding.stageFlags != otherBinding.stageFlags) + return false; + + // All shaders automatically use dynamic uniform buffers, so + // potentially revert this change here + auto otherDescriptorType = otherBinding.descriptorType; + if (otherDescriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) + otherDescriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + + auto thisDescriptorType = binding.descriptorType; + if (thisDescriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) + thisDescriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + + if (otherDescriptorType != thisDescriptorType) + return false; + + found = true; + } + + if (!found) + return false; + + } + + return true; + + } + + } + +} \ No newline at end of file diff --git a/src/engine/graphics/DescriptorSetLayout.h b/src/engine/graphics/DescriptorSetLayout.h new file mode 100644 index 000000000..d4260ab95 --- /dev/null +++ b/src/engine/graphics/DescriptorSetLayout.h @@ -0,0 +1,76 @@ +#pragma once + +#include "Common.h" + +#include +#include + +namespace Atlas { + + namespace Graphics { + + class GraphicsDevice; + class ShaderVariant; + class DescriptorPool; + + struct DescriptorSetBinding { + uint32_t bindingIdx = 0; + VkDescriptorType descriptorType; + + uint32_t descriptorCount = 1; + VkShaderStageFlags stageFlags = VK_SHADER_STAGE_ALL; + + uint64_t size = VK_WHOLE_SIZE; + uint32_t arrayElement = 0; + + bool bindless = false; + }; + + struct DescriptorSetLayoutDesc { + DescriptorSetBinding bindings[BINDINGS_PER_DESCRIPTOR_SET]; + uint32_t bindingCount = 0; + }; + + struct DescriptorSetSize { + uint32_t dynamicUniformBufferCount = 0; + uint32_t uniformBufferCount = 0; + uint32_t dynamicStorageBufferCount = 0; + uint32_t storageBufferCount = 0; + uint32_t combinedImageSamplerCount = 0; + uint32_t sampledImageCount = 0; + uint32_t storageImageCount = 0; + uint32_t samplerCount = 0; + }; + + class DescriptorSetLayout { + + friend ShaderVariant; + friend DescriptorPool; + + public: + DescriptorSetLayout(GraphicsDevice* device, const DescriptorSetLayoutDesc& desc); + + ~DescriptorSetLayout(); + + bool IsCompatible(const Ref& that) const; + + VkDescriptorSetLayout layout = {}; + + bool isComplete = false; + bool bindless = false; + + private: + GraphicsDevice* device; + + DescriptorSetSize size = {}; + + std::vector bindings; + + std::vector layoutBindings; + std::vector layoutBindingFlags; + + }; + + } + +} \ No newline at end of file diff --git a/src/engine/graphics/Framebuffer.cpp b/src/engine/graphics/Framebuffer.cpp index 7b07813fb..81c3b2a15 100644 --- a/src/engine/graphics/Framebuffer.cpp +++ b/src/engine/graphics/Framebuffer.cpp @@ -62,24 +62,24 @@ namespace Atlas { auto& rpColorAttachment = rpColorAttachments[i]; auto& colorAttachment = colorAttachments[i]; - assert(rpColorAttachment.isValid == colorAttachment.isValid && "Framebuffer color attachment \ + AE_ASSERT(rpColorAttachment.isValid == colorAttachment.isValid && "Framebuffer color attachment \ to render pass color attachment mismatch"); if (!rpColorAttachment.isValid) continue; - assert(rpColorAttachment.imageFormat == colorAttachment.image->format && "Image format doesn't \ + AE_ASSERT(rpColorAttachment.imageFormat == colorAttachment.image->format && "Image format doesn't \ match the format of the attachment in the render pass"); // Check if we want to write to this attachment if (colorAttachment.isValid) { - assert(colorAttachment.layer < colorAttachment.image->layers && + AE_ASSERT(colorAttachment.layer < colorAttachment.image->layers && "Image doesn't contain this layer"); imageViews.push_back(colorAttachment.image->attachmentViews[colorAttachment.layer]); } } - assert(rpDepthAttachment.isValid == depthAttachment.isValid && "Framebuffer depth attachment \ + AE_ASSERT(rpDepthAttachment.isValid == depthAttachment.isValid && "Framebuffer depth attachment \ to render pass depth attachment mismatch"); if (depthAttachment.isValid) { - assert(depthAttachment.layer < depthAttachment.image->layers && + AE_ASSERT(depthAttachment.layer < depthAttachment.image->layers && "Image doesn't contain this layer"); imageViews.push_back(depthAttachment.image->attachmentViews[depthAttachment.layer]); } @@ -109,8 +109,8 @@ namespace Atlas { void FrameBuffer::ChangeColorAttachmentImage(const Ref &image, const uint32_t slot) { - assert(slot < MAX_COLOR_ATTACHMENTS && "Color attachment slot is not available"); - assert(colorAttachments[slot].isValid && "Color attachment is not valid"); + AE_ASSERT(slot < MAX_COLOR_ATTACHMENTS && "Color attachment slot is not available"); + AE_ASSERT(colorAttachments[slot].isValid && "Color attachment is not valid"); colorAttachments[slot].image = image; @@ -118,8 +118,8 @@ namespace Atlas { void FrameBuffer::ChangeColorAttachmentImage(const Ref& image, const uint32_t layer, const uint32_t slot) { - assert(slot < MAX_COLOR_ATTACHMENTS && "Color attachment slot is not available"); - assert(colorAttachments[slot].isValid && "Color attachment is not valid"); + AE_ASSERT(slot < MAX_COLOR_ATTACHMENTS && "Color attachment slot is not available"); + AE_ASSERT(colorAttachments[slot].isValid && "Color attachment is not valid"); colorAttachments[slot].image = image; colorAttachments[slot].layer = layer; @@ -128,7 +128,7 @@ namespace Atlas { void FrameBuffer::ChangeDepthAttachmentImage(const Ref &image) { - assert(depthAttachment.isValid && "Depth attachment is not valid"); + AE_ASSERT(depthAttachment.isValid && "Depth attachment is not valid"); depthAttachment.image = image; @@ -136,7 +136,7 @@ namespace Atlas { Ref &FrameBuffer::GetColorImage(uint32_t slot) { - assert(slot < MAX_COLOR_ATTACHMENTS && "Color attachment slot is not available"); + AE_ASSERT(slot < MAX_COLOR_ATTACHMENTS && "Color attachment slot is not available"); return colorAttachments[slot].image; diff --git a/src/engine/graphics/GraphicsDevice.cpp b/src/engine/graphics/GraphicsDevice.cpp index b2a7e58cb..1124b7c23 100644 --- a/src/engine/graphics/GraphicsDevice.cpp +++ b/src/engine/graphics/GraphicsDevice.cpp @@ -21,10 +21,7 @@ namespace Atlas { instance = Instance::DefaultInstance; std::vector requiredExtensions = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME -#ifdef AE_OS_MACOS - , "VK_KHR_portability_subset" -#endif + VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; std::vector optionalExtensions = { @@ -32,28 +29,30 @@ namespace Atlas { VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, VK_KHR_RAY_QUERY_EXTENSION_NAME +#ifdef AE_BINDLESS + , VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME +#endif #ifdef AE_BUILDTYPE_DEBUG , VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME +#endif +#ifdef AE_OS_MACOS + , VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME #endif }; SelectPhysicalDevice(instance->instance, surface->GetNativeSurface(), requiredExtensions, optionalExtensions); - GetPhysicalDeviceProperties(physicalDevice); + auto availableOptionalExtension = CheckDeviceOptionalExtensionSupport(physicalDevice, optionalExtensions); + requiredExtensions.insert(requiredExtensions.end(), availableOptionalExtension.begin(), + availableOptionalExtension.end()); - auto optionalExtensionOverlap = CheckDeviceOptionalExtensionSupport(physicalDevice, optionalExtensions); - requiredExtensions.insert(requiredExtensions.end(), optionalExtensionOverlap.begin(), - optionalExtensionOverlap.end()); + GetPhysicalDeviceProperties(physicalDevice); auto queueCreateInfos = CreateQueueInfos(); BuildPhysicalDeviceFeatures(physicalDevice); -#ifdef AE_OS_MACOS - setenv("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "1", 1); -#endif - // Uses the physical device structures generated above CreateDevice(queueCreateInfos, requiredExtensions, enableValidationLayers); @@ -92,62 +91,67 @@ namespace Atlas { // We assume everything else is terminated by now, so this is the only thread still alive // In that case we don't lock all the mutexes for (auto& tlasRef : tlases.data) { - assert(tlasRef.use_count() == 1 && "TLAS wasn't deallocated or allocated wrongly"); + AE_ASSERT(tlasRef.use_count() == 1 && "TLAS wasn't deallocated or allocated wrongly"); tlasRef.reset(); } for (auto& blasRef : blases.data) { - assert(blasRef.use_count() == 1 && "BLAS wasn't deallocated or allocated wrongly"); + AE_ASSERT(blasRef.use_count() == 1 && "BLAS wasn't deallocated or allocated wrongly"); blasRef.reset(); } for (auto& pipelineRef : pipelines.data) { - assert(pipelineRef.use_count() == 1 && "Pipeline wasn't deallocated or allocated wrongly"); + AE_ASSERT(pipelineRef.use_count() == 1 && "Pipeline wasn't deallocated or allocated wrongly"); pipelineRef.reset(); } for (auto& frameBufferRef : frameBuffers.data) { - assert(frameBufferRef.use_count() == 1 && "Frame buffer wasn't deallocated or allocated wrongly"); + AE_ASSERT(frameBufferRef.use_count() == 1 && "Frame buffer wasn't deallocated or allocated wrongly"); frameBufferRef.reset(); } for (auto& renderPassRef : renderPasses.data) { - assert(renderPassRef.use_count() == 1 && "Render pass wasn't deallocated or allocated wrongly"); + AE_ASSERT(renderPassRef.use_count() == 1 && "Render pass wasn't deallocated or allocated wrongly"); renderPassRef.reset(); } for (auto& shaderRef : shaders.data) { - assert(shaderRef.use_count() == 1 && "Shader wasn't deallocated or allocated wrongly"); + AE_ASSERT(shaderRef.use_count() == 1 && "Shader wasn't deallocated or allocated wrongly"); shaderRef.reset(); } for (auto& bufferRef : buffers.data) { - assert(bufferRef.use_count() == 1 && "Buffer wasn't deallocated or allocated wrongly"); + AE_ASSERT(bufferRef.use_count() == 1 && "Buffer wasn't deallocated or allocated wrongly"); bufferRef.reset(); } for (auto& multiBufferRef : multiBuffers.data) { - assert(multiBufferRef.use_count() == 1 && "Multi buffer wasn't deallocated or allocated wrongly"); + AE_ASSERT(multiBufferRef.use_count() == 1 && "Multi buffer wasn't deallocated or allocated wrongly"); multiBufferRef.reset(); } for (auto& imageRef : images.data) { - assert(imageRef.use_count() == 1 && "Image wasn't deallocated or allocated wrongly"); + AE_ASSERT(imageRef.use_count() == 1 && "Image wasn't deallocated or allocated wrongly"); imageRef.reset(); } for (auto& samplerRef : samplers.data) { - assert(samplerRef.use_count() == 1 && "Sampler wasn't deallocated or allocated wrongly"); + AE_ASSERT(samplerRef.use_count() == 1 && "Sampler wasn't deallocated or allocated wrongly"); samplerRef.reset(); } for (auto& poolRef : descriptorPools.data) { - assert(poolRef.use_count() == 1 && "Descriptor pool wasn't deallocated or allocated wrongly"); + AE_ASSERT(poolRef.use_count() == 1 && "Descriptor pool wasn't deallocated or allocated wrongly"); poolRef.reset(); } + for (auto& descLayoutRef : descriptorSetLayouts.data) { + AE_ASSERT(descLayoutRef.use_count() == 1 && "Descriptor layout wasn't deallocated or allocated wrongly"); + descLayoutRef.reset(); + } + for (auto& poolRef : queryPools.data) { - assert(poolRef.use_count() == 1 && "Query pool wasn't deallocated or allocated wrongly"); + AE_ASSERT(poolRef.use_count() == 1 && "Query pool wasn't deallocated or allocated wrongly"); poolRef.reset(); } @@ -161,12 +165,11 @@ namespace Atlas { SwapChain* GraphicsDevice::CreateSwapChain(VkPresentModeKHR presentMode, ColorSpace preferredColorSpace) { auto nativeSurface = surface->GetNativeSurface(); - auto nativeWindow = surface->GetNativeWindow(); auto supportDetails = SwapChainSupportDetails(physicalDevice, nativeSurface); int32_t width, height; - SDL_GL_GetDrawableSize(nativeWindow, &width, &height); + surface->GetExtent(width, height); windowWidth = width; windowHeight = height; @@ -296,6 +299,17 @@ namespace Atlas { } + Ref GraphicsDevice::CreateDescriptorSetLayout(DescriptorSetLayoutDesc desc) { + + auto layout = std::make_shared(this, desc); + + std::lock_guard guard(descriptorSetLayouts.mutex); + descriptorSetLayouts.data.push_back(layout); + + return layout; + + } + Ref GraphicsDevice::CreateDescriptorPool() { auto pool = std::make_shared(this); @@ -367,10 +381,10 @@ namespace Atlas { void GraphicsDevice::SubmitCommandList(CommandList *cmd, VkPipelineStageFlags waitStage, ExecutionOrder order) { - assert(!cmd->frameIndependent && "Submitted command list is frame independent." + AE_ASSERT(!cmd->frameIndependent && "Submitted command list is frame independent." && "Please use the flush method instead"); - assert(swapChain->isComplete && "Swap chain should be complete." + AE_ASSERT(swapChain->isComplete && "Swap chain should be complete." && " The swap chain might have an invalid size due to a window resize"); auto frame = GetFrameData(); @@ -393,7 +407,7 @@ namespace Atlas { void GraphicsDevice::FlushCommandList(CommandList *cmd) { - assert(cmd->frameIndependent && "Flushed command list is not frame independent." + AE_ASSERT(cmd->frameIndependent && "Flushed command list is not frame independent." && "Please use the submit method instead"); VkSubmitInfo submit = {}; @@ -449,7 +463,7 @@ namespace Atlas { allListSubmitted &= commandList->isSubmitted; } - assert(allListSubmitted && "Not all command list were submitted before frame completion." && + AE_ASSERT(allListSubmitted && "Not all command list were submitted before frame completion." && "Consider using a frame independent command lists for longer executions."); auto presenterQueue = SubmitAllCommandLists(); @@ -642,7 +656,7 @@ namespace Atlas { } auto foundSuitableDevice = candidates.rbegin()->first > 0; - assert(foundSuitableDevice && "No suitable device found"); + AE_ASSERT(foundSuitableDevice && "No suitable device found"); // Check if the best candidate is suitable at all if (foundSuitableDevice) { physicalDevice = candidates.rbegin()->second; @@ -654,13 +668,13 @@ namespace Atlas { { FindQueueFamilies(physicalDevice, surface); auto completeIndices = queueFamilyIndices.IsComplete(); - assert(completeIndices && "No valid queue family found"); + AE_ASSERT(completeIndices && "No valid queue family found"); if (!completeIndices) { return false; } auto extensionsSupported = CheckDeviceExtensionSupport(physicalDevice, requiredExtensions); - assert(extensionsSupported && "Some required extensions are not supported"); + AE_ASSERT(extensionsSupported && "Some required extensions are not supported"); if (!extensionsSupported) { return false; } @@ -676,6 +690,8 @@ namespace Atlas { int32_t score = 0; + + VkPhysicalDeviceProperties physicalDeviceProperties; VkPhysicalDeviceFeatures physicalDeviceFeatures; vkGetPhysicalDeviceProperties(device, &physicalDeviceProperties); @@ -876,6 +892,8 @@ namespace Atlas { std::vector extensionOverlap; for (const auto extensionName : extensionNames) { for (const auto& extension : availableExtensions) { + supportedExtensions.insert(extension.extensionName); + if (std::string(extension.extensionName) == std::string(extensionName)) { extensionOverlap.push_back(extensionName); } @@ -907,14 +925,22 @@ namespace Atlas { void GraphicsDevice::GetPhysicalDeviceProperties(VkPhysicalDevice device) { + StructureChainBuilder propertiesBuilder(deviceProperties); accelerationStructureProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR; rayTracingPipelineProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR; deviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + deviceProperties11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES; + deviceProperties12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES; + + propertiesBuilder.Append(deviceProperties11); + propertiesBuilder.Append(deviceProperties12); - propertiesBuilder.Append(rayTracingPipelineProperties); - propertiesBuilder.Append(accelerationStructureProperties); + if (supportedExtensions.contains(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME)) + propertiesBuilder.Append(rayTracingPipelineProperties); + if (supportedExtensions.contains(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME)) + propertiesBuilder.Append(accelerationStructureProperties); vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProperties); @@ -932,12 +958,6 @@ namespace Atlas { createInfo.enabledExtensionCount = uint32_t(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); - std::set availableExtensions; - - for (auto extensionName : extensions) { - availableExtensions.insert(extensionName); - } - StructureChainBuilder featureBuilder(createInfo); VkPhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeature = {}; @@ -945,53 +965,63 @@ namespace Atlas { VkPhysicalDeviceRayTracingPipelineFeaturesKHR rtPipelineFeature = {}; rtPipelineFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR; - + VkPhysicalDeviceRayQueryFeaturesKHR rayQueryFeature = {}; rayQueryFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR; // Check for ray tracing extension support - if (availableExtensions.contains(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME) && - availableExtensions.contains(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME) && - availableExtensions.contains(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME) && - availableExtensions.contains(VK_KHR_RAY_QUERY_EXTENSION_NAME)) { + if (supportedExtensions.contains(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME) && + supportedExtensions.contains(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME) && + supportedExtensions.contains(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME) && + supportedExtensions.contains(VK_KHR_RAY_QUERY_EXTENSION_NAME)) { accelerationStructureFeature.accelerationStructure = VK_TRUE; rtPipelineFeature.rayTracingPipeline = VK_TRUE; rayQueryFeature.rayQuery = VK_TRUE; - + featureBuilder.Append(accelerationStructureFeature); featureBuilder.Append(rtPipelineFeature); featureBuilder.Append(rayQueryFeature); - support.hardwareRayTracing = true; + support.hardwareRayTracing = false; } - if (availableExtensions.contains(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME)) { + if (supportedExtensions.contains(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME)) { support.shaderPrintf = true; } +#ifdef AE_BINDLESS + if (supportedExtensions.contains(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME) && + features12.descriptorBindingPartiallyBound && features12.runtimeDescriptorArray) { + support.bindless = true; + } +#endif + #ifdef AE_OS_MACOS VkPhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures = {}; - // This is hacked since I can't get it to work otherwise - // See VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR in vulkan_core.h - portabilityFeatures.sType = static_cast(1000163000); - portabilityFeatures.mutableComparisonSamplers = VK_TRUE; - // This feature struct is the last one in the pNext chain for now - featureBuilder.Append(portabilityFeatures); + if (supportedExtensions.contains(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) { + // This is hacked since I can't get it to work otherwise + // See VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR in vulkan_core.h + portabilityFeatures.sType = static_cast(1000163000); + portabilityFeatures.mutableComparisonSamplers = VK_TRUE; + + // This feature struct is the last one in the pNext chain for now + featureBuilder.Append(portabilityFeatures); + } #endif featureBuilder.Append(features); + featureBuilder.Append(features11); + featureBuilder.Append(features12); - VK_CHECK(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device)) + VK_CHECK_MESSAGE(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device), "Error creating graphics device") } bool GraphicsDevice::CheckForWindowResize() { - auto nativeWindow = surface->GetNativeWindow(); - int32_t width, height; - SDL_GL_GetDrawableSize(nativeWindow, &width, &height); + surface->GetExtent(width, height); if (width != windowWidth || height != windowHeight) { windowWidth = width; @@ -1047,6 +1077,7 @@ namespace Atlas { DeleteOutdatedResources(multiBuffers); DeleteOutdatedResources(images); DeleteOutdatedResources(samplers); + DeleteOutdatedResources(descriptorSetLayouts); DeleteOutdatedResources(descriptorPools); DeleteOutdatedResources(queryPools); DeleteOutdatedResources(blases); diff --git a/src/engine/graphics/GraphicsDevice.h b/src/engine/graphics/GraphicsDevice.h index 3c4e12a23..41b875788 100644 --- a/src/engine/graphics/GraphicsDevice.h +++ b/src/engine/graphics/GraphicsDevice.h @@ -10,7 +10,8 @@ #include "Buffer.h" #include "Image.h" #include "Sampler.h" -#include "Descriptor.h" +#include "DescriptorSetLayout.h" +#include "DescriptorPool.h" #include "QueryPool.h" #include "BLAS.h" #include "TLAS.h" @@ -22,6 +23,7 @@ #include #include #include +#include namespace Atlas { @@ -33,6 +35,7 @@ namespace Atlas { struct DeviceSupport { bool hardwareRayTracing = false; bool shaderPrintf = false; + bool bindless = false; }; struct CommandListSubmission { @@ -94,7 +97,7 @@ namespace Atlas { GraphicsDevice& operator=(const GraphicsDevice& that) = delete; - SwapChain* CreateSwapChain(VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR, + SwapChain* CreateSwapChain(VkPresentModeKHR presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR, ColorSpace preferredColorSpace = SRGB_NONLINEAR); Ref CreateRenderPass(RenderPassDesc desc); @@ -115,6 +118,8 @@ namespace Atlas { Ref CreateSampler(SamplerDesc desc); + Ref CreateDescriptorSetLayout(DescriptorSetLayoutDesc desc); + Ref CreateDescriptorPool(); Ref CreateQueryPool(QueryPoolDesc desc); @@ -141,6 +146,12 @@ namespace Atlas { void ForceMemoryCleanup(); + template + struct Resources { + std::vector> data; + std::mutex mutex; + }; + Instance* instance = nullptr; SwapChain* swapChain = nullptr; MemoryManager* memoryManager = nullptr; @@ -150,6 +161,8 @@ namespace Atlas { VkDevice device; VkPhysicalDeviceProperties2 deviceProperties = {}; + VkPhysicalDeviceVulkan11Properties deviceProperties11 = {}; + VkPhysicalDeviceVulkan12Properties deviceProperties12 = {}; VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingPipelineProperties = {}; VkPhysicalDeviceAccelerationStructurePropertiesKHR accelerationStructureProperties = {}; @@ -159,6 +172,20 @@ namespace Atlas { DeviceSupport support; + Resources renderPasses; + Resources frameBuffers; + Resources shaders; + Resources pipelines; + Resources buffers; + Resources multiBuffers; + Resources images; + Resources samplers; + Resources descriptorSetLayouts; + Resources descriptorPools; + Resources queryPools; + Resources blases; + Resources tlases; + bool isComplete = false; static GraphicsDevice* DefaultDevice; @@ -180,18 +207,17 @@ namespace Atlas { std::vector families; bool IsComplete() { +#ifndef AE_HEADLESS return queueFamilies[QueueType::GraphicsQueue].has_value() && queueFamilies[QueueType::PresentationQueue].has_value() && queueFamilies[QueueType::TransferQueue].has_value(); +#else + return queueFamilies[QueueType::GraphicsQueue].has_value() && + queueFamilies[QueueType::TransferQueue].has_value(); +#endif } }; - template - struct Resources { - std::vector> data; - std::mutex mutex; - }; - QueueRef SubmitAllCommandLists(); void SubmitCommandList(CommandListSubmission* submission, VkSemaphore previousSemaphore, @@ -255,19 +281,6 @@ namespace Atlas { QueueFamilyIndices queueFamilyIndices; - Resources renderPasses; - Resources frameBuffers; - Resources shaders; - Resources pipelines; - Resources buffers; - Resources multiBuffers; - Resources images; - Resources samplers; - Resources descriptorPools; - Resources queryPools; - Resources blases; - Resources tlases; - std::mutex commandListsMutex; std::vector commandLists; @@ -276,6 +289,9 @@ namespace Atlas { int32_t windowWidth = 0; int32_t windowHeight = 0; + + std::set supportedExtensions; + }; } diff --git a/src/engine/graphics/Image.cpp b/src/engine/graphics/Image.cpp index 247ec617b..26655421f 100644 --- a/src/engine/graphics/Image.cpp +++ b/src/engine/graphics/Image.cpp @@ -1,4 +1,5 @@ #include "Image.h" +#include "Format.h" #include "GraphicsDevice.h" namespace Atlas { @@ -14,6 +15,8 @@ namespace Atlas { imageExtent.height = desc.height; imageExtent.depth = desc.depth; + bitDepth = GetFormatSize(desc.format) / GetFormatChannels(desc.format); + VkImageCreateInfo imageInfo = Initializers::InitImageCreateInfo(desc.format, desc.usageFlags, imageExtent, GetImageType()); if (desc.mipMapping) { diff --git a/src/engine/graphics/Image.h b/src/engine/graphics/Image.h index f36be3e53..33467f34f 100644 --- a/src/engine/graphics/Image.h +++ b/src/engine/graphics/Image.h @@ -78,6 +78,8 @@ namespace Atlas { uint32_t layers = 1; VkFormat format; + uint32_t bitDepth = 8; + uint32_t mipLevels = 1; ImageDomain domain; diff --git a/src/engine/graphics/Instance.cpp b/src/engine/graphics/Instance.cpp index 1f139b2f7..7ff5143f5 100644 --- a/src/engine/graphics/Instance.cpp +++ b/src/engine/graphics/Instance.cpp @@ -1,8 +1,11 @@ #include "Instance.h" +#include "StructureChainBuilder.h" #include "../Log.h" #include #include +#include +#include namespace Atlas { @@ -10,14 +13,14 @@ namespace Atlas { Instance* Instance::DefaultInstance = nullptr; - Instance::Instance(const std::string &instanceName, bool enableValidationLayers) : - name(instanceName), validationLayersEnabled(enableValidationLayers) { + Instance::Instance(const InstanceDesc& desc) : name(desc.instanceName), + validationLayersEnabled(desc.enableValidationLayers), validationLayerSeverity(desc.validationLayerSeverity) { VK_CHECK(volkInitialize()); VkApplicationInfo appInfo{}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pApplicationName = instanceName.c_str(); + appInfo.pApplicationName = name.c_str(); appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.pEngineName = "Atlas Engine"; appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); @@ -26,16 +29,20 @@ namespace Atlas { LoadSupportedLayersAndExtensions(); auto requiredExtensions = extensionNames; -#ifdef AE_OS_MACOS - requiredExtensions.emplace_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); +#ifdef AE_HEADLESS + AE_ASSERT(supportedExtensions.contains(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME) && "Headless instance extension not supported"); + requiredExtensions.push_back(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME); #endif + CheckExtensionSupport(requiredExtensions); + const std::vector validationLayers = { - "VK_LAYER_KHRONOS_validation" + "VK_LAYER_KHRONOS_validation", }; - if (enableValidationLayers && !CheckValidationLayerSupport(validationLayers)) { - return; + if (validationLayersEnabled && !CheckValidationLayerSupport(validationLayers)) { + Log::Warning("Required validation layers were not found. Disabling validation layers"); + validationLayersEnabled = false; } VkInstanceCreateInfo createInfo{}; @@ -43,7 +50,9 @@ namespace Atlas { createInfo.pApplicationInfo = &appInfo; createInfo.enabledLayerCount = 0; #ifdef AE_OS_MACOS - createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + if (supportedExtensions.contains(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) { + createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + } #endif createInfo.enabledExtensionCount = uint32_t(requiredExtensions.size()); @@ -56,25 +65,27 @@ namespace Atlas { VkValidationFeatureEnableEXT enables[] = {VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT}; VkValidationFeaturesEXT validationFeatures = {}; - if (enableValidationLayers) { + StructureChainBuilder structureChainBuilder(createInfo); + + if (validationLayersEnabled) { createInfo.enabledLayerCount = uint32_t(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); - createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo; + structureChainBuilder.Append(debugCreateInfo); -#ifdef AE_BUILDTYPE_DEBUG +#ifdef AE_BUILDTYPE_DEBUG validationFeatures.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; validationFeatures.enabledValidationFeatureCount = 1; validationFeatures.pEnabledValidationFeatures = enables; - debugCreateInfo.pNext = &validationFeatures; + structureChainBuilder.Append(validationFeatures); #endif } else { createInfo.enabledLayerCount = 0; } - VK_CHECK(vkCreateInstance(&createInfo, nullptr, &instance)); + VK_CHECK_MESSAGE(vkCreateInstance(&createInfo, nullptr, &instance), "Error creating instance"); volkLoadInstance(instance); @@ -124,6 +135,20 @@ namespace Atlas { } + Surface* Instance::CreateHeadlessSurface() { + + bool success = false; + auto surface = new Surface(this, success); + if (!success) { + return nullptr; + } + + surfaces.push_back(surface); + + return surface; + + } + void Instance::LoadSupportedLayersAndExtensions() { unsigned int extensionCount = 0; @@ -138,6 +163,7 @@ namespace Atlas { std::string(extensionProperty.extensionName) == "VK_LUNARG_direct_driver_loading") continue; extensionNames.push_back(extensionProperty.extensionName); + supportedExtensions.insert(extensionProperty.extensionName); } unsigned int layerCount = 0; @@ -153,13 +179,29 @@ namespace Atlas { bool Instance::CheckExtensionSupport(const std::vector& extensionNames) { - return CheckRequiredVector(this->extensionNames, extensionNames); + std::vector availableElements(this->extensionNames.begin(), this->extensionNames.end()); + std::set requiredElements(extensionNames.begin(), extensionNames.end()); + + for (const auto& element : availableElements) { + requiredElements.erase(element); + } + + AE_ASSERT(requiredElements.empty() && "Not all required instance extensions were found"); + + return requiredElements.empty(); } bool Instance::CheckValidationLayerSupport(const std::vector& validationLayerNames) { - return CheckRequiredVector(this->layerNames, validationLayerNames); + std::vector availableElements(this->layerNames.begin(), this->layerNames.end()); + std::set requiredElements(validationLayerNames.begin(), validationLayerNames.end()); + + for (const auto& element : availableElements) { + requiredElements.erase(element); + } + + return requiredElements.empty(); } @@ -180,12 +222,19 @@ namespace Atlas { VkDebugUtilsMessengerCreateInfoEXT Instance::GetDebugMessengerCreateInfo() { + int32_t allowedSeverity = static_cast(validationLayerSeverity); + VkDebugUtilsMessengerCreateInfoEXT createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + if (allowedSeverity <= 1) { + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + } + if (allowedSeverity <= 0) { + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + } + createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; @@ -195,21 +244,6 @@ namespace Atlas { } - bool Instance::CheckRequiredVector(const std::vector &available, - const std::vector &required) { - - std::vector availableElements(available.begin(), available.end()); - std::set requiredElements(required.begin(), required.end()); - - for (const auto& element : availableElements) { - requiredElements.erase(element); - } - assert(requiredElements.empty() && "Not all required elements were found"); - - return requiredElements.empty(); - - } - VkBool32 Instance::DebugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -218,10 +252,6 @@ namespace Atlas { int32_t logType = Log::Type::TYPE_MESSAGE, logSeverity = Log::Severity::SEVERITY_LOW; - // Filter notifications - //if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) - // return VK_FALSE; - std::string output = "Vulkan debug log:\n"; output.append(pCallbackData->pMessage); output.append("\nType: "); @@ -250,6 +280,14 @@ namespace Atlas { default: break; } + // Ignore errors, might happen if the device just provides 4 possible descriptor set locations + // (This layer uses it's own descriptor set and engine uses 4 already) + if (pCallbackData->pMessageIdName != nullptr && + std::string(pCallbackData->pMessageIdName) == "UNASSIGNED-DEBUG-PRINTF") { + logType = Log::Type::TYPE_WARNING; + logSeverity = Log::Severity::SEVERITY_MEDIUM; + } + switch (logType) { case Log::Type::TYPE_MESSAGE: Log::Message(pCallbackData->pMessage, logSeverity); break; case Log::Type::TYPE_WARNING: Log::Warning(pCallbackData->pMessage, logSeverity); break; diff --git a/src/engine/graphics/Instance.h b/src/engine/graphics/Instance.h index 14db7ee81..af74a8587 100644 --- a/src/engine/graphics/Instance.h +++ b/src/engine/graphics/Instance.h @@ -2,6 +2,7 @@ #include #include +#include #define VK_NO_PROTOTYPES #include @@ -17,6 +18,13 @@ namespace Atlas { namespace Graphics { + struct InstanceDesc { + std::string instanceName; + + bool enableValidationLayers = false; + Log::Severity validationLayerSeverity = Log::Severity::SEVERITY_LOW; + }; + class Instance { friend GraphicsDevice; @@ -25,7 +33,7 @@ namespace Atlas { friend Extensions; public: - explicit Instance(const std::string& instanceName, bool enableValidationLayers = false); + explicit Instance(const InstanceDesc& desc); Instance(const Instance& that) = delete; @@ -39,6 +47,8 @@ namespace Atlas { Surface* CreateSurface(SDL_Window* window); + Surface* CreateHeadlessSurface(); + bool isComplete = false; static Instance* DefaultInstance; @@ -58,8 +68,6 @@ namespace Atlas { VkDebugUtilsMessengerCreateInfoEXT GetDebugMessengerCreateInfo(); - bool CheckRequiredVector(const std::vector& available, const std::vector& required); - const std::string name; bool validationLayersEnabled; @@ -68,12 +76,16 @@ namespace Atlas { std::vector extensionProperties; std::vector extensionNames; + std::set supportedExtensions; + VkInstance instance; VkDebugUtilsMessengerEXT debugMessenger; GraphicsDevice* graphicsDevice; std::vector surfaces; + Log::Severity validationLayerSeverity; + private: static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, diff --git a/src/engine/graphics/MemoryManager.cpp b/src/engine/graphics/MemoryManager.cpp index c5e9c81c7..dc7ba1993 100644 --- a/src/engine/graphics/MemoryManager.cpp +++ b/src/engine/graphics/MemoryManager.cpp @@ -99,6 +99,13 @@ namespace Atlas { } + void MemoryManager::DestroyAllocation(Ref& allocation) { + + deleteDescriptorSetLayoutAllocations + .emplace_back(DeleteResource { allocation, frameIndex + framesToDeletion }); + + } + void MemoryManager::DestroyAllocation(Ref& allocation) { deleteDescriptorPoolAllocations @@ -170,6 +177,7 @@ namespace Atlas { DeleteAllocations(deleteImageAllocations); DeleteAllocations(deleteSamplerAllocations); DeleteAllocations(deleteDescriptorPoolAllocations); + DeleteAllocations(deleteDescriptorSetLayoutAllocations); DeleteAllocations(deleteQueryPoolAllocations); } diff --git a/src/engine/graphics/MemoryManager.h b/src/engine/graphics/MemoryManager.h index e0372ac76..83bdc52ff 100644 --- a/src/engine/graphics/MemoryManager.h +++ b/src/engine/graphics/MemoryManager.h @@ -7,7 +7,8 @@ #include "Buffer.h" #include "Image.h" #include "Sampler.h" -#include "Descriptor.h" +#include "DescriptorSetLayout.h" +#include "DescriptorPool.h" #include "QueryPool.h" #include "Framebuffer.h" #include "BLAS.h" @@ -71,6 +72,8 @@ namespace Atlas { void DestroyAllocation(Ref& allocation); + void DestroyAllocation(Ref& allocation); + void DestroyAllocation(Ref& allocation); void DestroyAllocation(Ref& allocation); @@ -102,7 +105,7 @@ namespace Atlas { auto &allocation = deleteAllocations.front(); // This should never happen - assert(allocation.resource.use_count() == 1 && "Resource allocation is not uniquely owned"); + AE_ASSERT(allocation.resource.use_count() == 1 && "Resource allocation is not uniquely owned"); allocation.resource.reset(); deleteAllocations.pop_front(); @@ -121,6 +124,7 @@ namespace Atlas { std::deque> deleteMultiBufferAllocations; std::deque> deleteImageAllocations; std::deque> deleteSamplerAllocations; + std::deque> deleteDescriptorSetLayoutAllocations; std::deque> deleteDescriptorPoolAllocations; std::deque> deleteQueryPoolAllocations; std::deque> deleteBLASAllocations; diff --git a/src/engine/graphics/MemoryTransferManager.cpp b/src/engine/graphics/MemoryTransferManager.cpp index 33226cbe8..859192ba4 100644 --- a/src/engine/graphics/MemoryTransferManager.cpp +++ b/src/engine/graphics/MemoryTransferManager.cpp @@ -221,14 +221,17 @@ namespace Atlas { VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 1, &bufferBarrier, 0, nullptr); } + commandList->EndCommands(); + + device->FlushCommandList(commandList); + device->WaitForIdle(); + void* src; vmaMapMemory(allocator, stagingAllocation.allocation, &src); std::memcpy(data, src, pixelCount * formatSize); vmaUnmapMemory(allocator, stagingAllocation.allocation); - commandList->EndCommands(); - - device->FlushCommandList(commandList); + DestroyStagingBuffer(stagingAllocation); } diff --git a/src/engine/graphics/Pipeline.cpp b/src/engine/graphics/Pipeline.cpp index 358194a81..efb7cc91c 100644 --- a/src/engine/graphics/Pipeline.cpp +++ b/src/engine/graphics/Pipeline.cpp @@ -12,10 +12,10 @@ namespace Atlas { bindPoint(VK_PIPELINE_BIND_POINT_GRAPHICS), shader(desc.shader), frameBuffer(desc.frameBuffer), device(device) { - assert(!desc.shader->isCompute && "Can't create a graphics pipeline with a compute shader"); + AE_ASSERT(!desc.shader->isCompute && "Can't create a graphics pipeline with a compute shader"); if (desc.shader->isCompute) return; - assert((desc.swapChain || desc.frameBuffer) && "Must provide a swap chain or a frame buffer"); + AE_ASSERT((desc.swapChain || desc.frameBuffer) && "Must provide a swap chain or a frame buffer"); GeneratePipelineLayoutFromShader(); @@ -60,6 +60,17 @@ namespace Atlas { auto vertexInputInfo = GenerateVertexInputStateInfoFromShader(desc.vertexInputInfo, bindingDescriptions, attributeDescriptions); + auto assemblyInputInfo = desc.assemblyInputInfo; +#ifdef AE_OS_MACOS + // Need to disable it for other LIST topologies as well, but they are not in use right now + if (assemblyInputInfo.topology != VK_PRIMITIVE_TOPOLOGY_LINE_LIST && + assemblyInputInfo.topology != VK_PRIMITIVE_TOPOLOGY_POINT_LIST && + assemblyInputInfo.topology != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST && + assemblyInputInfo.topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) { + assemblyInputInfo.primitiveRestartEnable = VK_TRUE; + } +#endif + VkGraphicsPipelineCreateInfo pipelineInfo = {}; pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineInfo.pNext = nullptr; @@ -67,7 +78,7 @@ namespace Atlas { pipelineInfo.stageCount = uint32_t(desc.shader->stageCreateInfos.size()); pipelineInfo.pStages = desc.shader->stageCreateInfos.data(); pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &desc.assemblyInputInfo; + pipelineInfo.pInputAssemblyState = &assemblyInputInfo; pipelineInfo.pDepthStencilState = &desc.depthStencilInputInfo; pipelineInfo.pViewportState = &viewportState; pipelineInfo.pRasterizationState = &desc.rasterizer; @@ -96,7 +107,7 @@ namespace Atlas { Pipeline::Pipeline(GraphicsDevice* device, const ComputePipelineDesc& desc) : bindPoint(VK_PIPELINE_BIND_POINT_COMPUTE), shader(desc.shader), device(device) { - assert(desc.shader->isCompute && "Can't create a compute pipeline without a compute shader"); + AE_ASSERT(desc.shader->isCompute && "Can't create a compute pipeline without a compute shader"); if (!desc.shader->isCompute) return; GeneratePipelineLayoutFromShader(); @@ -148,8 +159,8 @@ namespace Atlas { std::vector descriptorSetLayouts; for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) { // We need to check if there are any bindings at all - //if (!shader->sets[i].bindingCount) continue; - descriptorSetLayouts.push_back(shader->sets[i].layout); + //if (!shader->sets[i].) continue; + descriptorSetLayouts.push_back(shader->sets[i].layout->layout); } if (descriptorSetLayouts.size() > 0) { @@ -166,7 +177,7 @@ namespace Atlas { std::vector& bindingDescriptions, std::vector& attributeDescriptions) { - assert(descVertexInputState.vertexBindingDescriptionCount == + AE_ASSERT(descVertexInputState.vertexBindingDescriptionCount == descVertexInputState.vertexAttributeDescriptionCount && "Expected bindings and attributes \ to have the same amount of elements"); @@ -182,7 +193,7 @@ namespace Atlas { break; } } - assert(found && "Vertex input was not specified in the pipeline desc"); + AE_ASSERT(found && "Vertex input was not specified in the pipeline desc"); } return Graphics::Initializers::InitPipelineVertexInputStateCreateInfo( diff --git a/src/engine/graphics/Profiler.cpp b/src/engine/graphics/Profiler.cpp index 95aad6ca8..3e8700066 100644 --- a/src/engine/graphics/Profiler.cpp +++ b/src/engine/graphics/Profiler.cpp @@ -18,14 +18,14 @@ namespace Atlas { size_t Profiler::frameIdx = -1; #ifndef AE_OS_MACOS - bool Profiler::activate = true; + std::atomic_bool Profiler::enable = true; #else - bool Profiler::activate = false; + std::atomic_bool Profiler::enable = false; #endif void Profiler::BeginFrame() { - if (!activate) return; + if (!enable) return; threadContextCount = 0; threadContexts.clear(); @@ -78,7 +78,7 @@ namespace Atlas { void Profiler::BeginThread(const std::string &name, CommandList *commandList) { - if (!activate) return; + if (!enable) return; // We don't expect to have more than 2000 profiler queries auto queryPoolDesc = QueryPoolDesc { @@ -97,7 +97,7 @@ namespace Atlas { }; auto idx = threadContextCount.fetch_add(1); - assert(idx < PROFILER_MAX_THREADS && "Too many threads for this frame"); + AE_ASSERT(idx < PROFILER_MAX_THREADS && "Too many threads for this frame"); threadContexts[idx] = context; @@ -108,7 +108,7 @@ namespace Atlas { void Profiler::EndThread() { - if (!activate) return; + if (!enable) return; std::lock_guard lock(unevaluatedThreadContextsMutex); @@ -121,7 +121,7 @@ namespace Atlas { void Profiler::SetCommandList(CommandList *commandList) { - if (!activate) return; + if (!enable) return; auto& context = GetThreadContext(); context.commandList = commandList; @@ -130,13 +130,13 @@ namespace Atlas { void Profiler::BeginQuery(const std::string& name) { - if (!activate) return; + if (!enable) return; - assert(name.length() > 0 && "Query names shouldn't be empty"); + AE_ASSERT(name.length() > 0 && "Query names shouldn't be empty"); auto& context = GetThreadContext(); - assert(context.commandList && "A command list must be set before \ + AE_ASSERT(context.commandList && "A command list must be set before \ the first BeginQuery() call in the current thread"); Query query; @@ -155,11 +155,11 @@ namespace Atlas { void Profiler::EndQuery() { - if (!activate) return; + if (!enable) return; auto& context = GetThreadContext(); - assert(context.stack.size() && "Stack was empty. Maybe called EndQuery too many \ + AE_ASSERT(context.stack.size() && "Stack was empty. Maybe called EndQuery too many \ times or code misses a BeginQuery."); auto query = context.stack.back(); @@ -183,7 +183,7 @@ namespace Atlas { void Profiler::EndAndBeginQuery(const std::string& name) { - if (!activate) return; + if (!enable) return; EndQuery(); BeginQuery(name); @@ -317,7 +317,7 @@ namespace Atlas { return threadId == context.id && context.isValid; }); - assert(it != threadContexts.end() && "Thread context not found. Missing a \ + AE_ASSERT(it != threadContexts.end() && "Thread context not found. Missing a \ BeginThread() call somewhere"); return *it; diff --git a/src/engine/graphics/Profiler.h b/src/engine/graphics/Profiler.h index 6a6dbfef5..6f54c92e3 100644 --- a/src/engine/graphics/Profiler.h +++ b/src/engine/graphics/Profiler.h @@ -8,6 +8,7 @@ #include #include #include +#include #define PROFILER_MAX_THREADS 32 @@ -141,6 +142,8 @@ namespace Atlas { static std::vector GetQueriesAverage(uint32_t frameCount = 32, OrderBy order = OrderBy::CHRONO); + static std::atomic_bool enable; + private: struct ThreadContext { std::thread::id id; @@ -180,8 +183,6 @@ namespace Atlas { static std::unordered_map queryHistory; static size_t frameIdx; - static bool activate; - }; } diff --git a/src/engine/graphics/RenderPass.cpp b/src/engine/graphics/RenderPass.cpp index e374064f6..891537821 100644 --- a/src/engine/graphics/RenderPass.cpp +++ b/src/engine/graphics/RenderPass.cpp @@ -30,7 +30,7 @@ namespace Atlas { void RenderPass::AttachColor(const RenderPassColorAttachment& attachment, uint32_t slot) { - assert(slot < MAX_COLOR_ATTACHMENTS && "Color attachment slot is not available"); + AE_ASSERT(slot < MAX_COLOR_ATTACHMENTS && "Color attachment slot is not available"); colorAttachments[slot] = attachment; colorAttachments[slot].isValid = true; diff --git a/src/engine/graphics/Shader.cpp b/src/engine/graphics/Shader.cpp index 621cf7ab3..ca9d05a05 100644 --- a/src/engine/graphics/Shader.cpp +++ b/src/engine/graphics/Shader.cpp @@ -20,25 +20,17 @@ namespace Atlas { const std::string ShaderStageFile::GetGlslCode(const std::vector& macros) const { - auto device = GraphicsDevice::DefaultDevice; - std::string glslCode = ""; glslCode.append("#version 460\n\n"); - if (Extensions::IsSupported("GL_EXT_texture_shadow_lod")) { - glslCode.append("#define AE_TEXTURE_SHADOW_LOD\n"); - } - - if (device->support.shaderPrintf) { - glslCode.append("#define AE_SHADER_PRINTF\n"); - } + auto envMacros = GetEnvironmentMacros(); - if (device->support.hardwareRayTracing) { - glslCode.append("#define AE_HARDWARE_RAYTRACING\n"); + for (const auto& macro : envMacros) { + glslCode.append("#define " + macro + "\n"); } // Extensions have to come first - for (auto& extension : extensions) { + for (const auto& extension : extensions) { for (auto& ifdef : extension.ifdefs) glslCode += ifdef + "\n"; glslCode += extension.extension + "\n"; @@ -46,7 +38,7 @@ namespace Atlas { glslCode += "#endif\n"; } - for (auto& macro : macros) { + for (const auto& macro : macros) { glslCode.append("#define " + macro + "\n"); } @@ -57,6 +49,31 @@ namespace Atlas { } + const std::vector ShaderStageFile::GetEnvironmentMacros() const { + + auto device = GraphicsDevice::DefaultDevice; + + std::vector macros; + + if (Extensions::IsSupported("GL_EXT_texture_shadow_lod")) { + macros.push_back("AE_TEXTURE_SHADOW_LOD"); + } + if (device->support.shaderPrintf) { + macros.push_back("AE_SHADER_PRINTF"); + } + + if (device->support.hardwareRayTracing) { + macros.push_back("AE_HARDWARE_RAYTRACING"); + } + + if (device->support.bindless) { + macros.push_back("AE_BINDLESS"); + } + + return macros; + + } + Shader::Shader(GraphicsDevice *device, const ShaderDesc &desc) : device(device), shaderStageFiles(desc.stages) { @@ -195,14 +212,14 @@ namespace Atlas { uint32_t allStageFlags = 0; std::vector shaderModules(stages.size()); - modules.resize(stages.size()); + modules.resize(stages.size(), VK_NULL_HANDLE); for (size_t i = 0; i < stages.size(); i++) { auto& stage = stages[i]; bool isCompiled = false; - auto spirvBinary = ShaderCompiler::Compile(stage, macros, isCompiled); + auto spirvBinary = ShaderCompiler::Compile(stage, macros, true, isCompiled); - assert(isCompiled && "Shader compilation was unsuccessful"); + AE_ASSERT(isCompiled && "Shader compilation was unsuccessful"); if (!isCompiled) return; VkShaderModuleCreateInfo createInfo = {}; @@ -211,13 +228,10 @@ namespace Atlas { createInfo.codeSize = spirvBinary.size() * sizeof(uint32_t); createInfo.pCode = spirvBinary.data(); - bool success = vkCreateShaderModule(device->device, &createInfo, - nullptr, &modules[i]) == VK_SUCCESS; - assert(success && "Error creating shader module"); - if (!success) { - auto stream = Loader::AssetLoader::WriteFile("dump.txt", std::ios::out | std::ios::binary); - stream.write(reinterpret_cast(spirvBinary.data()), spirvBinary.size() * sizeof(uint32_t)); - stream.close(); + auto result = vkCreateShaderModule(device->device, &createInfo, + nullptr, &modules[i]); + VK_CHECK_MESSAGE(result, "Error creating shader module"); + if (result != VK_SUCCESS) { return; } @@ -255,7 +269,7 @@ namespace Atlas { for (uint32_t i = 0; i < set.bindingCount; i++) { auto& binding = set.bindings[i]; if (bindings.contains(binding.hash)) { - bindings[binding.hash].layoutBinding.stageFlags |= shaderModule.shaderStageFlag; + bindings[binding.hash].binding.stageFlags |= shaderModule.shaderStageFlag; } else { bindings[binding.hash] = binding; @@ -268,25 +282,21 @@ namespace Atlas { pushConstantRanges.push_back(range); } + DescriptorSetLayoutDesc layoutDesc[DESCRIPTOR_SET_COUNT]; for (auto& [key, binding] : bindings) { auto layoutIdx = sets[binding.set].bindingCount++; - auto idx = binding.layoutBinding.binding; + auto idx = binding.binding.bindingIdx; sets[binding.set].bindings[idx] = binding; - sets[binding.set].layoutBindings[layoutIdx] = binding.layoutBinding; + layoutDesc[binding.set].bindings[layoutIdx] = binding.binding; } for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) { - VkDescriptorSetLayoutCreateInfo setInfo = {}; - setInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - setInfo.pNext = nullptr; - setInfo.bindingCount = sets[i].bindingCount; - setInfo.flags = 0; - setInfo.pBindings = sets[i].bindingCount ? sets[i].layoutBindings : VK_NULL_HANDLE; - - VK_CHECK(vkCreateDescriptorSetLayout(device->device, &setInfo, nullptr, &sets[i].layout)) + layoutDesc[i].bindingCount = sets[i].bindingCount; + + sets[i].layout = device->CreateDescriptorSetLayout(layoutDesc[i]); } isComplete = true; @@ -295,13 +305,9 @@ namespace Atlas { ShaderVariant::~ShaderVariant() { - if (!isComplete) return; - - for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) { - vkDestroyDescriptorSetLayout(device->device, sets[i].layout, nullptr); - } - for (auto module : modules) { + if (module == VK_NULL_HANDLE) continue; + vkDestroyShaderModule(device->device, module, nullptr); } @@ -319,20 +325,48 @@ namespace Atlas { } + bool ShaderVariant::TryOverrideDescriptorSetLayout(Ref layout, uint32_t set) { + + AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set index out of range"); + + if (set >= DESCRIPTOR_SET_COUNT || + !layout->IsCompatible(sets[set].layout)) { + return false; + } + + sets[set].layout = layout; + sets[set].bindingCount = uint32_t(layout->bindings.size()); + + for (size_t i = 0; i < BINDINGS_PER_DESCRIPTOR_SET; i++) { + sets[set].bindings[i].valid = false; + } + + for (size_t i = 0; i < layout->layoutBindings.size(); i++) { + const auto& binding = layout->bindings[i]; + + auto idx = binding.bindingIdx; + sets[set].bindings[idx].binding = binding; + sets[set].bindings[idx].valid = true; + } + + return true; + + } + void ShaderVariant::GenerateReflectionData(ShaderModule &shaderModule, const std::vector& spirvBinary) { SpvReflectShaderModule module; SpvReflectResult result = spvReflectCreateShaderModule(spirvBinary.size() * sizeof(uint32_t), spirvBinary.data(), &module); - assert(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't create shader reflection module"); + AE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't create shader reflection module"); uint32_t pushConstantCount = 0; result = spvReflectEnumeratePushConstantBlocks(&module, &pushConstantCount, nullptr); - assert(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve push constants"); + AE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve push constants"); std::vector pushConstants(pushConstantCount); result = spvReflectEnumeratePushConstantBlocks(&module, &pushConstantCount, pushConstants.data()); - assert(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve push constants"); + AE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve push constants"); for (auto pushConstant : pushConstants) { PushConstantRange range; @@ -351,16 +385,16 @@ namespace Atlas { uint32_t descSetCount = 0; result = spvReflectEnumerateDescriptorSets(&module, &descSetCount, nullptr); - assert(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve descriptor sets"); + AE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve descriptor sets"); std::vector descSets(descSetCount); result = spvReflectEnumerateDescriptorSets(&module, &descSetCount, descSets.data()); - assert(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve descriptor sets"); + AE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve descriptor sets"); for (auto descriptorSet : descSets) { ShaderDescriptorSet bindGroup; - assert(descriptorSet->binding_count <= BINDINGS_PER_DESCRIPTOR_SET && "Too many bindings for this shader"); + AE_ASSERT(descriptorSet->binding_count <= BINDINGS_PER_DESCRIPTOR_SET && "Too many bindings for this shader"); for(uint32_t i = 0; i < descriptorSet->binding_count; i++) { auto descriptorBinding = descriptorSet->bindings[i]; @@ -369,23 +403,26 @@ namespace Atlas { binding.name.assign(descriptorBinding->name); binding.set = descriptorBinding->set; - binding.size = descriptorBinding->block.size; - binding.arrayElement = 0; + binding.valid = true; - assert(binding.set < DESCRIPTOR_SET_COUNT && "Too many descriptor sets for this shader"); + AE_ASSERT(binding.set < DESCRIPTOR_SET_COUNT && "Too many descriptor sets for this shader"); - binding.layoutBinding.binding = descriptorBinding->binding; - binding.layoutBinding.descriptorCount = descriptorBinding->count; - binding.layoutBinding.descriptorType = (VkDescriptorType)descriptorBinding->descriptor_type; - binding.layoutBinding.stageFlags = shaderModule.shaderStageFlag; + binding.binding.bindingIdx = descriptorBinding->binding; + binding.binding.descriptorCount = descriptorBinding->count; + binding.binding.descriptorType = (VkDescriptorType)descriptorBinding->descriptor_type; + binding.binding.size = descriptorBinding->block.size; + binding.binding.arrayElement = 0; + binding.binding.stageFlags = VK_SHADER_STAGE_ALL; + binding.binding.bindless = descriptorBinding->array.dims_count == 1 && + descriptorBinding->array.dims[0] == 1 && device->support.bindless; - binding.layoutBinding.descriptorType = - binding.layoutBinding.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ? - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : binding.layoutBinding.descriptorType; + binding.binding.descriptorType = + binding.binding.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ? + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : binding.binding.descriptorType; HashCombine(binding.hash, binding.set); - HashCombine(binding.hash, binding.layoutBinding.binding); + HashCombine(binding.hash, binding.binding.bindingIdx); bindGroup.bindings[i] = binding; bindGroup.bindingCount++; @@ -397,11 +434,11 @@ namespace Atlas { if (shaderModule.shaderStageFlag == VK_SHADER_STAGE_VERTEX_BIT) { uint32_t inputVariableCount = 0; result = spvReflectEnumerateInputVariables(&module, &inputVariableCount, nullptr); - assert(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve descriptor sets"); + AE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve descriptor sets"); std::vector inputVariables(inputVariableCount); result = spvReflectEnumerateInputVariables(&module, &inputVariableCount, inputVariables.data()); - assert(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve descriptor sets"); + AE_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS && "Couldn't retrieve descriptor sets"); for (auto inputVariable: inputVariables) { // Reject all build in variables diff --git a/src/engine/graphics/Shader.h b/src/engine/graphics/Shader.h index 78e25ae00..59a286a4e 100644 --- a/src/engine/graphics/Shader.h +++ b/src/engine/graphics/Shader.h @@ -1,6 +1,8 @@ #pragma once #include "Common.h" +#include "DescriptorSetLayout.h" + #include #include #include @@ -21,6 +23,8 @@ namespace Atlas { const std::string GetGlslCode(const std::vector& macros) const; + const std::vector GetEnvironmentMacros() const; + struct Extension { std::string extension; std::vector ifdefs; @@ -51,9 +55,8 @@ namespace Atlas { size_t hash = 0; uint32_t set = 0; - uint32_t size = 0; - uint32_t arrayElement = 0; - VkDescriptorSetLayoutBinding layoutBinding = {}; + + DescriptorSetBinding binding; bool valid = false; }; @@ -62,8 +65,7 @@ namespace Atlas { ShaderDescriptorBinding bindings[BINDINGS_PER_DESCRIPTOR_SET]; uint32_t bindingCount = 0; - VkDescriptorSetLayout layout = {}; - VkDescriptorSetLayoutBinding layoutBindings[BINDINGS_PER_DESCRIPTOR_SET]; + Ref layout; }; struct ShaderVertexInput { @@ -82,6 +84,8 @@ namespace Atlas { PushConstantRange* GetPushConstantRange(const std::string& name); + bool TryOverrideDescriptorSetLayout(Ref layout, uint32_t set); + std::vector modules; std::vector stageCreateInfos; diff --git a/src/engine/graphics/ShaderCompiler.cpp b/src/engine/graphics/ShaderCompiler.cpp index 316f97494..ba79a2855 100644 --- a/src/engine/graphics/ShaderCompiler.cpp +++ b/src/engine/graphics/ShaderCompiler.cpp @@ -1,6 +1,7 @@ #include "ShaderCompiler.h" #include "GraphicsDevice.h" #include "Log.h" +#include "loader/AssetLoader.h" #include #include @@ -14,6 +15,9 @@ namespace Atlas { void LogError(const ShaderStageFile &shaderStageFile, const std::vector& macros, glslang::TShader& shader); + std::unordered_map ShaderCompiler::cache; + const std::string ShaderCompiler::cachePath = ".cache/"; + void ShaderCompiler::Init() { glslang::InitializeProcess(); @@ -24,10 +28,29 @@ namespace Atlas { glslang::FinalizeProcess(); + for (auto& [_, entry] : cache) { + if (!entry.wasModified) continue; + + auto fileStream = Loader::AssetLoader::WriteFile(entry.fileName, std::ios::out | std::ios::binary); + if (fileStream.is_open()) { + fileStream.write(reinterpret_cast(entry.spirvBinary.data()), + entry.spirvBinary.size() * sizeof(uint32_t)); + + fileStream.close(); + } + } + } std::vector ShaderCompiler::Compile(const ShaderStageFile& shaderFile, - const std::vector& macros, bool& success) { + const std::vector& macros, bool useCache, bool& success) { + + if (useCache) { + auto cacheHit = TryFindCacheEntry(shaderFile, macros, success); + if (success) { + return cacheHit.spirvBinary; + } + } auto device = GraphicsDevice::DefaultDevice; @@ -38,7 +61,7 @@ namespace Atlas { EShLanguage stage = FindLanguage(shaderFile.shaderStage); glslang::TShader shader(stage); - + if (device->support.hardwareRayTracing) { // Mac struggles with Spv 1.4, so use only when necessary shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_4); @@ -53,6 +76,7 @@ namespace Atlas { auto glslCode = shaderFile.GetGlslCode(macros); const char* shaderStrings[] = { glslCode.data() }; shader.setStrings(shaderStrings, 1); + shader.getIntermediate()->addSourceText(glslCode.data(), glslCode.size()); if (!shader.parse(&Resources, 100, false, messages)) { LogError(shaderFile, macros, shader); @@ -79,13 +103,88 @@ namespace Atlas { std::vector optimizedBinary; if (opt.Run(spirvBinary.data(), spirvBinary.size(), &optimizedBinary)) { + AddCacheEntry(shaderFile, macros, optimizedBinary); return optimizedBinary; } + AddCacheEntry(shaderFile, macros, optimizedBinary); return spirvBinary; } + ShaderCompiler::SpvCacheEntry ShaderCompiler::TryFindCacheEntry( + const ShaderStageFile& shaderFile, const std::vector& macros, bool& success) { + + auto hash = CalculateHash(shaderFile, macros); + + success = true; + + SpvCacheEntry entry; + if (cache.contains(hash)) { + entry = cache[hash]; + } + else { + auto path = cachePath + std::to_string(hash); + if (Loader::AssetLoader::FileExists(path)) { + entry.fileName = path; + entry.lastModified = Loader::AssetLoader::GetFileLastModifiedTime(path, std::filesystem::file_time_type::min()); + + auto fileStream = Loader::AssetLoader::ReadFile(path, std::ios::in | std::ios::binary); + if (fileStream.is_open()) { + auto content = Loader::AssetLoader::GetFileContent(fileStream); + fileStream.close(); + + // Quick and dirty memcpy + entry.spirvBinary.resize(content.size() / sizeof(uint32_t)); + std::memcpy(entry.spirvBinary.data(), content.data(), content.size()); + } + else { + success = false; + } + } + else { + success = false; + } + } + + success &= shaderFile.lastModified <= entry.lastModified; + return entry; + + } + + void ShaderCompiler::AddCacheEntry(const ShaderStageFile& shaderFile, + const std::vector& macros, const std::vector& binary) { + + auto hash = CalculateHash(shaderFile, macros); + + SpvCacheEntry entry { + .fileName = cachePath + std::to_string(hash), + + .lastModified = shaderFile.lastModified, + .spirvBinary = binary, + + .wasModified = true + }; + cache[hash] = entry; + + } + + Hash ShaderCompiler::CalculateHash(const ShaderStageFile& shaderFile, + const std::vector& macros) { + + auto envMacros = shaderFile.GetEnvironmentMacros(); + + Hash hash = 0; + HashCombine(hash, shaderFile.filename); + for (auto& macro : macros) + HashCombine(hash, macro); + for (auto& macro : envMacros) + HashCombine(hash, macro); + + return hash; + + } + void InitBuildInResources(TBuiltInResource &Resources) { Resources.maxLights = 32; Resources.maxClipPlanes = 6; diff --git a/src/engine/graphics/ShaderCompiler.h b/src/engine/graphics/ShaderCompiler.h index 3df4c622f..9d4ebd0a2 100644 --- a/src/engine/graphics/ShaderCompiler.h +++ b/src/engine/graphics/ShaderCompiler.h @@ -2,9 +2,11 @@ #include "Common.h" #include "Shader.h" +#include "common/Hash.h" #include #include +#include namespace Atlas { @@ -18,8 +20,31 @@ namespace Atlas { static void Shutdown(); static std::vector Compile(const ShaderStageFile& shaderFile, + const std::vector& macros, bool useCache, bool& success); + + private: + struct SpvCacheEntry { + std::string fileName; + + std::filesystem::file_time_type lastModified; + std::vector spirvBinary; + + bool wasModified = false; + }; + + static SpvCacheEntry TryFindCacheEntry(const ShaderStageFile& shaderFile, const std::vector& macros, bool& success); + static void AddCacheEntry(const ShaderStageFile& shaderFile, + const std::vector& macros, const std::vector& binary); + + static Hash CalculateHash(const ShaderStageFile& shaderFile, + const std::vector& macros); + + static std::unordered_map cache; + + static const std::string cachePath; + }; } diff --git a/src/engine/graphics/Surface.cpp b/src/engine/graphics/Surface.cpp index 492198cea..6efc906ea 100644 --- a/src/engine/graphics/Surface.cpp +++ b/src/engine/graphics/Surface.cpp @@ -14,7 +14,26 @@ namespace Atlas { auto nativeInstance = instance->GetNativeInstance(); success = SDL_Vulkan_CreateSurface(window, nativeInstance, &surface); - assert(success && "Error creating surface for window"); + AE_ASSERT(success && "Error creating surface for window"); + + } + + Surface::Surface(Instance* instance, bool& success) : + instance(instance), window(nullptr) { + + width = 1920; + height = 1080; + + auto nativeInstance = instance->GetNativeInstance(); + + VkHeadlessSurfaceCreateInfoEXT createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT; + createInfo.flags = 0; + + auto result = vkCreateHeadlessSurfaceEXT(nativeInstance, &createInfo, nullptr, &surface); + VK_CHECK_MESSAGE(result, "Error creating headless surface"); + + success = result == VK_SUCCESS; } @@ -31,9 +50,22 @@ namespace Atlas { } - SDL_Window *Surface::GetNativeWindow() const { + void Surface::SetExtent(int32_t width, int32_t height) { + + this->width = width; + this->height = height; + + } + + void Surface::GetExtent(int32_t& width, int32_t& height) { - return window; + if ((this->width < 0 || this->height < 0) && window != nullptr) { + SDL_GL_GetDrawableSize(window, &width, &height); + } + else if (window == nullptr) { + width = this->width; + height = this->height; + } } diff --git a/src/engine/graphics/Surface.h b/src/engine/graphics/Surface.h index 8b94b0444..3651b783d 100644 --- a/src/engine/graphics/Surface.h +++ b/src/engine/graphics/Surface.h @@ -13,11 +13,15 @@ namespace Atlas { public: Surface(Instance* instance, SDL_Window* window, bool& success); + Surface(Instance* instance, bool& success); + ~Surface(); VkSurfaceKHR GetNativeSurface() const; - SDL_Window* GetNativeWindow() const; + void SetExtent(int32_t width, int32_t height); + + void GetExtent(int32_t& width, int32_t& height); private: Instance* instance = nullptr; @@ -25,6 +29,9 @@ namespace Atlas { VkSurfaceKHR surface; SDL_Window* window = nullptr; + int32_t width = -1; + int32_t height = -1; + }; } diff --git a/src/engine/lighting/SSGI.cpp b/src/engine/lighting/SSGI.cpp new file mode 100644 index 000000000..c85c1bd2f --- /dev/null +++ b/src/engine/lighting/SSGI.cpp @@ -0,0 +1,12 @@ +#include "SSGI.h" + +namespace Atlas { + + namespace Lighting { + + + + + } + +} \ No newline at end of file diff --git a/src/engine/lighting/SSGI.h b/src/engine/lighting/SSGI.h new file mode 100644 index 000000000..9bee1ae22 --- /dev/null +++ b/src/engine/lighting/SSGI.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../System.h" + +namespace Atlas { + + namespace Lighting { + + class SSGI { + + public: + SSGI() = default; + + bool enable = true; + bool enableAo = true; + + float radius = 1.0f; + + int32_t rayCount = 2; + int32_t sampleCount = 8; + + float irradianceLimit = 10.0f; + float aoStrength = 1.0f; + + bool rt = false; + bool opacityCheck = false; + + }; + + } + +} \ No newline at end of file diff --git a/src/engine/loader/AssetLoader.cpp b/src/engine/loader/AssetLoader.cpp index efc11aa66..15c8b12f1 100644 --- a/src/engine/loader/AssetLoader.cpp +++ b/src/engine/loader/AssetLoader.cpp @@ -160,6 +160,22 @@ namespace Atlas { } + std::filesystem::file_time_type AssetLoader::GetFileLastModifiedTime(const std::string& path, + const std::filesystem::file_time_type defaultTime) { + + const int32_t tryCount = 2; + + for (int32_t i = 0; i < tryCount; i++) { + try { + return std::filesystem::last_write_time(GetFullPath(path)); + } + catch (...) {} + } + + return defaultTime; + + } + std::vector AssetLoader::GetFileContent(std::ifstream& stream) { auto size = AssetLoader::GetFileSize(stream); diff --git a/src/engine/loader/AssetLoader.h b/src/engine/loader/AssetLoader.h index 6ce8f1596..91e75a2d8 100644 --- a/src/engine/loader/AssetLoader.h +++ b/src/engine/loader/AssetLoader.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace Atlas { @@ -75,6 +76,13 @@ namespace Atlas { */ static size_t GetFileSize(std::ifstream& stream); + /** + * @param filename A path to the file relative to the asset directory. + * @param defaultTime A default time to be returned if the file can't be checked, e.g. if it is opened + */ + static std::filesystem::file_time_type GetFileLastModifiedTime(const std::string& path, + const std::filesystem::file_time_type defaultTime); + /** * Returns the content of the file as a byte vector. * @param stream An ifstream object. diff --git a/src/engine/loader/ModelLoader.cpp b/src/engine/loader/ModelLoader.cpp index 0a11e30d9..1b1d67adb 100644 --- a/src/engine/loader/ModelLoader.cpp +++ b/src/engine/loader/ModelLoader.cpp @@ -183,6 +183,10 @@ namespace Atlas { threads[i].join(); } + for (auto& images : materialImages) { + ImagesToTexture(images); + } + meshData.subData = std::vector(scene->mNumMaterials); for (uint32_t i = 0; i < scene->mNumMaterials; i++) { @@ -343,6 +347,10 @@ namespace Atlas { threads[i].join(); } + for (auto& images : materialImages) { + ImagesToTexture(images); + } + std::map> meshMap; for (uint32_t i = 0; i < assimpScene->mNumMeshes; i++) { @@ -557,29 +565,29 @@ namespace Atlas { assimpMaterial->Get(AI_MATKEY_METALLIC_FACTOR, material.metalness); assimpMaterial->Get(AI_MATKEY_ROUGHNESS_FACTOR, material.roughness); } - + if (images.baseColorImage && images.baseColorImage->HasData()) { - material.baseColorMap = std::make_shared(images.baseColorImage); + material.baseColorMap = images.baseColorTexture; material.baseColorMapPath = images.baseColorImage->fileName; } if (images.opacityImage && images.opacityImage->HasData()) { - material.opacityMap = std::make_shared(images.opacityImage); + material.opacityMap = images.opacityTexture; material.opacityMapPath = images.opacityImage->fileName; } if (images.roughnessImage && images.roughnessImage->HasData()) { - material.roughnessMap = std::make_shared(images.roughnessImage); + material.roughnessMap = images.roughnessTexture; material.roughnessMapPath = images.roughnessImage->fileName; } if (images.metallicImage && images.metallicImage->HasData()) { - material.metalnessMap = std::make_shared(images.metallicImage); + material.metalnessMap = images.metallicTexture; material.metalnessMapPath = images.metallicImage->fileName; } if (images.normalImage && images.normalImage->HasData()) { - material.normalMap = std::make_shared(images.normalImage); + material.normalMap = images.normalTexture; material.normalMapPath = images.normalImage->fileName; } if (images.displacementImage && images.displacementImage->HasData()) { - material.displacementMap = std::make_shared(images.displacementImage); + material.displacementMap = images.displacementTexture; material.displacementMapPath = images.displacementImage->fileName; } @@ -689,6 +697,29 @@ namespace Atlas { } } + void ModelLoader::ImagesToTexture(MaterialImages& images) { + + if (images.baseColorImage && images.baseColorImage->HasData()) { + images.baseColorTexture = std::make_shared(images.baseColorImage);; + } + if (images.opacityImage && images.opacityImage->HasData()) { + images.opacityTexture = std::make_shared(images.opacityImage); + } + if (images.roughnessImage && images.roughnessImage->HasData()) { + images.roughnessTexture = std::make_shared(images.roughnessImage); + } + if (images.metallicImage && images.metallicImage->HasData()) { + images.metallicTexture = std::make_shared(images.metallicImage); + } + if (images.normalImage && images.normalImage->HasData()) { + images.normalTexture = std::make_shared(images.normalImage); + } + if (images.displacementImage && images.displacementImage->HasData()) { + images.displacementTexture = std::make_shared(images.displacementImage); + } + + } + std::string ModelLoader::GetDirectoryPath(std::string filename) { auto directoryPath = Common::Path::GetDirectory(filename); diff --git a/src/engine/loader/ModelLoader.h b/src/engine/loader/ModelLoader.h index e3a21c461..d1cef5c7f 100644 --- a/src/engine/loader/ModelLoader.h +++ b/src/engine/loader/ModelLoader.h @@ -36,6 +36,13 @@ namespace Atlas { Ref> metallicImage; Ref> normalImage; Ref> displacementImage; + + Ref baseColorTexture; + Ref opacityTexture; + Ref roughnessTexture; + Ref metallicTexture; + Ref normalTexture; + Ref displacementTexture; }; static void LoadMaterial(aiMaterial* assimpMaterial, MaterialImages& images, Material& material); @@ -44,6 +51,8 @@ namespace Atlas { const std::string& directory, bool isObj, bool hasTangents, int32_t maxTextureResolution, bool rgbSupport); + static void ImagesToTexture(MaterialImages& images); + static std::string GetDirectoryPath(std::string filename); template diff --git a/src/engine/loader/ShaderLoader.cpp b/src/engine/loader/ShaderLoader.cpp index 8d116fc83..9ef7cfa91 100644 --- a/src/engine/loader/ShaderLoader.cpp +++ b/src/engine/loader/ShaderLoader.cpp @@ -39,7 +39,7 @@ namespace Atlas { auto path = sourceDirectory.length() != 0 ? sourceDirectory + "/" : ""; path += filename; - pathLastModified = GetModifiedTime(Loader::AssetLoader::GetFullPath(path), fileTime); + pathLastModified = AssetLoader::GetFileLastModifiedTime(Loader::AssetLoader::GetFullPath(path), fileTime); return pathLastModified > fileTime; } @@ -59,7 +59,7 @@ namespace Atlas { std::stringstream shaderStream; if (mainFile) - lastModified = GetModifiedTime(Loader::AssetLoader::GetFullPath(filename), lastModified); + lastModified = AssetLoader::GetFileLastModifiedTime(Loader::AssetLoader::GetFullPath(filename), lastModified); shaderFile = Loader::AssetLoader::ReadFile(filename, std::ios::in); @@ -117,7 +117,7 @@ namespace Atlas { includes.push_back(includePath); - auto includeLastModified = GetModifiedTime(includePath, lastModified); + auto includeLastModified = AssetLoader::GetFileLastModifiedTime(includePath, lastModified); lastModified = includeLastModified > lastModified ? includeLastModified : lastModified; auto includeCode = ReadShaderFile(includePath, false, includes, extensions, lastModified); @@ -224,24 +224,6 @@ namespace Atlas { } - std::filesystem::file_time_type ShaderLoader::GetModifiedTime(const std::string& path, - const std::filesystem::file_time_type defaultTime) { - - const int32_t tryCount = 2; - - for (int32_t i = 0; i < tryCount; i++) { - try { - return std::filesystem::last_write_time(path); - } - catch (...) {} - } - - Log::Warning("Couldn't access shader for reload checking: " + path); - - return defaultTime; - - } - } } \ No newline at end of file diff --git a/src/engine/loader/ShaderLoader.h b/src/engine/loader/ShaderLoader.h index 4ad421219..38d3da061 100644 --- a/src/engine/loader/ShaderLoader.h +++ b/src/engine/loader/ShaderLoader.h @@ -32,9 +32,6 @@ namespace Atlas { static std::vector ExtractExtensions(std::vector codeLines, std::vector& extensions); - static std::filesystem::file_time_type GetModifiedTime(const std::string& path, - const std::filesystem::file_time_type defaultTime); - static std::string sourceDirectory; }; diff --git a/src/engine/mesh/DataComponent.h b/src/engine/mesh/DataComponent.h index 782996523..9aad8b433 100644 --- a/src/engine/mesh/DataComponent.h +++ b/src/engine/mesh/DataComponent.h @@ -205,28 +205,28 @@ namespace Atlas { switch(format) { case ComponentFormat::UnsignedInt: return VK_FORMAT_R32_UINT; case ComponentFormat::UnsignedShort: return VK_FORMAT_R16_UINT; - default: assert(0 && "Invalid combination of formats"); + default: AE_ASSERT(0 && "Invalid combination of formats"); } } if constexpr(std::is_same_v) { switch(format) { case ComponentFormat::Float: return VK_FORMAT_R32_SFLOAT; case ComponentFormat::HalfFloat: return VK_FORMAT_R16_SFLOAT; - default: assert(0 && "Invalid combination of formats"); + default: AE_ASSERT(0 && "Invalid combination of formats"); } } if constexpr(std::is_same_v) { switch(format) { case ComponentFormat::Float: return VK_FORMAT_R32G32_SFLOAT; case ComponentFormat::HalfFloat: return VK_FORMAT_R16G16_SFLOAT; - default: assert(0 && "Invalid combination of formats"); + default: AE_ASSERT(0 && "Invalid combination of formats"); } } if constexpr(std::is_same_v) { switch(format) { case ComponentFormat::Float: return VK_FORMAT_R32G32B32_SFLOAT; case ComponentFormat::HalfFloat: return VK_FORMAT_R16G16B16_SFLOAT; - default: assert(0 && "Invalid combination of formats"); + default: AE_ASSERT(0 && "Invalid combination of formats"); } } if constexpr(std::is_same_v) { @@ -235,11 +235,11 @@ namespace Atlas { case ComponentFormat::HalfFloat: return VK_FORMAT_R16G16B16A16_SFLOAT; case ComponentFormat::PackedNormal: return VK_FORMAT_A2B10G10R10_SNORM_PACK32; case ComponentFormat::PackedColor: return VK_FORMAT_R8G8B8A8_UNORM; - default: assert(0 && "Invalid combination of formats"); + default: AE_ASSERT(0 && "Invalid combination of formats"); } } - assert(0 && "Invalid combination of formats"); + AE_ASSERT(0 && "Invalid combination of formats"); return VK_FORMAT_R32G32B32A32_SFLOAT; } @@ -326,12 +326,16 @@ namespace Atlas { data.clear(); converted.clear(); + data.shrink_to_fit(); + converted.shrink_to_fit(); + } template void DataComponent::ClearConverted() { converted.clear(); + converted.shrink_to_fit(); } diff --git a/src/engine/mesh/Mesh.cpp b/src/engine/mesh/Mesh.cpp index 0e886d1a7..a3ddf12e8 100644 --- a/src/engine/mesh/Mesh.cpp +++ b/src/engine/mesh/Mesh.cpp @@ -4,6 +4,8 @@ #include "../renderer/OpaqueRenderer.h" #include "../renderer/ShadowRenderer.h" +#include "../graphics/ASBuilder.h" + namespace Atlas { namespace Mesh { @@ -78,6 +80,71 @@ namespace Atlas { } + void Mesh::BuildBVH(bool parallelBuild) { + + auto device = Graphics::GraphicsDevice::DefaultDevice; + bool hardwareRayTracing = device->support.hardwareRayTracing; + bool bindless = device->support.bindless; + + AE_ASSERT(data.indexCount > 0 && "There is no data in this mesh"); + + if (data.indexCount == 0 || !bindless) return; + + data.BuildBVH(parallelBuild); + + triangleBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit, sizeof(GPUTriangle)); + triangleBuffer.SetSize(data.gpuTriangles.size()); + triangleBuffer.SetData(data.gpuTriangles.data(), 0, data.gpuTriangles.size()); + + if (!hardwareRayTracing) { + blasNodeBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit, sizeof(GPUBVHNode)); + blasNodeBuffer.SetSize(data.gpuBvhNodes.size()); + blasNodeBuffer.SetData(data.gpuBvhNodes.data(), 0, data.gpuBvhNodes.size()); + + bvhTriangleBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit, sizeof(GPUBVHTriangle)); + bvhTriangleBuffer.SetSize(data.gpuBvhTriangles.size()); + bvhTriangleBuffer.SetData(data.gpuBvhTriangles.data(), 0, data.gpuBvhTriangles.size()); + } + else { + Graphics::ASBuilder asBuilder; + + std::vector geometryRegions; + for (auto& subData : data.subData) { + geometryRegions.emplace_back(Graphics::ASGeometryRegion{ + .indexCount = subData.indicesCount, + .indexOffset = subData.indicesOffset, + .opaque = !subData.material->HasOpacityMap() && subData.material->opacity == 1.0f + }); + } + + auto blasDesc = asBuilder.GetBLASDescForTriangleGeometry(vertexBuffer.buffer, indexBuffer.buffer, + vertexBuffer.elementCount, vertexBuffer.elementSize, indexBuffer.elementSize, geometryRegions); + + blas = device->CreateBLAS(blasDesc); + + std::vector triangleOffsets; + for (const auto& subData : data.subData) { + auto triangleOffset = subData.indicesOffset / 3; + triangleOffsets.push_back(triangleOffset); + } + + triangleOffsetBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit, sizeof(uint32_t)); + triangleOffsetBuffer.SetSize(triangleOffsets.size(), triangleOffsets.data()); + + needsBvhRefresh = true; + + } + + isBvhBuilt = true; + + } + + bool Mesh::IsBVHBuilt() const { + + return isBvhBuilt; + + } + } } \ No newline at end of file diff --git a/src/engine/mesh/Mesh.h b/src/engine/mesh/Mesh.h index feb1f8483..6bf0a6b9e 100644 --- a/src/engine/mesh/Mesh.h +++ b/src/engine/mesh/Mesh.h @@ -13,6 +13,10 @@ namespace Atlas { + namespace Scene { + class RTData; + } + namespace Mesh { enum class MeshMobility { @@ -27,8 +31,12 @@ namespace Atlas { HostAccessBit = (1 << 1), } MeshUsageBits; + + class Mesh { + friend Scene::RTData; + public: Mesh() = default; @@ -54,6 +62,14 @@ namespace Atlas { */ void UpdateVertexArray(); + /** + * Builds up BVH and fills raytracing related buffers + */ + void BuildBVH(bool parallelBuild = true); + + + bool IsBVHBuilt() const; + std::string name = ""; MeshData data; @@ -69,6 +85,11 @@ namespace Atlas { Buffer::VertexBuffer tangentBuffer; Buffer::VertexBuffer colorBuffer; + Buffer::Buffer blasNodeBuffer; + Buffer::Buffer triangleBuffer; + Buffer::Buffer bvhTriangleBuffer; + Buffer::Buffer triangleOffsetBuffer; + Ref blas = nullptr; Ref impostor = nullptr; @@ -89,6 +110,9 @@ namespace Atlas { private: bool isLoaded = false; + std::atomic_bool isBvhBuilt = false; + std::atomic_bool needsBvhRefresh = false; + }; diff --git a/src/engine/mesh/MeshData.cpp b/src/engine/mesh/MeshData.cpp index a11b95489..384c72d23 100644 --- a/src/engine/mesh/MeshData.cpp +++ b/src/engine/mesh/MeshData.cpp @@ -116,7 +116,7 @@ namespace Atlas { } - void MeshData::BuildBVH() { + void MeshData::BuildBVH(bool parallelBuild) { auto device = Graphics::GraphicsDevice::DefaultDevice; bool hardwareRayTracing = device->support.hardwareRayTracing; @@ -139,6 +139,7 @@ namespace Atlas { vec4 color2; int32_t materialIdx; + float opacity; }; uint32_t triangleCount = 0; @@ -193,6 +194,7 @@ namespace Atlas { } triangles[k].materialIdx = sub.materialIdx; + triangles[k].opacity = sub.material->HasOpacityMap() ? -1.0f : sub.material->opacity; auto min = glm::min(glm::min(triangles[k].v0, triangles[k].v1), triangles[k].v2); @@ -215,7 +217,7 @@ namespace Atlas { Volume::BVH bvh; if (!hardwareRayTracing) { // Generate BVH - bvh = Volume::BVH(aabbs, bvhTriangles); + bvh = Volume::BVH(aabbs, bvhTriangles, parallelBuild); bvhTriangles.clear(); bvhTriangles.shrink_to_fit(); @@ -288,15 +290,15 @@ namespace Atlas { gpuTriangle.v2 = vec4(triangle.v2, cn2); gpuTriangle.d0 = vec4(cuv0, cuv1, cuv2, reinterpret_cast(triangle.materialIdx)); gpuTriangle.d1 = vec4(ct, cbt, bvhTriangle.endOfNode ? 1.0f : -1.0f, 0.0f); - gpuTriangle.d2 = vec4(cc0, cc1, cc2, 0.0f); + gpuTriangle.d2 = vec4(cc0, cc1, cc2, triangle.opacity); gpuTriangles.push_back(gpuTriangle); if (!hardwareRayTracing) { - BVHTriangle gpuBvhTriangle; + GPUBVHTriangle gpuBvhTriangle; gpuBvhTriangle.v0 = vec4(triangle.v0, bvhTriangle.endOfNode ? 1.0f : -1.0f); gpuBvhTriangle.v1 = vec4(triangle.v1, reinterpret_cast(triangle.materialIdx)); - gpuBvhTriangle.v2 = vec4(triangle.v2, 0.0f); + gpuBvhTriangle.v2 = vec4(triangle.v2, triangle.opacity); gpuBvhTriangles.push_back(gpuBvhTriangle); } @@ -324,6 +326,12 @@ namespace Atlas { } + bool MeshData::IsBVHBuilt() { + + return gpuBvhTriangles.size() > 0; + + } + void MeshData::DeepCopy(const MeshData& that) { filename = that.filename; diff --git a/src/engine/mesh/MeshData.h b/src/engine/mesh/MeshData.h index 6d769087b..92338bf45 100644 --- a/src/engine/mesh/MeshData.h +++ b/src/engine/mesh/MeshData.h @@ -32,6 +32,7 @@ namespace Atlas { class MeshData { + friend class Mesh; friend class Scene::RTData; public: @@ -85,7 +86,13 @@ namespace Atlas { /** * Builds a blas from the data */ - void BuildBVH(); + void BuildBVH(bool parallelBuild); + + /** + * + * @return + */ + bool IsBVHBuilt(); std::string filename; @@ -113,7 +120,7 @@ namespace Atlas { int32_t vertexCount = 0; std::vector gpuTriangles; - std::vector gpuBvhTriangles; + std::vector gpuBvhTriangles; std::vector gpuBvhNodes; }; diff --git a/src/engine/ocean/Ocean.h b/src/engine/ocean/Ocean.h index f175854f8..1eecdd8c7 100644 --- a/src/engine/ocean/Ocean.h +++ b/src/engine/ocean/Ocean.h @@ -44,8 +44,8 @@ namespace Atlas { vec3 scatterColor = vec3(0.3f, 0.7f, 0.6f); vec2 waterColorIntensity = vec2(0.1f, 0.2f); - float displacementScale = 0.11f; - float choppynessScale = 0.21f; + float displacementScale = 0.011f; + float choppynessScale = 0.021f; float tiling = 64.0f; diff --git a/src/engine/ocean/OceanSimulation.h b/src/engine/ocean/OceanSimulation.h index 89020a97c..805c6a88f 100644 --- a/src/engine/ocean/OceanSimulation.h +++ b/src/engine/ocean/OceanSimulation.h @@ -40,17 +40,17 @@ namespace Atlas { float choppinessScale = 3.0f; float displacementScale = 4.0f; - float tiling = 64.0f; + float tiling = 20.0f; vec4 spectrumTilingFactors = vec4(1.0f, 3.0f, 6.0f, 9.0f); vec4 spectrumWeights = vec4(1.0f, 0.5f, 0.25f, 0.125f); vec4 spectrumFadeoutDistances = vec4(200.0f, 400.0f, 1000.0f, 4000.0f); - float waveAmplitude = 1.0f; + float waveAmplitude = 0.45e-3f; float waveSurpression = 0.001f; vec2 windDirection = vec2(0.8f, 0.6f); - float windSpeed = 60.0f; + float windSpeed = 100.0f; float windDependency = 0.9f; float foamTemporalWeight = 0.985f; diff --git a/src/engine/pipeline/PipelineManager.cpp b/src/engine/pipeline/PipelineManager.cpp index e17cfe22d..a8a7b49d8 100644 --- a/src/engine/pipeline/PipelineManager.cpp +++ b/src/engine/pipeline/PipelineManager.cpp @@ -13,15 +13,28 @@ namespace Atlas { #endif std::mutex PipelineManager::shaderToVariantsMutex; std::unordered_map> PipelineManager::shaderToVariantsMap; + Ref PipelineManager::globalDescriptorSetLayoutOverrides[DESCRIPTOR_SET_COUNT]; void PipelineManager::Init() { - + for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) { + globalDescriptorSetLayoutOverrides[i] = nullptr; + } } void PipelineManager::Shutdown() { + Clear(); + + } + + void PipelineManager::Clear() { + + for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) { + globalDescriptorSetLayoutOverrides[i] = nullptr; + } + shaderToVariantsMap.clear(); } @@ -33,6 +46,8 @@ namespace Atlas { std::unordered_map lastModifiedMap; if (hotReload) { for (auto& [_, variants] : shaderToVariantsMap) { + std::lock_guard innerLock(variants->variantsMutex); + for (auto& stageFile : variants->shader->shaderStageFiles) { for (auto& includePath : stageFile.includes) { if (lastModifiedMap.find(includePath) != lastModifiedMap.end()) @@ -49,9 +64,11 @@ namespace Atlas { // Do check for hot reload only once per frame for (auto& [hash, variants] : shaderToVariantsMap) { + std::lock_guard innerLock(variants->variantsMutex); + if (hotReload && variants->shader->Reload(lastModifiedMap)) { // Clear variants on hot reload - variants->variants.clear(); + variants->pipelines.clear(); } } @@ -81,6 +98,40 @@ namespace Atlas { } + void PipelineManager::OverrideDescriptorSetLayout(Ref layout, uint32_t set) { + + std::lock_guard lock(shaderToVariantsMutex); + + if (set < DESCRIPTOR_SET_COUNT) { + + globalDescriptorSetLayoutOverrides[set] = layout; + + for (auto& [_, variants] : shaderToVariantsMap) { + + std::unordered_map> validPipelines; + + std::lock_guard innerLock(variants->variantsMutex); + + for (auto& [pipelineHash, pipeline] : variants->pipelines) { + + // Note: We need to only recreate pipelines where the override layout + // is compatible with the shader. Other pipelines should remain valid + // Also: We might override a layout per shader several times, but that doesn't hurt + if (!pipeline->shader->TryOverrideDescriptorSetLayout(layout, set)) { + validPipelines[pipelineHash] = pipeline; + } + + } + + // Overwrite old piplines + variants->pipelines = validPipelines; + + } + + } + + } + Ref PipelineManager::GetOrCreatePipeline(PipelineConfig &config) { auto graphicsDevice = Graphics::GraphicsDevice::DefaultDevice; @@ -115,21 +166,33 @@ namespace Atlas { } Ref pipeline; - if (!variants->variants.contains(config.variantHash)) { + if (!variants->pipelines.contains(config.variantHash)) { + auto shaderVariant = variants->shader->GetVariant(config.macros); + + for (uint32_t i = 0; i < DESCRIPTOR_SET_COUNT; i++) { + auto& layout = globalDescriptorSetLayoutOverrides[i]; + if (layout == nullptr) continue; + + // Only override layout if it is compatible + if (shaderVariant->sets[i].bindingCount > 0) { + shaderVariant->TryOverrideDescriptorSetLayout(layout, i); + } + } + if (config.isCompute) { - auto pipelineDesc = Graphics::ComputePipelineDesc { - .shader = variants->shader->GetVariant(config.macros) + auto pipelineDesc = Graphics::ComputePipelineDesc{ + .shader = shaderVariant }; pipeline = graphicsDevice->CreatePipeline(pipelineDesc); } else { - config.graphicsPipelineDesc.shader = variants->shader->GetVariant(config.macros); + config.graphicsPipelineDesc.shader = shaderVariant; pipeline = graphicsDevice->CreatePipeline(config.graphicsPipelineDesc); } - variants->variants[config.variantHash] = pipeline; + variants->pipelines[config.variantHash] = pipeline; } else { - pipeline = variants->variants[config.variantHash]; + pipeline = variants->pipelines[config.variantHash]; } return pipeline; diff --git a/src/engine/pipeline/PipelineManager.h b/src/engine/pipeline/PipelineManager.h index 80529905c..08c563742 100644 --- a/src/engine/pipeline/PipelineManager.h +++ b/src/engine/pipeline/PipelineManager.h @@ -20,6 +20,8 @@ namespace Atlas { static void Shutdown(); + static void Clear(); + static void Update(); static void EnableHotReload(); @@ -30,12 +32,14 @@ namespace Atlas { static void AddPipeline(PipelineConfig& config); + static void OverrideDescriptorSetLayout(Ref layout, uint32_t set); + private: struct PipelineVariants { Ref shader; std::mutex variantsMutex; - std::unordered_map> variants; + std::unordered_map> pipelines; bool isComplete = false; }; @@ -46,6 +50,8 @@ namespace Atlas { static std::mutex shaderToVariantsMutex; static std::unordered_map> shaderToVariantsMap; + static Ref globalDescriptorSetLayoutOverrides[DESCRIPTOR_SET_COUNT]; + }; } \ No newline at end of file diff --git a/src/engine/postprocessing/PostProcessing.h b/src/engine/postprocessing/PostProcessing.h index 70619b3e8..f448f7668 100644 --- a/src/engine/postprocessing/PostProcessing.h +++ b/src/engine/postprocessing/PostProcessing.h @@ -16,6 +16,7 @@ namespace Atlas { public: float saturation = 1.0f; + float contrast = 1.0f; float whitePoint = 10.0f; bool filmicTonemapping = false; diff --git a/src/engine/renderer/AORenderer.cpp b/src/engine/renderer/AORenderer.cpp index 4d636000f..c1128ec6b 100644 --- a/src/engine/renderer/AORenderer.cpp +++ b/src/engine/renderer/AORenderer.cpp @@ -1,7 +1,6 @@ #include "AORenderer.h" #include "Clock.h" -#include "../common/RandomHelper.h" namespace Atlas { diff --git a/src/engine/renderer/ExampleRenderer.cpp b/src/engine/renderer/ExampleRenderer.cpp index 56ae339fc..54f38d211 100644 --- a/src/engine/renderer/ExampleRenderer.cpp +++ b/src/engine/renderer/ExampleRenderer.cpp @@ -116,7 +116,7 @@ namespace Atlas { mainFrameBuffer = device->CreateFrameBuffer(frameBufferDesc); } { - auto mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoader( + mesh = Atlas::ResourceManager::GetOrLoadResourceWithLoader( "sponza/sponza.obj", Atlas::Loader::ModelLoader::LoadMesh, false, glm::mat4(1.0f), 2048 ); @@ -165,11 +165,13 @@ namespace Atlas { void ExampleRenderer::Render(Camera* camera) { + auto swapChain = device->swapChain; + if (!swapChain->isComplete) return; + auto blue = abs(sin(Clock::Get())); auto commandList = device->GetCommandList(Graphics::GraphicsQueue); - auto swapChain = device->swapChain; - + Graphics::Profiler::BeginThread("Main thread", commandList); commandList->BeginCommands(); diff --git a/src/engine/renderer/ExampleRenderer.h b/src/engine/renderer/ExampleRenderer.h index b55655cfa..2e8d02b00 100644 --- a/src/engine/renderer/ExampleRenderer.h +++ b/src/engine/renderer/ExampleRenderer.h @@ -31,7 +31,7 @@ namespace Atlas { Ref queryPool = nullptr; - Ref mesh = nullptr; + ResourceHandle mesh; struct PushConstants { mat4 vMatrix; diff --git a/src/engine/renderer/GBufferDownscaleRenderer.h b/src/engine/renderer/GBufferDownscaleRenderer.h index 0fa66fce7..3c68d48ac 100644 --- a/src/engine/renderer/GBufferDownscaleRenderer.h +++ b/src/engine/renderer/GBufferDownscaleRenderer.h @@ -23,9 +23,6 @@ namespace Atlas { PipelineConfig downscalePipelineConfig; PipelineConfig downscaleDepthOnlyPipelineConfig; - //OldShader::OldShader downscale; - //OldShader::OldShader downscaleDepthOnly; - }; } diff --git a/src/engine/renderer/GIRenderer.cpp b/src/engine/renderer/GIRenderer.cpp new file mode 100644 index 000000000..15fa5f950 --- /dev/null +++ b/src/engine/renderer/GIRenderer.cpp @@ -0,0 +1,296 @@ +#include "GIRenderer.h" + +#include "Clock.h" +#include "../common/RandomHelper.h" + +namespace Atlas { + + namespace Renderer { + + void GIRenderer::Init(Graphics::GraphicsDevice *device) { + + this->device = device; + + const int32_t filterSize = 3; + blurFilter.CalculateBoxFilter(filterSize); + + auto noiseImage = Loader::ImageLoader::LoadImage("scrambling_ranking.png", false, 4); + scramblingRankingTexture = Texture::Texture2D(noiseImage->width, noiseImage->height, + VK_FORMAT_R8G8B8A8_UNORM); + scramblingRankingTexture.SetData(noiseImage->GetData()); + + noiseImage = Loader::ImageLoader::LoadImage("sobol.png"); + sobolSequenceTexture = Texture::Texture2D(noiseImage->width, noiseImage->height, + VK_FORMAT_R8G8B8A8_UNORM); + sobolSequenceTexture.SetData(noiseImage->GetData()); + + ssgiPipelineConfig = PipelineConfig("ssgi/ssgi.csh"); + //rtaoPipelineConfig = PipelineConfig("ao/rtao.csh"); + temporalPipelineConfig = PipelineConfig("ssgi/temporal.csh"); + + horizontalBlurPipelineConfig = PipelineConfig("bilateralBlur.csh", + {"HORIZONTAL", "DEPTH_WEIGHT","NORMAL_WEIGHT", "BLUR_RGBA"}); + verticalBlurPipelineConfig = PipelineConfig("bilateralBlur.csh", + {"VERTICAL", "DEPTH_WEIGHT","NORMAL_WEIGHT", "BLUR_RGBA"}); + + rtUniformBuffer = Buffer::UniformBuffer(sizeof(RTUniforms)); + ssUniformBuffer = Buffer::UniformBuffer(sizeof(SSUniforms)); + // If we don't set the element size to the whole thing, otherwise uniform buffer element alignment kicks in + blurWeightsUniformBuffer = Buffer::UniformBuffer(sizeof(float) * (size_t(filterSize) + 1)); + + } + + void GIRenderer::Render(Viewport* viewport, RenderTarget* target, Camera* camera, + Scene::Scene* scene, Graphics::CommandList* commandList) { + + auto ssgi = scene->ssgi; + if (!ssgi || !ssgi->enable) return; + + if (!scene->IsRtDataValid() && ssgi->rt) return; + helper.SetScene(scene, 8); + + ivec2 res = ivec2(target->giTexture.width, target->giTexture.height); + + Graphics::Profiler::BeginQuery("Render SSGI"); + + auto downsampledRT = target->GetData(target->GetGIResolution()); + auto downsampledHistoryRT = target->GetHistoryData(target->GetGIResolution()); + + // Should be reflection resolution + auto depthTexture = downsampledRT->depthTexture; + auto normalTexture = downsampledRT->geometryNormalTexture; + auto roughnessTexture = downsampledRT->roughnessMetallicAoTexture; + auto offsetTexture = downsampledRT->offsetTexture; + auto velocityTexture = downsampledRT->velocityTexture; + auto materialIdxTexture = downsampledRT->materialIdxTexture; + auto lightingTexture = &target->lightingTexture; + + auto historyDepthTexture = downsampledHistoryRT->depthTexture; + auto historyMaterialIdxTexture = downsampledHistoryRT->materialIdxTexture; + auto historyNormalTexture = downsampledHistoryRT->geometryNormalTexture; + + // Bind the geometry normal texure and depth texture + commandList->BindImage(normalTexture->image, normalTexture->sampler, 3, 1); + commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 2); + commandList->BindImage(roughnessTexture->image, roughnessTexture->sampler, 3, 3); + commandList->BindImage(offsetTexture->image, offsetTexture->sampler, 3, 4); + commandList->BindImage(materialIdxTexture->image, materialIdxTexture->sampler, 3, 5); + commandList->BindImage(lightingTexture->image, lightingTexture->sampler, 3, 6); + + commandList->BindImage(scramblingRankingTexture.image, scramblingRankingTexture.sampler, 3, 7); + commandList->BindImage(sobolSequenceTexture.image, sobolSequenceTexture.sampler, 3, 8); + + // Calculate RTAO + if (ssgi->rt) { + /* + Graphics::Profiler::BeginQuery("Trace rays/calculate ao"); + + ivec2 groupCount = ivec2(res.x / 8, res.y / 4); + groupCount.x += ((groupCount.x * 8 == res.x) ? 0 : 1); + groupCount.y += ((groupCount.y * 4 == res.y) ? 0 : 1); + + rtaoPipelineConfig.ManageMacro("OPACITY_CHECK", ao->opacityCheck); + + auto pipeline = PipelineManager::GetPipeline(rtaoPipelineConfig); + + auto uniforms = RTUniforms { + .radius = ao->radius, + .frameSeed = frameCount++, + }; + rtUniformBuffer.SetData(&uniforms, 0); + + commandList->ImageMemoryBarrier(target->swapAoTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT); + + helper.DispatchAndHit(commandList, pipeline, ivec3(groupCount.x * groupCount.y, 1, 1), + [=]() { + commandList->BindImage(target->swapAoTexture.image, 3, 0); + + commandList->BindImage(normalTexture->image, normalTexture->sampler, 3, 1); + commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 2); + commandList->BindImage(offsetTexture->image, offsetTexture->sampler, 3, 3); + + commandList->BindImage(scramblingRankingTexture.image, scramblingRankingTexture.sampler, 3, 4); + commandList->BindImage(sobolSequenceTexture.image, sobolSequenceTexture.sampler, 3, 5); + + commandList->BindBuffer(rtUniformBuffer.Get(), 3, 6); + }); + + commandList->ImageMemoryBarrier(target->swapAoTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); + */ + + } + else { + static int32_t frameCount = 0; + + Graphics::Profiler::BeginQuery("Main pass"); + + ivec2 groupCount = ivec2(res.x / 8, res.y / 4); + groupCount.x += ((res.x % 8 == 0) ? 0 : 1); + groupCount.y += ((res.y % 4 == 0) ? 0 : 1); + + auto pipeline = PipelineManager::GetPipeline(ssgiPipelineConfig); + commandList->BindPipeline(pipeline); + + auto uniforms = SSUniforms { + .radianceLimit = ssgi->irradianceLimit, + .frameSeed = uint32_t(frameCount++), + .radius = ssgi->radius, + .rayCount = uint32_t(ssgi->rayCount), + .sampleCount = uint32_t(ssgi->sampleCount), + }; + ssUniformBuffer.SetData(&uniforms, 0); + + commandList->BindImage(target->swapGiTexture.image, 3, 0); + commandList->BindBuffer(ssUniformBuffer.Get(), 3, 9); + + commandList->ImageMemoryBarrier(target->swapGiTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + commandList->ImageMemoryBarrier(target->swapGiTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); + } + + + if (true) { + Graphics::Profiler::EndAndBeginQuery("Temporal filter"); + + std::vector imageBarriers; + std::vector bufferBarriers; + + ivec2 groupCount = ivec2(res.x / 8, res.y / 8); + groupCount.x += ((groupCount.x * 8 == res.x) ? 0 : 1); + groupCount.y += ((groupCount.y * 8 == res.y) ? 0 : 1); + + auto pipeline = PipelineManager::GetPipeline(temporalPipelineConfig); + commandList->BindPipeline(pipeline); + + imageBarriers = { + {target->giTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + {target->giLengthTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + {target->historyGiTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {target->historyGiLengthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT} + }; + commandList->PipelineBarrier(imageBarriers, bufferBarriers); + + commandList->BindImage(target->giTexture.image, 3, 0); + commandList->BindImage(target->giLengthTexture.image, 3, 1); + + commandList->BindImage(target->swapGiTexture.image, target->swapGiTexture.sampler, 3, 2); + commandList->BindImage(velocityTexture->image, velocityTexture->sampler, 3, 3); + commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 4); + commandList->BindImage(roughnessTexture->image, roughnessTexture->sampler, 3, 5); + commandList->BindImage(normalTexture->image, normalTexture->sampler, 3, 6); + commandList->BindImage(materialIdxTexture->image, materialIdxTexture->sampler, 3, 7); + + commandList->BindImage(target->historyGiTexture.image, target->historyGiTexture.sampler, 3, 8); + commandList->BindImage(target->historyGiLengthTexture.image, target->historyGiLengthTexture.sampler, 3, 9); + commandList->BindImage(historyDepthTexture->image, historyDepthTexture->sampler, 3, 10); + commandList->BindImage(historyNormalTexture->image, historyNormalTexture->sampler, 3, 11); + commandList->BindImage(historyMaterialIdxTexture->image, historyMaterialIdxTexture->sampler, 3, 12); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + // Need barriers for all four images + imageBarriers = { + {target->giTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, + {target->giLengthTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, + {target->historyGiTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, + {target->historyGiLengthTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT} + }; + commandList->PipelineBarrier(imageBarriers, bufferBarriers, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + + commandList->CopyImage(target->giTexture.image, target->historyGiTexture.image); + commandList->CopyImage(target->giLengthTexture.image, target->historyGiLengthTexture.image); + + // Need barriers for all four images + imageBarriers = { + {target->giTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {target->giLengthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {target->historyGiTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {target->historyGiLengthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + }; + commandList->PipelineBarrier(imageBarriers, bufferBarriers, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + } + + { + Graphics::Profiler::EndAndBeginQuery("Blur"); + + const int32_t groupSize = 256; + + std::vector kernelWeights; + std::vector kernelOffsets; + + blurFilter.GetLinearized(&kernelWeights, &kernelOffsets, false); + + auto mean = (kernelWeights.size() - 1) / 2; + kernelWeights = std::vector(kernelWeights.begin() + mean, kernelWeights.end()); + kernelOffsets = std::vector(kernelOffsets.begin() + mean, kernelOffsets.end()); + + auto kernelSize = int32_t(kernelWeights.size() - 1); + + auto horizontalBlurPipeline = PipelineManager::GetPipeline(horizontalBlurPipelineConfig); + auto verticalBlurPipeline = PipelineManager::GetPipeline(verticalBlurPipelineConfig); + + blurWeightsUniformBuffer.SetData(kernelWeights.data(), 0); + + commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 2); + commandList->BindImage(normalTexture->image, normalTexture->sampler, 3, 3); + commandList->BindBuffer(blurWeightsUniformBuffer.Get(), 3, 4); + + std::vector bufferBarriers; + + for (int32_t i = 0; i < 3; i++) { + ivec2 groupCount = ivec2(res.x / groupSize, res.y); + groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); + + std::vector imageBarriers = { + {target->giTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {target->swapGiTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + }; + commandList->PipelineBarrier(imageBarriers, bufferBarriers); + + commandList->BindPipeline(horizontalBlurPipeline); + commandList->PushConstants("constants", &kernelSize); + + commandList->BindImage(target->swapGiTexture.image, 3, 0); + commandList->BindImage(target->giTexture.image, target->giTexture.sampler, 3, 1); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + groupCount = ivec2(res.x, res.y / groupSize); + groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); + + imageBarriers = { + {target->swapGiTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {target->giTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + }; + commandList->PipelineBarrier(imageBarriers, bufferBarriers); + + commandList->BindPipeline(verticalBlurPipeline); + commandList->PushConstants("constants", &kernelSize); + + commandList->BindImage(target->giTexture.image, 3, 0); + commandList->BindImage(target->swapGiTexture.image, target->swapGiTexture.sampler, 3, 1); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + } + } + + commandList->ImageMemoryBarrier(target->giTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_SHADER_READ_BIT); + + Graphics::Profiler::EndQuery(); + Graphics::Profiler::EndQuery(); + + } + + } + +} diff --git a/src/engine/renderer/GIRenderer.h b/src/engine/renderer/GIRenderer.h new file mode 100644 index 000000000..9e9282e58 --- /dev/null +++ b/src/engine/renderer/GIRenderer.h @@ -0,0 +1,57 @@ +#pragma once + +#include "Renderer.h" +#include "helper/RayTracingHelper.h" + +namespace Atlas { + + namespace Renderer { + + class GIRenderer : public Renderer { + + public: + GIRenderer() = default; + + void Init(Graphics::GraphicsDevice* device); + + void Render(Viewport* viewport, RenderTarget* target, Camera* camera, Scene::Scene* scene, + Graphics::CommandList* commandList); + + private: + Graphics::GraphicsDevice* device; + + struct alignas(16) RTUniforms { + float radius; + int32_t frameSeed; + }; + + struct alignas(16) SSUniforms { + float radianceLimit; + uint32_t frameSeed; + float radius; + uint32_t rayCount; + uint32_t sampleCount; + }; + + Filter blurFilter; + Helper::RayTracingHelper helper; + + Texture::Texture2D scramblingRankingTexture; + Texture::Texture2D sobolSequenceTexture; + + PipelineConfig ssgiPipelineConfig; + PipelineConfig rtaoPipelineConfig; + PipelineConfig temporalPipelineConfig; + + PipelineConfig horizontalBlurPipelineConfig; + PipelineConfig verticalBlurPipelineConfig; + + Buffer::UniformBuffer rtUniformBuffer; + Buffer::UniformBuffer ssUniformBuffer; + Buffer::UniformBuffer blurWeightsUniformBuffer; + + }; + + } + +} \ No newline at end of file diff --git a/src/engine/renderer/IndirectLightRenderer.cpp b/src/engine/renderer/IndirectLightRenderer.cpp index 603a103a9..f15fdf655 100644 --- a/src/engine/renderer/IndirectLightRenderer.cpp +++ b/src/engine/renderer/IndirectLightRenderer.cpp @@ -23,15 +23,22 @@ namespace Atlas { auto volume = scene->irradianceVolume; auto ao = scene->ao; auto reflection = scene->reflection; + auto ssgi = scene->ssgi; auto rtDataValid = scene->IsRtDataValid(); auto ddgiEnabled = volume && volume->enable && rtDataValid; auto reflectionEnabled = reflection && reflection->enable && rtDataValid; auto aoEnabled = ao && ao->enable && (!ao->rt || rtDataValid); + auto ssgiEnabled = ssgi && ssgi->enable && (!ssgi->rt || rtDataValid); + + bool ssgiAo = ssgi && ssgi->enable && ssgi->enableAo; + aoEnabled |= ssgiAo; + pipelineConfig.ManageMacro("DDGI", ddgiEnabled); pipelineConfig.ManageMacro("REFLECTION", reflectionEnabled); pipelineConfig.ManageMacro("AO", aoEnabled); + pipelineConfig.ManageMacro("SSGI", ssgiEnabled); auto depthTexture = target->GetData(HALF_RES)->depthTexture; @@ -44,19 +51,23 @@ namespace Atlas { if (reflectionEnabled) { commandList->BindImage(target->reflectionTexture.image, target->reflectionTexture.sampler, 3, 2); } + if (ssgiEnabled) { + commandList->BindImage(target->giTexture.image, target->giTexture.sampler, 3, 3); + } auto uniforms = Uniforms{ .aoEnabled = aoEnabled ? 1 : 0, - .aoDownsampled2x = target->GetAOResolution() == RenderResolution::HALF_RES, + .aoDownsampled2x = ssgiAo ? target->GetGIResolution() == RenderResolution::HALF_RES : + target->GetAOResolution() == RenderResolution::HALF_RES, .reflectionEnabled = reflectionEnabled ? 1 : 0, - .aoStrength = aoEnabled ? ao->strength : 1.0f, + .aoStrength = aoEnabled ? (ssgiAo ? ssgi->aoStrength / sqrt(ssgi->radius) : ao->strength) : 1.0f, .specularProbeMipLevels = int32_t(scene->sky.GetProbe() ? scene->sky.GetProbe()->cubemap.image->mipLevels : 1) }; uniformBuffer.SetData(&uniforms, 0); commandList->BindImage(target->lightingTexture.image, 3, 0); - commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 3); - commandList->BindBuffer(uniformBuffer.Get(), 3, 4); + commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 4); + commandList->BindBuffer(uniformBuffer.Get(), 3, 5); auto resolution = ivec2(target->GetWidth(), target->GetHeight()); auto groupCount = resolution / 8; diff --git a/src/engine/renderer/MainRenderer.cpp b/src/engine/renderer/MainRenderer.cpp index 2a36504bd..57d154752 100644 --- a/src/engine/renderer/MainRenderer.cpp +++ b/src/engine/renderer/MainRenderer.cpp @@ -22,6 +22,8 @@ namespace Atlas { this->device = device; + CreateGlobalDescriptorSetLayout(); + Helper::GeometryHelper::GenerateRectangleVertexArray(vertexArray); Helper::GeometryHelper::GenerateCubeVertexArray(cubeVertexArray); @@ -49,6 +51,7 @@ namespace Atlas { terrainShadowRenderer.Init(device); downscaleRenderer.Init(device); ddgiRenderer.Init(device); + giRenderer.Init(device); aoRenderer.Init(device); rtrRenderer.Init(device); sssRenderer.Init(device); @@ -71,6 +74,9 @@ namespace Atlas { void MainRenderer::RenderScene(Viewport* viewport, RenderTarget* target, Camera* camera, Scene::Scene* scene, Texture::Texture2D* texture, RenderBatch* batch) { + if (!device->swapChain->isComplete) + return; + auto commandList = device->GetCommandList(Graphics::QueueType::GraphicsQueue); commandList->BeginCommands(); @@ -99,10 +105,26 @@ namespace Atlas { PrepareMaterials(scene, materials, materialMap); + std::vector> images; + std::vector> blasBuffers, triangleBuffers, bvhTriangleBuffers, triangleOffsetBuffers; + PrepareBindlessData(scene, images, blasBuffers, triangleBuffers, bvhTriangleBuffers, triangleOffsetBuffers); + SetUniforms(scene, camera); - commandList->BindImage(dfgPreintegrationTexture.image, dfgPreintegrationTexture.sampler, 0, 1); - commandList->BindBuffer(globalUniformBuffer, 0, 0); + commandList->BindBuffer(globalUniformBuffer, 0, 3); + commandList->BindImage(dfgPreintegrationTexture.image, dfgPreintegrationTexture.sampler, 1, 12); + commandList->BindSampler(globalSampler, 1, 13); + commandList->BindBuffers(triangleBuffers, 0, 1); + if (images.size()) + commandList->BindSampledImages(images, 0, 4); + + if (device->support.hardwareRayTracing) { + commandList->BindBuffers(triangleOffsetBuffers, 0, 2); + } + else { + commandList->BindBuffers(blasBuffers, 0, 0); + commandList->BindBuffers(bvhTriangleBuffers, 0, 2); + } auto materialBufferDesc = Graphics::BufferDesc { .usageFlags = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, @@ -112,7 +134,7 @@ namespace Atlas { .size = sizeof(PackedMaterial) * glm::max(materials.size(), size_t(1)), }; auto materialBuffer = device->CreateBuffer(materialBufferDesc); - commandList->BindBuffer(materialBuffer, 0, 2); + commandList->BindBuffer(materialBuffer, 1, 14); if (scene->vegetation) vegetationRenderer.helper.PrepareInstanceBuffer(*scene->vegetation, camera, commandList); @@ -132,9 +154,9 @@ namespace Atlas { } // Bind before any shadows etc. are rendered, this is a shared buffer for all these passes - commandList->BindBuffer(renderList.currentMatricesBuffer, 1, 0); - commandList->BindBuffer(renderList.lastMatricesBuffer, 1, 1); - commandList->BindBuffer(renderList.impostorMatricesBuffer, 1, 2); + commandList->BindBuffer(renderList.currentMatricesBuffer, 1, 1); + commandList->BindBuffer(renderList.lastMatricesBuffer, 1, 2); + commandList->BindBuffer(renderList.impostorMatricesBuffer, 1, 3); if (scene->irradianceVolume) { commandList->BindBuffer(ddgiUniformBuffer, 2, 26); @@ -148,9 +170,9 @@ namespace Atlas { if (scene->sky.GetProbe()) { commandList->BindImage(scene->sky.GetProbe()->filteredSpecular.image, - scene->sky.GetProbe()->filteredSpecular.sampler, 1, 9); + scene->sky.GetProbe()->filteredSpecular.sampler, 1, 10); commandList->BindImage(scene->sky.GetProbe()->filteredDiffuse.image, - scene->sky.GetProbe()->filteredDiffuse.sampler, 1, 10); + scene->sky.GetProbe()->filteredDiffuse.sampler, 1, 11); } volumetricCloudRenderer.RenderShadow(viewport, target, camera, scene, commandList); @@ -208,12 +230,12 @@ namespace Atlas { auto targetData = target->GetData(FULL_RES); - commandList->BindImage(targetData->baseColorTexture->image, targetData->baseColorTexture->sampler, 1, 2); - commandList->BindImage(targetData->normalTexture->image, targetData->normalTexture->sampler, 1, 3); - commandList->BindImage(targetData->geometryNormalTexture->image, targetData->geometryNormalTexture->sampler, 1, 4); - commandList->BindImage(targetData->roughnessMetallicAoTexture->image, targetData->roughnessMetallicAoTexture->sampler, 1, 5); - commandList->BindImage(targetData->materialIdxTexture->image, targetData->materialIdxTexture->sampler, 1, 6); - commandList->BindImage(targetData->depthTexture->image, targetData->depthTexture->sampler, 1, 7); + commandList->BindImage(targetData->baseColorTexture->image, targetData->baseColorTexture->sampler, 1, 3); + commandList->BindImage(targetData->normalTexture->image, targetData->normalTexture->sampler, 1, 4); + commandList->BindImage(targetData->geometryNormalTexture->image, targetData->geometryNormalTexture->sampler, 1, 5); + commandList->BindImage(targetData->roughnessMetallicAoTexture->image, targetData->roughnessMetallicAoTexture->sampler, 1, 6); + commandList->BindImage(targetData->materialIdxTexture->image, targetData->materialIdxTexture->sampler, 1, 7); + commandList->BindImage(targetData->depthTexture->image, targetData->depthTexture->sampler, 1, 8); if (!target->HasHistory()) { auto rtData = target->GetHistoryData(HALF_RES); @@ -289,6 +311,11 @@ namespace Atlas { directLightRenderer.Render(viewport, target, camera, scene, commandList); + commandList->ImageMemoryBarrier(target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + + giRenderer.Render(viewport, target, camera, scene, commandList); + commandList->ImageMemoryBarrier(target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); @@ -332,49 +359,96 @@ namespace Atlas { void MainRenderer::PathTraceScene(Viewport *viewport, PathTracerRenderTarget *target, Camera *camera, Scene::Scene *scene, Texture::Texture2D *texture) { + if (!scene->IsRtDataValid() || !device->swapChain->isComplete) + return; + + static vec2 lastJitter = vec2(0.0f); + auto commandList = device->GetCommandList(Graphics::QueueType::GraphicsQueue); + auto jitter = 2.0f * haltonSequence[haltonIndex] - 1.0f; + jitter.x /= (float)target->GetWidth(); + jitter.y /= (float)target->GetHeight(); + + camera->Jitter(jitter * 0.0f); + commandList->BeginCommands(); Graphics::Profiler::BeginThread("Path tracing", commandList); Graphics::Profiler::BeginQuery("Buffer operations"); - commandList->BindImage(dfgPreintegrationTexture.image, dfgPreintegrationTexture.sampler, 0, 1); - - auto globalUniforms = GlobalUniforms { - .vMatrix = camera->viewMatrix, - .pMatrix = camera->projectionMatrix, - .ivMatrix = camera->invViewMatrix, - .ipMatrix = camera->invProjectionMatrix, - .pvMatrixLast = camera->GetLastJitteredMatrix(), - .pvMatrixCurrent = camera->projectionMatrix * camera->viewMatrix, - .jitterLast = camera->GetLastJitter(), - .jitterCurrent = camera->GetJitter(), - .cameraLocation = vec4(camera->location, 0.0f), - .cameraDirection = vec4(camera->direction, 0.0f), - .cameraUp = vec4(camera->up, 0.0f), - .cameraRight = vec4(camera->right, 0.0f), - .time = Clock::Get(), - .deltaTime = Clock::GetDelta() + auto globalUniforms = GlobalUniforms{ + .vMatrix = camera->viewMatrix, + .pMatrix = camera->projectionMatrix, + .ivMatrix = camera->invViewMatrix, + .ipMatrix = camera->invProjectionMatrix, + .pvMatrixLast = camera->GetLastJitteredMatrix(), + .pvMatrixCurrent = camera->projectionMatrix * camera->viewMatrix, + .jitterLast = lastJitter, + .jitterCurrent = jitter, + .cameraLocation = vec4(camera->location, 0.0f), + .cameraDirection = vec4(camera->direction, 0.0f), + .cameraUp = vec4(camera->up, 0.0f), + .cameraRight = vec4(camera->right, 0.0f), + .planetCenter = vec4(scene->sky.planetCenter, 0.0f), + .planetRadius = scene->sky.planetRadius, + .time = Clock::Get(), + .deltaTime = Clock::GetDelta(), + .frameCount = frameCount }; + lastJitter = jitter; + pathTraceGlobalUniformBuffer->SetData(&globalUniforms, 0, sizeof(GlobalUniforms)); - commandList->BindBuffer(pathTraceGlobalUniformBuffer, 0, 0); + + std::vector> images; + std::vector> blasBuffers, triangleBuffers, bvhTriangleBuffers, triangleOffsetBuffers; + PrepareBindlessData(scene, images, blasBuffers, triangleBuffers, bvhTriangleBuffers, triangleOffsetBuffers); + + commandList->BindBuffer(pathTraceGlobalUniformBuffer, 0, 3); + commandList->BindImage(dfgPreintegrationTexture.image, dfgPreintegrationTexture.sampler, 1, 12); + commandList->BindSampler(globalSampler, 1, 13); + commandList->BindBuffers(triangleBuffers, 0, 1); + commandList->BindSampledImages(images, 0, 4); + + if (device->support.hardwareRayTracing) { + commandList->BindBuffers(triangleOffsetBuffers, 0, 2); + } + else { + commandList->BindBuffers(blasBuffers, 0, 0); + commandList->BindBuffers(bvhTriangleBuffers, 0, 2); + } Graphics::Profiler::EndQuery(); + // No probe filtering required + if (scene->sky.atmosphere) { + atmosphereRenderer.Render(&scene->sky.atmosphere->probe, scene, commandList); + } + pathTracingRenderer.Render(viewport, target, ivec2(1, 1), camera, scene, commandList); - Graphics::Profiler::BeginQuery("Post processing"); + if (pathTracingRenderer.realTime) { + taaRenderer.Render(viewport, target, camera, scene, commandList); - commandList->BeginRenderPass(device->swapChain, true); - - textureRenderer.RenderTexture2D(commandList, viewport, &target->texture, - 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0f, 1.0f, false, true); + postProcessRenderer.Render(viewport, target, camera, scene, commandList); + } + else { + Graphics::Profiler::BeginQuery("Post processing"); - commandList->EndRenderPass(); + if (device->swapChain->isComplete) { + commandList->BeginRenderPass(device->swapChain, true); + + textureRenderer.RenderTexture2D(commandList, viewport, &target->texture, + 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0f, 1.0f, false, true); + + commandList->EndRenderPass(); + } + + Graphics::Profiler::EndQuery(); + } + - Graphics::Profiler::EndQuery(); Graphics::Profiler::EndThread(); commandList->EndCommands(); @@ -785,6 +859,51 @@ namespace Atlas { } + void MainRenderer::CreateGlobalDescriptorSetLayout() { + + if (!device->support.bindless) + return; + + auto samplerDesc = Graphics::SamplerDesc { + .filter = VK_FILTER_LINEAR, + .mode = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .maxLod = 12, + .anisotropicFiltering = true + }; + globalSampler = device->CreateSampler(samplerDesc); + + auto layoutDesc = Graphics::DescriptorSetLayoutDesc{ + .bindings = { + { + .bindingIdx = 0, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 8192, .bindless = true + }, + { + .bindingIdx = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 8192, .bindless = true + }, + { + .bindingIdx = 2, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 8192, .bindless = true + }, + { + .bindingIdx = 3, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 8192, .bindless = true + }, + { + .bindingIdx = 4, .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .descriptorCount = 16384, .bindless = true + } + }, + .bindingCount = 5 + }; + globalDescriptorSetLayout = device->CreateDescriptorSetLayout(layoutDesc); + + PipelineManager::OverrideDescriptorSetLayout(globalDescriptorSetLayout, 0); + + } + void MainRenderer::SetUniforms(Scene::Scene *scene, Camera *camera) { auto globalUniforms = GlobalUniforms { @@ -843,7 +962,6 @@ namespace Atlas { } auto meshes = scene->GetMeshes(); - for (auto& mesh : meshes) { if (!mesh.IsLoaded() || !mesh->impostor) continue; @@ -970,6 +1088,46 @@ namespace Atlas { } + void MainRenderer::PrepareBindlessData(Scene::Scene* scene, std::vector>& images, + std::vector>& blasBuffers, std::vector>& triangleBuffers, + std::vector>& bvhTriangleBuffers, std::vector>& triangleOffsetBuffers) { + + if (!device->support.bindless) + return; + + blasBuffers.resize(scene->meshIdToBindlessIdx.size()); + triangleBuffers.resize(scene->meshIdToBindlessIdx.size()); + bvhTriangleBuffers.resize(scene->meshIdToBindlessIdx.size()); + triangleOffsetBuffers.resize(scene->meshIdToBindlessIdx.size()); + + for (const auto& [meshId, idx] : scene->meshIdToBindlessIdx) { + if (!scene->rootMeshMap.contains(meshId)) continue; + + const auto& mesh = scene->rootMeshMap[meshId].mesh; + + auto blasBuffer = mesh->blasNodeBuffer.Get(); + auto triangleBuffer = mesh->triangleBuffer.Get(); + auto bvhTriangleBuffer = mesh->bvhTriangleBuffer.Get(); + auto triangleOffsetBuffer = mesh->triangleOffsetBuffer.Get(); + + AE_ASSERT(triangleBuffer != nullptr); + + blasBuffers[idx] = blasBuffer; + triangleBuffers[idx] = triangleBuffer; + bvhTriangleBuffers[idx] = bvhTriangleBuffer; + triangleOffsetBuffers[idx] = triangleOffsetBuffer; + } + + images.resize(scene->textureToBindlessIdx.size()); + + for (const auto& [texture, idx] : scene->textureToBindlessIdx) { + + images[idx] = texture->image; + + } + + } + void MainRenderer::FillRenderList(Scene::Scene *scene, Atlas::Camera *camera) { renderList.NewFrame(); @@ -1030,7 +1188,7 @@ namespace Atlas { uint32_t groupCount = res / 8; - commandList->BindImage(dfgPreintegrationTexture.image, 0, 0); + commandList->BindImage(dfgPreintegrationTexture.image, 3, 0); commandList->Dispatch(groupCount, groupCount, 1); barrier = Graphics::ImageBarrier(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, diff --git a/src/engine/renderer/MainRenderer.h b/src/engine/renderer/MainRenderer.h index bc4ad2857..9c6514f59 100644 --- a/src/engine/renderer/MainRenderer.h +++ b/src/engine/renderer/MainRenderer.h @@ -22,6 +22,7 @@ #include "PostProcessRenderer.h" #include "GBufferDownscaleRenderer.h" #include "TextRenderer.h" +#include "GIRenderer.h" #include "DDGIRenderer.h" #include "AORenderer.h" #include "RTReflectionRenderer.h" @@ -193,11 +194,17 @@ namespace Atlas { int32_t volumeEnabled; }; + void CreateGlobalDescriptorSetLayout(); + void SetUniforms(Scene::Scene* scene, Camera* camera); void PrepareMaterials(Scene::Scene* scene, std::vector& materials, std::unordered_map& materialMap); + void PrepareBindlessData(Scene::Scene* scene, std::vector>& images, + std::vector>& blasBuffers, std::vector>& triangleBuffers, + std::vector>& bvhTriangleBuffers, std::vector>& triangleOffsetBuffers); + void FillRenderList(Scene::Scene* scene, Camera* camera); void PreintegrateBRDF(); @@ -209,6 +216,8 @@ namespace Atlas { Ref globalUniformBuffer; Ref pathTraceGlobalUniformBuffer; Ref ddgiUniformBuffer; + Ref globalDescriptorSetLayout; + Ref globalSampler; Buffer::VertexArray vertexArray; Buffer::VertexArray cubeVertexArray; @@ -228,6 +237,7 @@ namespace Atlas { SkyboxRenderer skyboxRenderer; PostProcessRenderer postProcessRenderer; GBufferDownscaleRenderer downscaleRenderer; + GIRenderer giRenderer; DDGIRenderer ddgiRenderer; AORenderer aoRenderer; RTReflectionRenderer rtrRenderer; diff --git a/src/engine/renderer/OceanRenderer.cpp b/src/engine/renderer/OceanRenderer.cpp index ad806dcbe..f2361c8e7 100644 --- a/src/engine/renderer/OceanRenderer.cpp +++ b/src/engine/renderer/OceanRenderer.cpp @@ -34,6 +34,10 @@ namespace Atlas { }; shadowSampler = device->CreateSampler(samplerDesc); + std::vector dummyData = { 0 }; + dummyTexture = Texture::Texture2D(1, 1, VK_FORMAT_R8_UNORM); + dummyTexture.SetData(dummyData); + } void OceanRenderer::Render(Viewport* viewport, RenderTarget* target, Camera* camera, @@ -193,8 +197,8 @@ namespace Atlas { commandList->CopyImage(depthImage, depthTexture.image); imageBarriers = { - {colorImage, VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT}, - {depthImage, VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT}, + {colorImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {depthImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {refractionTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {depthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; @@ -255,7 +259,12 @@ namespace Atlas { ocean->simulation.displacementMap.Bind(commandList, 3, 0); ocean->simulation.normalMap.Bind(commandList, 3, 1); - ocean->foamTexture.Bind(commandList, 3, 2); + if (ocean->foamTexture.IsValid()) { + ocean->foamTexture.Bind(commandList, 3, 2); + } + else { + dummyTexture.Bind(commandList, 3, 2); + } if (scene->sky.GetProbe()) { scene->sky.GetProbe()->cubemap.Bind(commandList, 3, 3); @@ -296,6 +305,9 @@ namespace Atlas { if (ocean->rippleTexture.IsValid()) { ocean->rippleTexture.Bind(commandList, 3, 10); } + else { + dummyTexture.Bind(commandList, 3, 10); + } uniformBuffer.SetData(&uniforms, 0); uniformBuffer.Bind(commandList, 3, 11); diff --git a/src/engine/renderer/OceanRenderer.h b/src/engine/renderer/OceanRenderer.h index 27c5ca54d..91e446b23 100644 --- a/src/engine/renderer/OceanRenderer.h +++ b/src/engine/renderer/OceanRenderer.h @@ -83,6 +83,7 @@ namespace Atlas { Texture::Texture2D refractionTexture; Texture::Texture2D depthTexture; + Texture::Texture2D dummyTexture; Buffer::UniformBuffer uniformBuffer; Buffer::UniformBuffer depthUniformBuffer; diff --git a/src/engine/renderer/PathTracingRenderer.cpp b/src/engine/renderer/PathTracingRenderer.cpp index cad42956b..20de4991a 100644 --- a/src/engine/renderer/PathTracingRenderer.cpp +++ b/src/engine/renderer/PathTracingRenderer.cpp @@ -36,28 +36,42 @@ namespace Atlas { void PathTracingRenderer::Render(Viewport* viewport, PathTracerRenderTarget* renderTarget, ivec2 imageSubdivisions, Camera* camera, Scene::Scene* scene, Graphics::CommandList* commandList) { - if (!scene->IsRtDataValid()) return; + if (!scene->IsRtDataValid()) + return; Graphics::Profiler::BeginQuery("Path tracing"); auto width = renderTarget->GetWidth(); auto height = renderTarget->GetHeight(); + auto rayCount = realTime ? 2 * width * height * realTimeSamplesPerFrame : 2 * width * height; + if (glm::distance(camera->GetLocation(), cameraLocation) > 1e-3f || glm::distance(camera->rotation, cameraRotation) > 1e-3f || - helper.GetRayBuffer()->GetElementCount() != 2 * width * height) { + helper.GetRayBuffer()->GetElementCount() != rayCount) { cameraLocation = camera->GetLocation(); cameraRotation = camera->rotation; sampleCount = 0; imageOffset = ivec2(0); - helper.SetRayBufferSize(width * height); + // Helper automatically double buffers, no need for 2 * rayCount + helper.SetRayBufferSize(rayCount / 2); + } + + if (realTime) { + imageSubdivisions = ivec2(1); + sampleCount = 0; + frameCount++; + renderTarget->Swap(); } + rayGenPipelineConfig.ManageMacro("REALTIME", realTime); + rayHitPipelineConfig.ManageMacro("REALTIME", realTime); + // Check if the scene has changed. A change might happen when an actor has been updated, // new actors have been added or old actors have been removed. If this happens we update // the data structures. - helper.SetScene(scene, 1, true); + helper.SetScene(scene, 1, sampleEmissives); helper.UpdateLights(); ivec2 resolution = ivec2(width, height); @@ -68,6 +82,12 @@ namespace Atlas { rayHitUniformBuffer.SetSize(bounces + 1); } + auto samplesPerFrame = realTime ? realTimeSamplesPerFrame : 1; +#ifdef AE_OS_MACOS + // MoltenVK and Metal don't seem to support atomic add, so limit to 1 sample per frame + samplesPerFrame = 1; +#endif + for (int32_t i = 0; i <= bounces; i++) { RayHitUniforms uniforms; uniforms.maxBounces = bounces; @@ -80,30 +100,59 @@ namespace Atlas { uniforms.exposure = camera->exposure; - rayHitUniformBuffer.SetData(&uniforms, i); - } + uniforms.samplesPerFrame = samplesPerFrame; + uniforms.maxRadiance = maxRadiance; - // Bind texture only for writing - commandList->ImageMemoryBarrier(renderTarget->texture.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_WRITE_BIT); - commandList->BindImage(renderTarget->texture.image, 3, 1); - if (sampleCount % 2 == 0) { - commandList->ImageMemoryBarrier(renderTarget->accumTexture0.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_READ_BIT); - commandList->ImageMemoryBarrier(renderTarget->accumTexture1.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_WRITE_BIT); - commandList->BindImage(renderTarget->accumTexture0.image, 3, 2); - commandList->BindImage(renderTarget->accumTexture1.image, 3, 3); + uniforms.frameCount = frameCount; + + rayHitUniformBuffer.SetData(&uniforms, i); + } + + std::vector imageBarriers; + std::vector bufferBarriers; + + imageBarriers.push_back({ renderTarget->texture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); + imageBarriers.push_back({ renderTarget->radianceTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); + imageBarriers.push_back({ renderTarget->historyRadianceTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT }); + + if (!realTime) { + commandList->BindImage(renderTarget->texture.image, 3, 1); + commandList->BindImage(renderTarget->radianceTexture.image, 3, 3); + commandList->BindImage(renderTarget->historyRadianceTexture.image, 3, 2); } else { - commandList->ImageMemoryBarrier(renderTarget->accumTexture0.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_WRITE_BIT); - commandList->ImageMemoryBarrier(renderTarget->accumTexture1.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_READ_BIT); - commandList->BindImage(renderTarget->accumTexture0.image, 3, 3); - commandList->BindImage(renderTarget->accumTexture1.image, 3, 2); + imageBarriers.push_back({ renderTarget->frameAccumTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); + imageBarriers.push_back({ renderTarget->radianceTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); + imageBarriers.push_back({ renderTarget->velocityTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); + imageBarriers.push_back({ renderTarget->depthTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); + imageBarriers.push_back({ renderTarget->normalTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); + imageBarriers.push_back({ renderTarget->materialIdxTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); + imageBarriers.push_back({ renderTarget->albedoTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); + + commandList->BindImage(renderTarget->frameAccumTexture.image, 3, 1); + commandList->BindImage(renderTarget->radianceTexture.image, 3, 3); + + renderTarget->historyRadianceTexture.Bind(commandList, 3, 2); + + commandList->BindImage(renderTarget->velocityTexture.image, 3, 5); + commandList->BindImage(renderTarget->depthTexture.image, 3, 6); + commandList->BindImage(renderTarget->normalTexture.image, 3, 7); + commandList->BindImage(renderTarget->materialIdxTexture.image, 3, 8); + commandList->BindImage(renderTarget->albedoTexture.image, 3, 9); } + commandList->PipelineBarrier(imageBarriers, bufferBarriers); + auto tileResolution = resolution / imageSubdivisions; auto groupCount = tileResolution / 8; @@ -113,7 +162,7 @@ namespace Atlas { Graphics::Profiler::BeginQuery("Ray generation"); helper.DispatchRayGen(commandList, PipelineManager::GetPipeline(rayGenPipelineConfig), - ivec3(groupCount.x, groupCount.y, 1), false, + ivec3(groupCount.x, groupCount.y, samplesPerFrame), false, [=]() { auto corners = camera->GetFrustumCorners(camera->nearPlane, camera->farPlane); @@ -122,7 +171,7 @@ namespace Atlas { uniforms.right = vec4(corners[5] - corners[4], 1.0f); uniforms.bottom = vec4(corners[6] - corners[4], 1.0f); - uniforms.sampleCount = sampleCount; + uniforms.sampleCount = realTime ? frameCount : sampleCount; uniforms.pixelOffset = ivec2(renderTarget->GetWidth(), renderTarget->GetHeight()) / imageSubdivisions * imageOffset; @@ -137,6 +186,11 @@ namespace Atlas { for (int32_t i = 0; i <= bounces; i++) { + commandList->ImageMemoryBarrier(renderTarget->frameAccumTexture.image, VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + commandList->ImageMemoryBarrier(renderTarget->velocityTexture.image, VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + Graphics::Profiler::EndAndBeginQuery("Bounce " + std::to_string(i)); helper.DispatchHitClosest(commandList, PipelineManager::GetPipeline(rayHitPipelineConfig), false, true, @@ -149,6 +203,77 @@ namespace Atlas { Graphics::Profiler::EndQuery(); + if (realTime) { + + imageBarriers.clear(); + + imageBarriers.push_back({ renderTarget->frameAccumTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT }); + imageBarriers.push_back({ renderTarget->velocityTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); + imageBarriers.push_back({ renderTarget->depthTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); + imageBarriers.push_back({ renderTarget->normalTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); + imageBarriers.push_back({ renderTarget->materialIdxTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); + imageBarriers.push_back({ renderTarget->albedoTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); + imageBarriers.push_back({ renderTarget->historyDepthTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); + imageBarriers.push_back({ renderTarget->historyNormalTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); + imageBarriers.push_back({ renderTarget->historyMaterialIdxTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); + + commandList->PipelineBarrier(imageBarriers, bufferBarriers); + + commandList->BindImage(renderTarget->texture.image, 3, 0); + + renderTarget->velocityTexture.Bind(commandList, 3, 4); + renderTarget->depthTexture.Bind(commandList, 3, 5); + renderTarget->normalTexture.Bind(commandList, 3, 6); + renderTarget->materialIdxTexture.Bind(commandList, 3, 7); + renderTarget->historyDepthTexture.Bind(commandList, 3, 9); + renderTarget->historyNormalTexture.Bind(commandList, 3, 10); + renderTarget->historyMaterialIdxTexture.Bind(commandList, 3, 11); + + struct alignas(16) PushConstants { + float historyClipMax; + float currentClipFactor; + float maxHistoryLength; + float exposure; + int samplesPerFrame; + float maxRadiance; + }; + + Graphics::Profiler::BeginQuery("Temporal"); + + PushConstants constants = { + .historyClipMax = historyClipMax, + .currentClipFactor = currentClipFactor, + .maxHistoryLength = float(historyLengthMax), + .exposure = camera->exposure, + .samplesPerFrame = samplesPerFrame, + .maxRadiance = maxRadiance + }; + commandList->PushConstants("constants", &constants); + + auto pipelineConfig = PipelineConfig("pathtracer/temporal.csh"); + auto pipeline = PipelineManager::GetPipeline(pipelineConfig); + commandList->BindPipeline(pipeline); + + groupCount = resolution / 16; + + groupCount.x += ((groupCount.x * 16 == resolution.x) ? 0 : 1); + groupCount.y += ((groupCount.y * 16 == resolution.y) ? 0 : 1); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + Graphics::Profiler::EndQuery(); + + } + imageOffset.x++; if (imageOffset.x == imageSubdivisions.x) { @@ -159,6 +284,10 @@ namespace Atlas { if (imageOffset.y == imageSubdivisions.y) { imageOffset.y = 0; sampleCount++; + + // We don't want to swap already here when rendering in realtime + if (!realTime) + renderTarget->Swap(); } commandList->ImageTransition(renderTarget->texture.image, diff --git a/src/engine/renderer/PathTracingRenderer.h b/src/engine/renderer/PathTracingRenderer.h index 2589cf057..fedad6d1e 100644 --- a/src/engine/renderer/PathTracingRenderer.h +++ b/src/engine/renderer/PathTracingRenderer.h @@ -21,8 +21,31 @@ namespace Atlas { texture = Texture::Texture2D(width, height, VK_FORMAT_R8G8B8A8_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); - accumTexture0 = Texture::Texture2D(width, height, VK_FORMAT_R32G32B32A32_SFLOAT); - accumTexture1 = Texture::Texture2D(width, height, VK_FORMAT_R32G32B32A32_SFLOAT); + frameAccumTexture = Texture::Texture2DArray(width, height, 3, VK_FORMAT_R32_UINT); + + albedoTexture = Texture::Texture2D(width, height, VK_FORMAT_R8G8B8A8_UNORM); + + velocityTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16_SFLOAT); + historyVelocityTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16_SFLOAT); + + depthTexture = Texture::Texture2D(width, height, VK_FORMAT_R32_SFLOAT); + historyDepthTexture = Texture::Texture2D(width, height, VK_FORMAT_R32_SFLOAT); + + normalTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16_SFLOAT); + historyNormalTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16_SFLOAT); + + materialIdxTexture = Texture::Texture2D(width, height, VK_FORMAT_R16_UINT); + historyMaterialIdxTexture = Texture::Texture2D(width, height, VK_FORMAT_R16_UINT); + + radianceTexture = Texture::Texture2D(width, height, VK_FORMAT_R32G32B32A32_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + historyRadianceTexture = Texture::Texture2D(width, height, VK_FORMAT_R32G32B32A32_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + + postProcessTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16B16A16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + historyPostProcessTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16B16A16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); } void Resize(int32_t width, int32_t height) { @@ -31,8 +54,38 @@ namespace Atlas { texture.Resize(width, height); - accumTexture0.Resize(width, height); - accumTexture1.Resize(width, height); + frameAccumTexture.Resize(width, height, 3); + + albedoTexture.Resize(width, height); + + velocityTexture.Resize(width, height); + historyVelocityTexture.Resize(width, height); + + depthTexture.Resize(width, height); + historyDepthTexture.Resize(width, height); + + normalTexture.Resize(width, height); + historyNormalTexture.Resize(width, height); + + materialIdxTexture.Resize(width, height); + historyMaterialIdxTexture.Resize(width, height); + + radianceTexture.Resize(width, height); + historyRadianceTexture.Resize(width, height); + + postProcessTexture.Resize(width, height); + historyPostProcessTexture.Resize(width, height); + } + + void Swap() { + + std::swap(radianceTexture, historyRadianceTexture); + std::swap(velocityTexture, historyVelocityTexture); + std::swap(depthTexture, historyDepthTexture); + std::swap(normalTexture, historyNormalTexture); + std::swap(materialIdxTexture, historyMaterialIdxTexture); + std::swap(postProcessTexture, historyPostProcessTexture); + } int32_t GetWidth() const { return width; } @@ -40,8 +93,24 @@ namespace Atlas { Texture::Texture2D texture; - Texture::Texture2D accumTexture0; - Texture::Texture2D accumTexture1; + Texture::Texture2DArray frameAccumTexture; + + Texture::Texture2D albedoTexture; + Texture::Texture2D velocityTexture; + Texture::Texture2D depthTexture; + Texture::Texture2D normalTexture; + Texture::Texture2D materialIdxTexture; + + Texture::Texture2D historyVelocityTexture; + Texture::Texture2D historyDepthTexture; + Texture::Texture2D historyNormalTexture; + Texture::Texture2D historyMaterialIdxTexture; + + Texture::Texture2D radianceTexture; + Texture::Texture2D historyRadianceTexture; + + Texture::Texture2D postProcessTexture; + Texture::Texture2D historyPostProcessTexture; int32_t sampleCount = 0; @@ -75,6 +144,17 @@ namespace Atlas { int32_t bvhDepth = 0; int32_t lightCount = 512; + bool realTime = true; + int32_t realTimeSamplesPerFrame = 4; + + int32_t historyLengthMax = 32; + float historyClipMax = 0.1f; + float currentClipFactor = 2.0f; + + float maxRadiance = 65535.0f; + + bool sampleEmissives = false; + private: struct alignas(16) RayGenUniforms { vec4 origin; @@ -93,6 +173,9 @@ namespace Atlas { int32_t bounceCount; float seed; float exposure; + int32_t samplesPerFrame; + float maxRadiance; + int32_t frameCount; }; Helper::RayTracingHelper helper; @@ -109,6 +192,8 @@ namespace Atlas { Buffer::UniformBuffer rayGenUniformBuffer; Buffer::UniformBuffer rayHitUniformBuffer; + size_t frameCount = 0; + }; } diff --git a/src/engine/renderer/PostProcessRenderer.cpp b/src/engine/renderer/PostProcessRenderer.cpp index b62a62fb3..bfdc613d4 100644 --- a/src/engine/renderer/PostProcessRenderer.cpp +++ b/src/engine/renderer/PostProcessRenderer.cpp @@ -114,6 +114,99 @@ namespace Atlas { } + void PostProcessRenderer::Render(Viewport* viewport, PathTracerRenderTarget* target, Camera* camera, + Scene::Scene* scene, Graphics::CommandList* commandList) { + + Graphics::Profiler::BeginQuery("Postprocessing"); + + auto& postProcessing = scene->postProcessing; + + const auto& chromaticAberration = postProcessing.chromaticAberration; + const auto& vignette = postProcessing.vignette; + const auto& taa = postProcessing.taa; + auto& sharpen = postProcessing.sharpen; + + ivec2 resolution = ivec2(target->GetWidth(), target->GetHeight()); + + if (sharpen.enable) { + Graphics::Profiler::BeginQuery("Sharpen"); + + auto pipeline = PipelineManager::GetPipeline(sharpenPipelineConfig); + + commandList->BindPipeline(pipeline); + + ivec2 groupCount = resolution / 8; + groupCount.x += ((groupCount.x * 8 == resolution.x) ? 0 : 1); + groupCount.y += ((groupCount.y * 8 == resolution.y) ? 0 : 1); + + auto& image = target->historyPostProcessTexture.image; + + commandList->ImageMemoryBarrier(image, VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + commandList->BindImage(image, 3, 0); + + if (taa.enable) { + target->postProcessTexture.Bind(commandList, 3, 1); + } + else { + target->radianceTexture.Bind(commandList, 3, 1); + } + + commandList->PushConstants("constants", &sharpen.factor); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + commandList->ImageMemoryBarrier(image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + + Graphics::Profiler::EndQuery(); + } + + { + Graphics::Profiler::BeginQuery("Main"); + + // We can't return here because of the queries + if (device->swapChain->isComplete) { + commandList->BeginRenderPass(device->swapChain, true); + + auto pipelineConfig = GetMainPipelineConfig(); + pipelineConfig.ManageMacro("FILMIC_TONEMAPPING", postProcessing.filmicTonemapping); + pipelineConfig.ManageMacro("VIGNETTE", postProcessing.vignette.enable); + pipelineConfig.ManageMacro("CHROMATIC_ABERRATION", postProcessing.chromaticAberration.enable); + pipelineConfig.ManageMacro("FILM_GRAIN", postProcessing.filmGrain.enable); + + auto pipeline = PipelineManager::GetPipeline(pipelineConfig); + commandList->BindPipeline(pipeline); + + SetUniforms(camera, scene); + + if (sharpen.enable) { + target->historyPostProcessTexture.Bind(commandList, 3, 0); + } + else { + if (taa.enable) { + target->postProcessTexture.Bind(commandList, 3, 0); + } + else { + target->radianceTexture.Bind(commandList, 3, 0); + } + } + commandList->BindBuffer(uniformBuffer, 3, 4); + + commandList->Draw(6, 1, 0, 0); + + commandList->EndRenderPass(); + } + + Graphics::Profiler::EndQuery(); + } + + Graphics::Profiler::EndQuery(); + + } + void PostProcessRenderer::SetUniforms(Camera* camera, Scene::Scene* scene) { const auto& postProcessing = scene->postProcessing; @@ -126,6 +219,7 @@ namespace Atlas { .exposure = camera->exposure, .whitePoint = postProcessing.whitePoint, .saturation = postProcessing.saturation, + .contrast = postProcessing.contrast }; if (chromaticAberration.enable) { diff --git a/src/engine/renderer/PostProcessRenderer.h b/src/engine/renderer/PostProcessRenderer.h index 5e5fd117a..6cdd6f233 100644 --- a/src/engine/renderer/PostProcessRenderer.h +++ b/src/engine/renderer/PostProcessRenderer.h @@ -2,6 +2,7 @@ #include "../System.h" #include "Renderer.h" +#include "PathTracingRenderer.h" namespace Atlas { @@ -17,11 +18,15 @@ namespace Atlas { void Render(Viewport* viewport, RenderTarget* target, Camera* camera, Scene::Scene* scene, Graphics::CommandList* commandList); + void Render(Viewport* viewport, PathTracerRenderTarget* target, Camera* camera, + Scene::Scene* scene, Graphics::CommandList* commandList); + private: struct alignas(16) Uniforms { float exposure; float whitePoint; float saturation; + float contrast; float filmGrainStrength; int32_t bloomPasses; float aberrationStrength; diff --git a/src/engine/renderer/RTReflectionRenderer.cpp b/src/engine/renderer/RTReflectionRenderer.cpp index 994d64cf6..01422be3f 100644 --- a/src/engine/renderer/RTReflectionRenderer.cpp +++ b/src/engine/renderer/RTReflectionRenderer.cpp @@ -76,7 +76,6 @@ namespace Atlas { auto offsetTexture = downsampledRT->offsetTexture; auto velocityTexture = downsampledRT->velocityTexture; auto materialIdxTexture = downsampledRT->materialIdxTexture; - auto randomTexture = &scene->ao->noiseTexture; auto historyDepthTexture = downsampledHistoryRT->depthTexture; auto historyMaterialIdxTexture = downsampledHistoryRT->materialIdxTexture; diff --git a/src/engine/renderer/TemporalAARenderer.cpp b/src/engine/renderer/TemporalAARenderer.cpp index 754d2d052..1d9bf87cd 100644 --- a/src/engine/renderer/TemporalAARenderer.cpp +++ b/src/engine/renderer/TemporalAARenderer.cpp @@ -21,6 +21,8 @@ namespace Atlas { Graphics::Profiler::BeginQuery("TAA"); + pipelineConfig.ManageMacro("PATHTRACE", false); + auto pipeline = PipelineManager::GetPipeline(pipelineConfig); commandList->BindPipeline(pipeline); @@ -33,12 +35,12 @@ namespace Atlas { auto targetData = target->GetData(FULL_RES); - auto lastHistory = target->GetLastHistory(); - auto history = target->GetHistory(); - auto lastVelocity = target->GetLastVelocity(); - auto velocity = target->GetVelocity(); - auto depth = targetData->depthTexture; - auto stencil = targetData->stencilTexture; + const auto lastHistory = target->GetLastHistory(); + const auto history = target->GetHistory(); + const auto lastVelocity = target->GetLastVelocity(); + auto const velocity = target->GetVelocity(); + const auto depth = targetData->depthTexture; + const auto stencil = targetData->stencilTexture; std::vector bufferBarriers; std::vector imageBarriers; @@ -72,6 +74,70 @@ namespace Atlas { } + void TemporalAARenderer::Render(Viewport* viewport, PathTracerRenderTarget* target, Camera* camera, + Scene::Scene* scene, Graphics::CommandList* commandList) { + + pipelineConfig.ManageMacro("PATHTRACE", true); + + const auto output = &target->postProcessTexture; + const auto history = &target->historyPostProcessTexture; + const auto lastVelocity = &target->historyVelocityTexture; + auto const velocity = &target->velocityTexture; + auto const depth = &target->depthTexture; + + std::vector bufferBarriers; + std::vector imageBarriers; + imageBarriers.push_back({ output->image,VK_IMAGE_LAYOUT_GENERAL,VK_ACCESS_SHADER_WRITE_BIT }); + imageBarriers.push_back({ history->image,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,VK_ACCESS_SHADER_READ_BIT }); + imageBarriers.push_back({ lastVelocity->image,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,VK_ACCESS_SHADER_READ_BIT }); + commandList->PipelineBarrier(imageBarriers, bufferBarriers); + + PushConstants constants; + Render(output, &target->radianceTexture, history, velocity, lastVelocity, depth, + nullptr, constants, commandList); + + commandList->ImageMemoryBarrier(output->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + + } + + void TemporalAARenderer::Render(const Texture::Texture2D* outputTexture, const Texture::Texture2D* currentTexture, const Texture::Texture2D* historyTexture, + const Texture::Texture2D* velocityTexture, const Texture::Texture2D* historyVelocityTexture, const Texture::Texture2D* depthTexture, + const Texture::Texture2D* stencilTexture, PushConstants& constants, Graphics::CommandList* commandList) { + + Graphics::Profiler::BeginQuery("TAA"); + + auto pipeline = PipelineManager::GetPipeline(pipelineConfig); + commandList->BindPipeline(pipeline); + + auto res = ivec2(outputTexture->width, outputTexture->height); + + const int32_t groupSize = 8; + ivec2 groupCount = res / groupSize; + groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); + groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); + + commandList->BindImage(outputTexture->image, 3, 0); + commandList->BindImage(historyTexture->image, historyTexture->sampler, 3, 1); + commandList->BindImage(currentTexture->image, currentTexture->sampler, 3, 2); + commandList->BindImage(velocityTexture->image, velocityTexture->sampler, 3, 3); + commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 4); + commandList->BindImage(historyVelocityTexture->image, historyVelocityTexture->sampler, 3, 5); + + if (stencilTexture != nullptr) + commandList->BindImage(stencilTexture->image, stencilTexture->sampler, 3, 6); + + constants.resolution = vec2((float)outputTexture->width, (float)outputTexture->height); + constants.invResolution = 1.0f / vec2((float)outputTexture->width, (float)outputTexture->height); + + commandList->PushConstants("constants", &constants); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + Graphics::Profiler::EndQuery(); + + } + } } \ No newline at end of file diff --git a/src/engine/renderer/TemporalAARenderer.h b/src/engine/renderer/TemporalAARenderer.h index ca6997712..680878778 100644 --- a/src/engine/renderer/TemporalAARenderer.h +++ b/src/engine/renderer/TemporalAARenderer.h @@ -2,6 +2,7 @@ #include "../System.h" #include "Renderer.h" +#include "PathTracingRenderer.h" namespace Atlas { @@ -17,6 +18,9 @@ namespace Atlas { void Render(Viewport* viewport, RenderTarget* target, Camera* camera, Scene::Scene* scene, Graphics::CommandList* commandList); + void Render(Viewport* viewport, PathTracerRenderTarget* target, Camera* camera, + Scene::Scene* scene, Graphics::CommandList* commandList); + private: struct PushConstants { vec2 resolution; @@ -24,9 +28,11 @@ namespace Atlas { vec2 jitter; }; - PipelineConfig pipelineConfig; + void Render(const Texture::Texture2D* outputTexture, const Texture::Texture2D* currentTexture, const Texture::Texture2D* historyTexture, + const Texture::Texture2D* velocityTexture, const Texture::Texture2D* historyVelocityTexture, const Texture::Texture2D* depthTexture, + const Texture::Texture2D* stencilTexture, PushConstants& constants, Graphics::CommandList* commandList); - mat4 pvMatrixPrev = mat4(1.0f); + PipelineConfig pipelineConfig; }; diff --git a/src/engine/renderer/helper/RayTracingHelper.cpp b/src/engine/renderer/helper/RayTracingHelper.cpp index 654cbac5f..02506f2ff 100644 --- a/src/engine/renderer/helper/RayTracingHelper.cpp +++ b/src/engine/renderer/helper/RayTracingHelper.cpp @@ -119,36 +119,6 @@ namespace Atlas { // Bind textures and buffers { - if (rtData.baseColorTextureAtlas.slices.size()) - commandList->BindImage(rtData.baseColorTextureAtlas.textureArray.image, - rtData.baseColorTextureAtlas.textureArray.sampler, 2, 0); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 0); - if (rtData.opacityTextureAtlas.slices.size()) - commandList->BindImage(rtData.opacityTextureAtlas.textureArray.image, - rtData.opacityTextureAtlas.textureArray.sampler, 2, 1); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 1); - if (rtData.normalTextureAtlas.slices.size()) - commandList->BindImage(rtData.normalTextureAtlas.textureArray.image, - rtData.normalTextureAtlas.textureArray.sampler, 2, 2); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 2); - if (rtData.roughnessTextureAtlas.slices.size()) - commandList->BindImage(rtData.roughnessTextureAtlas.textureArray.image, - rtData.roughnessTextureAtlas.textureArray.sampler, 2, 3); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 3); - if (rtData.metalnessTextureAtlas.slices.size()) - commandList->BindImage(rtData.metalnessTextureAtlas.textureArray.image, - rtData.metalnessTextureAtlas.textureArray.sampler, 2, 4); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 4); - if (rtData.aoTextureAtlas.slices.size()) - commandList->BindImage(rtData.aoTextureAtlas.textureArray.image, - rtData.aoTextureAtlas.textureArray.sampler, 2, 5); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 5); if (scene->sky.GetProbe()) commandList->BindImage(scene->sky.GetProbe()->cubemap.image, scene->sky.GetProbe()->cubemap.sampler, 2, 6); @@ -156,17 +126,14 @@ namespace Atlas { commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 6); rtData.materialBuffer.Bind(commandList, 2, 7); - rtData.triangleBuffer.Bind(commandList, 2, 8); rtData.bvhInstanceBuffer.Bind(commandList, 2, 21); + rtData.lastMatricesBuffer.Bind(commandList, 2, 27); lightBuffer.Bind(commandList, 2, 11); if (rtData.hardwareRayTracing) { commandList->BindTLAS(rtData.tlas, 2, 23); - rtData.geometryTriangleOffsetBuffer.Bind(commandList, 2, 22); } else { - rtData.bvhTriangleBuffer.Bind(commandList, 2, 9); - rtData.blasNodeBuffer.Bind(commandList, 2, 10); rtData.tlasNodeBuffer.Bind(commandList, 2, 22); } } @@ -272,36 +239,6 @@ namespace Atlas { // Bind textures and buffers { - if (rtData.baseColorTextureAtlas.slices.size()) - commandList->BindImage(rtData.baseColorTextureAtlas.textureArray.image, - rtData.baseColorTextureAtlas.textureArray.sampler, 2, 0); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 0); - if (rtData.opacityTextureAtlas.slices.size()) - commandList->BindImage(rtData.opacityTextureAtlas.textureArray.image, - rtData.opacityTextureAtlas.textureArray.sampler, 2, 1); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 1); - if (rtData.normalTextureAtlas.slices.size()) - commandList->BindImage(rtData.normalTextureAtlas.textureArray.image, - rtData.normalTextureAtlas.textureArray.sampler, 2, 2); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 2); - if (rtData.roughnessTextureAtlas.slices.size()) - commandList->BindImage(rtData.roughnessTextureAtlas.textureArray.image, - rtData.roughnessTextureAtlas.textureArray.sampler, 2, 3); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 3); - if (rtData.metalnessTextureAtlas.slices.size()) - commandList->BindImage(rtData.metalnessTextureAtlas.textureArray.image, - rtData.metalnessTextureAtlas.textureArray.sampler, 2, 4); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 4); - if (rtData.aoTextureAtlas.slices.size()) - commandList->BindImage(rtData.aoTextureAtlas.textureArray.image, - rtData.aoTextureAtlas.textureArray.sampler, 2, 5); - else - commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 5); if (scene->sky.GetProbe()) commandList->BindImage(scene->sky.GetProbe()->cubemap.image, scene->sky.GetProbe()->cubemap.sampler, 2, 6); @@ -309,17 +246,14 @@ namespace Atlas { commandList->BindImage(dummyTexture.image, dummyTexture.sampler, 2, 6); rtData.materialBuffer.Bind(commandList, 2, 7); - rtData.triangleBuffer.Bind(commandList, 2, 8); rtData.bvhInstanceBuffer.Bind(commandList, 2, 21); + rtData.lastMatricesBuffer.Bind(commandList, 2, 27); lightBuffer.Bind(commandList, 2, 11); if (rtData.hardwareRayTracing) { commandList->BindTLAS(rtData.tlas, 2, 23); - rtData.geometryTriangleOffsetBuffer.Bind(commandList, 2, 22); } else { - rtData.bvhTriangleBuffer.Bind(commandList, 2, 9); - rtData.blasNodeBuffer.Bind(commandList, 2, 10); rtData.tlasNodeBuffer.Bind(commandList, 2, 22); } } diff --git a/src/engine/resource/ResourceManager.h b/src/engine/resource/ResourceManager.h index f2c815c8b..0f465390a 100644 --- a/src/engine/resource/ResourceManager.h +++ b/src/engine/resource/ResourceManager.h @@ -180,6 +180,8 @@ namespace Atlas { static ResourceHandle AddResource(const std::string& path, Ref> resource, bool& alreadyExisted) { + CheckInitialization(); + { std::lock_guard lock(mutex); if (resources.contains(path)) { diff --git a/src/engine/scene/RTData.cpp b/src/engine/scene/RTData.cpp index 804beffe5..a7cf4b95e 100644 --- a/src/engine/scene/RTData.cpp +++ b/src/engine/scene/RTData.cpp @@ -6,6 +6,9 @@ #include "../graphics/ASBuilder.h" #include "../common/ColorConverter.h" +#include +#include + namespace Atlas { namespace Scene { @@ -16,75 +19,70 @@ namespace Atlas { hardwareRayTracing = device->support.hardwareRayTracing; - triangleBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit, sizeof(GPUTriangle)); - bvhTriangleBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit, sizeof(BVHTriangle)); - blasNodeBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit, sizeof(GPUBVHNode)); - geometryTriangleOffsetBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit, sizeof(uint32_t)); - auto bufferUsage = Buffer::BufferUsageBits::StorageBufferBit | - Buffer::BufferUsageBits::HostAccessBit | Buffer::BufferUsageBits::MultiBufferedBit; + Buffer::BufferUsageBits::MultiBufferedBit | Buffer::BufferUsageBits::HostAccessBit; materialBuffer = Buffer::Buffer(bufferUsage, sizeof(GPUMaterial)); bvhInstanceBuffer = Buffer::Buffer(bufferUsage, sizeof(GPUBVHInstance)); tlasNodeBuffer = Buffer::Buffer(bufferUsage, sizeof(GPUBVHNode)); + lastMatricesBuffer = Buffer::Buffer(bufferUsage, sizeof(mat4x3)); } - void RTData::Build() { - - isValid = false; - - if (hardwareRayTracing) { - BuildForHardwareRayTracing(); - } - else { - BuildForSoftwareRayTracing(); - } - - std::vector materials; - UpdateMaterials(materials, true); - - isValid = true; + void RTData::Update(bool updateTriangleLights) { - } + auto device = Graphics::GraphicsDevice::DefaultDevice; - void RTData::Update(bool updateTriangleLights) { + if (!device->swapChain->isComplete) return; auto actors = scene->GetMeshActors(); - if (!actors.size()) return; - UpdateMaterials(); + auto meshes = scene->GetMeshes(); + for (auto& mesh : meshes) { + if (!mesh.IsLoaded() || !mesh->IsBVHBuilt() || + !scene->meshIdToBindlessIdx.contains(mesh.GetID())) + continue; + + if (!meshInfos.contains(mesh.GetID())) { + meshInfos[mesh.GetID()] = {}; + BuildTriangleLightsForMesh(mesh); + } + + auto &meshInfo = meshInfos[mesh.GetID()]; + meshInfo.offset = int32_t(scene->meshIdToBindlessIdx[mesh.GetID()]); + } std::vector gpuBvhInstances; std::vector actorAABBs; + std::vector lastMatrices; for (auto& [_, meshInfo] : meshInfos) { meshInfo.instanceIndices.clear(); meshInfo.matrices.clear(); } - for (auto& actor : actors) { - if (!actor->mesh.IsLoaded()) - continue; + UpdateMaterials(); + for (auto& actor : actors) { if (!meshInfos.contains(actor->mesh.GetID())) continue; actorAABBs.push_back(actor->aabb); - auto& meshInfo = meshInfos[actor->mesh.GetID()]; + auto &meshInfo = meshInfos[actor->mesh.GetID()]; auto inverseMatrix = mat3x4(glm::transpose(actor->inverseGlobalMatrix)); GPUBVHInstance gpuBvhInstance = { .inverseMatrix = inverseMatrix, - .blasOffset = meshInfo.offset, - .triangleOffset = meshInfo.triangleOffset + .meshOffset = meshInfo.offset, + .materialOffset = meshInfo.materialOffset }; meshInfo.matrices.push_back(actor->globalMatrix); meshInfo.instanceIndices.push_back(uint32_t(gpuBvhInstances.size())); gpuBvhInstances.push_back(gpuBvhInstance); + lastMatrices.push_back(glm::transpose(actor->lastGlobalMatrix)); } if (!gpuBvhInstances.size()) @@ -94,53 +92,56 @@ namespace Atlas { UpdateForHardwareRayTracing(actors); } else { - gpuBvhInstances = UpdateForSoftwareRayTracing(gpuBvhInstances, actorAABBs); + UpdateForSoftwareRayTracing(gpuBvhInstances, lastMatrices, actorAABBs); } - std::vector materials; - UpdateMaterials(materials, false); - if (updateTriangleLights) UpdateTriangleLights(); - bvhInstanceBuffer.SetSize(gpuBvhInstances.size()); + if (bvhInstanceBuffer.GetElementCount() < gpuBvhInstances.size()) { + bvhInstanceBuffer.SetSize(gpuBvhInstances.size()); + lastMatricesBuffer.SetSize(lastMatrices.size()); + } + bvhInstanceBuffer.SetData(gpuBvhInstances.data(), 0, gpuBvhInstances.size()); + lastMatricesBuffer.SetData(lastMatrices.data(), 0, lastMatrices.size()); } - void RTData::UpdateMaterials(bool updateTextures) { + void RTData::UpdateMaterials() { std::vector materials; - UpdateMaterials(materials, updateTextures); + UpdateMaterials(materials); } - void RTData::UpdateMaterials(std::vector& materials, - bool updateTextures) { + void RTData::UpdateMaterials(std::vector& materials) { std::lock_guard lock(mutex); auto actors = scene->GetMeshActors(); - int32_t materialCount = 0; - - if (updateTextures) UpdateTextures(); - auto meshes = scene->GetMeshes(); - materials.resize(materialAccess.size()); + materials.clear(); for (auto& mesh : meshes) { - if (!mesh.IsLoaded()) + if (!meshInfos.contains(mesh.GetID())) continue; - for (auto& material : mesh->data.materials) { - if (!materialAccess.contains(material.get())) - continue; + auto& meshInfo = meshInfos[mesh.GetID()]; + meshInfo.materialOffset = int32_t(materials.size()); - auto materialIdx = materialAccess[material.get()]; + int32_t meshMaterialID = 0; + for (auto& material : mesh->data.materials) { GPUMaterial gpuMaterial; + size_t hash = mesh.GetID(); + HashCombine(hash, meshMaterialID++); + + // Only is persistent when no materials are reorderd in mesh + gpuMaterial.ID = int32_t(hash % 65535); + gpuMaterial.baseColor = Common::ColorConverter::ConvertSRGBToLinear(material->baseColor); gpuMaterial.emissiveColor = Common::ColorConverter::ConvertSRGBToLinear(material->emissiveColor) * material->emissiveIntensity; @@ -157,316 +158,60 @@ namespace Atlas { gpuMaterial.invertUVs = mesh->invertUVs ? 1 : 0; gpuMaterial.twoSided = material->twoSided ? 1 : 0; + gpuMaterial.useVertexColors = material->vertexColors ? 1 : 0; if (material->HasBaseColorMap()) { - auto& slices = baseColorTextureAtlas.slices[material->baseColorMap.get()]; - - gpuMaterial.baseColorTexture = CreateGPUTextureStruct(slices); + gpuMaterial.baseColorTexture = scene->textureToBindlessIdx[material->baseColorMap]; } if (material->HasOpacityMap()) { - auto& slices = opacityTextureAtlas.slices[material->opacityMap.get()]; - - gpuMaterial.opacityTexture = CreateGPUTextureStruct(slices); + gpuMaterial.opacityTexture = scene->textureToBindlessIdx[material->opacityMap]; } if (material->HasNormalMap()) { - auto& slices = normalTextureAtlas.slices[material->normalMap.get()]; - - gpuMaterial.normalTexture = CreateGPUTextureStruct(slices); + gpuMaterial.normalTexture = scene->textureToBindlessIdx[material->normalMap]; } if (material->HasRoughnessMap()) { - auto& slices = roughnessTextureAtlas.slices[material->roughnessMap.get()]; - - gpuMaterial.roughnessTexture = CreateGPUTextureStruct(slices); + gpuMaterial.roughnessTexture = scene->textureToBindlessIdx[material->roughnessMap]; } if (material->HasMetalnessMap()) { - auto& slices = metalnessTextureAtlas.slices[material->metalnessMap.get()]; - - gpuMaterial.metalnessTexture = CreateGPUTextureStruct(slices); + gpuMaterial.metalnessTexture = scene->textureToBindlessIdx[material->metalnessMap]; } if (material->HasAoMap()) { - auto& slices = aoTextureAtlas.slices[material->aoMap.get()]; - - gpuMaterial.aoTexture = CreateGPUTextureStruct(slices); + gpuMaterial.aoTexture = scene->textureToBindlessIdx[material->aoMap]; } - materials[materialIdx] = gpuMaterial; + materials.push_back(gpuMaterial); } } + if (!materials.size()) + return; + materialBuffer.SetSize(materials.size()); materialBuffer.SetData(materials.data(), 0, materials.size()); } - void RTData::UpdateTextures() { - - auto meshes = scene->GetMeshes(); - - std::vector> baseColorTextures; - std::vector> opacityTextures; - std::vector> normalTextures; - std::vector> roughnessTextures; - std::vector> metalnessTextures; - std::vector> aoTextures; - - for (auto& mesh : meshes) { - if (!mesh.IsLoaded()) - continue; - - for (auto& material : mesh->data.materials) { - if (!materialAccess.contains(material.get())) - continue; - - if (material->HasBaseColorMap()) - baseColorTextures.push_back(material->baseColorMap); - if (material->HasOpacityMap()) - opacityTextures.push_back(material->opacityMap); - if (material->HasNormalMap()) - normalTextures.push_back(material->normalMap); - if (material->HasRoughnessMap()) - roughnessTextures.push_back(material->roughnessMap); - if (material->HasMetalnessMap()) - metalnessTextures.push_back(material->metalnessMap); - if (material->HasAoMap()) - aoTextures.push_back(material->aoMap); - } - } - - const int32_t textureDownscale = 1; - baseColorTextureAtlas = Texture::TextureAtlas(baseColorTextures, 1, textureDownscale); - opacityTextureAtlas = Texture::TextureAtlas(opacityTextures, 1, textureDownscale); - normalTextureAtlas = Texture::TextureAtlas(normalTextures, 1, textureDownscale); - roughnessTextureAtlas = Texture::TextureAtlas(roughnessTextures, 1, textureDownscale); - metalnessTextureAtlas = Texture::TextureAtlas(metalnessTextures, 1, textureDownscale); - aoTextureAtlas = Texture::TextureAtlas(aoTextures, 1, textureDownscale); - - } - void RTData::Clear() { - isValid = false; - - baseColorTextureAtlas.Clear(); - opacityTextureAtlas.Clear(); - normalTextureAtlas.Clear(); - roughnessTextureAtlas.Clear(); - metalnessTextureAtlas.Clear(); - aoTextureAtlas.Clear(); + meshInfos.clear(); } bool RTData::IsValid() { - return isValid; - - } - - GPUTexture RTData::CreateGPUTextureStruct(std::vector slices) { - - GPUTexture texture; - - if (slices.size() > 0) texture.level0 = CreateGPUTextureLevelStruct(slices[0]); - if (slices.size() > 1) texture.level1 = CreateGPUTextureLevelStruct(slices[1]); - if (slices.size() > 2) texture.level2 = CreateGPUTextureLevelStruct(slices[2]); - if (slices.size() > 3) texture.level3 = CreateGPUTextureLevelStruct(slices[3]); - if (slices.size() > 4) texture.level4 = CreateGPUTextureLevelStruct(slices[4]); - - texture.valid = 1; - - return texture; - - } - - GPUTextureLevel RTData::CreateGPUTextureLevelStruct(Texture::TextureAtlas::Slice slice) { - - GPUTextureLevel level; - - level.x = slice.offset.x; - level.y = slice.offset.y; - - level.width = slice.size.x; - level.height = slice.size.y; - - level.layer = slice.layer; - level.valid = 1; - - return level; - - } - - void RTData::BuildForSoftwareRayTracing() { - - std::vector gpuTriangles; - std::vector gpuBvhTriangles; - std::vector gpuBvhNodes; - - auto meshes = scene->GetMeshes(); - - materialAccess.clear(); - - int32_t materialCount = 0; - for (auto& mesh : meshes) { - if (!mesh.IsLoaded()) - continue; - - // Not all meshes might have a bvh - if (!mesh->data.gpuTriangles.size()) - continue; - - auto triangleOffset = int32_t(gpuTriangles.size()); - auto nodeOffset = int32_t(gpuBvhNodes.size()); - - for (size_t i = 0; i < mesh->data.gpuBvhNodes.size(); i++) { - auto gpuBvhNode = mesh->data.gpuBvhNodes[i]; - - auto leftPtr = gpuBvhNode.leftPtr; - auto rightPtr = gpuBvhNode.rightPtr; - - gpuBvhNode.leftPtr = leftPtr < 0 ? ~((~leftPtr) + triangleOffset) : leftPtr + nodeOffset; - gpuBvhNode.rightPtr = rightPtr < 0 ? ~((~rightPtr) + triangleOffset) : rightPtr + nodeOffset; - - gpuBvhNodes.push_back(gpuBvhNode); - } - - // Subtract and reassign material offset - for (size_t i = 0; i < mesh->data.gpuTriangles.size(); i++) { - auto gpuTriangle = mesh->data.gpuTriangles[i]; - auto gpuBvhTriangle = mesh->data.gpuBvhTriangles[i]; - - auto localMaterialIdx = reinterpret_cast(gpuTriangle.d0.w); - auto materialIdx = localMaterialIdx + materialCount; - - gpuTriangle.d0.w = reinterpret_cast(materialIdx); - gpuBvhTriangle.v1.w = reinterpret_cast(materialIdx); - - gpuTriangles.push_back(gpuTriangle); - gpuBvhTriangles.push_back(gpuBvhTriangle); - } - - for (auto& material : mesh->data.materials) { - materialAccess[material.get()] = materialCount++; - } - - MeshInfo meshInfo = { - .offset = nodeOffset, - .triangleOffset = triangleOffset - }; - meshInfos[mesh.GetID()] = meshInfo; - - BuildTriangleLightsForMesh(mesh); - - } - - if (!gpuTriangles.size()) - return; - - // Upload triangles - triangleBuffer.SetSize(gpuTriangles.size()); - triangleBuffer.SetData(gpuTriangles.data(), 0, gpuTriangles.size()); - - bvhTriangleBuffer.SetSize(gpuBvhTriangles.size()); - bvhTriangleBuffer.SetData(gpuBvhTriangles.data(), 0, gpuBvhTriangles.size()); - - blasNodeBuffer.SetSize(gpuBvhNodes.size()); - blasNodeBuffer.SetData(gpuBvhNodes.data(), 0, gpuBvhNodes.size()); - - } - - void RTData::BuildForHardwareRayTracing() { - auto device = Graphics::GraphicsDevice::DefaultDevice; - std::vector triangleOffsets; - std::vector gpuTriangles; - - Graphics::ASBuilder asBuilder; - - auto meshes = scene->GetMeshes(); - - materialAccess.clear(); - blases.clear(); - - int32_t materialCount = 0; - for (auto& mesh : meshes) { - if (!mesh.IsLoaded()) - continue; - - // Not all meshes might have a bvh - if (!mesh->data.gpuTriangles.size()) - continue; - - auto triangleOffset = int32_t(gpuTriangles.size()); - auto offset = int32_t(triangleOffsets.size()); - - // Subtract and reassign material offset - for (size_t i = 0; i < mesh->data.gpuTriangles.size(); i++) { - auto gpuTriangle = mesh->data.gpuTriangles[i]; - - auto localMaterialIdx = reinterpret_cast(gpuTriangle.d0.w); - auto materialIdx = localMaterialIdx + materialCount; - - gpuTriangle.d0.w = reinterpret_cast(materialIdx); - - gpuTriangles.push_back(gpuTriangle); - } - - for (auto& material : mesh->data.materials) { - materialAccess[material.get()] = materialCount++; - } - - std::vector geometryRegions; - for (auto& subData : mesh->data.subData) { - geometryRegions.emplace_back(Graphics::ASGeometryRegion{ - .indexCount = subData.indicesCount, - .indexOffset = subData.indicesOffset, - .opaque = !subData.material->HasOpacityMap() && subData.material->opacity == 1.0f - }); - } - - auto blasDesc = asBuilder.GetBLASDescForTriangleGeometry(mesh->vertexBuffer.buffer, mesh->indexBuffer.buffer, - mesh->vertexBuffer.elementCount, mesh->vertexBuffer.elementSize, - mesh->indexBuffer.elementSize, geometryRegions); - - blases.push_back(device->CreateBLAS(blasDesc)); - - MeshInfo meshInfo = { - .blas = blases.back(), - - .offset = offset, - .triangleOffset = triangleOffset - }; - meshInfos[mesh.GetID()] = meshInfo; - - for (auto& subData : mesh->data.subData) { - auto totalTriangleOffset = triangleOffset + subData.indicesOffset / 3; - triangleOffsets.push_back(totalTriangleOffset); - } - - BuildTriangleLightsForMesh(mesh); - - } - - if (!gpuTriangles.size()) - return; - - // Upload triangles - triangleBuffer.SetSize(gpuTriangles.size()); - triangleBuffer.SetData(gpuTriangles.data(), 0, gpuTriangles.size()); - - geometryTriangleOffsetBuffer.SetSize(triangleOffsets.size()); - geometryTriangleOffsetBuffer.SetData(triangleOffsets.data(), 0, triangleOffsets.size()); - - asBuilder.BuildBLAS(blases); + return materialBuffer.GetSize() > 0 && device->support.bindless && !meshInfos.empty(); } - std::vector RTData::UpdateForSoftwareRayTracing(std::vector& gpuBvhInstances, - std::vector& actorAABBs) { + void RTData::UpdateForSoftwareRayTracing(std::vector& gpuBvhInstances, + std::vector& lastMatrices, std::vector& actorAABBs) { auto bvh = Volume::BVH(actorAABBs); @@ -486,14 +231,22 @@ namespace Atlas { // Order after the BVH build to fit the node indices std::vector orderedGpuBvhInstances(bvh.refs.size()); + std::vector orderedLastMatrices(bvh.refs.size()); for (size_t i = 0; i < bvh.refs.size(); i++) { + const auto& ref = bvh.refs[i]; + orderedLastMatrices[i] = lastMatrices[bvh.refs[i].idx]; orderedGpuBvhInstances[i] = gpuBvhInstances[bvh.refs[i].idx]; + orderedGpuBvhInstances[i].nextInstance = ref.endOfNode ? -1 : int32_t(i) + 1; + } + + if (tlasNodeBuffer.GetElementCount() < gpuBvhNodes.size()) { + tlasNodeBuffer.SetSize(gpuBvhNodes.size()); } - tlasNodeBuffer.SetSize(gpuBvhNodes.size()); tlasNodeBuffer.SetData(gpuBvhNodes.data(), 0, gpuBvhNodes.size()); - return orderedGpuBvhInstances; + gpuBvhInstances = orderedGpuBvhInstances; + lastMatrices = orderedLastMatrices; } @@ -503,12 +256,31 @@ namespace Atlas { Graphics::ASBuilder asBuilder; - std::vector instances; + auto meshes = scene->GetMeshes(); - for (auto actor : actors) { - if (!actor->mesh.IsLoaded()) + blases.clear(); + + int32_t meshCount = 0; + for (auto& mesh : meshes) { + if (!meshInfos.contains(mesh.GetID())) continue; + if (mesh->needsBvhRefresh) { + blases.push_back(mesh->blas); + mesh->needsBvhRefresh = false; + } + + auto& meshInfo = meshInfos[mesh.GetID()]; + meshInfo.blas = mesh->blas; + meshInfo.idx = meshCount++; + } + + if (blases.size()) + asBuilder.BuildBLAS(blases); + + std::vector instances; + + for (auto actor : actors) { if (!meshInfos.contains(actor->mesh.GetID())) continue; @@ -542,6 +314,7 @@ namespace Atlas { auto& materials = mesh->data.materials; auto& meshInfo = meshInfos[mesh.GetID()]; + meshInfo.triangleLights.clear(); // Triangle lights for (size_t i = 0; i < gpuTriangles.size(); i++) { @@ -549,8 +322,7 @@ namespace Atlas { auto idx = reinterpret_cast(triangle.d0.w); auto& material = materials[idx]; - auto radiance = Common::ColorConverter::ConvertSRGBToLinear(material->emissiveColor) - * material->emissiveIntensity; + auto radiance = Common::ColorConverter::ConvertSRGBToLinear(material->emissiveColor); auto brightness = dot(radiance, vec3(0.3333f)); if (brightness > 0.0f) { @@ -583,7 +355,7 @@ namespace Atlas { GPULight light; light.P = vec4(P, 1.0f); light.N = vec4(N, 0.0f); - light.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(radiance), 0.0f); + light.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(radiance) * material->emissiveIntensity, 0.0f); light.data = vec4(cd, weight, area, 0.0f); meshInfo.triangleLights.push_back(light); diff --git a/src/engine/scene/RTData.h b/src/engine/scene/RTData.h index d9663e3e5..5c5ea2be1 100644 --- a/src/engine/scene/RTData.h +++ b/src/engine/scene/RTData.h @@ -29,13 +29,9 @@ namespace Atlas { RTData(Scene* scene); - void Build(); - void Update(bool updateTriangleLights); - void UpdateMaterials(bool updateTextures = false); - - void UpdateTextures(); + void UpdateMaterials(); bool IsValid(); @@ -46,25 +42,19 @@ namespace Atlas { Ref blas = nullptr; int32_t offset = 0; - int32_t triangleOffset = 0; + int32_t materialOffset = 0; + + int32_t idx = 0; std::vector triangleLights; std::vector instanceIndices; std::vector matrices; }; - void UpdateMaterials(std::vector& materials, bool updateTextures); - - GPUTexture CreateGPUTextureStruct(std::vector slices); + void UpdateMaterials(std::vector& materials); - GPUTextureLevel CreateGPUTextureLevelStruct(Texture::TextureAtlas::Slice slice); - - void BuildForSoftwareRayTracing(); - - void BuildForHardwareRayTracing(); - - std::vector UpdateForSoftwareRayTracing(std::vector& gpuBvhInstances, - std::vector& actorAABBs); + void UpdateForSoftwareRayTracing(std::vector& gpuBvhInstances, + std::vector& lastMatrices, std::vector& actorAABBs); void UpdateForHardwareRayTracing(std::vector& actors); @@ -77,29 +67,18 @@ namespace Atlas { Ref tlas; std::vector> blases; - Buffer::Buffer triangleBuffer; - Buffer::Buffer bvhTriangleBuffer; Buffer::Buffer materialBuffer; Buffer::Buffer bvhInstanceBuffer; Buffer::Buffer tlasNodeBuffer; - Buffer::Buffer blasNodeBuffer; - Buffer::Buffer geometryTriangleOffsetBuffer; - - Texture::TextureAtlas baseColorTextureAtlas; - Texture::TextureAtlas opacityTextureAtlas; - Texture::TextureAtlas normalTextureAtlas; - Texture::TextureAtlas roughnessTextureAtlas; - Texture::TextureAtlas metalnessTextureAtlas; - Texture::TextureAtlas aoTextureAtlas; + Buffer::Buffer lastMatricesBuffer; std::vector triangleLights; - std::unordered_map materialAccess; std::unordered_map meshInfos; bool hardwareRayTracing = false; - std::atomic_bool isValid = false; + std::atomic_bool isValid = true; std::mutex mutex; }; diff --git a/src/engine/scene/RTStructures.h b/src/engine/scene/RTStructures.h index 36c7dc089..dd15ceaa2 100644 --- a/src/engine/scene/RTStructures.h +++ b/src/engine/scene/RTStructures.h @@ -15,7 +15,7 @@ namespace Atlas { vec4 d2; }; - struct BVHTriangle { + struct GPUBVHTriangle { vec4 v0; vec4 v1; vec4 v2; @@ -44,6 +44,8 @@ namespace Atlas { }; struct GPUMaterial { + int32_t ID; + vec3 baseColor; vec3 emissiveColor; @@ -59,13 +61,14 @@ namespace Atlas { int32_t invertUVs; int32_t twoSided; - - GPUTexture baseColorTexture; - GPUTexture opacityTexture; - GPUTexture normalTexture; - GPUTexture roughnessTexture; - GPUTexture metalnessTexture; - GPUTexture aoTexture; + int32_t useVertexColors; + + int32_t baseColorTexture = -1; + int32_t opacityTexture = -1; + int32_t normalTexture = -1; + int32_t roughnessTexture = -1; + int32_t metalnessTexture = -1; + int32_t aoTexture = -1; }; struct GPUAABB { @@ -76,10 +79,10 @@ namespace Atlas { struct GPUBVHInstance { mat3x4 inverseMatrix; - int32_t blasOffset; - int32_t triangleOffset; + int32_t meshOffset; + int32_t materialOffset; - int32_t padding0; + int32_t nextInstance; int32_t padding1; }; diff --git a/src/engine/scene/Scene.cpp b/src/engine/scene/Scene.cpp index a8905f387..928512fe6 100644 --- a/src/engine/scene/Scene.cpp +++ b/src/engine/scene/Scene.cpp @@ -54,11 +54,11 @@ namespace Atlas { hasChanged = SceneNode::Update(camera, deltaTime, mat4(1.0f), false); + UpdateBindlessIndexMaps(); + // Make sure this is changed just once at the start of a frame + rtData.Update(true); rtDataValid = rtData.IsValid(); - if (rtDataValid) { - rtData.Update(true); - } } @@ -125,14 +125,9 @@ namespace Atlas { } - void Scene::BuildRTStructures() { - - rtData.Build(); - - } - void Scene::ClearRTStructures() { + rtDataValid = false; rtData.Clear(); } @@ -167,6 +162,50 @@ namespace Atlas { } + void Scene::UpdateBindlessIndexMaps() { + + std::set> textures; + + uint32_t textureIdx = 0; + uint32_t bufferIdx = 0; + + textureToBindlessIdx.clear(); + meshIdToBindlessIdx.clear(); + + auto meshes = GetMeshes(); + for (auto& mesh : meshes) { + if (!mesh.IsLoaded()) continue; + + for (auto &material: mesh->data.materials) { + if (material->HasBaseColorMap()) + textures.insert(material->baseColorMap); + if (material->HasOpacityMap()) + textures.insert(material->opacityMap); + if (material->HasNormalMap()) + textures.insert(material->normalMap); + if (material->HasRoughnessMap()) + textures.insert(material->roughnessMap); + if (material->HasMetalnessMap()) + textures.insert(material->metalnessMap); + if (material->HasAoMap()) + textures.insert(material->aoMap); + } + + // Not all meshes might have a bvh + if (!mesh->IsBVHBuilt()) + continue; + + meshIdToBindlessIdx[mesh.GetID()] = bufferIdx++; + } + + for (const auto& texture : textures) { + + textureToBindlessIdx[texture] = textureIdx++; + + } + + } + } } \ No newline at end of file diff --git a/src/engine/scene/Scene.h b/src/engine/scene/Scene.h index 1c4519e5c..31ab10bbf 100644 --- a/src/engine/scene/Scene.h +++ b/src/engine/scene/Scene.h @@ -11,6 +11,7 @@ #include "../lighting/Reflection.h" #include "../lighting/VolumetricClouds.h" #include "../lighting/SSS.h" +#include "../lighting/SSGI.h" #include "../ocean/Ocean.h" #include "../postprocessing/PostProcessing.h" #include "../Decal.h" @@ -28,7 +29,9 @@ namespace Atlas { class Scene : public SceneNode, public SpacePartitioning { + friend class RTData; friend class Renderer::Helper::RayTracingHelper; + friend class Renderer::MainRenderer; public: /** @@ -92,12 +95,6 @@ namespace Atlas { */ std::vector GetMaterials(); - /** - * Builds the BVH and texture atlases. - * @note The scene needs to be updated first. - */ - void BuildRTStructures(); - /** * */ @@ -137,10 +134,15 @@ namespace Atlas { Ref ao = nullptr; Ref reflection = nullptr; Ref sss = nullptr; + Ref ssgi = nullptr; PostProcessing::PostProcessing postProcessing; private: + void UpdateBindlessIndexMaps(); + std::unordered_map rootMeshMap; + std::unordered_map, uint32_t> textureToBindlessIdx; + std::unordered_map meshIdToBindlessIdx; RTData rtData; diff --git a/src/engine/texture/Texture.cpp b/src/engine/texture/Texture.cpp index 0911993cb..46f5f5d18 100644 --- a/src/engine/texture/Texture.cpp +++ b/src/engine/texture/Texture.cpp @@ -31,7 +31,7 @@ namespace Atlas { bool Texture::IsValid() const { return width > 0 && height > 0 && - channels > 0 && depth > 0; + channels > 0 && depth > 0 && image != nullptr; } diff --git a/src/engine/texture/Texture.h b/src/engine/texture/Texture.h index d81d8987f..815991b64 100644 --- a/src/engine/texture/Texture.h +++ b/src/engine/texture/Texture.h @@ -100,6 +100,9 @@ namespace Atlas { */ template std::vector GetData(int32_t depth = 0); + /** + * + */ void GenerateMipmap(); /** @@ -139,7 +142,8 @@ namespace Atlas { std::vector Texture::GetData(int32_t layerOffset) { static_assert(std::is_same_v || std::is_same_v || - std::is_same_v, "Unsupported type. Supported are uint8_t, uint16_t and float"); + std::is_same_v || std::is_same_v, + "Unsupported type. Supported are uint8_t, uint16_t, float and float16"); std::vector data(width * height * channels); diff --git a/src/engine/texture/Texture2D.h b/src/engine/texture/Texture2D.h index 9bd6c7550..82e5354b2 100644 --- a/src/engine/texture/Texture2D.h +++ b/src/engine/texture/Texture2D.h @@ -70,7 +70,7 @@ namespace Atlas { /** * Saves the texture to a the supported format. - * @param filename The name of the file + * @param filename The name of the file (without a file type, which is automatically selectecd) * @note Only uint8_t, uint16_t and float are supported typenames. */ template @@ -88,23 +88,38 @@ namespace Atlas { auto image = CreateRef>(width, height, channels); + std::vector data; + if constexpr (std::is_same_v) { - image.fileFormat = Common::ImageFormat::PNG; + image->fileFormat = Common::ImageFormat::PNG; filename += ".png"; + data = GetData(); } else if constexpr (std::is_same_v) { - image.fileFormat = Common::ImageFormat::PGM; + image->fileFormat = Common::ImageFormat::PGM; filename += ".pgm"; + data = GetData(); } else if constexpr (std::is_same_v) { - image.fileFormat = Common::ImageFormat::HDR; + image->fileFormat = Common::ImageFormat::HDR; filename += ".hdr"; + // We have a lot of 16bit float textures, need some care + if (this->image->bitDepth < 32) { + auto texData = GetData(); + std::transform(texData.begin(), texData.end(), std::back_inserter(data), + [](float16 x) { + return glm::detail::toFloat32(x); + }); + } + else { + data = GetData(); + } } - image.SetData(GetData()); + image->SetData(data); if (flipHorizontally) - image.FlipHorizontally(); + image->FlipHorizontally(); Loader::ImageLoader::SaveImage(image, filename); diff --git a/src/engine/texture/Texture2DArray.cpp b/src/engine/texture/Texture2DArray.cpp index 7fae106af..75453701e 100644 --- a/src/engine/texture/Texture2DArray.cpp +++ b/src/engine/texture/Texture2DArray.cpp @@ -65,7 +65,7 @@ namespace Atlas { } - void Texture2DArray::SaveToPNG(std::string filename, int32_t depth) { + void Texture2DArray::SaveToPNG(const std::string& filename, int32_t depth) { diff --git a/src/engine/texture/Texture2DArray.h b/src/engine/texture/Texture2DArray.h index e582fb7c6..a1b73283b 100644 --- a/src/engine/texture/Texture2DArray.h +++ b/src/engine/texture/Texture2DArray.h @@ -107,7 +107,7 @@ namespace Atlas { * @param filename The name of the file * @param layer */ - void SaveToPNG(std::string filename, int32_t layer); + void SaveToPNG(const std::string& filename, int32_t layer); }; diff --git a/src/engine/texture/TextureAtlas.cpp b/src/engine/texture/TextureAtlas.cpp index fe66de943..19ca6c70c 100644 --- a/src/engine/texture/TextureAtlas.cpp +++ b/src/engine/texture/TextureAtlas.cpp @@ -15,7 +15,7 @@ namespace Atlas { } - TextureAtlas::TextureAtlas(const std::vector>& textures, int32_t padding, + TextureAtlas::TextureAtlas(const std::set>& textures, int32_t padding, int32_t downscale) : padding(padding), downscale(downscale) { Update(textures); @@ -37,7 +37,7 @@ namespace Atlas { } - void TextureAtlas::Update(const std::vector>& textures) { + void TextureAtlas::Update(const std::set>& textures) { if (!textures.size()) return; @@ -82,9 +82,8 @@ namespace Atlas { // Approximation of total padding by assuming to stuff // the smallest texture over and over again. - auto& smallest = textureStructures.back(); - auto smallestSize = glm::max(ivec2(smallest.width, smallest.height), glm::ivec2(1)); - ivec2 totalPadding = ivec2(width, height) / smallestSize * padding; + auto largest = textureStructures.front(); + ivec2 totalPadding = ivec2(glm::ceil(glm::log2(glm::vec2(float(largest.width), float(largest.height))))) * padding; // Add total padding to total size width += totalPadding.x; diff --git a/src/engine/texture/TextureAtlas.h b/src/engine/texture/TextureAtlas.h index 311723718..b17dfeba0 100644 --- a/src/engine/texture/TextureAtlas.h +++ b/src/engine/texture/TextureAtlas.h @@ -8,6 +8,7 @@ #include #include +#include namespace Atlas { @@ -34,7 +35,7 @@ namespace Atlas { * @param that Another TextureAtlas object. * @note Texture atlases are only available as AE_RGBA8. */ - explicit TextureAtlas(const std::vector>& textures, + explicit TextureAtlas(const std::set>& textures, int32_t padding = 1, int32_t downscale = 1); /** @@ -45,7 +46,7 @@ namespace Atlas { */ TextureAtlas& operator=(const TextureAtlas& that); - void Update(const std::vector>& textures); + void Update(const std::set>& textures); void Clear(); @@ -58,7 +59,7 @@ namespace Atlas { }; Texture2DArray textureArray; - std::vector> textures; + std::set> textures; std::map> slices; private: diff --git a/src/engine/tools/ImpostorTool.cpp b/src/engine/tools/ImpostorTool.cpp index b2983df59..cf047e01f 100644 --- a/src/engine/tools/ImpostorTool.cpp +++ b/src/engine/tools/ImpostorTool.cpp @@ -89,6 +89,8 @@ namespace Atlas { impostor->normalTexture.GenerateMipmap(); impostor->depthTexture.GenerateMipmap(); + + return impostor; } diff --git a/src/engine/volume/BVH.cpp b/src/engine/volume/BVH.cpp index f9de8ca17..5ced008fa 100644 --- a/src/engine/volume/BVH.cpp +++ b/src/engine/volume/BVH.cpp @@ -17,7 +17,7 @@ namespace Atlas { uint32_t BVHBuilder::spatialSplitCount = 0; float BVHBuilder::totalSurfaceArea = 0.0f; - BVH::BVH(std::vector& aabbs, std::vector& data) { + BVH::BVH(const std::vector& aabbs, const std::vector& data, bool parallelBuild) { Log::Message("Started BVH build"); @@ -41,7 +41,7 @@ namespace Atlas { auto minOverlap = aabb.GetSurfaceArea() * 10e-6f; auto builder = new BVHBuilder(aabb, 0, refs.size(), minOverlap, 256); - builder->Build(refs, data); + builder->Build(refs, data, parallelBuild); refs.reserve(data.size()); Log::Message("Build: " + std::to_string(perfCounter.StepStamp().delta)); @@ -72,7 +72,7 @@ namespace Atlas { } - BVH::BVH(std::vector& aabbs) { + BVH::BVH(const std::vector& aabbs, bool parallelBuild) { refs.resize(aabbs.size()); for (size_t i = 0; i < refs.size(); i++) { @@ -88,7 +88,7 @@ namespace Atlas { auto builder = new BVHBuilder(aabb, 0, refs.size(), 128); - builder->Build(refs); + builder->Build(refs, parallelBuild); refs.reserve(data.size()); @@ -271,11 +271,11 @@ namespace Atlas { } - void BVHBuilder::Build(std::vector& refs, const std::vector& data) { + void BVHBuilder::Build(std::vector& refs, const std::vector& data, bool parallelBuild) { const size_t refCount = 2; // Create leaf node - if (refs.size() <= refCount || depth >= 32) { + if ((refs.size() <= refCount || depth >= 32) && depth > 0) { CreateLeaf(refs); return; } @@ -302,8 +302,14 @@ namespace Atlas { // If we haven't found a cost improvement we create a leaf node if ((objectSplit.axis < 0 || objectSplit.cost >= nodeCost) && (spatialSplit.axis < 0 || spatialSplit.cost >= nodeCost)) { - CreateLeaf(refs); - return; + // In the rare case there was not enough improvements with just a few triangles + if (depth == 0) { + split = PerformMedianSplit(refs, rightRefs, leftRefs); + } + else { + CreateLeaf(refs); + return; + } } else { if (spatialSplit.cost < objectSplit.cost) { @@ -325,17 +331,17 @@ namespace Atlas { refs.clear(); refs.shrink_to_fit(); - if (depth <= 5) { + if (depth <= 5 && parallelBuild) { auto leftLambda = [&]() { if (!leftRefs.size()) return; leftChild = new BVHBuilder(split.leftAABB, depth + 1, leftRefs.size(), minOverlap, binCount); - leftChild->Build(leftRefs, data); + leftChild->Build(leftRefs, data, parallelBuild); }; auto rightLambda = [&]() { if (!rightRefs.size()) return; rightChild = new BVHBuilder(split.rightAABB, depth + 1, rightRefs.size(), minOverlap, binCount); - rightChild->Build(rightRefs, data); + rightChild->Build(rightRefs, data, parallelBuild); }; auto leftBuilderFuture = std::async(leftLambda); @@ -347,18 +353,18 @@ namespace Atlas { else { if (leftRefs.size()) { leftChild = new BVHBuilder(split.leftAABB, depth + 1, leftRefs.size(), minOverlap, binCount); - leftChild->Build(leftRefs, data); + leftChild->Build(leftRefs, data, parallelBuild); } if (rightRefs.size()) { rightChild = new BVHBuilder(split.rightAABB, depth + 1, rightRefs.size(), minOverlap, binCount); - rightChild->Build(rightRefs, data); + rightChild->Build(rightRefs, data, parallelBuild); } } } - void BVHBuilder::Build(std::vector& refs) { + void BVHBuilder::Build(std::vector& refs, bool parallelBuild) { // Create leaf node if (refs.size() == 1) { @@ -390,17 +396,17 @@ namespace Atlas { refs.clear(); refs.shrink_to_fit(); - if (depth <= 5) { + if (depth <= 5 && parallelBuild) { auto leftLambda = [&]() { if (!leftRefs.size()) return; leftChild = new BVHBuilder(split.leftAABB, depth + 1, leftRefs.size(), binCount); - leftChild->Build(leftRefs); + leftChild->Build(leftRefs, parallelBuild); }; auto rightLambda = [&]() { if (!rightRefs.size()) return; rightChild = new BVHBuilder(split.rightAABB, depth + 1, rightRefs.size(), binCount); - rightChild->Build(rightRefs); + rightChild->Build(rightRefs, parallelBuild); }; auto leftBuilderFuture = std::async(leftLambda); @@ -413,12 +419,12 @@ namespace Atlas { else { if (leftRefs.size()) { leftChild = new BVHBuilder(split.leftAABB, depth + 1, leftRefs.size(), binCount); - leftChild->Build(leftRefs); + leftChild->Build(leftRefs, parallelBuild); } if (rightRefs.size()) { rightChild = new BVHBuilder(split.rightAABB, depth + 1, rightRefs.size(), binCount); - rightChild->Build(rightRefs); + rightChild->Build(rightRefs, parallelBuild); } } @@ -438,6 +444,10 @@ namespace Atlas { const auto nodeIdx = nodes.size(); nodes.push_back(BVHNode()); + // Reorder such that shadow rays hit large surface are first + if (leftChild->aabb.GetSurfaceArea() < rightChild->aabb.GetSurfaceArea()) + std::swap(leftChild, rightChild); + // Flatten recursively if (leftChild) { auto leaf = leftChild->refs.size() > 0; @@ -818,26 +828,51 @@ namespace Atlas { Split split; auto dimensions = aabb.max - aabb.min; - auto axis = dimensions.x > dimensions.y ? dimensions.x > dimensions.z ? 0 : 2 : - dimensions.y > dimensions.z ? 1 : 2; - std::sort(refs.begin(), refs.end(), [&](const Ref& ref0, const Ref& ref1) { - auto center0 = ref0.aabb.max[axis] - ref0.aabb.min[axis]; - auto center1 = ref1.aabb.max[axis] - ref1.aabb.min[axis]; - return center0 < center1; - }); - - auto splitIdx = uint32_t(refs.size() / 2); - - for (uint32_t i = 0; i < splitIdx; i++) { - const auto& ref = refs[i]; - split.leftAABB.Grow(ref.aabb); - leftRefs.push_back(ref); + + auto axis = 0; + axis = dimensions.y > dimensions[axis] ? 1 : axis; + axis = dimensions.z > dimensions[axis] ? 2 : axis; + + auto splitCutoff = aabb.min[axis] + dimensions[axis] / 2.0f; + + for (auto& ref : refs) { + auto center = (ref.aabb.max[axis] - ref.aabb.min[axis]) * 0.5f + ref.aabb.min[axis]; + if (center < splitCutoff) { + split.leftAABB.Grow(ref.aabb); + leftRefs.push_back(ref); + } + else { + split.rightAABB.Grow(ref.aabb); + rightRefs.push_back(ref); + } } - for (uint32_t i = splitIdx; i < uint32_t(refs.size()); i++) { - const auto& ref = refs[i]; - split.rightAABB.Grow(ref.aabb); - rightRefs.push_back(ref); + // Use this as a fallback, otherwise we might get endless recursions + if (!leftRefs.size() || !rightRefs.size()) { + split = Split(); + + leftRefs.clear(); + rightRefs.clear(); + + std::sort(refs.begin(), refs.end(), [&](const Ref& ref0, const Ref& ref1) { + auto center0 = ref0.aabb.max[axis] - ref0.aabb.min[axis]; + auto center1 = ref1.aabb.max[axis] - ref1.aabb.min[axis]; + return center0 < center1; + }); + + auto splitIdx = uint32_t(refs.size() / 2); + + for (uint32_t i = 0; i < splitIdx; i++) { + const auto& ref = refs[i]; + split.leftAABB.Grow(ref.aabb); + leftRefs.push_back(ref); + } + + for (uint32_t i = splitIdx; i < uint32_t(refs.size()); i++) { + const auto& ref = refs[i]; + split.rightAABB.Grow(ref.aabb); + rightRefs.push_back(ref); + } } return split; diff --git a/src/engine/volume/BVH.h b/src/engine/volume/BVH.h index 7ef42f2c5..4e249e213 100644 --- a/src/engine/volume/BVH.h +++ b/src/engine/volume/BVH.h @@ -49,9 +49,9 @@ namespace Atlas { ~BVHBuilder(); - void Build(std::vector& refs, const std::vector& data); + void Build(std::vector& refs, const std::vector& data, bool parallelBuild); - void Build(std::vector& refs); + void Build(std::vector& refs, bool parallelBuild); void Flatten(std::vector& nodes, std::vector& refs); @@ -120,9 +120,9 @@ namespace Atlas { public: BVH() = default; - BVH(std::vector& aabbs, std::vector& data); + BVH(const std::vector& aabbs, const std::vector& data, bool parallelBuild = true); - BVH(std::vector& aabbs); + BVH(const std::vector& aabbs, bool parallelBuild = true); bool GetIntersection(std::vector>& stack, Ray ray, BVHTriangle& closest, glm::vec3& intersection); diff --git a/src/tests/App.cpp b/src/tests/App.cpp new file mode 100644 index 000000000..21d1fce30 --- /dev/null +++ b/src/tests/App.cpp @@ -0,0 +1,401 @@ +#include "App.h" + +#include +#include + +const Atlas::EngineConfig Atlas::EngineInstance::engineConfig = { + .assetDirectory = "../../../data", + .shaderDirectory = "shader", + .validationLayerSeverity = Log::SEVERITY_MEDIUM, +}; + +void App::LoadContent(AppConfiguration config) { + + this->config = config; + + renderTarget = Atlas::RenderTarget(1920, 1080); + pathTraceTarget = Atlas::Renderer::PathTracerRenderTarget(1920, 1080); + + auto icon = Atlas::Texture::Texture2D("icon.png"); + window.SetIcon(&icon); + + loadingTexture = Atlas::CreateRef("loading.png"); + + font = Atlas::Font("font/roboto.ttf", 22, 5); + + camera = Atlas::Camera(47.0f, 2.0f, 1.0f, 400.0f, + glm::vec3(30.0f, 25.0f, 0.0f), glm::vec2(-3.14f / 2.0f, 0.0f)); + + scene = Atlas::CreateRef(glm::vec3(-2048.0f), glm::vec3(2048.0f)); + + mouseHandler = Atlas::Input::MouseHandler(&camera, 1.5f, 6.0f); + keyboardHandler = Atlas::Input::KeyboardHandler(&camera, 7.0f, 6.0f); + + Atlas::Events::EventManager::KeyboardEventDelegate.Subscribe( + [this](Atlas::Events::KeyboardEvent event) { + if (event.keyCode == AE_KEY_ESCAPE) { + Exit(); + } + if (event.keyCode == AE_KEY_F11 && event.state == AE_BUTTON_RELEASED) { + renderUI = !renderUI; + } + if (event.keyCode == AE_KEY_LSHIFT && event.state == AE_BUTTON_PRESSED) { + keyboardHandler.speed = cameraSpeed * 4.0f; + } + if (event.keyCode == AE_KEY_LSHIFT && event.state == AE_BUTTON_RELEASED) { + keyboardHandler.speed = cameraSpeed; + } + }); + + Atlas::PipelineManager::EnableHotReload(); + + directionalLight = Atlas::CreateRef(AE_MOVABLE_LIGHT); + directionalLight->direction = glm::vec3(0.0f, -1.0f, 0.33f); + directionalLight->intensity = 100.0f; + directionalLight->color = glm::vec3(255, 236, 209) / 255.0f; + glm::mat4 orthoProjection = glm::ortho(-100.0f, 100.0f, -70.0f, 120.0f, -120.0f, 120.0f); + directionalLight->AddShadow(200.0f, 3.0f, 4096, glm::vec3(0.0f), orthoProjection); + directionalLight->AddVolumetric(10, 0.28f); + + scene->sky.sun = directionalLight; + + scene->ao = Atlas::CreateRef(16); + + if (config.reflection) { + scene->reflection = Atlas::CreateRef(1); + scene->reflection->useShadowMap = true; + } + + if (config.ddgi) { + scene->irradianceVolume = std::make_shared( + Atlas::Volume::AABB(glm::vec3(-100.0f), glm::vec3(100.0f)), glm::ivec3(20)); + scene->irradianceVolume->SetRayCount(128, 32); + scene->irradianceVolume->strength = 1.5f; + } + + if (config.ssgi) { + scene->ssgi = Atlas::CreateRef(); + } + + if (config.fog) { + scene->fog = Atlas::CreateRef(); + scene->fog->enable = true; + scene->fog->density = 0.0068f; + scene->fog->heightFalloff = 0.0284f; + scene->fog->height = 0.0f; + } + + if (config.clouds) { + scene->sky.clouds = Atlas::CreateRef(); + scene->sky.clouds->minHeight = 1400.0f; + scene->sky.clouds->maxHeight = 1700.0f; + scene->sky.clouds->castShadow = false; + } + + scene->sky.atmosphere = Atlas::CreateRef(); + + if (config.taa) { + scene->postProcessing.taa = Atlas::PostProcessing::TAA(0.99f); + } + + if (config.sharpen) { + scene->postProcessing.sharpen.enable = true; + scene->postProcessing.sharpen.factor = 0.15f; + } + + if (config.sss) { + scene->sss = Atlas::CreateRef(); + } + + if (config.ocean) { + scene->ocean = Atlas::CreateRef(9, 4096.0f, + glm::vec3(0.0f, 5.0f, 0.0f), 512, 86); + } + + if (config.exampleRenderer) { + exampleRenderer.Init(graphicsDevice); + } + + LoadScene(); + + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + imguiWrapper.Load(&window); + +} + +void App::UnloadContent() { + + UnloadScene(); + imguiWrapper.Unload(); + +} + +void App::Update(float deltaTime) { + + if (sceneReload) { + UnloadScene(); + LoadScene(); + sceneReload = false; + } + + const ImGuiIO& io = ImGui::GetIO(); + + imguiWrapper.Update(&window, deltaTime); + + if (io.WantCaptureMouse) { + mouseHandler.lock = true; + } + else { + mouseHandler.lock = false; + } + + mouseHandler.Update(&camera, deltaTime); + keyboardHandler.Update(&camera, deltaTime); + + if (rotateCamera) { + camera.rotation.y += rotateCameraSpeed * cos(Atlas::Clock::Get()); + mouseHandler.Reset(&camera); + } + + if(moveCamera) { + camera.location += camera.right * moveCameraSpeed * cos(Atlas::Clock::Get()); + mouseHandler.Reset(&camera); + } + + camera.UpdateView(); + camera.UpdateProjection(); + + scene->Update(&camera, deltaTime); + + CheckLoadScene(); + + if (frameCount > FRAME_DATA_COUNT + 1) { + Exit(); + } + +} + +void App::Render(float deltaTime) { + + static bool pathTrace = false; + +#ifndef AE_HEADLESS + /* + auto windowFlags = window.GetFlags(); + if (windowFlags & AE_WINDOW_HIDDEN || windowFlags & AE_WINDOW_MINIMIZED || !(windowFlags & AE_WINDOW_SHOWN)) { + return; + } + */ +#endif + + if (!loadingComplete) { + DisplayLoadingScreen(deltaTime); + return; + } + + frameCount++; + + if (config.resize && frameCount == 2) { + SetResolution(2560, 1440); + } + if (config.recreateSwapchain && frameCount == 2) { + Atlas::Graphics::GraphicsDevice::DefaultDevice->CreateSwapChain(); + } + if (config.minimizeWindow && frameCount == 2) { + window.Minimize(); + } + if (config.minimizeWindow && frameCount == 3) { + window.Maximize(); + } + + if (config.exampleRenderer) { + exampleRenderer.Render(&camera); + } + else if (pathTrace) { + viewport.Set(0, 0, pathTraceTarget.GetWidth(), pathTraceTarget.GetHeight()); + mainRenderer->PathTraceScene(&viewport, &pathTraceTarget, &camera, scene.get()); + } + else { + mainRenderer->RenderScene(&viewport, &renderTarget, &camera, scene.get()); + } + + ImGui::NewFrame(); + ImGui::ShowDemoWindow(); + ImGui::Render(); + + imguiWrapper.Render(); + +} + +void App::DisplayLoadingScreen(float deltaTime) { + + auto commandList = graphicsDevice->GetCommandList(); + + commandList->BeginCommands(); + graphicsDevice->swapChain->colorClearValue.color = {0.0f, 0.0f, 0.0f, 1.0f}; + commandList->BeginRenderPass(graphicsDevice->swapChain, true); + + auto windowSize = window.GetDrawableSize(); + + float width = float(loadingTexture->width); + float height = float(loadingTexture->height); + + float x = windowSize.x / 2 - width / 2; + float y = windowSize.y / 2 - height / 2; + + static float rotation = 0.0f; + + rotation += deltaTime * abs(sin(Atlas::Clock::Get())) * 10.0f; + + mainRenderer->textureRenderer.RenderTexture2D(commandList, &viewport, + loadingTexture.get(), x, y, width, height, rotation); + + float textWidth, textHeight; + font.ComputeDimensions("Loading...", 2.0f, &textWidth, &textHeight); + + x = windowSize.x / 2 - textWidth / 2; + y = windowSize.y / 2 - textHeight / 2 + float(loadingTexture->height) + 20.0f; + + viewport.Set(0, 0, windowSize.x, windowSize.y); + mainRenderer->textRenderer.Render(commandList, &viewport, &font, + "Loading...", x, y, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), 2.0f); + + commandList->EndRenderPass(); + commandList->EndCommands(); + + graphicsDevice->SubmitCommandList(commandList); + +} + +bool App::LoadScene() { + + bool successful = false; + loadingComplete = false; + + using namespace Atlas::Loader; + + meshes.reserve(3); + glm::mat4 transform = glm::scale(glm::mat4(1.0f), glm::vec3(.05f)); + auto sponzaMesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( + "sponza/sponza.obj", ModelLoader::LoadMesh, false, transform, 2048 + ); + meshes.push_back(sponzaMesh); + + transform = glm::scale(glm::mat4(1.0f), glm::vec3(1.f)); + auto wallMesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( + "metallicwall.gltf", ModelLoader::LoadMesh, Atlas::Mesh::MeshMobility::Movable, + false, transform, 2048 + ); + meshes.push_back(wallMesh); + + transform = glm::scale(glm::mat4(1.0f), glm::vec3(1.f)); + auto sphereMesh = Atlas::ResourceManager::GetOrLoadResourceWithLoaderAsync( + "chromesphere.gltf", ModelLoader::LoadMesh, Atlas::Mesh::MeshMobility::Movable, + false, transform, 2048 + ); + meshes.push_back(sphereMesh); + + auto meshCount = 0; + for (const auto& mesh : meshes) { + if (meshCount == 10) { + meshCount++; + continue; + } + actors.push_back(Atlas::Actor::MovableMeshActor{ mesh, glm::translate(glm::mat4(1.0f), + glm::vec3(0.0f)) }); + + meshCount++; + } + + for (auto& actor : actors) { + scene->Add(&actor); + } + + camera.Update(); + scene->Update(&camera, 1.0f); + + // Reset input handlers + keyboardHandler.Reset(&camera); + mouseHandler.Reset(&camera); + + Atlas::Clock::ResetAverage(); + + return successful; + +} + +void App::UnloadScene() { + + for (auto& actor : actors) scene->Remove(&actor); + + actors.clear(); + meshes.clear(); + + actors.shrink_to_fit(); + meshes.shrink_to_fit(); + + scene->ClearRTStructures(); + + graphicsDevice->WaitForIdle(); + graphicsDevice->ForceMemoryCleanup(); + +} + +void App::CheckLoadScene() { + + if (!scene->IsFullyLoaded() || loadingComplete) + return; + + static std::future future; + + auto buildRTStructure = [&]() { + auto sceneMeshes = scene->GetMeshes(); + + for (auto& mesh : sceneMeshes) { + mesh->BuildBVH(); + } + }; + + if (!future.valid()) { + future = std::async(std::launch::async, buildRTStructure); + return; + } + else { + if (future.wait_for(std::chrono::microseconds(0)) != std::future_status::ready) { + return; + } + future.get(); + } + + auto sceneAABB = Atlas::Volume::AABB(glm::vec3(std::numeric_limits::max()), + glm::vec3(-std::numeric_limits::max())); + + auto sceneActors = scene->GetMeshActors(); + for (const auto& actor : sceneActors) { + sceneAABB.Grow(actor->aabb); + } + + for (const auto& mesh : meshes) { + mesh->invertUVs = true; + mesh->cullBackFaces = true; + } + + Atlas::Clock::ResetAverage(); + + loadingComplete = true; + +} + +void App::SetResolution(int32_t width, int32_t height) { + + renderTarget.Resize(width, height); + pathTraceTarget.Resize(width, height); + +} + +Atlas::EngineInstance* GetEngineInstance() { + + return new App(); + +} diff --git a/src/tests/App.h b/src/tests/App.h new file mode 100644 index 000000000..d088db3c9 --- /dev/null +++ b/src/tests/App.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define WINDOW_FLAGS AE_WINDOW_RESIZABLE | AE_WINDOW_HIGH_DPI + +struct AppConfiguration { + bool clouds = true; + bool sss = true; + bool fog = true; + bool taa = true; + bool sharpen = false; + bool ssgi = true; + bool ddgi = true; + bool reflection = true; + bool ocean = true; + bool resize = false; + bool recreateSwapchain = false; + bool minimizeWindow = false; + bool exampleRenderer = false; +}; + +class App : public Atlas::EngineInstance { + + template + using Ref = Atlas::Ref; + +public: + App() : EngineInstance("Atlas Engine Demo", 1920, 1080, WINDOW_FLAGS) {} + + virtual void LoadContent() final {}; + + virtual void LoadContent(AppConfiguration config) final; + + virtual void UnloadContent() final; + + virtual void Update(float deltaTime) final; + + virtual void Render(float deltaTime) final; + + + +private: + void DisplayLoadingScreen(float deltaTime); + + bool LoadScene(); + void UnloadScene(); + void CheckLoadScene(); + + void SetResolution(int32_t width, int32_t height); + + Atlas::Renderer::PathTracerRenderTarget pathTraceTarget; + Atlas::RenderTarget renderTarget; + Atlas::Viewport viewport; + + Atlas::Font font; + + Atlas::Camera camera; + + Ref scene; + Ref directionalLight; + + std::vector> meshes; + std::vector actors; + + Atlas::Lighting::EnvironmentProbe probe; + + Atlas::Input::MouseHandler mouseHandler; + Atlas::Input::KeyboardHandler keyboardHandler; + + Ref loadingTexture; + + Atlas::Renderer::ExampleRenderer exampleRenderer; + + bool renderUI = true; + bool renderEnvProbe = true; + bool spheresVisible = false; + + bool rotateCamera = false; + bool moveCamera = false; + float rotateCameraSpeed = 0.01f; + float moveCameraSpeed = 0.1f; + + int32_t windowWidth = 1920; + int32_t windowHeight = 1080; + + float cameraSpeed = 7.0f; + + bool loadingComplete = false; + bool sceneReload = false; + + int32_t frameCount = 0; + + ImguiWrapper imguiWrapper; + + AppConfiguration config; + +}; \ No newline at end of file diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt new file mode 100644 index 000000000..4c50b8147 --- /dev/null +++ b/src/tests/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.24) + +project(AtlasEngineTests) + +# Note: For this project, the root CMakeLists.txt turns +# the ATLAS_IMGUI and ATLAS_EXPORT_MAIN options on. + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/) + +set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin/tests/${CMAKE_BUILD_TYPE}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +file(GLOB_RECURSE TESTS_SOURCE_FILES + "*.cpp" + "*.c" + "*.h" + "*.hpp" + ) + +# Required: Set both the source and dependency directories +# as include directories +include_directories(../engine) +include_directories(../../libs) + +foreach(SOURCE_FILE IN ITEMS ${TESTS_SOURCE_FILES}) + if (IS_ABSOLUTE "${SOURCE_FILE}") + file(RELATIVE_PATH SOURCE_FILE_REL "${CMAKE_CURRENT_SOURCE_DIR}" "${SOURCE_FILE}") + else() + set(SOURCE_FILE_REL "${SOURCE_FILE}") + endif() + get_filename_component(SOURCE_PATH "${SOURCE_FILE_REL}" PATH) + string(REPLACE "/" "\\" SOURCE_PATH_CONVERTED "${SOURCE_PATH}") + source_group("${SOURCE_PATH_CONVERTED}" FILES "${SOURCE_FILE}") +endforeach() + +# We want to make sure that the linker searches for local libraries first +if (UNIX AND NOT APPLE AND NOT ANDROID) +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'") +endif() + +# We use the exported main file from the AtlasEngine library to able to use +# the app class. Alternatively, you can write a main function yourself. +# To export the main file the ATLAS_EXPORT_MAIN option has to be turned on. +add_executable(${PROJECT_NAME} ${TESTS_SOURCE_FILES}) +# Required: Add the compile definitions of the library, such that includes work properly +target_compile_definitions(${PROJECT_NAME} PUBLIC ${ATLAS_ENGINE_COMPILE_DEFINITIONS}) +# We want to use both ImGui and the AtlasEngine. For ImGui, the ATLAS_IMGUI option +# needs to be turned on. +target_link_libraries (${PROJECT_NAME} AtlasEngine ImguiExtension GTest::gmock GTest::gtest) \ No newline at end of file diff --git a/src/tests/Main.cpp b/src/tests/Main.cpp new file mode 100644 index 000000000..1ce5188c9 --- /dev/null +++ b/src/tests/Main.cpp @@ -0,0 +1,117 @@ +#include +#include "Engine.h" +#include "EngineInstance.h" +#include "graphics/Instance.h" +#include "common/Path.h" +#include "App.h" + +#if defined(AE_OS_ANDROID) || defined(AE_OS_MACOS) || defined(AE_OS_LINUX) +#include +#endif + +#ifdef AE_OS_WINDOWS +#include +#include +#endif + +extern Atlas::EngineInstance* GetEngineInstance(); + +// The fixture for testing class Foo. +class EngineEndToEndTest : public testing::TestWithParam { +protected: + void SetUp() override { + graphicsInstance = Atlas::Graphics::Instance::DefaultInstance; + graphicsDevice = Atlas::Graphics::GraphicsDevice::DefaultDevice; + + engineInstance = GetEngineInstance(); + ASSERT_NE(engineInstance, nullptr); + } + + void TearDown() override { + delete engineInstance; + + Atlas::PipelineManager::Clear(); + + graphicsDevice->ForceMemoryCleanup(); + } + + Atlas::Graphics::Instance* graphicsInstance = nullptr; + Atlas::Graphics::GraphicsDevice* graphicsDevice = nullptr; + Atlas::EngineInstance* engineInstance = nullptr; + +public: + static void SetUpTestSuite() { + Atlas::Engine::Init(Atlas::EngineInstance::engineConfig); + + auto graphicsInstance = Atlas::Graphics::Instance::DefaultInstance; + ASSERT_EQ(graphicsInstance->isComplete, true); + } + + static void TearDownTestSuite() { + Atlas::Engine::Shutdown(); + delete Atlas::Graphics::Instance::DefaultInstance; + } + +}; + +TEST_P(EngineEndToEndTest, DemoTest) { + ASSERT_NO_FATAL_FAILURE({ + + bool quit = false; + Atlas::Events::EventManager::QuitEventDelegate.Subscribe( + [&quit]() { + quit = true; + }); + + dynamic_cast(engineInstance)->LoadContent(GetParam()); + + while (!quit) { + + Atlas::Engine::Update(); + + auto deltaTime = Atlas::Clock::GetDelta(); + + engineInstance->Update(); + + engineInstance->Update(deltaTime); + engineInstance->Render(deltaTime); + + graphicsDevice->CompleteFrame(); + + } + + engineInstance->UnloadContent(); + + }); +} + +auto testingValues = testing::Values( + AppConfiguration { .sss = false }, + AppConfiguration { .clouds = false }, + AppConfiguration { .fog = false }, + AppConfiguration { .taa = false }, + AppConfiguration { .ssgi = false }, + AppConfiguration { .ocean = false }, +#ifdef AE_BINDLESS + AppConfiguration { .ddgi = false }, + AppConfiguration { .reflection = false }, +#endif + AppConfiguration { .sharpen = false }, + AppConfiguration { .recreateSwapchain = true }, + AppConfiguration { .resize = true }, + AppConfiguration { .exampleRenderer = true }, + AppConfiguration{ .minimizeWindow = true } + ); + +INSTANTIATE_TEST_SUITE_P(DemoTestSuite, EngineEndToEndTest, testingValues); + +int main(int argc, char** argv) { + +#if defined(AE_OS_MACOS) && defined(AE_BINDLESS) + setenv("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "2", 1); +#endif + + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/vcpkg.json b/vcpkg.json index 654bd56e5..46b3a154d 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -25,12 +25,14 @@ "assimp", "vulkan-memory-allocator", "volk", + "vulkan-headers", { "name": "glslang", "features": [] }, "spirv-reflect", - "spirv-tools" + "spirv-tools", + "gtest" ], "overrides": [ { "name": "glm", "version": "0.9.8.0" }, @@ -38,6 +40,8 @@ { "name": "spirv-tools", "version": "2022.1#0" }, { "name": "glslang", "version": "11.11.0#1" }, { "name": "sdl2", "version": "2.28.2" }, - { "name": "vulkan-memory-allocator", "version": "3.0.1#3" } + { "name": "vulkan-memory-allocator", "version": "3.0.1#3" }, + { "name": "volk", "version": "1.3.250#0" }, + { "name": "vulkan-headers", "version": "1.3.268.0#0" } ] } From 3e739325f7d4b87826c607f3ee2d1160fc51dade Mon Sep 17 00:00:00 2001 From: Simon Tippe Date: Tue, 9 Jan 2024 01:19:52 +0100 Subject: [PATCH 3/3] Fixes and improvements (#43) * Reactivated hardware raytracing * Fixed temporal filter for reflections * Fixed some AO/SSGI issues * Fixed another SSGI issue --- data/shader/deferred/indirect.csh | 17 ++- data/shader/reflection/temporal.csh | 100 +++++++++++------- src/demo/App.cpp | 2 + src/engine/graphics/GraphicsDevice.cpp | 2 +- src/engine/renderer/IndirectLightRenderer.cpp | 9 +- 5 files changed, 79 insertions(+), 51 deletions(-) diff --git a/data/shader/deferred/indirect.csh b/data/shader/deferred/indirect.csh index 25b02874b..bbdf7cf5a 100644 --- a/data/shader/deferred/indirect.csh +++ b/data/shader/deferred/indirect.csh @@ -90,6 +90,8 @@ float UpsampleAo2x(float referenceDepth) { float invocationDepths[9]; + referenceDepth = ConvertDepthToViewSpaceDepth(referenceDepth); + for (uint i = 0; i < 9; i++) { int sharedMemoryOffset = Flatten2D(pixel + offsets[i], unflattenedDepthDataSize); invocationDepths[i] = depths[sharedMemoryOffset]; @@ -222,19 +224,24 @@ void main() { indirectSpecular *= EvaluateIndirectSpecularBRDF(surface); indirect = (indirectDiffuse + indirectSpecular) * surface.material.ao; +#ifdef SSGI + vec4 ssgi = UpsampleGi2x(depth, texCoord); +#endif + // This normally only accounts for diffuse occlusion, we need seperate terms // for diffuse and specular. #ifdef AO float occlusionFactor = Uniforms.aoEnabled > 0 ? Uniforms.aoDownsampled2x > 0 ? UpsampleAo2x(depth) : texture(aoTexture, texCoord).r : 1.0; -#ifdef SSGI - vec4 ssgi = UpsampleGi2x(depth, texCoord); - occlusionFactor = ssgi.a; -#endif + indirect *= vec3(pow(occlusionFactor, Uniforms.aoStrength)); +#endif #ifdef SSGI - indirect += EvaluateIndirectDiffuseBRDF(surface) * ssgi.rgb; + // Only apply SSGI ao if normal AO is turned off +#ifndef AO + indirect *= vec3(pow(ssgi.a, Uniforms.aoStrength)); #endif + indirect += EvaluateIndirectDiffuseBRDF(surface) * ssgi.rgb; #endif } diff --git a/data/shader/reflection/temporal.csh b/data/shader/reflection/temporal.csh index 5480747f3..06bcba7b8 100644 --- a/data/shader/reflection/temporal.csh +++ b/data/shader/reflection/temporal.csh @@ -1,3 +1,5 @@ +//#define BICUBIC_FILTER + #include <../common/utility.hsh> #include <../common/convert.hsh> #include <../common/PI.hsh> @@ -221,12 +223,37 @@ bool SampleHistory(ivec2 pixel, vec2 historyPixel, out vec4 history, out vec4 hi } -void SampleCatmullRom(ivec2 pixel, vec2 uv, out vec4 history) { +float IsHistoryPixelValid(ivec2 pixel, float linearDepth, vec3 normal) { + + float confidence = 1.0; + + vec3 historyNormal = DecodeNormal(texelFetch(historyNormalTexture, pixel, 0).rg); + confidence *= pow(abs(dot(historyNormal, normal)), 16.0); + + float depthPhi = max(1.0, abs(0.25 * linearDepth)); + float historyDepth = texelFetch(historyDepthTexture, pixel, 0).r; + float historyLinearDepth = historyDepth; + confidence *= min(1.0 , exp(-abs(linearDepth - historyLinearDepth))); + + return confidence > 0.1 ? 1.0 : 0.0; + +} + +vec4 GetCatmullRomSample(ivec2 pixel, inout float weight, float linearDepth, vec3 normal) { + + pixel = clamp(pixel, ivec2(0), ivec2(imageSize(resolveImage) - 1)); + + weight *= IsHistoryPixelValid(pixel, linearDepth, normal); + + return texelFetch(historyTexture, pixel, 0) * weight; + +} + +bool SampleCatmullRom(ivec2 pixel, vec2 uv, out vec4 history) { + + vec3 normal = DecodeNormal(texelFetch(normalTexture, pixel, 0).rg); + float depth = texelFetch(depthTexture, pixel, 0).r; - // http://advances.realtimerendering.com/s2016/Filmic%20SMAA%20v7.pptx - // Credit: Jorge Jimenez (SIGGRAPH 2016) - // Ignores the 4 corners of the 4x4 grid - // Learn more: http://vec3.ca/bicubic-filtering-in-fewer-taps/ vec2 position = uv * resolution; vec2 center = floor(position - 0.5) + 0.5; @@ -239,45 +266,35 @@ void SampleCatmullRom(ivec2 pixel, vec2 uv, out vec4 history) { vec2 w3 = 0.5 * (f3 - f2); vec2 w2 = 1.0 - w0 - w1 - w3; - vec2 w12 = w1 + w2; - - vec2 tc0 = (center - 1.0) * invResolution; - vec2 tc12 = (center + w2 / w12) * invResolution; - vec2 tc3 = (center + 2.0) * invResolution; - - vec2 uv0 = clamp(vec2(tc12.x, tc0.y), vec2(0.0), vec2(1.0)) * resolution - vec2(0.5); - vec2 uv1 = clamp(vec2(tc0.x, tc12.y), vec2(0.0), vec2(1.0)) * resolution - vec2(0.5); - vec2 uv2 = clamp(vec2(tc12.x, tc12.y), vec2(0.0), vec2(1.0)) * resolution - vec2(0.5); - vec2 uv3 = clamp(vec2(tc3.x, tc12.y), vec2(0.0), vec2(1.0)) * resolution - vec2(0.5); - vec2 uv4 = clamp(vec2(tc12.x, tc3.y), vec2(0.0), vec2(1.0)) * resolution - vec2(0.5); + ivec2 uv0 = ivec2(center - 1.0); + ivec2 uv1 = ivec2(center); + ivec2 uv2 = ivec2(center + 1.0); + ivec2 uv3 = ivec2(center + 2.0); - float weight0 = w12.x * w0.y; - float weight1 = w0.x * w12.y; - float weight2 = w12.x * w12.y; - float weight3 = w3.x * w12.y; - float weight4 = w12.x * w3.y; - - vec4 sample0, sample1, sample2, sample3, sample4; - vec4 moments0, moments1, moments2, moments3, moments4; + ivec2 uvs[4] = { uv0, uv1, uv2, uv3 }; + vec2 weights[4] = { w0, w1, w2, w3 }; - SampleHistory(pixel, uv0, sample0, moments0); - SampleHistory(pixel, uv1, sample1, moments1); - SampleHistory(pixel, uv2, sample2, moments2); - SampleHistory(pixel, uv3, sample3, moments3); - SampleHistory(pixel, uv4, sample4, moments4); + history = vec4(0.0); - sample0 *= weight0; - sample1 *= weight1; - sample2 *= weight2; - sample3 *= weight3; - sample4 *= weight4; + float totalWeight = 0.0; - float totalWeight = weight0 + weight1 + - weight2 + weight3 + weight4; + for (int x = 0; x <= 3; x++) { + for (int y = 0; y <= 3; y++) { + float weight = weights[x].x * weights[y].y; + ivec2 uv = ivec2(uvs[x].x, uvs[y].y); - history = sample0 + sample1 + - sample2 + sample3 + sample4; + history += GetCatmullRomSample(uv, weight, depth, normal); + totalWeight += weight; + } + } + + if (totalWeight > 0.5) { + history /= totalWeight; + + return true; + } + return false; } void ComputeVarianceMinMax(out vec3 mean, out vec3 std) { @@ -341,7 +358,12 @@ void main() { vec4 historyMoments; valid = SampleHistory(pixel, historyPixel, history, historyMoments); - SampleCatmullRom(pixel, uv, history); +#ifdef BICUBIC_FILTER + // This should be implemented more efficiently, see + vec4 catmullRomHistory; + bool success = SampleCatmullRom(pixel, historyPixel, catmullRomHistory); + history = success && valid ? catmullRomHistory : history; +#endif vec3 historyColor = history.rgb; vec3 currentColor = texelFetch(currentTexture, pixel, 0).rgb; diff --git a/src/demo/App.cpp b/src/demo/App.cpp index 4aa1a184e..a609601fd 100644 --- a/src/demo/App.cpp +++ b/src/demo/App.cpp @@ -58,6 +58,8 @@ void App::LoadContent() { scene->ao = Atlas::CreateRef(16); scene->ao->rt = true; + // Use SSGI by default + scene->ao->enable = false; scene->reflection = Atlas::CreateRef(1); scene->reflection->useShadowMap = true; diff --git a/src/engine/graphics/GraphicsDevice.cpp b/src/engine/graphics/GraphicsDevice.cpp index 1124b7c23..2d0b33379 100644 --- a/src/engine/graphics/GraphicsDevice.cpp +++ b/src/engine/graphics/GraphicsDevice.cpp @@ -983,7 +983,7 @@ namespace Atlas { featureBuilder.Append(rtPipelineFeature); featureBuilder.Append(rayQueryFeature); - support.hardwareRayTracing = false; + support.hardwareRayTracing = true; } if (supportedExtensions.contains(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME)) { diff --git a/src/engine/renderer/IndirectLightRenderer.cpp b/src/engine/renderer/IndirectLightRenderer.cpp index f15fdf655..54d4c9cdd 100644 --- a/src/engine/renderer/IndirectLightRenderer.cpp +++ b/src/engine/renderer/IndirectLightRenderer.cpp @@ -30,10 +30,7 @@ namespace Atlas { auto reflectionEnabled = reflection && reflection->enable && rtDataValid; auto aoEnabled = ao && ao->enable && (!ao->rt || rtDataValid); auto ssgiEnabled = ssgi && ssgi->enable && (!ssgi->rt || rtDataValid); - - bool ssgiAo = ssgi && ssgi->enable && ssgi->enableAo; - aoEnabled |= ssgiAo; - + bool ssgiAo = ssgi && ssgi->enable && ssgi->enableAo; pipelineConfig.ManageMacro("DDGI", ddgiEnabled); pipelineConfig.ManageMacro("REFLECTION", reflectionEnabled); @@ -56,11 +53,11 @@ namespace Atlas { } auto uniforms = Uniforms{ - .aoEnabled = aoEnabled ? 1 : 0, + .aoEnabled = aoEnabled || ssgiAo ? 1 : 0, .aoDownsampled2x = ssgiAo ? target->GetGIResolution() == RenderResolution::HALF_RES : target->GetAOResolution() == RenderResolution::HALF_RES, .reflectionEnabled = reflectionEnabled ? 1 : 0, - .aoStrength = aoEnabled ? (ssgiAo ? ssgi->aoStrength / sqrt(ssgi->radius) : ao->strength) : 1.0f, + .aoStrength = aoEnabled || ssgiAo ? (aoEnabled ? ao->strength : ssgi->aoStrength / sqrt(ssgi->radius)) : 1.0f, .specularProbeMipLevels = int32_t(scene->sky.GetProbe() ? scene->sky.GetProbe()->cubemap.image->mipLevels : 1) }; uniformBuffer.SetData(&uniforms, 0);