diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3e9cb91e9..5f528a570 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,8 @@ env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE_DEBUG: Debug
BUILD_TYPE_RELEASE: Release
+ USE_CACHE: true
+ RUN_TESTS: true
jobs:
windows-build:
@@ -24,13 +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: 93895b28ea7bc8cda10f156c5d336f3fc070f8b1
+ 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: Install Vulkan SDK
+ uses: humbletim/install-vulkan-sdk@v1.1.1
+ with:
+ version: 1.3.261.1
+ cache: true
- name: Setup Microsoft Visual C++ CLI
uses: ilammy/msvc-dev-cmd@v1
@@ -41,21 +66,34 @@ 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
+ - 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:
@@ -64,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
@@ -80,10 +147,11 @@ jobs:
THIRDPARTY.md
${{ github.workspace }}/bin/**/AtlasEngineDemo.exe
${{ github.workspace }}/bin/**/*.dll
+ !${{ github.workspace }}/bin/tests
!**/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:
@@ -93,13 +161,28 @@ 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: 93895b28ea7bc8cda10f156c5d336f3fc070f8b1
+ committish: 6c937c32233bdf295ab2140dbce97fd00084a5f3
+ cache: ${{ env.USE_CACHE }}
- name: Setup Ninja
uses: ashutoshvarma/setup-ninja@master
@@ -107,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-use-cache: true
-
+ version: 1.3.261.1
+ cache: true
+
- name: Run scripts
shell: bash
# Add additional scripting steps here
@@ -124,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
@@ -133,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
@@ -149,6 +276,7 @@ jobs:
THIRDPARTY.md
${{ github.workspace }}/bin/**/AtlasEngineDemo
${{ github.workspace }}/bin/**/*.so*
+ !${{ github.workspace }}/bin/tests
!**/CMakeFiles
macos-build:
@@ -164,11 +292,26 @@ 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: 93895b28ea7bc8cda10f156c5d336f3fc070f8b1
+ committish: 6c937c32233bdf295ab2140dbce97fd00084a5f3
+ cache: ${{ env.USE_CACHE }}
- name: Setup Ninja
uses: ashutoshvarma/setup-ninja@master
@@ -176,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-use-cache: true
+ version: 1.3.261.1
+ cache: true
- name: Run scripts
shell: bash
@@ -192,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
@@ -201,6 +362,47 @@ 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
+ 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*
+ !${{ github.workspace }}/bin/tests
+ !**/CMakeFiles
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f0f82aee7..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: 93895b28ea7bc8cda10f156c5d336f3fc070f8b1
+ 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-use-cache: true
+ 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 }}
@@ -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: 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-use-cache: true
+ 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 d4ab4e05c..1176dd1f9 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
###################################################################
@@ -27,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})
@@ -52,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 ################################################################################
@@ -64,14 +78,19 @@ 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)
find_package(nlohmann_json CONFIG REQUIRED)
+if (ATLAS_TESTS)
+find_package(GTest CONFIG REQUIRED)
+endif()
+
add_subdirectory(${ATLAS_LOCATION})
if (ATLAS_IMGUI)
@@ -81,3 +100,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 448bb7a8c..031cec409 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
-
*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))*
+
*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
@@ -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 36e5520c5..56b525abf 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 000000000..445902a12
Binary files /dev/null and b/data/chromesphere.bin differ
diff --git a/data/chromesphere.gltf b/data/chromesphere.gltf
index 017d28af1..18926cadc 100644
--- a/data/chromesphere.gltf
+++ b/data/chromesphere.gltf
@@ -3,6 +3,10 @@
"generator":"Khronos glTF Blender I/O v3.6.27",
"version":"2.0"
},
+ "extensionsUsed":[
+ "KHR_materials_specular",
+ "KHR_materials_ior"
+ ],
"scene":0,
"scenes":[
{
@@ -15,20 +19,37 @@
"nodes":[
{
"mesh":0,
- "name":"Icosphere",
- "translation":[
- 0,
- 1.0394843816757202,
- 0
- ]
+ "name":"Icosphere"
}
],
"materials":[
{
"doubleSided":true,
+ "emissiveFactor":[
+ 0.007193333003669977,
+ 0.007193333003669977,
+ 0.007193333003669977
+ ],
+ "extensions":{
+ "KHR_materials_specular":{
+ "specularColorFactor":[
+ 2.371357614441398,
+ 2.371357614441398,
+ 2.371357614441398
+ ]
+ },
+ "KHR_materials_ior":{
+ "ior":1.4500000476837158
+ }
+ },
"name":"Chrome",
"pbrMetallicRoughness":{
- "metallicFactor":0,
+ "baseColorFactor":[
+ 0.800000011920929,
+ 0.800000011920929,
+ 0.800000011920929,
+ 1
+ ],
"roughnessFactor":0
}
}
@@ -39,12 +60,11 @@
"primitives":[
{
"attributes":{
- "COLOR_0":0,
- "POSITION":1,
- "NORMAL":2,
- "TEXCOORD_0":3
+ "POSITION":0,
+ "NORMAL":1,
+ "TEXCOORD_0":2
},
- "indices":4,
+ "indices":3,
"material":0
}
]
@@ -53,15 +73,8 @@
"accessors":[
{
"bufferView":0,
- "componentType":5123,
- "count":729,
- "normalized":true,
- "type":"VEC4"
- },
- {
- "bufferView":1,
"componentType":5126,
- "count":729,
+ "count":3840,
"max":[
1,
1,
@@ -75,19 +88,19 @@
"type":"VEC3"
},
{
- "bufferView":2,
+ "bufferView":1,
"componentType":5126,
- "count":729,
+ "count":3840,
"type":"VEC3"
},
{
- "bufferView":3,
+ "bufferView":2,
"componentType":5126,
- "count":729,
+ "count":3840,
"type":"VEC2"
},
{
- "bufferView":4,
+ "bufferView":3,
"componentType":5123,
"count":3840,
"type":"SCALAR"
@@ -96,39 +109,33 @@
"bufferViews":[
{
"buffer":0,
- "byteLength":5832,
+ "byteLength":46080,
"byteOffset":0,
"target":34962
},
{
"buffer":0,
- "byteLength":8748,
- "byteOffset":5832,
- "target":34962
- },
- {
- "buffer":0,
- "byteLength":8748,
- "byteOffset":14580,
+ "byteLength":46080,
+ "byteOffset":46080,
"target":34962
},
{
"buffer":0,
- "byteLength":5832,
- "byteOffset":23328,
+ "byteLength":30720,
+ "byteOffset":92160,
"target":34962
},
{
"buffer":0,
"byteLength":7680,
- "byteOffset":29160,
+ "byteOffset":122880,
"target":34963
}
],
"buffers":[
{
- "byteLength":36840,
- "uri":"data:application/octet-stream;base64,//++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP/////s63T7/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////tHr7GP////++bQAA/////75tAAD/////G+7///////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////xvu////////G+7///////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////xvu////////vm0AAP////++bQAA/////75tAAD/////vm0AAP////8b7v///////75tAAD/////G+7///////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////8Dp9fb/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////yRvMgX/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////8b7v///////75tAAD/////vm0AAP////++bQAA/////75tAAD/////G+7///////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////8b7v///////75tAAD/////vm0AAP////++bQAA/////xvu////////G+7///////++bQAA/////75tAAD/////vm0AAP////++bTIC/////xvu////////G+7///////8b7v///////xvu////////G+7///////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////8b7v///////zJ8kxn/////vm0AAP/////Rg6Ml/////xvu////////G+7///////++bQAA/////75tAAD/////vm0AAP/////s63T7/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////99zM2P////8b7v///////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP////++bQAA/////75tAAD/////vm0AAP//AAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAAAAAAAAAgL8AAACAVD45P/X55L7vlQY/u4KNvgD65L4mxFk/u4KNvgD65L4mxFk/Hvlkv3L55L4AAACAu4KNvgD65L4mxFm/VD45P/X55L7vlQa/u4KNPgD65D4mxFk/VD45v/X55D7vlQY/VD45v/X55D7vlQY/VD45v/X55D7vlQa/u4KNPgD65D4mxFm/HvlkP3L55D4AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAAAAAAAAAgD8AAACAQRCEvrb5DL9wOks/QRCEvrb5DL9wOks/w2huvi5TKL+ucDc/w2huvi5TKL+ucDc/RoFNvsWpQr9eHx4/RoFNvsWpQr9eHx4/v1omvnzEWb9h//8+v1omvnzEWb9h//8+6Jr2vbLDa78Qv70+6Jr2vbLDa78Qv70+O/CevY7Ld7/QlXQ+O/CevY7Ld7/QlXQ+pu0WvWgsfr89Qug9pu0WvWgsfr89Qug9XJLFPWYsfr/mio89XJLFPWYsfr/mio89qQ5QPovLd78iKRc+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KAFKARkBSgEbARkBKgFLAUoBSwFMAUoBSgFMARsBTAEdARsBLAFNAUsBTQFOAUsBSwFOAUwBTgFPAUwBTAFPAR0BTwEfAR0BLgFQAU0BUAFRAU0BTQFRAU4BUQFSAU4BTgFSAU8BUgFTAU8BTwFTAR8BUwEhAR8BMAFUAVABVAFVAVABUAFVAVEBVQFWAVEBUQFWAVIBVgFXAVIBUgFXAVMBVwFYAVMBUwFYASEBWAEjASEBMgFZAVQBWQFaAVQBVAFaAVUBWgFbAVUBVQFbAVYBWwFcAVYBVgFcAVcBXAFdAVcBVwFdAVgBXQFeAVgBWAFeASMBXgElASMBNAHnAFkB5wDmAFkBWQHmAFoB5gDlAFoBWgHlAFsB5QDkAFsBWwHkAFwB5ADjAFwBXAHjAF0B4wDiAF0BXQHiAF4B4gDhAF4BXgHhACUB4QAOACUBGgEMARUAHAFfARoBHgFgARwBIAFiAR4BIgFlASABJAFpASIBJgFuASQBGgFfAQwBXwEOAQwBHAFgAV8BYAFhAV8BXwFhAQ4BYQEQAQ4BHgFiAWABYgFjAWABYAFjAWEBYwFkAWEBYQFkARABZAESARABIAFlAWIBZQFmAWIBYgFmAWMBZgFnAWMBYwFnAWQBZwFoAWQBZAFoARIBaAEUARIBIgFpAWUBaQFqAWUBZQFqAWYBagFrAWYBZgFrAWcBawFsAWcBZwFsAWgBbAFtAWgBaAFtARQBbQEWARQBJAFuAWkBbgFvAWkBaQFvAWoBbwFwAWoBagFwAWsBcAFxAWsBawFxAWwBcQFyAWwBbAFyAW0BcgFzAW0BbQFzARYBcwEYARYBJgHgAG4B4ADfAG4BbgHfAG8B3wDeAG8BbwHeAHAB3gDdAHABcAHdAHEB3QDcAHEBcQHcAHIB3ADbAHIBcgHbAHMB2wDaAHMBcwHaABgB2gANABgBCwHvABEADQF0AQsBDwF1AQ0BEQF3AQ8BEwF6AREBFQF+ARMBFwGDARUBCwF0Ae8AdAHxAO8ADQF1AXQBdQF2AXQBdAF2AfEAdgHzAPEADwF3AXUBdwF4AXUBdQF4AXYBeAF5AXYBdgF5AfMAeQH1APMAEQF6AXcBegF7AXcBdwF7AXgBewF8AXgBeAF8AXkBfAF9AXkBeQF9AfUAfQH3APUAEwF+AXoBfgF/AXoBegF/AXsBfwGAAXsBewGAAXwBgAGBAXwBfAGBAX0BgQGCAX0BfQGCAfcAggH5APcAFQGDAX4BgwGEAX4BfgGEAX8BhAGFAX8BfwGFAYABhQGGAYABgAGGAYEBhgGHAYEBgQGHAYIBhwGIAYIBggGIAfkAiAH7APkAFwHZAIMB2QDYAIMBgwHYAIQB2ADXAIQBhAHXAIUB1wDWAIUBhQHWAIYB1gDVAIYBhgHVAIcB1QDUAIcBhwHUAIgB1ADTAIgBiAHTAPsA0wALAPsA8AAJARIA8gCJAfAA9ACKAfIA9gCMAfQA+ACPAfYA+gCTAfgA/ACYAfoA8ACJAQkBiQEHAQkB8gCKAYkBigGLAYkBiQGLAQcBiwEFAQcB9ACMAYoBjAGNAYoBigGNAYsBjQGOAYsBiwGOAQUBjgEDAQUB9gCPAYwBjwGQAYwBjAGQAY0BkAGRAY0BjQGRAY4BkQGSAY4BjgGSAQMBkgEBAQMB+ACTAY8BkwGUAY8BjwGUAZABlAGVAZABkAGVAZEBlQGWAZEBkQGWAZIBlgGXAZIBkgGXAQEBlwH/AAEB+gCYAZMBmAGZAZMBkwGZAZQBmQGaAZQBlAGaAZUBmgGbAZUBlQGbAZYBmwGcAZYBlgGcAZcBnAGdAZcBlwGdAf8AnQH9AP8A/ADSAJgB0gDRAJgBmAHRAJkB0QDQAJkBmQHQAJoB0ADPAJoBmgHPAJsBzwDOAJsBmwHOAJwBzgDNAJwBnAHNAJ0BzQDMAJ0BnQHMAP0AzAAQAP0AjADuABAAiwCeAYwAigCfAYsAiQChAYoAiACkAYkAhwCoAYgAhgCtAYcAjACeAe4AngHtAO4AiwCfAZ4BnwGgAZ4BngGgAe0AoAHsAO0AigChAZ8BoQGiAZ8BnwGiAaABogGjAaABoAGjAewAowHrAOwAiQCkAaEBpAGlAaEBoQGlAaIBpQGmAaIBogGmAaMBpgGnAaMBowGnAesApwHqAOsAiACoAaQBqAGpAaQBpAGpAaUBqQGqAaUBpQGqAaYBqgGrAaYBpgGrAacBqwGsAacBpwGsAeoArAHpAOoAhwCtAagBrQGuAagBqAGuAakBrgGvAakBqQGvAaoBrwGwAaoBqgGwAasBsAGxAasBqwGxAawBsQGyAawBrAGyAekAsgHoAOkAhgDEAK0BxADDAK0BrQHDAK4BwwDCAK4BrgHCAK8BwgDBAK8BrwHBALABwQDAALABsAHAALEBwAC/ALEBsQG/ALIBvwC+ALIBsgG+AOgAvgAPAOgAywDnAA8AygCzAcsAyQC0AcoAyAC2AckAxwC5AcgAxgC9AccAxQDCAcYAywCzAecAswHmAOcAygC0AbMBtAG1AbMBswG1AeYAtQHlAOYAyQC2AbQBtgG3AbQBtAG3AbUBtwG4AbUBtQG4AeUAuAHkAOUAyAC5AbYBuQG6AbYBtgG6AbcBugG7AbcBtwG7AbgBuwG8AbgBuAG8AeQAvAHjAOQAxwC9AbkBvQG+AbkBuQG+AboBvgG/AboBugG/AbsBvwHAAbsBuwHAAbwBwAHBAbwBvAHBAeMAwQHiAOMAxgDCAb0BwgHDAb0BvQHDAb4BwwHEAb4BvgHEAb8BxAHFAb8BvwHFAcABxQHGAcABwAHGAcEBxgHHAcEBwQHHAeIAxwHhAOIAxQC2AMIBtgC1AMIBwgG1AMMBtQC0AMMBwwG0AMQBtACzAMQBxAGzAMUBswCyAMUBxQGyAMYBsgCxAMYBxgGxAMcBsQCwAMcBxwGwAOEAsAAOAOEAvQDgAA4AvADIAb0AuwDJAbwAugDLAbsAuQDOAboAuADSAbkAtwDXAbgAvQDIAeAAyAHfAOAAvADJAcgByQHKAcgByAHKAd8AygHeAN8AuwDLAckBywHMAckByQHMAcoBzAHNAcoBygHNAd4AzQHdAN4AugDOAcsBzgHPAcsBywHPAcwBzwHQAcwBzAHQAc0B0AHRAc0BzQHRAd0A0QHcAN0AuQDSAc4B0gHTAc4BzgHTAc8B0wHUAc8BzwHUAdAB1AHVAdAB0AHVAdEB1QHWAdEB0QHWAdwA1gHbANwAuADXAdIB1wHYAdIB0gHYAdMB2AHZAdMB0wHZAdQB2QHaAdQB1AHaAdUB2gHbAdUB1QHbAdYB2wHcAdYB1gHcAdsA3AHaANsAtwChANcBoQCgANcB1wGgANgBoACfANgB2AGfANkBnwCeANkB2QGeANoBngCdANoB2gGdANsBnQCcANsB2wGcANwBnACbANwB3AGbANoAmwANANoArgDZAAwArADdAa4AqgDeAawAqADgAaoApgDjAagApADnAaYAogDsAaQArgDdAdkA3QHYANkArADeAd0B3gHfAd0B3QHfAdgA3wHXANgAqgDgAd4B4AHhAd4B3gHhAd8B4QHiAd8B3wHiAdcA4gHWANcAqADjAeAB4wHkAeAB4AHkAeEB5AHlAeEB4QHlAeIB5QHmAeIB4gHmAdYA5gHVANYApgDnAeMB5wHoAeMB4wHoAeQB6AHpAeQB5AHpAeUB6QHqAeUB5QHqAeYB6gHrAeYB5gHrAdUA6wHUANUApADsAecB7AHtAecB5wHtAegB7QHuAegB6AHuAekB7gHvAekB6QHvAeoB7wHwAeoB6gHwAesB8AHxAesB6wHxAdQA8QHTANQAogCTAOwBkwCSAOwB7AGSAO0BkgCRAO0B7QGRAO4BkQCQAO4B7gGQAO8BkACPAO8B7wGPAPABjwCOAPAB8AGOAPEBjgCNAPEB8QGNANMAjQALANMAmgDSAAsAmQDyAZoAmADzAZkAlwD1AZgAlgD4AZcAlQD8AZYAlAABApUAmgDyAdIA8gHRANIAmQDzAfIB8wH0AfIB8gH0AdEA9AHQANEAmAD1AfMB9QH2AfMB8wH2AfQB9gH3AfQB9AH3AdAA9wHPANAAlwD4AfUB+AH5AfUB9QH5AfYB+QH6AfYB9gH6AfcB+gH7AfcB9wH7Ac8A+wHOAM8AlgD8AfgB/AH9AfgB+AH9AfkB/QH+AfkB+QH+AfoB/gH/AfoB+gH/AfsB/wEAAvsB+wEAAs4AAALNAM4AlQABAvwBAQICAvwB/AECAv0BAgIDAv0B/QEDAv4BAwIEAv4B/gEEAv8BBAIFAv8B/wEFAgACBQIGAgACAAIGAs0ABgLMAM0AlACFAAEChQCEAAECAQKEAAIChACDAAICAgKDAAMCgwCCAAMCAwKCAAQCggCBAAQCBAKBAAUCgQCAAAUCBQKAAAYCgAB/AAYCBgJ/AMwAfwAQAMwAvgDLAA8AvwAHAr4AwAAIAr8AwQAKAsAAwgANAsEAwwARAsIAxAAWAsMAvgAHAssABwLKAMsAvwAIAgcCCAIJAgcCBwIJAsoACQLJAMoAwAAKAggCCgILAggCCAILAgkCCwIMAgkCCQIMAskADALIAMkAwQANAgoCDQIOAgoCCgIOAgsCDgIPAgsCCwIPAgwCDwIQAgwCDAIQAsgAEALHAMgAwgARAg0CEQISAg0CDQISAg4CEgITAg4CDgITAg8CEwIUAg8CDwIUAhACFAIVAhACEAIVAscAFQLGAMcAwwAWAhECFgIXAhECEQIXAhICFwIYAhICEgIYAhMCGAIZAhMCEwIZAhQCGQIaAhQCFAIaAhUCGgIbAhUCFQIbAsYAGwLFAMYAxAB+ABYCfgB9ABYCFgJ9ABcCfQB8ABcCFwJ8ABgCfAB7ABgCGAJ7ABkCewB6ABkCGQJ6ABoCegB5ABoCGgJ5ABsCeQB4ABsCGwJ4AMUAeAAJAMUAsAC9AA4AsQAcArAAsgAdArEAswAfArIAtAAiArMAtQAmArQAtgArArUAsAAcAr0AHAK8AL0AsQAdAhwCHQIeAhwCHAIeArwAHgK7ALwAsgAfAh0CHwIgAh0CHQIgAh4CIAIhAh4CHgIhArsAIQK6ALsAswAiAh8CIgIjAh8CHwIjAiACIwIkAiACIAIkAiECJAIlAiECIQIlAroAJQK5ALoAtAAmAiICJgInAiICIgInAiMCJwIoAiMCIwIoAiQCKAIpAiQCJAIpAiUCKQIqAiUCJQIqArkAKgK4ALkAtQArAiYCKwIsAiYCJgIsAicCLAItAicCJwItAigCLQIuAigCKAIuAikCLgIvAikCKQIvAioCLwIwAioCKgIwArgAMAK3ALgAtgB3ACsCdwB2ACsCKwJ2ACwCdgB1ACwCLAJ1AC0CdQB0AC0CLQJ0AC4CdABzAC4CLgJzAC8CcwByAC8CLwJyADACcgBxADACMAJxALcAcQAIALcAmwCvAA0AnAAxApsAnQAyApwAngA0Ap0AnwA3Ap4AoAA7Ap8AoQBAAqAAmwAxAq8AMQKtAK8AnAAyAjECMgIzAjECMQIzAq0AMwKrAK0AnQA0AjICNAI1AjICMgI1AjMCNQI2AjMCMwI2AqsANgKpAKsAngA3AjQCNwI4AjQCNAI4AjUCOAI5AjUCNQI5AjYCOQI6AjYCNgI6AqkAOgKnAKkAnwA7AjcCOwI8AjcCNwI8AjgCPAI9AjgCOAI9AjkCPQI+AjkCOQI+AjoCPgI/AjoCOgI/AqcAPwKlAKcAoABAAjsCQAJBAjsCOwJBAjwCQQJCAjwCPAJCAj0CQgJDAj0CPQJDAj4CQwJEAj4CPgJEAj8CRAJFAj8CPwJFAqUARQKjAKUAoQBiAEACYgBhAEACQAJhAEECYQBgAEECQQJgAEICYABfAEICQgJfAEMCXwBeAEMCQwJeAEQCXgBdAEQCRAJdAEUCXQBcAEUCRQJcAKMAXAAHAKMAjQCaAAsAjgBGAo0AjwBHAo4AkABJAo8AkQBMApAAkgBQApEAkwBVApIAjQBGApoARgKZAJoAjgBHAkYCRwJIAkYCRgJIApkASAKYAJkAjwBJAkcCSQJKAkcCRwJKAkgCSgJLAkgCSAJLApgASwKXAJgAkABMAkkCTAJNAkkCSQJNAkoCTQJOAkoCSgJOAksCTgJPAksCSwJPApcATwKWAJcAkQBQAkwCUAJRAkwCTAJRAk0CUQJSAk0CTQJSAk4CUgJTAk4CTgJTAk8CUwJUAk8CTwJUApYAVAKVAJYAkgBVAlACVQJWAlACUAJWAlECVgJXAlECUQJXAlICVwJYAlICUgJYAlMCWAJZAlMCUwJZAlQCWQJaAlQCVAJaApUAWgKUAJUAkwA4AFUCOAA3AFUCVQI3AFYCNwA2AFYCVgI2AFcCNgA1AFcCVwI1AFgCNQA0AFgCWAI0AFkCNAAzAFkCWQIzAFoCMwAyAFoCWgIyAJQAMgAFAJQAfwCMABAAgABbAn8AgQBcAoAAggBeAoEAgwBhAoIAhABlAoMAhQBqAoQAfwBbAowAWwKLAIwAgABcAlsCXAJdAlsCWwJdAosAXQKKAIsAgQBeAlwCXgJfAlwCXAJfAl0CXwJgAl0CXQJgAooAYAKJAIoAggBhAl4CYQJiAl4CXgJiAl8CYgJjAl8CXwJjAmACYwJkAmACYAJkAokAZAKIAIkAgwBlAmECZQJmAmECYQJmAmICZgJnAmICYgJnAmMCZwJoAmMCYwJoAmQCaAJpAmQCZAJpAogAaQKHAIgAhABqAmUCagJrAmUCZQJrAmYCawJsAmYCZgJsAmcCbAJtAmcCZwJtAmgCbQJuAmgCaAJuAmkCbgJvAmkCaQJvAocAbwKGAIcAhQA/AGoCPwA+AGoCagI+AGsCPgA9AGsCawI9AGwCPQA8AGwCbAI8AG0CPAA7AG0CbQI7AG4COwA6AG4CbgI6AG8COgA5AG8CbwI5AIYAOQAKAIYATQB+AAoASwBwAk0ASQBxAksARwBzAkkARQB2AkcAQwB6AkUAQQB/AkMATQBwAn4AcAJ9AH4ASwBxAnACcQJyAnACcAJyAn0AcgJ8AH0ASQBzAnECcwJ0AnECcQJ0AnICdAJ1AnICcgJ1AnwAdQJ7AHwARwB2AnMCdgJ3AnMCcwJ3AnQCdwJ4AnQCdAJ4AnUCeAJ5AnUCdQJ5AnsAeQJ6AHsARQB6AnYCegJ7AnYCdgJ7AncCewJ8AncCdwJ8AngCfAJ9AngCeAJ9AnkCfQJ+AnkCeQJ+AnoAfgJ5AHoAQwB/AnoCfwKAAnoCegKAAnsCgAKBAnsCewKBAnwCgQKCAnwCfAKCAn0CggKDAn0CfQKDAn4CgwKEAn4CfgKEAnkAhAJ4AHkAQQBvAH8CbwBtAH8CfwJtAIACbQBrAIACgAJrAIECawBpAIECgQJpAIICaQBnAIICggJnAIMCZwBlAIMCgwJlAIQCZQBjAIQChAJjAHgAYwAJAHgAZAB3AAkAZgCFAmQAaACGAmYAagCIAmgAbACLAmoAbgCPAmwAcACUAm4AZACFAncAhQJ2AHcAZgCGAoUChgKHAoUChQKHAnYAhwJ1AHYAaACIAoYCiAKJAoYChgKJAocCiQKKAocChwKKAnUAigJ0AHUAagCLAogCiwKMAogCiAKMAokCjAKNAokCiQKNAooCjQKOAooCigKOAnQAjgJzAHQAbACPAosCjwKQAosCiwKQAowCkAKRAowCjAKRAo0CkQKSAo0CjQKSAo4CkgKTAo4CjgKTAnMAkwJyAHMAbgCUAo8ClAKVAo8CjwKVApAClQKWApACkAKWApEClgKXApECkQKXApIClwKYApICkgKYApMCmAKZApMCkwKZAnIAmQJxAHIAcABaAJQCWgBYAJQClAJYAJUCWABWAJUClQJWAJYCVgBUAJYClgJUAJcCVABSAJcClwJSAJgCUgBQAJgCmAJQAJkCUABOAJkCmQJOAHEATgAIAHEATwBiAAgAUQCaAk8AUwCbAlEAVQCdAlMAVwCgAlUAWQCkAlcAWwCpAlkATwCaAmIAmgJhAGIAUQCbApoCmwKcApoCmgKcAmEAnAJgAGEAUwCdApsCnQKeApsCmwKeApwCngKfApwCnAKfAmAAnwJfAGAAVQCgAp0CoAKhAp0CnQKhAp4CoQKiAp4CngKiAp8CogKjAp8CnwKjAl8AowJeAF8AVwCkAqACpAKlAqACoAKlAqECpQKmAqECoQKmAqICpgKnAqICogKnAqMCpwKoAqMCowKoAl4AqAJdAF4AWQCpAqQCqQKqAqQCpAKqAqUCqgKrAqUCpQKrAqYCqwKsAqYCpgKsAqcCrAKtAqcCpwKtAqgCrQKuAqgCqAKuAl0ArgJcAF0AWwAjAKkCIwAhAKkCqQIhAKoCIQAfAKoCqgIfAKsCHwAdAKsCqwIdAKwCHQAbAKwCrAIbAK0CGwAZAK0CrQIZAK4CGQAXAK4CrgIXAFwAFwAHAFwAOQBMAAoAOgCvAjkAOwCwAjoAPACyAjsAPQC1AjwAPgC5Aj0APwC+Aj4AOQCvAkwArwJKAEwAOgCwAq8CsAKxAq8CrwKxAkoAsQJIAEoAOwCyArACsgKzArACsAKzArECswK0ArECsQK0AkgAtAJGAEgAPAC1ArICtQK2ArICsgK2ArMCtgK3ArMCswK3ArQCtwK4ArQCtAK4AkYAuAJEAEYAPQC5ArUCuQK6ArUCtQK6ArYCugK7ArYCtgK7ArcCuwK8ArcCtwK8ArgCvAK9ArgCuAK9AkQAvQJCAEQAPgC+ArkCvgK/ArkCuQK/AroCvwLAAroCugLAArsCwALBArsCuwLBArwCwQLCArwCvALCAr0CwgLDAr0CvQLDAkIAwwJAAEIAPwAxAL4CMQAvAL4CvgIvAL8CLwAtAL8CvwItAMACLQArAMACwAIrAMECKwApAMECwQIpAMICKQAnAMICwgInAMMCJwAlAMMCwwIlAEAAJQABAEAAFgA4AAYAGADEAhYAGgDFAhgAHADHAhoAHgDKAhwAIADOAh4AIgDTAiAAFgDEAjgAxAI3ADgAGADFAsQCxQLGAsQCxALGAjcAxgI2ADcAGgDHAsUCxwLIAsUCxQLIAsYCyALJAsYCxgLJAjYAyQI1ADYAHADKAscCygLLAscCxwLLAsgCywLMAsgCyALMAskCzALNAskCyQLNAjUAzQI0ADUAHgDOAsoCzgLPAsoCygLPAssCzwLQAssCywLQAswC0ALRAswCzALRAs0C0QLSAs0CzQLSAjQA0gIzADQAIADTAs4C0wLUAs4CzgLUAs8C1ALVAs8CzwLVAtAC1QLWAtAC0ALWAtEC1gLXAtEC0QLXAtIC1wLYAtIC0gLYAjMA2AIyADMAIgAkANMCJAAmANMC0wImANQCJgAoANQC1AIoANUCKAAqANUC1QIqANYCKgAsANYC1gIsANcCLAAuANcC1wIuANgCLgAwANgC2AIwADIAMAAFADIA"
+ "byteLength":130560,
+ "uri":"chromesphere.bin"
}
]
}
diff --git a/data/shader/ao/rtao.csh b/data/shader/ao/rtao.csh
index a9b82952d..630651742 100644
--- a/data/shader/ao/rtao.csh
+++ b/data/shader/ao/rtao.csh
@@ -1,3 +1,5 @@
+#define LOCAL_STACK
+
#include <../globals.hsh>
#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 a9358a25b..acba66193 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);
@@ -104,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;
@@ -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/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 06bd5e9db..fcf1fa9c6 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;
@@ -53,12 +54,12 @@ 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 = -vec3(0.0, cloudUniforms.planetRadius, 0.0);
+vec3 cloudPlanetCenter = cloudUniforms.planetCenter.xyz;
#define MULTISCATTER_OCTAVES 2
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/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/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/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/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 c240482f2..d733f5e5a 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,16 +172,12 @@ 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;
- 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 dfe483e42..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,36 +71,32 @@ 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
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/deferred/indirect.csh b/data/shader/deferred/indirect.csh
index efe0336d9..bbdf7cf5a 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();
@@ -81,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];
@@ -101,12 +112,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 +133,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 +184,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 +200,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
@@ -181,17 +224,29 @@ 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;
- indirect *= pow(occlusionFactor, Uniforms.aoStrength);
+
+ indirect *= vec3(pow(occlusionFactor, Uniforms.aoStrength));
+#endif
+#ifdef SSGI
+ // 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
}
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 e0c78900e..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;
@@ -12,9 +20,16 @@ layout(std140, set = 0, binding = 0) uniform GlobalBuffer {
vec4 cameraDirection;
vec4 cameraUp;
vec4 cameraRight;
+ vec4 planetCenter;
+ float planetRadius;
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/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..7811ac31b 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>
@@ -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/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..dd3339024
--- /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[0].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..571998bb7
--- /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[0].cameraLocation.xyz);
+
+ float perlinScale, shoreScaling;
+ vec3 normalShoreWave;
+ fPosition += GetOceanDisplacement(fPosition, distanceToCamera, perlinScale, shoreScaling, normalShoreWave);
+
+ vec3 position = vec3(globalData[0].vMatrix * vec4(fPosition, 1.0));
+ vec4 fClipSpace = globalData[0].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..fa8c60e05 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[0].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,18 +98,18 @@ void main() {
// Normal mapping normal (offsets actual normal)
vec3 rippleNormal = vec3(0.0, 1.0, 0.0);
- if (Uniforms.hasRippleTexture > 0) {
- 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);
+#ifdef RIPPLE_TEXTURE
+ 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
// Scale ripples based on actual (not view) depth of water
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));
@@ -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,21 +130,20 @@ 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);
- 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);
@@ -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,41 +180,53 @@ 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[0].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[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;
- 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..610081308 100644
--- a/data/shader/ocean/ocean.vsh
+++ b/data/shader/ocean/ocean.vsh
@@ -1,28 +1,23 @@
#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;
-layout(location=10) out float perlinScale;
-
-const float shoreStartScaling = 15.0;
-const float shoreOffsetScaling = 5.0;
-const float minShoreScaling = 0.3;
+#endif
vec3 stitch(vec3 position) {
@@ -53,72 +48,28 @@ 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;
-
- fModelCoord = fPosition;
- fPosition = vec3(globalData.vMatrix * vec4(fPosition, 1.0));
- fClipSpace = globalData.pMatrix * vec4(fPosition, 1.0);
+#ifndef TERRAIN
+ vec3 normalShoreWave;
+#endif
+ float perlinScale;
+ float distanceToCamera = distance(fOriginalCoord.xyz, globalData[0].cameraLocation.xyz);
+ fPosition += GetOceanDisplacement(fPosition, distanceToCamera, perlinScale, shoreScaling, normalShoreWave);
+ fModelCoord = fPosition;
- fTexCoord = vTexCoord;
+ 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/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..f9999cf72 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) {
@@ -20,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;
@@ -55,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
new file mode 100644
index 000000000..8fea519c7
--- /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[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);
+
+ 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[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[0].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/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..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>
@@ -149,12 +151,109 @@ 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;
+
+}
+
+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;
@@ -167,38 +266,35 @@ vec4 SampleCatmullRom(vec2 uv) {
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));
- 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));
+ 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;
+ ivec2 uvs[4] = { uv0, uv1, uv2, uv3 };
+ vec2 weights[4] = { w0, w1, w2, w3 };
- 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;
+ history = vec4(0.0);
- float totalWeight = weight0 + weight1 +
- weight2 + weight3 + weight4;
+ float totalWeight = 0.0;
- vec4 totalSample = sample0 + sample1 +
- sample2 + sample3 + sample4;
+ 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);
- return totalSample / totalWeight;
+ 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) {
@@ -239,58 +335,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 +358,13 @@ void main() {
vec4 historyMoments;
valid = SampleHistory(pixel, historyPixel, history, historyMoments);
+#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;
@@ -332,7 +383,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 +391,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 +416,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 6b7e3b880..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),
@@ -343,7 +349,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)));
@@ -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 0c14f43cb..adf8bcf4e 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)) {
@@ -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/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..b80089c06 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));
+ vec3 viewPosition = vec3(globalData[0].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[0].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..32a5f7243
--- /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[0].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..590aee507 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
@@ -91,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;
@@ -106,36 +110,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 +130,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);
+ vec3 worldDirection = normalize(vec3(globalData[0].ivMatrix * vec4(viewPosition, 0.0)));
- 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.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 1173958f4..3ad1e7288 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
@@ -7,7 +6,7 @@
// Atlas engine includes
#include
-#include
+#include
class ImguiWrapper {
@@ -54,6 +53,6 @@ class ImguiWrapper {
std::unordered_map imageViewToDescriptorSetMap;
-};
+ bool initialized = false;
-#endif
\ No newline at end of file
+};
diff --git a/src/demo/App.cpp b/src/demo/App.cpp
index 5cdae5c4b..a609601fd 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) {
@@ -43,8 +44,10 @@ void App::LoadContent() {
keyboardHandler.speed = cameraSpeed;
}
});
+
+ 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);
@@ -53,30 +56,28 @@ 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);
+ // Use SSGI by default
+ scene->ao->enable = false;
+ 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 = 100.0f;
- scene->sky.clouds->maxHeight = 600.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();
@@ -112,8 +113,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());
@@ -155,15 +161,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);
@@ -179,9 +188,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);
@@ -202,6 +211,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(),
@@ -229,6 +242,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;
@@ -267,7 +281,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));
@@ -283,10 +298,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;
@@ -344,7 +355,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());
@@ -425,7 +448,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);
@@ -434,6 +477,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);
@@ -491,10 +545,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 +559,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();
@@ -539,6 +595,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");
@@ -562,15 +619,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;
@@ -594,6 +647,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 +737,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 +759,6 @@ void App::Render(float deltaTime) {
firstFrame = false;
}
-
}
void App::DisplayLoadingScreen(float deltaTime) {
@@ -751,6 +812,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 +830,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;
@@ -777,7 +845,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 +864,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 +900,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 +920,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 +940,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 +962,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 +981,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 +1001,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);
@@ -970,23 +1040,67 @@ 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);
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);
@@ -1005,7 +1119,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) {
@@ -1076,16 +1190,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()) {
@@ -1141,7 +1263,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));
@@ -1167,13 +1289,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 bba14d1db..29c8a8caf 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
@@ -40,7 +39,9 @@ class App : public Atlas::EngineInstance {
PICAPICA,
SUBWAY,
MATERIALS,
- FOREST
+ FOREST,
+ EMERALDSQUARE,
+ FLYINGWORLD
};
void DisplayLoadingScreen(float deltaTime);
@@ -74,6 +75,7 @@ class App : public Atlas::EngineInstance {
Atlas::Input::MouseHandler mouseHandler;
Atlas::Input::KeyboardHandler keyboardHandler;
+ Atlas::Input::ControllerHandler controllerHandler;
Ref loadingTexture;
@@ -96,6 +98,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 875b55f99..8857b2ebf 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)
@@ -39,8 +39,8 @@ endif()
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 nlohmann_json::nlohmann_json
- volk::volk volk::volk_headers unofficial::vulkan-memory-allocator::vulkan-memory-allocator
+ set(ATLAS_ENGINE_LIBS assimp::assimp SDL2::SDL2 SDL2::SDL2main SPIRV-Tools-opt
+ volk::volk volk::volk_headers GPUOpen::VulkanMemoryAllocator
unofficial::spirv-reflect::spirv-reflect glslang::SPIRV)
endif()
@@ -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 nlohmann_json::nlohmann_json
- unofficial::vulkan-memory-allocator::vulkan-memory-allocator
+ volk::volk SPIRV-Tools-opt
+ GPUOpen::VulkanMemoryAllocator Vulkan::Vulkan Vulkan::Headers
unofficial::spirv-reflect::spirv-reflect glslang::SPIRV)
endif()
@@ -75,8 +77,8 @@ if(UNIX AND NOT APPLE AND NOT ANDROID)
set(CMAKE_INSTALL_RPATH "./")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
- set(ATLAS_ENGINE_LIBS assimp::assimp SDL2::SDL2 SDL2::SDL2main SDL2::SDL2-static nlohmann_json::nlohmann_json
- volk::volk volk::volk_headers unofficial::vulkan-memory-allocator::vulkan-memory-allocator
+ set(ATLAS_ENGINE_LIBS assimp::assimp SDL2::SDL2 SDL2::SDL2main SDL2::SDL2-static
+ volk::volk volk::volk_headers GPUOpen::VulkanMemoryAllocator
unofficial::spirv-reflect::spirv-reflect SPIRV-Tools-opt 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/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..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", true);
+ .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 a04a63bfb..50bae3354 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"
@@ -30,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 {
@@ -63,6 +67,4 @@ namespace Atlas {
};
-}
-
-#endif
\ No newline at end of file
+}
\ No newline at end of file
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 3cf33d73a..9f1da8d69 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"
@@ -67,7 +66,7 @@ namespace Atlas {
const static EngineConfig engineConfig;
/**
- * The main window to which the context is attached to
+ * The main window
*/
Window window;
@@ -77,8 +76,6 @@ namespace Atlas {
*/
std::vector args;
- static EngineInstance* GetInstance();
-
protected:
/**
* Returns the size of the screen.
@@ -93,18 +90,12 @@ 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;
};
-}
-
-
-#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/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/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..08644c3e1 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,9 +87,32 @@ 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_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_UNDEFINED,
+ .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+ };
+
+ auto oceanRenderPassDesc = Graphics::RenderPassDesc {
+ .colorAttachments = {colorAttachments[0]},
+ .depthAttachment = depthAttachment
+ };
+ oceanRenderPass = graphicsDevice->CreateRenderPass(oceanRenderPassDesc);
+ }
CreateFrameBuffers();
+ SetGIResolution(HALF_RES);
SetAOResolution(HALF_RES);
SetVolumetricResolution(HALF_RES);
SetReflectionResolution(HALF_RES);
@@ -108,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);
@@ -163,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);
@@ -338,6 +394,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..ed3b68bee 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"
@@ -117,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
*/
@@ -168,8 +177,17 @@ namespace Atlas {
Ref lightingFrameBuffer;
Ref lightingFrameBufferWithStencil;
+ Ref oceanRenderPass;
+ Ref oceanDepthOnlyFrameBuffer;
+
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;
@@ -178,6 +196,9 @@ namespace Atlas {
Texture::Texture2D sssTexture;
+ Texture::Texture2D oceanDepthTexture;
+ Texture::Texture2D oceanStencilTexture;
+
Texture::Texture2D volumetricTexture;
Texture::Texture2D swapVolumetricTexture;
@@ -207,6 +228,7 @@ namespace Atlas {
int32_t width = 0;
int32_t height = 0;
+ RenderResolution giResolution;
RenderResolution aoResolution;
RenderResolution volumetricResolution;
RenderResolution reflectionResolution;
@@ -216,7 +238,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..3aa0e3dbe 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
@@ -31,6 +30,8 @@
#endif
+#define AE_ASSERT assert
+
// SDL
#ifdef AE_NO_APP
#define SDL_MAIN_HANDLED
@@ -63,6 +64,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.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 3f84f4b91..fb30d8081 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"
@@ -41,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();
@@ -157,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
*/
@@ -169,8 +162,6 @@ namespace Atlas {
const std::string title;
- Graphics::Surface* surface;
-
Events::EventDelegate windowEventDelegate;
Events::EventDelegate keyboardEventDelegate;
Events::EventDelegate mouseButtonEventDelegate;
@@ -213,6 +204,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..7139718be 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"
@@ -428,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)) {
@@ -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 14d9e709a..35ea11ac8 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"
@@ -185,6 +184,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..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,172 +389,205 @@ 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);
}
- void CommandList::BindBuffer(const Ref& buffer, uint32_t set, uint32_t binding) {
+ void CommandList::BindVertexBuffers(std::vector[> &buffers, uint32_t bindingOffset,
+ uint32_t bindingCount) {
- 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(pipelineInUse && "No pipeline is bound");
+ if (!pipelineInUse) return;
+
+ std::vector offsets;
+ std::vector bindBuffers;
+ for (const auto& buffer : buffers) {
+ if (!buffer->buffer) return;
+ AE_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) {
+
+ 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;
}
@@ -681,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();
@@ -695,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();
@@ -708,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();
@@ -720,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();
@@ -733,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();
@@ -845,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];
@@ -860,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;
}
@@ -878,62 +930,41 @@ 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.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 = {};
setWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
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;
@@ -943,27 +974,30 @@ 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;
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.dstSet = descriptorBindingData.sets[i]->set;
setWrite.descriptorCount = 1;
setWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
setWrite.pImageInfo = &imageInfo;
@@ -972,40 +1006,76 @@ 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;
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.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;
@@ -1018,22 +1088,137 @@ 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;
- 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);
}
}
@@ -1058,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;
@@ -1072,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;
@@ -1089,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 ecd807c52..67b6fd210 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"
@@ -9,7 +8,7 @@
#include "RenderPass.h"
#include "Pipeline.h"
#include "Buffer.h"
-#include "Descriptor.h"
+#include "DescriptorPool.h"
#include "Sampler.h"
#include "QueryPool.h"
@@ -78,10 +77,14 @@ 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);
+ 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);
@@ -90,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();
@@ -183,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;
@@ -216,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 {
@@ -242,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;
@@ -256,6 +298,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..2e87154a7 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"
@@ -11,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
@@ -22,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); \
} \
}
@@ -74,6 +83,4 @@ namespace Atlas {
}
-}
-
-#endif
\ No newline at end of file
+}
\ No newline at end of file
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 50%
rename from src/engine/graphics/Descriptor.h
rename to src/engine/graphics/DescriptorPool.h
index 4d79589e0..19ff727e5 100644
--- a/src/engine/graphics/Descriptor.h
+++ b/src/engine/graphics/DescriptorPool.h
@@ -1,7 +1,7 @@
-#ifndef AE_GRAPHICSDESCRIPTOR_H
-#define AE_GRAPHICSDESCRIPTOR_H
+#pragma once
#include "Common.h"
+#include "DescriptorSetLayout.h"
#include
#include
@@ -13,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:
@@ -24,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;
@@ -48,6 +55,4 @@ namespace Atlas {
}
-}
-
-#endif
\ No newline at end of file
+}
\ No newline at end of file
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/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.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/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..2d0b33379 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);
@@ -89,63 +88,70 @@ namespace Atlas {
// so delete all of the memoryManager content before cleaning the rest
memoryManager->DestroyAllImmediate();
- for (auto& tlasRef : tlases) {
- assert(tlasRef.use_count() == 1 && "TLAS wasn't deallocated or allocated wrongly");
+ // 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) {
+ AE_ASSERT(tlasRef.use_count() == 1 && "TLAS wasn't deallocated or allocated wrongly");
tlasRef.reset();
}
- for (auto& blasRef : blases) {
- assert(blasRef.use_count() == 1 && "BLAS wasn't deallocated or allocated wrongly");
+ for (auto& blasRef : blases.data) {
+ AE_ASSERT(blasRef.use_count() == 1 && "BLAS wasn't deallocated or allocated wrongly");
blasRef.reset();
}
- for (auto& pipelineRef : pipelines) {
- assert(pipelineRef.use_count() == 1 && "Pipeline wasn't deallocated or allocated wrongly");
+ for (auto& pipelineRef : pipelines.data) {
+ AE_ASSERT(pipelineRef.use_count() == 1 && "Pipeline wasn't deallocated or allocated wrongly");
pipelineRef.reset();
}
- for (auto& frameBufferRef : frameBuffers) {
- assert(frameBufferRef.use_count() == 1 && "Frame buffer wasn't deallocated or allocated wrongly");
+ for (auto& frameBufferRef : frameBuffers.data) {
+ AE_ASSERT(frameBufferRef.use_count() == 1 && "Frame buffer wasn't deallocated or allocated wrongly");
frameBufferRef.reset();
}
- for (auto& renderPassRef : renderPasses) {
- assert(renderPassRef.use_count() == 1 && "Render pass wasn't deallocated or allocated wrongly");
+ for (auto& renderPassRef : renderPasses.data) {
+ AE_ASSERT(renderPassRef.use_count() == 1 && "Render pass wasn't deallocated or allocated wrongly");
renderPassRef.reset();
}
- for (auto& shaderRef : shaders) {
- assert(shaderRef.use_count() == 1 && "Shader wasn't deallocated or allocated wrongly");
+ for (auto& shaderRef : shaders.data) {
+ AE_ASSERT(shaderRef.use_count() == 1 && "Shader wasn't deallocated or allocated wrongly");
shaderRef.reset();
}
- for (auto& bufferRef : buffers) {
- assert(bufferRef.use_count() == 1 && "Buffer wasn't deallocated or allocated wrongly");
+ for (auto& bufferRef : buffers.data) {
+ AE_ASSERT(bufferRef.use_count() == 1 && "Buffer wasn't deallocated or allocated wrongly");
bufferRef.reset();
}
- for (auto& multiBufferRef : multiBuffers) {
- assert(multiBufferRef.use_count() == 1 && "Multi buffer wasn't deallocated or allocated wrongly");
+ for (auto& multiBufferRef : multiBuffers.data) {
+ AE_ASSERT(multiBufferRef.use_count() == 1 && "Multi buffer wasn't deallocated or allocated wrongly");
multiBufferRef.reset();
}
- for (auto& imageRef : images) {
- assert(imageRef.use_count() == 1 && "Image wasn't deallocated or allocated wrongly");
+ for (auto& imageRef : images.data) {
+ AE_ASSERT(imageRef.use_count() == 1 && "Image wasn't deallocated or allocated wrongly");
imageRef.reset();
}
- for (auto& samplerRef : samplers) {
- assert(samplerRef.use_count() == 1 && "Sampler wasn't deallocated or allocated wrongly");
+ for (auto& samplerRef : samplers.data) {
+ AE_ASSERT(samplerRef.use_count() == 1 && "Sampler wasn't deallocated or allocated wrongly");
samplerRef.reset();
}
- for (auto& poolRef : descriptorPools) {
- assert(poolRef.use_count() == 1 && "Descriptor pool wasn't deallocated or allocated wrongly");
+ for (auto& poolRef : descriptorPools.data) {
+ AE_ASSERT(poolRef.use_count() == 1 && "Descriptor pool wasn't deallocated or allocated wrongly");
poolRef.reset();
}
- for (auto& poolRef : queryPools) {
- assert(poolRef.use_count() == 1 && "Query pool wasn't deallocated or allocated wrongly");
+ 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) {
+ AE_ASSERT(poolRef.use_count() == 1 && "Query pool wasn't deallocated or allocated wrongly");
poolRef.reset();
}
@@ -159,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;
@@ -199,7 +204,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 +215,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 +226,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 +237,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 +248,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 +259,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 +270,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 +281,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,17 +292,30 @@ 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;
}
+ 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);
- descriptorPools.push_back(pool);
+ std::lock_guard guard(descriptorPools.mutex);
+ descriptorPools.data.push_back(pool);
return pool;
@@ -299,7 +325,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 +336,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 +347,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;
@@ -352,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();
@@ -378,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 = {};
@@ -434,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();
@@ -482,8 +511,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();
@@ -623,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;
@@ -635,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;
}
@@ -657,6 +690,8 @@ namespace Atlas {
int32_t score = 0;
+
+
VkPhysicalDeviceProperties physicalDeviceProperties;
VkPhysicalDeviceFeatures physicalDeviceFeatures;
vkGetPhysicalDeviceProperties(device, &physicalDeviceProperties);
@@ -857,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);
}
@@ -888,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);
@@ -913,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 = {};
@@ -926,20 +965,20 @@ 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);
@@ -947,32 +986,42 @@ namespace Atlas {
support.hardwareRayTracing = true;
}
- 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;
@@ -1020,115 +1069,19 @@ 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]