Compare commits

..
Author SHA1 Message Date
Benjamin Doherty 031cd302dd Capture command callstacks for debugging 2023-10-30 14:42:24 -07:00
114 changed files with 1303 additions and 2343 deletions
+2 -1
View File
@@ -206,7 +206,8 @@ jobs:
TAG: ${{ steps.git_ref.outputs.tag }} TAG: ${{ steps.git_ref.outputs.tag }}
run: | run: |
build\windows\build-github.bat release build\windows\build-github.bat release
move out\filament-windows.tgz out\filament-$Env:TAG-windows.tgz cd ..\..
move out\filament-windows.tgz out\filament-%TAG%-windows.tgz
shell: cmd shell: cmd
- uses: actions/github-script@v6 - uses: actions/github-script@v6
env: env:
+64 -64
View File
@@ -40,20 +40,20 @@ inside the Filament source tree.
To trigger an incremental debug build: To trigger an incremental debug build:
```shell ```
./build.sh debug $ ./build.sh debug
``` ```
To trigger an incremental release build: To trigger an incremental release build:
```shell ```
./build.sh release $ ./build.sh release
``` ```
To trigger both incremental debug and release builds: To trigger both incremental debug and release builds:
```shell ```
./build.sh debug release $ ./build.sh debug release
``` ```
To install the libraries and executables in `out/debug/` and `out/release/`, add the `-i` flag. To install the libraries and executables in `out/debug/` and `out/release/`, add the `-i` flag.
@@ -76,9 +76,9 @@ The following CMake options are boolean options specific to Filament:
To turn an option on or off: To turn an option on or off:
```shell ```
cd <cmake-build-directory> $ cd <cmake-build-directory>
cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF $ cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF
``` ```
Options can also be set with the CMake GUI. Options can also be set with the CMake GUI.
@@ -102,20 +102,20 @@ script.
If you'd like to run `cmake` directly rather than using the build script, it can be invoked as If you'd like to run `cmake` directly rather than using the build script, it can be invoked as
follows, with some caveats that are explained further down. follows, with some caveats that are explained further down.
```shell ```
mkdir out/cmake-release $ mkdir out/cmake-release
cd out/cmake-release $ cd out/cmake-release
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../.. $ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
``` ```
Your Linux distribution might default to `gcc` instead of `clang`, if that's the case invoke Your Linux distribution might default to `gcc` instead of `clang`, if that's the case invoke
`cmake` with the following command: `cmake` with the following command:
```shell ```
mkdir out/cmake-release $ mkdir out/cmake-release
cd out/cmake-release $ cd out/cmake-release
# Or use a specific version of clang, for instance /usr/bin/clang-14 # Or use a specific version of clang, for instance /usr/bin/clang-14
CC=/usr/bin/clang CXX=/usr/bin/clang++ CXXFLAGS=-stdlib=libc++ \ $ CC=/usr/bin/clang CXX=/usr/bin/clang++ CXXFLAGS=-stdlib=libc++ \
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../.. cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
``` ```
@@ -123,17 +123,17 @@ You can also export the `CC` and `CXX` environment variables to always point to
solution is to use `update-alternatives` to both change the default compiler, and point to a solution is to use `update-alternatives` to both change the default compiler, and point to a
specific version of clang: specific version of clang:
```shell ```
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 100 $ update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 100
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-14 100 $ update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-14 100
update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 $ update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100
update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 $ update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100
``` ```
Finally, invoke `ninja`: Finally, invoke `ninja`:
```shell ```
ninja $ ninja
``` ```
This will build Filament, its tests and samples, and various host tools. This will build Filament, its tests and samples, and various host tools.
@@ -143,8 +143,8 @@ This will build Filament, its tests and samples, and various host tools.
To compile Filament you must have the most recent version of Xcode installed and you need to To compile Filament you must have the most recent version of Xcode installed and you need to
make sure the command line tools are setup by running: make sure the command line tools are setup by running:
```shell ```
xcode-select --install $ xcode-select --install
``` ```
If you wish to run the Vulkan backend instead of the default Metal backend, you must install If you wish to run the Vulkan backend instead of the default Metal backend, you must install
@@ -152,11 +152,11 @@ the LunarG SDK, enable "System Global Components", and reboot your machine.
Then run `cmake` and `ninja` to trigger a build: Then run `cmake` and `ninja` to trigger a build:
```shell ```
mkdir out/cmake-release $ mkdir out/cmake-release
cd out/cmake-release $ cd out/cmake-release
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../.. $ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
ninja $ ninja
``` ```
### iOS ### iOS
@@ -164,8 +164,8 @@ ninja
The easiest way to build Filament for iOS is to use `build.sh` and the The easiest way to build Filament for iOS is to use `build.sh` and the
`-p ios` flag. For instance to build the debug target: `-p ios` flag. For instance to build the debug target:
```shell ```
./build.sh -p ios debug $ ./build.sh -p ios debug
``` ```
See [ios/samples/README.md](./ios/samples/README.md) for more information. See [ios/samples/README.md](./ios/samples/README.md) for more information.
@@ -191,10 +191,10 @@ using `fsutil.exe file queryCaseSensitiveInfo`.
Next, open `x64 Native Tools Command Prompt for VS 2019`, create a working directory, and run Next, open `x64 Native Tools Command Prompt for VS 2019`, create a working directory, and run
CMake in it: CMake in it:
```bat ```
mkdir out > mkdir out
cd out > cd out
cmake .. > cmake ..
``` ```
Open the generated solution file `TNT.sln` in Visual Studio. Open the generated solution file `TNT.sln` in Visual Studio.
@@ -204,15 +204,15 @@ target in the _Solution Explorer_ and choose _Build_ to build a specific target.
For example, build the `material_sandbox` sample and run it from the `out` directory with: For example, build the `material_sandbox` sample and run it from the `out` directory with:
```bat ```
samples\Debug\material_sandbox.exe ..\assets\models\monkey\monkey.obj > samples\Debug\material_sandbox.exe ..\assets\models\monkey\monkey.obj
``` ```
You can also use CMake to invoke the build without opening Visual Studio. For example, from the You can also use CMake to invoke the build without opening Visual Studio. For example, from the
`out` folder run the following command. `out` folder run the following command.
```bat ```
cmake --build . --target gltf_viewer --config Release > cmake --build . --target gltf_viewer --config Release
``` ```
### Android ### Android
@@ -237,8 +237,8 @@ To build Android on Windows machines, see [android/Windows.md](android/Windows.m
The easiest way to build Filament for Android is to use `build.sh` and the The easiest way to build Filament for Android is to use `build.sh` and the
`-p android` flag. For instance to build the release target: `-p android` flag. For instance to build the release target:
```shell ```
./build.sh -p android release $ ./build.sh -p android release
``` ```
Run `build.sh -h` for more information. Run `build.sh -h` for more information.
@@ -248,23 +248,23 @@ Run `build.sh -h` for more information.
Invoke CMake in a build directory of your choice, inside of filament's directory. The commands Invoke CMake in a build directory of your choice, inside of filament's directory. The commands
below show how to build Filament for ARM 64-bit (`aarch64`). below show how to build Filament for ARM 64-bit (`aarch64`).
```shell ```
mkdir out/android-build-release-aarch64 $ mkdir out/android-build-release-aarch64
cd out/android-build-release-aarch64 $ cd out/android-build-release-aarch64
cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=../../build/toolchain-aarch64-linux-android.cmake \ $ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=../../build/toolchain-aarch64-linux-android.cmake \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../android-release/filament ../.. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../android-release/filament ../..
``` ```
And then invoke `ninja`: And then invoke `ninja`:
```shell ```
ninja install $ ninja install
``` ```
or or
```shell ```
ninja install/strip $ ninja install/strip
``` ```
This will generate Filament's Android binaries in `out/android-release`. This location is important This will generate Filament's Android binaries in `out/android-release`. This location is important
@@ -296,8 +296,8 @@ AAR.
Alternatively you can build the AAR from the command line by executing the following in the Alternatively you can build the AAR from the command line by executing the following in the
`android/` directory: `android/` directory:
```shell ```
./gradlew -Pcom.google.android.filament.dist-dir=../../out/android-release/filament assembleRelease $ ./gradlew -Pcom.google.android.filament.dist-dir=../../out/android-release/filament assembleRelease
``` ```
The `-Pcom.google.android.filament.dist-dir` can be used to specify a different installation The `-Pcom.google.android.filament.dist-dir` can be used to specify a different installation
@@ -311,7 +311,7 @@ sure to add the newly created module as a dependency to your application.
If you do not wish to include all supported ABIs, make sure to create the appropriate flavors in If you do not wish to include all supported ABIs, make sure to create the appropriate flavors in
your Gradle build file. For example: your Gradle build file. For example:
```gradle ```
flavorDimensions 'cpuArch' flavorDimensions 'cpuArch'
productFlavors { productFlavors {
arm8 { arm8 {
@@ -353,7 +353,7 @@ started, follow the instructions for building Filament on your platform ([macOS]
Next, you need to install the Emscripten SDK. The following instructions show how to install the Next, you need to install the Emscripten SDK. The following instructions show how to install the
same version that our continuous builds use. same version that our continuous builds use.
```shell ```
cd <your chosen parent folder for the emscripten SDK> cd <your chosen parent folder for the emscripten SDK>
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.15.zip > emsdk.zip curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.15.zip > emsdk.zip
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
@@ -364,7 +364,7 @@ source ./emsdk_env.sh
After this you can invoke the [easy build](#easy-build) script as follows: After this you can invoke the [easy build](#easy-build) script as follows:
```shell ```
export EMSDK=<your chosen home for the emscripten SDK> export EMSDK=<your chosen home for the emscripten SDK>
./build.sh -p webgl release ./build.sh -p webgl release
``` ```
@@ -374,7 +374,7 @@ creates a `samples` folder that can be used as the root of a simple static web s
cannot open the HTML directly from the filesystem due to CORS. We recommend using the emrun tool cannot open the HTML directly from the filesystem due to CORS. We recommend using the emrun tool
to create a quick localhost server: to create a quick localhost server:
```shell ```
emrun out/cmake-webgl-release/web/samples --no_browser --port 8000 emrun out/cmake-webgl-release/web/samples --no_browser --port 8000
``` ```
@@ -395,7 +395,7 @@ Some of the samples accept FBX/OBJ meshes while others rely on the `filamesh` fi
generate a `filamesh ` file from an FBX/OBJ asset, run the `filamesh` tool generate a `filamesh ` file from an FBX/OBJ asset, run the `filamesh` tool
(`./tools/filamesh/filamesh` in your build directory): (`./tools/filamesh/filamesh` in your build directory):
```shell ```
filamesh ./assets/models/monkey/monkey.obj monkey.filamesh filamesh ./assets/models/monkey/monkey.obj monkey.filamesh
``` ```
@@ -405,7 +405,7 @@ files for the IBL (which are PNGs containing `R11F_G11F_B10F` data) or a path to
containing two `.ktx` files (one for the IBL itself, one for the skybox). To generate an IBL containing two `.ktx` files (one for the IBL itself, one for the skybox). To generate an IBL
simply use this command: simply use this command:
```shell ```
cmgen -f ktx -x ./ibls/ my_ibl.exr cmgen -f ktx -x ./ibls/ my_ibl.exr
``` ```
@@ -427,9 +427,9 @@ value is the desired roughness between 0 and 1.
To generate the documentation you must first install `doxygen` and `graphviz`, then run the To generate the documentation you must first install `doxygen` and `graphviz`, then run the
following commands: following commands:
```shell ```
cd filament/filament $ cd filament/filament
doxygen docs/doxygen/filament.doxygen $ doxygen docs/doxygen/filament.doxygen
``` ```
Finally simply open `docs/html/index.html` in your web browser. Finally simply open `docs/html/index.html` in your web browser.
@@ -439,7 +439,7 @@ Finally simply open `docs/html/index.html` in your web browser.
To try out Filament's Vulkan support with SwiftShader, first build SwiftShader and set the To try out Filament's Vulkan support with SwiftShader, first build SwiftShader and set the
`SWIFTSHADER_LD_LIBRARY_PATH` variable to the folder that contains `libvk_swiftshader.dylib`: `SWIFTSHADER_LD_LIBRARY_PATH` variable to the folder that contains `libvk_swiftshader.dylib`:
```shell ```
git clone https://github.com/google/swiftshader.git git clone https://github.com/google/swiftshader.git
cd swiftshader/build cd swiftshader/build
cmake .. && make -j cmake .. && make -j
@@ -454,7 +454,7 @@ Continuous testing turnaround can be quite slow if you need to build SwiftShader
provide an Ubuntu-based Docker image that has it already built. The Docker image also includes provide an Ubuntu-based Docker image that has it already built. The Docker image also includes
everything necessary for building Filament. You can fetch and run the image as follows: everything necessary for building Filament. You can fetch and run the image as follows:
```shell ```
docker pull ghcr.io/filament-assets/swiftshader docker pull ghcr.io/filament-assets/swiftshader
docker run -it ghcr.io/filament-assets/swiftshader docker run -it ghcr.io/filament-assets/swiftshader
``` ```
-8
View File
@@ -3,14 +3,6 @@
# ================================================================================================== # ==================================================================================================
cmake_minimum_required(VERSION 3.19) cmake_minimum_required(VERSION 3.19)
# ==================================================================================================
# Toolchain configuration
# ==================================================================================================
if (APPLE AND NOT IOS)
# This must be set before project() is called
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15 CACHE STRING "")
endif()
# ================================================================================================== # ==================================================================================================
# Project declaration # Project declaration
# ================================================================================================== # ==================================================================================================
+1 -1
View File
@@ -27,7 +27,7 @@ again.
## Code Style ## Code Style
See [CODE_STYLE.md](/CODE_STYLE.md) See [CodeStyle.md](/CODE_STYLE.md)
## Code reviews ## Code reviews
+6
View File
@@ -7,3 +7,9 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut ## Release notes for next branch cut
- Fix possible NPE when updating fog options from Java/Kotlin
- The `emissive` property was not applied properly to `MASKED` materials, and could cause
dark fringes to appear (recompile materials)
- Allow glTF materials with transmission/volume extensions to choose their alpha mode
instead of forcing `MASKED`
+3 -3
View File
@@ -31,7 +31,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'com.google.android.filament:filament-android:1.43.1' implementation 'com.google.android.filament:filament-android:1.42.0'
} }
``` ```
@@ -50,8 +50,8 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release: iOS projects can use CocoaPods to install the latest release:
```shell ```
pod 'Filament', '~> 1.43.1' pod 'Filament', '~> 1.42.0'
``` ```
### Snapshots ### Snapshots
-22
View File
@@ -7,30 +7,8 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.43.1
## v1.43.0
- gltfio: Fix possible change of scale sign when decomposing transform matrix for animation
- engine: Fixes "stable" shadows (see b/299310624)
## v1.42.2
- Fix possible NPE when updating fog options from Java/Kotlin
- The `emissive` property was not applied properly to `MASKED` materials, and could cause
dark fringes to appear (recompile materials)
- Allow glTF materials with transmission/volume extensions to choose their alpha mode
instead of forcing `MASKED`
- Fix a crash in gltfio when not using ubershaders
- Use flatmat for mat parameter in jsbinding
- Fix TextureFlags for sheenRoughnessMap when textures of sheenRoughnessMap and sheenColorMap is same
- Directional shadows can now be transformed (b/297095805)
## v1.42.1 ## v1.42.1
- Fix potential `EXC_BAD_ACCESS` with Metal backend: b/297059776
- `setFrameCompletedCallback` now takes a `backend::CallbackHandler`.
## v1.42.0 ## v1.42.0
- engine: add preliminary support for instanced stereoscopic rendering [⚠️ **Recompile materials**] - engine: add preliminary support for instanced stereoscopic rendering [⚠️ **Recompile materials**]
@@ -79,8 +79,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
jfloat shadowFarHint, jboolean stable, jboolean lispsm, jfloat shadowFarHint, jboolean stable, jboolean lispsm,
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope, jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
jboolean screenSpaceContactShadows, jint stepCount, jboolean screenSpaceContactShadows, jint stepCount,
jfloat maxShadowDistance, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius, jfloat maxShadowDistance, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
jfloatArray transform) {
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder; LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
LightManager::ShadowOptions shadowOptions { LightManager::ShadowOptions shadowOptions {
.mapSize = (uint32_t)mapSize, .mapSize = (uint32_t)mapSize,
@@ -103,18 +102,12 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
}, },
.shadowBulbRadius = shadowBulbRadius .shadowBulbRadius = shadowBulbRadius
}; };
jfloat *nativeSplits = env->GetFloatArrayElements(splitPositions, NULL); jfloat *nativeSplits = env->GetFloatArrayElements(splitPositions, NULL);
const jsize splitCount = std::min((jsize) 3, env->GetArrayLength(splitPositions)); const jsize splitCount = std::min((jsize) 3, env->GetArrayLength(splitPositions));
std::copy_n(nativeSplits, splitCount, shadowOptions.cascadeSplitPositions); for (jsize i = 0; i < splitCount; i++) {
shadowOptions.cascadeSplitPositions[i] = nativeSplits[i];
}
env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0); env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0);
jfloat* nativeTransform = env->GetFloatArrayElements(transform, NULL);
std::copy_n(nativeTransform,
std::min(4, env->GetArrayLength(transform)),
shadowOptions.transform.xyzw.v);
env->ReleaseFloatArrayElements(transform, nativeTransform, 0);
builder->shadowOptions(shadowOptions); builder->shadowOptions(shadowOptions);
} }
@@ -258,7 +258,6 @@ public class LightManager {
* shadows that are too far and wouldn't contribute to the scene much, improving * shadows that are too far and wouldn't contribute to the scene much, improving
* performance and quality. This value is always positive. * performance and quality. This value is always positive.
* Use 0.0f to use the camera far distance. * Use 0.0f to use the camera far distance.
* This only affect directional lights.
*/ */
public float shadowFar = 0.0f; public float shadowFar = 0.0f;
@@ -369,17 +368,6 @@ public class LightManager {
* enabled. (2cm by default). * enabled. (2cm by default).
*/ */
public float shadowBulbRadius = 0.02f; public float shadowBulbRadius = 0.02f;
/**
* Transforms the shadow direction. Must be a unit quaternion.
* The default is identity.
* Ignored if the light type isn't directional. For artistic use. Use with caution.
* The quaternion is stored as the imaginary part in the first 3 elements and the real
* part in the last element of the transform array.
*/
@NonNull
@Size(min = 4, max = 4)
public float[] transform = { 0.0f, 0.0f, 0.0f, 1.0f };
} }
public static class ShadowCascades { public static class ShadowCascades {
@@ -518,7 +506,7 @@ public class LightManager {
options.polygonOffsetConstant, options.polygonOffsetSlope, options.polygonOffsetConstant, options.polygonOffsetSlope,
options.screenSpaceContactShadows, options.screenSpaceContactShadows,
options.stepCount, options.maxShadowDistance, options.stepCount, options.maxShadowDistance,
options.elvsm, options.blurWidth, options.shadowBulbRadius, options.transform); options.elvsm, options.blurWidth, options.shadowBulbRadius);
return this; return this;
} }
@@ -1181,7 +1169,7 @@ public class LightManager {
boolean stable, boolean lispsm, boolean stable, boolean lispsm,
float polygonOffsetConstant, float polygonOffsetSlope, float polygonOffsetConstant, float polygonOffsetSlope,
boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance,
boolean elvsm, float blurWidth, float shadowBulbRadius, float[] transform); boolean elvsm, float blurWidth, float shadowBulbRadius);
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled); private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z); private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z); private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);
@@ -1418,17 +1418,6 @@ public class View {
* limit highlights to this value before bloom [10, +inf] * limit highlights to this value before bloom [10, +inf]
*/ */
public float highlight = 1000.0f; public float highlight = 1000.0f;
/**
* Bloom quality level.
* LOW (default): use a more optimized down-sampling filter, however there can be artifacts
* with dynamic resolution, this can be alleviated by using the homogenous mode.
* MEDIUM: Good balance between quality and performance.
* HIGH: In this mode the bloom resolution is automatically increased to avoid artifacts.
* This mode can be significantly slower on mobile, especially at high resolution.
* This mode greatly improves the anamorphic bloom.
*/
@NonNull
public QualityLevel quality = QualityLevel.LOW;
/** /**
* enable screen-space lens flare * enable screen-space lens flare
*/ */
-3
View File
@@ -52,7 +52,6 @@ set(GLTFIO_SRCS
${GLTFIO_DIR}/include/gltfio/FilamentInstance.h ${GLTFIO_DIR}/include/gltfio/FilamentInstance.h
${GLTFIO_DIR}/include/gltfio/MaterialProvider.h ${GLTFIO_DIR}/include/gltfio/MaterialProvider.h
${GLTFIO_DIR}/include/gltfio/NodeManager.h ${GLTFIO_DIR}/include/gltfio/NodeManager.h
${GLTFIO_DIR}/include/gltfio/TrsTransformManager.h
${GLTFIO_DIR}/include/gltfio/ResourceLoader.h ${GLTFIO_DIR}/include/gltfio/ResourceLoader.h
${GLTFIO_DIR}/include/gltfio/TextureProvider.h ${GLTFIO_DIR}/include/gltfio/TextureProvider.h
${GLTFIO_DIR}/include/gltfio/math.h ${GLTFIO_DIR}/include/gltfio/math.h
@@ -70,12 +69,10 @@ set(GLTFIO_SRCS
${GLTFIO_DIR}/src/FilamentAsset.cpp ${GLTFIO_DIR}/src/FilamentAsset.cpp
${GLTFIO_DIR}/src/FilamentInstance.cpp ${GLTFIO_DIR}/src/FilamentInstance.cpp
${GLTFIO_DIR}/src/FNodeManager.h ${GLTFIO_DIR}/src/FNodeManager.h
${GLTFIO_DIR}/src/FTrsTransformManager.h
${GLTFIO_DIR}/src/GltfEnums.h ${GLTFIO_DIR}/src/GltfEnums.h
${GLTFIO_DIR}/src/Ktx2Provider.cpp ${GLTFIO_DIR}/src/Ktx2Provider.cpp
${GLTFIO_DIR}/src/MaterialProvider.cpp ${GLTFIO_DIR}/src/MaterialProvider.cpp
${GLTFIO_DIR}/src/NodeManager.cpp ${GLTFIO_DIR}/src/NodeManager.cpp
${GLTFIO_DIR}/src/TrsTransformManager.cpp
${GLTFIO_DIR}/src/ResourceLoader.cpp ${GLTFIO_DIR}/src/ResourceLoader.cpp
${GLTFIO_DIR}/src/StbProvider.cpp ${GLTFIO_DIR}/src/StbProvider.cpp
${GLTFIO_DIR}/src/TangentsJob.cpp ${GLTFIO_DIR}/src/TangentsJob.cpp
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament GROUP=com.google.android.filament
VERSION_NAME=1.43.1 VERSION_NAME=1.42.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android. POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+8 -18
View File
@@ -87,9 +87,9 @@ compile Filament's native library and Filament's AAR for this project. The easie
is to install all the required dependencies and to run the following commands at the root of the is to install all the required dependencies and to run the following commands at the root of the
source tree: source tree:
```shell ```
./build.sh -p desktop -i release $ ./build.sh -p desktop -i release
./build.sh -p android release $ ./build.sh -p android release
``` ```
This will build all the native components and the AAR required by this sample application. This will build all the native components and the AAR required by this sample application.
@@ -100,8 +100,8 @@ distribution/install directory for desktop (produced by make/ninja install). Thi
contain `bin/matc` and `bin/cmgen`. contain `bin/matc` and `bin/cmgen`.
Example: Example:
```shell ```
./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug $ ./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug
``` ```
## Important: SDK location ## Important: SDK location
@@ -110,24 +110,14 @@ Either ensure your `ANDROID_HOME` environment variable is set or make sure the r
contains a `local.properties` file with the `sdk.dir` property pointing to your installation of contains a `local.properties` file with the `sdk.dir` property pointing to your installation of
the Android SDK. the Android SDK.
## Compiling ## Android Studio
### Android Studio
You must use the latest stable release of Android Studio. To open the project, point Studio to the You must use the latest stable release of Android Studio. To open the project, point Studio to the
`android` folder. After opening the project and syncing to gradle, select the sample of your choice `android` folder. After opening the project and syncing to gradle, select the sample of your choice
using the drop-down widget in the toolbar. using the drop-down widget in the toolbar.
## Compiling
To compile and run each sample make sure you have selected the appropriate build variant To compile and run each sample make sure you have selected the appropriate build variant
(arm7, arm8, x86 or x86_64). If you are not sure you can simply select the "universal" (arm7, arm8, x86 or x86_64). If you are not sure you can simply select the "universal"
variant which includes all the other ones. variant which includes all the other ones.
### Command Line
From the `android` directory in the project root:
```shell
./gradlew :samples:sample-hello-triangle:installDebug
```
Replace `sample-hello-triangle` with your preferred project.
+32 -51
View File
@@ -210,10 +210,13 @@ function build_desktop_target {
echo "Building ${lc_target} in out/cmake-${lc_target}..." echo "Building ${lc_target} in out/cmake-${lc_target}..."
mkdir -p "out/cmake-${lc_target}" mkdir -p "out/cmake-${lc_target}"
pushd "out/cmake-${lc_target}" > /dev/null cd "out/cmake-${lc_target}"
# On macOS, set the deployment target to 10.15.
local lc_name=$(echo "${UNAME}" | tr '[:upper:]' '[:lower:]') local lc_name=$(echo "${UNAME}" | tr '[:upper:]' '[:lower:]')
if [[ "${lc_name}" == "darwin" ]]; then if [[ "${lc_name}" == "darwin" ]]; then
local deployment_target="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15"
if [[ "${BUILD_UNIVERSAL_LIBRARIES}" == "true" ]]; then if [[ "${BUILD_UNIVERSAL_LIBRARIES}" == "true" ]]; then
local architectures="-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" local architectures="-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64"
fi fi
@@ -230,6 +233,7 @@ function build_desktop_target {
${MATDBG_OPTION} \ ${MATDBG_OPTION} \
${MATOPT_OPTION} \ ${MATOPT_OPTION} \
${ASAN_UBSAN_OPTION} \ ${ASAN_UBSAN_OPTION} \
${deployment_target} \
${architectures} \ ${architectures} \
../.. ../..
fi fi
@@ -243,13 +247,12 @@ function build_desktop_target {
if [[ -d "../${lc_target}/filament" ]]; then if [[ -d "../${lc_target}/filament" ]]; then
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
echo "Generating out/filament-${lc_target}-${LC_UNAME}.tgz..." echo "Generating out/filament-${lc_target}-${LC_UNAME}.tgz..."
pushd "../${lc_target}" > /dev/null cd "../${lc_target}"
tar -czvf "../filament-${lc_target}-${LC_UNAME}.tgz" filament tar -czvf "../filament-${lc_target}-${LC_UNAME}.tgz" filament
popd > /dev/null
fi fi
fi fi
popd > /dev/null cd ../..
} }
function build_desktop { function build_desktop {
@@ -267,7 +270,7 @@ function build_webgl_with_target {
echo "Building WebGL ${lc_target}..." echo "Building WebGL ${lc_target}..."
mkdir -p "out/cmake-webgl-${lc_target}" mkdir -p "out/cmake-webgl-${lc_target}"
pushd "out/cmake-webgl-${lc_target}" > /dev/null cd "out/cmake-webgl-${lc_target}"
if [[ ! "${BUILD_TARGETS}" ]]; then if [[ ! "${BUILD_TARGETS}" ]]; then
BUILD_TARGETS=${BUILD_CUSTOM_TARGETS} BUILD_TARGETS=${BUILD_CUSTOM_TARGETS}
@@ -304,17 +307,17 @@ function build_webgl_with_target {
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
echo "Generating out/filament-${lc_target}-web.tgz..." echo "Generating out/filament-${lc_target}-web.tgz..."
pushd web/filament-js > /dev/null cd web/filament-js
tar -cvf "../../../filament-${lc_target}-web.tar" filament.js tar -cvf "../../../filament-${lc_target}-web.tar" filament.js
tar -rvf "../../../filament-${lc_target}-web.tar" filament.wasm tar -rvf "../../../filament-${lc_target}-web.tar" filament.wasm
tar -rvf "../../../filament-${lc_target}-web.tar" filament.d.ts tar -rvf "../../../filament-${lc_target}-web.tar" filament.d.ts
popd > /dev/null cd -
gzip -c "../filament-${lc_target}-web.tar" > "../filament-${lc_target}-web.tgz" gzip -c "../filament-${lc_target}-web.tar" > "../filament-${lc_target}-web.tgz"
rm "../filament-${lc_target}-web.tar" rm "../filament-${lc_target}-web.tar"
fi fi
fi fi
popd > /dev/null cd ../..
} }
function build_webgl { function build_webgl {
@@ -345,7 +348,7 @@ function build_android_target {
echo "Building Android ${lc_target} (${arch})..." echo "Building Android ${lc_target} (${arch})..."
mkdir -p "out/cmake-android-${lc_target}-${arch}" mkdir -p "out/cmake-android-${lc_target}-${arch}"
pushd "out/cmake-android-${lc_target}-${arch}" > /dev/null cd "out/cmake-android-${lc_target}-${arch}"
if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then
cmake \ cmake \
@@ -364,7 +367,7 @@ function build_android_target {
# We must always install Android libraries to build the AAR # We must always install Android libraries to build the AAR
${BUILD_COMMAND} install ${BUILD_COMMAND} install
popd > /dev/null cd ../..
} }
function build_android_arch { function build_android_arch {
@@ -385,9 +388,9 @@ function archive_android {
if [[ -d "out/android-${lc_target}/filament" ]]; then if [[ -d "out/android-${lc_target}/filament" ]]; then
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
echo "Generating out/filament-android-${lc_target}-${LC_UNAME}.tgz..." echo "Generating out/filament-android-${lc_target}-${LC_UNAME}.tgz..."
pushd "out/android-${lc_target}" > /dev/null cd "out/android-${lc_target}"
tar -czvf "../filament-android-${lc_target}-${LC_UNAME}.tgz" filament tar -czvf "../filament-android-${lc_target}-${LC_UNAME}.tgz" filament
popd > /dev/null cd ../..
fi fi
fi fi
} }
@@ -465,7 +468,7 @@ function build_android {
archive_android "Release" archive_android "Release"
fi fi
pushd android > /dev/null cd android
if [[ "${ISSUE_DEBUG_BUILD}" == "true" ]]; then if [[ "${ISSUE_DEBUG_BUILD}" == "true" ]]; then
./gradlew \ ./gradlew \
@@ -566,7 +569,7 @@ function build_android {
fi fi
fi fi
popd > /dev/null cd ..
} }
function build_ios_target { function build_ios_target {
@@ -577,7 +580,7 @@ function build_ios_target {
echo "Building iOS ${lc_target} (${arch}) for ${platform}..." echo "Building iOS ${lc_target} (${arch}) for ${platform}..."
mkdir -p "out/cmake-ios-${lc_target}-${arch}" mkdir -p "out/cmake-ios-${lc_target}-${arch}"
pushd "out/cmake-ios-${lc_target}-${arch}" > /dev/null cd "out/cmake-ios-${lc_target}-${arch}"
if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then
cmake \ cmake \
@@ -601,7 +604,7 @@ function build_ios_target {
${BUILD_COMMAND} ${INSTALL_COMMAND} ${BUILD_COMMAND} ${INSTALL_COMMAND}
fi fi
popd > /dev/null cd ../..
} }
function archive_ios { function archive_ios {
@@ -610,9 +613,9 @@ function archive_ios {
if [[ -d "out/ios-${lc_target}/filament" ]]; then if [[ -d "out/ios-${lc_target}/filament" ]]; then
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
echo "Generating out/filament-${lc_target}-ios.tgz..." echo "Generating out/filament-${lc_target}-ios.tgz..."
pushd "out/ios-${lc_target}" > /dev/null cd "out/ios-${lc_target}"
tar -czvf "../filament-${lc_target}-ios.tgz" filament tar -czvf "../filament-${lc_target}-ios.tgz" filament
popd > /dev/null cd ../..
fi fi
fi fi
} }
@@ -671,14 +674,14 @@ function build_web_docs {
mkdir -p out/web-docs mkdir -p out/web-docs
cp -f docs/web-docs-package.json out/web-docs/package.json cp -f docs/web-docs-package.json out/web-docs/package.json
pushd out/web-docs > /dev/null cd out/web-docs
npm install > /dev/null npm install > /dev/null
# Generate documents # Generate documents
npx markdeep-rasterizer ../../docs/Filament.md.html ../../docs/Materials.md.html ../../docs/ npx markdeep-rasterizer ../../docs/Filament.md.html ../../docs/Materials.md.html ../../docs/
popd > /dev/null cd ../..
} }
function validate_build_command { function validate_build_command {
@@ -751,16 +754,6 @@ function run_tests {
fi fi
} }
function check_debug_release_build {
if [[ "${ISSUE_DEBUG_BUILD}" == "true" || "${ISSUE_RELEASE_BUILD}" == "true" ]]; then
"$@";
else
echo "You must declare a debug or release target for $@ builds."
echo ""
exit 1
fi
}
# Beginning of the script # Beginning of the script
pushd "$(dirname "$0")" > /dev/null pushd "$(dirname "$0")" > /dev/null
@@ -769,7 +762,7 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
case ${opt} in case ${opt} in
h) h)
print_help print_help
exit 0 exit 1
;; ;;
a) a)
ISSUE_ARCHIVES=true ISSUE_ARCHIVES=true
@@ -805,7 +798,7 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
platforms=$(echo "${OPTARG}" | tr ',' '\n') platforms=$(echo "${OPTARG}" | tr ',' '\n')
for platform in ${platforms} for platform in ${platforms}
do do
case $(echo "${platform}" | tr '[:upper:]' '[:lower:]') in case ${platform} in
desktop) desktop)
ISSUE_DESKTOP_BUILD=true ISSUE_DESKTOP_BUILD=true
;; ;;
@@ -824,12 +817,6 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
ISSUE_DESKTOP_BUILD=true ISSUE_DESKTOP_BUILD=true
ISSUE_WEBGL_BUILD=false ISSUE_WEBGL_BUILD=false
;; ;;
*)
echo "Unknown platform ${platform}"
echo "Platform must be one of [desktop|android|ios|webgl|all]"
echo ""
exit 1
;;
esac esac
done done
;; ;;
@@ -842,7 +829,7 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
abis=$(echo "${OPTARG}" | tr ',' '\n') abis=$(echo "${OPTARG}" | tr ',' '\n')
for abi in ${abis} for abi in ${abis}
do do
case $(echo "${abi}" | tr '[:upper:]' '[:lower:]') in case ${abi} in
armeabi-v7a) armeabi-v7a)
ABI_ARMEABI_V7A=true ABI_ARMEABI_V7A=true
;; ;;
@@ -861,12 +848,6 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
ABI_X86=true ABI_X86=true
ABI_X86_64=true ABI_X86_64=true
;; ;;
*)
echo "Unknown abi ${abi}"
echo "ABI must be one of [armeabi-v7a|arm64-v8a|x86|x86_64|all]"
echo ""
exit 1
;;
esac esac
done done
;; ;;
@@ -930,9 +911,9 @@ fi
shift $((OPTIND - 1)) shift $((OPTIND - 1))
for arg; do for arg; do
if [[ $(echo "${arg}" | tr '[:upper:]' '[:lower:]') == "release" ]]; then if [[ "${arg}" == "release" ]]; then
ISSUE_RELEASE_BUILD=true ISSUE_RELEASE_BUILD=true
elif [[ $(echo "${arg}" | tr '[:upper:]' '[:lower:]') == "debug" ]]; then elif [[ "${arg}" == "debug" ]]; then
ISSUE_DEBUG_BUILD=true ISSUE_DEBUG_BUILD=true
else else
BUILD_CUSTOM_TARGETS="${BUILD_CUSTOM_TARGETS} ${arg}" BUILD_CUSTOM_TARGETS="${BUILD_CUSTOM_TARGETS} ${arg}"
@@ -950,19 +931,19 @@ if [[ "${ISSUE_CLEAN_AGGRESSIVE}" == "true" ]]; then
fi fi
if [[ "${ISSUE_DESKTOP_BUILD}" == "true" ]]; then if [[ "${ISSUE_DESKTOP_BUILD}" == "true" ]]; then
check_debug_release_build build_desktop build_desktop
fi fi
if [[ "${ISSUE_ANDROID_BUILD}" == "true" ]]; then if [[ "${ISSUE_ANDROID_BUILD}" == "true" ]]; then
check_debug_release_build build_android build_android
fi fi
if [[ "${ISSUE_IOS_BUILD}" == "true" ]]; then if [[ "${ISSUE_IOS_BUILD}" == "true" ]]; then
check_debug_release_build build_ios build_ios
fi fi
if [[ "${ISSUE_WEBGL_BUILD}" == "true" ]]; then if [[ "${ISSUE_WEBGL_BUILD}" == "true" ]]; then
check_debug_release_build build_webgl build_webgl
fi fi
if [[ "${ISSUE_WEB_DOCS}" == "true" ]]; then if [[ "${ISSUE_WEB_DOCS}" == "true" ]]; then
-2
View File
@@ -230,8 +230,6 @@ set(MATERIAL_SRCS
src/materials/flare/flare.mat src/materials/flare/flare.mat
src/materials/blitLow.mat src/materials/blitLow.mat
src/materials/bloom/bloomDownsample.mat src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
src/materials/bloom/bloomUpsample.mat src/materials/bloom/bloomUpsample.mat
src/materials/ssao/bilateralBlur.mat src/materials/ssao/bilateralBlur.mat
src/materials/ssao/bilateralBlurBentNormals.mat src/materials/ssao/bilateralBlurBentNormals.mat
+7 -7
View File
@@ -61,7 +61,7 @@ with the platform name, for example, `filament-20181009-linux.tgz`.
Create a file, `main.cpp`, in the same directory with the following contents: Create a file, `main.cpp`, in the same directory with the following contents:
```c++ ```
#include <filament/FilamentAPI.h> #include <filament/FilamentAPI.h>
#include <filament/Engine.h> #include <filament/Engine.h>
@@ -91,7 +91,7 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory.
### Linux ### Linux
```make ```
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl
CC=clang++ CC=clang++
@@ -109,7 +109,7 @@ clean:
### macOS ### macOS
```make ```
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl
FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo
CC=clang++ CC=clang++
@@ -137,7 +137,7 @@ be sure to also include `matdbg.lib` in `FILAMENT_LIBS`.
When building Filament from source, the `USE_STATIC_CRT` CMake option can be When building Filament from source, the `USE_STATIC_CRT` CMake option can be
used to change the run-time library version. used to change the run-time library version.
```make ```
FILAMENT_LIBS=filament.lib backend.lib bluegl.lib bluevk.lib filabridge.lib filaflat.lib \ FILAMENT_LIBS=filament.lib backend.lib bluegl.lib bluevk.lib filabridge.lib filaflat.lib \
utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib
CC=cl.exe CC=cl.exe
@@ -174,9 +174,9 @@ and invoke `nmake` instead of `make`.
To generate the documentation you must first install `doxygen` and `graphviz`, then run the To generate the documentation you must first install `doxygen` and `graphviz`, then run the
following commands: following commands:
```shell ```
cd filament/filament $ cd filament/filament
doxygen docs/doxygen/filament.doxygen $ doxygen docs/doxygen/filament.doxygen
``` ```
Finally simply open `docs/html/index.html` in your web browser. Finally simply open `docs/html/index.html` in your web browser.
-1
View File
@@ -412,7 +412,6 @@ if (APPLE)
test/test_RenderExternalImage.cpp test/test_RenderExternalImage.cpp
test/test_StencilBuffer.cpp test/test_StencilBuffer.cpp
test/test_Scissor.cpp test/test_Scissor.cpp
test/test_MipLevels.cpp
) )
target_link_libraries(backend_test PRIVATE target_link_libraries(backend_test PRIVATE
@@ -813,22 +813,22 @@ struct SamplerParams { // NOLINT
struct Hasher { struct Hasher {
size_t operator()(SamplerParams p) const noexcept { size_t operator()(SamplerParams p) const noexcept {
// we don't use std::hash<> here, so we don't have to include <functional> // we don't use std::hash<> here, so we don't have to include <functional>
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p)); return *reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&p));
} }
}; };
struct EqualTo { struct EqualTo {
bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept { bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept {
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs)); auto* pLhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs)); auto* pRhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs == *pRhs; return *pLhs == *pRhs;
} }
}; };
struct LessThan { struct LessThan {
bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept { bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept {
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs)); auto* pLhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs)); auto* pRhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs == *pRhs; return *pLhs == *pRhs;
} }
}; };
@@ -838,7 +838,6 @@ private:
return SamplerParams::LessThan{}(lhs, rhs); return SamplerParams::LessThan{}(lhs, rhs);
} }
}; };
static_assert(sizeof(SamplerParams) == 4);
// The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code // The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code
// see android/.../TextureSampler.cpp // see android/.../TextureSampler.cpp
+2 -3
View File
@@ -67,7 +67,7 @@ public:
Program& operator=(const Program& rhs) = delete; Program& operator=(const Program& rhs) = delete;
Program(Program&& rhs) noexcept; Program(Program&& rhs) noexcept;
Program& operator=(Program&& rhs) noexcept = delete; Program& operator=(Program&& rhs) noexcept;
~Program() noexcept; ~Program() noexcept;
@@ -104,9 +104,8 @@ public:
Sampler const* samplers, size_t count) noexcept; Sampler const* samplers, size_t count) noexcept;
struct SpecializationConstant { struct SpecializationConstant {
using Type = std::variant<int32_t, float, bool>;
uint32_t id; // id set in glsl uint32_t id; // id set in glsl
Type value; // value and type std::variant<int32_t, float, bool> value; // value and type
}; };
Program& specializationConstants( Program& specializationConstants(
@@ -73,14 +73,32 @@ public:
// a cost here (writing and reading the stack at each iteration), in the end it's // a cost here (writing and reading the stack at each iteration), in the end it's
// probably better to pay the cost at just one location. // probably better to pay the cost at just one location.
intptr_t next; intptr_t next;
driver.mCurrentExecutingCommand = this;
mExecute(driver, this, &next); mExecute(driver, this, &next);
return reinterpret_cast<CommandBase*>(reinterpret_cast<intptr_t>(this) + next); return reinterpret_cast<CommandBase*>(reinterpret_cast<intptr_t>(this) + next);
} }
inline void captureCallstack() noexcept {
auto c = utils::CallStack::unwind(4);
size_t i = 0;
for (; i < c.getFrameCount() && i < 16; i++) {
mCallstack[i] = c[i];
}
for (; i < 16; i++) {
mCallstack[i] = 0;
}
}
void printCallstack() noexcept {
auto c = utils::CallStack(mCallstack);
utils::slog.d << c << utils::io::endl;
}
inline ~CommandBase() noexcept = default; inline ~CommandBase() noexcept = default;
private: private:
Execute mExecute; Execute mExecute;
std::array<intptr_t, 16> mCallstack = {0};
}; };
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@@ -218,6 +236,7 @@ public:
using Cmd = COMMAND_TYPE(methodName); \ using Cmd = COMMAND_TYPE(methodName); \
void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \ void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \
new(p) Cmd(mDispatcher.methodName##_, APPLY(std::move, params)); \ new(p) Cmd(mDispatcher.methodName##_, APPLY(std::move, params)); \
((Cmd*)p)->captureCallstack(); \
DEBUG_COMMAND_END(methodName, false); \ DEBUG_COMMAND_END(methodName, false); \
} }
@@ -237,6 +256,7 @@ public:
using Cmd = COMMAND_TYPE(methodName##R); \ using Cmd = COMMAND_TYPE(methodName##R); \
void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \ void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \
new(p) Cmd(mDispatcher.methodName##_, RetType(result), APPLY(std::move, params)); \ new(p) Cmd(mDispatcher.methodName##_, RetType(result), APPLY(std::move, params)); \
((Cmd*)p)->captureCallstack(); \
DEBUG_COMMAND_END(methodName, false); \ DEBUG_COMMAND_END(methodName, false); \
return result; \ return result; \
} }
@@ -53,6 +53,7 @@ template<typename T>
class ConcreteDispatcher; class ConcreteDispatcher;
class Dispatcher; class Dispatcher;
class CommandStream; class CommandStream;
class CommandBase;
class Driver { class Driver {
public: public:
@@ -83,6 +84,8 @@ public:
virtual void debugCommandEnd(CommandStream* cmds, virtual void debugCommandEnd(CommandStream* cmds,
bool synchronous, const char* methodName) noexcept = 0; bool synchronous, const char* methodName) noexcept = 0;
CommandBase* mCurrentExecutingCommand = nullptr;
/* /*
* Asynchronous calls here only to provide a type to CommandStream. They must be non-virtual * Asynchronous calls here only to provide a type to CommandStream. They must be non-virtual
* so that calling the concrete implementation won't go through a vtable. * so that calling the concrete implementation won't go through a vtable.
+11
View File
@@ -26,6 +26,17 @@ Program::Program() noexcept { // NOLINT(modernize-use-equals-default)
Program::Program(Program&& rhs) noexcept = default; Program::Program(Program&& rhs) noexcept = default;
Program& Program::operator=(Program&& rhs) noexcept {
mUniformBlocks.operator=(rhs.mUniformBlocks);
mSamplerGroups.operator=(std::move(rhs.mSamplerGroups));
mShadersSource.operator=(std::move(rhs.mShadersSource));
mName.operator=(std::move(rhs.mName));
mLogger.operator=(std::move(rhs.mLogger));
mSpecializationConstants.operator=(std::move(rhs.mSpecializationConstants));
mBindingUniformInfo.operator=(std::move(rhs.mBindingUniformInfo));
return *this;
}
Program::~Program() noexcept = default; Program::~Program() noexcept = default;
Program& Program::priorityQueue(CompilerPriorityQueue priorityQueue) noexcept { Program& Program::priorityQueue(CompilerPriorityQueue priorityQueue) noexcept {
+67 -41
View File
@@ -345,7 +345,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
auto colorTexture = handle_cast<MetalTexture>(buffer.handle); auto colorTexture = handle_cast<MetalTexture>(buffer.handle);
ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(), ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(),
"Color texture passed to render target has no texture allocation"); "Color texture passed to render target has no texture allocation");
colorTexture->extendLodRangeTo(buffer.level); colorTexture->updateLodRange(buffer.level);
colorAttachments[i] = { colorTexture, color[i].level, color[i].layer }; colorAttachments[i] = { colorTexture, color[i].level, color[i].layer };
} }
@@ -356,7 +356,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
auto depthTexture = handle_cast<MetalTexture>(depth.handle); auto depthTexture = handle_cast<MetalTexture>(depth.handle);
ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(), ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(),
"Depth texture passed to render target has no texture allocation."); "Depth texture passed to render target has no texture allocation.");
depthTexture->extendLodRangeTo(depth.level); depthTexture->updateLodRange(depth.level);
depthAttachment = { depthTexture, depth.level, depth.layer }; depthAttachment = { depthTexture, depth.level, depth.layer };
} }
@@ -367,7 +367,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
auto stencilTexture = handle_cast<MetalTexture>(stencil.handle); auto stencilTexture = handle_cast<MetalTexture>(stencil.handle);
ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(), ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(),
"Stencil texture passed to render target has no texture allocation."); "Stencil texture passed to render target has no texture allocation.");
stencilTexture->extendLodRangeTo(stencil.level); stencilTexture->updateLodRange(stencil.level);
stencilAttachment = { stencilTexture, stencil.level, stencil.layer }; stencilAttachment = { stencilTexture, stencil.level, stencil.layer };
} }
@@ -789,8 +789,6 @@ void MetalDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t ind
} }
void MetalDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) { void MetalDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
auto tex = handle_cast<MetalTexture>(th);
tex->setLodRange(minLevel, maxLevel);
} }
void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level, void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level,
@@ -902,24 +900,29 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
// 2. LOD-clamped textures // 2. LOD-clamped textures
// //
// Both of these cases prevent us from knowing the final id<MTLTexture> that will be bound into // Both of these cases prevent us from knowing the final id<MTLTexture> that will be bound into
// the argument buffer representing the sampler group. So, we wait until draw call time to bind // the argument buffer representing the sampler group. So, we bind what we can now and wait
// textures (done in finalizeSamplerGroup). // until draw call time to bind any special cases (done in finalizeSamplerGroup).
// The good news is that once a render pass has started, the texture bindings won't change. // The good news is that once a render pass has started, the texture bindings won't change.
// A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in // A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in
// a draw call. // a draw call.
// finalizeSamplerGroup has one additional responsibility: to call useResources for all the // Even if we do know all the final textures at this point, we still wait until draw call time
// textures, which is required by Metal. // to call finalizeSamplerGroup, which has one additional responsibility: to call useResources
// for all the textures, which is required by Metal.
for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) { for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) {
if (!samplers[s].t) { if (!samplers[s].t) {
// Assign a default sampler to empty slots. // Assign a default texture / sampler to empty slots.
// Metal requires all samplers referenced in shaders to be bound. // Metal requires all samplers referenced in shaders to be bound.
// An empty texture will be assigned inside finalizeSamplerGroup. id<MTLTexture> empty = getOrCreateEmptyTexture(mContext);
sb->setFinalizedTexture(s, empty);
id<MTLSamplerState> sampler = mContext->samplerStateCache.getOrCreateState({}); id<MTLSamplerState> sampler = mContext->samplerStateCache.getOrCreateState({});
sb->setFinalizedSampler(s, sampler); sb->setFinalizedSampler(s, sampler);
continue; continue;
} }
// Bind the sampler state. We always know the full sampler state at updateSamplerGroup time. // First, bind the sampler state. We always know the full sampler state at
// updateSamplerGroup time.
SamplerState samplerState { SamplerState samplerState {
.samplerParams = samplers[s].s, .samplerParams = samplers[s].s,
}; };
@@ -927,6 +930,27 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
sb->setFinalizedSampler(s, sampler); sb->setFinalizedSampler(s, sampler);
sb->setTextureHandle(s, samplers[s].t); sb->setTextureHandle(s, samplers[s].t);
auto* t = handle_cast<MetalTexture>(samplers[s].t);
assert_invariant(t);
// If this texture is an external texture, we defer binding the texture until draw call time
// (in finalizeSamplerGroup).
if (t->target == SamplerType::SAMPLER_EXTERNAL) {
continue;
}
if (!t->allLodsValid()) {
// The texture doesn't have all of its LODs loaded, and this could change by the time we
// issue a draw call with this sampler group. So, we defer binding the texture until
// draw call time (in finalizeSamplerGroup).
continue;
}
// If we get here, we know we have a valid MTLTexture that's guaranteed not to change.
id<MTLTexture> mtlTexture = t->getMtlTextureForRead();
assert_invariant(mtlTexture);
sb->setFinalizedTexture(s, mtlTexture);
} }
scheduleDestroy(std::move(data)); scheduleDestroy(std::move(data));
@@ -1352,8 +1376,8 @@ void MetalDriver::blit(TargetBufferFlags buffers,
} }
void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) { void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
// All the id<MTLSamplerState> objects have already been bound to the argument buffer. // All of the id<MTLSamplerState> objects have already been bound to the argument buffer.
// Here we bind all the textures. // Here we bind any textures that were unable to be bound in updateSamplerGroup.
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext); id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);
@@ -1375,38 +1399,28 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
} }
#endif #endif
utils::FixedCapacityVector<id<MTLTexture>> newTextures(samplerGroup->size, nil);
for (size_t binding = 0; binding < samplerGroup->size; binding++) { for (size_t binding = 0; binding < samplerGroup->size; binding++) {
auto [th, _] = samplerGroup->getFinalizedTexture(binding); auto [th, t] = samplerGroup->getFinalizedTexture(binding);
if (!th) { // This may be an external texture, in which case we can't cache the id<MTLTexture>, we
// Bind an empty texture. // need to refetch it in case the external image has changed.
newTextures[binding] = getOrCreateEmptyTexture(mContext); bool isExternalImage = false;
if (th) {
auto* texture = handle_cast<MetalTexture>(th);
isExternalImage = texture->target == SamplerType::SAMPLER_EXTERNAL;
}
// If t is non-nil, then we've already finalized this texture.
if (t && !isExternalImage) {
continue; continue;
} }
// It's possible that some texture handles are null, but we should have already handled
// these inside updateSamplerGroup by binding an "empty" texture.
assert_invariant(th); assert_invariant(th);
auto* texture = handle_cast<MetalTexture>(th); auto* texture = handle_cast<MetalTexture>(th);
// External images // Determine if this SamplerGroup needs mutation.
if (texture->target == SamplerType::SAMPLER_EXTERNAL) {
if (texture->externalImage.isValid()) {
id<MTLTexture> mtlTexture = texture->externalImage.getMetalTextureForDraw();
assert_invariant(mtlTexture);
newTextures[binding] = mtlTexture;
} else {
// Bind an empty texture.
newTextures[binding] = getOrCreateEmptyTexture(mContext);
}
continue;
}
newTextures[binding] = texture->getMtlTextureForRead();
}
if (!std::equal(newTextures.begin(), newTextures.end(), samplerGroup->textures.begin())) {
// One or more of the id<MTLTexture>s has changed.
// First, determine if this SamplerGroup needs mutation.
// We can't just simply mutate the SamplerGroup, since it could currently be in use by the // We can't just simply mutate the SamplerGroup, since it could currently be in use by the
// GPU from a prior render pass. // GPU from a prior render pass.
// If the SamplerGroup does need mutation, then there's two cases: // If the SamplerGroup does need mutation, then there's two cases:
@@ -1414,17 +1428,29 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
// draw call). We're free to mutate it. // draw call). We're free to mutate it.
// 2. The SamplerGroup is finalized. We must call mutate(), which will create a new argument // 2. The SamplerGroup is finalized. We must call mutate(), which will create a new argument
// buffer that we can then mutate freely. // buffer that we can then mutate freely.
// TODO: don't just always call mutate, check to see if the texture is actually different.
if (samplerGroup->isFinalized()) { if (samplerGroup->isFinalized()) {
samplerGroup->mutate(cmdBuffer); samplerGroup->mutate(cmdBuffer);
} }
for (size_t binding = 0; binding < samplerGroup->size; binding++) { // External images
samplerGroup->setFinalizedTexture(binding, newTextures[binding]); if (texture->target == SamplerType::SAMPLER_EXTERNAL) {
if (texture->externalImage.isValid()) {
id<MTLTexture> mtlTexture = texture->externalImage.getMetalTextureForDraw();
assert_invariant(mtlTexture);
samplerGroup->setFinalizedTexture(binding, mtlTexture);
} else {
// Bind an empty texture.
samplerGroup->setFinalizedTexture(binding, getOrCreateEmptyTexture(mContext));
}
continue;
}
samplerGroup->setFinalizedTexture(binding, texture->getMtlTextureForRead());
} }
samplerGroup->finalize(); samplerGroup->finalize();
}
// At this point, all the id<MTLTextures> should be set to valid textures. Some of them will be // At this point, all the id<MTLTextures> should be set to valid textures. Some of them will be
// the "empty" texture. Per Apple documentation, the useResource method must be called once per // the "empty" texture. Per Apple documentation, the useResource method must be called once per
+11 -4
View File
@@ -217,14 +217,21 @@ public:
void generateMipmaps() noexcept; void generateMipmaps() noexcept;
// A texture starts out with none of its mip levels (also referred to as LODs) available for // A texture starts out with none of its mip levels (also referred to as LODs) available for
// reading. 4 actions update the range of LODs available: // reading. 3 actions update the range of LODs available:
// - calling loadImage // - calling loadImage
// - calling generateMipmaps // - calling generateMipmaps
// - using the texture as a render target attachment // - using the texture as a render target attachment
// - calling setMinMaxLevels // The range of available mips can only increase, never decrease.
// A texture's available mips are consistent throughout a render pass. // A texture's available mips are consistent throughout a render pass.
void setLodRange(uint32_t minLevel, uint32_t maxLevel); void updateLodRange(uint32_t level);
void extendLodRangeTo(uint32_t level); void updateLodRange(uint32_t minLevel, uint32_t maxLevel);
// Returns true if the texture has all of its mip levels accessible for reading.
// For any MetalTexture, once this is true, will always return true.
// The value returned will remain consistent for an entire render pass.
bool allLodsValid() const {
return minLod == 0 && maxLod == levels - 1;
}
static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format); static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);
+7 -7
View File
@@ -513,7 +513,7 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
: HwTexture(target, levels, samples, width, height, depth, format, usage), context(context), : HwTexture(target, levels, samples, width, height, depth, format, usage), context(context),
externalImage(context) { externalImage(context) {
texture = metalTexture; texture = metalTexture;
setLodRange(0, levels - 1); updateLodRange(0, levels - 1);
} }
MetalTexture::~MetalTexture() { MetalTexture::~MetalTexture() {
@@ -658,14 +658,14 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
} }
} }
extendLodRangeTo(level); updateLodRange(level);
} }
void MetalTexture::generateMipmaps() noexcept { void MetalTexture::generateMipmaps() noexcept {
id <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(&context) blitCommandEncoder]; id <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(&context) blitCommandEncoder];
[blitEncoder generateMipmapsForTexture:texture]; [blitEncoder generateMipmapsForTexture:texture];
[blitEncoder endEncoding]; [blitEncoder endEncoding];
setLodRange(0, texture.mipmapLevelCount - 1); updateLodRange(0, texture.mipmapLevelCount - 1);
} }
void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice, void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
@@ -788,18 +788,18 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit"); context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit");
} }
void MetalTexture::extendLodRangeTo(uint32_t level) { void MetalTexture::updateLodRange(uint32_t level) {
assert_invariant(!isInRenderPass(&context)); assert_invariant(!isInRenderPass(&context));
minLod = std::min(minLod, level); minLod = std::min(minLod, level);
maxLod = std::max(maxLod, level); maxLod = std::max(maxLod, level);
lodTextureView = nil; lodTextureView = nil;
} }
void MetalTexture::setLodRange(uint32_t min, uint32_t max) { void MetalTexture::updateLodRange(uint32_t min, uint32_t max) {
assert_invariant(!isInRenderPass(&context)); assert_invariant(!isInRenderPass(&context));
assert_invariant(min <= max); assert_invariant(min <= max);
minLod = min; minLod = std::min(minLod, min);
maxLod = max; maxLod = std::max(maxLod, max);
lodTextureView = nil; lodTextureView = nil;
} }
@@ -103,11 +103,6 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
return programData; return programData;
} }
void wait() const noexcept {
std::unique_lock l(lock);
cond.wait(l, [this](){ return signaled; });
}
// Checks if the programBinary is ready. // Checks if the programBinary is ready.
// This is similar to std::future::wait_for(0s) // This is similar to std::future::wait_for(0s)
bool isReady() const noexcept { bool isReady() const noexcept {
@@ -341,23 +336,7 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t&
token->canceled = true; token->canceled = true;
bool canceled = token->compiler.cancelTickOp(token); token->compiler.cancelTickOp(token);
if (token->compiler.mShaderCompilerThreadCount) {
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
if (!job) {
// The job is being executed right now. We need to wait for it to finish to avoid a
// race.
token->wait();
} else {
// The job has not been executed, but we still need to inform the callback manager in
// order for future callbacks to be successfully called.
token->compiler.mCallbackManager.put(token->handle);
}
} else if (canceled) {
// Since the tick op was canceled, we need to .put the token here.
token->compiler.mCallbackManager.put(token->handle);
}
for (GLuint& shader: token->gl.shaders) { for (GLuint& shader: token->gl.shaders) {
if (shader) { if (shader) {
@@ -683,7 +662,7 @@ void ShaderCompilerService::runAtNextTick(CompilerPriorityQueue priority,
SYSTRACE_VALUE32("ShaderCompilerService Jobs", mRunAtNextTickOps.size()); SYSTRACE_VALUE32("ShaderCompilerService Jobs", mRunAtNextTickOps.size());
} }
bool ShaderCompilerService::cancelTickOp(program_token_t token) noexcept { void ShaderCompilerService::cancelTickOp(program_token_t token) noexcept {
// We do a linear search here, but this is rare, and we know the list is pretty small. // We do a linear search here, but this is rare, and we know the list is pretty small.
auto& ops = mRunAtNextTickOps; auto& ops = mRunAtNextTickOps;
auto pos = std::find_if(ops.begin(), ops.end(), [&](const auto& item) { auto pos = std::find_if(ops.begin(), ops.end(), [&](const auto& item) {
@@ -691,11 +670,9 @@ bool ShaderCompilerService::cancelTickOp(program_token_t token) noexcept {
}); });
if (pos != ops.end()) { if (pos != ops.end()) {
ops.erase(pos); ops.erase(pos);
return true;
} }
SYSTRACE_CONTEXT(); SYSTRACE_CONTEXT();
SYSTRACE_VALUE32("ShaderCompilerService Jobs", ops.size()); SYSTRACE_VALUE32("ShaderCompilerService Jobs", ops.size());
return false;
} }
void ShaderCompilerService::executeTickOps() noexcept { void ShaderCompilerService::executeTickOps() noexcept {
@@ -141,7 +141,7 @@ private:
void runAtNextTick(CompilerPriorityQueue priority, void runAtNextTick(CompilerPriorityQueue priority,
const program_token_t& token, Job job) noexcept; const program_token_t& token, Job job) noexcept;
void executeTickOps() noexcept; void executeTickOps() noexcept;
bool cancelTickOp(program_token_t token) noexcept; void cancelTickOp(program_token_t token) noexcept;
// order of insertion is important // order of insertion is important
using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>; using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>;
+12 -8
View File
@@ -28,6 +28,8 @@
#include "generated/vkshaders/vkshaders.h" #include "generated/vkshaders/vkshaders.h"
#define FILAMENT_VULKAN_CHECK_BLIT_FORMAT 0
using namespace bluevk; using namespace bluevk;
using namespace utils; using namespace utils;
@@ -67,7 +69,7 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
.layerCount = 1, .layerCount = 1,
}; };
if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) { if constexpr (FILAMENT_VULKAN_VERBOSE) {
utils::slog.d << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level utils::slog.d << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
<< "layout=" << src.getLayout() << "layout=" << src.getLayout()
<< " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level << " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level
@@ -130,7 +132,7 @@ void VulkanBlitter::blitColor(BlitArgs args) {
const VulkanAttachment dst = args.dstTarget->getColor(0); const VulkanAttachment dst = args.dstTarget->getColor(0);
const VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT; const VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT;
#if FVK_ENABLED(FVK_DEBUG_BLIT_FORMAT) #if FILAMENT_VULKAN_CHECK_BLIT_FORMAT
VkPhysicalDevice const gpu = mPhysicalDevice; VkPhysicalDevice const gpu = mPhysicalDevice;
VkFormatProperties info; VkFormatProperties info;
vkGetPhysicalDeviceFormatProperties(gpu, src.getFormat(), &info); vkGetPhysicalDeviceFormatProperties(gpu, src.getFormat(), &info);
@@ -145,7 +147,7 @@ void VulkanBlitter::blitColor(BlitArgs args) {
} }
#endif #endif
VulkanCommandBuffer& commands = mCommands->get(); VulkanCommandBuffer& commands = mCommands->get();
VkCommandBuffer const cmdbuffer = commands.buffer(); VkCommandBuffer const cmdbuffer = commands.cmdbuffer;
commands.acquire(src.texture); commands.acquire(src.texture);
commands.acquire(dst.texture); commands.acquire(dst.texture);
@@ -158,7 +160,7 @@ void VulkanBlitter::blitDepth(BlitArgs args) {
const VulkanAttachment dst = args.dstTarget->getDepth(); const VulkanAttachment dst = args.dstTarget->getDepth();
const VkImageAspectFlags aspect = VK_IMAGE_ASPECT_DEPTH_BIT; const VkImageAspectFlags aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
#if FVK_ENABLED(FVK_DEBUG_BLIT_FORMAT) #if FILAMENT_VULKAN_CHECK_BLIT_FORMAT
VkPhysicalDevice const gpu = mPhysicalDevice; VkPhysicalDevice const gpu = mPhysicalDevice;
VkFormatProperties info; VkFormatProperties info;
vkGetPhysicalDeviceFormatProperties(gpu, src.getFormat(), &info); vkGetPhysicalDeviceFormatProperties(gpu, src.getFormat(), &info);
@@ -182,7 +184,7 @@ void VulkanBlitter::blitDepth(BlitArgs args) {
} }
VulkanCommandBuffer& commands = mCommands->get(); VulkanCommandBuffer& commands = mCommands->get();
VkCommandBuffer const cmdbuffer = commands.buffer(); VkCommandBuffer const cmdbuffer = commands.cmdbuffer;
commands.acquire(src.texture); commands.acquire(src.texture);
commands.acquire(dst.texture); commands.acquire(dst.texture);
blitFast(cmdbuffer, aspect, args.filter, args.srcTarget->getExtent(), src, dst, args.srcRectPair, blitFast(cmdbuffer, aspect, args.filter, args.srcTarget->getExtent(), src, dst, args.srcRectPair,
@@ -195,11 +197,13 @@ void VulkanBlitter::terminate() noexcept {
mDepthResolveProgram = nullptr; mDepthResolveProgram = nullptr;
if (mTriangleBuffer) { if (mTriangleBuffer) {
mTriangleBuffer->terminate();
delete mTriangleBuffer; delete mTriangleBuffer;
mTriangleBuffer = nullptr; mTriangleBuffer = nullptr;
} }
if (mParamsBuffer) { if (mParamsBuffer) {
mParamsBuffer->terminate();
delete mParamsBuffer; delete mParamsBuffer;
mParamsBuffer = nullptr; mParamsBuffer = nullptr;
} }
@@ -239,7 +243,7 @@ void VulkanBlitter::lazyInit() noexcept {
mDepthResolveProgram->samplerGroupInfo[0].samplers.reserve(1); mDepthResolveProgram->samplerGroupInfo[0].samplers.reserve(1);
mDepthResolveProgram->samplerGroupInfo[0].samplers.resize(1); mDepthResolveProgram->samplerGroupInfo[0].samplers.resize(1);
if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) { if constexpr (FILAMENT_VULKAN_VERBOSE) {
utils::slog.d << "Created Shader Module for VulkanBlitter " utils::slog.d << "Created Shader Module for VulkanBlitter "
<< "shaders = (" << vertexShader << ", " << fragmentShader << ")" << "shaders = (" << vertexShader << ", " << fragmentShader << ")"
<< utils::io::endl; << utils::io::endl;
@@ -253,7 +257,7 @@ void VulkanBlitter::lazyInit() noexcept {
}; };
VulkanCommandBuffer& commands = mCommands->get(); VulkanCommandBuffer& commands = mCommands->get();
VkCommandBuffer const cmdbuffer = commands.buffer(); VkCommandBuffer const cmdbuffer = commands.cmdbuffer;
mTriangleBuffer = new VulkanBuffer(mAllocator, mStagePool, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, mTriangleBuffer = new VulkanBuffer(mAllocator, mStagePool, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
sizeof(kTriangleVertices)); sizeof(kTriangleVertices));
@@ -274,7 +278,7 @@ void VulkanBlitter::blitSlowDepth(VkFilter filter, const VkExtent2D srcExtent, V
lazyInit(); lazyInit();
VulkanCommandBuffer* commands = &mCommands->get(); VulkanCommandBuffer* commands = &mCommands->get();
VkCommandBuffer const cmdbuffer = commands->buffer(); VkCommandBuffer const cmdbuffer = commands->cmdbuffer;
commands->acquire(src.texture); commands->acquire(src.texture);
commands->acquire(dst.texture); commands->acquire(dst.texture);
@@ -45,7 +45,14 @@ VulkanBuffer::VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool,
} }
VulkanBuffer::~VulkanBuffer() { VulkanBuffer::~VulkanBuffer() {
assert_invariant(mGpuMemory == VK_NULL_HANDLE);
assert_invariant(mGpuBuffer == VK_NULL_HANDLE);
}
void VulkanBuffer::terminate() {
vmaDestroyBuffer(mAllocator, mGpuBuffer, mGpuMemory); vmaDestroyBuffer(mAllocator, mGpuBuffer, mGpuMemory);
mGpuMemory = VK_NULL_HANDLE;
mGpuBuffer = VK_NULL_HANDLE;
} }
void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint32_t byteOffset, void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint32_t byteOffset,
@@ -28,6 +28,7 @@ public:
VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool, VkBufferUsageFlags usage, VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool, VkBufferUsageFlags usage,
uint32_t numBytes); uint32_t numBytes);
~VulkanBuffer(); ~VulkanBuffer();
void terminate();
void loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint32_t byteOffset, void loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint32_t byteOffset,
uint32_t numBytes) const; uint32_t numBytes) const;
VkBuffer getGpuBuffer() const { VkBuffer getGpuBuffer() const {
+70 -99
View File
@@ -33,32 +33,23 @@ using namespace utils;
namespace filament::backend { namespace filament::backend {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
using Timestamp = VulkanGroupMarkers::Timestamp; using Timestamp = VulkanGroupMarkers::Timestamp;
#endif
VulkanCmdFence::VulkanCmdFence(VkFence ifence) VulkanCmdFence::VulkanCmdFence(VkDevice device, bool signaled) : device(device) {
: fence(ifence) { VkFenceCreateInfo fenceCreateInfo { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
if (signaled) {
fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
}
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence);
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence gets // Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence gets
// submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually finishes // submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually finishes
// executing the command buffer, the status changes to VK_SUCCESS. // executing the command buffer, the status changes to VK_SUCCESS.
status.store(VK_INCOMPLETE); status.store(VK_INCOMPLETE);
} }
VulkanCommandBuffer::VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device, VulkanCmdFence::~VulkanCmdFence() {
VkCommandPool pool) vkDestroyFence(device, fence, VKALLOC);
: mResourceManager(allocator) {
// Create the low-level command buffer.
const VkCommandBufferAllocateInfo allocateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
// The buffer allocated here will be implicitly reset when vkBeginCommandBuffer is called.
// We don't need to deallocate since destroying the pool will free all of the buffers.
vkAllocateCommandBuffers(device, &allocateInfo, &mBuffer);
} }
CommandBufferObserver::~CommandBufferObserver() {} CommandBufferObserver::~CommandBufferObserver() {}
@@ -66,20 +57,19 @@ CommandBufferObserver::~CommandBufferObserver() {}
static VkCommandPool createPool(VkDevice device, uint32_t queueFamilyIndex) { static VkCommandPool createPool(VkDevice device, uint32_t queueFamilyIndex) {
VkCommandPoolCreateInfo createInfo = { VkCommandPoolCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT .flags =
| VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
.queueFamilyIndex = queueFamilyIndex, .queueFamilyIndex = queueFamilyIndex,
}; };
VkCommandPool pool; VkCommandPool pool;
vkCreateCommandPool(device, &createInfo, VKALLOC, &pool); vkCreateCommandPool(device, &createInfo, VKALLOC, &pool);
return pool; return pool;
} }
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept { void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept {
mMarkers.push_back(marker); mMarkers.push_back(marker);
#if FILAMENT_VULKAN_VERBOSE
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
mTimestamps.push_back(start.time_since_epoch().count() > 0.0 mTimestamps.push_back(start.time_since_epoch().count() > 0.0
? start ? start
: std::chrono::high_resolution_clock::now()); : std::chrono::high_resolution_clock::now());
@@ -90,7 +80,7 @@ std::pair<std::string, Timestamp> VulkanGroupMarkers::pop() noexcept {
auto const marker = mMarkers.back(); auto const marker = mMarkers.back();
mMarkers.pop_back(); mMarkers.pop_back();
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) #if FILAMENT_VULKAN_VERBOSE
auto const timestamp = mTimestamps.back(); auto const timestamp = mTimestamps.back();
mTimestamps.pop_back(); mTimestamps.pop_back();
return std::make_pair(marker, timestamp); return std::make_pair(marker, timestamp);
@@ -103,7 +93,7 @@ std::pair<std::string, Timestamp> VulkanGroupMarkers::pop_bottom() noexcept {
auto const marker = mMarkers.front(); auto const marker = mMarkers.front();
mMarkers.pop_front(); mMarkers.pop_front();
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) #if FILAMENT_VULKAN_VERBOSE
auto const timestamp = mTimestamps.front(); auto const timestamp = mTimestamps.front();
mTimestamps.pop_front(); mTimestamps.pop_front();
return std::make_pair(marker, timestamp); return std::make_pair(marker, timestamp);
@@ -115,8 +105,8 @@ std::pair<std::string, Timestamp> VulkanGroupMarkers::pop_bottom() noexcept {
std::pair<std::string, Timestamp> VulkanGroupMarkers::top() const { std::pair<std::string, Timestamp> VulkanGroupMarkers::top() const {
assert_invariant(!empty()); assert_invariant(!empty());
auto const marker = mMarkers.back(); auto const marker = mMarkers.back();
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) #if FILAMENT_VULKAN_VERBOSE
auto const topTimestamp = mTimestamps.front(); auto const topTimestamp = mTimestamps.top();
return std::make_pair(marker, topTimestamp); return std::make_pair(marker, topTimestamp);
#else #else
return std::make_pair(marker, Timestamp{}); return std::make_pair(marker, Timestamp{});
@@ -127,8 +117,6 @@ bool VulkanGroupMarkers::empty() const noexcept {
return mMarkers.empty(); return mMarkers.empty();
} }
#endif // FVK_DEBUG_GROUP_MARKERS
VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex, VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VulkanContext* context, VulkanResourceAllocator* allocator) VulkanContext* context, VulkanResourceAllocator* allocator)
: mDevice(device), : mDevice(device),
@@ -141,18 +129,9 @@ VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFam
vkCreateSemaphore(mDevice, &sci, nullptr, &semaphore); vkCreateSemaphore(mDevice, &sci, nullptr, &semaphore);
} }
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
for (auto& fence: mFences) {
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence);
}
for (size_t i = 0; i < CAPACITY; ++i) { for (size_t i = 0; i < CAPACITY; ++i) {
mStorage[i] = std::make_unique<VulkanCommandBuffer>(allocator, mDevice, mPool); mStorage[i] = std::make_unique<VulkanCommandBuffer>(allocator);
} }
#if !FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
(void) mContext;
#endif
} }
VulkanCommands::~VulkanCommands() { VulkanCommands::~VulkanCommands() {
@@ -162,9 +141,6 @@ VulkanCommands::~VulkanCommands() {
for (VkSemaphore sema : mSubmissionSignals) { for (VkSemaphore sema : mSubmissionSignals) {
vkDestroySemaphore(mDevice, sema, VKALLOC); vkDestroySemaphore(mDevice, sema, VKALLOC);
} }
for (VkFence fence: mFences) {
vkDestroyFence(mDevice, fence, VKALLOC);
}
} }
VulkanCommandBuffer& VulkanCommands::get() { VulkanCommandBuffer& VulkanCommands::get() {
@@ -175,8 +151,8 @@ VulkanCommandBuffer& VulkanCommands::get() {
// If we ran out of available command buffers, stall until one finishes. This is very rare. // If we ran out of available command buffers, stall until one finishes. This is very rare.
// It occurs only when Filament invokes commit() or endFrame() a large number of times without // It occurs only when Filament invokes commit() or endFrame() a large number of times without
// presenting the swap chain or waiting on a fence. // presenting the swap chain or waiting on a fence.
while (mAvailableBufferCount == 0) { while (mAvailableCount == 0) {
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER) #if VK_REPORT_STALLS
slog.i << "VulkanCommands has stalled. " slog.i << "VulkanCommands has stalled. "
<< "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS." << "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS."
<< io::endl; << io::endl;
@@ -189,7 +165,7 @@ VulkanCommandBuffer& VulkanCommands::get() {
// Find an available slot. // Find an available slot.
for (size_t i = 0; i < CAPACITY; ++i) { for (size_t i = 0; i < CAPACITY; ++i) {
auto wrapper = mStorage[i].get(); auto wrapper = mStorage[i].get();
if (wrapper->buffer() == VK_NULL_HANDLE) { if (wrapper->cmdbuffer == VK_NULL_HANDLE) {
mCurrentCommandBufferIndex = static_cast<int8_t>(i); mCurrentCommandBufferIndex = static_cast<int8_t>(i);
currentbuf = wrapper; currentbuf = wrapper;
break; break;
@@ -197,33 +173,41 @@ VulkanCommandBuffer& VulkanCommands::get() {
} }
assert_invariant(currentbuf); assert_invariant(currentbuf);
mAvailableBufferCount--; --mAvailableCount;
// Create the low-level command buffer.
const VkCommandBufferAllocateInfo allocateInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = mPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1
};
vkAllocateCommandBuffers(mDevice, &allocateInfo, &currentbuf->cmdbuffer);
// Note that the fence wrapper uses shared_ptr because a DriverAPI fence can also have ownership // Note that the fence wrapper uses shared_ptr because a DriverAPI fence can also have ownership
// over it. The destruction of the low-level fence occurs either in VulkanCommands::gc(), or in // over it. The destruction of the low-level fence occurs either in VulkanCommands::gc(), or in
// VulkanDriver::destroyFence(), both of which are safe spots. // VulkanDriver::destroyFence(), both of which are safe spots.
currentbuf->fence = std::make_shared<VulkanCmdFence>(mFences[mCurrentCommandBufferIndex]); currentbuf->fence = std::make_shared<VulkanCmdFence>(mDevice);
// Begin writing into the command buffer. // Begin writing into the command buffer.
const VkCommandBufferBeginInfo binfo { const VkCommandBufferBeginInfo binfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
}; };
vkBeginCommandBuffer(currentbuf->buffer(), &binfo); vkBeginCommandBuffer(currentbuf->cmdbuffer, &binfo);
// Notify the observer that a new command buffer has been activated. // Notify the observer that a new command buffer has been activated.
if (mObserver) { if (mObserver) {
mObserver->onCommandBuffer(*currentbuf); mObserver->onCommandBuffer(*currentbuf);
} }
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
// We push the current markers onto a temporary stack. This must be placed after currentbuf is // We push the current markers onto a temporary stack. This must be placed after currentbuf is
// set to the new command buffer since pushGroupMarker also calls get(). // set to the new command buffer since pushGroupMarker also calls get().
while (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) { while (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) {
auto [marker, time] = mCarriedOverMarkers->pop(); auto [marker, time] = mCarriedOverMarkers->pop();
pushGroupMarker(marker.c_str(), time); pushGroupMarker(marker.c_str(), time);
} }
#endif
return *currentbuf; return *currentbuf;
} }
@@ -233,25 +217,11 @@ bool VulkanCommands::flush() {
return false; return false;
} }
// Before actually submitting, we need to pop any leftover group markers.
// Note that this needs to occur before vkEndCommandBuffer.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
while (mGroupMarkers && !mGroupMarkers->empty()) {
if (!mCarriedOverMarkers) {
mCarriedOverMarkers = std::make_unique<VulkanGroupMarkers>();
}
auto const [marker, time] = mGroupMarkers->top();
mCarriedOverMarkers->push(marker, time);
// We still need to call through to vkCmdEndDebugUtilsLabelEXT.
popGroupMarker();
}
#endif
int8_t const index = mCurrentCommandBufferIndex; int8_t const index = mCurrentCommandBufferIndex;
VulkanCommandBuffer const* currentbuf = mStorage[index].get(); VulkanCommandBuffer const* currentbuf = mStorage[index].get();
VkSemaphore const renderingFinished = mSubmissionSignals[index]; VkSemaphore const renderingFinished = mSubmissionSignals[index];
vkEndCommandBuffer(currentbuf->buffer()); vkEndCommandBuffer(currentbuf->cmdbuffer);
// If the injected semaphore is an "image available" semaphore that has not yet been signaled, // If the injected semaphore is an "image available" semaphore that has not yet been signaled,
// it is sometimes fine to start executing commands anyway, as along as we stall the GPU at the // it is sometimes fine to start executing commands anyway, as along as we stall the GPU at the
@@ -269,15 +239,13 @@ bool VulkanCommands::flush() {
VK_NULL_HANDLE, VK_NULL_HANDLE,
}; };
VkCommandBuffer const cmdbuffer = currentbuf->buffer();
VkSubmitInfo submitInfo { VkSubmitInfo submitInfo {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 0, .waitSemaphoreCount = 0,
.pWaitSemaphores = signals, .pWaitSemaphores = signals,
.pWaitDstStageMask = waitDestStageMasks, .pWaitDstStageMask = waitDestStageMasks,
.commandBufferCount = 1, .commandBufferCount = 1,
.pCommandBuffers = &cmdbuffer, .pCommandBuffers = &currentbuf->cmdbuffer,
.signalSemaphoreCount = 1u, .signalSemaphoreCount = 1u,
.pSignalSemaphores = &renderingFinished, .pSignalSemaphores = &renderingFinished,
}; };
@@ -295,14 +263,24 @@ bool VulkanCommands::flush() {
submitInfo.pWaitSemaphores = VK_NULL_HANDLE; submitInfo.pWaitSemaphores = VK_NULL_HANDLE;
} }
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER) #if FILAMENT_VULKAN_VERBOSE
slog.i << "Submitting cmdbuffer=" << cmdbuffer slog.i << "Submitting cmdbuffer=" << currentbuf->cmdbuffer
<< " wait=(" << signals[0] << ", " << signals[1] << ") " << " wait=(" << signals[0] << ", " << signals[1] << ") "
<< " signal=" << renderingFinished << " signal=" << renderingFinished
<< " fence=" << currentbuf->fence->fence << io::endl;
<< utils::io::endl;
#endif #endif
// Before actually submitting, we need to pop any leftover group markers.
while (mGroupMarkers && !mGroupMarkers->empty()) {
if (!mCarriedOverMarkers) {
mCarriedOverMarkers = std::make_unique<VulkanGroupMarkers>();
}
auto const [marker, time] = mGroupMarkers->top();
mCarriedOverMarkers->push(marker, time);
// We still need to call through to vkCmdEndDebugUtilsLabelEXT.
popGroupMarker();
}
auto& cmdfence = currentbuf->fence; auto& cmdfence = currentbuf->fence;
std::unique_lock<utils::Mutex> lock(cmdfence->mutex); std::unique_lock<utils::Mutex> lock(cmdfence->mutex);
cmdfence->status.store(VK_NOT_READY); cmdfence->status.store(VK_NOT_READY);
@@ -310,7 +288,7 @@ bool VulkanCommands::flush() {
cmdfence->condition.notify_all(); cmdfence->condition.notify_all();
lock.unlock(); lock.unlock();
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER) #if FILAMENT_VULKAN_VERBOSE
if (result != VK_SUCCESS) { if (result != VK_SUCCESS) {
utils::slog.d <<"Failed command buffer submission result: " << result << utils::io::endl; utils::slog.d <<"Failed command buffer submission result: " << result << utils::io::endl;
} }
@@ -326,7 +304,7 @@ bool VulkanCommands::flush() {
VkSemaphore VulkanCommands::acquireFinishedSignal() { VkSemaphore VulkanCommands::acquireFinishedSignal() {
VkSemaphore semaphore = mSubmissionSignal; VkSemaphore semaphore = mSubmissionSignal;
mSubmissionSignal = VK_NULL_HANDLE; mSubmissionSignal = VK_NULL_HANDLE;
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER) #if FILAMENT_VULKAN_VERBOSE
slog.i << "Acquiring " << semaphore << " (e.g. for vkQueuePresentKHR)" << io::endl; slog.i << "Acquiring " << semaphore << " (e.g. for vkQueuePresentKHR)" << io::endl;
#endif #endif
return semaphore; return semaphore;
@@ -335,7 +313,7 @@ VkSemaphore VulkanCommands::acquireFinishedSignal() {
void VulkanCommands::injectDependency(VkSemaphore next) { void VulkanCommands::injectDependency(VkSemaphore next) {
assert_invariant(mInjectedSignal == VK_NULL_HANDLE); assert_invariant(mInjectedSignal == VK_NULL_HANDLE);
mInjectedSignal = next; mInjectedSignal = next;
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER) #if FILAMENT_VULKAN_VERBOSE
slog.i << "Injecting " << next << " (e.g. due to vkAcquireNextImageKHR)" << io::endl; slog.i << "Injecting " << next << " (e.g. due to vkAcquireNextImageKHR)" << io::endl;
#endif #endif
} }
@@ -345,49 +323,44 @@ void VulkanCommands::wait() {
size_t count = 0; size_t count = 0;
for (size_t i = 0; i < CAPACITY; i++) { for (size_t i = 0; i < CAPACITY; i++) {
auto wrapper = mStorage[i].get(); auto wrapper = mStorage[i].get();
if (wrapper->buffer() != VK_NULL_HANDLE if (wrapper->cmdbuffer != VK_NULL_HANDLE
&& mCurrentCommandBufferIndex != static_cast<int8_t>(i)) { && mCurrentCommandBufferIndex != static_cast<int8_t>(i)) {
fences[count++] = wrapper->fence->fence; fences[count++] = wrapper->fence->fence;
} }
} }
if (count > 0) { if (count > 0) {
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX); vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
updateFences();
} }
} }
void VulkanCommands::gc() { void VulkanCommands::gc() {
FVK_SYSTRACE_CONTEXT(); VkCommandBuffer buffers[CAPACITY];
FVK_SYSTRACE_START("commands::gc");
VkFence fences[CAPACITY];
size_t count = 0; size_t count = 0;
for (size_t i = 0; i < CAPACITY; i++) { for (size_t i = 0; i < CAPACITY; i++) {
auto wrapper = mStorage[i].get(); auto wrapper = mStorage[i].get();
if (wrapper->buffer() == VK_NULL_HANDLE) { if (wrapper->cmdbuffer == VK_NULL_HANDLE) {
continue; continue;
} }
VkResult const result = vkGetFenceStatus(mDevice, wrapper->fence->fence); VkResult const result = vkWaitForFences(mDevice, 1, &wrapper->fence->fence, VK_TRUE, 0);
if (result != VK_SUCCESS) { if (result != VK_SUCCESS) {
continue; continue;
} }
fences[count++] = wrapper->fence->fence; buffers[count++] = wrapper->cmdbuffer;
wrapper->cmdbuffer = VK_NULL_HANDLE;
wrapper->fence->status.store(VK_SUCCESS); wrapper->fence->status.store(VK_SUCCESS);
wrapper->reset(); wrapper->fence.reset();
mAvailableBufferCount++; wrapper->clearResources();
++mAvailableCount;
} }
if (count > 0) { if (count > 0) {
vkResetFences(mDevice, count, fences); vkFreeCommandBuffers(mDevice, mPool, count, buffers);
} }
FVK_SYSTRACE_END();
} }
void VulkanCommands::updateFences() { void VulkanCommands::updateFences() {
for (size_t i = 0; i < CAPACITY; i++) { for (size_t i = 0; i < CAPACITY; i++) {
auto wrapper = mStorage[i].get(); auto wrapper = mStorage[i].get();
if (wrapper->buffer() != VK_NULL_HANDLE) { if (wrapper->cmdbuffer != VK_NULL_HANDLE) {
VulkanCmdFence* fence = wrapper->fence.get(); VulkanCmdFence* fence = wrapper->fence.get();
if (fence) { if (fence) {
VkResult status = vkGetFenceStatus(mDevice, fence->fence); VkResult status = vkGetFenceStatus(mDevice, fence->fence);
@@ -398,10 +371,8 @@ void VulkanCommands::updateFences() {
} }
} }
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp) { void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp) {
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) #if FILAMENT_VULKAN_VERBOSE
// If the timestamp is not 0, then we are carrying over a marker across buffer submits. // If the timestamp is not 0, then we are carrying over a marker across buffer submits.
// If it is 0, then this is a normal marker push and we should just print debug line as usual. // If it is 0, then this is a normal marker push and we should just print debug line as usual.
if (timestamp.time_since_epoch().count() == 0.0) { if (timestamp.time_since_epoch().count() == 0.0) {
@@ -410,7 +381,7 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest
#endif #endif
// TODO: Add group marker color to the Driver API // TODO: Add group marker color to the Driver API
VkCommandBuffer const cmdbuffer = get().buffer(); const VkCommandBuffer cmdbuffer = get().cmdbuffer;
if (!mGroupMarkers) { if (!mGroupMarkers) {
mGroupMarkers = std::make_unique<VulkanGroupMarkers>(); mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
@@ -438,8 +409,8 @@ void VulkanCommands::popGroupMarker() {
assert_invariant(mGroupMarkers); assert_invariant(mGroupMarkers);
if (!mGroupMarkers->empty()) { if (!mGroupMarkers->empty()) {
VkCommandBuffer const cmdbuffer = get().buffer(); const VkCommandBuffer cmdbuffer = get().cmdbuffer;
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) #if FILAMENT_VULKAN_VERBOSE
auto const [marker, startTime] = mGroupMarkers->pop(); auto const [marker, startTime] = mGroupMarkers->pop();
auto const endTime = std::chrono::high_resolution_clock::now(); auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = endTime - startTime; std::chrono::duration<double> diff = endTime - startTime;
@@ -448,6 +419,7 @@ void VulkanCommands::popGroupMarker() {
#else #else
mGroupMarkers->pop(); mGroupMarkers->pop();
#endif #endif
if (mContext->isDebugUtilsSupported()) { if (mContext->isDebugUtilsSupported()) {
vkCmdEndDebugUtilsLabelEXT(cmdbuffer); vkCmdEndDebugUtilsLabelEXT(cmdbuffer);
} else if (mContext->isDebugMarkersSupported()) { } else if (mContext->isDebugMarkersSupported()) {
@@ -462,7 +434,7 @@ void VulkanCommands::popGroupMarker() {
} }
void VulkanCommands::insertEventMarker(char const* string, uint32_t len) { void VulkanCommands::insertEventMarker(char const* string, uint32_t len) {
VkCommandBuffer const cmdbuffer = get().buffer(); VkCommandBuffer const cmdbuffer = get().cmdbuffer;
if (mContext->isDebugUtilsSupported()) { if (mContext->isDebugUtilsSupported()) {
VkDebugUtilsLabelEXT labelInfo = { VkDebugUtilsLabelEXT labelInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
@@ -486,7 +458,6 @@ std::string VulkanCommands::getTopGroupMarker() const {
} }
return std::get<0>(mGroupMarkers->top()); return std::get<0>(mGroupMarkers->top());
} }
#endif // FVK_DEBUG_GROUP_MARKERS
}// namespace filament::backend }// namespace filament::backend
+11 -26
View File
@@ -39,7 +39,6 @@ namespace filament::backend {
struct VulkanContext; struct VulkanContext;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
class VulkanGroupMarkers { class VulkanGroupMarkers {
public: public:
using Timestamp = std::chrono::time_point<std::chrono::high_resolution_clock>; using Timestamp = std::chrono::time_point<std::chrono::high_resolution_clock>;
@@ -52,17 +51,16 @@ public:
private: private:
std::list<std::string> mMarkers; std::list<std::string> mMarkers;
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) #if FILAMENT_VULKAN_VERBOSE
std::list<Timestamp> mTimestamps; std::list<Timestamp> mTimestamps;
#endif #endif
}; };
#endif // FVK_DEBUG_GROUP_MARKERS
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences. // Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
struct VulkanCmdFence { struct VulkanCmdFence {
VulkanCmdFence(VkFence ifence); VulkanCmdFence(VkDevice device, bool signaled = false);
~VulkanCmdFence() = default; ~VulkanCmdFence();
const VkDevice device;
VkFence fence; VkFence fence;
utils::Condition condition; utils::Condition condition;
utils::Mutex mutex; utils::Mutex mutex;
@@ -73,10 +71,13 @@ struct VulkanCmdFence {
// DriverApi fence object and should not be destroyed until both the DriverApi object is freed and // DriverApi fence object and should not be destroyed until both the DriverApi object is freed and
// we're done waiting on the most recent submission of the given command buffer. // we're done waiting on the most recent submission of the given command buffer.
struct VulkanCommandBuffer { struct VulkanCommandBuffer {
VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device, VkCommandPool pool); VulkanCommandBuffer(VulkanResourceAllocator* allocator)
: mResourceManager(allocator) {}
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete; VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete; VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete;
VkCommandBuffer cmdbuffer = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> fence;
inline void acquire(VulkanResource* resource) { inline void acquire(VulkanResource* resource) {
mResourceManager.acquire(resource); mResourceManager.acquire(resource);
@@ -86,23 +87,12 @@ struct VulkanCommandBuffer {
mResourceManager.acquire(srcResources); mResourceManager.acquire(srcResources);
} }
inline void reset() { inline void clearResources() {
fence.reset();
mResourceManager.clear(); mResourceManager.clear();
} }
inline VkCommandBuffer buffer() const {
if (fence) {
return mBuffer;
}
return VK_NULL_HANDLE;
}
std::shared_ptr<VulkanCmdFence> fence;
private: private:
VulkanAcquireOnlyResourceManager mResourceManager; VulkanAcquireOnlyResourceManager mResourceManager;
VkCommandBuffer mBuffer;
}; };
// Allows classes to be notified after a new command buffer has been activated. // Allows classes to be notified after a new command buffer has been activated.
@@ -173,7 +163,6 @@ class VulkanCommands {
// The observer's event handler can only be called during get(). // The observer's event handler can only be called during get().
void setObserver(CommandBufferObserver* observer) { mObserver = observer; } void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {}); void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
void popGroupMarker(); void popGroupMarker();
@@ -181,10 +170,9 @@ class VulkanCommands {
void insertEventMarker(char const* string, uint32_t len); void insertEventMarker(char const* string, uint32_t len);
std::string getTopGroupMarker() const; std::string getTopGroupMarker() const;
#endif
private: private:
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS; static constexpr int CAPACITY = VK_MAX_COMMAND_BUFFERS;
VkDevice const mDevice; VkDevice const mDevice;
VkQueue const mQueue; VkQueue const mQueue;
VkCommandPool const mPool; VkCommandPool const mPool;
@@ -196,15 +184,12 @@ class VulkanCommands {
VkSemaphore mSubmissionSignal = {}; VkSemaphore mSubmissionSignal = {};
VkSemaphore mInjectedSignal = {}; VkSemaphore mInjectedSignal = {};
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage; utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
VkFence mFences[CAPACITY] = {};
VkSemaphore mSubmissionSignals[CAPACITY] = {}; VkSemaphore mSubmissionSignals[CAPACITY] = {};
uint8_t mAvailableBufferCount = CAPACITY; size_t mAvailableCount = CAPACITY;
CommandBufferObserver* mObserver = nullptr; CommandBufferObserver* mObserver = nullptr;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers; std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers; std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
#endif
}; };
} // namespace filament::backend } // namespace filament::backend
+18 -70
View File
@@ -19,6 +19,13 @@
#include <stdint.h> #include <stdint.h>
#define FILAMENT_VULKAN_VERBOSE 0
#define FILAMENT_VULKAN_DUMP_API 0
#ifndef FILAMENT_VULKAN_HANDLE_ARENA_SIZE_IN_MB
#define FILAMENT_VULKAN_HANDLE_ARENA_SIZE_IN_MB 8
#endif
// In debug builds, we enable validation layers and set up a debug callback. // In debug builds, we enable validation layers and set up a debug callback.
// //
// To enable validation layers in Android, also be sure to set the jniLibs property in the gradle // To enable validation layers in Android, also be sure to set the jniLibs property in the gradle
@@ -36,80 +43,21 @@
// ndkVersion "23.1.7779620" // ndkVersion "23.1.7779620"
// //
// Also consider changing the root `gradle.properties` to point to a debug build, although this is // Also consider changing the root `gradle.properties` to point to a debug build, although this is
// not required for validation. // not required for validation if you change the definition of VK_ENABLE_VALIDATION below.
#if defined(NDEBUG)
// FVK is short for Filament Vulkan #define VK_ENABLE_VALIDATION 0
#define FVK_DEBUG_SYSTRACE 0x00000001
#define FVK_DEBUG_GROUP_MARKERS 0x00000002
#define FVK_DEBUG_TEXTURE 0x00000004
#define FVK_DEBUG_LAYOUT_TRANSITION 0x00000008
#define FVK_DEBUG_COMMAND_BUFFER 0x00000010
#define FVK_DEBUG_DUMP_API 0x00000020
#define FVK_DEBUG_VALIDATION 0x00000040
#define FVK_DEBUG_PRINT_GROUP_MARKERS 0x00000080
#define FVK_DEBUG_BLIT_FORMAT 0x00000100
#define FVK_DEBUG_BLITTER 0x00000200
#define FVK_DEBUG_FBO_CACHE 0x00000400
#define FVK_DEBUG_SHADER_MODULE 0x00000800
#define FVK_DEBUG_READ_PIXELS 0x00001000
#define FVK_DEBUG_PIPELINE_CACHE 0x00002000
#define FVK_DEBUG_ALLOCATION 0x00004000
// Usefaul default combinations
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
#define FVK_DEBUG_PERFORMANCE \
FVK_DEBUG_SYSTRACE | \
FVK_DEBUG_GROUP_MARKERS
#define FVK_DEBUG_CORRECTNESS \
FVK_DEBUG_VALIDATION | \
FVK_DEBUG_SHADER_MODULE | \
FVK_DEBUG_TEXTURE | \
FVK_DEBUG_LAYOUT_TRANSITION
#define FVK_DEBUG_RENDER_PASSES \
FVK_DEBUG_GROUP_MARKERS | \
FVK_DEBUG_PRINT_GROUP_MARKERS
#ifndef NDEBUG
#define FVK_DEBUG_FLAGS FVK_DEBUG_PERFORMANCE
#else #else
#define FVK_DEBUG_FLAGS 0 #define VK_ENABLE_VALIDATION 1
#endif #endif
#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags)) #define VK_REPORT_STALLS 0
#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(flags))
// Ensure dependencies are met between debug options
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
static_assert(FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS));
#endif
// end dependcy checks
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#include <utils/Systrace.h>
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
#else
#define FVK_SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker)
#define FVK_SYSTRACE_END()
#endif
#ifndef FVK_HANDLE_ARENA_SIZE_IN_MB
#define FVK_HANDLE_ARENA_SIZE_IN_MB 8
#endif
// All vkCreate* functions take an optional allocator. For now we select the default allocator by // All vkCreate* functions take an optional allocator. For now we select the default allocator by
// passing in a null pointer, and we highlight the argument by using the VKALLOC constant. // passing in a null pointer, and we highlight the argument by using the VKALLOC constant.
constexpr struct VkAllocationCallbacks* VKALLOC = nullptr; constexpr struct VkAllocationCallbacks* VKALLOC = nullptr;
constexpr static const int FVK_REQUIRED_VERSION_MAJOR = 1; constexpr static const int VK_REQUIRED_VERSION_MAJOR = 1;
constexpr static const int FVK_REQUIRED_VERSION_MINOR = 1; constexpr static const int VK_REQUIRED_VERSION_MINOR = 1;
// Maximum number of VkCommandBuffer handles managed simultaneously by VulkanCommands. // Maximum number of VkCommandBuffer handles managed simultaneously by VulkanCommands.
// //
@@ -117,18 +65,18 @@ constexpr static const int FVK_REQUIRED_VERSION_MINOR = 1;
// buffers that have been submitted but have not yet finished rendering. Note that Filament can // buffers that have been submitted but have not yet finished rendering. Note that Filament can
// issue multiple commit calls in a single frame, and that we use a triple buffered swap chain on // issue multiple commit calls in a single frame, and that we use a triple buffered swap chain on
// some platforms. // some platforms.
constexpr static const int FVK_MAX_COMMAND_BUFFERS = 10; constexpr static const int VK_MAX_COMMAND_BUFFERS = 10;
// Number of command buffer submissions that should occur before an unused pipeline is removed // Number of command buffer submissions that should occur before an unused pipeline is removed
// from the cache. // from the cache.
// //
// If this number is low, VkPipeline construction will occur frequently, which can // If this number is low, VkPipeline construction will occur frequently, which can
// be extremely slow. If this number is high, the memory footprint will be large. // be extremely slow. If this number is high, the memory footprint will be large.
constexpr static const int FVK_MAX_PIPELINE_AGE = 10; constexpr static const int VK_MAX_PIPELINE_AGE = 10;
// VulkanPipelineCache does not track which command buffers contain references to which pipelines, // VulkanPipelineCache does not track which command buffers contain references to which pipelines,
// instead it simply waits for at least FVK_MAX_COMMAND_BUFFERS submissions to occur before // instead it simply waits for at least VK_MAX_COMMAND_BUFFERS submissions to occur before
// destroying any unused pipeline object. // destroying any unused pipeline object.
static_assert(FVK_MAX_PIPELINE_AGE >= FVK_MAX_COMMAND_BUFFERS); static_assert(VK_MAX_PIPELINE_AGE >= VK_MAX_COMMAND_BUFFERS);
#endif #endif
@@ -117,9 +117,8 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
VulkanTimerQuery* query) { VulkanTimerQuery* query) {
uint32_t const index = query->getStartingQueryIndex(); uint32_t const index = query->getStartingQueryIndex();
auto const cmdbuffer = commands->buffer(); vkCmdResetQueryPool(commands->cmdbuffer, mPool, index, 2);
vkCmdResetQueryPool(cmdbuffer, mPool, index, 2); vkCmdWriteTimestamp(commands->cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
vkCmdWriteTimestamp(cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
// We stash this because getResult might come before the query is actually processed. // We stash this because getResult might come before the query is actually processed.
query->setFence(commands->fence); query->setFence(commands->fence);
@@ -128,7 +127,7 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands, void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands,
VulkanTimerQuery const* query) { VulkanTimerQuery const* query) {
uint32_t const index = query->getStoppingQueryIndex(); uint32_t const index = query->getStoppingQueryIndex();
vkCmdWriteTimestamp(commands->buffer(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index); vkCmdWriteTimestamp(commands->cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
} }
VulkanTimestamps::QueryResult VulkanTimestamps::getResult(VulkanTimerQuery const* query) { VulkanTimestamps::QueryResult VulkanTimestamps::getResult(VulkanTimerQuery const* query) {
+51 -78
View File
@@ -30,9 +30,10 @@
#include <utils/CString.h> #include <utils/CString.h>
#include <utils/FixedCapacityVector.h> #include <utils/FixedCapacityVector.h>
#include <utils/Panic.h> #include <utils/Panic.h>
#include <utils/Systrace.h>
#ifndef NDEBUG #ifndef NDEBUG
#include <set> // For VulkanDriver::debugCommandBegin #include <set>
#endif #endif
using namespace bluevk; using namespace bluevk;
@@ -100,7 +101,7 @@ VulkanTexture* createEmptyTexture(VkDevice device, VkPhysicalDevice physicalDevi
return emptyTexture; return emptyTexture;
} }
#if FVK_ENABLED(FVK_DEBUG_VALIDATION) #if VK_ENABLE_VALIDATION
VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags, VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location,
int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) { int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) {
@@ -135,7 +136,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
utils::slog.e << utils::io::endl; utils::slog.e << utils::io::endl;
return VK_FALSE; return VK_FALSE;
} }
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION) #endif// VK_ENABLE_VALIDATION
} // anonymous namespace } // anonymous namespace
@@ -158,7 +159,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mBlitter(mStagePool, mPipelineCache, mFramebufferCache, mSamplerCache), mBlitter(mStagePool, mPipelineCache, mFramebufferCache, mSamplerCache),
mReadPixels(mPlatform->getDevice()) { mReadPixels(mPlatform->getDevice()) {
#if FVK_ENABLED(FVK_DEBUG_VALIDATION) #if VK_ENABLE_VALIDATION
UTILS_UNUSED const PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback UTILS_UNUSED const PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback
= vkCreateDebugReportCallbackEXT; = vkCreateDebugReportCallbackEXT;
VkResult result; VkResult result;
@@ -213,7 +214,7 @@ UTILS_NOINLINE
Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& context, Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept { Platform::DriverConfig const& driverConfig) noexcept {
assert_invariant(platform); assert_invariant(platform);
size_t defaultSize = FVK_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U; size_t defaultSize = FILAMENT_VULKAN_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig{ Platform::DriverConfig validConfig{
.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize)}; .handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize)};
return new VulkanDriver(platform, context, validConfig); return new VulkanDriver(platform, context, validConfig);
@@ -265,15 +266,24 @@ void VulkanDriver::tick(int) {
// rather than the wall clock, because we must wait 3 frames after a DriverAPI-level resource has // rather than the wall clock, because we must wait 3 frames after a DriverAPI-level resource has
// been destroyed for safe destruction, due to outstanding command buffers and triple buffering. // been destroyed for safe destruction, due to outstanding command buffers and triple buffering.
void VulkanDriver::collectGarbage() { void VulkanDriver::collectGarbage() {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("gc");
// Command buffers need to be submitted and completed before other resources can be gc'd. And // Command buffers need to be submitted and completed before other resources can be gc'd. And
// its gc() function carrys out the *wait*. // its gc() function carrys out the *wait*.
SYSTRACE_CONTEXT();
SYSTRACE_NAME_BEGIN("gc");
mCommands->gc(); mCommands->gc();
SYSTRACE_NAME_BEGIN("stagepool-gc");
mStagePool.gc(); mStagePool.gc();
SYSTRACE_NAME_END();
SYSTRACE_NAME_BEGIN("framebuffercache-gc");
mFramebufferCache.gc(); mFramebufferCache.gc();
FVK_SYSTRACE_END(); SYSTRACE_NAME_END();
SYSTRACE_NAME_END(); // "gc"
} }
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) { void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
// Do nothing. // Do nothing.
} }
@@ -296,21 +306,14 @@ void VulkanDriver::endFrame(uint32_t frameId) {
} }
void VulkanDriver::flush(int) { void VulkanDriver::flush(int) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("flush");
mCommands->flush(); mCommands->flush();
FVK_SYSTRACE_END();
} }
void VulkanDriver::finish(int dummy) { void VulkanDriver::finish(int dummy) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("finish");
mCommands->flush(); mCommands->flush();
mCommands->wait(); mCommands->wait();
mReadPixels.runUntilComplete(); mReadPixels.runUntilComplete();
FVK_SYSTRACE_END();
} }
void VulkanDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t count) { void VulkanDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t count) {
@@ -523,22 +526,21 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
<< utils::io::endl; << utils::io::endl;
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE); flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
} }
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext, mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext, mAllocator,
mAllocator, mCommands.get(), mStagePool, nativeWindow, flags); mCommands.get(), mStagePool, nativeWindow, flags);
mResourceManager.acquire(swapChain);
} }
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width, void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags) { uint32_t height, uint64_t flags) {
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) { if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 &&
!isSRGBSwapChainSupported()) {
utils::slog.w << "sRGB swapchain requested, but Platform does not support it" utils::slog.w << "sRGB swapchain requested, but Platform does not support it"
<< utils::io::endl; << utils::io::endl;
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE); flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
} }
assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions."); assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions.");
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext, mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext, mAllocator,
mAllocator, mCommands.get(), mStagePool, nullptr, flags, VkExtent2D{width, height}); mCommands.get(), mStagePool, nullptr, flags, VkExtent2D{width, height});
mResourceManager.acquire(swapChain);
} }
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) { void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
@@ -688,15 +690,16 @@ FenceStatus VulkanDriver::getFenceStatus(Handle<HwFence> fh) {
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". // Internally we use the VK_INCOMPLETE status to mean "not yet submitted".
// When this fence gets submitted, its status changes to VK_NOT_READY. // When this fence gets submitted, its status changes to VK_NOT_READY.
std::unique_lock<utils::Mutex> lock(cmdfence->mutex); std::unique_lock<utils::Mutex> lock(cmdfence->mutex);
if (cmdfence->status.load() == VK_SUCCESS) { if (cmdfence->status.load() == VK_INCOMPLETE) {
return FenceStatus::CONDITION_SATISFIED; // This will obviously timeout if Filament creates a fence and immediately waits on it
// without calling endFrame() or commit().
cmdfence->condition.wait(lock);
} else {
lock.unlock();
} }
VkResult result =
// Two other states are possible: vkWaitForFences(mPlatform->getDevice(), 1, &cmdfence->fence, VK_TRUE, 0);
// - VK_INCOMPLETE: the corresponding buffer has not yet been submitted. return result == VK_SUCCESS ? FenceStatus::CONDITION_SATISFIED : FenceStatus::TIMEOUT_EXPIRED;
// - VK_NOT_READY: the buffer has been submitted but not yet signaled.
// In either case, we return TIMEOUT_EXPIRED to indicate the fence has not been signaled.
return FenceStatus::TIMEOUT_EXPIRED;
} }
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that // We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
@@ -869,7 +872,7 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
VulkanCommandBuffer& commands = mCommands->get(); VulkanCommandBuffer& commands = mCommands->get();
auto ib = mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh); auto ib = mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh);
commands.acquire(ib); commands.acquire(ib);
ib->buffer.loadFromCpu(commands.buffer(), p.buffer, byteOffset, p.size); ib->buffer.loadFromCpu(commands.cmdbuffer, p.buffer, byteOffset, p.size);
scheduleDestroy(std::move(p)); scheduleDestroy(std::move(p));
} }
@@ -880,7 +883,7 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh); auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
commands.acquire(bo); commands.acquire(bo);
bo->buffer.loadFromCpu(commands.buffer(), bd.buffer, byteOffset, bd.size); bo->buffer.loadFromCpu(commands.cmdbuffer, bd.buffer, byteOffset, bd.size);
scheduleDestroy(std::move(bd)); scheduleDestroy(std::move(bd));
} }
@@ -891,7 +894,7 @@ void VulkanDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> boh,
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh); auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
commands.acquire(bo); commands.acquire(bo);
// TODO: implement unsynchronized version // TODO: implement unsynchronized version
bo->buffer.loadFromCpu(commands.buffer(), bd.buffer, byteOffset, bd.size); bo->buffer.loadFromCpu(commands.cmdbuffer, bd.buffer, byteOffset, bd.size);
mResourceManager.acquire(bo); mResourceManager.acquire(bo);
scheduleDestroy(std::move(bd)); scheduleDestroy(std::move(bd));
} }
@@ -987,9 +990,6 @@ void VulkanDriver::compilePrograms(CompilerPriorityQueue priority,
} }
void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) { void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("beginRenderPass");
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth); VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth);
const VkExtent2D extent = rt->getExtent(); const VkExtent2D extent = rt->getExtent();
assert_invariant(extent.width > 0 && extent.height > 0); assert_invariant(extent.width > 0 && extent.height > 0);
@@ -1008,8 +1008,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
} }
VulkanAttachment depth = rt->getSamples() == 1 ? rt->getDepth() : rt->getMsaaDepth(); VulkanAttachment depth = rt->getSamples() == 1 ? rt->getDepth() : rt->getMsaaDepth();
#if FILAMENT_VULKAN_VERBOSE
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
if (depth.texture) { if (depth.texture) {
depth.texture->print(); depth.texture->print();
} }
@@ -1021,7 +1020,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
// the non-sampling case. // the non-sampling case.
bool samplingDepthAttachment = false; bool samplingDepthAttachment = false;
VulkanCommandBuffer& commands = mCommands->get(); VulkanCommandBuffer& commands = mCommands->get();
VkCommandBuffer const cmdbuffer = commands.buffer(); VkCommandBuffer const cmdbuffer = commands.cmdbuffer;
UTILS_NOUNROLL UTILS_NOUNROLL
for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT; for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT;
@@ -1154,7 +1153,6 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey); VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
// Assign a label to the framebuffer for debugging purposes. // Assign a label to the framebuffer for debugging purposes.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
if (UTILS_UNLIKELY(mContext.isDebugUtilsSupported())) { if (UTILS_UNLIKELY(mContext.isDebugUtilsSupported())) {
auto const topMarker = mCommands->getTopGroupMarker(); auto const topMarker = mCommands->getTopGroupMarker();
if (!topMarker.empty()) { if (!topMarker.empty()) {
@@ -1168,7 +1166,6 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
vkSetDebugUtilsObjectNameEXT(mPlatform->getDevice(), &info); vkSetDebugUtilsObjectNameEXT(mPlatform->getDevice(), &info);
} }
} }
#endif
// The current command buffer now owns a reference to the render target and its attachments. // The current command buffer now owns a reference to the render target and its attachments.
// Note that we must acquire parent textures, not sidecars. // Note that we must acquire parent textures, not sidecars.
@@ -1245,15 +1242,11 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
.params = params, .params = params,
.currentSubpass = 0, .currentSubpass = 0,
}; };
FVK_SYSTRACE_END();
} }
void VulkanDriver::endRenderPass(int) { void VulkanDriver::endRenderPass(int) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("endRenderPass");
VulkanCommandBuffer& commands = mCommands->get(); VulkanCommandBuffer& commands = mCommands->get();
VkCommandBuffer cmdbuffer = commands.buffer(); VkCommandBuffer cmdbuffer = commands.cmdbuffer;
vkCmdEndRenderPass(cmdbuffer); vkCmdEndRenderPass(cmdbuffer);
VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
@@ -1295,7 +1288,6 @@ void VulkanDriver::endRenderPass(int) {
} }
mCurrentRenderPass.renderTarget = nullptr; mCurrentRenderPass.renderTarget = nullptr;
mCurrentRenderPass.renderPass = VK_NULL_HANDLE; mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
FVK_SYSTRACE_END();
} }
void VulkanDriver::nextSubpass(int) { void VulkanDriver::nextSubpass(int) {
@@ -1306,7 +1298,7 @@ void VulkanDriver::nextSubpass(int) {
assert_invariant(renderTarget); assert_invariant(renderTarget);
assert_invariant(mCurrentRenderPass.params.subpassMask); assert_invariant(mCurrentRenderPass.params.subpassMask);
vkCmdNextSubpass(mCommands->get().buffer(), VK_SUBPASS_CONTENTS_INLINE); vkCmdNextSubpass(mCommands->get().cmdbuffer, VK_SUBPASS_CONTENTS_INLINE);
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass, mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
++mCurrentRenderPass.currentSubpass); ++mCurrentRenderPass.currentSubpass);
@@ -1342,9 +1334,6 @@ void VulkanDriver::setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph,
} }
void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) { void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("makeCurrent");
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch, ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
"Vulkan driver does not support distinct draw/read swap chains."); "Vulkan driver does not support distinct draw/read swap chains.");
VulkanSwapChain* swapChain = mCurrentSwapChain VulkanSwapChain* swapChain = mCurrentSwapChain
@@ -1360,14 +1349,9 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
if (UTILS_LIKELY(mDefaultRenderTarget)) { if (UTILS_LIKELY(mDefaultRenderTarget)) {
mDefaultRenderTarget->bindToSwapChain(*swapChain); mDefaultRenderTarget->bindToSwapChain(*swapChain);
} }
FVK_SYSTRACE_END();
} }
void VulkanDriver::commit(Handle<HwSwapChain> sch) { void VulkanDriver::commit(Handle<HwSwapChain> sch) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("commit");
VulkanSwapChain* swapChain = mResourceAllocator.handle_cast<VulkanSwapChain*>(sch); VulkanSwapChain* swapChain = mResourceAllocator.handle_cast<VulkanSwapChain*>(sch);
if (mCommands->flush()) { if (mCommands->flush()) {
@@ -1376,7 +1360,6 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
// Present the backbuffer after the most recent command buffer submission has finished. // Present the backbuffer after the most recent command buffer submission has finished.
swapChain->present(); swapChain->present();
FVK_SYSTRACE_END();
} }
void VulkanDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh) { void VulkanDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh) {
@@ -1408,26 +1391,24 @@ void VulkanDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
} }
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) { void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands->insertEventMarker(string, len); mCommands->insertEventMarker(string, len);
#endif
} }
void VulkanDriver::pushGroupMarker(char const* string, uint32_t) { void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
// Turns out all the markers are 0-terminated, so we can just pass it without len. // Turns out all the markers are 0-terminated, so we can just pass it without len.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands->pushGroupMarker(string); mCommands->pushGroupMarker(string);
#endif {
FVK_SYSTRACE_CONTEXT(); SYSTRACE_CONTEXT();
FVK_SYSTRACE_START(string); SYSTRACE_NAME_BEGIN(string);
}
} }
void VulkanDriver::popGroupMarker(int) { void VulkanDriver::popGroupMarker(int) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands->popGroupMarker(); mCommands->popGroupMarker();
#endif {
FVK_SYSTRACE_CONTEXT(); SYSTRACE_CONTEXT();
FVK_SYSTRACE_END(); SYSTRACE_NAME_END();
}
} }
void VulkanDriver::startCapture(int) {} void VulkanDriver::startCapture(int) {}
@@ -1458,9 +1439,6 @@ void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,
void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, Viewport dstRect, void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect, SamplerMagFilter filter) { Handle<HwRenderTarget> src, Viewport srcRect, SamplerMagFilter filter) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("blit");
assert_invariant(mCurrentRenderPass.renderPass == VK_NULL_HANDLE); assert_invariant(mCurrentRenderPass.renderPass == VK_NULL_HANDLE);
// blit operation only support COLOR0 color buffer // blit operation only support COLOR0 color buffer
@@ -1500,16 +1478,12 @@ void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, V
if (any(buffers & TargetBufferFlags::COLOR0)) { if (any(buffers & TargetBufferFlags::COLOR0)) {
mBlitter.blitColor({ dstTarget, dstOffsets, srcTarget, srcOffsets, vkfilter, int(0) }); mBlitter.blitColor({ dstTarget, dstOffsets, srcTarget, srcOffsets, vkfilter, int(0) });
} }
FVK_SYSTRACE_END();
} }
void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph, void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
const uint32_t instanceCount) { const uint32_t instanceCount) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw");
VulkanCommandBuffer* commands = &mCommands->get(); VulkanCommandBuffer* commands = &mCommands->get();
VkCommandBuffer cmdbuffer = commands->buffer(); VkCommandBuffer cmdbuffer = commands->cmdbuffer;
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph); const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
Handle<HwProgram> programHandle = pipelineState.program; Handle<HwProgram> programHandle = pipelineState.program;
@@ -1523,7 +1497,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
commands->acquire(prim.vertexBuffer); commands->acquire(prim.vertexBuffer);
// If this is a debug build, validate the current shader. // If this is a debug build, validate the current shader.
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) #if !defined(NDEBUG)
if (program->bundle.vertex == VK_NULL_HANDLE || program->bundle.fragment == VK_NULL_HANDLE) { if (program->bundle.vertex == VK_NULL_HANDLE || program->bundle.fragment == VK_NULL_HANDLE) {
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl; utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
} }
@@ -1639,7 +1613,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// This fallback path is very flaky because the dummy texture might not have // This fallback path is very flaky because the dummy texture might not have
// matching characteristics. (e.g. if the missing texture is a 3D texture) // matching characteristics. (e.g. if the missing texture is a 3D texture)
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) { if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE) #ifndef NDEBUG
utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'"; utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
utils::slog.w << " in material '" << program->name.c_str() << "'"; utils::slog.w << " in material '" << program->name.c_str() << "'";
utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl; utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl;
@@ -1713,7 +1687,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
const uint32_t firstInstId = 0; const uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId); vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
FVK_SYSTRACE_END();
} }
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) { void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
@@ -23,7 +23,7 @@
// If any VkRenderPass or VkFramebuffer is unused for more than TIME_BEFORE_EVICTION frames, it // If any VkRenderPass or VkFramebuffer is unused for more than TIME_BEFORE_EVICTION frames, it
// is evicted from the cache. // is evicted from the cache.
static constexpr uint32_t TIME_BEFORE_EVICTION = FVK_MAX_COMMAND_BUFFERS; static constexpr uint32_t TIME_BEFORE_EVICTION = VK_MAX_COMMAND_BUFFERS;
using namespace bluevk; using namespace bluevk;
@@ -97,7 +97,7 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config) noexcept {
attachments[attachmentCount++] = config.depth; attachments[attachmentCount++] = config.depth;
} }
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE) #if FILAMENT_VULKAN_VERBOSE
utils::slog.d << "Creating framebuffer " << config.width << "x" << config.height << " " utils::slog.d << "Creating framebuffer " << config.width << "x" << config.height << " "
<< "for render pass " << config.renderPass << ", " << "for render pass " << config.renderPass << ", "
<< "samples = " << int(config.samples) << ", " << "samples = " << int(config.samples) << ", "
@@ -312,7 +312,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
ASSERT_POSTCONDITION(!error, "Unable to create render pass."); ASSERT_POSTCONDITION(!error, "Unable to create render pass.");
mRenderPassCache[config] = {renderPass, mCurrentTime}; mRenderPassCache[config] = {renderPass, mCurrentTime};
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE) #if FILAMENT_VULKAN_VERBOSE
utils::slog.d << "Created render pass " << renderPass << " with " utils::slog.d << "Created render pass " << renderPass << " with "
<< "samples = " << int(config.samples) << ", " << "samples = " << int(config.samples) << ", "
<< "depth = " << (hasDepth ? 1 : 0) << ", " << "depth = " << (hasDepth ? 1 : 0) << ", "
@@ -338,9 +338,6 @@ void VulkanFboCache::reset() noexcept {
// Frees up old framebuffers and render passes, then nulls out their key. Doesn't bother removing // Frees up old framebuffers and render passes, then nulls out their key. Doesn't bother removing
// the actual map entry since it is fairly small. // the actual map entry since it is fairly small.
void VulkanFboCache::gc() noexcept { void VulkanFboCache::gc() noexcept {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("fbocache::gc");
// If this is one of the first few frames, return early to avoid wrapping unsigned integers. // If this is one of the first few frames, return early to avoid wrapping unsigned integers.
if (++mCurrentTime <= TIME_BEFORE_EVICTION) { if (++mCurrentTime <= TIME_BEFORE_EVICTION) {
return; return;
@@ -362,7 +359,6 @@ void VulkanFboCache::gc() noexcept {
iter.value().handle = VK_NULL_HANDLE; iter.value().handle = VK_NULL_HANDLE;
} }
} }
FVK_SYSTRACE_END();
} }
} // namespace filament::backend } // namespace filament::backend
+11 -16
View File
@@ -89,35 +89,30 @@ VulkanProgram::VulkanProgram(VkDevice device, const Program& builder) noexcept
}; };
for (size_t i = 0; i < specializationConstants.size(); i++) { for (size_t i = 0; i < specializationConstants.size(); i++) {
uint32_t const offset = uint32_t(i) * 4; const uint32_t offset = uint32_t(i) * 4;
std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
pEntries[i] = { pEntries[i] = {
.constantID = specializationConstants[i].id, .constantID = specializationConstants[i].id,
.offset = offset, .offset = offset,
// Note that bools are 4-bytes in Vulkan // Turns out vulkan expects the size of bool to be 4 (verified through
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBool32.html // validation layer). So all expected types are of 4 bytes.
.size = 4, .size = 4,
}; };
T* const addr = (T*)((char*)pData + offset);
*addr = arg;
}, specializationConstants[i].value);
}
using SpecConstant = Program::SpecializationConstant::Type;
char const* addr = (char*)pData + offset;
SpecConstant const& arg = specializationConstants[i].value;
if (std::holds_alternative<bool>(arg)) {
*((VkBool32*)addr) = std::get<bool>(arg) ? VK_TRUE : VK_FALSE;
} else if (std::holds_alternative<float>(arg)) {
*((float*)addr) = std::get<float>(arg);
} else {
*((int32_t*)addr) = std::get<int32_t>(arg);
}
}
bundle.specializationInfos = pInfo; bundle.specializationInfos = pInfo;
} }
// Make a copy of the binding map // Make a copy of the binding map
samplerGroupInfo = builder.getSamplerGroupInfo(); samplerGroupInfo = builder.getSamplerGroupInfo();
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) if constexpr (FILAMENT_VULKAN_VERBOSE) {
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex
<< ", " << bundle.fragment << ")" << utils::io::endl; << ", " << bundle.fragment << ")" << utils::io::endl;
#endif }
} }
VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs) noexcept VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs) noexcept
+10 -1
View File
@@ -96,6 +96,10 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
void setBuffer(VulkanBufferObject* bufferObject, uint32_t index); void setBuffer(VulkanBufferObject* bufferObject, uint32_t index);
inline void terminate() {
mResources.clear();
}
utils::FixedCapacityVector<VulkanBuffer const*> buffers; utils::FixedCapacityVector<VulkanBuffer const*> buffers;
private: private:
@@ -110,6 +114,9 @@ struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource {
buffer(allocator, stagePool, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount), buffer(allocator, stagePool, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount),
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32) {} indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32) {}
void terminate() {
buffer.terminate();
}
VulkanBuffer buffer; VulkanBuffer buffer;
const VkIndexType indexType; const VkIndexType indexType;
}; };
@@ -117,7 +124,9 @@ struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource {
struct VulkanBufferObject : public HwBufferObject, VulkanResource { struct VulkanBufferObject : public HwBufferObject, VulkanResource {
VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool, uint32_t byteCount, VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage); BufferObjectBinding bindingType, BufferUsage usage);
void terminate() {
buffer.terminate();
}
VulkanBuffer buffer; VulkanBuffer buffer;
const BufferObjectBinding bindingType; const BufferObjectBinding bindingType;
}; };
@@ -218,7 +218,7 @@ bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange&
return false; return false;
} }
#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION | FVK_DEBUG_TEXTURE) #if FILAMENT_VULKAN_VERBOSE
#define CASE(VALUE) \ #define CASE(VALUE) \
case filament::backend::VulkanLayout::VALUE: { \ case filament::backend::VulkanLayout::VALUE: { \
out << #VALUE; \ out << #VALUE; \
@@ -166,7 +166,7 @@ bool VulkanPipelineCache::bindDescriptors(VkCommandBuffer cmdbuffer) noexcept {
} }
bool VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) noexcept { bool VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) noexcept {
VkCommandBuffer const cmdbuffer = commands->buffer(); VkCommandBuffer const cmdbuffer = commands->cmdbuffer;
PipelineMap::iterator pipelineIter = mPipelines.find(mPipelineRequirements); PipelineMap::iterator pipelineIter = mPipelines.find(mPipelineRequirements);
@@ -481,10 +481,10 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
PipelineCacheEntry cacheEntry = {}; PipelineCacheEntry cacheEntry = {};
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) if constexpr (FILAMENT_VULKAN_VERBOSE) {
utils::slog.d << "vkCreateGraphicsPipelines with shaders = (" utils::slog.d << "vkCreateGraphicsPipelines with shaders = ("
<< shaderStages[0].module << ", " << shaderStages[1].module << ")" << utils::io::endl; << shaderStages[0].module << ", " << shaderStages[1].module << ")" << utils::io::endl;
#endif }
VkResult error = vkCreateGraphicsPipelines(mDevice, VK_NULL_HANDLE, 1, &pipelineCreateInfo, VkResult error = vkCreateGraphicsPipelines(mDevice, VK_NULL_HANDLE, 1, &pipelineCreateInfo,
VKALLOC, &cacheEntry.handle); VKALLOC, &cacheEntry.handle);
assert_invariant(error == VK_SUCCESS); assert_invariant(error == VK_SUCCESS);
@@ -678,10 +678,10 @@ void VulkanPipelineCache::terminate() noexcept {
mDummyMemory = VK_NULL_HANDLE; mDummyMemory = VK_NULL_HANDLE;
} }
void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& commands) { void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& cmdbuffer) {
// The timestamp associated with a given cache entry represents "time" as a count of flush // The timestamp associated with a given cache entry represents "time" as a count of flush
// events since the cache was constructed. If any cache entry was most recently used over // events since the cache was constructed. If any cache entry was most recently used over
// FVK_MAX_PIPELINE_AGE flush events in the past, then we can be sure that it is no longer // VK_MAX_PIPELINE_AGE flush events in the past, then we can be sure that it is no longer
// being used by the GPU, and is therefore safe to destroy or reclaim. // being used by the GPU, and is therefore safe to destroy or reclaim.
++mCurrentTime; ++mCurrentTime;
@@ -698,7 +698,7 @@ void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& commands) {
using ConstDescIterator = decltype(mDescriptorSets)::const_iterator; using ConstDescIterator = decltype(mDescriptorSets)::const_iterator;
for (ConstDescIterator iter = mDescriptorSets.begin(); iter != mDescriptorSets.end();) { for (ConstDescIterator iter = mDescriptorSets.begin(); iter != mDescriptorSets.end();) {
const DescriptorCacheEntry& cacheEntry = iter.value(); const DescriptorCacheEntry& cacheEntry = iter.value();
if (cacheEntry.lastUsed + FVK_MAX_PIPELINE_AGE < mCurrentTime) { if (cacheEntry.lastUsed + VK_MAX_PIPELINE_AGE < mCurrentTime) {
auto& arenas = mPipelineLayouts[cacheEntry.pipelineLayout].descriptorSetArenas; auto& arenas = mPipelineLayouts[cacheEntry.pipelineLayout].descriptorSetArenas;
for (uint32_t i = 0; i < DESCRIPTOR_TYPE_COUNT; ++i) { for (uint32_t i = 0; i < DESCRIPTOR_TYPE_COUNT; ++i) {
arenas[i].push_back(cacheEntry.handles[i]); arenas[i].push_back(cacheEntry.handles[i]);
@@ -712,11 +712,11 @@ void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& commands) {
} }
// Evict any pipelines that have not been used in a while. // Evict any pipelines that have not been used in a while.
// Any pipeline older than FVK_MAX_COMMAND_BUFFERS can be safely destroyed. // Any pipeline older than VK_MAX_COMMAND_BUFFERS can be safely destroyed.
using ConstPipeIterator = decltype(mPipelines)::const_iterator; using ConstPipeIterator = decltype(mPipelines)::const_iterator;
for (ConstPipeIterator iter = mPipelines.begin(); iter != mPipelines.end();) { for (ConstPipeIterator iter = mPipelines.begin(); iter != mPipelines.end();) {
const PipelineCacheEntry& cacheEntry = iter.value(); const PipelineCacheEntry& cacheEntry = iter.value();
if (cacheEntry.lastUsed + FVK_MAX_PIPELINE_AGE < mCurrentTime) { if (cacheEntry.lastUsed + VK_MAX_PIPELINE_AGE < mCurrentTime) {
vkDestroyPipeline(mDevice, iter->second.handle, VKALLOC); vkDestroyPipeline(mDevice, iter->second.handle, VKALLOC);
iter = mPipelines.erase(iter); iter = mPipelines.erase(iter);
} else { } else {
@@ -728,10 +728,10 @@ void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& commands) {
using ConstLayoutIterator = decltype(mPipelineLayouts)::const_iterator; using ConstLayoutIterator = decltype(mPipelineLayouts)::const_iterator;
for (ConstLayoutIterator iter = mPipelineLayouts.begin(); iter != mPipelineLayouts.end();) { for (ConstLayoutIterator iter = mPipelineLayouts.begin(); iter != mPipelineLayouts.end();) {
const PipelineLayoutCacheEntry& cacheEntry = iter.value(); const PipelineLayoutCacheEntry& cacheEntry = iter.value();
if (cacheEntry.lastUsed + FVK_MAX_PIPELINE_AGE < mCurrentTime) { if (cacheEntry.lastUsed + VK_MAX_PIPELINE_AGE < mCurrentTime) {
vkDestroyPipelineLayout(mDevice, iter->second.handle, VKALLOC); vkDestroyPipelineLayout(mDevice, iter->second.handle, VKALLOC);
for (auto setLayout : iter->second.descriptorSetLayouts) { for (auto setLayout : iter->second.descriptorSetLayouts) {
#if FVK_ENABLED(FVK_DEBUG_PIPELINE_CACHE) #ifndef NDEBUG
PipelineLayoutKey key = iter.key(); PipelineLayoutKey key = iter.key();
for (auto& pair : mDescriptorSets) { for (auto& pair : mDescriptorSets) {
assert_invariant(pair.second.pipelineLayout != key); assert_invariant(pair.second.pipelineLayout != key);
@@ -755,7 +755,7 @@ void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& commands) {
// destroy the extinct pools, which implicitly frees their associated descriptor sets. // destroy the extinct pools, which implicitly frees their associated descriptor sets.
bool canPurgeExtinctPools = true; bool canPurgeExtinctPools = true;
for (auto& bundle : mExtinctDescriptorBundles) { for (auto& bundle : mExtinctDescriptorBundles) {
if (bundle.lastUsed + FVK_MAX_PIPELINE_AGE >= mCurrentTime) { if (bundle.lastUsed + VK_MAX_PIPELINE_AGE >= mCurrentTime) {
canPurgeExtinctPools = false; canPurgeExtinctPools = false;
break; break;
} }
@@ -799,7 +799,7 @@ VkDescriptorPool VulkanPipelineCache::createDescriptorPool(uint32_t size) const
void VulkanPipelineCache::destroyLayoutsAndDescriptors() noexcept { void VulkanPipelineCache::destroyLayoutsAndDescriptors() noexcept {
// Our current descriptor set strategy can cause the # of descriptor sets to explode in certain // Our current descriptor set strategy can cause the # of descriptor sets to explode in certain
// situations, so it's interesting to report the number that get stuffed into the cache. // situations, so it's interesting to report the number that get stuffed into the cache.
#if FVK_ENABLED(FVK_DEBUG_PIPELINE_CACHE) #ifndef NDEBUG
utils::slog.d << "Destroying " << mDescriptorSets.size() << " bundles of descriptor sets." utils::slog.d << "Destroying " << mDescriptorSets.size() << " bundles of descriptor sets."
<< utils::io::endl; << utils::io::endl;
#endif #endif
@@ -808,7 +808,7 @@ void VulkanPipelineCache::destroyLayoutsAndDescriptors() noexcept {
// Our current layout bundle strategy can cause the # of layout bundles to explode in certain // Our current layout bundle strategy can cause the # of layout bundles to explode in certain
// situations, so it's interesting to report the number that get stuffed into the cache. // situations, so it's interesting to report the number that get stuffed into the cache.
#if FVK_ENABLED(FVK_DEBUG_PIPELINE_CACHE) #ifndef NDEBUG
utils::slog.d << "Destroying " << mPipelineLayouts.size() << " pipeline layouts." utils::slog.d << "Destroying " << mPipelineLayouts.size() << " pipeline layouts."
<< utils::io::endl; << utils::io::endl;
#endif #endif
@@ -340,7 +340,7 @@ private:
// The timestamp associated with a given cache entry represents time as a count of flush // The timestamp associated with a given cache entry represents time as a count of flush
// events since the cache was constructed. If any cache entry was most recently used over // events since the cache was constructed. If any cache entry was most recently used over
// FVK_MAX_PIPELINE_AGE flushes in the past, then we can be sure that it is no longer // VK_MAX_PIPELINE_AGE flushes in the past, then we can be sure that it is no longer
// being used by the GPU, and is therefore safe to destroy or reclaim. // being used by the GPU, and is therefore safe to destroy or reclaim.
using Timestamp = uint64_t; using Timestamp = uint64_t;
Timestamp mCurrentTime = 0; Timestamp mCurrentTime = 0;
@@ -167,7 +167,7 @@ void VulkanReadPixels::run(VulkanRenderTarget const* srcTarget, uint32_t const x
VkImage stagingImage; VkImage stagingImage;
vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage); vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage);
#if FVK_ENABLED(FVK_DEBUG_READ_PIXELS) #if FILAMENT_VULKAN_VERBOSE
utils::slog.d << "readPixels created image=" << stagingImage utils::slog.d << "readPixels created image=" << stagingImage
<< " to copy from image=" << srcTexture->getVkImage() << " to copy from image=" << srcTexture->getVkImage()
<< " src-layout=" << srcTexture->getLayout(0, 0) << utils::io::endl; << " src-layout=" << srcTexture->getLayout(0, 0) << utils::io::endl;
@@ -101,6 +101,10 @@ public:
template<typename D, typename B> template<typename D, typename B>
inline void destruct(Handle<B> handle) noexcept { inline void destruct(Handle<B> handle) noexcept {
auto obj = handle_cast<D*>(handle); auto obj = handle_cast<D*>(handle);
if constexpr (std::is_base_of_v<VulkanIndexBuffer, D>
|| std::is_base_of_v<VulkanBufferObject, D>) {
obj->terminate();
}
TRACK_DECREMENT(); TRACK_DECREMENT();
mHandleAllocatorImpl.deallocate(handle, obj); mHandleAllocatorImpl.deallocate(handle, obj);
} }
@@ -164,10 +164,6 @@ private:
public: public:
using const_iterator = FixedSizeArray::const_iterator; using const_iterator = FixedSizeArray::const_iterator;
inline ~FixedCapacityResourceSet() {
clear();
}
inline const_iterator begin() { inline const_iterator begin() {
if (mInd == 0) { if (mInd == 0) {
return mArray.cend(); return mArray.cend();
@@ -22,7 +22,7 @@
#include <utils/Panic.h> #include <utils/Panic.h>
static constexpr uint32_t TIME_BEFORE_EVICTION = FVK_MAX_COMMAND_BUFFERS; static constexpr uint32_t TIME_BEFORE_EVICTION = VK_MAX_COMMAND_BUFFERS;
namespace filament::backend { namespace filament::backend {
@@ -59,7 +59,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
UTILS_UNUSED_IN_RELEASE VkResult result = vmaCreateBuffer(mAllocator, &bufferInfo, UTILS_UNUSED_IN_RELEASE VkResult result = vmaCreateBuffer(mAllocator, &bufferInfo,
&allocInfo, &stage->buffer, &stage->memory, nullptr); &allocInfo, &stage->buffer, &stage->memory, nullptr);
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION) #ifndef NDEBUG
if (result != VK_SUCCESS) { if (result != VK_SUCCESS) {
utils::slog.e << "Allocation error: " << result << utils::io::endl; utils::slog.e << "Allocation error: " << result << utils::io::endl;
} }
@@ -111,7 +111,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
assert_invariant(result == VK_SUCCESS); assert_invariant(result == VK_SUCCESS);
VkImageAspectFlags const aspectFlags = getImageAspect(vkformat); VkImageAspectFlags const aspectFlags = getImageAspect(vkformat);
VkCommandBuffer const cmdbuffer = mCommands->get().buffer(); const VkCommandBuffer cmdbuffer = mCommands->get().cmdbuffer;
// We use VK_IMAGE_LAYOUT_GENERAL here because the spec says: // We use VK_IMAGE_LAYOUT_GENERAL here because the spec says:
// "Host access to image memory is only well-defined for linear images and for image // "Host access to image memory is only well-defined for linear images and for image
@@ -129,9 +129,6 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
} }
void VulkanStagePool::gc() noexcept { void VulkanStagePool::gc() noexcept {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("stagepool::gc");
// If this is one of the first few frames, return early to avoid wrapping unsigned integers. // If this is one of the first few frames, return early to avoid wrapping unsigned integers.
if (++mCurrentFrame <= TIME_BEFORE_EVICTION) { if (++mCurrentFrame <= TIME_BEFORE_EVICTION) {
return; return;
@@ -185,7 +182,6 @@ void VulkanStagePool::gc() noexcept {
mUsedImages.insert(image); mUsedImages.insert(image);
} }
} }
FVK_SYSTRACE_END();
} }
void VulkanStagePool::terminate() noexcept { void VulkanStagePool::terminate() noexcept {
@@ -79,7 +79,7 @@ void VulkanSwapChain::update() {
void VulkanSwapChain::present() { void VulkanSwapChain::present() {
if (!mHeadless) { if (!mHeadless) {
VkCommandBuffer const cmdbuf = mCommands->get().buffer(); VkCommandBuffer const cmdbuf = mCommands->get().cmdbuffer;
VkImageSubresourceRange const subresources{ VkImageSubresourceRange const subresources{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0, .baseMipLevel = 0,
@@ -106,7 +106,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
if (any(usage & TextureUsage::SAMPLEABLE)) { if (any(usage & TextureUsage::SAMPLEABLE)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE) #if VK_ENABLE_VALIDATION
// Validate that the format is actually sampleable. // Validate that the format is actually sampleable.
VkFormatProperties props; VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(physicalDevice, mVkFormat, &props); vkGetPhysicalDeviceFormatProperties(physicalDevice, mVkFormat, &props);
@@ -160,7 +160,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.samples = (VkSampleCountFlagBits) samples; imageInfo.samples = (VkSampleCountFlagBits) samples;
VkResult error = vkCreateImage(mDevice, &imageInfo, VKALLOC, &mTextureImage); VkResult error = vkCreateImage(mDevice, &imageInfo, VKALLOC, &mTextureImage);
if (error || FVK_ENABLED_BOOL(FVK_DEBUG_TEXTURE)) { if (error || FILAMENT_VULKAN_VERBOSE) {
utils::slog.d << "vkCreateImage: " utils::slog.d << "vkCreateImage: "
<< "image = " << mTextureImage << ", " << "image = " << mTextureImage << ", "
<< "result = " << error << ", " << "result = " << error << ", "
@@ -227,7 +227,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VkImageSubresourceRange range = { getImageAspect(), 0, levels, 0, layers }; VkImageSubresourceRange range = { getImageAspect(), 0, levels, 0, layers };
VulkanCommandBuffer& commands = mCommands->get(); VulkanCommandBuffer& commands = mCommands->get();
VkCommandBuffer const cmdbuf = commands.buffer(); VkCommandBuffer const cmdbuf = commands.cmdbuffer;
commands.acquire(this); commands.acquire(this);
transitionLayout(cmdbuf, range, ImgUtil::getDefaultLayout(imageInfo.usage)); transitionLayout(cmdbuf, range, ImgUtil::getDefaultLayout(imageInfo.usage));
@@ -280,7 +280,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
vmaFlushAllocation(mAllocator, stage->memory, 0, hostData->size); vmaFlushAllocation(mAllocator, stage->memory, 0, hostData->size);
VulkanCommandBuffer& commands = mCommands->get(); VulkanCommandBuffer& commands = mCommands->get();
VkCommandBuffer const cmdbuf = commands.buffer(); VkCommandBuffer const cmdbuf = commands.cmdbuffer;
commands.acquire(this); commands.acquire(this);
VkBufferImageCopy copyRegion = { VkBufferImageCopy copyRegion = {
@@ -343,7 +343,7 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
vmaFlushAllocation(mAllocator, stage->memory, 0, hostData.size); vmaFlushAllocation(mAllocator, stage->memory, 0, hostData.size);
VulkanCommandBuffer& commands = mCommands->get(); VulkanCommandBuffer& commands = mCommands->get();
VkCommandBuffer const cmdbuf = commands.buffer(); VkCommandBuffer const cmdbuf = commands.cmdbuffer;
commands.acquire(this); commands.acquire(this);
// TODO: support blit-based format conversion for 3D images and cubemaps. // TODO: support blit-based format conversion for 3D images and cubemaps.
@@ -417,7 +417,7 @@ void VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, const VkImageSubres
VulkanLayout newLayout) { VulkanLayout newLayout) {
VulkanLayout oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel); VulkanLayout oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel);
#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION) #if FILAMENT_VULKAN_VERBOSE
utils::slog.i << "transition layout of " << mTextureImage << ",layer=" << range.baseArrayLayer utils::slog.i << "transition layout of " << mTextureImage << ",layer=" << range.baseArrayLayer
<< ",level=" << range.baseMipLevel << " from=" << oldLayout << " to=" << newLayout << ",level=" << range.baseMipLevel << " from=" << oldLayout << " to=" << newLayout
<< " format=" << mVkFormat << " format=" << mVkFormat
@@ -463,7 +463,7 @@ VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const {
return mSubresourceLayouts.get(key); return mSubresourceLayouts.get(key);
} }
#if FVK_ENABLED(FVK_DEBUG_TEXTURE) #if FILAMENT_VULKAN_VERBOSE
void VulkanTexture::print() const { void VulkanTexture::print() const {
const uint32_t firstLayer = 0; const uint32_t firstLayer = 0;
const uint32_t lastLayer = firstLayer + mPrimaryViewRange.layerCount; const uint32_t lastLayer = firstLayer + mPrimaryViewRange.layerCount;
+1 -1
View File
@@ -84,7 +84,7 @@ struct VulkanTexture : public HwTexture, VulkanResource {
// For now this always returns either DEPTH or COLOR. // For now this always returns either DEPTH or COLOR.
VkImageAspectFlags getImageAspect() const; VkImageAspectFlags getImageAspect() const;
#if FVK_ENABLED(FVK_DEBUG_TEXTURE) #if FILAMENT_VULKAN_VERBOSE
void print() const; void print() const;
#endif #endif
@@ -45,11 +45,11 @@ constexpr uint32_t const INVALID_VK_INDEX = 0xFFFFFFFF;
typedef std::unordered_set<std::string_view> ExtensionSet; typedef std::unordered_set<std::string_view> ExtensionSet;
#if FVK_ENABLED(FVK_DEBUG_VALIDATION) #if VK_ENABLE_VALIDATION
// These strings need to be allocated outside a function stack // These strings need to be allocated outside a function stack
const std::string_view DESIRED_LAYERS[] = { const std::string_view DESIRED_LAYERS[] = {
"VK_LAYER_KHRONOS_validation", "VK_LAYER_KHRONOS_validation",
#if FVK_ENABLED(FVK_DEBUG_DUMP_API) #if FILAMENT_VULKAN_DUMP_API
"VK_LAYER_LUNARG_api_dump", "VK_LAYER_LUNARG_api_dump",
#endif #endif
#if defined(ENABLE_RENDERDOC) #if defined(ENABLE_RENDERDOC)
@@ -75,7 +75,7 @@ FixedCapacityVector<const char*> getEnabledLayers() {
} }
return enabledLayers; return enabledLayers;
} }
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION) #endif
void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) { void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
// Print some driver or MoltenVK information if it is available. // Print some driver or MoltenVK information if it is available.
@@ -129,10 +129,10 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
<< minor << ")" << utils::io::endl; << minor << ")" << utils::io::endl;
} }
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
void printDepthFormats(VkPhysicalDevice device) { void printDepthFormats(VkPhysicalDevice device) {
// For diagnostic purposes, print useful information about available depth formats. // For diagnostic purposes, print useful information about available depth formats.
// Note that Vulkan is more constrained than OpenGL ES 3.1 in this area. // Note that Vulkan is more constrained than OpenGL ES 3.1 in this area.
if constexpr (VK_ENABLE_VALIDATION && FILAMENT_VULKAN_VERBOSE) {
const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
| VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
utils::slog.i << "Sampleable depth formats: "; utils::slog.i << "Sampleable depth formats: ";
@@ -149,10 +149,10 @@ void printDepthFormats(VkPhysicalDevice device) {
format = (VkFormat) (1 + (int) format); format = (VkFormat) (1 + (int) format);
} }
} }
#endif }
ExtensionSet getInstanceExtensions() { ExtensionSet getInstanceExtensions() {
std::string_view const TARGET_EXTS[] = { std::array<std::string_view, 4> const TARGET_EXTS = {
// Request all cross-platform extensions. // Request all cross-platform extensions.
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
@@ -160,10 +160,6 @@ ExtensionSet getInstanceExtensions() {
// Request these if available. // Request these if available.
VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
#endif
}; };
ExtensionSet exts; ExtensionSet exts;
FixedCapacityVector<VkExtensionProperties> const availableExts FixedCapacityVector<VkExtensionProperties> const availableExts
@@ -180,7 +176,7 @@ ExtensionSet getInstanceExtensions() {
} }
ExtensionSet getDeviceExtensions(VkPhysicalDevice device) { ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
std::string_view const TARGET_EXTS[] = { std::array<std::string_view, 5> const TARGET_EXTS = {
VK_EXT_DEBUG_MARKER_EXTENSION_NAME, VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
VK_KHR_MAINTENANCE1_EXTENSION_NAME, VK_KHR_MAINTENANCE1_EXTENSION_NAME,
@@ -207,7 +203,7 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
VkInstanceCreateInfo instanceCreateInfo = {}; VkInstanceCreateInfo instanceCreateInfo = {};
bool validationFeaturesSupported = false; bool validationFeaturesSupported = false;
#if FVK_ENABLED(FVK_DEBUG_VALIDATION) #if VK_ENABLE_VALIDATION
auto const enabledLayers = getEnabledLayers(); auto const enabledLayers = getEnabledLayers();
if (!enabledLayers.empty()) { if (!enabledLayers.empty()) {
// If layers are supported, Check if VK_EXT_validation_features is supported. // If layers are supported, Check if VK_EXT_validation_features is supported.
@@ -229,17 +225,19 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
#else #else
utils::slog.d << "Validation layer not available; did you install the Vulkan SDK?\n" utils::slog.d << "Validation layer not available; did you install the Vulkan SDK?\n"
<< "Please ensure that VK_LAYER_PATH is set correctly." << utils::io::endl; << "Please ensure that VK_LAYER_PATH is set correctly." << utils::io::endl;
#endif // __ANDROID__ #endif
} }
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION) #endif// VK_ENABLE_VALIDATION
// The Platform class can require 1 or 2 instance extensions, plus we'll request at most 5 // The Platform class can require 1 or 2 instance extensions, plus we'll request at most 5
// instance extensions here in the common code. So that's a max of 7. // instance extensions here in the common code. So that's a max of 7.
static constexpr uint32_t MAX_INSTANCE_EXTENSION_COUNT = 8; static constexpr uint32_t MAX_INSTANCE_EXTENSION_COUNT = 7;
const char* ppEnabledExtensions[MAX_INSTANCE_EXTENSION_COUNT]; const char* ppEnabledExtensions[MAX_INSTANCE_EXTENSION_COUNT];
uint32_t enabledExtensionCount = 0; uint32_t enabledExtensionCount = 0;
#if VK_ENABLE_VALIDATION && defined(__ANDROID__)
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
#endif
if (validationFeaturesSupported) { if (validationFeaturesSupported) {
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME; ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME;
} }
@@ -253,7 +251,7 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
VkApplicationInfo appInfo = {}; VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.apiVersion appInfo.apiVersion
= VK_MAKE_API_VERSION(0, FVK_REQUIRED_VERSION_MAJOR, FVK_REQUIRED_VERSION_MINOR, 0); = VK_MAKE_API_VERSION(0, VK_REQUIRED_VERSION_MAJOR, VK_REQUIRED_VERSION_MINOR, 0);
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceCreateInfo.pApplicationInfo = &appInfo; instanceCreateInfo.pApplicationInfo = &appInfo;
instanceCreateInfo.enabledExtensionCount = enabledExtensionCount; instanceCreateInfo.enabledExtensionCount = enabledExtensionCount;
@@ -275,8 +273,12 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
} }
VkResult result = vkCreateInstance(&instanceCreateInfo, VKALLOC, &instance); VkResult result = vkCreateInstance(&instanceCreateInfo, VKALLOC, &instance);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan instance. Result=%d", #ifndef NDEBUG
result); if (result != VK_SUCCESS) {
utils::slog.e << "Unable to create instance: " << result << utils::io::endl;
}
#endif
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan instance.");
return instance; return instance;
} }
@@ -440,10 +442,10 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
int const minor = VK_VERSION_MINOR(targetDeviceProperties.apiVersion); int const minor = VK_VERSION_MINOR(targetDeviceProperties.apiVersion);
// Does the device support the required Vulkan level? // Does the device support the required Vulkan level?
if (major < FVK_REQUIRED_VERSION_MAJOR) { if (major < VK_REQUIRED_VERSION_MAJOR) {
continue; continue;
} }
if (major == FVK_REQUIRED_VERSION_MAJOR && minor < FVK_REQUIRED_VERSION_MINOR) { if (major == VK_REQUIRED_VERSION_MAJOR && minor < VK_REQUIRED_VERSION_MINOR) {
continue; continue;
} }
@@ -510,7 +512,7 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
VkFormat findSupportedFormat(VkPhysicalDevice device) { VkFormat findSupportedFormat(VkPhysicalDevice device) {
VkFormatFeatureFlags const features = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; VkFormatFeatureFlags const features = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
VkFormat const formats[] = {VK_FORMAT_D32_SFLOAT, VK_FORMAT_X8_D24_UNORM_PACK32}; std::array<VkFormat, 2> const formats = {VK_FORMAT_D32_SFLOAT, VK_FORMAT_X8_D24_UNORM_PACK32};
for (VkFormat format: formats) { for (VkFormat format: formats) {
VkFormatProperties props; VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(device, format, &props); vkGetPhysicalDeviceFormatProperties(device, format, &props);
@@ -664,9 +666,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
// just yet, since that would require a corollary change to the "aspect" flags for the VkImage. // just yet, since that would require a corollary change to the "aspect" flags for the VkImage.
context.mDepthFormat = findSupportedFormat(mImpl->mPhysicalDevice); context.mDepthFormat = findSupportedFormat(mImpl->mPhysicalDevice);
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
printDepthFormats(mImpl->mPhysicalDevice); printDepthFormats(mImpl->mPhysicalDevice);
#endif
// Keep a copy of context for swapchains. // Keep a copy of context for swapchains.
mImpl->mContext = context; mImpl->mContext = context;
+61
View File
@@ -211,5 +211,66 @@ int runTests() {
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }
void getPixelInfo(PixelDataFormat format, PixelDataType type, size_t& outComponents, int& outBpp) {
assert_invariant(type != PixelDataType::COMPRESSED);
switch (format) {
case PixelDataFormat::UNUSED:
case PixelDataFormat::R:
case PixelDataFormat::R_INTEGER:
case PixelDataFormat::DEPTH_COMPONENT:
case PixelDataFormat::ALPHA:
outComponents = 1;
break;
case PixelDataFormat::RG:
case PixelDataFormat::RG_INTEGER:
case PixelDataFormat::DEPTH_STENCIL:
outComponents = 2;
break;
case PixelDataFormat::RGB:
case PixelDataFormat::RGB_INTEGER:
outComponents = 3;
break;
case PixelDataFormat::RGBA:
case PixelDataFormat::RGBA_INTEGER:
outComponents = 4;
break;
}
outBpp = outComponents;
switch (type) {
case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings
case PixelDataType::UBYTE:
case PixelDataType::BYTE:
// nothing to do
break;
case PixelDataType::USHORT:
case PixelDataType::SHORT:
case PixelDataType::HALF:
outBpp *= 2;
break;
case PixelDataType::UINT:
case PixelDataType::INT:
case PixelDataType::FLOAT:
outBpp *= 4;
break;
case PixelDataType::UINT_10F_11F_11F_REV:
// Special case, format must be RGB and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGB);
outBpp = 4;
break;
case PixelDataType::UINT_2_10_10_10_REV:
// Special case, format must be RGBA and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGBA);
outBpp = 4;
break;
case PixelDataType::USHORT_565:
// Special case, format must be RGB and uses 2 bytes
assert_invariant(format == PixelDataFormat::RGB);
outBpp = 2;
break;
}
}
} // namespace test } // namespace test
+6
View File
@@ -75,6 +75,12 @@ private:
filament::backend::Handle<filament::backend::HwBufferObject> uniform; filament::backend::Handle<filament::backend::HwBufferObject> uniform;
}; };
// Utilities
void getPixelInfo(filament::backend::PixelDataFormat format, filament::backend::PixelDataType type,
size_t& outComponents, int& outBpp);
} // namespace test } // namespace test
#endif #endif
-174
View File
@@ -1,174 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef TNT_BACKENDTESTUTILS_H
#define TNT_BACKENDTESTUTILS_H
#include <cstddef>
#include <backend/PixelBufferDescriptor.h>
using namespace filament;
using namespace filament::backend;
inline void getPixelInfo(PixelDataFormat format, PixelDataType type, size_t& outComponents, int& outBpp) {
assert_invariant(type != PixelDataType::COMPRESSED);
switch (format) {
case PixelDataFormat::UNUSED:
case PixelDataFormat::R:
case PixelDataFormat::R_INTEGER:
case PixelDataFormat::DEPTH_COMPONENT:
case PixelDataFormat::ALPHA:
outComponents = 1;
break;
case PixelDataFormat::RG:
case PixelDataFormat::RG_INTEGER:
case PixelDataFormat::DEPTH_STENCIL:
outComponents = 2;
break;
case PixelDataFormat::RGB:
case PixelDataFormat::RGB_INTEGER:
outComponents = 3;
break;
case PixelDataFormat::RGBA:
case PixelDataFormat::RGBA_INTEGER:
outComponents = 4;
break;
}
outBpp = outComponents;
switch (type) {
case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings
case PixelDataType::UBYTE:
case PixelDataType::BYTE:
// nothing to do
break;
case PixelDataType::USHORT:
case PixelDataType::SHORT:
case PixelDataType::HALF:
outBpp *= 2;
break;
case PixelDataType::UINT:
case PixelDataType::INT:
case PixelDataType::FLOAT:
outBpp *= 4;
break;
case PixelDataType::UINT_10F_11F_11F_REV:
// Special case, format must be RGB and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGB);
outBpp = 4;
break;
case PixelDataType::UINT_2_10_10_10_REV:
// Special case, format must be RGBA and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGBA);
outBpp = 4;
break;
case PixelDataType::USHORT_565:
// Special case, format must be RGB and uses 2 bytes
assert_invariant(format == PixelDataFormat::RGB);
outBpp = 2;
break;
}
}
template<typename ComponentType>
static void fillCheckerboard(void* buffer, size_t size, size_t stride, size_t components,
ComponentType value) {
ComponentType* row = (ComponentType*)buffer;
int p = 0;
for (int r = 0; r < size; r++) {
ComponentType* pixel = row;
for (int col = 0; col < size; col++) {
// Generate a checkerboard pattern.
if ((p & 0x0010) ^ ((p / size) & 0x0010)) {
// Turn on the first component (red).
pixel[0] = value;
}
pixel += components;
p++;
}
row += stride * components;
}
}
static PixelBufferDescriptor checkerboardPixelBuffer(PixelDataFormat format, PixelDataType type,
size_t size, size_t bufferPadding = 0) {
size_t components; int bpp;
getPixelInfo(format, type, components, bpp);
size_t bufferSize = size + bufferPadding * 2;
uint8_t* buffer = (uint8_t*) calloc(1, bufferSize * bufferSize * bpp);
uint8_t* ptr = buffer + (bufferSize * bufferPadding * bpp) + (bufferPadding * bpp);
switch (type) {
case PixelDataType::BYTE:
fillCheckerboard<int8_t>(ptr, size, bufferSize, components, 1);
break;
case PixelDataType::UBYTE:
fillCheckerboard<uint8_t>(ptr, size, bufferSize, components, 0xFF);
break;
case PixelDataType::SHORT:
fillCheckerboard<int16_t>(ptr, size, bufferSize, components, 1);
break;
case PixelDataType::USHORT:
fillCheckerboard<uint16_t>(ptr, size, bufferSize, components, 1u);
break;
case PixelDataType::UINT:
fillCheckerboard<uint32_t>(ptr, size, bufferSize, components, 1u);
break;
case PixelDataType::INT:
fillCheckerboard<int32_t>(ptr, size, bufferSize, components, 1);
break;
case PixelDataType::FLOAT:
fillCheckerboard<float>(ptr, size, bufferSize, components, 1.0f);
break;
case PixelDataType::HALF:
fillCheckerboard<math::half>(ptr, size, bufferSize, components, math::half(1.0f));
break;
case PixelDataType::UINT_2_10_10_10_REV:
fillCheckerboard<uint32_t>(ptr, size, bufferSize, 1, 0xC00003FF /* red */);
break;
case PixelDataType::USHORT_565:
fillCheckerboard<uint16_t>(ptr, size, bufferSize, 1, 0xF800 /* red */);
break;
case PixelDataType::UINT_10F_11F_11F_REV:
fillCheckerboard<uint32_t>(ptr, size, bufferSize, 1, 0x000003C0 /* red */);
break;
case PixelDataType::COMPRESSED:
break;
}
PixelBufferDescriptor descriptor(buffer, bufferSize * bufferSize * bpp, format, type,
1, bufferPadding, bufferPadding, bufferSize, [](void* buffer, size_t size, void* user) {
free(buffer);
}, nullptr);
return descriptor;
}
#endif // TNT_BACKENDTESTUTILS_H
+84 -1
View File
@@ -18,7 +18,6 @@
#include "ShaderGenerator.h" #include "ShaderGenerator.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
#include "BackendTestUtils.h"
#include "private/filament/SamplerInterfaceBlock.h" #include "private/filament/SamplerInterfaceBlock.h"
#include "private/backend/SamplerGroup.h" #include "private/backend/SamplerGroup.h"
@@ -114,7 +113,91 @@ namespace test {
template<typename componentType> inline componentType getMaxValue(); template<typename componentType> inline componentType getMaxValue();
template<typename ComponentType>
static void fillCheckerboard(void* buffer, size_t size, size_t stride, size_t components,
ComponentType value) {
ComponentType* row = (ComponentType*)buffer;
int p = 0;
for (int r = 0; r < size; r++) {
ComponentType* pixel = row;
for (int col = 0; col < size; col++) {
// Generate a checkerboard pattern.
if ((p & 0x0010) ^ ((p / size) & 0x0010)) {
// Turn on the first component (red).
pixel[0] = value;
}
pixel += components;
p++;
}
row += stride * components;
}
}
static PixelBufferDescriptor checkerboardPixelBuffer(PixelDataFormat format, PixelDataType type,
size_t size, size_t bufferPadding = 0) {
size_t components; int bpp;
getPixelInfo(format, type, components, bpp);
size_t bufferSize = size + bufferPadding * 2;
uint8_t* buffer = (uint8_t*) calloc(1, bufferSize * bufferSize * bpp);
uint8_t* ptr = buffer + (bufferSize * bufferPadding * bpp) + (bufferPadding * bpp);
switch (type) {
case PixelDataType::BYTE:
fillCheckerboard<int8_t>(ptr, size, bufferSize, components, 1);
break;
case PixelDataType::UBYTE:
fillCheckerboard<uint8_t>(ptr, size, bufferSize, components, 0xFF);
break;
case PixelDataType::SHORT:
fillCheckerboard<int16_t>(ptr, size, bufferSize, components, 1);
break;
case PixelDataType::USHORT:
fillCheckerboard<uint16_t>(ptr, size, bufferSize, components, 1u);
break;
case PixelDataType::UINT:
fillCheckerboard<uint32_t>(ptr, size, bufferSize, components, 1u);
break;
case PixelDataType::INT:
fillCheckerboard<int32_t>(ptr, size, bufferSize, components, 1);
break;
case PixelDataType::FLOAT:
fillCheckerboard<float>(ptr, size, bufferSize, components, 1.0f);
break;
case PixelDataType::HALF:
fillCheckerboard<math::half>(ptr, size, bufferSize, components, math::half(1.0f));
break;
case PixelDataType::UINT_2_10_10_10_REV:
fillCheckerboard<uint32_t>(ptr, size, bufferSize, 1, 0xC00003FF /* red */);
break;
case PixelDataType::USHORT_565:
fillCheckerboard<uint16_t>(ptr, size, bufferSize, 1, 0xF800 /* red */);
break;
case PixelDataType::UINT_10F_11F_11F_REV:
fillCheckerboard<uint32_t>(ptr, size, bufferSize, 1, 0x000003C0 /* red */);
break;
case PixelDataType::COMPRESSED:
break;
}
PixelBufferDescriptor descriptor(buffer, bufferSize * bufferSize * bpp, format, type,
1, bufferPadding, bufferPadding, bufferSize, [](void* buffer, size_t size, void* user) {
free(buffer);
}, nullptr);
return descriptor;
}
inline std::string stringReplace(const std::string& find, const std::string& replace, inline std::string stringReplace(const std::string& find, const std::string& replace,
std::string source) { std::string source) {
-245
View File
@@ -1,245 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "BackendTest.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "BackendTestUtils.h"
#include "private/backend/SamplerGroup.h"
namespace {
////////////////////////////////////////////////////////////////////////////////////////////////////
// Shaders
////////////////////////////////////////////////////////////////////////////////////////////////////
std::string vertex (R"(#version 450 core
layout(location = 0) in vec4 mesh_position;
layout(location = 0) out vec2 uv;
void main() {
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
uv = (mesh_position.xy * 0.5 + 0.5);
}
)");
std::string fragment (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
layout(location = 0) in vec2 uv;
layout(location = 0, set = 1) uniform sampler2D backend_test_sib_tex;
void main() {
fragColor = textureLod(backend_test_sib_tex, uv, 1);
}
)");
std::string whiteFragment (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
layout(location = 0) in vec2 uv;
layout(location = 0, set = 1) uniform sampler2D backend_test_sib_tex;
void main() {
fragColor = vec4(1.0);
}
)");
}
namespace test {
using namespace filament;
using namespace filament::backend;
TEST_F(BackendTest, SetMinMaxLevel) {
auto& api = getDriverApi();
api.startCapture(0);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a SwapChain and make it current.
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program that draws only white.
Handle<HwProgram> whiteProgram;
{
ShaderGenerator shaderGen(vertex, whiteFragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
Program::Sampler sampler{utils::CString("backend_test_sib_tex"), 0};
p.setSamplerGroup(0, ShaderStageFlags::FRAGMENT, &sampler, 1);
whiteProgram = api.createProgram(std::move(p));
}
// Create a program that samples a texture.
Handle<HwProgram> textureProgram;
{
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program p = shaderGen.getProgram(api);
Program::Sampler sampler{utils::CString("backend_test_sib_tex"), 0};
p.setSamplerGroup(0, ShaderStageFlags::FRAGMENT, &sampler, 1);
textureProgram = api.createProgram(std::move(p));
}
// Create a texture that has 4 mip levels. Each level is a different color.
// Level 0: 128x128 (red)
// Level 1: 64x64 (green)
// Level 2: 32x32 (blue)
// Level 3: 16x16 (yellow)
const size_t kTextureSize = 128;
const size_t kMipLevels = 4;
Handle<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE);
// Create image data.
auto pixelFormat = PixelDataFormat::RGBA;
auto pixelType = PixelDataType::UBYTE;
size_t components; int bpp;
getPixelInfo(pixelFormat, pixelType, components, bpp);
uint32_t colors[] = {
0xFF0000FF, /* red */
0xFF00FF00, /* green */
0xFFFF0000, /* blue */
0xFF00FFFF, /* yellow */
};
for (int l = 0; l < kMipLevels; l++) {
size_t mipSize = kTextureSize >> l;
auto* buffer = (uint8_t*)calloc(1, mipSize * mipSize * bpp);
fillCheckerboard<uint32_t>(buffer, mipSize, mipSize, 1, colors[l]);
PixelBufferDescriptor descriptor(
buffer, mipSize * mipSize * bpp, pixelFormat, pixelType, 1, 0, 0,
mipSize, [](void* buffer, size_t size, void* user) { free(buffer); },
nullptr);
api.update3DImage(
texture, l, 0, 0, 0, mipSize, mipSize, 1, std::move(descriptor));
}
TrianglePrimitive triangle(api);
api.beginFrame(0, 0);
// We set the base mip to 1, and the max mip to 3
// Level 0: 128x128 (red)
// Level 1: 64x64 (green) <-- base
// Level 2: 32x32 (blue) <--- white triangle rendered
// Level 3: 16x16 (yellow) <-- max
api.setMinMaxLevels(texture, 1, 3);
// Render a white triangle into level 2.
// We specify mip level 2, because minMaxLevels has no effect when rendering into a texture.
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, 32, 32, 1,
{texture, 2 /* level */, 0 /* layer */}, {}, {});
{
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = whiteProgram;
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
api.beginRenderPass(renderTarget, params);
api.draw(ps, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
}
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = {0.f, 0.f, 0.5f, 1.f};
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state;
state.scissor = params.viewport;
state.program = textureProgram;
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = SamplerCompareFunc::A;
state.rasterState.culling = CullingMode::NONE;
SamplerGroup samplers(1);
SamplerParams samplerParams {};
samplerParams.filterMag = SamplerMagFilter::NEAREST;
samplerParams.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST;
samplers.setSampler(0, { texture, samplerParams });
backend::Handle<HwSamplerGroup> samplerGroup = api.createSamplerGroup(1);
api.updateSamplerGroup(samplerGroup, samplers.toBufferDescriptor(api));
api.bindSamplers(0, samplerGroup);
// Render a triangle to the screen, sampling from mip level 1.
// Because the min level is 1, the result color should be the white triangle drawn in the
// previous pass.
api.beginRenderPass(defaultRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
// Adjust the base mip to 2.
// Note that this is done without another call to updateSamplerGroup.
api.setMinMaxLevels(texture, 2, 3);
// Render a second, smaller, triangle, again sampling from mip level 1.
// This triangle should be yellow striped.
static filament::math::float2 vertices[3] = {
{ -0.5, -0.5 },
{ 0.5, -0.5 },
{ -0.5, 0.5 }
};
triangle.updateVertices(vertices);
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
api.beginRenderPass(defaultRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
api.commit(swapChain);
api.endFrame(0);
api.stopCapture(0);
// Cleanup.
api.destroySwapChain(swapChain);
api.destroyRenderTarget(renderTarget);
api.destroyTexture(texture);
api.destroyProgram(whiteProgram);
api.destroyProgram(textureProgram);
}
api.finish();
executeCommands();
getDriver().purge();
}
} // namespace test
@@ -18,7 +18,6 @@
#include "ShaderGenerator.h" #include "ShaderGenerator.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
#include "BackendTestUtils.h"
#include <utils/Hash.h> #include <utils/Hash.h>
+2 -11
View File
@@ -25,7 +25,6 @@
#include <utils/EntityInstance.h> #include <utils/EntityInstance.h>
#include <math/mathfwd.h> #include <math/mathfwd.h>
#include <math/quat.h>
namespace utils { namespace utils {
class Entity; class Entity;
@@ -245,7 +244,6 @@ public:
* shadows that are too far and wouldn't contribute to the scene much, improving * shadows that are too far and wouldn't contribute to the scene much, improving
* performance and quality. This value is always positive. * performance and quality. This value is always positive.
* Use 0.0f to use the camera far distance. * Use 0.0f to use the camera far distance.
* This only affect directional lights.
*/ */
float shadowFar = 0.0f; float shadowFar = 0.0f;
@@ -362,13 +360,6 @@ public:
* enabled. (2cm by default). * enabled. (2cm by default).
*/ */
float shadowBulbRadius = 0.02f; float shadowBulbRadius = 0.02f;
/**
* Transforms the shadow direction. Must be a unit quaternion.
* The default is identity.
* Ignored if the light type isn't directional. For artistic use. Use with caution.
*/
math::quatf transform{ 1.0f };
}; };
struct ShadowCascades { struct ShadowCascades {
@@ -680,7 +671,7 @@ public:
* @return true is this light is a type of directional light * @return true is this light is a type of directional light
*/ */
inline bool isDirectional(Instance i) const noexcept { inline bool isDirectional(Instance i) const noexcept {
Type const type = getType(i); Type type = getType(i);
return type == Type::DIRECTIONAL || type == Type::SUN; return type == Type::DIRECTIONAL || type == Type::SUN;
} }
@@ -701,7 +692,7 @@ public:
* @return true is this light is a type of spot light * @return true is this light is a type of spot light
*/ */
inline bool isSpotLight(Instance i) const noexcept { inline bool isSpotLight(Instance i) const noexcept {
Type const type = getType(i); Type type = getType(i);
return type == Type::SPOT || type == Type::FOCUSED_SPOT; return type == Type::SPOT || type == Type::FOCUSED_SPOT;
} }
-11
View File
@@ -141,17 +141,6 @@ struct BloomOptions {
bool enabled = false; //!< enable or disable bloom bool enabled = false; //!< enable or disable bloom
float highlight = 1000.0f; //!< limit highlights to this value before bloom [10, +inf] float highlight = 1000.0f; //!< limit highlights to this value before bloom [10, +inf]
/**
* Bloom quality level.
* LOW (default): use a more optimized down-sampling filter, however there can be artifacts
* with dynamic resolution, this can be alleviated by using the homogenous mode.
* MEDIUM: Good balance between quality and performance.
* HIGH: In this mode the bloom resolution is automatically increased to avoid artifacts.
* This mode can be significantly slower on mobile, especially at high resolution.
* This mode greatly improves the anamorphic bloom.
*/
QualityLevel quality = QualityLevel::LOW;
bool lensFlare = false; //!< enable screen-space lens flare bool lensFlare = false; //!< enable screen-space lens flare
bool starburst = true; //!< enable starburst effect on lens flare bool starburst = true; //!< enable starburst effect on lens flare
float chromaticAberration = 0.005f; //!< amount of chromatic aberration float chromaticAberration = 0.005f; //!< amount of chromatic aberration
+5 -5
View File
@@ -285,7 +285,7 @@ void PerViewUniforms::prepareDirectionalLight(FEngine& engine,
float exposure, float exposure,
float3 const& sceneSpaceDirection, float3 const& sceneSpaceDirection,
PerViewUniforms::LightManagerInstance directionalLight) noexcept { PerViewUniforms::LightManagerInstance directionalLight) noexcept {
FLightManager const& lcm = engine.getLightManager(); FLightManager& lcm = engine.getLightManager();
auto& s = mUniforms.edit(); auto& s = mUniforms.edit();
const float3 l = -sceneSpaceDirection; // guaranteed normalized const float3 l = -sceneSpaceDirection; // guaranteed normalized
@@ -302,11 +302,11 @@ void PerViewUniforms::prepareDirectionalLight(FEngine& engine,
// The last parameter must be < 0.0f for regular directional lights // The last parameter must be < 0.0f for regular directional lights
float4 sun{ 0.0f, 0.0f, 0.0f, -1.0f }; float4 sun{ 0.0f, 0.0f, 0.0f, -1.0f };
if (UTILS_UNLIKELY(isSun && colorIntensity.w > 0.0f)) { if (UTILS_UNLIKELY(isSun && colorIntensity.w > 0.0f)) {
// Currently we have only a single directional light, so it's probably likely that it's // currently we have only a single directional light, so it's probably likely that it's
// also the Sun. However, conceptually, most directional lights won't be sun lights. // also the Sun. However, conceptually, most directional lights won't be sun lights.
float const radius = lcm.getSunAngularRadius(directionalLight); float radius = lcm.getSunAngularRadius(directionalLight);
float const haloSize = lcm.getSunHaloSize(directionalLight); float haloSize = lcm.getSunHaloSize(directionalLight);
float const haloFalloff = lcm.getSunHaloFalloff(directionalLight); float haloFalloff = lcm.getSunHaloFalloff(directionalLight);
sun.x = std::cos(radius); sun.x = std::cos(radius);
sun.y = std::sin(radius); sun.y = std::sin(radius);
sun.z = 1.0f / (std::cos(radius * haloSize) - sun.x); sun.z = 1.0f / (std::cos(radius * haloSize) - sun.x);
+135 -149
View File
@@ -212,8 +212,6 @@ static const PostProcessManager::MaterialInfo sMaterialList[] = {
{ "bilateralBlurBentNormals", MATERIAL(BILATERALBLURBENTNORMALS) }, { "bilateralBlurBentNormals", MATERIAL(BILATERALBLURBENTNORMALS) },
{ "blitLow", MATERIAL(BLITLOW) }, { "blitLow", MATERIAL(BLITLOW) },
{ "bloomDownsample", MATERIAL(BLOOMDOWNSAMPLE) }, { "bloomDownsample", MATERIAL(BLOOMDOWNSAMPLE) },
{ "bloomDownsample2x", MATERIAL(BLOOMDOWNSAMPLE2X) },
{ "bloomDownsample9", MATERIAL(BLOOMDOWNSAMPLE9) },
{ "bloomUpsample", MATERIAL(BLOOMUPSAMPLE) }, { "bloomUpsample", MATERIAL(BLOOMUPSAMPLE) },
{ "colorGrading", MATERIAL(COLORGRADING) }, { "colorGrading", MATERIAL(COLORGRADING) },
{ "colorGradingAsSubpass", MATERIAL(COLORGRADINGASSUBPASS) }, { "colorGradingAsSubpass", MATERIAL(COLORGRADINGASSUBPASS) },
@@ -452,6 +450,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
for (size_t level = 0; level < levelCount - 1; level++) { for (size_t level = 0; level < levelCount - 1; level++) {
auto out = resources.getRenderPassInfo(level); auto out = resources.getRenderPassInfo(level);
driver.setMinMaxLevels(in, level, level); driver.setMinMaxLevels(in, level, level);
mi->setParameter("level", uint32_t(level));
commitAndRender(out, material, driver); commitAndRender(out, material, driver);
} }
driver.setMinMaxLevels(in, 0, levelCount - 1); driver.setMinMaxLevels(in, 0, levelCount - 1);
@@ -1597,6 +1596,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
auto const& out = resources.getRenderPassInfo(data.rp[level]); auto const& out = resources.getRenderPassInfo(data.rp[level]);
driver.setMinMaxLevels(inOutColor, level, level); driver.setMinMaxLevels(inOutColor, level, level);
driver.setMinMaxLevels(inOutCoc, level, level); driver.setMinMaxLevels(inOutCoc, level, level);
mi->setParameter("mip", uint32_t(level));
mi->setParameter("weightScale", 0.5f / float(1u<<level)); // FIXME: halfres? mi->setParameter("weightScale", 0.5f / float(1u<<level)); // FIXME: halfres?
mi->setParameter("texelSize", float2{ 1.0f / w, 1.0f / h }); mi->setParameter("texelSize", float2{ 1.0f / w, 1.0f / h });
mi->commit(driver); mi->commit(driver);
@@ -1870,53 +1870,15 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
return bloomPass(fg, input, outFormat, inoutBloomOptions, scale); return bloomPass(fg, input, outFormat, inoutBloomOptions, scale);
} }
FrameGraphId<FrameGraphTexture> PostProcessManager::downscalePass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphTexture::Descriptor const& outDesc,
bool threshold, float highlight, bool fireflies) noexcept {
struct DownsampleData {
FrameGraphId<FrameGraphTexture> input;
FrameGraphId<FrameGraphTexture> output;
};
auto& downsamplePass = fg.addPass<DownsampleData>("Downsample",
[&](FrameGraph::Builder& builder, auto& data) {
data.input = builder.sample(input);
data.output = builder.createTexture("Downsample-output", outDesc);
builder.declareRenderPass(data.output);
},
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
auto const& material = getPostProcessMaterial("bloomDownsample2x");
auto* mi = material.getMaterialInstance(mEngine);
mi->setParameter("source", resources.getTexture(data.input), {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR
});
mi->setParameter("level", 0);
mi->setParameter("threshold", threshold ? 1.0f : 0.0f);
mi->setParameter("fireflies", fireflies ? 1.0f : 0.0f);
mi->setParameter("invHighlight", std::isinf(highlight) ? 0.0f : 1.0f / highlight);
commitAndRender(resources.getRenderPassInfo(), material, driver);
});
return downsamplePass->output;
}
PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg, PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, TextureFormat outFormat, FrameGraphId<FrameGraphTexture> input, TextureFormat outFormat,
BloomOptions& inoutBloomOptions, float2 scale) noexcept { BloomOptions& inoutBloomOptions, float2 scale) noexcept {
// Figure out a good size for the bloom buffer.
// Figure out a good size for the bloom buffer. We must use a fixed bloom buffer size so auto const& desc = fg.getDescriptor(input);
// that the size/strength of the bloom doesn't vary much with the resolution, otherwise
// dynamic resolution would affect the bloom effect too much.
auto desc = fg.getDescriptor(input);
// width and height after dynamic resolution upscaling // width and height after dynamic resolution upscaling
const float aspect = (float(desc.width) * scale.y) / (float(desc.height) * scale.x); const float aspect = (float(desc.width) * scale.y) / (float(desc.height) * scale.x);
// FIXME: don't allow inoutBloomOptions.resolution to be larger than input's resolution
// (avoid upscale) but how does this affect dynamic resolution
// FIXME: check what happens on WebGL and intel's processors
// compute the desired bloom buffer size // compute the desired bloom buffer size
float bloomHeight = float(inoutBloomOptions.resolution); float bloomHeight = float(inoutBloomOptions.resolution);
float bloomWidth = bloomHeight * aspect; float bloomWidth = bloomHeight * aspect;
@@ -1931,73 +1893,38 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg
bloomHeight *= inoutBloomOptions.anamorphism; bloomHeight *= inoutBloomOptions.anamorphism;
} }
// convert back to integer width/height
const uint32_t width = std::max(1u, uint32_t(std::floor(bloomWidth)));
const uint32_t height = std::max(1u, uint32_t(std::floor(bloomHeight)));
// we might need to adjust the max # of levels // we might need to adjust the max # of levels
const uint32_t major = uint32_t(std::max(bloomWidth, bloomHeight)); const uint32_t major = uint32_t(std::max(bloomWidth, bloomHeight));
const uint8_t maxLevels = FTexture::maxLevelCount(major); const uint8_t maxLevels = FTexture::maxLevelCount(major);
inoutBloomOptions.levels = std::min(inoutBloomOptions.levels, maxLevels); inoutBloomOptions.levels = std::min(inoutBloomOptions.levels, maxLevels);
inoutBloomOptions.levels = std::min(inoutBloomOptions.levels, kMaxBloomLevels); inoutBloomOptions.levels = std::min(inoutBloomOptions.levels, kMaxBloomLevels);
if (inoutBloomOptions.quality == QualityLevel::LOW) { if (2 * width < desc.width || 2 * height < desc.height) {
// In low quality mode, we adjust the bloom buffer size so that both dimensions // if we're scaling down by more than 2x, prescale the image with a blit to improve
// have enough exact mip levels. This can slightly affect the aspect ratio causing // performance. This is important on mobile/tilers.
// some artifacts: input = opaqueBlit(fg, input, { 0, 0, desc.width, desc.height }, {
// - add some anamorphism (experimentally not visible) .width = std::max(1u, desc.width / 2),
// - visible bloom size changes with dynamic resolution in non-homogenous mode .height = std::max(1u, desc.height / 2),
// This allows us to use the 9 sample downsampling filter (instead of 13)
// for at least 4 levels.
uint32_t width = std::max(1u, uint32_t(std::floor(bloomWidth)));
uint32_t height = std::max(1u, uint32_t(std::floor(bloomHeight)));
width &= ~((1 << 4) - 1); // at least 4 levels
height &= ~((1 << 4) - 1);
bloomWidth = float(width);
bloomHeight = float(height);
}
bool threshold = inoutBloomOptions.threshold;
while (2 * bloomWidth < float(desc.width) || 2 * bloomHeight < float(desc.height)) {
if (inoutBloomOptions.quality == QualityLevel::LOW ||
inoutBloomOptions.quality == QualityLevel::MEDIUM) {
input = downscalePass(fg, input, {
.width = (desc.width = std::max(1u, desc.width / 2)),
.height = (desc.height = std::max(1u, desc.height / 2)),
.format = outFormat .format = outFormat
}, });
threshold, inoutBloomOptions.highlight, threshold);
threshold = false; // we do the thresholding only once during down sampling
} else if (inoutBloomOptions.quality == QualityLevel::HIGH ||
inoutBloomOptions.quality == QualityLevel::ULTRA) {
// In high quality mode, we increase the size of the bloom buffer such that the
// first scaling is less than 2x, and we increase the number of levels accordingly.
if (bloomWidth * 2.0f > 2048.0f || bloomHeight * 2.0f > 2048.0f) {
// but we can't scale above the h/w guaranteed minspec
break;
} }
bloomWidth *= 2.0f;
bloomHeight *= 2.0f;
inoutBloomOptions.levels++;
}
}
// convert back to integer width/height
uint32_t const width = std::max(1u, uint32_t(std::floor(bloomWidth)));
uint32_t const height = std::max(1u, uint32_t(std::floor(bloomHeight)));
input = downscalePass(fg, input,
{ .width = width, .height = height, .format = outFormat },
threshold, inoutBloomOptions.highlight, threshold);
struct BloomPassData { struct BloomPassData {
FrameGraphId<FrameGraphTexture> in;
FrameGraphId<FrameGraphTexture> out; FrameGraphId<FrameGraphTexture> out;
FrameGraphId<FrameGraphTexture> stage;
uint32_t outRT[kMaxBloomLevels]; uint32_t outRT[kMaxBloomLevels];
uint32_t stageRT[kMaxBloomLevels];
}; };
// Creating a mip-chain poses a "feedback" loop problem on some GPU. We will disable // downsample phase
// Bloom on these.
// See: https://github.com/google/filament/issues/2338
auto& bloomDownsamplePass = fg.addPass<BloomPassData>("Bloom Downsample", auto& bloomDownsamplePass = fg.addPass<BloomPassData>("Bloom Downsample",
[&](FrameGraph::Builder& builder, auto& data) { [&](FrameGraph::Builder& builder, auto& data) {
data.in = builder.sample(input);
data.out = builder.createTexture("Bloom Out Texture", { data.out = builder.createTexture("Bloom Out Texture", {
.width = width, .width = width,
.height = height, .height = height,
@@ -2006,107 +1933,165 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg
}); });
data.out = builder.sample(data.out); data.out = builder.sample(data.out);
data.stage = builder.createTexture("Bloom Stage Texture", {
.width = width,
.height = height,
.levels = inoutBloomOptions.levels,
.format = outFormat
});
data.stage = builder.sample(data.stage);
for (size_t i = 0; i < inoutBloomOptions.levels; i++) { for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
auto out = builder.createSubresource(data.out, "Bloom Out Texture mip", auto out = builder.createSubresource(data.out, "Bloom Out Texture mip",
{ .level = uint8_t(i) }); { .level = uint8_t(i) });
if (i == 0) { auto stage = builder.createSubresource(data.stage,
// this causes the last blit above to render into this mip "Bloom Stage Texture mip", { .level = uint8_t(i) });
fg.forwardResource(out, input);
}
builder.declareRenderPass(out, &data.outRT[i]); builder.declareRenderPass(out, &data.outRT[i]);
builder.declareRenderPass(stage, &data.stageRT[i]);
} }
}, },
[=](FrameGraphResources const& resources, [=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) { auto const& data, DriverApi& driver) {
// TODO: if downsampling is not exactly a multiple of two, use the 13 samples auto hwIn = resources.getTexture(data.in);
// filter. This is generally the accepted solution, however, the 13 samples
// filter is not correct either when we don't sample at integer coordinates,
// but it seems ot create less artifacts.
// A better solution might be to use the filter described in
// Castaño, 2013, "Shadow Mapping Summary Part 1", which is 5x5 filter with
// 9 samples, but works at all coordinates.
auto hwOut = resources.getTexture(data.out); auto hwOut = resources.getTexture(data.out);
auto hwStage = resources.getTexture(data.stage);
auto const& material9 = getPostProcessMaterial("bloomDownsample9"); auto const& material = getPostProcessMaterial("bloomDownsample");
auto const& material13 = getPostProcessMaterial("bloomDownsample"); auto const* ma = material.getMaterial(mEngine);
auto* mi9 = material9.getMaterialInstance(mEngine); FMaterialInstance* mis[] = {
auto* mi13 = material13.getMaterialInstance(mEngine); ma->createInstance("bloomDownsample-ping"),
ma->createInstance("bloomDownsample-pong"),
ma->createInstance("bloomDownsample-first"),
};
mi9->setParameter("source", hwOut, { mis[0]->setParameter("source", hwOut, {
.filterMag = SamplerMagFilter::LINEAR, .filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST }); .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
mi13->setParameter("source", hwOut, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST });
mi9->commit(driver);
mi13->commit(driver);
// PipelineState for both materials should be the same
const PipelineState pipeline(material9.getPipelineState(mEngine));
for (size_t i = 1; i < inoutBloomOptions.levels; i++) {
auto hwDstRT = resources.getRenderPassInfo(data.outRT[i]);
hwDstRT.params.flags.discardStart = TargetBufferFlags::COLOR;
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
// if downsampling is a multiple of 2 in each dimension we can use the
// 9 samples filter.
auto vp = resources.getRenderPassInfo(data.outRT[i-1]).params.viewport;
auto* const mi = (vp.width & 1 || vp.height & 1) ? mi13 : mi9;
mi->use(driver);
driver.setMinMaxLevels(hwOut, i - 1, i - 1); // this offsets baseLevel to i-1
render(hwDstRT, pipeline, driver);
}
driver.setMinMaxLevels(hwOut, 0, inoutBloomOptions.levels - 1);
}); });
// output of bloom downsample pass becomes input of next (flare) pass mis[1]->setParameter("source", hwStage, {
input = bloomDownsamplePass->out; .filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
});
mis[2]->setParameter("source", hwIn, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
});
for (auto* mi : mis) {
mi->setParameter("level", 0.0f);
mi->setParameter("threshold", inoutBloomOptions.threshold ? 1.0f : 0.0f);
mi->setParameter("invHighlight", std::isinf(inoutBloomOptions.highlight)
? 0.0f : 1.0f / inoutBloomOptions.highlight);
mi->commit(driver);
}
const PipelineState pipeline(material.getPipelineState(mEngine));
{ // first iteration
auto hwDstRT = resources.getRenderPassInfo(data.outRT[0]);
hwDstRT.params.flags.discardStart = TargetBufferFlags::COLOR;
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
mis[2]->use(driver);
render(hwDstRT, pipeline, driver);
}
for (size_t i = 1; i < inoutBloomOptions.levels; i++) {
const size_t parity = 1u - (i & 0x1u);
auto hwDstRT = resources.getRenderPassInfo(parity ? data.outRT[i] : data.stageRT[i]);
hwDstRT.params.flags.discardStart = TargetBufferFlags::COLOR;
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
mis[parity]->setParameter("level", float(i - 1));
mis[parity]->commit(driver);
mis[parity]->use(driver);
render(hwDstRT, pipeline, driver);
}
for (auto& mi : mis) {
mEngine.destroy(mi);
}
});
FrameGraphId<FrameGraphTexture> output = bloomDownsamplePass->out;
FrameGraphId<FrameGraphTexture> stage = bloomDownsamplePass->stage;
// flare pass // flare pass
auto flare = flarePass(fg, input, width, height, outFormat, inoutBloomOptions); auto flare = flarePass(fg, bloomDownsamplePass->out, width, height, outFormat, inoutBloomOptions);
// upsample phase
auto& bloomUpsamplePass = fg.addPass<BloomPassData>("Bloom Upsample", auto& bloomUpsamplePass = fg.addPass<BloomPassData>("Bloom Upsample",
[&](FrameGraph::Builder& builder, auto& data) { [&](FrameGraph::Builder& builder, auto& data) {
data.out = builder.sample(input); data.out = builder.sample(output);
data.stage = builder.sample(stage);
for (size_t i = 0; i < inoutBloomOptions.levels; i++) { for (size_t i = 0; i < inoutBloomOptions.levels; i++) {
auto out = builder.createSubresource(data.out, "Bloom Out Texture mip", auto out = builder.createSubresource(data.out, "Bloom Out Texture mip",
{ .level = uint8_t(i) }); { .level = uint8_t(i) });
auto staging = builder.createSubresource(data.stage,
"Bloom Stage Texture mip", { .level = uint8_t(i) });
builder.declareRenderPass(out, &data.outRT[i]); builder.declareRenderPass(out, &data.outRT[i]);
builder.declareRenderPass(staging, &data.stageRT[i]);
} }
}, },
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
auto hwOut = resources.getTexture(data.out); auto hwOut = resources.getTexture(data.out);
auto hwStage = resources.getTexture(data.stage);
auto const& outDesc = resources.getDescriptor(data.out); auto const& outDesc = resources.getDescriptor(data.out);
auto const& material = getPostProcessMaterial("bloomUpsample"); auto const& material = getPostProcessMaterial("bloomUpsample");
auto* mi = material.getMaterialInstance(mEngine); auto const* ma = material.getMaterial(mEngine);
mi->setParameter("source", hwOut, {
FMaterialInstance* mis[] = {
ma->createInstance("bloomUpsample-ping"),
ma->createInstance("bloomUpsample-pong"),
};
mis[0]->setParameter("source", hwOut, {
.filterMag = SamplerMagFilter::LINEAR, .filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST}); .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
mi->use(driver); });
mis[1]->setParameter("source", hwStage, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
});
PipelineState pipeline(material.getPipelineState(mEngine)); PipelineState pipeline(material.getPipelineState(mEngine));
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE; pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE; pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
for (size_t j = inoutBloomOptions.levels, i = j - 1; i >= 1; i--, j++) { for (size_t j = inoutBloomOptions.levels, i = j - 1; i >= 1; i--, j++) {
auto hwDstRT = resources.getRenderPassInfo(data.outRT[i - 1]); const size_t parity = 1u - (j % 2u);
auto hwDstRT = resources.getRenderPassInfo(
parity ? data.outRT[i - 1] : data.stageRT[i - 1]);
hwDstRT.params.flags.discardStart = TargetBufferFlags::NONE; // b/c we'll blend hwDstRT.params.flags.discardStart = TargetBufferFlags::NONE; // b/c we'll blend
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE; hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
auto w = FTexture::valueForLevel(i - 1, outDesc.width); auto w = FTexture::valueForLevel(i - 1, outDesc.width);
auto h = FTexture::valueForLevel(i - 1, outDesc.height); auto h = FTexture::valueForLevel(i - 1, outDesc.height);
mi->setParameter("resolution", float4{ w, h, 1.0f / w, 1.0f / h }); mis[parity]->setParameter("resolution", float4{ w, h, 1.0f / w, 1.0f / h });
mi->commit(driver); mis[parity]->setParameter("level", float(i));
driver.setMinMaxLevels(hwOut, i, i); // this offsets baseLevel to i mis[parity]->commit(driver);
mis[parity]->use(driver);
render(hwDstRT, pipeline, driver); render(hwDstRT, pipeline, driver);
} }
driver.setMinMaxLevels(hwOut, 0, inoutBloomOptions.levels - 1);
for (auto& mi : mis) {
mEngine.destroy(mi);
}
// Every other level is missing from the out texture, so we need to do
// blits to complete the chain.
const SamplerMagFilter filter = SamplerMagFilter::NEAREST;
for (size_t i = 1; i < inoutBloomOptions.levels; i += 2) {
auto in = resources.getRenderPassInfo(data.stageRT[i]);
auto out = resources.getRenderPassInfo(data.outRT[i]);
driver.blit(TargetBufferFlags::COLOR, out.target, out.params.viewport,
in.target, in.params.viewport, filter);
}
}); });
return { bloomUpsamplePass->out, flare }; return { bloomUpsamplePass->out, flare };
@@ -3051,6 +3036,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
.filterMag = SamplerMagFilter::LINEAR, .filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
}); });
mi->setParameter("level", uint32_t(level));
mi->setParameter("layer", uint32_t(layer)); mi->setParameter("layer", uint32_t(layer));
mi->setParameter("uvscale", 1.0f / float(dim)); mi->setParameter("uvscale", 1.0f / float(dim));
mi->commit(driver); mi->commit(driver);
-5
View File
@@ -292,11 +292,6 @@ private:
FrameGraphId<FrameGraphTexture> input, backend::TextureFormat outFormat, FrameGraphId<FrameGraphTexture> input, backend::TextureFormat outFormat,
BloomOptions& inoutBloomOptions, math::float2 scale) noexcept; BloomOptions& inoutBloomOptions, math::float2 scale) noexcept;
FrameGraphId<FrameGraphTexture> downscalePass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphTexture::Descriptor const& outDesc,
bool threshold, float highlight, bool fireflies) noexcept;
void commitAndRender(FrameGraphResources::RenderPassInfo const& out, void commitAndRender(FrameGraphResources::RenderPassInfo const& out,
PostProcessMaterial const& material, uint8_t variant, PostProcessMaterial const& material, uint8_t variant,
backend::DriverApi& driver) const noexcept; backend::DriverApi& driver) const noexcept;
+79 -123
View File
@@ -110,7 +110,7 @@ math::mat4f ShadowMap::getPointLightViewMatrix(backend::TextureCubemapFace face,
} }
ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
FScene::LightSoa const& lightData, size_t index, const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera, filament::CameraInfo const& camera,
ShadowMapInfo const& shadowMapInfo, ShadowMapInfo const& shadowMapInfo,
SceneInfo const& sceneInfo) noexcept { SceneInfo const& sceneInfo) noexcept {
@@ -131,7 +131,20 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
else params.options.shadowFarHint = dzf * dz + camera.zf; else params.options.shadowFarHint = dzf * dz + camera.zf;
#endif #endif
const auto direction = params.options.transform * lightData.elementAt<FScene::DIRECTION>(index); // Adjust the camera's projection for the light's shadowFar
const mat4f cullingProjection{ [&](auto p) {
if (params.options.shadowFar > 0.0f) {
float const n = camera.zn;
float const f = params.options.shadowFar;
// orthographic projection
assert_invariant(std::abs(p[2].w) <= std::numeric_limits<float>::epsilon());
p[2].z = 2.0f / (n - f);
p[3].z = (f + n) / (n - f);
}
return p;
}(camera.cullingProjection) };
const auto direction = lightData.elementAt<FScene::DIRECTION>(index);
/* /*
* Compute the light's model matrix * Compute the light's model matrix
@@ -151,7 +164,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// view frustum vertices in world-space // view frustum vertices in world-space
float3 wsViewFrustumVertices[8]; float3 wsViewFrustumVertices[8];
const mat4f worldToClipMatrix = camera.cullingProjection * camera.view; const mat4f worldToClipMatrix = cullingProjection * camera.view;
const Frustum wsFrustum(worldToClipMatrix); const Frustum wsFrustum(worldToClipMatrix);
computeFrustumCorners(wsViewFrustumVertices, inverse(worldToClipMatrix), sceneInfo.csNearFar); computeFrustumCorners(wsViewFrustumVertices, inverse(worldToClipMatrix), sceneInfo.csNearFar);
@@ -203,7 +216,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
} }
// Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such // Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such
// that znear = 0, this is only needed for VSM, but doesn't hurt PCF. // that znear = 0, this is only need for VSM, but doesn't hurt PCF.
const mat4f Mv = getDirectionalLightViewMatrix(direction, direction * -lsLightFrustumBounds.max.z); const mat4f Mv = getDirectionalLightViewMatrix(direction, direction * -lsLightFrustumBounds.max.z);
// near / far planes are specified relative to the direction the eye is looking at // near / far planes are specified relative to the direction the eye is looking at
@@ -227,11 +240,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
const float4 shadowReceiverVolumeBoundingSphere = computeBoundingSphere( const float4 shadowReceiverVolumeBoundingSphere = computeBoundingSphere(
wsShadowReceiversVolume.getCorners().data(), 8); wsShadowReceiversVolume.getCorners().data(), 8);
// in stable mode we simply take the view volume bounding sphere, but we calculate it // in stable mode we simply take the view volume, bounding sphere
// in view space, so that it's perfectly stable. viewVolumeBoundingSphere = computeBoundingSphere(wsViewFrustumVertices, 8);
float3 vertices[8];
computeFrustumCorners(vertices, inverse(camera.cullingProjection), sceneInfo.csNearFar);
viewVolumeBoundingSphere = computeBoundingSphere(vertices, 8);
if (shadowReceiverVolumeBoundingSphere.w < viewVolumeBoundingSphere.w) { if (shadowReceiverVolumeBoundingSphere.w < viewVolumeBoundingSphere.w) {
@@ -310,43 +320,26 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// //
// In LiPSM mode, we're using the warped space here. // In LiPSM mode, we're using the warped space here.
float2 s, o; Aabb bounds;
if (params.options.stable) { if (params.options.stable && viewVolumeBoundingSphere.w > 0) {
if (viewVolumeBoundingSphere.w > 0) { bounds = compute2DBounds(Mv, viewVolumeBoundingSphere);
s = 1.0f / viewVolumeBoundingSphere.w;
o = mat4f::project(LMpMv * camera.model, viewVolumeBoundingSphere.xyz).xy;
} else { } else {
Aabb const bounds = compute2DBounds(LMpMv, bounds = compute2DBounds(WLMpMv, wsClippedShadowReceiverVolume.data(), vertexCount);
wsClippedShadowReceiverVolume.data(), vertexCount);
if (UTILS_UNLIKELY((bounds.min.x >= bounds.max.x) || (bounds.min.y >= bounds.max.y))) {
// this could happen if the only thing visible is a perfectly horizontal or
// vertical thin line
mHasVisibleShadows = false;
return {};
} }
assert_invariant(bounds.min.x < bounds.max.x);
assert_invariant(bounds.min.y < bounds.max.y);
s = 2.0f / float2(bounds.max.xy - bounds.min.xy);
o = float2(bounds.max.xy + bounds.min.xy) * 0.5f;
// Quantize the scale in world-space units. This value can be very small because
// if it wasn't for floating-point imprecision, the scale would be a constant.
double2 const quantizer = 0.0625;
s = 1.0 / (ceil(1.0 / (s * quantizer)) * quantizer);
}
} else {
Aabb const bounds = compute2DBounds(WLMpMv,
wsClippedShadowReceiverVolume.data(), vertexCount);
lsLightFrustumBounds.min.xy = bounds.min.xy; lsLightFrustumBounds.min.xy = bounds.min.xy;
lsLightFrustumBounds.max.xy = bounds.max.xy; lsLightFrustumBounds.max.xy = bounds.max.xy;
if (params.options.stable) {
// in stable mode we can't do anything that can change the scaling of the texture
} else {
// For directional lights, we further constraint the light frustum to the // For directional lights, we further constraint the light frustum to the
// intersection of the shadow casters & shadow receivers in light-space. // intersection of the shadow casters & shadow receivers in light-space.
// ** This relies on the 1-texel shadow map border ** // ** This relies on the 1-texel shadow map border **
if (engine.debug.shadowmap.focus_shadowcasters) { if (engine.debug.shadowmap.focus_shadowcasters) {
intersectWithShadowCasters(&lsLightFrustumBounds, WLMpMv, wsShadowCastersVolume); intersectWithShadowCasters(lsLightFrustumBounds, WLMpMv, wsShadowCastersVolume);
} }
}
if (UTILS_UNLIKELY((lsLightFrustumBounds.min.x >= lsLightFrustumBounds.max.x) || if (UTILS_UNLIKELY((lsLightFrustumBounds.min.x >= lsLightFrustumBounds.max.x) ||
(lsLightFrustumBounds.min.y >= lsLightFrustumBounds.max.y))) { (lsLightFrustumBounds.min.y >= lsLightFrustumBounds.max.y))) {
// this could happen if the only thing visible is a perfectly horizontal or // this could happen if the only thing visible is a perfectly horizontal or
@@ -354,22 +347,18 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
mHasVisibleShadows = false; mHasVisibleShadows = false;
return {}; return {};
} }
assert_invariant(lsLightFrustumBounds.min.x < lsLightFrustumBounds.max.x); assert_invariant(lsLightFrustumBounds.min.x < lsLightFrustumBounds.max.x);
assert_invariant(lsLightFrustumBounds.min.y < lsLightFrustumBounds.max.y); assert_invariant(lsLightFrustumBounds.min.y < lsLightFrustumBounds.max.y);
s = 2.0f / float2(lsLightFrustumBounds.max.xy - lsLightFrustumBounds.min.xy); // compute focus scale and offset
o = float2(lsLightFrustumBounds.max.xy + lsLightFrustumBounds.min.xy) * 0.5f; float2 s = 2.0f / float2(lsLightFrustumBounds.max.xy - lsLightFrustumBounds.min.xy);
float2 o = -s * float2(lsLightFrustumBounds.max.xy + lsLightFrustumBounds.min.xy) * 0.5f;
// TODO: we could quantize `s` here to give some stability when lispsm is disabled,
// however, the quantization paramater should probably be user settable.
}
// adjust offset for scale
o = -s * o;
if (params.options.stable) { if (params.options.stable) {
snapLightFrustum(s, o, LMpMv, // Use the world origin as reference point, fixed w.r.t. the camera
sceneInfo.wsShadowCastersVolume.center(), shadowMapInfo.shadowDimension); snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz,
1.0f / float(shadowMapInfo.shadowDimension));
} }
const mat4f F(mat4f::row_major_init { const mat4f F(mat4f::row_major_init {
@@ -388,7 +377,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// Computes St the transform to use in the shader to access the shadow map texture // Computes St the transform to use in the shader to access the shadow map texture
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap // i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, getViewport()); const backend::Viewport viewport = getViewport();
const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, viewport);
const mat4f St = math::highPrecisionMultiply(Mt, S); const mat4f St = math::highPrecisionMultiply(Mt, S);
ShadowMap::ShaderParameters shaderParameters; ShadowMap::ShaderParameters shaderParameters;
@@ -410,7 +400,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar); shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar);
} }
shaderParameters.scissorNormalized = getClampToEdgeCoords(shadowMapInfo); shaderParameters.scissorNormalized = getViewportNormalized(shadowMapInfo);
// We apply the constant bias in world space (as opposed to light-space) to account // We apply the constant bias in world space (as opposed to light-space) to account
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost // for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
@@ -443,8 +433,9 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual(
assert_invariant(shadowMapInfo.textureDimension == mOptions->mapSize); assert_invariant(shadowMapInfo.textureDimension == mOptions->mapSize);
// Final shadow transform // Final shadow transform
const backend::Viewport viewport = getViewport();
const mat4f S = math::highPrecisionMultiply(Mp, Mv); const mat4f S = math::highPrecisionMultiply(Mp, Mv);
const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, getViewport()); const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, viewport);
const mat4f St = math::highPrecisionMultiply(Mt, S); const mat4f St = math::highPrecisionMultiply(Mt, S);
// TODO: focus projection // TODO: focus projection
@@ -471,7 +462,7 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual(
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane); shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
} }
shaderParameters.scissorNormalized = getClampToEdgeCoords(shadowMapInfo); shaderParameters.scissorNormalized = getViewportNormalized(shadowMapInfo);
const float3 direction = -transpose(Mv)[2].xyz; const float3 direction = -transpose(Mv)[2].xyz;
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias; const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
@@ -768,12 +759,20 @@ Aabb ShadowMap::compute2DBounds(const mat4f& lightView,
return bounds; return bounds;
} }
void ShadowMap::intersectWithShadowCasters(Aabb* UTILS_RESTRICT lightFrustum, Aabb ShadowMap::compute2DBounds(const mat4f& lightView, float4 const& sphere) noexcept {
// this assumes a rigid body transform
float4 s;
s.xyz = (lightView * float4{sphere.xyz, 1.0f}).xyz;
s.w = sphere.w;
return Aabb{s.xyz - s.w, s.xyz + s.w};
}
void ShadowMap::intersectWithShadowCasters(Aabb& UTILS_RESTRICT lightFrustum,
mat4f const& lightView, Aabb const& wsShadowCastersVolume) noexcept { mat4f const& lightView, Aabb const& wsShadowCastersVolume) noexcept {
// construct the Focus transform (scale + offset) // construct the Focus transform (scale + offset)
const float2 s = 2.0f / float2(lightFrustum->max.xy - lightFrustum->min.xy); const float2 s = 2.0f / float2(lightFrustum.max.xy - lightFrustum.min.xy);
const float2 o = -s * float2(lightFrustum->max.xy + lightFrustum->min.xy) * 0.5f; const float2 o = -s * float2(lightFrustum.max.xy + lightFrustum.min.xy) * 0.5f;
const mat4f F(mat4f::row_major_init { const mat4f F(mat4f::row_major_init {
s.x, 0.0f, 0.0f, o.x, s.x, 0.0f, 0.0f, o.x,
0.0f, s.y, 0.0f, o.y, 0.0f, s.y, 0.0f, o.y,
@@ -799,8 +798,8 @@ void ShadowMap::intersectWithShadowCasters(Aabb* UTILS_RESTRICT lightFrustum,
Aabb const box = compute2DBounds(lightView, wsClippedShadowCasterVolumeVertices.data(), vertexCount); Aabb const box = compute2DBounds(lightView, wsClippedShadowCasterVolumeVertices.data(), vertexCount);
// intersect shadow-caster and current light frustum bounds // intersect shadow-caster and current light frustum bounds
lightFrustum->min.xy = max(box.min.xy, lightFrustum->min.xy); lightFrustum.min.xy = max(box.min.xy, lightFrustum.min.xy);
lightFrustum->max.xy = min(box.max.xy, lightFrustum->max.xy); lightFrustum.max.xy = min(box.max.xy, lightFrustum.max.xy);
} }
void ShadowMap::computeFrustumCorners(float3* UTILS_RESTRICT out, void ShadowMap::computeFrustumCorners(float3* UTILS_RESTRICT out,
@@ -826,39 +825,21 @@ void ShadowMap::computeFrustumCorners(float3* UTILS_RESTRICT out,
} }
void ShadowMap::snapLightFrustum(float2& s, float2& o, void ShadowMap::snapLightFrustum(float2& s, float2& o,
mat4f const& Mv, double3 wsSnapCoords, int2 resolution) noexcept { mat4f const& Mv, float3 worldOrigin, float2 shadowMapResolution) noexcept {
auto proj = [](mat4 m, double3 v) -> double3 { auto fmod = [](float2 x, float2 y) -> float2 {
// for directional light p.w == 1, exactly auto mod = [](float x, float y) -> float { return std::fmod(x, y); };
auto p = m * v; return float2{ mod(x[0], y[0]), mod(x[1], y[1]) };
assert_invariant(p.w == 1.0);
return p.xyz;
}; };
auto fract = [](auto v) { // This snaps the shadow map bounds to texels.
using namespace std; // The 2.0 comes from Mv having a NDC in the range -1,1 (so a range of 2).
using T = decltype(v); const float2 r = 2.0f * shadowMapResolution;
return fmod(v, T{1}); o -= fmod(o, r);
};
const mat4 F(mat4::row_major_init {
s.x, 0.0, 0.0, o.x,
0.0, s.y, 0.0, o.y,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
});
// The (resolution * 0.5) comes from Mv having a NDC in the range -1,1 (so a range of 2).
// focused light-space
mat4 const FMv{ F * Mv };
// This offsets the texture coordinates, so it has a fixed offset w.r.t the world // This offsets the texture coordinates, so it has a fixed offset w.r.t the world
double2 const lsOrigin = proj(FMv, wsSnapCoords).xy; const float2 lsOrigin = mat4f::project(Mv, worldOrigin).xy * s;
double2 const d = (fract(lsOrigin * resolution * 0.5) * 2.0) / resolution; o -= fmod(lsOrigin, r);
// adjust offset
o -= d;
} }
size_t ShadowMap::intersectFrustumWithBox( size_t ShadowMap::intersectFrustumWithBox(
@@ -1061,7 +1042,7 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p,
} }
float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture, float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture,
uint16_t shadowDimension) noexcept { uint16_t shadowDimension) const noexcept {
// The Jacobian of the transformation from texture-to-world is the matrix itself for // The Jacobian of the transformation from texture-to-world is the matrix itself for
// orthographic projections. We just need to inverse worldToShadowTexture, // orthographic projections. We just need to inverse worldToShadowTexture,
// which is guaranteed to be orthographic. // which is guaranteed to be orthographic.
@@ -1076,7 +1057,7 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture,
} }
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF, float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF,
uint16_t shadowDimension) noexcept { uint16_t shadowDimension) const noexcept {
// Here we compute the Jacobian of inverse(MbMtF * Wp). // Here we compute the Jacobian of inverse(MbMtF * Wp).
// The expression below has been computed with Mathematica. However, it's not very hard, // The expression below has been computed with Mathematica. However, it's not very hard,
// albeit error-prone, to do it by hand because MbMtF is a linear transform. // albeit error-prone, to do it by hand because MbMtF is a linear transform.
@@ -1097,7 +1078,7 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF,
constexpr bool JACOBIAN_ESTIMATE = false; constexpr bool JACOBIAN_ESTIMATE = false;
if constexpr (JACOBIAN_ESTIMATE) { if constexpr (JACOBIAN_ESTIMATE) {
// This estimates the Jacobian -- this is a lot heavier. This is mostly for reference // this estimates the Jacobian -- this is a lot heavier. This is mostly for reference
// and testing. // and testing.
const mat4f Si(inverse(MbMtF * Wp)); const mat4f Si(inverse(MbMtF * Wp));
const float3 p0 = mat4f::project(Si, p); const float3 p0 = mat4f::project(Si, p);
@@ -1236,58 +1217,33 @@ void ShadowMap::updateSceneInfoSpot(mat4f const& Mv, FScene const& scene,
} }
backend::Viewport ShadowMap::getViewport() const noexcept { backend::Viewport ShadowMap::getViewport() const noexcept {
// We set a viewport with a 1-texel border for when we index outside the texture. // We set a viewport with a 1-texel border for when we index outside the
// This happens only for directional lights when "focus shadow casters" is used, // texture. This can only happen for the directional light when "focus shadow casters is used".
// or when shadowFar is smaller than the camera far.
// For spot- and point-lights we also use a 1-texel border, so that bilinear filtering
// can work properly if the shadowmap is in an atlas (and we can't rely on h/w clamp).
const uint32_t dim = mOptions->mapSize; const uint32_t dim = mOptions->mapSize;
const uint16_t border = 1u; const uint16_t border = 1u;
return { border, border, dim - 2u * border, dim - 2u * border }; return { border, border, dim - 2u * border, dim - 2u * border };
} }
backend::Viewport ShadowMap::getScissor() const noexcept { backend::Viewport ShadowMap::getScissor() const noexcept {
// We set a viewport with a 1-texel border for when we index outside the texture. // We set a viewport with a 1-texel border for when we index outside the
// This happens only for directional lights when "focus shadow casters" is used, // texture. This can only happen for the directional light when "focus shadow casters is used".
// or when shadowFar is smaller than the camera far.
// For spot- and point-lights we also use a 1-texel border, so that bilinear filtering
// can work properly if the shadowmap is in an atlas (and we can't rely on h/w clamp), so we
// don't scissor the border, so it gets filled with correct neighboring texels.
const uint32_t dim = mOptions->mapSize; const uint32_t dim = mOptions->mapSize;
const uint16_t border = 1u; const uint16_t border = 1u;
switch (mShadowType) { switch (mShadowType) {
case ShadowType::DIRECTIONAL: case ShadowType::DIRECTIONAL:
return { border, border, dim - 2u * border, dim - 2u * border }; return { border, border, dim - 2u * border, dim - 2u * border };
case ShadowType::SPOT: case ShadowType::SPOT:
case ShadowType::POINT: case ShadowType::POINT:
default:
return { 0, 0, dim, dim }; return { 0, 0, dim, dim };
} }
} }
math::float4 ShadowMap::getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept { math::float4 ShadowMap::getViewportNormalized(ShadowMapInfo const& shadowMapInfo) const noexcept {
float border; // shadowmap border in texels const auto [l, b, w, h] = getViewport();
switch (mShadowType) { const float texel = 1.0f / float(shadowMapInfo.atlasDimension);
case ShadowType::DIRECTIONAL: const float4 v = float4{ l, b, l + w, b + h } * texel;
// For directional lights, we need to allow the sampling to reach the border, it
// happens when "focus shadow casters" is used for instance.
border = 0.5f;
break;
case ShadowType::SPOT:
case ShadowType::POINT:
// For spot and point light, this is equal to the viewport. i.e. the valid
// texels are inside the viewport (w/ 1-texel border), the border will be used
// for bilinear filtering.
border = 1.0f;
break;
}
float const texel = 1.0f / float(shadowMapInfo.atlasDimension);
float const dim = float(mOptions->mapSize);
float const l = border;
float const b = border;
float const w = dim - 2.0f * border;
float const h = dim - 2.0f * border;
float4 const v = float4{ l, b, l + w, b + h } * texel;
if (shadowMapInfo.textureSpaceFlipped) { if (shadowMapInfo.textureSpaceFlipped) {
// this is equivalent to calling uvToRenderTargetUV() in the shader *after* clamping // this is equivalent to calling uvToRenderTargetUV() in the shader *after* clamping
// texture coordinates to this normalized viewport. // texture coordinates to this normalized viewport.
+10 -8
View File
@@ -222,7 +222,7 @@ private:
const math::float3& dir); const math::float3& dir);
static inline void snapLightFrustum(math::float2& s, math::float2& o, static inline void snapLightFrustum(math::float2& s, math::float2& o,
math::mat4f const& Mv, math::double3 wsSnapCoords, math::int2 resolution) noexcept; math::mat4f const& Mv, math::float3 worldOrigin, math::float2 shadowMapResolution) noexcept;
static inline void computeFrustumCorners(math::float3* out, static inline void computeFrustumCorners(math::float3* out,
const math::mat4f& projectionViewInverse, math::float2 csNearFar = { -1.0f, 1.0f }) noexcept; const math::mat4f& projectionViewInverse, math::float2 csNearFar = { -1.0f, 1.0f }) noexcept;
@@ -240,8 +240,10 @@ private:
static inline Aabb compute2DBounds(const math::mat4f& lightView, static inline Aabb compute2DBounds(const math::mat4f& lightView,
math::float3 const* wsVertices, size_t count) noexcept; math::float3 const* wsVertices, size_t count) noexcept;
static inline void intersectWithShadowCasters(Aabb* lightFrustum, static inline Aabb compute2DBounds(const math::mat4f& lightView,
math::mat4f const& lightView, math::float4 const& sphere) noexcept;
static inline void intersectWithShadowCasters(Aabb& lightFrustum, const math::mat4f& lightView,
Aabb const& wsShadowCastersVolume) noexcept; Aabb const& wsShadowCastersVolume) noexcept;
static inline math::float2 computeNearFarOfWarpSpace(math::mat4f const& lightView, static inline math::float2 computeNearFarOfWarpSpace(math::mat4f const& lightView,
@@ -279,13 +281,13 @@ private:
static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf, static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf,
const math::mat4f& Mv, float znear, float zfar) noexcept; const math::mat4f& Mv, float znear, float zfar) noexcept;
math::float4 getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept; math::float4 getViewportNormalized(ShadowMapInfo const& shadowMapInfo) const noexcept;
static float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture, float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
uint16_t shadowDimension) noexcept; uint16_t shadowDimension) const noexcept;
static float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF, float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
uint16_t shadowDimension) noexcept; uint16_t shadowDimension) const noexcept;
static constexpr const Segment sBoxSegments[12] = { static constexpr const Segment sBoxSegments[12] = {
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 }, { 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
+11 -24
View File
@@ -435,9 +435,8 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
} }
ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine, ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine,
FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData, FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept { FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept {
FScene* scene = view.getScene(); FScene* scene = view.getScene();
auto& lcm = engine.getLightManager(); auto& lcm = engine.getLightManager();
@@ -445,24 +444,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
FLightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight); FLightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight);
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight); FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
// Adjust the camera's projection for the light's shadowFar
cameraInfo.zf = params.options.shadowFar > 0.0f ? params.options.shadowFar : cameraInfo.zf;
if (UTILS_UNLIKELY(params.options.shadowFar > 0.0f)) {
cameraInfo.zf = params.options.shadowFar;
float const n = cameraInfo.zn;
float const f = cameraInfo.zf;
if (std::abs(cameraInfo.cullingProjection[2].w) > std::numeric_limits<float>::epsilon()) {
// perspective projection
cameraInfo.cullingProjection[2].z = (f + n) / (n - f);
cameraInfo.cullingProjection[3].z = (2 * f * n) / (n - f);
} else {
// orthographic projection
cameraInfo.cullingProjection[2].z = 2.0f / (n - f);
cameraInfo.cullingProjection[3].z = (f + n) / (n - f);
}
}
const ShadowMap::ShadowMapInfo shadowMapInfo{ const ShadowMap::ShadowMapInfo shadowMapInfo{
.atlasDimension = mTextureAtlasRequirements.size, .atlasDimension = mTextureAtlasRequirements.size,
.textureDimension = uint16_t(options.mapSize), .textureDimension = uint16_t(options.mapSize),
@@ -478,7 +459,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// entire camera frustum, as if we only had a single cascade. // entire camera frustum, as if we only had a single cascade.
ShadowMap& shadowMap = *mCascadeShadowMaps[0]; ShadowMap& shadowMap = *mCascadeShadowMaps[0];
const auto direction = options.transform * lightData.elementAt<FScene::DIRECTION>(0); const auto direction = lightData.elementAt<FScene::DIRECTION>(0);
// We compute the directional light's model matrix using the origin's as the light position. // We compute the directional light's model matrix using the origin's as the light position.
// The choice of the light's origin initially doesn't matter for a directional light. // The choice of the light's origin initially doesn't matter for a directional light.
@@ -523,13 +504,19 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
splitPercentages[i] = options.cascadeSplitPositions[i - 1]; splitPercentages[i] = options.cascadeSplitPositions[i - 1];
} }
const CascadeSplits splits({ const CascadeSplits::Params p{
.proj = cameraInfo.cullingProjection, .proj = cameraInfo.cullingProjection,
.near = vsNear, .near = vsNear,
.far = vsFar, .far = vsFar,
.cascadeCount = cascadeCount, .cascadeCount = cascadeCount,
.splitPositions = splitPercentages .splitPositions = splitPercentages
}); };
if (p != mCascadeSplitParams) {
mCascadeSplits = CascadeSplits{ p };
mCascadeSplitParams = p;
}
const CascadeSplits& splits = mCascadeSplits;
// The split positions uniform is a float4. To save space, we chop off the first split position // The split positions uniform is a float4. To save space, we chop off the first split position
// (which is the near plane, and doesn't need to be communicated to the shaders). // (which is the near plane, and doesn't need to be communicated to the shaders).
@@ -543,7 +530,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform; mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
// When computing the required bias we need a half-texel size, so we multiply by 0.5 here. // when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
// note: normalBias is set to zero for VSM // note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0); const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
+15 -3
View File
@@ -109,7 +109,7 @@ public:
private: private:
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine, ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData, FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept; FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept;
ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine, ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine,
@@ -145,8 +145,17 @@ private:
float far = 0.0f; float far = 0.0f;
size_t cascadeCount = 1; size_t cascadeCount = 1;
std::array<float, SPLIT_COUNT> splitPositions = { 0.0f }; std::array<float, SPLIT_COUNT> splitPositions = { 0.0f };
bool operator!=(const Params& rhs) const {
return proj != rhs.proj ||
near != rhs.near ||
far != rhs.far ||
cascadeCount != rhs.cascadeCount ||
splitPositions != rhs.splitPositions;
}
}; };
CascadeSplits() noexcept : CascadeSplits(Params{}) {}
explicit CascadeSplits(Params const& params) noexcept; explicit CascadeSplits(Params const& params) noexcept;
// Split positions in world-space. // Split positions in world-space.
@@ -177,6 +186,9 @@ private:
SoftShadowOptions mSoftShadowOptions; SoftShadowOptions mSoftShadowOptions;
CascadeSplits::Params mCascadeSplitParams;
CascadeSplits mCascadeSplits;
mutable TypedUniformBuffer<ShadowUib> mShadowUb; mutable TypedUniformBuffer<ShadowUib> mShadowUb;
backend::Handle<backend::HwBufferObject> mShadowUbh; backend::Handle<backend::HwBufferObject> mShadowUbh;
@@ -192,8 +204,8 @@ private:
utils::FixedCapacityVector<ShadowMap*>::with_capacity( utils::FixedCapacityVector<ShadowMap*>::with_capacity(
CONFIG_MAX_SHADOWMAPS - CONFIG_MAX_SHADOW_CASCADES) }; CONFIG_MAX_SHADOWMAPS - CONFIG_MAX_SHADOW_CASCADES) };
// Inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly. // inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly.
// Because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations. // because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations.
// Each ShadowMap is currently 40 bytes (total of 2.5KB for 64 shadow maps) // Each ShadowMap is currently 40 bytes (total of 2.5KB for 64 shadow maps)
using ShadowMapStorage = std::aligned_storage<sizeof(ShadowMap), alignof(ShadowMap)>::type; using ShadowMapStorage = std::aligned_storage<sizeof(ShadowMap), alignof(ShadowMap)>::type;
std::array<ShadowMapStorage, CONFIG_MAX_SHADOWMAPS> mShadowMapCache; std::array<ShadowMapStorage, CONFIG_MAX_SHADOWMAPS> mShadowMapCache;
+2 -2
View File
@@ -127,12 +127,12 @@ public:
} }
bool isSpotLight(Instance i) const noexcept { bool isSpotLight(Instance i) const noexcept {
Type const type = getType(i); Type type = getType(i);
return type == Type::FOCUSED_SPOT || type == Type::SPOT; return type == Type::FOCUSED_SPOT || type == Type::SPOT;
} }
bool isDirectionalLight(Instance i) const noexcept { bool isDirectionalLight(Instance i) const noexcept {
Type const type = getType(i); Type type = getType(i);
return type == Type::DIRECTIONAL || type == Type::SUN; return type == Type::DIRECTIONAL || type == Type::SUN;
} }
+2 -3
View File
@@ -1080,9 +1080,8 @@ void FView::setSoftShadowOptions(SoftShadowOptions options) noexcept {
void FView::setBloomOptions(BloomOptions options) noexcept { void FView::setBloomOptions(BloomOptions options) noexcept {
options.dirtStrength = math::saturate(options.dirtStrength); options.dirtStrength = math::saturate(options.dirtStrength);
options.resolution = math::clamp(options.resolution, 2u, 2048u); options.levels = math::clamp(options.levels, uint8_t(1), uint8_t(11));
options.levels = math::clamp(options.levels, uint8_t(1), options.resolution = math::clamp(options.resolution, 1u << options.levels, 2048u);
FTexture::maxLevelCount(options.resolution));
options.anamorphism = math::clamp(options.anamorphism, 1.0f/32.0f, 32.0f); options.anamorphism = math::clamp(options.anamorphism, 1.0f/32.0f, 32.0f);
options.highlight = std::max(10.0f, options.highlight); options.highlight = std::max(10.0f, options.highlight);
mBloomOptions = options; mBloomOptions = options;
@@ -5,6 +5,18 @@ material {
type : sampler2d, type : sampler2d,
name : source, name : source,
precision: medium precision: medium
},
{
type : float,
name : level
},
{
type : float,
name : threshold
},
{
type : float,
name : invHighlight
} }
], ],
variables : [ variables : [
@@ -25,43 +37,85 @@ fragment {
void dummy(){} void dummy(){}
void threshold(inout vec3 c) {
// threshold everything below 1.0
c = max(vec3(0.0), c - 1.0);
// crush everything above 1
highp float f = max3(c);
c *= 1.0 / (1.0 + f * materialParams.invHighlight);
}
vec3 box4x4(vec3 s0, vec3 s1, vec3 s2, vec3 s3) { vec3 box4x4(vec3 s0, vec3 s1, vec3 s2, vec3 s3) {
return (s0 + s1 + s2 + s3) * 0.25; return (s0 + s1 + s2 + s3) * 0.25;
} }
vec3 box4x4Reinhard(vec3 s0, vec3 s1, vec3 s2, vec3 s3) {
float w0 = 1.0 / (1.0 + max3(s0));
float w1 = 1.0 / (1.0 + max3(s1));
float w2 = 1.0 / (1.0 + max3(s2));
float w3 = 1.0 / (1.0 + max3(s3));
return (s0 * w0 + s1 * w1 + s2 * w2 + s3 * w3) * (1.0 / (w0 + w1 + w2 + w3));
}
void postProcess(inout PostProcessInputs postProcess) { void postProcess(inout PostProcessInputs postProcess) {
float lod = materialParams.level;
highp vec2 uv = variable_vertex.xy; highp vec2 uv = variable_vertex.xy;
// see SIGGRAPH 2014: Advances in Real-Time Rendering // see SIGGRAPH 2014: Advances in Real-Time Rendering
// "Next Generation Post-Processing in Call of Duty Advanced Warfare" // "Next Generation Post-Processing in Call of Duty Advanced Warfare"
// Jorge Jimenez // Jorge Jimenez
vec3 c = textureLod(materialParams_source, uv, 0.0).rgb; vec3 c = textureLod(materialParams_source, uv, lod).rgb;
// The offsets below are in "source" texture space // The offsets below are in "source" texture space
vec3 lt = textureLodOffset(materialParams_source, uv, 0.0, ivec2(-1, -1)).rgb; vec3 lt = textureLodOffset(materialParams_source, uv, lod, ivec2(-1, -1)).rgb;
vec3 rt = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 1, -1)).rgb; vec3 rt = textureLodOffset(materialParams_source, uv, lod, ivec2( 1, -1)).rgb;
vec3 rb = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 1, 1)).rgb; vec3 rb = textureLodOffset(materialParams_source, uv, lod, ivec2( 1, 1)).rgb;
vec3 lb = textureLodOffset(materialParams_source, uv, 0.0, ivec2(-1, 1)).rgb; vec3 lb = textureLodOffset(materialParams_source, uv, lod, ivec2(-1, 1)).rgb;
vec3 lt2 = textureLodOffset(materialParams_source, uv, 0.0, ivec2(-2, -2)).rgb; vec3 lt2 = textureLodOffset(materialParams_source, uv, lod, ivec2(-2, -2)).rgb;
vec3 rt2 = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 2, -2)).rgb; vec3 rt2 = textureLodOffset(materialParams_source, uv, lod, ivec2( 2, -2)).rgb;
vec3 rb2 = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 2, 2)).rgb; vec3 rb2 = textureLodOffset(materialParams_source, uv, lod, ivec2( 2, 2)).rgb;
vec3 lb2 = textureLodOffset(materialParams_source, uv, 0.0, ivec2(-2, 2)).rgb; vec3 lb2 = textureLodOffset(materialParams_source, uv, lod, ivec2(-2, 2)).rgb;
vec3 l = textureLodOffset(materialParams_source, uv, 0.0, ivec2(-2, 0)).rgb; vec3 l = textureLodOffset(materialParams_source, uv, lod, ivec2(-2, 0)).rgb;
vec3 t = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 0, -2)).rgb; vec3 t = textureLodOffset(materialParams_source, uv, lod, ivec2( 0, -2)).rgb;
vec3 r = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 2, 0)).rgb; vec3 r = textureLodOffset(materialParams_source, uv, lod, ivec2( 2, 0)).rgb;
vec3 b = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 0, 2)).rgb; vec3 b = textureLodOffset(materialParams_source, uv, lod, ivec2( 0, 2)).rgb;
// five h4x4 boxes // five h4x4 boxes
vec3 c0, c1; vec3 c0, c1;
if (materialParams.level <= 0.5) {
if (materialParams.threshold > 0.0) {
// Threshold the first level blur
threshold(c);
threshold(lt);
threshold(rt);
threshold(rb);
threshold(lb);
threshold(lt2);
threshold(rt2);
threshold(rb2);
threshold(lb2);
threshold(l);
threshold(t);
threshold(r);
threshold(b);
}
// Also apply fireflies (flickering) filtering
c0 = box4x4Reinhard(lt, rt, rb, lb);
c1 = box4x4Reinhard(c, l, t, lt2);
c1 += box4x4Reinhard(c, r, t, rt2);
c1 += box4x4Reinhard(c, r, b, rb2);
c1 += box4x4Reinhard(c, l, b, lb2);
} else {
// common case // common case
c0 = box4x4(lt, rt, rb, lb); c0 = box4x4(lt, rt, rb, lb);
c1 = box4x4(c, l, t, lt2); c1 = box4x4(c, l, t, lt2);
c1 += box4x4(c, r, t, rt2); c1 += box4x4(c, r, t, rt2);
c1 += box4x4(c, r, b, rb2); c1 += box4x4(c, r, b, rb2);
c1 += box4x4(c, l, b, lb2); c1 += box4x4(c, l, b, lb2);
}
// weighted average of the five boxes // weighted average of the five boxes
postProcess.color.rgb = c0 * 0.5 + c1 * 0.125; postProcess.color.rgb = c0 * 0.5 + c1 * 0.125;
@@ -1,98 +0,0 @@
material {
name : bloomDownsample2x,
parameters : [
{
type : sampler2d,
name : source,
precision: medium
},
{
type : float,
name : level
},
{
type : float,
name : threshold
},
{
type : float,
name : fireflies
},
{
type : float,
name : invHighlight
}
],
variables : [
vertex
],
domain : postprocess,
depthWrite : false,
depthCulling : false
}
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.vertex.xy = uvToRenderTargetUV(postProcess.normalizedUV);
}
}
fragment {
void dummy(){}
void threshold(inout vec3 c) {
// threshold everything below 1.0
c = max(vec3(0.0), c - 1.0);
// crush everything above 1
highp float f = max3(c);
c *= 1.0 / (1.0 + f * materialParams.invHighlight);
}
void postProcess(inout PostProcessInputs postProcess) {
float lod = materialParams.level;
highp vec2 size = vec2(textureSize(materialParams_source, int(lod)));
highp vec2 texelSize = vec2(1.0) / size;
// Castaño, 2013, "Shadow Mapping Summary Part 1"
// 3x3 gaussian filter with 4 linear samples
vec2 offset = vec2(0.5);
highp vec2 uv = (variable_vertex.xy * size) + offset;
highp vec2 base = (floor(uv) - offset) * texelSize;
highp vec2 st = fract(uv);
vec2 uw = vec2(3.0 - 2.0 * st.x, 1.0 + 2.0 * st.x);
vec2 vw = vec2(3.0 - 2.0 * st.y, 1.0 + 2.0 * st.y);
highp vec2 u = vec2((2.0 - st.x) / uw.x - 1.0, st.x / uw.y + 1.0) * texelSize.x;
highp vec2 v = vec2((2.0 - st.y) / vw.x - 1.0, st.y / vw.y + 1.0) * texelSize.y;
vec3 c0 = textureLod(materialParams_source, base + vec2(u.x, v.x), lod).rgb;
vec3 c1 = textureLod(materialParams_source, base + vec2(u.y, v.x), lod).rgb;
vec3 c2 = textureLod(materialParams_source, base + vec2(u.x, v.y), lod).rgb;
vec3 c3 = textureLod(materialParams_source, base + vec2(u.y, v.y), lod).rgb;
float w0 = uw.x * vw.x * (1.0 / 16.0);
float w1 = uw.y * vw.x * (1.0 / 16.0);
float w2 = uw.x * vw.y * (1.0 / 16.0);
float w3 = uw.y * vw.y * (1.0 / 16.0);
if (materialParams.fireflies > 0.0) {
w0 /= (1.0 + max3(c0));
w1 /= (1.0 + max3(c1));
w2 /= (1.0 + max3(c2));
w3 /= (1.0 + max3(c3));
float w = 1.0 / (w0 + w1 + w2 + w3);
w0 *= w;
w1 *= w;
w2 *= w;
w3 *= w;
}
vec3 c = c0 * w0 + c1 * w1 + c2 * w2 + c3 * w3;
if (materialParams.threshold > 0.0) {
threshold(c);
}
postProcess.color.rgb = c;
}
}
@@ -1,65 +0,0 @@
material {
name : bloomDownsample9,
parameters : [
{
type : sampler2d,
name : source,
precision: medium
}
],
variables : [
vertex
],
domain : postprocess,
depthWrite : false,
depthCulling : false
}
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.vertex.xy = uvToRenderTargetUV(postProcess.normalizedUV);
}
}
fragment {
void dummy(){}
// see https://www.shadertoy.com/view/cslczj
// 6x6 downsampling kernel implemented via 9 bilinear samples
void postProcess(inout PostProcessInputs postProcess) {
highp vec2 uv = variable_vertex.xy;
highp vec2 size = vec2(1.0) / vec2(textureSize(materialParams_source, 0));
float o = 1.5 + 0.261629;
float wa = 7.46602 / 32.0;
float wb = 1.0 - wa * 2.0;
float wab = wa * wb;
float waa = wa * wa;
float wbb = wb * wb;
size *= o;
vec3 c = textureLod(materialParams_source, uv + vec2(0.0) , 0.0).rgb;
vec3 l = textureLod(materialParams_source, uv + vec2(-size.x, 0.0), 0.0).rgb;
vec3 r = textureLod(materialParams_source, uv + vec2( size.x, 0.0), 0.0).rgb;
vec3 b = textureLod(materialParams_source, uv + vec2( 0.0,-size.y), 0.0).rgb;
vec3 t = textureLod(materialParams_source, uv + vec2( 0.0, size.y), 0.0).rgb;
vec3 lb = textureLod(materialParams_source, uv + vec2(-size.x,-size.y), 0.0).rgb;
vec3 rb = textureLod(materialParams_source, uv + vec2( size.x,-size.y), 0.0).rgb;
vec3 lt = textureLod(materialParams_source, uv + vec2(-size.x, size.y), 0.0).rgb;
vec3 rt = textureLod(materialParams_source, uv + vec2( size.x, size.y), 0.0).rgb;
postProcess.color.rgb =
(c * wbb +
(l * wab +
(r * wab +
(b * wab +
(t * wab +
(lb * waa +
(rb * waa +
(lt * waa +
(rt * waa)))))))));
}
}
+18 -13
View File
@@ -10,6 +10,10 @@ material {
type : float4, type : float4,
name : resolution, name : resolution,
precision: high precision: high
},
{
type : float,
name : level
} }
], ],
variables : [ variables : [
@@ -30,15 +34,16 @@ fragment {
void dummy(){} void dummy(){}
void postProcess(inout PostProcessInputs postProcess) { void postProcess(inout PostProcessInputs postProcess) {
float lod = materialParams.level;
highp vec2 uv = variable_vertex.xy; highp vec2 uv = variable_vertex.xy;
#if FILAMENT_QUALITY < FILAMENT_QUALITY_HIGH #if FILAMENT_QUALITY < FILAMENT_QUALITY_HIGH
highp vec4 d = vec4(materialParams.resolution.zw, -materialParams.resolution.zw) * 0.5; highp vec4 d = vec4(materialParams.resolution.zw, -materialParams.resolution.zw) * 0.5;
vec3 c; vec3 c;
c = textureLod(materialParams_source, uv + d.zw, 0.0).rgb; c = textureLod(materialParams_source, uv + d.zw, lod).rgb;
c += textureLod(materialParams_source, uv + d.xw, 0.0).rgb; c += textureLod(materialParams_source, uv + d.xw, lod).rgb;
c += textureLod(materialParams_source, uv + d.xy, 0.0).rgb; c += textureLod(materialParams_source, uv + d.xy, lod).rgb;
c += textureLod(materialParams_source, uv + d.zy, 0.0).rgb; c += textureLod(materialParams_source, uv + d.zy, lod).rgb;
postProcess.color.rgb = c * 0.25; postProcess.color.rgb = c * 0.25;
#else #else
// see SIGGRAPH 2014: Advances in Real-Time Rendering // see SIGGRAPH 2014: Advances in Real-Time Rendering
@@ -47,15 +52,15 @@ fragment {
const float radius = 1.0; const float radius = 1.0;
highp vec4 d = vec4(materialParams.resolution.zw, -materialParams.resolution.zw) * radius; highp vec4 d = vec4(materialParams.resolution.zw, -materialParams.resolution.zw) * radius;
vec3 c0, c1; vec3 c0, c1;
c0 = textureLod(materialParams_source, uv + d.zw, 0.0).rgb; c0 = textureLod(materialParams_source, uv + d.zw, lod).rgb;
c0 += textureLod(materialParams_source, uv + d.xw, 0.0).rgb; c0 += textureLod(materialParams_source, uv + d.xw, lod).rgb;
c0 += textureLod(materialParams_source, uv + d.xy, 0.0).rgb; c0 += textureLod(materialParams_source, uv + d.xy, lod).rgb;
c0 += textureLod(materialParams_source, uv + d.zy, 0.0).rgb; c0 += textureLod(materialParams_source, uv + d.zy, lod).rgb;
c0 += 4.0 * textureLod(materialParams_source, uv, 0.0).rgb; c0 += 4.0 * textureLod(materialParams_source, uv, lod).rgb;
c1 = textureLod(materialParams_source, uv + vec2(d.z, 0.0), 0.0).rgb; c1 = textureLod(materialParams_source, uv + vec2(d.z, 0.0), lod).rgb;
c1 += textureLod(materialParams_source, uv + vec2(0.0, d.w), 0.0).rgb; c1 += textureLod(materialParams_source, uv + vec2(0.0, d.w), lod).rgb;
c1 += textureLod(materialParams_source, uv + vec2(d.x, 0.0), 0.0).rgb; c1 += textureLod(materialParams_source, uv + vec2(d.x, 0.0), lod).rgb;
c1 += textureLod(materialParams_source, uv + vec2( 0.0, d.y), 0.0).rgb; c1 += textureLod(materialParams_source, uv + vec2( 0.0, d.y), lod).rgb;
postProcess.color.rgb = (c0 + 2.0 * c1) * (1.0 / 16.0); postProcess.color.rgb = (c0 + 2.0 * c1) * (1.0 / 16.0);
#endif #endif
} }
+13 -8
View File
@@ -11,6 +11,10 @@ material {
name : coc, name : coc,
precision: medium precision: medium
}, },
{
type : int,
name : mip
},
{ {
type : float, type : float,
name : weightScale name : weightScale
@@ -61,18 +65,19 @@ void postProcess(inout PostProcessInputs postProcess) {
// the bilateral weights need to be scaled by to match the lower resolution // the bilateral weights need to be scaled by to match the lower resolution
float weightScale = materialParams.weightScale; float weightScale = materialParams.weightScale;
float mip = float(materialParams.mip);
vec4 s01 = textureLodOffset(materialParams_color, uv, 0.0, ivec2(0, 1)); vec4 s01 = textureLodOffset(materialParams_color, uv, mip, ivec2(0, 1));
vec4 s11 = textureLodOffset(materialParams_color, uv, 0.0, ivec2(1, 1)); vec4 s11 = textureLodOffset(materialParams_color, uv, mip, ivec2(1, 1));
vec4 s10 = textureLodOffset(materialParams_color, uv, 0.0, ivec2(1, 0)); vec4 s10 = textureLodOffset(materialParams_color, uv, mip, ivec2(1, 0));
vec4 s00 = textureLodOffset(materialParams_color, uv, 0.0, ivec2(0, 0)); vec4 s00 = textureLodOffset(materialParams_color, uv, mip, ivec2(0, 0));
// fetch the 4 corresponding CoC (textureGather with LOD doesn't exist) // fetch the 4 corresponding CoC (textureGather with LOD doesn't exist)
vec4 c; vec4 c;
c[0] = textureLodOffset(materialParams_coc, uv, 0.0, ivec2(0, 1)).r; c[0] = textureLodOffset(materialParams_coc, uv, mip, ivec2(0, 1)).r;
c[1] = textureLodOffset(materialParams_coc, uv, 0.0, ivec2(1, 1)).r; c[1] = textureLodOffset(materialParams_coc, uv, mip, ivec2(1, 1)).r;
c[2] = textureLodOffset(materialParams_coc, uv, 0.0, ivec2(1, 0)).r; c[2] = textureLodOffset(materialParams_coc, uv, mip, ivec2(1, 0)).r;
c[3] = textureLodOffset(materialParams_coc, uv, 0.0, ivec2(0, 0)).r; c[3] = textureLodOffset(materialParams_coc, uv, mip, ivec2(0, 0)).r;
float outCoc = downsampleCoC(c); float outCoc = downsampleCoC(c);
vec4 w = downsampleCocWeights(c, outCoc, weightScale); vec4 w = downsampleCocWeights(c, outCoc, weightScale);
+6 -1
View File
@@ -5,6 +5,10 @@ material {
type : sampler2d, type : sampler2d,
name : depth, name : depth,
precision: high precision: high
},
{
type : int,
name : level
} }
], ],
variables : [ variables : [
@@ -26,8 +30,9 @@ fragment {
// We use a rotated grid sub-sample as it's cheap and gives good results // We use a rotated grid sub-sample as it's cheap and gives good results
// See Scalable Ambient Obscurance by McGuire and al. // See Scalable Ambient Obscurance by McGuire and al.
void postProcess(inout PostProcessInputs postProcess) { void postProcess(inout PostProcessInputs postProcess) {
int level = materialParams.level;
ivec2 icoord = ivec2(gl_FragCoord.xy); ivec2 icoord = ivec2(gl_FragCoord.xy);
postProcess.depth = texelFetch(materialParams_depth, postProcess.depth = texelFetch(materialParams_depth,
2 * icoord + ivec2(icoord.y & 1, icoord.x & 1), 0).r; 2 * icoord + ivec2(icoord.y & 1, icoord.x & 1), level).r;
} }
} }
+6 -1
View File
@@ -10,6 +10,10 @@ material {
type : int, type : int,
name : layer name : layer
}, },
{
type : int,
name : level
},
{ {
type : float, type : float,
name : uvscale name : uvscale
@@ -32,6 +36,7 @@ fragment {
void postProcess(inout PostProcessInputs postProcess) { void postProcess(inout PostProcessInputs postProcess) {
highp vec2 uv = gl_FragCoord.xy * materialParams.uvscale; highp vec2 uv = gl_FragCoord.xy * materialParams.uvscale;
postProcess.color = textureLod(materialParams_color, postProcess.color = textureLod(materialParams_color,
vec3(uv, materialParams.layer), 0.0); vec3(uv, materialParams.layer),
float(materialParams.level));
} }
} }
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec| Pod::Spec.new do |spec|
spec.name = "Filament" spec.name = "Filament"
spec.version = "1.43.1" spec.version = "1.42.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament" spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC." spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0" spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.43.1/filament-v1.43.1-ios.tgz" } spec.source = { :http => "https://github.com/google/filament/releases/download/v1.42.0/filament-v1.42.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = { spec.pod_target_xcconfig = {
@@ -17,8 +17,6 @@
#ifndef TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H #ifndef TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H
#define TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H #define TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H
#include <functional>
#include <math/vec2.h> #include <math/vec2.h>
#include <math/vec3.h> #include <math/vec3.h>
#include <math/vec4.h> #include <math/vec4.h>
@@ -17,8 +17,6 @@
#ifndef TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H #ifndef TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H
#define TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H #define TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H
#include <functional>
#include <math/vec2.h> #include <math/vec2.h>
#include <math/vec3.h> #include <math/vec3.h>
#include <math/vec4.h> #include <math/vec4.h>
@@ -28,7 +28,7 @@
namespace filament { namespace filament {
// update this when a new version of filament wouldn't work with older materials // update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 43; static constexpr size_t MATERIAL_VERSION = 42;
/** /**
* Supported shading models * Supported shading models
+6 -6
View File
@@ -35,7 +35,7 @@ for example, `filament-20181009-linux.tgz`.
Create a file, `main.cpp`, in the same directory with the following contents: Create a file, `main.cpp`, in the same directory with the following contents:
```c++ ```
#include <filamat/MaterialBuilder.h> #include <filamat/MaterialBuilder.h>
#include <iostream> #include <iostream>
@@ -85,7 +85,7 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory.
### Linux ### Linux
```make ```
FILAMENT_LIBS=-lfilamat -lfilabridge -lshaders -lutils -lsmol-v FILAMENT_LIBS=-lfilamat -lfilabridge -lshaders -lutils -lsmol-v
CC=clang++ CC=clang++
@@ -103,7 +103,7 @@ clean:
### macOS ### macOS
```make ```
FILAMENT_LIBS=-lfilamat -lfilabridge -lshaders -lutils -lsmol-v FILAMENT_LIBS=-lfilamat -lfilabridge -lshaders -lutils -lsmol-v
CC=clang++ CC=clang++
@@ -129,7 +129,7 @@ flags](https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-tim
When building Filamat from source, the `USE_STATIC_CRT` CMake option can be When building Filamat from source, the `USE_STATIC_CRT` CMake option can be
used to change the run-time library version. used to change the run-time library version.
```make ```
FILAMENT_LIBS=lib/x86_64/mt/filamat.lib lib/x86_64/mt/filabridge.lib lib/x86_64/mt/shaders.lib \ FILAMENT_LIBS=lib/x86_64/mt/filamat.lib lib/x86_64/mt/filabridge.lib lib/x86_64/mt/shaders.lib \
lib/x86_64/mt/utils.lib lib/x86_64/mt/smol-v.lib lib/x86_64/mt/utils.lib lib/x86_64/mt/smol-v.lib
CC=clang-cl.exe CC=clang-cl.exe
@@ -164,7 +164,7 @@ and invoke `nmake` instead of `make`.
For simplicity, this demo doesn't do anything useful with the built material package. To use the For simplicity, this demo doesn't do anything useful with the built material package. To use the
material with Filament, pass the material package's data into a Filament Material builder: material with Filament, pass the material package's data into a Filament Material builder:
```c++ ```
Package package = builder.build(); Package package = builder.build();
filament::Material* myMaterial = Material::Builder() filament::Material* myMaterial = Material::Builder()
.package(package.getData(), package.getSize()) .package(package.getData(), package.getSize())
@@ -186,7 +186,7 @@ In addition, `filamat_lite` only performs a simple text match to determine which
`MaterialInputs` structure are set. The `material` input variable must also always be refered to by `MaterialInputs` structure are set. The `material` input variable must also always be refered to by
the name `material`. the name `material`.
```glsl ```
void anotherFunction(inout MaterialInputs m) { void anotherFunction(inout MaterialInputs m) {
// Incorrect! The MaterialInputs is being referred to by the name "m". // Incorrect! The MaterialInputs is being referred to by the name "m".
m.metallic = 0.0; m.metallic = 0.0;
+2 -6
View File
@@ -16,7 +16,6 @@ set(PUBLIC_HDRS
include/gltfio/FilamentInstance.h include/gltfio/FilamentInstance.h
include/gltfio/MaterialProvider.h include/gltfio/MaterialProvider.h
include/gltfio/NodeManager.h include/gltfio/NodeManager.h
include/gltfio/TrsTransformManager.h
include/gltfio/ResourceLoader.h include/gltfio/ResourceLoader.h
include/gltfio/TextureProvider.h include/gltfio/TextureProvider.h
include/gltfio/math.h include/gltfio/math.h
@@ -36,12 +35,10 @@ set(SRCS
src/FilamentAsset.cpp src/FilamentAsset.cpp
src/FilamentInstance.cpp src/FilamentInstance.cpp
src/FNodeManager.h src/FNodeManager.h
src/FTrsTransformManager.h
src/GltfEnums.h src/GltfEnums.h
src/Ktx2Provider.cpp src/Ktx2Provider.cpp
src/MaterialProvider.cpp src/MaterialProvider.cpp
src/NodeManager.cpp src/NodeManager.cpp
src/TrsTransformManager.cpp
src/ResourceLoader.cpp src/ResourceLoader.cpp
src/StbProvider.cpp src/StbProvider.cpp
src/TangentsJob.cpp src/TangentsJob.cpp
@@ -189,7 +186,8 @@ if (NOT WEBGL AND NOT ANDROID AND NOT IOS)
# ================================================================================================== # ==================================================================================================
# Compiler flags # Compiler flags
# ================================================================================================== # ==================================================================================================
if (NOT MSVC) if (MSVC)
else()
target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register) target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register)
endif() endif()
@@ -227,9 +225,7 @@ if (TNT_DEV AND NOT WEBGL AND NOT ANDROID AND NOT IOS)
add_dependencies(${TEST_TARGET} test_gltfio_files) add_dependencies(${TEST_TARGET} test_gltfio_files)
target_link_libraries(${TEST_TARGET} PRIVATE ${TARGET} filament filabridge gtest uberarchive) target_link_libraries(${TEST_TARGET} PRIVATE ${TARGET} filament filabridge gtest uberarchive)
if (NOT MSVC)
target_compile_options(${TEST_TARGET} PRIVATE -Wno-deprecated-register) target_compile_options(${TEST_TARGET} PRIVATE -Wno-deprecated-register)
endif()
set_target_properties(${TEST_TARGET} PROPERTIES FOLDER Tests) set_target_properties(${TEST_TARGET} PROPERTIES FOLDER Tests)
endif() endif()
@@ -1,114 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_TRSTRANSFORMMANAGER_H
#define GLTFIO_TRSTRANSFORMMANAGER_H
#include <filament/FilamentAPI.h>
#include <utils/compiler.h>
#include <utils/EntityInstance.h>
#include <math/quat.h>
#include <math/vec3.h>
#include <math/mat4.h>
using namespace filament::math;
namespace utils {
class Entity;
} // namespace utils
namespace filament::gltfio {
class FTrsTransformManager;
/**
* TrsTransformManager is used to add entities with glTF-specific trs information.
*
* Trs information here just used for Animation, DON'T use for transform.
*/
class UTILS_PUBLIC TrsTransformManager {
public:
using Instance = utils::EntityInstance<TrsTransformManager>;
using Entity = utils::Entity;
/**
* Returns whether a particular Entity is associated with a component of this TrsTransformManager
* @param e An Entity.
* @return true if this Entity has a component associated with this manager.
*/
bool hasComponent(Entity e) const noexcept;
/**
* Gets an Instance representing the trs transform component associated with the given Entity.
* @param e An Entity.
* @return An Instance object, which represents the trs transform component associated with the Entity e.
* @note Use Instance::isValid() to make sure the component exists.
* @see hasComponent()
*/
Instance getInstance(Entity e) const noexcept;
/**
* Creates a trs transform component and associates it with the given entity.
* @param entity An Entity to associate a trs transform component with.
* @param translation The translation to initialize the trs transform component with.
* @param rotation The rotation to initialize the trs transform component with.
* @param scale The scale to initialize the trs transform component with.
*
* If this component already exists on the given entity, it is first destroyed as if
* destroy(Entity e) was called.
*
* @see destroy()
*/
void create(Entity entity);
void create(Entity entity, const float3& translation, const quatf& rotation,
const float3& scale); //!< \overload
/**
* Destroys this component from the given entity.
* @param e An entity.
*
* @see create()
*/
void destroy(Entity e) noexcept;
void setTranslation(Instance ci, const float3& translation) noexcept;
const float3& getTranslation(Instance ci) const noexcept;
void setRotation(Instance ci, const quatf& rotation) noexcept;
const quatf& getRotation(Instance ci) const noexcept;
void setScale(Instance ci, const float3& scale) noexcept;
const float3& getScale(Instance ci) const noexcept;
void setTrs(Instance ci, const float3& translation, const quatf& rotation,
const float3& scale) noexcept;
const mat4f getTransform(Instance ci) const noexcept;
protected:
TrsTransformManager() noexcept = default;
~TrsTransformManager() = default;
public:
TrsTransformManager(TrsTransformManager const&) = delete;
TrsTransformManager(TrsTransformManager&&) = delete;
TrsTransformManager& operator=(TrsTransformManager const&) = delete;
TrsTransformManager& operator=(TrsTransformManager&&) = delete;
};
} // namespace filament::gltfio
#endif // GLTFIO_TRSTRANSFORMMANAGER_H
+11 -11
View File
@@ -19,7 +19,6 @@
#include "FFilamentAsset.h" #include "FFilamentAsset.h"
#include "FFilamentInstance.h" #include "FFilamentInstance.h"
#include "FTrsTransformManager.h"
#include "downcast.h" #include "downcast.h"
#include <filament/VertexBuffer.h> #include <filament/VertexBuffer.h>
@@ -75,7 +74,6 @@ struct AnimatorImpl {
FFilamentInstance* instance = nullptr; FFilamentInstance* instance = nullptr;
RenderableManager* renderableManager; RenderableManager* renderableManager;
TransformManager* transformManager; TransformManager* transformManager;
TrsTransformManager* trsTransformManager;
vector<float> weights; vector<float> weights;
FixedCapacityVector<mat4f> crossFade; FixedCapacityVector<mat4f> crossFade;
void addChannels(const FixedCapacityVector<Entity>& nodeMap, const cgltf_animation& srcAnim, void addChannels(const FixedCapacityVector<Entity>& nodeMap, const cgltf_animation& srcAnim,
@@ -192,7 +190,6 @@ Animator::Animator(FFilamentAsset const* asset, FFilamentInstance* instance) {
mImpl->instance = instance; mImpl->instance = instance;
mImpl->renderableManager = &asset->mEngine->getRenderableManager(); mImpl->renderableManager = &asset->mEngine->getRenderableManager();
mImpl->transformManager = &asset->mEngine->getTransformManager(); mImpl->transformManager = &asset->mEngine->getTransformManager();
mImpl->trsTransformManager = asset->getTrsTransformManager();
const cgltf_data* srcAsset = asset->mSourceAsset->hierarchy; const cgltf_data* srcAsset = asset->mSourceAsset->hierarchy;
const cgltf_animation* srcAnims = srcAsset->animations; const cgltf_animation* srcAnims = srcAsset->animations;
@@ -432,13 +429,20 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
size_t nextIndex) { size_t nextIndex) {
const Sampler* sampler = channel.sourceData; const Sampler* sampler = channel.sourceData;
const TimeValues& times = sampler->times; const TimeValues& times = sampler->times;
TrsTransformManager::Instance trsNode = trsTransformManager->getInstance(channel.targetEntity);
TransformManager::Instance node = transformManager->getInstance(channel.targetEntity); TransformManager::Instance node = transformManager->getInstance(channel.targetEntity);
// Perform the interpolation. This is a simple but inefficient implementation; Filament
// stores transforms as mat4's but glTF animation is based on TRS (translation rotation
// scale).
mat4f xform = transformManager->getTransform(node);
float3 scale;
quatf rotation;
float3 translation;
decomposeMatrix(xform, &translation, &rotation, &scale);
switch (channel.transformType) { switch (channel.transformType) {
case Channel::SCALE: { case Channel::SCALE: {
float3 scale;
const float3* srcVec3 = (const float3*) sampler->values.data(); const float3* srcVec3 = (const float3*) sampler->values.data();
if (sampler->interpolation == Sampler::CUBIC) { if (sampler->interpolation == Sampler::CUBIC) {
float3 vert0 = srcVec3[prevIndex * 3 + 1]; float3 vert0 = srcVec3[prevIndex * 3 + 1];
@@ -449,12 +453,10 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
} else { } else {
scale = ((1 - t) * srcVec3[prevIndex]) + (t * srcVec3[nextIndex]); scale = ((1 - t) * srcVec3[prevIndex]) + (t * srcVec3[nextIndex]);
} }
trsTransformManager->setScale(trsNode, scale);
break; break;
} }
case Channel::TRANSLATION: { case Channel::TRANSLATION: {
float3 translation;
const float3* srcVec3 = (const float3*) sampler->values.data(); const float3* srcVec3 = (const float3*) sampler->values.data();
if (sampler->interpolation == Sampler::CUBIC) { if (sampler->interpolation == Sampler::CUBIC) {
float3 vert0 = srcVec3[prevIndex * 3 + 1]; float3 vert0 = srcVec3[prevIndex * 3 + 1];
@@ -465,12 +467,10 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
} else { } else {
translation = ((1 - t) * srcVec3[prevIndex]) + (t * srcVec3[nextIndex]); translation = ((1 - t) * srcVec3[prevIndex]) + (t * srcVec3[nextIndex]);
} }
trsTransformManager->setTranslation(trsNode, translation);
break; break;
} }
case Channel::ROTATION: { case Channel::ROTATION: {
quatf rotation;
const quatf* srcQuat = (const quatf*) sampler->values.data(); const quatf* srcQuat = (const quatf*) sampler->values.data();
if (sampler->interpolation == Sampler::CUBIC) { if (sampler->interpolation == Sampler::CUBIC) {
quatf vert0 = srcQuat[prevIndex * 3 + 1]; quatf vert0 = srcQuat[prevIndex * 3 + 1];
@@ -481,7 +481,6 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
} else { } else {
rotation = slerp(srcQuat[prevIndex], srcQuat[nextIndex], t); rotation = slerp(srcQuat[prevIndex], srcQuat[nextIndex], t);
} }
trsTransformManager->setRotation(trsNode, rotation);
break; break;
} }
@@ -520,7 +519,8 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
} }
} }
transformManager->setTransform(node, trsTransformManager->getTransform(trsNode)); xform = composeMatrix(translation, rotation, scale);
transformManager->setTransform(node, xform);
} }
void AnimatorImpl::resetBoneMatrices(FFilamentInstance* instance) { void AnimatorImpl::resetBoneMatrices(FFilamentInstance* instance) {
+3 -9
View File
@@ -21,7 +21,6 @@
#include "FFilamentAsset.h" #include "FFilamentAsset.h"
#include "FNodeManager.h" #include "FNodeManager.h"
#include "FTrsTransformManager.h"
#include "GltfEnums.h" #include "GltfEnums.h"
#include <filament/Box.h> #include <filament/Box.h>
@@ -296,7 +295,6 @@ public:
MaterialProvider& mMaterials; MaterialProvider& mMaterials;
Engine& mEngine; Engine& mEngine;
FNodeManager mNodeManager; FNodeManager mNodeManager;
FTrsTransformManager mTrsTransformManager;
// Transient state used only for the asset currently being loaded: // Transient state used only for the asset currently being loaded:
FFilamentAsset* mAsset; FFilamentAsset* mAsset;
@@ -402,8 +400,7 @@ void FAssetLoader::createRootAsset(const cgltf_data* srcAsset) {
#endif #endif
mDummyBufferObject = nullptr; mDummyBufferObject = nullptr;
mAsset = new FFilamentAsset(&mEngine, mNameManager, &mEntityManager, &mNodeManager, mAsset = new FFilamentAsset(&mEngine, mNameManager, &mEntityManager, &mNodeManager, srcAsset);
&mTrsTransformManager, srcAsset);
// It is not an error for a glTF file to have zero scenes. // It is not an error for a glTF file to have zero scenes.
mAsset->mScenes.clear(); mAsset->mScenes.clear();
@@ -566,9 +563,7 @@ void FAssetLoader::recurseEntities(const cgltf_data* srcAsset, const cgltf_node*
quatf* rotation = (quatf*) &node->rotation[0]; quatf* rotation = (quatf*) &node->rotation[0];
float3* scale = (float3*) &node->scale[0]; float3* scale = (float3*) &node->scale[0];
float3* translation = (float3*) &node->translation[0]; float3* translation = (float3*) &node->translation[0];
mTrsTransformManager.create(entity, *translation, *rotation, *scale); localTransform = composeMatrix(*translation, *rotation, *scale);
localTransform = mTrsTransformManager.getTransform(
mTrsTransformManager.getInstance(entity));
} }
auto parentTransform = mTransformManager.getInstance(parent); auto parentTransform = mTransformManager.getInstance(parent);
@@ -1472,9 +1467,8 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsse
} }
} }
if (matkey.hasSheenRoughnessTexture) { if (matkey.hasSheenRoughnessTexture) {
bool sameTexture = shConfig.sheen_color_texture.texture == shConfig.sheen_roughness_texture.texture;
mAsset->addTextureBinding(mi, "sheenRoughnessMap", mAsset->addTextureBinding(mi, "sheenRoughnessMap",
shConfig.sheen_roughness_texture.texture, sameTexture ? sRGB : LINEAR); shConfig.sheen_roughness_texture.texture, LINEAR);
if (matkey.hasTextureTransforms) { if (matkey.hasTextureTransforms) {
const cgltf_texture_transform& uvt = shConfig.sheen_roughness_texture.transform; const cgltf_texture_transform& uvt = shConfig.sheen_roughness_texture.transform;
auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale); auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale);
+2 -8
View File
@@ -19,7 +19,6 @@
#include <gltfio/FilamentAsset.h> #include <gltfio/FilamentAsset.h>
#include <gltfio/NodeManager.h> #include <gltfio/NodeManager.h>
#include <gltfio/TrsTransformManager.h>
#include <filament/Engine.h> #include <filament/Engine.h>
#include <filament/IndexBuffer.h> #include <filament/IndexBuffer.h>
@@ -111,9 +110,9 @@ using MeshCache = utils::FixedCapacityVector<utils::FixedCapacityVector<Primitiv
struct FFilamentAsset : public FilamentAsset { struct FFilamentAsset : public FilamentAsset {
FFilamentAsset(Engine* engine, utils::NameComponentManager* names, FFilamentAsset(Engine* engine, utils::NameComponentManager* names,
utils::EntityManager* entityManager, NodeManager* nodeManager, utils::EntityManager* entityManager, NodeManager* nodeManager,
TrsTransformManager* trsTransformManager, const cgltf_data* srcAsset) : const cgltf_data* srcAsset) :
mEngine(engine), mNameManager(names), mEntityManager(entityManager), mEngine(engine), mNameManager(names), mEntityManager(entityManager),
mNodeManager(nodeManager), mTrsTransformManager(trsTransformManager), mNodeManager(nodeManager),
mSourceAsset(new SourceAsset {(cgltf_data*)srcAsset}), mSourceAsset(new SourceAsset {(cgltf_data*)srcAsset}),
mTextures(srcAsset->textures_count), mTextures(srcAsset->textures_count),
mMeshCache(srcAsset->meshes_count) {} mMeshCache(srcAsset->meshes_count) {}
@@ -196,10 +195,6 @@ struct FFilamentAsset : public FilamentAsset {
return mEngine; return mEngine;
} }
TrsTransformManager* getTrsTransformManager() const noexcept {
return mTrsTransformManager;
}
void releaseSourceData() noexcept; void releaseSourceData() noexcept;
const void* getSourceAsset() const noexcept { const void* getSourceAsset() const noexcept {
@@ -247,7 +242,6 @@ struct FFilamentAsset : public FilamentAsset {
utils::NameComponentManager* const mNameManager; utils::NameComponentManager* const mNameManager;
utils::EntityManager* const mEntityManager; utils::EntityManager* const mEntityManager;
NodeManager* const mNodeManager; NodeManager* const mNodeManager;
TrsTransformManager* const mTrsTransformManager;
std::vector<utils::Entity> mEntities; // sorted such that renderables come first std::vector<utils::Entity> mEntities; // sorted such that renderables come first
std::vector<utils::Entity> mLightEntities; std::vector<utils::Entity> mLightEntities;
std::vector<utils::Entity> mCameraEntities; std::vector<utils::Entity> mCameraEntities;
-165
View File
@@ -1,165 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GLTFIO_FTRSTRANSFORMMANAGER_H
#define GLTFIO_FTRSTRANSFORMMANAGER_H
#include "downcast.h"
#include "gltfio/math.h"
#include "math/quat.h"
#include "utils/debug.h"
#include <gltfio/TrsTransformManager.h>
#include <utils/compiler.h>
#include <utils/SingleInstanceComponentManager.h>
#include <utils/Entity.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Slice.h>
namespace filament::gltfio {
class UTILS_PRIVATE FTrsTransformManager : public TrsTransformManager {
public:
using Instance = TrsTransformManager::Instance;
FTrsTransformManager() noexcept {}
~FTrsTransformManager() noexcept {
assert_invariant(mManager.getComponentCount() == 0);
}
void terminate() noexcept;
bool hasComponent(utils::Entity e) const noexcept {
return mManager.hasComponent(e);
}
Instance getInstance(utils::Entity e) const noexcept {
return Instance(mManager.getInstance(e));
}
void create(utils::Entity entity) {
create(entity, float3{}, quatf{}, float3{1});
}
void create(utils::Entity entity, const float3& translation,
const quatf& rotation, const float3& scale) {
if (UTILS_UNLIKELY(mManager.hasComponent(entity))) {
destroy(entity);
}
UTILS_UNUSED_IN_RELEASE Instance ci = mManager.addComponent(entity);
assert_invariant(ci);
if (ci) {
setTrs(ci, translation, rotation, scale);
}
}
void destroy(utils::Entity e) noexcept {
if (Instance ci = mManager.getInstance(e); ci) {
mManager.removeComponent(e);
}
}
void gc(utils::EntityManager& em) noexcept {
mManager.gc(em);
}
void setTranslation(Instance ci, const float3& translation) noexcept {
assert_invariant(ci.isValid());
mManager[ci].translation = translation;
}
const float3& getTranslation(Instance ci) const noexcept {
return mManager[ci].translation;
}
void setRotation(Instance ci, const quatf& rotation) noexcept {
assert_invariant(ci.isValid());
mManager[ci].rotation = rotation;
}
const quatf& getRotation(Instance ci) const noexcept {
return mManager[ci].rotation;
}
void setScale(Instance ci, const float3& scale) noexcept {
assert_invariant(ci.isValid());
mManager[ci].scale = scale;
}
const float3& getScale(Instance ci) const noexcept {
return mManager[ci].scale;
}
void setTrs(Instance ci, const float3& translation,
const quatf& rotation, const float3& scale) noexcept {
setTranslation(ci, translation);
setRotation(ci, rotation);
setScale(ci, scale);
}
const mat4f getTransform(Instance ci) const noexcept {
return composeMatrix(getTranslation(ci), getRotation(ci), getScale(ci));
}
private:
enum {
TRANSLATION,
ROTATION,
SCALE,
};
using Base = utils::SingleInstanceComponentManager<
float3,
quatf,
float3>;
struct Sim : public Base {
using Base::gc;
using Base::swap;
typename Base::SoA& getSoA() { return mData; }
struct Proxy {
UTILS_ALWAYS_INLINE
Proxy(Base& sim, utils::EntityInstanceBase::Type i) noexcept :
translation{ sim, i } { }
union {
Field<TRANSLATION> translation;
Field<ROTATION> rotation;
Field<SCALE> scale;
};
};
UTILS_ALWAYS_INLINE Proxy operator[](Instance i) noexcept {
return { *this, i };
}
UTILS_ALWAYS_INLINE const Proxy operator[](Instance i) const noexcept {
return { const_cast<Sim&>(*this), i };
}
};
Sim mManager;
};
FILAMENT_DOWNCAST(TrsTransformManager)
} // namespace filament::gltfio
#endif // GLTFIO_FTRSTRANSFORMMANAGER_H
-5
View File
@@ -60,11 +60,6 @@ FFilamentAsset::~FFilamentAsset() {
} }
// Destroy gltfio trs transform components.
for (auto entity : mEntities) {
mTrsTransformManager->destroy(entity);
}
// Destroy all renderable, light, transform, and camera components, // Destroy all renderable, light, transform, and camera components,
// then destroy the actual entities. This includes instances. // then destroy the actual entities. This includes instances.
if (!mDetachedFilamentComponents) { if (!mDetachedFilamentComponents) {
+1 -1
View File
@@ -287,7 +287,7 @@ std::string shaderFromKey(const MaterialKey& config) {
// TODO: Provided by Filament, but this should really be provided/computed by gltfio // TODO: Provided by Filament, but this should really be provided/computed by gltfio
// TODO: This scale is per renderable and should include the scale of the mesh node // TODO: This scale is per renderable and should include the scale of the mesh node
float scale = getObjectUserData(); float scale = object_uniforms.userData;
material.thickness = materialParams.volumeThicknessFactor * scale; material.thickness = materialParams.volumeThicknessFactor * scale;
)SHADER"; )SHADER";
-98
View File
@@ -1,98 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FTrsTransformManager.h"
#include <utils/Log.h>
#include "downcast.h"
#include "gltfio/TrsTransformManager.h"
using namespace utils;
namespace filament::gltfio {
using Instance = TrsTransformManager::Instance;
void FTrsTransformManager::terminate() noexcept {
auto& manager = mManager;
if (!manager.empty()) {
#ifndef NDEBUG
utils::slog.d << "cleaning up " << manager.getComponentCount()
<< " leaked trs transform components" << utils::io::endl;
#endif
while (!manager.empty()) {
Instance ci = manager.end() - 1;
manager.removeComponent(manager.getEntity(ci));
}
}
}
bool TrsTransformManager::hasComponent(Entity e) const noexcept {
return downcast(this)->hasComponent(e);
}
Instance TrsTransformManager::getInstance(Entity e) const noexcept {
return downcast(this)->getInstance(e);
}
void TrsTransformManager::create(Entity entity) {
downcast(this)->create(entity);
}
void TrsTransformManager::create(Entity entity, const float3& translation,
const quatf& rotation, const float3& scale) {
downcast(this)->create(entity, translation, rotation, scale);
}
void TrsTransformManager::destroy(Entity e) noexcept {
downcast(this)->destroy(e);
}
void TrsTransformManager::setTranslation(Instance ci, const float3& translation) noexcept {
downcast(this)->setTranslation(ci, translation);
}
const float3& TrsTransformManager::getTranslation(Instance ci) const noexcept {
return downcast(this)->getTranslation(ci);
}
void TrsTransformManager::setRotation(Instance ci, const quatf& rotation) noexcept {
downcast(this)->setRotation(ci, rotation);
}
const quatf& TrsTransformManager::getRotation(Instance ci) const noexcept {
return downcast(this)->getRotation(ci);
}
void TrsTransformManager::setScale(Instance ci, const float3& scale) noexcept {
downcast(this)->setScale(ci, scale);
}
const float3& TrsTransformManager::getScale(Instance ci) const noexcept {
return downcast(this)->getScale(ci);
}
void TrsTransformManager::setTrs(Instance ci, const float3& translation,
const quatf& rotation, const float3& scale) noexcept {
downcast(this)->setTrs(ci, translation, rotation, scale);
}
const mat4f TrsTransformManager::getTransform(Instance ci) const noexcept {
return downcast(this)->getTransform(ci);
}
} // namespace filament::gltfio
+13 -7
View File
@@ -25,7 +25,10 @@
#include <stdint.h> #include <stdint.h>
#include <sys/types.h> #include <sys/types.h>
namespace filament::math::details { namespace filament {
namespace math {
namespace details {
// -------------------------------------------------------------------------------------
/* /*
* No user serviceable parts here. * No user serviceable parts here.
@@ -46,7 +49,7 @@ namespace filament::math::details {
template<template<typename T> class QUATERNION, typename T> template<template<typename T> class QUATERNION, typename T>
class TQuatProductOperators { class TQuatProductOperators {
public: public:
/* compound assignment from another quaternion of the same size but different /* compound assignment from a another quaternion of the same size but different
* element type. * element type.
*/ */
template<typename OTHER> template<typename OTHER>
@@ -82,7 +85,7 @@ public:
* (the first one, BASE<T> being known). * (the first one, BASE<T> being known).
*/ */
/* The operators below handle operation between quaternions of the same size /* The operators below handle operation between quaternion of the same size
* but of a different element type. * but of a different element type.
*/ */
template<typename RT> template<typename RT>
@@ -124,19 +127,19 @@ public:
*/ */
friend inline friend inline
constexpr QUATERNION<T> MATH_PURE operator*(QUATERNION<T> q, T scalar) { constexpr QUATERNION<T> MATH_PURE operator*(QUATERNION<T> q, T scalar) {
// don't pass q by reference because we need a copy anyway // don't pass q by reference because we need a copy anyways
return q *= scalar; return q *= scalar;
} }
friend inline friend inline
constexpr QUATERNION<T> MATH_PURE operator*(T scalar, QUATERNION<T> q) { constexpr QUATERNION<T> MATH_PURE operator*(T scalar, QUATERNION<T> q) {
// don't pass q by reference because we need a copy anyway // don't pass q by reference because we need a copy anyways
return q *= scalar; return q *= scalar;
} }
friend inline friend inline
constexpr QUATERNION<T> MATH_PURE operator/(QUATERNION<T> q, T scalar) { constexpr QUATERNION<T> MATH_PURE operator/(QUATERNION<T> q, T scalar) {
// don't pass q by reference because we need a copy anyway // don't pass q by reference because we need a copy anyways
return q /= scalar; return q /= scalar;
} }
}; };
@@ -280,6 +283,9 @@ public:
} }
}; };
} // namespace filament::math::details // -------------------------------------------------------------------------------------
} // namespace details
} // namespace math
} // namespace filament
#endif // TNT_MATH_TQUATHELPERS_H #endif // TNT_MATH_TQUATHELPERS_H
-31
View File
@@ -443,37 +443,6 @@ private:
return v; return v;
} }
template<typename U>
friend inline
VECTOR<T> MATH_PURE fmod(VECTOR<T> const& x, VECTOR<U> const& y) {
VECTOR<T> r;
for (size_t i = 0; i < r.size(); i++) {
r[i] = std::fmod(x[i], y[i]);
}
return r;
}
template<typename U>
friend inline
VECTOR<T> MATH_PURE remainder(VECTOR<T> const& x, VECTOR<U> const& y) {
VECTOR<T> r;
for (size_t i = 0; i < r.size(); i++) {
r[i] = std::remainder(x[i], y[i]);
}
return r;
}
template<typename U>
friend inline
VECTOR<T> MATH_PURE remquo(VECTOR<T> const& x, VECTOR<U> const& y,
VECTOR<int>* q) {
VECTOR<T> r;
for (size_t i = 0; i < r.size(); i++) {
r[i] = std::remquo(x[i], y[i], &((*q)[i]));
}
return r;
}
friend inline VECTOR<T> MATH_PURE inversesqrt(VECTOR<T> v) { friend inline VECTOR<T> MATH_PURE inversesqrt(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) { for (size_t i = 0; i < v.size(); i++) {
v[i] = T(1) / std::sqrt(v[i]); v[i] = T(1) / std::sqrt(v[i]);
+4 -7
View File
@@ -33,7 +33,8 @@
#include <stdint.h> #include <stdint.h>
namespace filament::math { namespace filament {
namespace math {
namespace details { namespace details {
template<typename T> class TVec2; template<typename T> class TVec2;
@@ -44,8 +45,6 @@ template<typename T> class TMat22;
template<typename T> class TMat33; template<typename T> class TMat33;
template<typename T> class TMat44; template<typename T> class TMat44;
template<typename T> class TQuaternion;
} // namespace details } // namespace details
using double2 = details::TVec2<double>; using double2 = details::TVec2<double>;
@@ -87,10 +86,8 @@ using mat3f = details::TMat33<float>;
using mat4 = details::TMat44<double>; using mat4 = details::TMat44<double>;
using mat4f = details::TMat44<float>; using mat4f = details::TMat44<float>;
using quat = details::TQuaternion<double>; } // namespace math
using quatf = details::TQuaternion<float>; } // namespace filament
} // namespace filament::math
#endif // _MSC_VER #endif // _MSC_VER
+16 -11
View File
@@ -26,7 +26,10 @@
#include <stdint.h> #include <stdint.h>
#include <sys/types.h> #include <sys/types.h>
namespace filament::math { namespace filament {
namespace math {
// -------------------------------------------------------------------------------------
namespace details { namespace details {
template<typename T> template<typename T>
@@ -86,15 +89,15 @@ public:
// constructors // constructors
// Leaves object uninitialized. Use with caution. // leaves object uninitialized. use with caution.
explicit constexpr TQuaternion(no_init) {} explicit constexpr TQuaternion(no_init) {}
// default constructor. sets all values to zero. // default constructor. sets all values to zero.
constexpr TQuaternion() : x(0), y(0), z(0), w(0) {} constexpr TQuaternion() : x(0), y(0), z(0), w(0) {}
// Handles implicit conversion to a quat. Must not be explicit. // handles implicit conversion to a quat. must not be explicit.
template<typename A, typename = enable_if_arithmetic_t<A>> template<typename A, typename = enable_if_arithmetic_t<A>>
constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) {} // NOLINT(google-explicit-constructor) constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) {}
// initialize from 4 values to w + xi + yj + zk // initialize from 4 values to w + xi + yj + zk
template<typename A, typename B, typename C, typename D, template<typename A, typename B, typename C, typename D,
@@ -172,29 +175,31 @@ typedef details::TQuaternion<float> quatf;
typedef details::TQuaternion<half> quath; typedef details::TQuaternion<half> quath;
constexpr inline quat operator "" _i(long double v) { constexpr inline quat operator "" _i(long double v) {
return { 0.0, double(v), 0.0, 0.0 }; return quat(0.0, double(v), 0.0, 0.0);
} }
constexpr inline quat operator "" _j(long double v) { constexpr inline quat operator "" _j(long double v) {
return { 0.0, 0.0, double(v), 0.0 }; return quat(0.0, 0.0, double(v), 0.0);
} }
constexpr inline quat operator "" _k(long double v) { constexpr inline quat operator "" _k(long double v) {
return { 0.0, 0.0, 0.0, double(v) }; return quat(0.0, 0.0, 0.0, double(v));
} }
constexpr inline quat operator "" _i(unsigned long long v) { constexpr inline quat operator "" _i(unsigned long long v) {
return { 0.0, double(v), 0.0, 0.0 }; return quat(0.0, double(v), 0.0, 0.0);
} }
constexpr inline quat operator "" _j(unsigned long long v) { constexpr inline quat operator "" _j(unsigned long long v) {
return { 0.0, 0.0, double(v), 0.0 }; return quat(0.0, 0.0, double(v), 0.0);
} }
constexpr inline quat operator "" _k(unsigned long long v) { constexpr inline quat operator "" _k(unsigned long long v) {
return { 0.0, 0.0, 0.0, double(v) }; return quat(0.0, 0.0, 0.0, double(v));
} }
} // namespace filament::math // ----------------------------------------------------------------------------------------
} // namespace math
} // namespace filament
#endif // TNT_MATH_QUAT_H #endif // TNT_MATH_QUAT_H
+9 -4
View File
@@ -36,6 +36,13 @@ public:
* @see CallStack::capture() * @see CallStack::capture()
*/ */
CallStack() = default; CallStack() = default;
template <unsigned long N>
explicit CallStack(const std::array<intptr_t, N>& symbols)
: m_frame_count(N) {
for (size_t i = 0; i < N; i++) {
m_stack[i] = symbols[i];
}
}
CallStack(const CallStack&) = default; CallStack(const CallStack&) = default;
~CallStack() = default; ~CallStack() = default;
@@ -114,12 +121,10 @@ private:
static constexpr size_t NUM_FRAMES = 20; static constexpr size_t NUM_FRAMES = 20;
struct StackFrameInfo { using StackFrameInfo = intptr_t;
intptr_t pc;
};
size_t m_frame_count = 0; size_t m_frame_count = 0;
StackFrameInfo m_stack[NUM_FRAMES]; StackFrameInfo m_stack[NUM_FRAMES] = {0};
}; };
} // namespace utils } // namespace utils
+2 -2
View File
@@ -70,7 +70,7 @@ intptr_t CallStack::operator[](size_t index) const {
#endif #endif
std::abort(); std::abort();
} }
return m_stack[index].pc; return m_stack[index];
} }
size_t CallStack::getFrameCount() const noexcept { size_t CallStack::getFrameCount() const noexcept {
@@ -91,7 +91,7 @@ void CallStack::update_gcc(size_t ignore) noexcept {
size -= ignore; size -= ignore;
#endif #endif
for (ssize_t i = 0; i < size; i++) { for (ssize_t i = 0; i < size; i++) {
m_stack[i].pc = intptr_t(array[ignore + i]); m_stack[i] = intptr_t(array[ignore + i]);
} }
size--; // the last one seems to always be 0x0 size--; // the last one seems to always be 0x0
+15 -28
View File
@@ -362,13 +362,19 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk,
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk,
LightManager::ShadowOptions* out) { LightManager::ShadowOptions* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int const size = tokens[i++].size; int size = tokens[i++].size;
math::float3 splitsVector; math::float3 splitsVector;
for (int j = 0; j < size; ++j) { for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i]; const jsmntok_t tok = tokens[i];
CHECK_KEY(tok); CHECK_KEY(tok);
if (compare(tok, jsonChunk, "mapSize") == 0) { if (compare(tok, jsonChunk, "mapSize") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->mapSize); i = parse(tokens, i + 1, jsonChunk, &out->mapSize);
} else if (compare(tok, jsonChunk, "stable") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->stable);
} else if (compare(tok, jsonChunk, "lispsm") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->lispsm);
} else if (compare(tok, jsonChunk, "screenSpaceContactShadows") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->screenSpaceContactShadows);
} else if (compare(tok, jsonChunk, "shadowCascades") == 0) { } else if (compare(tok, jsonChunk, "shadowCascades") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->shadowCascades); i = parse(tokens, i + 1, jsonChunk, &out->shadowCascades);
} else if (compare(tok, jsonChunk, "cascadeSplitPositions") == 0) { } else if (compare(tok, jsonChunk, "cascadeSplitPositions") == 0) {
@@ -376,27 +382,8 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk,
out->cascadeSplitPositions[0] = splitsVector[0]; out->cascadeSplitPositions[0] = splitsVector[0];
out->cascadeSplitPositions[1] = splitsVector[1]; out->cascadeSplitPositions[1] = splitsVector[1];
out->cascadeSplitPositions[2] = splitsVector[2]; out->cascadeSplitPositions[2] = splitsVector[2];
// TODO: constantBias
// TODO: normalBias
// TODO: shadowFar
// TODO: shadowNearHint
// TODO: shadowFarHint
} else if (compare(tok, jsonChunk, "stable") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->stable);
} else if (compare(tok, jsonChunk, "lispsm") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->lispsm);
// TODO: polygonOffsetConstant
// TODO: polygonOffsetSlope
} else if (compare(tok, jsonChunk, "screenSpaceContactShadows") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->screenSpaceContactShadows);
// TODO: stepCount
// TODO: maxShadowDistance
} else if (compare(tok, jsonChunk, "vsm") == 0) { } else if (compare(tok, jsonChunk, "vsm") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->vsm); i = parse(tokens, i + 1, jsonChunk, &out->vsm);
} else if (compare(tok, jsonChunk, "shadowBulbRadius") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->shadowBulbRadius);
} else if (compare(tok, jsonChunk, "transform") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->transform.xyzw);
} else { } else {
slog.w << "Invalid shadow options key: '" << STR(tok, jsonChunk) << "'" << io::endl; slog.w << "Invalid shadow options key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1); i = parse(tokens, i + 1);
@@ -578,9 +565,11 @@ void applySettings(Engine* engine, const LightSettings& settings, IndirectLight*
ibl->setRotation(math::mat3f::rotation(settings.iblRotation, math::float3 { 0, 1, 0 })); ibl->setRotation(math::mat3f::rotation(settings.iblRotation, math::float3 { 0, 1, 0 }));
} }
for (size_t i = 0; i < sceneLightCount; i++) { for (size_t i = 0; i < sceneLightCount; i++) {
auto const li = lm->getInstance(sceneLights[i]); light = lm->getInstance(sceneLights[i]);
lm->setShadowCaster(li, settings.enableShadows); if (lm->isSpotLight(light)) {
lm->setShadowOptions(li, settings.shadowOptions); lm->setShadowCaster(light, settings.enableShadows);
}
lm->setShadowOptions(light, settings.shadowOptions);
} }
view->setSoftShadowOptions(settings.softShadowOptions); view->setSoftShadowOptions(settings.softShadowOptions);
} }
@@ -723,20 +712,18 @@ static std::ostream& operator<<(std::ostream& out, const ColorGradingSettings& i
static std::ostream& operator<<(std::ostream& out, const LightManager::ShadowOptions& in) { static std::ostream& operator<<(std::ostream& out, const LightManager::ShadowOptions& in) {
const float* splits = in.cascadeSplitPositions; const float* splits = in.cascadeSplitPositions;
math::float3 const splitsVector = { splits[0], splits[1], splits[2] }; math::float3 splitsVector = { splits[0], splits[1], splits[2] };
return out << "{\n" return out << "{\n"
<< "\"vsm\": {\n" << "\"vsm\": {\n"
<< "\"elvsm\": " << to_string(in.vsm.elvsm) << ",\n" << "\"elvsm\": " << to_string(in.vsm.elvsm) << ",\n"
<< "\"blurWidth\": " << in.vsm.blurWidth << "\n" << "\"blurWidth\": " << in.vsm.blurWidth << "\n"
<< "},\n" << "},\n"
<< "\"mapSize\": " << in.mapSize << ",\n" << "\"mapSize\": " << in.mapSize << ",\n"
<< "\"shadowCascades\": " << int(in.shadowCascades) << ",\n"
<< "\"cascadeSplitPositions\": " << (splitsVector) << "\n"
<< "\"stable\": " << to_string(in.stable) << ",\n" << "\"stable\": " << to_string(in.stable) << ",\n"
<< "\"lispsm\": " << to_string(in.lispsm) << ",\n" << "\"lispsm\": " << to_string(in.lispsm) << ",\n"
<< "\"screenSpaceContactShadows\": " << to_string(in.screenSpaceContactShadows) << ",\n" << "\"screenSpaceContactShadows\": " << to_string(in.screenSpaceContactShadows) << ",\n"
<< "\"shadowBulbRadius\": " << in.shadowBulbRadius << ",\n" << "\"shadowCascades\": " << int(in.shadowCascades) << ",\n"
<< "\"transform\": " << in.transform.xyzw << ",\n" << "\"cascadeSplitPositions\": " << (splitsVector) << "\n"
<< "}"; << "}";
} }
-3
View File
@@ -249,8 +249,6 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, BloomOptions* o
i = parse(tokens, i + 1, jsonChunk, &out->enabled); i = parse(tokens, i + 1, jsonChunk, &out->enabled);
} else if (compare(tok, jsonChunk, "highlight") == 0) { } else if (compare(tok, jsonChunk, "highlight") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->highlight); i = parse(tokens, i + 1, jsonChunk, &out->highlight);
} else if (compare(tok, jsonChunk, "quality") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->quality);
} else if (compare(tok, jsonChunk, "lensFlare") == 0) { } else if (compare(tok, jsonChunk, "lensFlare") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->lensFlare); i = parse(tokens, i + 1, jsonChunk, &out->lensFlare);
} else if (compare(tok, jsonChunk, "starburst") == 0) { } else if (compare(tok, jsonChunk, "starburst") == 0) {
@@ -293,7 +291,6 @@ std::ostream& operator<<(std::ostream& out, const BloomOptions& in) {
<< "\"threshold\": " << to_string(in.threshold) << ",\n" << "\"threshold\": " << to_string(in.threshold) << ",\n"
<< "\"enabled\": " << to_string(in.enabled) << ",\n" << "\"enabled\": " << to_string(in.enabled) << ",\n"
<< "\"highlight\": " << (in.highlight) << ",\n" << "\"highlight\": " << (in.highlight) << ",\n"
<< "\"quality\": " << (in.quality) << ",\n"
<< "\"lensFlare\": " << to_string(in.lensFlare) << ",\n" << "\"lensFlare\": " << to_string(in.lensFlare) << ",\n"
<< "\"starburst\": " << to_string(in.starburst) << ",\n" << "\"starburst\": " << to_string(in.starburst) << ",\n"
<< "\"chromaticAberration\": " << (in.chromaticAberration) << ",\n" << "\"chromaticAberration\": " << (in.chromaticAberration) << ",\n"
-12
View File
@@ -777,10 +777,6 @@ void ViewerGui::updateUserInterface() {
ImGui::SliderInt("Levels", &levels, 3, 11); ImGui::SliderInt("Levels", &levels, 3, 11);
mSettings.view.bloom.levels = levels; mSettings.view.bloom.levels = levels;
int quality = (int) mSettings.view.bloom.quality;
ImGui::SliderInt("Bloom Quality", &quality, 0, 3);
mSettings.view.bloom.quality = (View::QualityLevel) quality;
ImGui::Checkbox("Lens Flare", &mSettings.view.bloom.lensFlare); ImGui::Checkbox("Lens Flare", &mSettings.view.bloom.lensFlare);
} }
@@ -857,14 +853,6 @@ void ViewerGui::updateUserInterface() {
ImGui::SliderFloat("Halo falloff", &light.sunlightHaloFalloff, 4.0f, 1024.0f); ImGui::SliderFloat("Halo falloff", &light.sunlightHaloFalloff, 4.0f, 1024.0f);
ImGui::SliderFloat("Sun radius", &light.sunlightAngularRadius, 0.1f, 10.0f); ImGui::SliderFloat("Sun radius", &light.sunlightAngularRadius, 0.1f, 10.0f);
ImGuiExt::DirectionWidget("Sun direction", light.sunlightDirection.v); ImGuiExt::DirectionWidget("Sun direction", light.sunlightDirection.v);
float3 shadowDirection = light.shadowOptions.transform * light.sunlightDirection;
ImGuiExt::DirectionWidget("Shadow direction", shadowDirection.v);
light.shadowOptions.transform = normalize(quatf{
cross(light.sunlightDirection, shadowDirection),
sqrt(length2(light.sunlightDirection) * length2(shadowDirection))
+ dot(light.sunlightDirection, shadowDirection)
});
} }
if (ImGui::CollapsingHeader("All lights")) { if (ImGui::CollapsingHeader("All lights")) {
ImGui::Checkbox("Enable shadows", &light.enableShadows); ImGui::Checkbox("Enable shadows", &light.enableShadows);
+1 -3
View File
@@ -101,7 +101,6 @@ struct App {
bool actualSize = false; bool actualSize = false;
bool originIsFarAway = false; bool originIsFarAway = false;
float originDistance = 6378137; // Earth's radius in [m]
struct Scene { struct Scene {
Entity groundPlane; Entity groundPlane;
@@ -760,7 +759,6 @@ int main(int argc, char** argv) {
ImGui::Checkbox("Disable buffer padding", debug.getPropertyAddress<bool>("d.renderer.disable_buffer_padding")); ImGui::Checkbox("Disable buffer padding", debug.getPropertyAddress<bool>("d.renderer.disable_buffer_padding"));
ImGui::Checkbox("Camera at origin", debug.getPropertyAddress<bool>("d.view.camera_at_origin")); ImGui::Checkbox("Camera at origin", debug.getPropertyAddress<bool>("d.view.camera_at_origin"));
ImGui::Checkbox("Far Origin", &app.originIsFarAway); ImGui::Checkbox("Far Origin", &app.originIsFarAway);
ImGui::SliderFloat("Origin", &app.originDistance, 0, 10000000);
auto dataSource = debug.getDataSource("d.view.frame_info"); auto dataSource = debug.getDataSource("d.view.frame_info");
if (dataSource.data) { if (dataSource.data) {
ImGuiExt::PlotLinesSeries("FrameInfo", 6, ImGuiExt::PlotLinesSeries("FrameInfo", 6,
@@ -958,7 +956,7 @@ int main(int argc, char** argv) {
tcm.setParent(tcm.getInstance(camera.getEntity()), root); tcm.setParent(tcm.getInstance(camera.getEntity()), root);
tcm.setParent(tcm.getInstance(app.asset->getRoot()), root); tcm.setParent(tcm.getInstance(app.asset->getRoot()), root);
tcm.setParent(tcm.getInstance(view->getFogEntity()), root); tcm.setParent(tcm.getInstance(view->getFogEntity()), root);
tcm.setTransform(root, mat4f::translation(float3{ app.originIsFarAway ? app.originDistance : 0.0f })); tcm.setTransform(root, mat4f::translation(float3{ app.originIsFarAway ? 1e6f : 0.0f }));
// Check if color grading has changed. // Check if color grading has changed.
ColorGradingSettings& options = app.viewer->getSettings().view.colorGrading; ColorGradingSettings& options = app.viewer->getSettings().view.colorGrading;
+2 -2
View File
@@ -109,7 +109,7 @@ highp vec4 getSpotLightSpacePosition(int index, highp vec3 dir, highp float zLig
// for spotlights, the bias depends on z // for spotlights, the bias depends on z
float bias = shadowUniforms.shadows[index].normalBias * zLight; float bias = shadowUniforms.shadows[index].normalBias * zLight;
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(), return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(),
dir, bias, lightFromWorldMatrix); dir, bias, lightFromWorldMatrix);
} }
#endif #endif
@@ -141,7 +141,7 @@ highp vec4 getCascadeLightSpacePosition(int cascade) {
return vertex_lightSpacePosition; return vertex_lightSpacePosition;
} }
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(), return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(),
frameUniforms.lightDirection, frameUniforms.lightDirection,
shadowUniforms.shadows[cascade].normalBias, shadowUniforms.shadows[cascade].normalBias,
shadowUniforms.shadows[cascade].lightFromWorldMatrix); shadowUniforms.shadows[cascade].lightFromWorldMatrix);
+2 -2
View File
@@ -45,8 +45,8 @@ end
Then run: Then run:
```shell ```
pod install $ pod install
``` ```
Close the project and then re-open the newly created HelloCocoaPods.xcworkspace file. Close the project and then re-open the newly created HelloCocoaPods.xcworkspace file.
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.19) cmake_minimum_required(VERSION 3.0)
if(POLICY CMP0077) if(POLICY CMP0077)
cmake_policy(SET CMP0077 NEW) # Enables override of options from parent CMakeLists.txt cmake_policy(SET CMP0077 NEW) # Enables override of options from parent CMakeLists.txt

Some files were not shown because too many files have changed in this diff Show More