Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
031cd302dd |
+66
-66
@@ -40,20 +40,20 @@ inside the Filament source tree.
|
||||
|
||||
To trigger an incremental debug build:
|
||||
|
||||
```shell
|
||||
./build.sh debug
|
||||
```
|
||||
$ ./build.sh debug
|
||||
```
|
||||
|
||||
To trigger an incremental release build:
|
||||
|
||||
```shell
|
||||
./build.sh release
|
||||
```
|
||||
$ ./build.sh release
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -76,9 +76,9 @@ The following CMake options are boolean options specific to Filament:
|
||||
|
||||
To turn an option on or off:
|
||||
|
||||
```shell
|
||||
cd <cmake-build-directory>
|
||||
cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF
|
||||
```
|
||||
$ cd <cmake-build-directory>
|
||||
$ cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF
|
||||
```
|
||||
|
||||
Options can also be set with the CMake GUI.
|
||||
@@ -102,38 +102,38 @@ script.
|
||||
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.
|
||||
|
||||
```shell
|
||||
mkdir out/cmake-release
|
||||
cd out/cmake-release
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
|
||||
```
|
||||
$ mkdir out/cmake-release
|
||||
$ cd out/cmake-release
|
||||
$ 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
|
||||
`cmake` with the following command:
|
||||
|
||||
```shell
|
||||
mkdir out/cmake-release
|
||||
cd out/cmake-release
|
||||
```
|
||||
$ mkdir out/cmake-release
|
||||
$ cd out/cmake-release
|
||||
# Or use a specific version of clang, for instance /usr/bin/clang-14
|
||||
CC=/usr/bin/clang CXX=/usr/bin/clang++ CXXFLAGS=-stdlib=libc++ \
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
|
||||
$ CC=/usr/bin/clang CXX=/usr/bin/clang++ CXXFLAGS=-stdlib=libc++ \
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
|
||||
```
|
||||
|
||||
You can also export the `CC` and `CXX` environment variables to always point to `clang`. Another
|
||||
solution is to use `update-alternatives` to both change the default compiler, and point to a
|
||||
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/cc cc /usr/bin/clang 100
|
||||
update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 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/c++ c++ /usr/bin/clang++ 100
|
||||
```
|
||||
|
||||
Finally, invoke `ninja`:
|
||||
|
||||
```shell
|
||||
ninja
|
||||
```
|
||||
$ ninja
|
||||
```
|
||||
|
||||
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
|
||||
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
|
||||
@@ -152,11 +152,11 @@ the LunarG SDK, enable "System Global Components", and reboot your machine.
|
||||
|
||||
Then run `cmake` and `ninja` to trigger a build:
|
||||
|
||||
```shell
|
||||
mkdir out/cmake-release
|
||||
cd out/cmake-release
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
|
||||
ninja
|
||||
```
|
||||
$ mkdir out/cmake-release
|
||||
$ cd out/cmake-release
|
||||
$ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
|
||||
$ ninja
|
||||
```
|
||||
|
||||
### iOS
|
||||
@@ -164,8 +164,8 @@ ninja
|
||||
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:
|
||||
|
||||
```shell
|
||||
./build.sh -p ios debug
|
||||
```
|
||||
$ ./build.sh -p ios debug
|
||||
```
|
||||
|
||||
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
|
||||
CMake in it:
|
||||
|
||||
```bat
|
||||
mkdir out
|
||||
cd out
|
||||
cmake ..
|
||||
```
|
||||
> mkdir out
|
||||
> cd out
|
||||
> cmake ..
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```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
|
||||
`out` folder run the following command.
|
||||
|
||||
```bat
|
||||
cmake --build . --target gltf_viewer --config Release
|
||||
```
|
||||
> cmake --build . --target gltf_viewer --config Release
|
||||
```
|
||||
|
||||
### 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
|
||||
`-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.
|
||||
@@ -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
|
||||
below show how to build Filament for ARM 64-bit (`aarch64`).
|
||||
|
||||
```shell
|
||||
mkdir out/android-build-release-aarch64
|
||||
cd out/android-build-release-aarch64
|
||||
cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=../../build/toolchain-aarch64-linux-android.cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../android-release/filament ../..
|
||||
```
|
||||
$ mkdir out/android-build-release-aarch64
|
||||
$ cd out/android-build-release-aarch64
|
||||
$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=../../build/toolchain-aarch64-linux-android.cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../android-release/filament ../..
|
||||
```
|
||||
|
||||
And then invoke `ninja`:
|
||||
|
||||
```shell
|
||||
ninja install
|
||||
```
|
||||
$ ninja install
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```shell
|
||||
ninja install/strip
|
||||
```
|
||||
$ ninja install/strip
|
||||
```
|
||||
|
||||
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
|
||||
`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
|
||||
@@ -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
|
||||
your Gradle build file. For example:
|
||||
|
||||
```gradle
|
||||
```
|
||||
flavorDimensions 'cpuArch'
|
||||
productFlavors {
|
||||
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
|
||||
same version that our continuous builds use.
|
||||
|
||||
```shell
|
||||
```
|
||||
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
|
||||
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:
|
||||
|
||||
```shell
|
||||
```
|
||||
export EMSDK=<your chosen home for the emscripten SDK>
|
||||
./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
|
||||
to create a quick localhost server:
|
||||
|
||||
```shell
|
||||
```
|
||||
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
|
||||
(`./tools/filamesh/filamesh` in your build directory):
|
||||
|
||||
```shell
|
||||
```
|
||||
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
|
||||
simply use this command:
|
||||
|
||||
```shell
|
||||
```
|
||||
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
|
||||
following commands:
|
||||
|
||||
```shell
|
||||
cd filament/filament
|
||||
doxygen docs/doxygen/filament.doxygen
|
||||
```
|
||||
$ cd filament/filament
|
||||
$ doxygen docs/doxygen/filament.doxygen
|
||||
```
|
||||
|
||||
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
|
||||
`SWIFTSHADER_LD_LIBRARY_PATH` variable to the folder that contains `libvk_swiftshader.dylib`:
|
||||
|
||||
```shell
|
||||
```
|
||||
git clone https://github.com/google/swiftshader.git
|
||||
cd swiftshader/build
|
||||
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
|
||||
everything necessary for building Filament. You can fetch and run the image as follows:
|
||||
|
||||
```shell
|
||||
```
|
||||
docker pull ghcr.io/filament-assets/swiftshader
|
||||
docker run -it ghcr.io/filament-assets/swiftshader
|
||||
```
|
||||
|
||||
@@ -3,14 +3,6 @@
|
||||
# ==================================================================================================
|
||||
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
|
||||
# ==================================================================================================
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ again.
|
||||
|
||||
## Code Style
|
||||
|
||||
See [CODE_STYLE.md](/CODE_STYLE.md)
|
||||
See [CodeStyle.md](/CODE_STYLE.md)
|
||||
|
||||
## Code reviews
|
||||
|
||||
|
||||
@@ -7,3 +7,9 @@ for next branch cut* header.
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## 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`
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.43.0'
|
||||
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:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.43.0'
|
||||
```
|
||||
pod 'Filament', '~> 1.42.0'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -7,28 +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
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## 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
|
||||
|
||||
- Fix potential `EXC_BAD_ACCESS` with Metal backend: b/297059776
|
||||
- `setFrameCompletedCallback` now takes a `backend::CallbackHandler`.
|
||||
|
||||
## v1.42.0
|
||||
|
||||
- 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 polygonOffsetConstant, jfloat polygonOffsetSlope,
|
||||
jboolean screenSpaceContactShadows, jint stepCount,
|
||||
jfloat maxShadowDistance, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius,
|
||||
jfloatArray transform) {
|
||||
jfloat maxShadowDistance, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
|
||||
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
|
||||
LightManager::ShadowOptions shadowOptions {
|
||||
.mapSize = (uint32_t)mapSize,
|
||||
@@ -103,18 +102,12 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
|
||||
},
|
||||
.shadowBulbRadius = shadowBulbRadius
|
||||
};
|
||||
|
||||
jfloat *nativeSplits = env->GetFloatArrayElements(splitPositions, NULL);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
+2
-14
@@ -258,7 +258,6 @@ public class LightManager {
|
||||
* shadows that are too far and wouldn't contribute to the scene much, improving
|
||||
* performance and quality. This value is always positive.
|
||||
* Use 0.0f to use the camera far distance.
|
||||
* This only affect directional lights.
|
||||
*/
|
||||
public float shadowFar = 0.0f;
|
||||
|
||||
@@ -369,17 +368,6 @@ public class LightManager {
|
||||
* enabled. (2cm by default).
|
||||
*/
|
||||
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 {
|
||||
@@ -518,7 +506,7 @@ public class LightManager {
|
||||
options.polygonOffsetConstant, options.polygonOffsetSlope,
|
||||
options.screenSpaceContactShadows,
|
||||
options.stepCount, options.maxShadowDistance,
|
||||
options.elvsm, options.blurWidth, options.shadowBulbRadius, options.transform);
|
||||
options.elvsm, options.blurWidth, options.shadowBulbRadius);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1181,7 +1169,7 @@ public class LightManager {
|
||||
boolean stable, boolean lispsm,
|
||||
float polygonOffsetConstant, float polygonOffsetSlope,
|
||||
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 nBuilderPosition(long nativeBuilder, float x, float y, float z);
|
||||
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);
|
||||
|
||||
@@ -52,7 +52,6 @@ set(GLTFIO_SRCS
|
||||
${GLTFIO_DIR}/include/gltfio/FilamentInstance.h
|
||||
${GLTFIO_DIR}/include/gltfio/MaterialProvider.h
|
||||
${GLTFIO_DIR}/include/gltfio/NodeManager.h
|
||||
${GLTFIO_DIR}/include/gltfio/TrsTransformManager.h
|
||||
${GLTFIO_DIR}/include/gltfio/ResourceLoader.h
|
||||
${GLTFIO_DIR}/include/gltfio/TextureProvider.h
|
||||
${GLTFIO_DIR}/include/gltfio/math.h
|
||||
@@ -70,12 +69,10 @@ set(GLTFIO_SRCS
|
||||
${GLTFIO_DIR}/src/FilamentAsset.cpp
|
||||
${GLTFIO_DIR}/src/FilamentInstance.cpp
|
||||
${GLTFIO_DIR}/src/FNodeManager.h
|
||||
${GLTFIO_DIR}/src/FTrsTransformManager.h
|
||||
${GLTFIO_DIR}/src/GltfEnums.h
|
||||
${GLTFIO_DIR}/src/Ktx2Provider.cpp
|
||||
${GLTFIO_DIR}/src/MaterialProvider.cpp
|
||||
${GLTFIO_DIR}/src/NodeManager.cpp
|
||||
${GLTFIO_DIR}/src/TrsTransformManager.cpp
|
||||
${GLTFIO_DIR}/src/ResourceLoader.cpp
|
||||
${GLTFIO_DIR}/src/StbProvider.cpp
|
||||
${GLTFIO_DIR}/src/TangentsJob.cpp
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.43.0
|
||||
VERSION_NAME=1.42.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -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
|
||||
source tree:
|
||||
|
||||
```shell
|
||||
./build.sh -p desktop -i release
|
||||
./build.sh -p android release
|
||||
```
|
||||
$ ./build.sh -p desktop -i release
|
||||
$ ./build.sh -p android release
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
Example:
|
||||
```shell
|
||||
./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug
|
||||
```
|
||||
$ ./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug
|
||||
```
|
||||
|
||||
## 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
|
||||
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
|
||||
`android` folder. After opening the project and syncing to gradle, select the sample of your choice
|
||||
using the drop-down widget in the toolbar.
|
||||
|
||||
## Compiling
|
||||
|
||||
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"
|
||||
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.
|
||||
|
||||
@@ -210,10 +210,13 @@ function build_desktop_target {
|
||||
echo "Building ${lc_target} in 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:]')
|
||||
if [[ "${lc_name}" == "darwin" ]]; then
|
||||
local deployment_target="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15"
|
||||
|
||||
if [[ "${BUILD_UNIVERSAL_LIBRARIES}" == "true" ]]; then
|
||||
local architectures="-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64"
|
||||
fi
|
||||
@@ -230,6 +233,7 @@ function build_desktop_target {
|
||||
${MATDBG_OPTION} \
|
||||
${MATOPT_OPTION} \
|
||||
${ASAN_UBSAN_OPTION} \
|
||||
${deployment_target} \
|
||||
${architectures} \
|
||||
../..
|
||||
fi
|
||||
@@ -243,13 +247,12 @@ function build_desktop_target {
|
||||
if [[ -d "../${lc_target}/filament" ]]; then
|
||||
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
|
||||
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
|
||||
popd > /dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
popd > /dev/null
|
||||
cd ../..
|
||||
}
|
||||
|
||||
function build_desktop {
|
||||
@@ -267,7 +270,7 @@ function build_webgl_with_target {
|
||||
|
||||
echo "Building 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
|
||||
BUILD_TARGETS=${BUILD_CUSTOM_TARGETS}
|
||||
@@ -304,17 +307,17 @@ function build_webgl_with_target {
|
||||
|
||||
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
|
||||
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 -rvf "../../../filament-${lc_target}-web.tar" filament.wasm
|
||||
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"
|
||||
rm "../filament-${lc_target}-web.tar"
|
||||
fi
|
||||
fi
|
||||
|
||||
popd > /dev/null
|
||||
cd ../..
|
||||
}
|
||||
|
||||
function build_webgl {
|
||||
@@ -345,7 +348,7 @@ function build_android_target {
|
||||
echo "Building 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
|
||||
cmake \
|
||||
@@ -364,7 +367,7 @@ function build_android_target {
|
||||
# We must always install Android libraries to build the AAR
|
||||
${BUILD_COMMAND} install
|
||||
|
||||
popd > /dev/null
|
||||
cd ../..
|
||||
}
|
||||
|
||||
function build_android_arch {
|
||||
@@ -385,9 +388,9 @@ function archive_android {
|
||||
if [[ -d "out/android-${lc_target}/filament" ]]; then
|
||||
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
|
||||
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
|
||||
popd > /dev/null
|
||||
cd ../..
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -465,7 +468,7 @@ function build_android {
|
||||
archive_android "Release"
|
||||
fi
|
||||
|
||||
pushd android > /dev/null
|
||||
cd android
|
||||
|
||||
if [[ "${ISSUE_DEBUG_BUILD}" == "true" ]]; then
|
||||
./gradlew \
|
||||
@@ -566,7 +569,7 @@ function build_android {
|
||||
fi
|
||||
fi
|
||||
|
||||
popd > /dev/null
|
||||
cd ..
|
||||
}
|
||||
|
||||
function build_ios_target {
|
||||
@@ -577,7 +580,7 @@ function build_ios_target {
|
||||
echo "Building iOS ${lc_target} (${arch}) for ${platform}..."
|
||||
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
|
||||
cmake \
|
||||
@@ -601,7 +604,7 @@ function build_ios_target {
|
||||
${BUILD_COMMAND} ${INSTALL_COMMAND}
|
||||
fi
|
||||
|
||||
popd > /dev/null
|
||||
cd ../..
|
||||
}
|
||||
|
||||
function archive_ios {
|
||||
@@ -610,9 +613,9 @@ function archive_ios {
|
||||
if [[ -d "out/ios-${lc_target}/filament" ]]; then
|
||||
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
|
||||
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
|
||||
popd > /dev/null
|
||||
cd ../..
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -671,14 +674,14 @@ function build_web_docs {
|
||||
|
||||
mkdir -p out/web-docs
|
||||
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
|
||||
|
||||
# Generate documents
|
||||
npx markdeep-rasterizer ../../docs/Filament.md.html ../../docs/Materials.md.html ../../docs/
|
||||
|
||||
popd > /dev/null
|
||||
cd ../..
|
||||
}
|
||||
|
||||
function validate_build_command {
|
||||
@@ -751,16 +754,6 @@ function run_tests {
|
||||
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
|
||||
|
||||
pushd "$(dirname "$0")" > /dev/null
|
||||
@@ -769,7 +762,7 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
|
||||
case ${opt} in
|
||||
h)
|
||||
print_help
|
||||
exit 0
|
||||
exit 1
|
||||
;;
|
||||
a)
|
||||
ISSUE_ARCHIVES=true
|
||||
@@ -805,7 +798,7 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
|
||||
platforms=$(echo "${OPTARG}" | tr ',' '\n')
|
||||
for platform in ${platforms}
|
||||
do
|
||||
case $(echo "${platform}" | tr '[:upper:]' '[:lower:]') in
|
||||
case ${platform} in
|
||||
desktop)
|
||||
ISSUE_DESKTOP_BUILD=true
|
||||
;;
|
||||
@@ -824,12 +817,6 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
|
||||
ISSUE_DESKTOP_BUILD=true
|
||||
ISSUE_WEBGL_BUILD=false
|
||||
;;
|
||||
*)
|
||||
echo "Unknown platform ${platform}"
|
||||
echo "Platform must be one of [desktop|android|ios|webgl|all]"
|
||||
echo ""
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
@@ -842,7 +829,7 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
|
||||
abis=$(echo "${OPTARG}" | tr ',' '\n')
|
||||
for abi in ${abis}
|
||||
do
|
||||
case $(echo "${abi}" | tr '[:upper:]' '[:lower:]') in
|
||||
case ${abi} in
|
||||
armeabi-v7a)
|
||||
ABI_ARMEABI_V7A=true
|
||||
;;
|
||||
@@ -861,12 +848,6 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
|
||||
ABI_X86=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
|
||||
done
|
||||
;;
|
||||
@@ -930,9 +911,9 @@ fi
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
for arg; do
|
||||
if [[ $(echo "${arg}" | tr '[:upper:]' '[:lower:]') == "release" ]]; then
|
||||
if [[ "${arg}" == "release" ]]; then
|
||||
ISSUE_RELEASE_BUILD=true
|
||||
elif [[ $(echo "${arg}" | tr '[:upper:]' '[:lower:]') == "debug" ]]; then
|
||||
elif [[ "${arg}" == "debug" ]]; then
|
||||
ISSUE_DEBUG_BUILD=true
|
||||
else
|
||||
BUILD_CUSTOM_TARGETS="${BUILD_CUSTOM_TARGETS} ${arg}"
|
||||
@@ -950,19 +931,19 @@ if [[ "${ISSUE_CLEAN_AGGRESSIVE}" == "true" ]]; then
|
||||
fi
|
||||
|
||||
if [[ "${ISSUE_DESKTOP_BUILD}" == "true" ]]; then
|
||||
check_debug_release_build build_desktop
|
||||
build_desktop
|
||||
fi
|
||||
|
||||
if [[ "${ISSUE_ANDROID_BUILD}" == "true" ]]; then
|
||||
check_debug_release_build build_android
|
||||
build_android
|
||||
fi
|
||||
|
||||
if [[ "${ISSUE_IOS_BUILD}" == "true" ]]; then
|
||||
check_debug_release_build build_ios
|
||||
build_ios
|
||||
fi
|
||||
|
||||
if [[ "${ISSUE_WEBGL_BUILD}" == "true" ]]; then
|
||||
check_debug_release_build build_webgl
|
||||
build_webgl
|
||||
fi
|
||||
|
||||
if [[ "${ISSUE_WEB_DOCS}" == "true" ]]; then
|
||||
|
||||
+8
-8
@@ -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:
|
||||
|
||||
```c++
|
||||
```
|
||||
#include <filament/FilamentAPI.h>
|
||||
#include <filament/Engine.h>
|
||||
|
||||
@@ -91,7 +91,7 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory.
|
||||
|
||||
### Linux
|
||||
|
||||
```make
|
||||
```
|
||||
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl
|
||||
CC=clang++
|
||||
|
||||
@@ -109,7 +109,7 @@ clean:
|
||||
|
||||
### macOS
|
||||
|
||||
```make
|
||||
```
|
||||
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl
|
||||
FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo
|
||||
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
|
||||
used to change the run-time library version.
|
||||
|
||||
```make
|
||||
```
|
||||
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
|
||||
CC=cl.exe
|
||||
@@ -171,12 +171,12 @@ and invoke `nmake` instead of `make`.
|
||||
|
||||
### Generating C++ documentation
|
||||
|
||||
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:
|
||||
|
||||
```shell
|
||||
cd filament/filament
|
||||
doxygen docs/doxygen/filament.doxygen
|
||||
```
|
||||
$ cd filament/filament
|
||||
$ doxygen docs/doxygen/filament.doxygen
|
||||
```
|
||||
|
||||
Finally simply open `docs/html/index.html` in your web browser.
|
||||
|
||||
@@ -412,7 +412,6 @@ if (APPLE)
|
||||
test/test_RenderExternalImage.cpp
|
||||
test/test_StencilBuffer.cpp
|
||||
test/test_Scissor.cpp
|
||||
test/test_MipLevels.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(backend_test PRIVATE
|
||||
|
||||
@@ -813,22 +813,22 @@ struct SamplerParams { // NOLINT
|
||||
struct Hasher {
|
||||
size_t operator()(SamplerParams p) const noexcept {
|
||||
// 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 {
|
||||
bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept {
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
auto* pLhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs == *pRhs;
|
||||
}
|
||||
};
|
||||
|
||||
struct LessThan {
|
||||
bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept {
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
auto* pLhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs == *pRhs;
|
||||
}
|
||||
};
|
||||
@@ -838,7 +838,6 @@ private:
|
||||
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
|
||||
// see android/.../TextureSampler.cpp
|
||||
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
Program& operator=(const Program& rhs) = delete;
|
||||
|
||||
Program(Program&& rhs) noexcept;
|
||||
Program& operator=(Program&& rhs) noexcept = delete;
|
||||
Program& operator=(Program&& rhs) noexcept;
|
||||
|
||||
~Program() noexcept;
|
||||
|
||||
@@ -104,9 +104,8 @@ public:
|
||||
Sampler const* samplers, size_t count) noexcept;
|
||||
|
||||
struct SpecializationConstant {
|
||||
using Type = std::variant<int32_t, float, bool>;
|
||||
uint32_t id; // id set in glsl
|
||||
Type value; // value and type
|
||||
std::variant<int32_t, float, bool> value; // value and type
|
||||
};
|
||||
|
||||
Program& specializationConstants(
|
||||
|
||||
@@ -73,14 +73,32 @@ public:
|
||||
// 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.
|
||||
intptr_t next;
|
||||
driver.mCurrentExecutingCommand = this;
|
||||
mExecute(driver, 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;
|
||||
|
||||
private:
|
||||
Execute mExecute;
|
||||
std::array<intptr_t, 16> mCallstack = {0};
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -218,6 +236,7 @@ public:
|
||||
using Cmd = COMMAND_TYPE(methodName); \
|
||||
void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \
|
||||
new(p) Cmd(mDispatcher.methodName##_, APPLY(std::move, params)); \
|
||||
((Cmd*)p)->captureCallstack(); \
|
||||
DEBUG_COMMAND_END(methodName, false); \
|
||||
}
|
||||
|
||||
@@ -237,6 +256,7 @@ public:
|
||||
using Cmd = COMMAND_TYPE(methodName##R); \
|
||||
void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \
|
||||
new(p) Cmd(mDispatcher.methodName##_, RetType(result), APPLY(std::move, params)); \
|
||||
((Cmd*)p)->captureCallstack(); \
|
||||
DEBUG_COMMAND_END(methodName, false); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ template<typename T>
|
||||
class ConcreteDispatcher;
|
||||
class Dispatcher;
|
||||
class CommandStream;
|
||||
class CommandBase;
|
||||
|
||||
class Driver {
|
||||
public:
|
||||
@@ -83,6 +84,8 @@ public:
|
||||
virtual void debugCommandEnd(CommandStream* cmds,
|
||||
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
|
||||
* so that calling the concrete implementation won't go through a vtable.
|
||||
|
||||
@@ -26,6 +26,17 @@ Program::Program() noexcept { // NOLINT(modernize-use-equals-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::priorityQueue(CompilerPriorityQueue priorityQueue) noexcept {
|
||||
|
||||
@@ -345,7 +345,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
auto colorTexture = handle_cast<MetalTexture>(buffer.handle);
|
||||
ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(),
|
||||
"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 };
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
auto depthTexture = handle_cast<MetalTexture>(depth.handle);
|
||||
ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(),
|
||||
"Depth texture passed to render target has no texture allocation.");
|
||||
depthTexture->extendLodRangeTo(depth.level);
|
||||
depthTexture->updateLodRange(depth.level);
|
||||
depthAttachment = { depthTexture, depth.level, depth.layer };
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
auto stencilTexture = handle_cast<MetalTexture>(stencil.handle);
|
||||
ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(),
|
||||
"Stencil texture passed to render target has no texture allocation.");
|
||||
stencilTexture->extendLodRangeTo(stencil.level);
|
||||
stencilTexture->updateLodRange(stencil.level);
|
||||
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) {
|
||||
auto tex = handle_cast<MetalTexture>(th);
|
||||
tex->setLodRange(minLevel, maxLevel);
|
||||
}
|
||||
|
||||
void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level,
|
||||
@@ -902,13 +900,14 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
|
||||
// 2. LOD-clamped textures
|
||||
//
|
||||
// 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
|
||||
// textures (done in finalizeSamplerGroup).
|
||||
// the argument buffer representing the sampler group. So, we bind what we can now and wait
|
||||
// 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.
|
||||
// A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in
|
||||
// a draw call.
|
||||
// finalizeSamplerGroup has one additional responsibility: to call useResources for all the
|
||||
// textures, which is required by Metal.
|
||||
// Even if we do know all the final textures at this point, we still wait until draw call time
|
||||
// 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++) {
|
||||
if (!samplers[s].t) {
|
||||
// Assign a default texture / sampler to empty slots.
|
||||
@@ -931,6 +930,27 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
|
||||
sb->setFinalizedSampler(s, sampler);
|
||||
|
||||
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));
|
||||
@@ -1356,8 +1376,8 @@ void MetalDriver::blit(TargetBufferFlags buffers,
|
||||
}
|
||||
|
||||
void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
|
||||
// All the id<MTLSamplerState> objects have already been bound to the argument buffer.
|
||||
// Here we bind all the textures.
|
||||
// All of the id<MTLSamplerState> objects have already been bound to the argument buffer.
|
||||
// Here we bind any textures that were unable to be bound in updateSamplerGroup.
|
||||
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);
|
||||
|
||||
|
||||
@@ -217,14 +217,21 @@ public:
|
||||
void generateMipmaps() noexcept;
|
||||
|
||||
// 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 generateMipmaps
|
||||
// - 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.
|
||||
void setLodRange(uint32_t minLevel, uint32_t maxLevel);
|
||||
void extendLodRangeTo(uint32_t level);
|
||||
void updateLodRange(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);
|
||||
|
||||
|
||||
@@ -513,7 +513,7 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
: HwTexture(target, levels, samples, width, height, depth, format, usage), context(context),
|
||||
externalImage(context) {
|
||||
texture = metalTexture;
|
||||
setLodRange(0, levels - 1);
|
||||
updateLodRange(0, levels - 1);
|
||||
}
|
||||
|
||||
MetalTexture::~MetalTexture() {
|
||||
@@ -658,14 +658,14 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
|
||||
}
|
||||
}
|
||||
|
||||
extendLodRangeTo(level);
|
||||
updateLodRange(level);
|
||||
}
|
||||
|
||||
void MetalTexture::generateMipmaps() noexcept {
|
||||
id <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(&context) blitCommandEncoder];
|
||||
[blitEncoder generateMipmapsForTexture:texture];
|
||||
[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,
|
||||
@@ -788,18 +788,18 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
|
||||
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));
|
||||
minLod = std::min(minLod, level);
|
||||
maxLod = std::max(maxLod, level);
|
||||
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(min <= max);
|
||||
minLod = min;
|
||||
maxLod = max;
|
||||
minLod = std::min(minLod, min);
|
||||
maxLod = std::max(maxLod, max);
|
||||
lodTextureView = nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,11 +103,6 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
|
||||
return programData;
|
||||
}
|
||||
|
||||
void wait() const noexcept {
|
||||
std::unique_lock l(lock);
|
||||
cond.wait(l, [this](){ return signaled; });
|
||||
}
|
||||
|
||||
// Checks if the programBinary is ready.
|
||||
// This is similar to std::future::wait_for(0s)
|
||||
bool isReady() const noexcept {
|
||||
@@ -341,23 +336,7 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t&
|
||||
|
||||
token->canceled = true;
|
||||
|
||||
bool canceled = 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);
|
||||
}
|
||||
token->compiler.cancelTickOp(token);
|
||||
|
||||
for (GLuint& shader: token->gl.shaders) {
|
||||
if (shader) {
|
||||
@@ -683,7 +662,7 @@ void ShaderCompilerService::runAtNextTick(CompilerPriorityQueue priority,
|
||||
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.
|
||||
auto& ops = mRunAtNextTickOps;
|
||||
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()) {
|
||||
ops.erase(pos);
|
||||
return true;
|
||||
}
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_VALUE32("ShaderCompilerService Jobs", ops.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShaderCompilerService::executeTickOps() noexcept {
|
||||
|
||||
@@ -141,7 +141,7 @@ private:
|
||||
void runAtNextTick(CompilerPriorityQueue priority,
|
||||
const program_token_t& token, Job job) noexcept;
|
||||
void executeTickOps() noexcept;
|
||||
bool cancelTickOp(program_token_t token) noexcept;
|
||||
void cancelTickOp(program_token_t token) noexcept;
|
||||
// order of insertion is important
|
||||
|
||||
using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>;
|
||||
|
||||
@@ -147,7 +147,7 @@ void VulkanBlitter::blitColor(BlitArgs args) {
|
||||
}
|
||||
#endif
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||
VkCommandBuffer const cmdbuffer = commands.cmdbuffer;
|
||||
commands.acquire(src.texture);
|
||||
commands.acquire(dst.texture);
|
||||
|
||||
@@ -184,7 +184,7 @@ void VulkanBlitter::blitDepth(BlitArgs args) {
|
||||
}
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||
VkCommandBuffer const cmdbuffer = commands.cmdbuffer;
|
||||
commands.acquire(src.texture);
|
||||
commands.acquire(dst.texture);
|
||||
blitFast(cmdbuffer, aspect, args.filter, args.srcTarget->getExtent(), src, dst, args.srcRectPair,
|
||||
@@ -197,11 +197,13 @@ void VulkanBlitter::terminate() noexcept {
|
||||
mDepthResolveProgram = nullptr;
|
||||
|
||||
if (mTriangleBuffer) {
|
||||
mTriangleBuffer->terminate();
|
||||
delete mTriangleBuffer;
|
||||
mTriangleBuffer = nullptr;
|
||||
}
|
||||
|
||||
if (mParamsBuffer) {
|
||||
mParamsBuffer->terminate();
|
||||
delete mParamsBuffer;
|
||||
mParamsBuffer = nullptr;
|
||||
}
|
||||
@@ -255,7 +257,7 @@ void VulkanBlitter::lazyInit() noexcept {
|
||||
};
|
||||
|
||||
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,
|
||||
sizeof(kTriangleVertices));
|
||||
@@ -276,7 +278,7 @@ void VulkanBlitter::blitSlowDepth(VkFilter filter, const VkExtent2D srcExtent, V
|
||||
lazyInit();
|
||||
|
||||
VulkanCommandBuffer* commands = &mCommands->get();
|
||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||
VkCommandBuffer const cmdbuffer = commands->cmdbuffer;
|
||||
commands->acquire(src.texture);
|
||||
commands->acquire(dst.texture);
|
||||
|
||||
|
||||
@@ -45,7 +45,14 @@ VulkanBuffer::VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
}
|
||||
|
||||
VulkanBuffer::~VulkanBuffer() {
|
||||
assert_invariant(mGpuMemory == VK_NULL_HANDLE);
|
||||
assert_invariant(mGpuBuffer == VK_NULL_HANDLE);
|
||||
}
|
||||
|
||||
void VulkanBuffer::terminate() {
|
||||
vmaDestroyBuffer(mAllocator, mGpuBuffer, mGpuMemory);
|
||||
mGpuMemory = VK_NULL_HANDLE;
|
||||
mGpuBuffer = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint32_t byteOffset,
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool, VkBufferUsageFlags usage,
|
||||
uint32_t numBytes);
|
||||
~VulkanBuffer();
|
||||
void terminate();
|
||||
void loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint32_t byteOffset,
|
||||
uint32_t numBytes) const;
|
||||
VkBuffer getGpuBuffer() const {
|
||||
|
||||
@@ -35,38 +35,31 @@ namespace filament::backend {
|
||||
|
||||
using Timestamp = VulkanGroupMarkers::Timestamp;
|
||||
|
||||
VulkanCmdFence::VulkanCmdFence(VkFence ifence)
|
||||
: fence(ifence) {
|
||||
VulkanCmdFence::VulkanCmdFence(VkDevice device, bool signaled) : device(device) {
|
||||
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
|
||||
// submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually finishes
|
||||
// executing the command buffer, the status changes to VK_SUCCESS.
|
||||
status.store(VK_INCOMPLETE);
|
||||
}
|
||||
|
||||
VulkanCommandBuffer::VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device,
|
||||
VkCommandPool pool)
|
||||
: 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);
|
||||
VulkanCmdFence::~VulkanCmdFence() {
|
||||
vkDestroyFence(device, fence, VKALLOC);
|
||||
}
|
||||
|
||||
CommandBufferObserver::~CommandBufferObserver() {}
|
||||
|
||||
static VkCommandPool createPool(VkDevice device, uint32_t queueFamilyIndex) {
|
||||
VkCommandPoolCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.flags =
|
||||
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
|
||||
.queueFamilyIndex = queueFamilyIndex,
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.flags =
|
||||
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
|
||||
.queueFamilyIndex = queueFamilyIndex,
|
||||
};
|
||||
VkCommandPool pool;
|
||||
vkCreateCommandPool(device, &createInfo, VKALLOC, &pool);
|
||||
@@ -113,7 +106,7 @@ std::pair<std::string, Timestamp> VulkanGroupMarkers::top() const {
|
||||
assert_invariant(!empty());
|
||||
auto const marker = mMarkers.back();
|
||||
#if FILAMENT_VULKAN_VERBOSE
|
||||
auto const topTimestamp = mTimestamps.front();
|
||||
auto const topTimestamp = mTimestamps.top();
|
||||
return std::make_pair(marker, topTimestamp);
|
||||
#else
|
||||
return std::make_pair(marker, Timestamp{});
|
||||
@@ -136,13 +129,8 @@ VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFam
|
||||
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) {
|
||||
mStorage[i] = std::make_unique<VulkanCommandBuffer>(allocator, mDevice, mPool);
|
||||
mStorage[i] = std::make_unique<VulkanCommandBuffer>(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,12 +138,9 @@ VulkanCommands::~VulkanCommands() {
|
||||
wait();
|
||||
gc();
|
||||
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
|
||||
for (VkSemaphore sema: mSubmissionSignals) {
|
||||
for (VkSemaphore sema : mSubmissionSignals) {
|
||||
vkDestroySemaphore(mDevice, sema, VKALLOC);
|
||||
}
|
||||
for (VkFence fence: mFences) {
|
||||
vkDestroyFence(mDevice, fence, VKALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
VulkanCommandBuffer& VulkanCommands::get() {
|
||||
@@ -166,11 +151,11 @@ VulkanCommandBuffer& VulkanCommands::get() {
|
||||
// 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
|
||||
// presenting the swap chain or waiting on a fence.
|
||||
while (mAvailableBufferCount == 0) {
|
||||
while (mAvailableCount == 0) {
|
||||
#if VK_REPORT_STALLS
|
||||
slog.i << "VulkanCommands has stalled. "
|
||||
<< "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS."
|
||||
<< io::endl;
|
||||
slog.i << "VulkanCommands has stalled. "
|
||||
<< "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS."
|
||||
<< io::endl;
|
||||
#endif
|
||||
wait();
|
||||
gc();
|
||||
@@ -180,7 +165,7 @@ VulkanCommandBuffer& VulkanCommands::get() {
|
||||
// Find an available slot.
|
||||
for (size_t i = 0; i < CAPACITY; ++i) {
|
||||
auto wrapper = mStorage[i].get();
|
||||
if (wrapper->buffer() == VK_NULL_HANDLE) {
|
||||
if (wrapper->cmdbuffer == VK_NULL_HANDLE) {
|
||||
mCurrentCommandBufferIndex = static_cast<int8_t>(i);
|
||||
currentbuf = wrapper;
|
||||
break;
|
||||
@@ -188,19 +173,28 @@ VulkanCommandBuffer& VulkanCommands::get() {
|
||||
}
|
||||
|
||||
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, ¤tbuf->cmdbuffer);
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
const VkCommandBufferBeginInfo binfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
const VkCommandBufferBeginInfo binfo {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.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.
|
||||
if (mObserver) {
|
||||
@@ -213,6 +207,7 @@ VulkanCommandBuffer& VulkanCommands::get() {
|
||||
auto [marker, time] = mCarriedOverMarkers->pop();
|
||||
pushGroupMarker(marker.c_str(), time);
|
||||
}
|
||||
|
||||
return *currentbuf;
|
||||
}
|
||||
|
||||
@@ -222,24 +217,11 @@ bool VulkanCommands::flush() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Before actually submitting, we need to pop any leftover group markers.
|
||||
// Note that this needs to occur before vkEndCommandBuffer.
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
int8_t const index = mCurrentCommandBufferIndex;
|
||||
VulkanCommandBuffer const* currentbuf = mStorage[index].get();
|
||||
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,
|
||||
// it is sometimes fine to start executing commands anyway, as along as we stall the GPU at the
|
||||
@@ -248,26 +230,24 @@ bool VulkanCommands::flush() {
|
||||
// the only safe option because the previously submitted command buffer might have set up some
|
||||
// state that the new command buffer depends on.
|
||||
VkPipelineStageFlags waitDestStageMasks[2] = {
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
};
|
||||
|
||||
VkSemaphore signals[2] = {
|
||||
VK_NULL_HANDLE,
|
||||
VK_NULL_HANDLE,
|
||||
VK_NULL_HANDLE,
|
||||
VK_NULL_HANDLE,
|
||||
};
|
||||
|
||||
VkCommandBuffer const cmdbuffer = currentbuf->buffer();
|
||||
|
||||
VkSubmitInfo submitInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.waitSemaphoreCount = 0,
|
||||
.pWaitSemaphores = signals,
|
||||
.pWaitDstStageMask = waitDestStageMasks,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &cmdbuffer,
|
||||
.signalSemaphoreCount = 1u,
|
||||
.pSignalSemaphores = &renderingFinished,
|
||||
VkSubmitInfo submitInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.waitSemaphoreCount = 0,
|
||||
.pWaitSemaphores = signals,
|
||||
.pWaitDstStageMask = waitDestStageMasks,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = ¤tbuf->cmdbuffer,
|
||||
.signalSemaphoreCount = 1u,
|
||||
.pSignalSemaphores = &renderingFinished,
|
||||
};
|
||||
|
||||
if (mSubmissionSignal) {
|
||||
@@ -284,13 +264,23 @@ bool VulkanCommands::flush() {
|
||||
}
|
||||
|
||||
#if FILAMENT_VULKAN_VERBOSE
|
||||
slog.i << "Submitting cmdbuffer=" << cmdbuffer
|
||||
<< " wait=(" << signals[0] << ", " << signals[1] << ") "
|
||||
<< " signal=" << renderingFinished
|
||||
<< " fence=" << currentbuf->fence->fence
|
||||
<< utils::io::endl;
|
||||
slog.i << "Submitting cmdbuffer=" << currentbuf->cmdbuffer
|
||||
<< " wait=(" << signals[0] << ", " << signals[1] << ") "
|
||||
<< " signal=" << renderingFinished
|
||||
<< io::endl;
|
||||
#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;
|
||||
std::unique_lock<utils::Mutex> lock(cmdfence->mutex);
|
||||
cmdfence->status.store(VK_NOT_READY);
|
||||
@@ -300,7 +290,7 @@ bool VulkanCommands::flush() {
|
||||
|
||||
#if FILAMENT_VULKAN_VERBOSE
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
assert_invariant(result == VK_SUCCESS);
|
||||
@@ -333,45 +323,44 @@ void VulkanCommands::wait() {
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < CAPACITY; i++) {
|
||||
auto wrapper = mStorage[i].get();
|
||||
if (wrapper->buffer() != VK_NULL_HANDLE
|
||||
if (wrapper->cmdbuffer != VK_NULL_HANDLE
|
||||
&& mCurrentCommandBufferIndex != static_cast<int8_t>(i)) {
|
||||
fences[count++] = wrapper->fence->fence;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
|
||||
updateFences();
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanCommands::gc() {
|
||||
VkFence fences[CAPACITY];
|
||||
VkCommandBuffer buffers[CAPACITY];
|
||||
size_t count = 0;
|
||||
|
||||
for (size_t i = 0; i < CAPACITY; i++) {
|
||||
auto wrapper = mStorage[i].get();
|
||||
if (wrapper->buffer() == VK_NULL_HANDLE) {
|
||||
if (wrapper->cmdbuffer == VK_NULL_HANDLE) {
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
fences[count++] = wrapper->fence->fence;
|
||||
buffers[count++] = wrapper->cmdbuffer;
|
||||
wrapper->cmdbuffer = VK_NULL_HANDLE;
|
||||
wrapper->fence->status.store(VK_SUCCESS);
|
||||
wrapper->reset();
|
||||
mAvailableBufferCount++;
|
||||
wrapper->fence.reset();
|
||||
wrapper->clearResources();
|
||||
++mAvailableCount;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
vkResetFences(mDevice, count, fences);
|
||||
vkFreeCommandBuffers(mDevice, mPool, count, buffers);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanCommands::updateFences() {
|
||||
for (size_t i = 0; i < CAPACITY; i++) {
|
||||
auto wrapper = mStorage[i].get();
|
||||
if (wrapper->buffer() != VK_NULL_HANDLE) {
|
||||
if (wrapper->cmdbuffer != VK_NULL_HANDLE) {
|
||||
VulkanCmdFence* fence = wrapper->fence.get();
|
||||
if (fence) {
|
||||
VkResult status = vkGetFenceStatus(mDevice, fence->fence);
|
||||
@@ -392,7 +381,7 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest
|
||||
#endif
|
||||
|
||||
// TODO: Add group marker color to the Driver API
|
||||
VkCommandBuffer const cmdbuffer = get().buffer();
|
||||
const VkCommandBuffer cmdbuffer = get().cmdbuffer;
|
||||
|
||||
if (!mGroupMarkers) {
|
||||
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
|
||||
@@ -417,19 +406,19 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest
|
||||
}
|
||||
|
||||
void VulkanCommands::popGroupMarker() {
|
||||
assert_invariant(mGroupMarkers);
|
||||
assert_invariant(mGroupMarkers);
|
||||
|
||||
if (!mGroupMarkers->empty()) {
|
||||
VkCommandBuffer const cmdbuffer = get().buffer();
|
||||
#if FILAMENT_VULKAN_VERBOSE
|
||||
auto const [marker, startTime] = mGroupMarkers->pop();
|
||||
auto const endTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> diff = endTime - startTime;
|
||||
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n"
|
||||
<< utils::io::flush;
|
||||
#else
|
||||
mGroupMarkers->pop();
|
||||
#endif
|
||||
const VkCommandBuffer cmdbuffer = get().cmdbuffer;
|
||||
#if FILAMENT_VULKAN_VERBOSE
|
||||
auto const [marker, startTime] = mGroupMarkers->pop();
|
||||
auto const endTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> diff = endTime - startTime;
|
||||
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n"
|
||||
<< utils::io::flush;
|
||||
#else
|
||||
mGroupMarkers->pop();
|
||||
#endif
|
||||
|
||||
if (mContext->isDebugUtilsSupported()) {
|
||||
vkCmdEndDebugUtilsLabelEXT(cmdbuffer);
|
||||
@@ -445,7 +434,7 @@ void VulkanCommands::popGroupMarker() {
|
||||
}
|
||||
|
||||
void VulkanCommands::insertEventMarker(char const* string, uint32_t len) {
|
||||
VkCommandBuffer const cmdbuffer = get().buffer();
|
||||
VkCommandBuffer const cmdbuffer = get().cmdbuffer;
|
||||
if (mContext->isDebugUtilsSupported()) {
|
||||
VkDebugUtilsLabelEXT labelInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
|
||||
@@ -455,9 +444,9 @@ void VulkanCommands::insertEventMarker(char const* string, uint32_t len) {
|
||||
vkCmdInsertDebugUtilsLabelEXT(cmdbuffer, &labelInfo);
|
||||
} else if (mContext->isDebugMarkersSupported()) {
|
||||
VkDebugMarkerMarkerInfoEXT markerInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
|
||||
.pMarkerName = string,
|
||||
.color = {0.0f, 1.0f, 0.0f, 1.0f},
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
|
||||
.pMarkerName = string,
|
||||
.color = {0.0f, 1.0f, 0.0f, 1.0f},
|
||||
};
|
||||
vkCmdDebugMarkerInsertEXT(cmdbuffer, &markerInfo);
|
||||
}
|
||||
|
||||
@@ -58,8 +58,9 @@ private:
|
||||
|
||||
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
|
||||
struct VulkanCmdFence {
|
||||
VulkanCmdFence(VkFence ifence);
|
||||
~VulkanCmdFence() = default;
|
||||
VulkanCmdFence(VkDevice device, bool signaled = false);
|
||||
~VulkanCmdFence();
|
||||
const VkDevice device;
|
||||
VkFence fence;
|
||||
utils::Condition condition;
|
||||
utils::Mutex mutex;
|
||||
@@ -70,10 +71,13 @@ struct VulkanCmdFence {
|
||||
// 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.
|
||||
struct VulkanCommandBuffer {
|
||||
VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device, VkCommandPool pool);
|
||||
VulkanCommandBuffer(VulkanResourceAllocator* allocator)
|
||||
: mResourceManager(allocator) {}
|
||||
|
||||
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
|
||||
VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete;
|
||||
VkCommandBuffer cmdbuffer = VK_NULL_HANDLE;
|
||||
std::shared_ptr<VulkanCmdFence> fence;
|
||||
|
||||
inline void acquire(VulkanResource* resource) {
|
||||
mResourceManager.acquire(resource);
|
||||
@@ -83,23 +87,12 @@ struct VulkanCommandBuffer {
|
||||
mResourceManager.acquire(srcResources);
|
||||
}
|
||||
|
||||
inline void reset() {
|
||||
fence.reset();
|
||||
inline void clearResources() {
|
||||
mResourceManager.clear();
|
||||
}
|
||||
|
||||
inline VkCommandBuffer buffer() const {
|
||||
if (fence) {
|
||||
return mBuffer;
|
||||
}
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
std::shared_ptr<VulkanCmdFence> fence;
|
||||
|
||||
private:
|
||||
VulkanAcquireOnlyResourceManager mResourceManager;
|
||||
VkCommandBuffer mBuffer;
|
||||
};
|
||||
|
||||
// Allows classes to be notified after a new command buffer has been activated.
|
||||
@@ -191,9 +184,8 @@ class VulkanCommands {
|
||||
VkSemaphore mSubmissionSignal = {};
|
||||
VkSemaphore mInjectedSignal = {};
|
||||
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
|
||||
VkFence mFences[CAPACITY] = {};
|
||||
VkSemaphore mSubmissionSignals[CAPACITY] = {};
|
||||
uint8_t mAvailableBufferCount = CAPACITY;
|
||||
size_t mAvailableCount = CAPACITY;
|
||||
CommandBufferObserver* mObserver = nullptr;
|
||||
|
||||
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
|
||||
|
||||
@@ -117,9 +117,8 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
|
||||
VulkanTimerQuery* query) {
|
||||
uint32_t const index = query->getStartingQueryIndex();
|
||||
|
||||
auto const cmdbuffer = commands->buffer();
|
||||
vkCmdResetQueryPool(cmdbuffer, mPool, index, 2);
|
||||
vkCmdWriteTimestamp(cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
|
||||
vkCmdResetQueryPool(commands->cmdbuffer, mPool, index, 2);
|
||||
vkCmdWriteTimestamp(commands->cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
|
||||
|
||||
// We stash this because getResult might come before the query is actually processed.
|
||||
query->setFence(commands->fence);
|
||||
@@ -128,7 +127,7 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
|
||||
void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands,
|
||||
VulkanTimerQuery const* query) {
|
||||
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) {
|
||||
|
||||
@@ -526,22 +526,21 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
<< utils::io::endl;
|
||||
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
|
||||
}
|
||||
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
|
||||
mAllocator, mCommands.get(), mStagePool, nativeWindow, flags);
|
||||
mResourceManager.acquire(swapChain);
|
||||
mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext, mAllocator,
|
||||
mCommands.get(), mStagePool, nativeWindow, flags);
|
||||
}
|
||||
|
||||
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||
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::io::endl;
|
||||
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
|
||||
}
|
||||
assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions.");
|
||||
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
|
||||
mAllocator, mCommands.get(), mStagePool, nullptr, flags, VkExtent2D{width, height});
|
||||
mResourceManager.acquire(swapChain);
|
||||
mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext, mAllocator,
|
||||
mCommands.get(), mStagePool, nullptr, flags, VkExtent2D{width, height});
|
||||
}
|
||||
|
||||
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
@@ -691,15 +690,16 @@ FenceStatus VulkanDriver::getFenceStatus(Handle<HwFence> fh) {
|
||||
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted".
|
||||
// When this fence gets submitted, its status changes to VK_NOT_READY.
|
||||
std::unique_lock<utils::Mutex> lock(cmdfence->mutex);
|
||||
if (cmdfence->status.load() == VK_SUCCESS) {
|
||||
return FenceStatus::CONDITION_SATISFIED;
|
||||
if (cmdfence->status.load() == VK_INCOMPLETE) {
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Two other states are possible:
|
||||
// - VK_INCOMPLETE: the corresponding buffer has not yet been submitted.
|
||||
// - 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;
|
||||
VkResult result =
|
||||
vkWaitForFences(mPlatform->getDevice(), 1, &cmdfence->fence, VK_TRUE, 0);
|
||||
return result == VK_SUCCESS ? FenceStatus::CONDITION_SATISFIED : FenceStatus::TIMEOUT_EXPIRED;
|
||||
}
|
||||
|
||||
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
|
||||
@@ -872,7 +872,7 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
auto ib = mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh);
|
||||
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));
|
||||
}
|
||||
@@ -883,7 +883,7 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript
|
||||
|
||||
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
|
||||
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));
|
||||
}
|
||||
@@ -894,7 +894,7 @@ void VulkanDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> boh,
|
||||
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
|
||||
commands.acquire(bo);
|
||||
// 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);
|
||||
scheduleDestroy(std::move(bd));
|
||||
}
|
||||
@@ -1020,7 +1020,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
// the non-sampling case.
|
||||
bool samplingDepthAttachment = false;
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||
VkCommandBuffer const cmdbuffer = commands.cmdbuffer;
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT;
|
||||
@@ -1246,7 +1246,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
|
||||
void VulkanDriver::endRenderPass(int) {
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
||||
VkCommandBuffer cmdbuffer = commands.cmdbuffer;
|
||||
vkCmdEndRenderPass(cmdbuffer);
|
||||
|
||||
VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
|
||||
@@ -1298,7 +1298,7 @@ void VulkanDriver::nextSubpass(int) {
|
||||
assert_invariant(renderTarget);
|
||||
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,
|
||||
++mCurrentRenderPass.currentSubpass);
|
||||
@@ -1483,7 +1483,7 @@ void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, V
|
||||
void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||
const uint32_t instanceCount) {
|
||||
VulkanCommandBuffer* commands = &mCommands->get();
|
||||
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||
VkCommandBuffer cmdbuffer = commands->cmdbuffer;
|
||||
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
|
||||
|
||||
Handle<HwProgram> programHandle = pipelineState.program;
|
||||
|
||||
@@ -89,26 +89,21 @@ VulkanProgram::VulkanProgram(VkDevice device, const Program& builder) noexcept
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < specializationConstants.size(); i++) {
|
||||
uint32_t const offset = uint32_t(i) * 4;
|
||||
pEntries[i] = {
|
||||
.constantID = specializationConstants[i].id,
|
||||
.offset = offset,
|
||||
// Note that bools are 4-bytes in Vulkan
|
||||
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBool32.html
|
||||
.size = 4,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
const uint32_t offset = uint32_t(i) * 4;
|
||||
std::visit([&](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
pEntries[i] = {
|
||||
.constantID = specializationConstants[i].id,
|
||||
.offset = offset,
|
||||
// Turns out vulkan expects the size of bool to be 4 (verified through
|
||||
// validation layer). So all expected types are of 4 bytes.
|
||||
.size = 4,
|
||||
};
|
||||
T* const addr = (T*)((char*)pData + offset);
|
||||
*addr = arg;
|
||||
}, specializationConstants[i].value);
|
||||
}
|
||||
|
||||
bundle.specializationInfos = pInfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,10 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
|
||||
|
||||
void setBuffer(VulkanBufferObject* bufferObject, uint32_t index);
|
||||
|
||||
inline void terminate() {
|
||||
mResources.clear();
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<VulkanBuffer const*> buffers;
|
||||
|
||||
private:
|
||||
@@ -110,6 +114,9 @@ struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource {
|
||||
buffer(allocator, stagePool, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount),
|
||||
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32) {}
|
||||
|
||||
void terminate() {
|
||||
buffer.terminate();
|
||||
}
|
||||
VulkanBuffer buffer;
|
||||
const VkIndexType indexType;
|
||||
};
|
||||
@@ -117,7 +124,9 @@ struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource {
|
||||
struct VulkanBufferObject : public HwBufferObject, VulkanResource {
|
||||
VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage);
|
||||
|
||||
void terminate() {
|
||||
buffer.terminate();
|
||||
}
|
||||
VulkanBuffer buffer;
|
||||
const BufferObjectBinding bindingType;
|
||||
};
|
||||
|
||||
@@ -166,7 +166,7 @@ bool VulkanPipelineCache::bindDescriptors(VkCommandBuffer cmdbuffer) noexcept {
|
||||
}
|
||||
|
||||
bool VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) noexcept {
|
||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||
VkCommandBuffer const cmdbuffer = commands->cmdbuffer;
|
||||
|
||||
PipelineMap::iterator pipelineIter = mPipelines.find(mPipelineRequirements);
|
||||
|
||||
@@ -678,7 +678,7 @@ void VulkanPipelineCache::terminate() noexcept {
|
||||
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
|
||||
// events since the cache was constructed. If any cache entry was most recently used over
|
||||
// VK_MAX_PIPELINE_AGE flush events in the past, then we can be sure that it is no longer
|
||||
|
||||
@@ -101,6 +101,10 @@ public:
|
||||
template<typename D, typename B>
|
||||
inline void destruct(Handle<B> handle) noexcept {
|
||||
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();
|
||||
mHandleAllocatorImpl.deallocate(handle, obj);
|
||||
}
|
||||
|
||||
@@ -164,10 +164,6 @@ private:
|
||||
public:
|
||||
using const_iterator = FixedSizeArray::const_iterator;
|
||||
|
||||
inline ~FixedCapacityResourceSet() {
|
||||
clear();
|
||||
}
|
||||
|
||||
inline const_iterator begin() {
|
||||
if (mInd == 0) {
|
||||
return mArray.cend();
|
||||
|
||||
@@ -111,7 +111,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
|
||||
assert_invariant(result == VK_SUCCESS);
|
||||
|
||||
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:
|
||||
// "Host access to image memory is only well-defined for linear images and for image
|
||||
|
||||
@@ -79,7 +79,7 @@ void VulkanSwapChain::update() {
|
||||
|
||||
void VulkanSwapChain::present() {
|
||||
if (!mHeadless) {
|
||||
VkCommandBuffer const cmdbuf = mCommands->get().buffer();
|
||||
VkCommandBuffer const cmdbuf = mCommands->get().cmdbuffer;
|
||||
VkImageSubresourceRange const subresources{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
|
||||
@@ -227,7 +227,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VkImageSubresourceRange range = { getImageAspect(), 0, levels, 0, layers };
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuf = commands.buffer();
|
||||
VkCommandBuffer const cmdbuf = commands.cmdbuffer;
|
||||
commands.acquire(this);
|
||||
|
||||
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);
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuf = commands.buffer();
|
||||
VkCommandBuffer const cmdbuf = commands.cmdbuffer;
|
||||
commands.acquire(this);
|
||||
|
||||
VkBufferImageCopy copyRegion = {
|
||||
@@ -343,7 +343,7 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
|
||||
vmaFlushAllocation(mAllocator, stage->memory, 0, hostData.size);
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuf = commands.buffer();
|
||||
VkCommandBuffer const cmdbuf = commands.cmdbuffer;
|
||||
commands.acquire(this);
|
||||
|
||||
// TODO: support blit-based format conversion for 3D images and cubemaps.
|
||||
|
||||
@@ -152,7 +152,7 @@ void printDepthFormats(VkPhysicalDevice device) {
|
||||
}
|
||||
|
||||
ExtensionSet getInstanceExtensions() {
|
||||
std::string_view const TARGET_EXTS[] = {
|
||||
std::array<std::string_view, 4> const TARGET_EXTS = {
|
||||
// Request all cross-platform extensions.
|
||||
VK_KHR_SURFACE_EXTENSION_NAME,
|
||||
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
||||
@@ -160,10 +160,6 @@ ExtensionSet getInstanceExtensions() {
|
||||
// Request these if available.
|
||||
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
||||
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
|
||||
|
||||
#if VK_ENABLE_VALIDATION
|
||||
VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
|
||||
#endif
|
||||
};
|
||||
ExtensionSet exts;
|
||||
FixedCapacityVector<VkExtensionProperties> const availableExts
|
||||
@@ -180,7 +176,7 @@ ExtensionSet getInstanceExtensions() {
|
||||
}
|
||||
|
||||
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_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
|
||||
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
|
||||
@@ -235,10 +231,13 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
|
||||
|
||||
// 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.
|
||||
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];
|
||||
uint32_t enabledExtensionCount = 0;
|
||||
|
||||
#if VK_ENABLE_VALIDATION && defined(__ANDROID__)
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
|
||||
#endif
|
||||
if (validationFeaturesSupported) {
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME;
|
||||
}
|
||||
@@ -513,7 +512,7 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
|
||||
|
||||
VkFormat findSupportedFormat(VkPhysicalDevice device) {
|
||||
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) {
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(device, format, &props);
|
||||
|
||||
@@ -211,5 +211,66 @@ int runTests() {
|
||||
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
|
||||
|
||||
|
||||
@@ -75,6 +75,12 @@ private:
|
||||
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
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
#include "BackendTestUtils.h"
|
||||
|
||||
#include "private/filament/SamplerInterfaceBlock.h"
|
||||
#include "private/backend/SamplerGroup.h"
|
||||
@@ -114,7 +113,91 @@ namespace test {
|
||||
|
||||
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,
|
||||
std::string source) {
|
||||
|
||||
@@ -1,171 +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);
|
||||
}
|
||||
)");
|
||||
|
||||
}
|
||||
|
||||
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 samples a texture.
|
||||
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);
|
||||
auto program = 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::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));
|
||||
}
|
||||
|
||||
api.setMinMaxLevels(texture, 1, 3);
|
||||
|
||||
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 = program;
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = SamplerCompareFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
api.beginFrame(0, 0);
|
||||
|
||||
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 blue.
|
||||
TrianglePrimitive triangle(api);
|
||||
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.finish();
|
||||
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
#include "BackendTestUtils.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <utils/EntityInstance.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
#include <math/quat.h>
|
||||
|
||||
namespace utils {
|
||||
class Entity;
|
||||
@@ -245,7 +244,6 @@ public:
|
||||
* shadows that are too far and wouldn't contribute to the scene much, improving
|
||||
* performance and quality. This value is always positive.
|
||||
* Use 0.0f to use the camera far distance.
|
||||
* This only affect directional lights.
|
||||
*/
|
||||
float shadowFar = 0.0f;
|
||||
|
||||
@@ -362,13 +360,6 @@ public:
|
||||
* enabled. (2cm by default).
|
||||
*/
|
||||
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 {
|
||||
@@ -680,7 +671,7 @@ public:
|
||||
* @return true is this light is a type of directional light
|
||||
*/
|
||||
inline bool isDirectional(Instance i) const noexcept {
|
||||
Type const type = getType(i);
|
||||
Type type = getType(i);
|
||||
return type == Type::DIRECTIONAL || type == Type::SUN;
|
||||
}
|
||||
|
||||
@@ -701,7 +692,7 @@ public:
|
||||
* @return true is this light is a type of spot light
|
||||
*/
|
||||
inline bool isSpotLight(Instance i) const noexcept {
|
||||
Type const type = getType(i);
|
||||
Type type = getType(i);
|
||||
return type == Type::SPOT || type == Type::FOCUSED_SPOT;
|
||||
}
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@ void PerViewUniforms::prepareDirectionalLight(FEngine& engine,
|
||||
float exposure,
|
||||
float3 const& sceneSpaceDirection,
|
||||
PerViewUniforms::LightManagerInstance directionalLight) noexcept {
|
||||
FLightManager const& lcm = engine.getLightManager();
|
||||
FLightManager& lcm = engine.getLightManager();
|
||||
auto& s = mUniforms.edit();
|
||||
|
||||
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
|
||||
float4 sun{ 0.0f, 0.0f, 0.0f, -1.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.
|
||||
float const radius = lcm.getSunAngularRadius(directionalLight);
|
||||
float const haloSize = lcm.getSunHaloSize(directionalLight);
|
||||
float const haloFalloff = lcm.getSunHaloFalloff(directionalLight);
|
||||
float radius = lcm.getSunAngularRadius(directionalLight);
|
||||
float haloSize = lcm.getSunHaloSize(directionalLight);
|
||||
float haloFalloff = lcm.getSunHaloFalloff(directionalLight);
|
||||
sun.x = std::cos(radius);
|
||||
sun.y = std::sin(radius);
|
||||
sun.z = 1.0f / (std::cos(radius * haloSize) - sun.x);
|
||||
|
||||
+75
-126
@@ -110,7 +110,7 @@ math::mat4f ShadowMap::getPointLightViewMatrix(backend::TextureCubemapFace face,
|
||||
}
|
||||
|
||||
ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
FScene::LightSoa const& lightData, size_t index,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
ShadowMapInfo const& shadowMapInfo,
|
||||
SceneInfo const& sceneInfo) noexcept {
|
||||
@@ -131,7 +131,20 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
else params.options.shadowFarHint = dzf * dz + camera.zf;
|
||||
#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
|
||||
@@ -151,7 +164,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
|
||||
// view frustum vertices in world-space
|
||||
float3 wsViewFrustumVertices[8];
|
||||
const mat4f worldToClipMatrix = camera.cullingProjection * camera.view;
|
||||
const mat4f worldToClipMatrix = cullingProjection * camera.view;
|
||||
const Frustum wsFrustum(worldToClipMatrix);
|
||||
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
|
||||
// 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);
|
||||
|
||||
// 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(
|
||||
wsShadowReceiversVolume.getCorners().data(), 8);
|
||||
|
||||
// in stable mode we simply take the view volume bounding sphere, but we calculate it
|
||||
// in view space, so that it's perfectly stable.
|
||||
float3 vertices[8];
|
||||
computeFrustumCorners(vertices, inverse(camera.cullingProjection), sceneInfo.csNearFar);
|
||||
viewVolumeBoundingSphere = computeBoundingSphere(vertices, 8);
|
||||
// in stable mode we simply take the view volume, bounding sphere
|
||||
viewVolumeBoundingSphere = computeBoundingSphere(wsViewFrustumVertices, 8);
|
||||
|
||||
if (shadowReceiverVolumeBoundingSphere.w < viewVolumeBoundingSphere.w) {
|
||||
|
||||
@@ -310,65 +320,45 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
//
|
||||
// In LiPSM mode, we're using the warped space here.
|
||||
|
||||
float2 s, o;
|
||||
Aabb bounds;
|
||||
if (params.options.stable && viewVolumeBoundingSphere.w > 0) {
|
||||
bounds = compute2DBounds(Mv, viewVolumeBoundingSphere);
|
||||
} else {
|
||||
bounds = compute2DBounds(WLMpMv, wsClippedShadowReceiverVolume.data(), vertexCount);
|
||||
}
|
||||
lsLightFrustumBounds.min.xy = bounds.min.xy;
|
||||
lsLightFrustumBounds.max.xy = bounds.max.xy;
|
||||
|
||||
if (params.options.stable) {
|
||||
if (viewVolumeBoundingSphere.w > 0) {
|
||||
s = 1.0f / viewVolumeBoundingSphere.w;
|
||||
o = mat4f::project(LMpMv * camera.model, viewVolumeBoundingSphere.xyz).xy;
|
||||
} else {
|
||||
Aabb const bounds = compute2DBounds(LMpMv,
|
||||
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);
|
||||
}
|
||||
// in stable mode we can't do anything that can change the scaling of the texture
|
||||
} else {
|
||||
Aabb const bounds = compute2DBounds(WLMpMv,
|
||||
wsClippedShadowReceiverVolume.data(), vertexCount);
|
||||
lsLightFrustumBounds.min.xy = bounds.min.xy;
|
||||
lsLightFrustumBounds.max.xy = bounds.max.xy;
|
||||
// For directional lights, we further constraint the light frustum to the
|
||||
// intersection of the shadow casters & shadow receivers in light-space.
|
||||
// ** This relies on the 1-texel shadow map border **
|
||||
if (engine.debug.shadowmap.focus_shadowcasters) {
|
||||
intersectWithShadowCasters(lsLightFrustumBounds, WLMpMv, wsShadowCastersVolume);
|
||||
}
|
||||
if (UTILS_UNLIKELY((lsLightFrustumBounds.min.x >= lsLightFrustumBounds.max.x) ||
|
||||
(lsLightFrustumBounds.min.y >= lsLightFrustumBounds.max.y))) {
|
||||
// this could happen if the only thing visible is a perfectly horizontal or
|
||||
// vertical thin line
|
||||
mHasVisibleShadows = false;
|
||||
return {};
|
||||
}
|
||||
assert_invariant(lsLightFrustumBounds.min.x < lsLightFrustumBounds.max.x);
|
||||
assert_invariant(lsLightFrustumBounds.min.y < lsLightFrustumBounds.max.y);
|
||||
|
||||
s = 2.0f / float2(bounds.max.xy - bounds.min.xy);
|
||||
o = float2(bounds.max.xy + bounds.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 (UTILS_UNLIKELY((lsLightFrustumBounds.min.x >= lsLightFrustumBounds.max.x) ||
|
||||
(lsLightFrustumBounds.min.y >= lsLightFrustumBounds.max.y))) {
|
||||
// this could happen if the only thing visible is a perfectly horizontal or
|
||||
// vertical thin line
|
||||
mHasVisibleShadows = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
assert_invariant(lsLightFrustumBounds.min.x < lsLightFrustumBounds.max.x);
|
||||
assert_invariant(lsLightFrustumBounds.min.y < lsLightFrustumBounds.max.y);
|
||||
|
||||
// compute focus scale and offset
|
||||
float2 s = 2.0f / float2(lsLightFrustumBounds.max.xy - lsLightFrustumBounds.min.xy);
|
||||
float2 o = -s * float2(lsLightFrustumBounds.max.xy + lsLightFrustumBounds.min.xy) * 0.5f;
|
||||
|
||||
if (params.options.stable) {
|
||||
snapLightFrustum(s, o, LMpMv,
|
||||
sceneInfo.wsShadowCastersVolume.center(), shadowMapInfo.shadowDimension);
|
||||
// Use the world origin as reference point, fixed w.r.t. the camera
|
||||
snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz,
|
||||
1.0f / float(shadowMapInfo.shadowDimension));
|
||||
}
|
||||
|
||||
const mat4f F(mat4f::row_major_init {
|
||||
@@ -387,7 +377,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
|
||||
// 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
|
||||
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);
|
||||
|
||||
ShadowMap::ShaderParameters shaderParameters;
|
||||
@@ -409,7 +400,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
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
|
||||
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
|
||||
@@ -442,8 +433,9 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual(
|
||||
assert_invariant(shadowMapInfo.textureDimension == mOptions->mapSize);
|
||||
|
||||
// Final shadow transform
|
||||
const backend::Viewport viewport = getViewport();
|
||||
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);
|
||||
|
||||
// TODO: focus projection
|
||||
@@ -470,7 +462,7 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual(
|
||||
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
|
||||
}
|
||||
|
||||
shaderParameters.scissorNormalized = getClampToEdgeCoords(shadowMapInfo);
|
||||
shaderParameters.scissorNormalized = getViewportNormalized(shadowMapInfo);
|
||||
|
||||
const float3 direction = -transpose(Mv)[2].xyz;
|
||||
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
@@ -833,39 +825,21 @@ void ShadowMap::computeFrustumCorners(float3* UTILS_RESTRICT out,
|
||||
}
|
||||
|
||||
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 {
|
||||
// for directional light p.w == 1, exactly
|
||||
auto p = m * v;
|
||||
assert_invariant(p.w == 1.0);
|
||||
return p.xyz;
|
||||
auto fmod = [](float2 x, float2 y) -> float2 {
|
||||
auto mod = [](float x, float y) -> float { return std::fmod(x, y); };
|
||||
return float2{ mod(x[0], y[0]), mod(x[1], y[1]) };
|
||||
};
|
||||
|
||||
auto fract = [](auto v) {
|
||||
using namespace std;
|
||||
using T = decltype(v);
|
||||
return fmod(v, T{1});
|
||||
};
|
||||
|
||||
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 snaps the shadow map bounds to texels.
|
||||
// The 2.0 comes from Mv having a NDC in the range -1,1 (so a range of 2).
|
||||
const float2 r = 2.0f * shadowMapResolution;
|
||||
o -= fmod(o, r);
|
||||
|
||||
// This offsets the texture coordinates, so it has a fixed offset w.r.t the world
|
||||
double2 const lsOrigin = proj(FMv, wsSnapCoords).xy;
|
||||
double2 const d = (fract(lsOrigin * resolution * 0.5) * 2.0) / resolution;
|
||||
|
||||
// adjust offset
|
||||
o -= d;
|
||||
const float2 lsOrigin = mat4f::project(Mv, worldOrigin).xy * s;
|
||||
o -= fmod(lsOrigin, r);
|
||||
}
|
||||
|
||||
size_t ShadowMap::intersectFrustumWithBox(
|
||||
@@ -1068,7 +1042,7 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p,
|
||||
}
|
||||
|
||||
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
|
||||
// orthographic projections. We just need to inverse worldToShadowTexture,
|
||||
// which is guaranteed to be orthographic.
|
||||
@@ -1083,7 +1057,7 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture,
|
||||
}
|
||||
|
||||
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).
|
||||
// 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.
|
||||
@@ -1104,7 +1078,7 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF,
|
||||
|
||||
constexpr bool JACOBIAN_ESTIMATE = false;
|
||||
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.
|
||||
const mat4f Si(inverse(MbMtF * Wp));
|
||||
const float3 p0 = mat4f::project(Si, p);
|
||||
@@ -1243,58 +1217,33 @@ void ShadowMap::updateSceneInfoSpot(mat4f const& Mv, FScene const& scene,
|
||||
}
|
||||
|
||||
backend::Viewport ShadowMap::getViewport() const noexcept {
|
||||
// We set a viewport with a 1-texel border for when we index outside the texture.
|
||||
// This happens only for directional lights 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).
|
||||
// We set a viewport with a 1-texel border for when we index outside the
|
||||
// texture. This can only happen for the directional light when "focus shadow casters is used".
|
||||
const uint32_t dim = mOptions->mapSize;
|
||||
const uint16_t border = 1u;
|
||||
return { border, border, dim - 2u * border, dim - 2u * border };
|
||||
}
|
||||
|
||||
backend::Viewport ShadowMap::getScissor() const noexcept {
|
||||
// We set a viewport with a 1-texel border for when we index outside the texture.
|
||||
// This happens only for directional lights 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.
|
||||
// We set a viewport with a 1-texel border for when we index outside the
|
||||
// texture. This can only happen for the directional light when "focus shadow casters is used".
|
||||
const uint32_t dim = mOptions->mapSize;
|
||||
const uint16_t border = 1u;
|
||||
|
||||
switch (mShadowType) {
|
||||
case ShadowType::DIRECTIONAL:
|
||||
return { border, border, dim - 2u * border, dim - 2u * border };
|
||||
case ShadowType::SPOT:
|
||||
case ShadowType::POINT:
|
||||
default:
|
||||
return { 0, 0, dim, dim };
|
||||
}
|
||||
}
|
||||
|
||||
math::float4 ShadowMap::getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept {
|
||||
float border; // shadowmap border in texels
|
||||
switch (mShadowType) {
|
||||
case ShadowType::DIRECTIONAL:
|
||||
// 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;
|
||||
math::float4 ShadowMap::getViewportNormalized(ShadowMapInfo const& shadowMapInfo) const noexcept {
|
||||
const auto [l, b, w, h] = getViewport();
|
||||
const float texel = 1.0f / float(shadowMapInfo.atlasDimension);
|
||||
const float4 v = float4{ l, b, l + w, b + h } * texel;
|
||||
if (shadowMapInfo.textureSpaceFlipped) {
|
||||
// this is equivalent to calling uvToRenderTargetUV() in the shader *after* clamping
|
||||
// texture coordinates to this normalized viewport.
|
||||
|
||||
@@ -222,7 +222,7 @@ private:
|
||||
const math::float3& dir);
|
||||
|
||||
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,
|
||||
const math::mat4f& projectionViewInverse, math::float2 csNearFar = { -1.0f, 1.0f }) noexcept;
|
||||
@@ -281,13 +281,13 @@ private:
|
||||
static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf,
|
||||
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,
|
||||
uint16_t shadowDimension) noexcept;
|
||||
float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
|
||||
uint16_t shadowDimension) const noexcept;
|
||||
|
||||
static float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
|
||||
uint16_t shadowDimension) noexcept;
|
||||
float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
|
||||
uint16_t shadowDimension) const noexcept;
|
||||
|
||||
static constexpr const Segment sBoxSegments[12] = {
|
||||
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
|
||||
|
||||
@@ -435,9 +435,8 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
}
|
||||
|
||||
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* scene = view.getScene();
|
||||
auto& lcm = engine.getLightManager();
|
||||
|
||||
@@ -445,24 +444,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
FLightManager::ShadowOptions const& options = lcm.getShadowOptions(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{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.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.
|
||||
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.
|
||||
// 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];
|
||||
}
|
||||
|
||||
const CascadeSplits splits({
|
||||
const CascadeSplits::Params p{
|
||||
.proj = cameraInfo.cullingProjection,
|
||||
.near = vsNear,
|
||||
.far = vsFar,
|
||||
.cascadeCount = cascadeCount,
|
||||
.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
|
||||
// (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;
|
||||
|
||||
// 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
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
|
||||
private:
|
||||
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;
|
||||
|
||||
ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine,
|
||||
@@ -145,8 +145,17 @@ private:
|
||||
float far = 0.0f;
|
||||
size_t cascadeCount = 1;
|
||||
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;
|
||||
|
||||
// Split positions in world-space.
|
||||
@@ -177,6 +186,9 @@ private:
|
||||
|
||||
SoftShadowOptions mSoftShadowOptions;
|
||||
|
||||
CascadeSplits::Params mCascadeSplitParams;
|
||||
CascadeSplits mCascadeSplits;
|
||||
|
||||
mutable TypedUniformBuffer<ShadowUib> mShadowUb;
|
||||
backend::Handle<backend::HwBufferObject> mShadowUbh;
|
||||
|
||||
@@ -192,8 +204,8 @@ private:
|
||||
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
|
||||
CONFIG_MAX_SHADOWMAPS - CONFIG_MAX_SHADOW_CASCADES) };
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
// Each ShadowMap is currently 40 bytes (total of 2.5KB for 64 shadow maps)
|
||||
using ShadowMapStorage = std::aligned_storage<sizeof(ShadowMap), alignof(ShadowMap)>::type;
|
||||
std::array<ShadowMapStorage, CONFIG_MAX_SHADOWMAPS> mShadowMapCache;
|
||||
|
||||
@@ -127,12 +127,12 @@ public:
|
||||
}
|
||||
|
||||
bool isSpotLight(Instance i) const noexcept {
|
||||
Type const type = getType(i);
|
||||
Type type = getType(i);
|
||||
return type == Type::FOCUSED_SPOT || type == Type::SPOT;
|
||||
}
|
||||
|
||||
bool isDirectionalLight(Instance i) const noexcept {
|
||||
Type const type = getType(i);
|
||||
Type type = getType(i);
|
||||
return type == Type::DIRECTIONAL || type == Type::SUN;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.43.0"
|
||||
spec.version = "1.42.0"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
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.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.43.0/filament-v1.43.0-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.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#ifndef TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H
|
||||
#define TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#ifndef TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H
|
||||
#define TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace filament {
|
||||
|
||||
// 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
|
||||
|
||||
@@ -35,7 +35,7 @@ for example, `filament-20181009-linux.tgz`.
|
||||
|
||||
Create a file, `main.cpp`, in the same directory with the following contents:
|
||||
|
||||
```c++
|
||||
```
|
||||
#include <filamat/MaterialBuilder.h>
|
||||
|
||||
#include <iostream>
|
||||
@@ -85,7 +85,7 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory.
|
||||
|
||||
### Linux
|
||||
|
||||
```make
|
||||
```
|
||||
FILAMENT_LIBS=-lfilamat -lfilabridge -lshaders -lutils -lsmol-v
|
||||
CC=clang++
|
||||
|
||||
@@ -103,7 +103,7 @@ clean:
|
||||
|
||||
### macOS
|
||||
|
||||
```make
|
||||
```
|
||||
FILAMENT_LIBS=-lfilamat -lfilabridge -lshaders -lutils -lsmol-v
|
||||
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
|
||||
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 \
|
||||
lib/x86_64/mt/utils.lib lib/x86_64/mt/smol-v.lib
|
||||
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
|
||||
material with Filament, pass the material package's data into a Filament Material builder:
|
||||
|
||||
```c++
|
||||
```
|
||||
Package package = builder.build();
|
||||
filament::Material* myMaterial = Material::Builder()
|
||||
.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
|
||||
the name `material`.
|
||||
|
||||
```glsl
|
||||
```
|
||||
void anotherFunction(inout MaterialInputs m) {
|
||||
// Incorrect! The MaterialInputs is being referred to by the name "m".
|
||||
m.metallic = 0.0;
|
||||
|
||||
@@ -16,7 +16,6 @@ set(PUBLIC_HDRS
|
||||
include/gltfio/FilamentInstance.h
|
||||
include/gltfio/MaterialProvider.h
|
||||
include/gltfio/NodeManager.h
|
||||
include/gltfio/TrsTransformManager.h
|
||||
include/gltfio/ResourceLoader.h
|
||||
include/gltfio/TextureProvider.h
|
||||
include/gltfio/math.h
|
||||
@@ -36,12 +35,10 @@ set(SRCS
|
||||
src/FilamentAsset.cpp
|
||||
src/FilamentInstance.cpp
|
||||
src/FNodeManager.h
|
||||
src/FTrsTransformManager.h
|
||||
src/GltfEnums.h
|
||||
src/Ktx2Provider.cpp
|
||||
src/MaterialProvider.cpp
|
||||
src/NodeManager.cpp
|
||||
src/TrsTransformManager.cpp
|
||||
src/ResourceLoader.cpp
|
||||
src/StbProvider.cpp
|
||||
src/TangentsJob.cpp
|
||||
@@ -189,7 +186,8 @@ if (NOT WEBGL AND NOT ANDROID AND NOT IOS)
|
||||
# ==================================================================================================
|
||||
# Compiler flags
|
||||
# ==================================================================================================
|
||||
if (NOT MSVC)
|
||||
if (MSVC)
|
||||
else()
|
||||
target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register)
|
||||
endif()
|
||||
|
||||
@@ -227,9 +225,7 @@ if (TNT_DEV AND NOT WEBGL AND NOT ANDROID AND NOT IOS)
|
||||
add_dependencies(${TEST_TARGET} test_gltfio_files)
|
||||
|
||||
target_link_libraries(${TEST_TARGET} PRIVATE ${TARGET} filament filabridge gtest uberarchive)
|
||||
if (NOT MSVC)
|
||||
target_compile_options(${TEST_TARGET} PRIVATE -Wno-deprecated-register)
|
||||
endif()
|
||||
target_compile_options(${TEST_TARGET} PRIVATE -Wno-deprecated-register)
|
||||
set_target_properties(${TEST_TARGET} PROPERTIES FOLDER Tests)
|
||||
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
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "FFilamentAsset.h"
|
||||
#include "FFilamentInstance.h"
|
||||
#include "FTrsTransformManager.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include <filament/VertexBuffer.h>
|
||||
@@ -75,7 +74,6 @@ struct AnimatorImpl {
|
||||
FFilamentInstance* instance = nullptr;
|
||||
RenderableManager* renderableManager;
|
||||
TransformManager* transformManager;
|
||||
TrsTransformManager* trsTransformManager;
|
||||
vector<float> weights;
|
||||
FixedCapacityVector<mat4f> crossFade;
|
||||
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->renderableManager = &asset->mEngine->getRenderableManager();
|
||||
mImpl->transformManager = &asset->mEngine->getTransformManager();
|
||||
mImpl->trsTransformManager = asset->getTrsTransformManager();
|
||||
|
||||
const cgltf_data* srcAsset = asset->mSourceAsset->hierarchy;
|
||||
const cgltf_animation* srcAnims = srcAsset->animations;
|
||||
@@ -432,13 +429,20 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
|
||||
size_t nextIndex) {
|
||||
const Sampler* sampler = channel.sourceData;
|
||||
const TimeValues& times = sampler->times;
|
||||
TrsTransformManager::Instance trsNode = trsTransformManager->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) {
|
||||
|
||||
case Channel::SCALE: {
|
||||
float3 scale;
|
||||
const float3* srcVec3 = (const float3*) sampler->values.data();
|
||||
if (sampler->interpolation == Sampler::CUBIC) {
|
||||
float3 vert0 = srcVec3[prevIndex * 3 + 1];
|
||||
@@ -449,12 +453,10 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
|
||||
} else {
|
||||
scale = ((1 - t) * srcVec3[prevIndex]) + (t * srcVec3[nextIndex]);
|
||||
}
|
||||
trsTransformManager->setScale(trsNode, scale);
|
||||
break;
|
||||
}
|
||||
|
||||
case Channel::TRANSLATION: {
|
||||
float3 translation;
|
||||
const float3* srcVec3 = (const float3*) sampler->values.data();
|
||||
if (sampler->interpolation == Sampler::CUBIC) {
|
||||
float3 vert0 = srcVec3[prevIndex * 3 + 1];
|
||||
@@ -465,12 +467,10 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
|
||||
} else {
|
||||
translation = ((1 - t) * srcVec3[prevIndex]) + (t * srcVec3[nextIndex]);
|
||||
}
|
||||
trsTransformManager->setTranslation(trsNode, translation);
|
||||
break;
|
||||
}
|
||||
|
||||
case Channel::ROTATION: {
|
||||
quatf rotation;
|
||||
const quatf* srcQuat = (const quatf*) sampler->values.data();
|
||||
if (sampler->interpolation == Sampler::CUBIC) {
|
||||
quatf vert0 = srcQuat[prevIndex * 3 + 1];
|
||||
@@ -481,7 +481,6 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
|
||||
} else {
|
||||
rotation = slerp(srcQuat[prevIndex], srcQuat[nextIndex], t);
|
||||
}
|
||||
trsTransformManager->setRotation(trsNode, rotation);
|
||||
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) {
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include "FFilamentAsset.h"
|
||||
#include "FNodeManager.h"
|
||||
#include "FTrsTransformManager.h"
|
||||
#include "GltfEnums.h"
|
||||
|
||||
#include <filament/Box.h>
|
||||
@@ -296,7 +295,6 @@ public:
|
||||
MaterialProvider& mMaterials;
|
||||
Engine& mEngine;
|
||||
FNodeManager mNodeManager;
|
||||
FTrsTransformManager mTrsTransformManager;
|
||||
|
||||
// Transient state used only for the asset currently being loaded:
|
||||
FFilamentAsset* mAsset;
|
||||
@@ -402,8 +400,7 @@ void FAssetLoader::createRootAsset(const cgltf_data* srcAsset) {
|
||||
#endif
|
||||
|
||||
mDummyBufferObject = nullptr;
|
||||
mAsset = new FFilamentAsset(&mEngine, mNameManager, &mEntityManager, &mNodeManager,
|
||||
&mTrsTransformManager, srcAsset);
|
||||
mAsset = new FFilamentAsset(&mEngine, mNameManager, &mEntityManager, &mNodeManager, srcAsset);
|
||||
|
||||
// It is not an error for a glTF file to have zero scenes.
|
||||
mAsset->mScenes.clear();
|
||||
@@ -566,9 +563,7 @@ void FAssetLoader::recurseEntities(const cgltf_data* srcAsset, const cgltf_node*
|
||||
quatf* rotation = (quatf*) &node->rotation[0];
|
||||
float3* scale = (float3*) &node->scale[0];
|
||||
float3* translation = (float3*) &node->translation[0];
|
||||
mTrsTransformManager.create(entity, *translation, *rotation, *scale);
|
||||
localTransform = mTrsTransformManager.getTransform(
|
||||
mTrsTransformManager.getInstance(entity));
|
||||
localTransform = composeMatrix(*translation, *rotation, *scale);
|
||||
}
|
||||
|
||||
auto parentTransform = mTransformManager.getInstance(parent);
|
||||
@@ -1472,9 +1467,8 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsse
|
||||
}
|
||||
}
|
||||
if (matkey.hasSheenRoughnessTexture) {
|
||||
bool sameTexture = shConfig.sheen_color_texture.texture == shConfig.sheen_roughness_texture.texture;
|
||||
mAsset->addTextureBinding(mi, "sheenRoughnessMap",
|
||||
shConfig.sheen_roughness_texture.texture, sameTexture ? sRGB : LINEAR);
|
||||
shConfig.sheen_roughness_texture.texture, LINEAR);
|
||||
if (matkey.hasTextureTransforms) {
|
||||
const cgltf_texture_transform& uvt = shConfig.sheen_roughness_texture.transform;
|
||||
auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale);
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
#include <gltfio/NodeManager.h>
|
||||
#include <gltfio/TrsTransformManager.h>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
@@ -111,9 +110,9 @@ using MeshCache = utils::FixedCapacityVector<utils::FixedCapacityVector<Primitiv
|
||||
struct FFilamentAsset : public FilamentAsset {
|
||||
FFilamentAsset(Engine* engine, utils::NameComponentManager* names,
|
||||
utils::EntityManager* entityManager, NodeManager* nodeManager,
|
||||
TrsTransformManager* trsTransformManager, const cgltf_data* srcAsset) :
|
||||
const cgltf_data* srcAsset) :
|
||||
mEngine(engine), mNameManager(names), mEntityManager(entityManager),
|
||||
mNodeManager(nodeManager), mTrsTransformManager(trsTransformManager),
|
||||
mNodeManager(nodeManager),
|
||||
mSourceAsset(new SourceAsset {(cgltf_data*)srcAsset}),
|
||||
mTextures(srcAsset->textures_count),
|
||||
mMeshCache(srcAsset->meshes_count) {}
|
||||
@@ -196,10 +195,6 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
return mEngine;
|
||||
}
|
||||
|
||||
TrsTransformManager* getTrsTransformManager() const noexcept {
|
||||
return mTrsTransformManager;
|
||||
}
|
||||
|
||||
void releaseSourceData() noexcept;
|
||||
|
||||
const void* getSourceAsset() const noexcept {
|
||||
@@ -247,7 +242,6 @@ struct FFilamentAsset : public FilamentAsset {
|
||||
utils::NameComponentManager* const mNameManager;
|
||||
utils::EntityManager* const mEntityManager;
|
||||
NodeManager* const mNodeManager;
|
||||
TrsTransformManager* const mTrsTransformManager;
|
||||
std::vector<utils::Entity> mEntities; // sorted such that renderables come first
|
||||
std::vector<utils::Entity> mLightEntities;
|
||||
std::vector<utils::Entity> mCameraEntities;
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
// then destroy the actual entities. This includes instances.
|
||||
if (!mDetachedFilamentComponents) {
|
||||
|
||||
@@ -287,7 +287,7 @@ std::string shaderFromKey(const MaterialKey& config) {
|
||||
|
||||
// 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
|
||||
float scale = getObjectUserData();
|
||||
float scale = object_uniforms.userData;
|
||||
material.thickness = materialParams.volumeThicknessFactor * scale;
|
||||
)SHADER";
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -25,7 +25,10 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace filament::math::details {
|
||||
namespace filament {
|
||||
namespace math {
|
||||
namespace details {
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* No user serviceable parts here.
|
||||
@@ -46,7 +49,7 @@ namespace filament::math::details {
|
||||
template<template<typename T> class QUATERNION, typename T>
|
||||
class TQuatProductOperators {
|
||||
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.
|
||||
*/
|
||||
template<typename OTHER>
|
||||
@@ -82,7 +85,7 @@ public:
|
||||
* (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.
|
||||
*/
|
||||
template<typename RT>
|
||||
@@ -124,19 +127,19 @@ public:
|
||||
*/
|
||||
friend inline
|
||||
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;
|
||||
}
|
||||
|
||||
friend inline
|
||||
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;
|
||||
}
|
||||
|
||||
friend inline
|
||||
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;
|
||||
}
|
||||
};
|
||||
@@ -280,6 +283,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace filament::math::details
|
||||
// -------------------------------------------------------------------------------------
|
||||
} // namespace details
|
||||
} // namespace math
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_MATH_TQUATHELPERS_H
|
||||
|
||||
@@ -443,37 +443,6 @@ private:
|
||||
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) {
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
v[i] = T(1) / std::sqrt(v[i]);
|
||||
|
||||
@@ -160,7 +160,7 @@ constexpr inline half makeHalf(uint16_t bits) noexcept {
|
||||
|
||||
#endif // __ARM_NEON
|
||||
|
||||
inline constexpr half operator""_h(long double v) {
|
||||
inline constexpr half operator "" _h(long double v) {
|
||||
return half( static_cast<float>(v) );
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::math {
|
||||
namespace filament {
|
||||
namespace math {
|
||||
namespace details {
|
||||
|
||||
template<typename T> class TVec2;
|
||||
@@ -44,8 +45,6 @@ template<typename T> class TMat22;
|
||||
template<typename T> class TMat33;
|
||||
template<typename T> class TMat44;
|
||||
|
||||
template<typename T> class TQuaternion;
|
||||
|
||||
} // namespace details
|
||||
|
||||
using double2 = details::TVec2<double>;
|
||||
@@ -87,10 +86,8 @@ using mat3f = details::TMat33<float>;
|
||||
using mat4 = details::TMat44<double>;
|
||||
using mat4f = details::TMat44<float>;
|
||||
|
||||
using quat = details::TQuaternion<double>;
|
||||
using quatf = details::TQuaternion<float>;
|
||||
|
||||
} // namespace filament::math
|
||||
} // namespace math
|
||||
} // namespace filament
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace filament::math {
|
||||
namespace filament {
|
||||
namespace math {
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
namespace details {
|
||||
|
||||
template<typename T>
|
||||
@@ -86,15 +89,15 @@ public:
|
||||
|
||||
// constructors
|
||||
|
||||
// Leaves object uninitialized. Use with caution.
|
||||
// leaves object uninitialized. use with caution.
|
||||
explicit constexpr TQuaternion(no_init) {}
|
||||
|
||||
// default constructor. sets all values to zero.
|
||||
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>>
|
||||
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
|
||||
template<typename A, typename B, typename C, typename D,
|
||||
@@ -172,29 +175,31 @@ typedef details::TQuaternion<float> quatf;
|
||||
typedef details::TQuaternion<half> quath;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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
|
||||
|
||||
@@ -36,6 +36,13 @@ public:
|
||||
* @see CallStack::capture()
|
||||
*/
|
||||
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() = default;
|
||||
|
||||
@@ -114,12 +121,10 @@ private:
|
||||
|
||||
static constexpr size_t NUM_FRAMES = 20;
|
||||
|
||||
struct StackFrameInfo {
|
||||
intptr_t pc;
|
||||
};
|
||||
using StackFrameInfo = intptr_t;
|
||||
|
||||
size_t m_frame_count = 0;
|
||||
StackFrameInfo m_stack[NUM_FRAMES];
|
||||
StackFrameInfo m_stack[NUM_FRAMES] = {0};
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
@@ -70,7 +70,7 @@ intptr_t CallStack::operator[](size_t index) const {
|
||||
#endif
|
||||
std::abort();
|
||||
}
|
||||
return m_stack[index].pc;
|
||||
return m_stack[index];
|
||||
}
|
||||
|
||||
size_t CallStack::getFrameCount() const noexcept {
|
||||
@@ -91,7 +91,7 @@ void CallStack::update_gcc(size_t ignore) noexcept {
|
||||
size -= ignore;
|
||||
#endif
|
||||
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
|
||||
|
||||
|
||||
@@ -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,
|
||||
LightManager::ShadowOptions* out) {
|
||||
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
|
||||
int const size = tokens[i++].size;
|
||||
int size = tokens[i++].size;
|
||||
math::float3 splitsVector;
|
||||
for (int j = 0; j < size; ++j) {
|
||||
const jsmntok_t tok = tokens[i];
|
||||
CHECK_KEY(tok);
|
||||
if (compare(tok, jsonChunk, "mapSize") == 0) {
|
||||
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) {
|
||||
i = parse(tokens, i + 1, jsonChunk, &out->shadowCascades);
|
||||
} 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[1] = splitsVector[1];
|
||||
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) {
|
||||
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 {
|
||||
slog.w << "Invalid shadow options key: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
||||
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 }));
|
||||
}
|
||||
for (size_t i = 0; i < sceneLightCount; i++) {
|
||||
auto const li = lm->getInstance(sceneLights[i]);
|
||||
lm->setShadowCaster(li, settings.enableShadows);
|
||||
lm->setShadowOptions(li, settings.shadowOptions);
|
||||
light = lm->getInstance(sceneLights[i]);
|
||||
if (lm->isSpotLight(light)) {
|
||||
lm->setShadowCaster(light, settings.enableShadows);
|
||||
}
|
||||
lm->setShadowOptions(light, settings.shadowOptions);
|
||||
}
|
||||
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) {
|
||||
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"
|
||||
<< "\"vsm\": {\n"
|
||||
<< "\"elvsm\": " << to_string(in.vsm.elvsm) << ",\n"
|
||||
<< "\"blurWidth\": " << in.vsm.blurWidth << "\n"
|
||||
<< "},\n"
|
||||
<< "\"mapSize\": " << in.mapSize << ",\n"
|
||||
<< "\"shadowCascades\": " << int(in.shadowCascades) << ",\n"
|
||||
<< "\"cascadeSplitPositions\": " << (splitsVector) << "\n"
|
||||
<< "\"stable\": " << to_string(in.stable) << ",\n"
|
||||
<< "\"lispsm\": " << to_string(in.lispsm) << ",\n"
|
||||
<< "\"screenSpaceContactShadows\": " << to_string(in.screenSpaceContactShadows) << ",\n"
|
||||
<< "\"shadowBulbRadius\": " << in.shadowBulbRadius << ",\n"
|
||||
<< "\"transform\": " << in.transform.xyzw << ",\n"
|
||||
<< "\"shadowCascades\": " << int(in.shadowCascades) << ",\n"
|
||||
<< "\"cascadeSplitPositions\": " << (splitsVector) << "\n"
|
||||
<< "}";
|
||||
}
|
||||
|
||||
|
||||
@@ -853,14 +853,6 @@ void ViewerGui::updateUserInterface() {
|
||||
ImGui::SliderFloat("Halo falloff", &light.sunlightHaloFalloff, 4.0f, 1024.0f);
|
||||
ImGui::SliderFloat("Sun radius", &light.sunlightAngularRadius, 0.1f, 10.0f);
|
||||
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")) {
|
||||
ImGui::Checkbox("Enable shadows", &light.enableShadows);
|
||||
|
||||
@@ -101,7 +101,6 @@ struct App {
|
||||
|
||||
bool actualSize = false;
|
||||
bool originIsFarAway = false;
|
||||
float originDistance = 6378137; // Earth's radius in [m]
|
||||
|
||||
struct Scene {
|
||||
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("Camera at origin", debug.getPropertyAddress<bool>("d.view.camera_at_origin"));
|
||||
ImGui::Checkbox("Far Origin", &app.originIsFarAway);
|
||||
ImGui::SliderFloat("Origin", &app.originDistance, 0, 10000000);
|
||||
auto dataSource = debug.getDataSource("d.view.frame_info");
|
||||
if (dataSource.data) {
|
||||
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(app.asset->getRoot()), 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.
|
||||
ColorGradingSettings& options = app.viewer->getSettings().view.colorGrading;
|
||||
|
||||
@@ -109,7 +109,7 @@ highp vec4 getSpotLightSpacePosition(int index, highp vec3 dir, highp float zLig
|
||||
// for spotlights, the bias depends on z
|
||||
float bias = shadowUniforms.shadows[index].normalBias * zLight;
|
||||
|
||||
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(),
|
||||
return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(),
|
||||
dir, bias, lightFromWorldMatrix);
|
||||
}
|
||||
#endif
|
||||
@@ -141,7 +141,7 @@ highp vec4 getCascadeLightSpacePosition(int cascade) {
|
||||
return vertex_lightSpacePosition;
|
||||
}
|
||||
|
||||
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(),
|
||||
return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(),
|
||||
frameUniforms.lightDirection,
|
||||
shadowUniforms.shadows[cascade].normalBias,
|
||||
shadowUniforms.shadows[cascade].lightFromWorldMatrix);
|
||||
|
||||
@@ -45,8 +45,8 @@ end
|
||||
|
||||
Then run:
|
||||
|
||||
```shell
|
||||
pod install
|
||||
```
|
||||
$ pod install
|
||||
```
|
||||
|
||||
Close the project and then re-open the newly created HelloCocoaPods.xcworkspace file.
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
if(POLICY CMP0077)
|
||||
cmake_policy(SET CMP0077 NEW) # Enables override of options from parent CMakeLists.txt
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
if (POLICY CMP0048)
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
endif()
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
# The SPIR-V headers from the SPIR-V Registry
|
||||
# https://www.khronos.org/registry/spir-v/
|
||||
#
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(SPIRV-Headers VERSION 1.5.5)
|
||||
|
||||
# There are two ways to use this project.
|
||||
|
||||
+72
-45
@@ -7,9 +7,9 @@ The tool can consume a HDR environment map in latlong format (equirectilinear) a
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
cmgen [options] <input-file>
|
||||
cmgen [options] <uv[N]>
|
||||
```
|
||||
$ cmgen [options] <input-file>
|
||||
$ cmgen [options] <uv[N]>
|
||||
```
|
||||
|
||||
## Supported input formats
|
||||
@@ -21,45 +21,72 @@ cmgen [options] <uv[N]>
|
||||
|
||||
## Options
|
||||
|
||||
```
|
||||
--help, -h
|
||||
Print this message
|
||||
--license
|
||||
Print copyright and license information
|
||||
--quiet, -q
|
||||
Quiet mode. Suppress all non-error output
|
||||
--type=[cubemap|equirect|octahedron|ktx], -t [cubemap|equirect|octahedron|ktx]
|
||||
Specify output type (default: cubemap)
|
||||
--format=[exr|hdr|psd|rgbm|rgb32f|png|dds|ktx], -f [format]
|
||||
Specify output file format. ktx implies -type=ktx.
|
||||
KTX files are always KTX1 files, not KTX2.
|
||||
They are encoded with 3-channel RGB_10_11_11_REV data
|
||||
--compression=COMPRESSION, -c COMPRESSION
|
||||
Format specific compression:
|
||||
KTX: ignored
|
||||
PNG: Ignored
|
||||
PNG RGBM: Ignored
|
||||
Radiance: Ignored
|
||||
Photoshop: 16 (default), 32
|
||||
OpenEXR: RAW, RLE, ZIPS, ZIP, PIZ (default)
|
||||
DDS: 8, 16 (default), 32
|
||||
--size=power-of-two, -s power-of-two
|
||||
Size of the output cubemaps (base level), 256 by default
|
||||
Also applies to DFG LUT
|
||||
--deploy=dir, -x dir
|
||||
Generate everything needed for deployment into <dir>
|
||||
--extract=dir
|
||||
Extract faces of the cubemap into <dir>
|
||||
--extract-blur=roughness
|
||||
Blurs the cubemap before saving the faces using the roughness blur
|
||||
--clamp
|
||||
Clamp environment before processing
|
||||
--no-mirror
|
||||
Skip mirroring of generated cubemaps (for assets with mirroring already backed in)
|
||||
--ibl-samples=numSamples
|
||||
Number of samples to use for IBL integrations (default 1024)
|
||||
--ibl-ld=dir
|
||||
Roughness pre-filter into <dir>
|
||||
--sh-shader
|
||||
Generate irradiance SH for shader code
|
||||
```
|
||||
- --license
|
||||
Print copyright and license information
|
||||
- --quiet, -q
|
||||
Quiet mode. Suppress all non-error output
|
||||
- --type=[cubemap|equirect|octahedron|ktx], -t [cubemap|equirect|octahedron|ktx]
|
||||
Specify output type (default: cubemap)
|
||||
- --format=[exr|hdr|psd|rgbm|rgb32f|png|dds|ktx], -f [exr|hdr|psd|rgbm|rgb32f|png|dds|ktx]
|
||||
Specify output file format. ktx implies -type=ktx.
|
||||
KTX files are always encoded with 3-channel RGB_10_11_11_REV data
|
||||
- --compression=COMPRESSION, -c COMPRESSION
|
||||
Format specific compression:
|
||||
KTX:
|
||||
astc_[fast|thorough]_[ldr|hdr]_WxH, where WxH is a valid block size
|
||||
s3tc_rgba_dxt5
|
||||
etc_FORMAT_METRIC_EFFORT
|
||||
FORMAT is rgb8_alpha, srgb8_alpha, rgba8, or srgb8_alpha8
|
||||
METRIC is rgba, rgbx, rec709, numeric, or normalxyz
|
||||
EFFORT is an integer between 0 and 100
|
||||
PNG: Ignored
|
||||
PNG RGBM: Ignored
|
||||
Radiance: Ignored
|
||||
Photoshop: 16 (default), 32
|
||||
OpenEXR: RAW, RLE, ZIPS, ZIP, PIZ (default)
|
||||
DDS: 8, 16 (default), 32
|
||||
- --size=power-of-two, -s power-of-two
|
||||
Size of the output cubemaps (base level), 256 by default
|
||||
Also aplies to DFG LUT
|
||||
- --deploy=dir, -x dir
|
||||
Generate everything needed for deployment into <dir>
|
||||
- --extract=dir
|
||||
Extract faces of the cubemap into <dir>
|
||||
- --extract-blur=roughness
|
||||
Blurs the cubemap before saving the faces using the roughness blur
|
||||
- --clamp
|
||||
Clamp environment before processing
|
||||
- --no-mirror
|
||||
Skip mirroring of generated cubemaps (for assets with mirroring already backed in)
|
||||
- --ibl-samples=numSamples
|
||||
Number of samples to use for IBL integrations (default 1024)
|
||||
- --ibl-ld=dir
|
||||
Roughness pre-filter into <dir>
|
||||
- --sh-shader
|
||||
Generate irradiance SH for shader code
|
||||
|
||||
Private use only:
|
||||
- --ibl-dfg=filename.[exr|hdr|psd|png|rgbm|rgb32f|dds|h|hpp|c|cpp|inc|txt]
|
||||
Compute the IBL DFG LUT
|
||||
- --ibl-dfg-multiscatter
|
||||
If --ibl-dfg is set, computes the DFG for multi-scattering GGX
|
||||
- --ibl-dfg-cloth
|
||||
If --ibl-dfg is set, adds a 3rd channel to the DFG for cloth shading
|
||||
- --ibl-is-mipmap=dir
|
||||
Generate mipmap for pre-filtered importance sampling
|
||||
- --ibl-irradiance=dir
|
||||
Diffuse irradiance into <dir>
|
||||
- --ibl-no-prefilter
|
||||
Use importance sampling instead of prefiltered importance sampling
|
||||
- --ibl-min-lod-size
|
||||
Minimum LOD size [default: 16]
|
||||
- --sh=bands
|
||||
SH decomposition of input cubemap
|
||||
- --sh-output=filename.[exr|hdr|psd|rgbm|rgb32f|png|dds|txt]
|
||||
SH output format. The filename extension determines the output format
|
||||
- --sh-irradiance, -i
|
||||
Irradiance SH coefficients
|
||||
- --sh-window=cutoff|no|auto (default), -w cutoff|no|auto (default)
|
||||
SH windowing to reduce ringing
|
||||
- --debug, -d
|
||||
Generate extra data for debugging
|
||||
|
||||
@@ -14,8 +14,8 @@ identified by an offset and count in the index buffer. Each part can have its ow
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
filamesh source_mesh destination_mesh
|
||||
```
|
||||
$ filamesh source_mesh destination_mesh
|
||||
```
|
||||
|
||||
## Format
|
||||
|
||||
@@ -5,6 +5,6 @@ used for debug purpose only.
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
matinfo [options] <material file>
|
||||
```
|
||||
$ matinfo [options] <material file>
|
||||
```
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
mipgen [options] <input_file> <output_pattern>
|
||||
```
|
||||
$ mipgen [options] <input_file> <output_pattern>
|
||||
```
|
||||
|
||||
Run `mipgen --help` for more information about available options.
|
||||
|
||||
@@ -11,8 +11,8 @@ grazing angles. See Hoffman 2019, "Fresnel Equations Considered Harmful".
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
specular-color <spectral data file>
|
||||
```
|
||||
$ specular-color <spectral data file>
|
||||
```
|
||||
|
||||
The spectral data files can be obtained from
|
||||
@@ -20,12 +20,12 @@ The spectral data files can be obtained from
|
||||
|
||||
For instance, to compute the base color of gold:
|
||||
|
||||
```shell
|
||||
specular-color data/gold.txt
|
||||
```
|
||||
$ specular-color data/gold.txt
|
||||
```
|
||||
|
||||
To set the second angle, use `-a` to specify the angle in degrees:
|
||||
|
||||
```shell
|
||||
specular-color -a 75 data/gold.txt
|
||||
```
|
||||
$ specular-color -a 75 data/gold.txt
|
||||
```
|
||||
|
||||
@@ -23,26 +23,27 @@ non-compressed variants for each texture, since not all platforms support the sa
|
||||
formats. First copy over the PNG files from the [monkey folder], then do:
|
||||
|
||||
```bash
|
||||
# Create mipmaps for base color
|
||||
mipgen albedo.png albedo.ktx2
|
||||
mipgen --compression=uastc albedo.png albedo.ktx2
|
||||
# Create mipmaps for base color and two compressed variants.
|
||||
mipgen albedo.png albedo.ktx
|
||||
mipgen --compression=astc_fast_ldr_4x4 albedo.png albedo_astc.ktx
|
||||
mipgen --compression=s3tc_rgb_dxt1 albedo.png albedo_s3tc_srgb.ktx
|
||||
|
||||
# Create mipmaps for the normal map and a compressed variant.
|
||||
mipgen --strip-alpha --kernel=NORMALS --linear normal.png normal.ktx
|
||||
mipgen --strip-alpha --kernel=NORMALS --linear --compression=uastc_normals \
|
||||
normal.png normal.ktx2
|
||||
mipgen --strip-alpha --kernel=NORMALS --linear --compression=etc_rgb8_normalxyz_40 \
|
||||
normal.png normal_etc.ktx
|
||||
|
||||
# Create mipmaps for the single-component roughness map and a compressed variant.
|
||||
mipgen --grayscale roughness.png roughness.ktx
|
||||
mipgen --grayscale --compression=uastc roughness.png roughness.ktx2
|
||||
mipgen --grayscale --compression=etc_r11_numeric_40 roughness.png roughness_etc.ktx
|
||||
|
||||
# Create mipmaps for the single-component metallic map and a compressed variant.
|
||||
mipgen --grayscale metallic.png metallic.ktx
|
||||
mipgen --grayscale --compression=uastc metallic.png metallic.ktx2
|
||||
mipgen --grayscale --compression=etc_r11_numeric_40 metallic.png metallic_etc.ktx
|
||||
|
||||
# Create mipmaps for the single-component occlusion map and a compressed variant.
|
||||
mipgen --grayscale ao.png ao.ktx
|
||||
mipgen --grayscale --compression=uastc ao.png ao.ktx2
|
||||
mipgen --grayscale --compression=etc_r11_numeric_40 ao.png ao_etc.ktx
|
||||
```
|
||||
|
||||
For more information on mipgen's arguments and supported formats, do `mipgen --help`.
|
||||
@@ -236,11 +237,11 @@ was created in the app constructor.
|
||||
|
||||
```js {fragment="fetch larger assets"}
|
||||
Filament.fetch([sky_large_url, albedo_url, roughness_url, metallic_url, normal_url, ao_url], () => {
|
||||
const albedo = this.engine.createTextureFromKtx2(albedo_url, {srgb: true});
|
||||
const roughness = this.engine.createTextureFromKtx2(roughness_url);
|
||||
const metallic = this.engine.createTextureFromKtx2(metallic_url);
|
||||
const normal = this.engine.createTextureFromKtx2(normal_url);
|
||||
const ao = this.engine.createTextureFromKtx2(ao_url);
|
||||
const albedo = this.engine.createTextureFromKtx1(albedo_url, {srgb: true});
|
||||
const roughness = this.engine.createTextureFromKtx1(roughness_url);
|
||||
const metallic = this.engine.createTextureFromKtx1(metallic_url);
|
||||
const normal = this.engine.createTextureFromKtx1(normal_url);
|
||||
const ao = this.engine.createTextureFromKtx1(ao_url);
|
||||
|
||||
const sampler = new Filament.TextureSampler(
|
||||
Filament.MinFilter.LINEAR_MIPMAP_LINEAR,
|
||||
|
||||
@@ -330,9 +330,7 @@ value_object<LightManager::ShadowOptions>("LightManager$ShadowOptions")
|
||||
.field("polygonOffsetSlope", &LightManager::ShadowOptions::polygonOffsetSlope)
|
||||
.field("screenSpaceContactShadows", &LightManager::ShadowOptions::screenSpaceContactShadows)
|
||||
.field("stepCount", &LightManager::ShadowOptions::stepCount)
|
||||
.field("maxShadowDistance", &LightManager::ShadowOptions::maxShadowDistance)
|
||||
.field("shadowBulbRadius", &LightManager::ShadowOptions::shadowBulbRadius)
|
||||
.field("transform", &LightManager::ShadowOptions::transform);
|
||||
.field("maxShadowDistance", &LightManager::ShadowOptions::maxShadowDistance);
|
||||
|
||||
// In JavaScript, a flat contiguous representation is best for matrices (see gl-matrix) so we
|
||||
// need to define a small wrapper here.
|
||||
@@ -1339,11 +1337,11 @@ class_<MaterialInstance>("MaterialInstance")
|
||||
(MaterialInstance* self, std::string name, filament::math::float4 value), {
|
||||
self->setParameter(name.c_str(), value); }), allow_raw_pointers())
|
||||
.function("setMat3Parameter", EMBIND_LAMBDA(void,
|
||||
(MaterialInstance* self, std::string name, flatmat3 value), {
|
||||
self->setParameter(name.c_str(), value.m); }), allow_raw_pointers())
|
||||
(MaterialInstance* self, std::string name, filament::math::mat3f value), {
|
||||
self->setParameter(name.c_str(), value); }), allow_raw_pointers())
|
||||
.function("setMat4Parameter", EMBIND_LAMBDA(void,
|
||||
(MaterialInstance* self, std::string name, flatmat4 value), {
|
||||
self->setParameter(name.c_str(), value.m); }), allow_raw_pointers())
|
||||
(MaterialInstance* self, std::string name, filament::math::mat4f value), {
|
||||
self->setParameter(name.c_str(), value); }), allow_raw_pointers())
|
||||
.function("setTextureParameter", EMBIND_LAMBDA(void,
|
||||
(MaterialInstance* self, std::string name, Texture* value, TextureSampler sampler), {
|
||||
self->setParameter(name.c_str(), value, sampler); }), allow_raw_pointers())
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.43.0",
|
||||
"version": "1.42.0",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user