Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
815629f14e | ||
|
|
c05a446e10 | ||
|
|
9d49205844 | ||
|
|
7099b6dd6f | ||
|
|
18becfc656 | ||
|
|
cd2d499e25 | ||
|
|
3bb72cefe9 | ||
|
|
1a67f9233b | ||
|
|
f7e20562a8 | ||
|
|
08af16239c | ||
|
|
52eb682498 | ||
|
|
5a5168a191 | ||
|
|
e85dfe75c8 | ||
|
|
6beb40b0a1 | ||
|
|
5cb96e5732 | ||
|
|
927aa57a4e | ||
|
|
36e775902d | ||
|
|
53e28f3b33 | ||
|
|
7ccbdb4633 | ||
|
|
f10d226565 | ||
|
|
28ecf5c35d | ||
|
|
9683eb649c | ||
|
|
3d10ae3ee3 | ||
|
|
44a75dd44b | ||
|
|
de7bcd2df7 | ||
|
|
6adf140fb4 | ||
|
|
b94c802076 | ||
|
|
b231de0b5b | ||
|
|
fca200d549 | ||
|
|
ba1d3f6c76 | ||
|
|
7734fd4ad9 | ||
|
|
7e6839f535 | ||
|
|
3d78322058 | ||
|
|
3e1ea7cdfd | ||
|
|
6d413a4faf | ||
|
|
af09c517b6 | ||
|
|
67b9c72442 | ||
|
|
c1a8d73384 | ||
|
|
2d98ac878e | ||
|
|
35ab5f768d | ||
|
|
705b3c39f4 | ||
|
|
56231db326 | ||
|
|
e97f7ef628 | ||
|
|
4570c2e710 | ||
|
|
4759237e66 | ||
|
|
543b93939a |
@@ -3,5 +3,5 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Set up dependency versions
|
||||
shell: bash
|
||||
shell: bash
|
||||
run: cat ./build/common/versions >> $GITHUB_ENV
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
name: 'Web Preqrequisites'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: ./.github/actions/dep-versions
|
||||
- name: Cache EMSDK
|
||||
id: emsdk-cache
|
||||
uses: actions/cache@v4 # Use a specific version
|
||||
with:
|
||||
path: emsdk
|
||||
key: ${{ runner.os }}-emsdk-${{ env.GITHUB_EMSDK_VERSION }}
|
||||
- name: Install Web Prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
bash ./build/common/get-emscripten.sh
|
||||
echo "EMSDK=$PWD/emsdk" >> $GITHUB_ENV
|
||||
@@ -26,4 +26,4 @@ jobs:
|
||||
path: out/filament-release-darwin.tgz
|
||||
- name: Check public headers
|
||||
run: |
|
||||
build/common/check-headers.sh out/release/filament/include
|
||||
test/check-headers/test.sh out/release/filament/include
|
||||
|
||||
@@ -96,6 +96,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- uses: ./.github/actions/web-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/web && printf "y" | ./build.sh presubmit
|
||||
@@ -123,13 +124,15 @@ jobs:
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Cache Mesa and deps
|
||||
id: mesa-cache
|
||||
uses: actions/cache@v4 # Use a specific version
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: mesa
|
||||
key: ${{ runner.os }}-mesa-deps-2-${{ vars.MESA_VERSION }}
|
||||
- name: Get Mesa
|
||||
id: mesa-prereq
|
||||
run: bash test/utils/get_mesa.sh
|
||||
- name: Prerequisites
|
||||
id: prereqs
|
||||
run: |
|
||||
bash build/common/get-mesa.sh
|
||||
pip install tifffile numpy
|
||||
- name: Run Test
|
||||
run: bash test/renderdiff/test.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
@@ -150,8 +153,8 @@ jobs:
|
||||
- name: Run test
|
||||
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
|
||||
|
||||
code-correcteness:
|
||||
name: code-correctness
|
||||
test-code-correctness:
|
||||
name: test-code-correctness
|
||||
runs-on: 'macos-14-xlarge'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
@@ -65,13 +65,9 @@ jobs:
|
||||
|
||||
build-mac:
|
||||
name: build-mac
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: macos-14-xlarge
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-14-xlarge, ubuntu-22.04-32core]
|
||||
|
||||
steps:
|
||||
- name: Decide Git ref
|
||||
id: git_ref
|
||||
@@ -118,6 +114,7 @@ jobs:
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- uses: ./.github/actions/web-prereq
|
||||
- name: Run build script
|
||||
env:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
@@ -166,6 +163,9 @@ jobs:
|
||||
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
|
||||
mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar
|
||||
mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar
|
||||
cd out/android-release/filament
|
||||
tar -czf ../../filament-${TAG}-android-native.tgz .
|
||||
cd ../../..
|
||||
- name: Sign sample-gltf-viewer
|
||||
run: |
|
||||
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
|
||||
@@ -187,7 +187,7 @@ jobs:
|
||||
script: |
|
||||
const upload = require('./build/common/upload-release-assets');
|
||||
const { TAG } = process.env;
|
||||
const globber = await glob.create(['out/*.aar', 'out/*.apk'].join('\n'));
|
||||
const globber = await glob.create(['out/*.aar', 'out/*.apk', 'out/*.tgz'].join('\n'));
|
||||
await upload({ github, context }, await globber.glob(), TAG);
|
||||
|
||||
build-ios:
|
||||
|
||||
@@ -17,6 +17,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- uses: ./.github/actions/web-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/web && printf "y" | ./build.sh continuous
|
||||
|
||||
@@ -363,6 +363,8 @@ python ./emsdk.py activate latest
|
||||
source ./emsdk_env.sh
|
||||
```
|
||||
|
||||
Alternatively, you can try running the script `build/common/get-emscripten.sh`.
|
||||
|
||||
After this you can invoke the [easy build](#easy-build) script as follows:
|
||||
|
||||
```shell
|
||||
|
||||
@@ -7,3 +7,5 @@ for next branch cut* header.
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- materials: sampler now export their type in the material binary [⚠️ **New Material Version**]
|
||||
|
||||
@@ -7,6 +7,11 @@ 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.60.0
|
||||
|
||||
- materials: remove dependence on per-view descset layout from filamat. [⚠️ **New Material Version**]
|
||||
- `ColorGrading::Builder::toneMapper` now takes a `shared_ptr<ToneMapper>`
|
||||
|
||||
## v1.59.5
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <filament/ColorGrading.h>
|
||||
#include <filament/ToneMapper.h>
|
||||
#include <memory>
|
||||
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
@@ -67,8 +68,9 @@ extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_ColorGrading_nBuilderToneMapper(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, jlong toneMapper_) {
|
||||
ColorGrading::Builder* builder = (ColorGrading::Builder*) nativeBuilder;
|
||||
const ToneMapper* toneMapper = (const ToneMapper*) toneMapper_;
|
||||
builder->toneMapper(toneMapper);
|
||||
ToneMapper* toneMapper = reinterpret_cast<ToneMapper*>(toneMapper_);
|
||||
std::shared_ptr<ToneMapper> toneMapperCopy(toneMapper->clone());
|
||||
builder->toneMapper(toneMapperCopy);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
|
||||
+3
-3
@@ -214,9 +214,9 @@ public class ColorGrading {
|
||||
*
|
||||
* The default tone mapping operator is {@link ToneMapper.ACESLegacy}.
|
||||
*
|
||||
* The specified tone mapper must have a lifecycle that exceeds the lifetime of
|
||||
* this builder. Since the build(Engine&) method is synchronous, it is safe to
|
||||
* delete the tone mapper object after that finishes executing.
|
||||
* The copy of the specified tone mapper is set to this builder. It is safe to delete
|
||||
* the original tone mapper object while the copied one is held by the built ColorGrading
|
||||
* object.
|
||||
*
|
||||
* @param toneMapper The tone mapping operator to apply to the HDR color buffer
|
||||
*
|
||||
|
||||
@@ -151,7 +151,7 @@ function print_fgviewer_help {
|
||||
}
|
||||
|
||||
# Unless explicitly specified, NDK version will be selected as highest available version within same major release chain
|
||||
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/android/ndk.version | cut -f 1 -d ".")}
|
||||
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/common/versions | grep GITHUB_NDK_VERSION | cut -f 1 -d ".")}
|
||||
|
||||
# Requirements
|
||||
CMAKE_MAJOR=3
|
||||
|
||||
+1
-28
@@ -1,28 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: the first argument selects the build type:
|
||||
# - release, to build release only
|
||||
# - debug, to build debug only
|
||||
# - continuous, to build release and debug
|
||||
# - presubmit, for presubmit builds
|
||||
#
|
||||
# The default is release
|
||||
|
||||
echo "This script is intended to run in a CI environment and may modify your current environment."
|
||||
echo "Please refer to BUILDING.md for more information."
|
||||
|
||||
read -r -p "Do you wish to proceed (y/n)? " choice
|
||||
case "${choice}" in
|
||||
y|Y)
|
||||
echo "Build will proceed..."
|
||||
;;
|
||||
n|N)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
source `dirname $0`/../common/ci-check.sh
|
||||
|
||||
set -e
|
||||
set -x
|
||||
@@ -30,11 +8,6 @@ set -x
|
||||
UNAME=`echo $(uname)`
|
||||
LC_UNAME=`echo $UNAME | tr '[:upper:]' '[:lower:]'`
|
||||
|
||||
# build-common.sh will generate the following variables:
|
||||
# $GENERATE_ARCHIVES
|
||||
# $BUILD_DEBUG
|
||||
# $BUILD_RELEASE
|
||||
source `dirname $0`/../common/ci-common.sh
|
||||
source `dirname $0`/../common/build-common.sh
|
||||
|
||||
if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# build-common.sh will generate the following variables:
|
||||
# $GENERATE_ARCHIVES
|
||||
# $BUILD_DEBUG
|
||||
# $BUILD_RELEASE
|
||||
|
||||
# Typically a build script (build.sh) would source this script. For example,
|
||||
# source `dirname $0`/../common/build-common.sh
|
||||
|
||||
# Usage: the first argument selects the build type:
|
||||
# - release, to build release only
|
||||
# - debug, to build debug only
|
||||
# - continuous, to build release and debug
|
||||
# - presubmit, for presubmit builds
|
||||
#
|
||||
# The default is release
|
||||
if [[ ! "$TARGET" ]]; then
|
||||
if [[ "$1" ]]; then
|
||||
TARGET=$1
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
echo "This script is intended to run in a CI environment and may modify your current environment."
|
||||
echo "Please refer to BUILDING.md for more information."
|
||||
|
||||
read -r -p "Do you wish to proceed (y/n)? " choice
|
||||
case "${choice}" in
|
||||
y|Y)
|
||||
echo "Build will proceed..."
|
||||
;;
|
||||
n|N)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
|
||||
fi
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
|
||||
fi
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -d "./emsdk" ]; then
|
||||
echo "emsdk folder found. Assume emsdk has been installed."
|
||||
cd emsdk
|
||||
./emsdk activate latest
|
||||
source ./emsdk_env.sh
|
||||
export EMSDK="$PWD"
|
||||
cd ..
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Install emscripten.
|
||||
EMSDK_VERSION=${GITHUB_EMSDK_VERSION-3.1.60}
|
||||
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/${EMSDK_VERSION}.zip > emsdk.zip
|
||||
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
|
||||
./emsdk install latest
|
||||
./emsdk activate latest
|
||||
source ./emsdk_env.sh
|
||||
|
||||
export EMSDK="$PWD"
|
||||
cd ..
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -x
|
||||
set -e
|
||||
if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
set -e
|
||||
set -x
|
||||
fi
|
||||
|
||||
OS_NAME=$(uname -s)
|
||||
@@ -35,7 +35,7 @@ source ${ORIG_DIR}/venv/bin/activate
|
||||
|
||||
NEEDED_PYTHON_DEPS=("mako" "setuptools" "pyyaml")
|
||||
for cmd in "${NEEDED_PYTHON_DEPS[@]}"; do
|
||||
if ! python3 -m pip show "${cmd}" >/dev/null 2>&1; then
|
||||
if ! python3 -m pip show -q "${cmd}" >/dev/null 2>&1; then
|
||||
python3 -m pip install ${cmd}
|
||||
fi
|
||||
done
|
||||
@@ -145,6 +145,6 @@ deactivate
|
||||
popd
|
||||
|
||||
if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
set +e
|
||||
set +x
|
||||
fi
|
||||
set +x
|
||||
set +e
|
||||
@@ -3,4 +3,5 @@ GITHUB_CMAKE_VERSION=3.19.5
|
||||
GITHUB_NINJA_VERSION=1.10.2
|
||||
GITHUB_MESA_VERSION=24.2.1
|
||||
GITHUB_LLVM_VERSION=16
|
||||
GITHUB_NDK_VERSION=27.0.11718014
|
||||
GITHUB_NDK_VERSION=27.0.11718014
|
||||
GITHUB_EMSDK_VERSION=3.1.60
|
||||
+1
-25
@@ -1,35 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: the first argument selects the build type:
|
||||
# - release, to build release only
|
||||
# - debug, to build debug only
|
||||
# - continuous, to build release and debug
|
||||
# - presubmit, for presubmit builds
|
||||
#
|
||||
# The default is release
|
||||
|
||||
echo "This script is intended to run in a CI environment and may modify your current environment."
|
||||
echo "Please refer to BUILDING.md for more information."
|
||||
|
||||
read -r -p "Do you wish to proceed (y/n)? " choice
|
||||
case "${choice}" in
|
||||
y|Y)
|
||||
echo "Build will proceed..."
|
||||
;;
|
||||
n|N)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
source `dirname $0`/../common/ci-check.sh
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
source `dirname $0`/../common/ci-common.sh
|
||||
source `dirname $0`/../common/build-common.sh
|
||||
|
||||
pushd `dirname $0`/../.. > /dev/null
|
||||
|
||||
# If we're generating an archive for release or continuous builds, then we'll also build for the
|
||||
|
||||
+2
-29
@@ -1,38 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: the first argument selects the build type:
|
||||
# - release, to build release only
|
||||
# - debug, to build debug only
|
||||
# - continuous, to build release and debug
|
||||
# - presubmit, for presubmit builds
|
||||
#
|
||||
# The default is release
|
||||
|
||||
echo "This script is intended to run in a CI environment and may modify your current environment."
|
||||
echo "Please refer to BUILDING.md for more information."
|
||||
|
||||
read -r -p "Do you wish to proceed (y/n)? " choice
|
||||
case "${choice}" in
|
||||
y|Y)
|
||||
echo "Build will proceed..."
|
||||
;;
|
||||
n|N)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
source `dirname $0`/../common/ci-check.sh
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
# build-common.sh will generate the following variables:
|
||||
# $GENERATE_ARCHIVES
|
||||
# $BUILD_DEBUG
|
||||
# $BUILD_RELEASE
|
||||
source `dirname $0`/../common/ci-common.sh
|
||||
source `dirname $0`/../common/build-common.sh
|
||||
|
||||
pushd `dirname $0`/../.. > /dev/null
|
||||
|
||||
./build.sh -c $RUN_TESTS $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
|
||||
|
||||
+2
-25
@@ -1,34 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: the first argument selects the build type:
|
||||
# - release, to build release only
|
||||
# - debug, to build debug only
|
||||
# - continuous, to build release and debug
|
||||
# - presubmit, for presubmit builds
|
||||
#
|
||||
# The default is release
|
||||
|
||||
echo "This script is intended to run in a CI environment and may modify your current environment."
|
||||
echo "Please refer to BUILDING.md for more information."
|
||||
|
||||
read -r -p "Do you wish to proceed (y/n)? " choice
|
||||
case "${choice}" in
|
||||
y|Y)
|
||||
echo "Build will proceed..."
|
||||
;;
|
||||
n|N)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
source `dirname $0`/../common/ci-check.sh
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
source `dirname $0`/../common/ci-common.sh
|
||||
source `dirname $0`/../common/build-common.sh
|
||||
|
||||
pushd `dirname $0`/../.. > /dev/null
|
||||
|
||||
./build.sh -c $RUN_TESTS $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
|
||||
|
||||
+1
-25
@@ -1,34 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: the first argument selects the build type:
|
||||
# - release, to build release only
|
||||
# - debug, to build debug only
|
||||
# - continuous, to build release and debug
|
||||
# - presubmit, for presubmit builds
|
||||
#
|
||||
# The default is release
|
||||
|
||||
echo "This script is intended to run in a CI environment and may modify your current environment."
|
||||
echo "Please refer to BUILDING.md for more information."
|
||||
|
||||
read -r -p "Do you wish to proceed (y/n)? " choice
|
||||
case "${choice}" in
|
||||
y|Y)
|
||||
echo "Build will proceed..."
|
||||
;;
|
||||
n|N)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
source `dirname $0`/../common/ci-check.sh
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
source `dirname $0`/../common/ci-common.sh
|
||||
source `dirname $0`/ci-common.sh
|
||||
source `dirname $0`/../common/build-common.sh
|
||||
pushd `dirname $0`/../.. > /dev/null
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Install emscripten.
|
||||
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.60.zip > emsdk.zip
|
||||
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
|
||||
./emsdk install latest
|
||||
./emsdk activate latest
|
||||
source ./emsdk_env.sh
|
||||
|
||||
export EMSDK="$PWD"
|
||||
cd ..
|
||||
@@ -18,6 +18,7 @@
|
||||
- [Metal](./notes/metal_debugging.md)
|
||||
- [Vulkan](./notes/vulkan_debugging.md)
|
||||
- [SPIR-V](./notes/spirv_debugging.md)
|
||||
- [Running with ASAN and UBSAN](./notes/asan_ubsan.md)
|
||||
- [Libraries](./notes/libs.md)
|
||||
- [bluegl](./dup/bluegl.md)
|
||||
- [bluevk](./dup/bluevk.md)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Running with ASAN/UBSAN
|
||||
|
||||
## Enabling
|
||||
|
||||
When building though build.sh, pass the `-b` flag. This sets the cmake variable
|
||||
`FILAMENT_ENABLE_ASAN_UBSAN=ON` which eventually passes `"-fsanitize=address -fsanitize=undefined"`
|
||||
to all compile and link operations.
|
||||
|
||||
If building through CMake directly, or an IDE like CLion that doesn't use build.sh, instead pass
|
||||
`-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to cmake in order to get the same result.
|
||||
|
||||
## Getting memory leak detection on Mac
|
||||
|
||||
Memory leak detection isn't enabled by default on MacOS. There are two issues to address, first is
|
||||
using a version of clang that supports memory leak detection and second is enabling it at runtime.
|
||||
|
||||
The version of clang distributed by Apple (with a version like "Apple clang version 16.0.0") doesn't
|
||||
currently support leak detection at all. Instead you will need to get or build a different LLVM,
|
||||
such as the one distributed through homebrew and get CMake to use that instead.
|
||||
|
||||
Then during runtime you'll need to have the environment variable `ASAN_OPTIONS` include the option
|
||||
`detect_leaks=1`. Multiple `ASAN_OPTIONS` values are concatenated with `:`.
|
||||
|
||||
## Getting memory leak output in CLion
|
||||
|
||||
### Setting variables
|
||||
|
||||
Under `Settings | Build, Execution, Deployment | Dynamic Analysis Tools | Sanitizers` there is an
|
||||
ASAN Settings field that overrides whatever other `ASAN_OPTIONS` you might set elsewhere, so you
|
||||
must use that instead of setting it through your Run/Debug Configuration.
|
||||
|
||||
To pass `-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to CMake you'll want to create a new CMake Profile and
|
||||
pass it as a CMake argument.
|
||||
|
||||
### Avoiding losing output
|
||||
|
||||
CMake will consume ASAN output and display it through a separate "Sanitizers" tab. Unfortunately
|
||||
certain leak detection errors that interrupt the executable seem to not show up in this tab, but are
|
||||
still removed from the user-visible console output. If this is happening and you need to see the
|
||||
unfiltered console output you'll need to go to `Settings | Build, Execution, Deployment | Dynamic
|
||||
Analysis Tools | Sanitizers` and uncheck "Use visual representation for Sanitizer's output".
|
||||
@@ -30,12 +30,14 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <string_view>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -158,7 +160,7 @@ enum class TimerQueryResult : int8_t {
|
||||
AVAILABLE = 1, // result is available
|
||||
};
|
||||
|
||||
static constexpr const char* backendToString(Backend backend) {
|
||||
constexpr std::string_view to_string(Backend const backend) noexcept {
|
||||
switch (backend) {
|
||||
case Backend::NOOP:
|
||||
return "Noop";
|
||||
@@ -170,9 +172,11 @@ static constexpr const char* backendToString(Backend backend) {
|
||||
return "Metal";
|
||||
case Backend::WEBGPU:
|
||||
return "WebGPU";
|
||||
default:
|
||||
return "Unknown";
|
||||
case Backend::DEFAULT:
|
||||
return "Default";
|
||||
break;
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +193,7 @@ enum class ShaderLanguage {
|
||||
WGSL = 5,
|
||||
};
|
||||
|
||||
static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) {
|
||||
constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) noexcept {
|
||||
switch (shaderLanguage) {
|
||||
case ShaderLanguage::ESSL1:
|
||||
return "ESSL 1.0";
|
||||
@@ -204,6 +208,7 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag
|
||||
case ShaderLanguage::WGSL:
|
||||
return "WGSL";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
enum class ShaderStage : uint8_t {
|
||||
@@ -221,7 +226,7 @@ enum class ShaderStageFlags : uint8_t {
|
||||
ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE
|
||||
};
|
||||
|
||||
static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept {
|
||||
constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept {
|
||||
switch (type) {
|
||||
case ShaderStage::VERTEX:
|
||||
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX));
|
||||
@@ -232,14 +237,114 @@ static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage t
|
||||
}
|
||||
}
|
||||
|
||||
enum class DescriptorType : uint8_t {
|
||||
UNIFORM_BUFFER,
|
||||
SHADER_STORAGE_BUFFER,
|
||||
SAMPLER,
|
||||
INPUT_ATTACHMENT,
|
||||
SAMPLER_EXTERNAL
|
||||
enum class TextureType : uint8_t {
|
||||
FLOAT,
|
||||
INT,
|
||||
UINT,
|
||||
DEPTH,
|
||||
STENCIL,
|
||||
DEPTH_STENCIL
|
||||
};
|
||||
|
||||
constexpr std::string_view to_string(TextureType type) noexcept {
|
||||
switch (type) {
|
||||
case TextureType::FLOAT: return "FLOAT";
|
||||
case TextureType::INT: return "INT";
|
||||
case TextureType::UINT: return "UINT";
|
||||
case TextureType::DEPTH: return "DEPTH";
|
||||
case TextureType::STENCIL: return "STENCIL";
|
||||
case TextureType::DEPTH_STENCIL: return "DEPTH_STENCIL";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
enum class DescriptorType : uint8_t {
|
||||
SAMPLER_2D_FLOAT,
|
||||
SAMPLER_2D_INT,
|
||||
SAMPLER_2D_UINT,
|
||||
SAMPLER_2D_DEPTH,
|
||||
|
||||
SAMPLER_2D_ARRAY_FLOAT,
|
||||
SAMPLER_2D_ARRAY_INT,
|
||||
SAMPLER_2D_ARRAY_UINT,
|
||||
SAMPLER_2D_ARRAY_DEPTH,
|
||||
|
||||
SAMPLER_CUBE_FLOAT,
|
||||
SAMPLER_CUBE_INT,
|
||||
SAMPLER_CUBE_UINT,
|
||||
SAMPLER_CUBE_DEPTH,
|
||||
|
||||
SAMPLER_CUBE_ARRAY_FLOAT,
|
||||
SAMPLER_CUBE_ARRAY_INT,
|
||||
SAMPLER_CUBE_ARRAY_UINT,
|
||||
SAMPLER_CUBE_ARRAY_DEPTH,
|
||||
|
||||
SAMPLER_3D_FLOAT,
|
||||
SAMPLER_3D_INT,
|
||||
SAMPLER_3D_UINT,
|
||||
|
||||
SAMPLER_EXTERNAL,
|
||||
UNIFORM_BUFFER,
|
||||
SHADER_STORAGE_BUFFER,
|
||||
INPUT_ATTACHMENT,
|
||||
};
|
||||
|
||||
constexpr bool isDepthDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool isFloatDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr std::string_view to_string(DescriptorType type) noexcept {
|
||||
#define DESCRIPTOR_TYPE_CASE(TYPE) case DescriptorType::TYPE: return #TYPE;
|
||||
switch (type) {
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_DEPTH)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_DEPTH)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_DEPTH)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_DEPTH)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_3D_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_3D_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_3D_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_EXTERNAL)
|
||||
DESCRIPTOR_TYPE_CASE(UNIFORM_BUFFER)
|
||||
DESCRIPTOR_TYPE_CASE(SHADER_STORAGE_BUFFER)
|
||||
DESCRIPTOR_TYPE_CASE(INPUT_ATTACHMENT)
|
||||
}
|
||||
return "UNKNOWN";
|
||||
#undef DESCRIPTOR_TYPE_CASE
|
||||
}
|
||||
|
||||
enum class DescriptorFlags : uint8_t {
|
||||
NONE = 0x00,
|
||||
DYNAMIC_OFFSET = 0x01
|
||||
@@ -250,6 +355,13 @@ using descriptor_set_t = uint8_t;
|
||||
using descriptor_binding_t = uint8_t;
|
||||
|
||||
struct DescriptorSetLayoutBinding {
|
||||
static bool isSampler(DescriptorType type) noexcept {
|
||||
return int(type) <= int(DescriptorType::SAMPLER_EXTERNAL);
|
||||
}
|
||||
static bool isBuffer(DescriptorType type) noexcept {
|
||||
return type == DescriptorType::UNIFORM_BUFFER ||
|
||||
type == DescriptorType::SHADER_STORAGE_BUFFER;
|
||||
}
|
||||
DescriptorType type;
|
||||
ShaderStageFlags stageFlags;
|
||||
descriptor_binding_t binding;
|
||||
@@ -260,7 +372,7 @@ struct DescriptorSetLayoutBinding {
|
||||
// no uninitialized padding bytes.
|
||||
// uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED;
|
||||
|
||||
friend inline bool operator==(DescriptorSetLayoutBinding const& lhs,
|
||||
friend bool operator==(DescriptorSetLayoutBinding const& lhs,
|
||||
DescriptorSetLayoutBinding const& rhs) noexcept {
|
||||
return lhs.type == rhs.type &&
|
||||
lhs.flags == rhs.flags &&
|
||||
@@ -293,7 +405,7 @@ enum class TargetBufferFlags : uint32_t {
|
||||
ALL = COLOR_ALL | DEPTH | STENCIL //!< Color, depth and stencil buffer selected.
|
||||
};
|
||||
|
||||
inline constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept {
|
||||
constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept {
|
||||
if (index == 0u) return TargetBufferFlags::COLOR0;
|
||||
if (index == 1u) return TargetBufferFlags::COLOR1;
|
||||
if (index == 2u) return TargetBufferFlags::COLOR2;
|
||||
@@ -450,6 +562,24 @@ enum class SamplerType : uint8_t {
|
||||
SAMPLER_CUBEMAP_ARRAY, //!< Cube map array texture (feature level 2)
|
||||
};
|
||||
|
||||
constexpr std::string_view to_string(SamplerType const type) noexcept {
|
||||
switch (type) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
return "SAMPLER_2D";
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
return "SAMPLER_2D_ARRAY";
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return "SAMPLER_CUBEMAP";
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
return "SAMPLER_EXTERNAL";
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return "SAMPLER_3D";
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return "SAMPLER_CUBEMAP_ARRAY";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
//! Subpass type
|
||||
enum class SubpassType : uint8_t {
|
||||
SUBPASS_INPUT
|
||||
@@ -463,6 +593,20 @@ enum class SamplerFormat : uint8_t {
|
||||
SHADOW = 3 //!< shadow sampler (PCF)
|
||||
};
|
||||
|
||||
constexpr std::string_view to_string(SamplerFormat const format) noexcept {
|
||||
switch (format) {
|
||||
case SamplerFormat::INT:
|
||||
return "INT";
|
||||
case SamplerFormat::UINT:
|
||||
return "UINT";
|
||||
case SamplerFormat::FLOAT:
|
||||
return "FLOAT";
|
||||
case SamplerFormat::SHADOW:
|
||||
return "SHADOW";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported element types
|
||||
*/
|
||||
@@ -763,6 +907,8 @@ enum class TextureFormat : uint16_t {
|
||||
SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB
|
||||
};
|
||||
|
||||
TextureType getTextureType(TextureFormat format) noexcept;
|
||||
|
||||
//! Bitmask describing the intended Texture Usage
|
||||
enum class TextureUsage : uint16_t {
|
||||
NONE = 0x0000,
|
||||
@@ -790,7 +936,7 @@ enum class TextureSwizzle : uint8_t {
|
||||
};
|
||||
|
||||
//! returns whether this format a depth format
|
||||
static constexpr bool isDepthFormat(TextureFormat format) noexcept {
|
||||
constexpr bool isDepthFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::DEPTH32F:
|
||||
case TextureFormat::DEPTH24:
|
||||
@@ -803,7 +949,7 @@ static constexpr bool isDepthFormat(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool isStencilFormat(TextureFormat format) noexcept {
|
||||
constexpr bool isStencilFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::STENCIL8:
|
||||
case TextureFormat::DEPTH24_STENCIL8:
|
||||
@@ -814,7 +960,7 @@ static constexpr bool isStencilFormat(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
inline constexpr bool isColorFormat(TextureFormat format) noexcept {
|
||||
constexpr bool isColorFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
// Standard color formats
|
||||
case TextureFormat::R8:
|
||||
@@ -841,7 +987,7 @@ inline constexpr bool isColorFormat(TextureFormat format) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr bool isUnsignedIntFormat(TextureFormat format) {
|
||||
constexpr bool isUnsignedIntFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::R8UI:
|
||||
case TextureFormat::R16UI:
|
||||
@@ -862,7 +1008,7 @@ static constexpr bool isUnsignedIntFormat(TextureFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool isSignedIntFormat(TextureFormat format) {
|
||||
constexpr bool isSignedIntFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::R8I:
|
||||
case TextureFormat::R16I:
|
||||
@@ -884,35 +1030,35 @@ static constexpr bool isSignedIntFormat(TextureFormat format) {
|
||||
}
|
||||
|
||||
//! returns whether this format is a compressed format
|
||||
static constexpr bool isCompressedFormat(TextureFormat format) noexcept {
|
||||
constexpr bool isCompressedFormat(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::EAC_R11;
|
||||
}
|
||||
|
||||
//! returns whether this format is an ETC2 compressed format
|
||||
static constexpr bool isETC2Compression(TextureFormat format) noexcept {
|
||||
constexpr bool isETC2Compression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8;
|
||||
}
|
||||
|
||||
//! returns whether this format is an S3TC compressed format
|
||||
static constexpr bool isS3TCCompression(TextureFormat format) noexcept {
|
||||
constexpr bool isS3TCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::DXT1_RGB && format <= TextureFormat::DXT5_SRGBA;
|
||||
}
|
||||
|
||||
static constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept {
|
||||
constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::DXT1_SRGB && format <= TextureFormat::DXT5_SRGBA;
|
||||
}
|
||||
|
||||
//! returns whether this format is an RGTC compressed format
|
||||
static constexpr bool isRGTCCompression(TextureFormat format) noexcept {
|
||||
constexpr bool isRGTCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::RED_RGTC1 && format <= TextureFormat::SIGNED_RED_GREEN_RGTC2;
|
||||
}
|
||||
|
||||
//! returns whether this format is an BPTC compressed format
|
||||
static constexpr bool isBPTCCompression(TextureFormat format) noexcept {
|
||||
constexpr bool isBPTCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::RGB_BPTC_SIGNED_FLOAT && format <= TextureFormat::SRGB_ALPHA_BPTC_UNORM;
|
||||
}
|
||||
|
||||
static constexpr bool isASTCCompression(TextureFormat format) noexcept {
|
||||
constexpr bool isASTCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::RGBA_ASTC_4x4 && format <= TextureFormat::SRGB8_ALPHA8_ASTC_12x12;
|
||||
}
|
||||
|
||||
@@ -1037,15 +1183,19 @@ struct SamplerParams { // NOLINT
|
||||
}
|
||||
};
|
||||
|
||||
bool isFiltered() const noexcept {
|
||||
return filterMag != SamplerMagFilter::NEAREST || filterMin != SamplerMinFilter::NEAREST;
|
||||
}
|
||||
|
||||
private:
|
||||
friend inline bool operator == (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return SamplerParams::EqualTo{}(lhs, rhs);
|
||||
friend bool operator == (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator != (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return !SamplerParams::EqualTo{}(lhs, rhs);
|
||||
friend bool operator != (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return !EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return SamplerParams::LessThan{}(lhs, rhs);
|
||||
friend bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return LessThan{}(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1095,15 +1245,15 @@ struct SamplerYcbcrConversion {// NOLINT
|
||||
};
|
||||
|
||||
private:
|
||||
friend inline bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
friend bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
friend bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
friend bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
|
||||
}
|
||||
@@ -1139,6 +1289,7 @@ struct ExternalSamplerDatum {
|
||||
static_assert(sizeof(ExternalSamplerDatum) == 12);
|
||||
|
||||
struct DescriptorSetLayout {
|
||||
std::variant<utils::StaticString, utils::CString, std::monostate> label;
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||
|
||||
// TODO: uncomment when needed
|
||||
|
||||
@@ -149,13 +149,6 @@ public:
|
||||
* - PlatformEGLAndroid
|
||||
*/
|
||||
bool assertNativeWindowIsValid = false;
|
||||
|
||||
/**
|
||||
* The action to take if a Drawable cannot be acquired. If true, the
|
||||
* frame is aborted instead of panic. This is only supported for:
|
||||
* - PlatformMetal
|
||||
*/
|
||||
bool metalDisablePanicOnDrawableFailure = false;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
|
||||
@@ -44,8 +44,8 @@ public:
|
||||
|
||||
struct Descriptor {
|
||||
utils::CString name;
|
||||
backend::DescriptorType type;
|
||||
backend::descriptor_binding_t binding;
|
||||
DescriptorType type;
|
||||
descriptor_binding_t binding;
|
||||
};
|
||||
|
||||
struct SpecializationConstant {
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "DataReshaper.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <string_view>
|
||||
@@ -223,6 +225,143 @@ size_t getFormatSize(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
TextureType getTextureType(TextureFormat const format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::UNUSED:
|
||||
// should not happen
|
||||
return TextureType::FLOAT;
|
||||
|
||||
case TextureFormat::R8:
|
||||
case TextureFormat::R16F:
|
||||
case TextureFormat::RG8:
|
||||
case TextureFormat::RGB565:
|
||||
case TextureFormat::RGB5_A1:
|
||||
case TextureFormat::RGBA4:
|
||||
case TextureFormat::R32F:
|
||||
case TextureFormat::RGB8:
|
||||
case TextureFormat::SRGB8:
|
||||
case TextureFormat::RG16F:
|
||||
case TextureFormat::R11F_G11F_B10F:
|
||||
case TextureFormat::RGB9_E5:
|
||||
case TextureFormat::RGBA8:
|
||||
case TextureFormat::SRGB8_A8:
|
||||
case TextureFormat::RGB10_A2:
|
||||
case TextureFormat::RGB16F:
|
||||
case TextureFormat::RG32F:
|
||||
case TextureFormat::RGBA16F:
|
||||
case TextureFormat::RGB32F:
|
||||
case TextureFormat::RGBA32F:
|
||||
return TextureType::FLOAT;
|
||||
|
||||
case TextureFormat::R8_SNORM:
|
||||
case TextureFormat::RG8_SNORM:
|
||||
case TextureFormat::RGB8_SNORM:
|
||||
case TextureFormat::RGBA8_SNORM:
|
||||
// SNORM are treated as float
|
||||
return TextureType::FLOAT;
|
||||
|
||||
case TextureFormat::R8UI:
|
||||
case TextureFormat::R16UI:
|
||||
case TextureFormat::RG8UI:
|
||||
case TextureFormat::RGB8UI:
|
||||
case TextureFormat::R32UI:
|
||||
case TextureFormat::RG16UI:
|
||||
case TextureFormat::RGBA8UI:
|
||||
case TextureFormat::RGB16UI:
|
||||
case TextureFormat::RG32UI:
|
||||
case TextureFormat::RGBA16UI:
|
||||
case TextureFormat::RGB32UI:
|
||||
case TextureFormat::RGBA32UI:
|
||||
return TextureType::UINT;
|
||||
|
||||
case TextureFormat::R8I:
|
||||
case TextureFormat::R16I:
|
||||
case TextureFormat::RG8I:
|
||||
case TextureFormat::RGB8I:
|
||||
case TextureFormat::R32I:
|
||||
case TextureFormat::RG16I:
|
||||
case TextureFormat::RGBA8I:
|
||||
case TextureFormat::RGB16I:
|
||||
case TextureFormat::RG32I:
|
||||
case TextureFormat::RGBA16I:
|
||||
case TextureFormat::RGB32I:
|
||||
case TextureFormat::RGBA32I:
|
||||
return TextureType::INT;
|
||||
|
||||
case TextureFormat::DEPTH16:
|
||||
case TextureFormat::DEPTH24:
|
||||
case TextureFormat::DEPTH32F:
|
||||
return TextureType::DEPTH;
|
||||
|
||||
case TextureFormat::STENCIL8:
|
||||
return TextureType::STENCIL;
|
||||
|
||||
case TextureFormat::DEPTH24_STENCIL8:
|
||||
case TextureFormat::DEPTH32F_STENCIL8:
|
||||
return TextureType::DEPTH_STENCIL;
|
||||
|
||||
// Compressed formats ---------------------------------------------------------------------
|
||||
|
||||
case TextureFormat::EAC_RG11:
|
||||
case TextureFormat::EAC_RG11_SIGNED:
|
||||
case TextureFormat::ETC2_EAC_RGBA8:
|
||||
case TextureFormat::ETC2_EAC_SRGBA8:
|
||||
case TextureFormat::EAC_R11:
|
||||
case TextureFormat::EAC_R11_SIGNED:
|
||||
case TextureFormat::ETC2_RGB8:
|
||||
case TextureFormat::ETC2_SRGB8:
|
||||
case TextureFormat::ETC2_RGB8_A1:
|
||||
case TextureFormat::ETC2_SRGB8_A1:
|
||||
case TextureFormat::DXT1_RGB:
|
||||
case TextureFormat::DXT1_RGBA:
|
||||
case TextureFormat::DXT1_SRGB:
|
||||
case TextureFormat::DXT1_SRGBA:
|
||||
case TextureFormat::DXT3_RGBA:
|
||||
case TextureFormat::DXT3_SRGBA:
|
||||
case TextureFormat::DXT5_RGBA:
|
||||
case TextureFormat::DXT5_SRGBA:
|
||||
case TextureFormat::RED_RGTC1:
|
||||
case TextureFormat::SIGNED_RED_RGTC1:
|
||||
case TextureFormat::RED_GREEN_RGTC2:
|
||||
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
|
||||
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
|
||||
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
|
||||
case TextureFormat::RGBA_BPTC_UNORM:
|
||||
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
|
||||
case TextureFormat::RGBA_ASTC_4x4:
|
||||
case TextureFormat::RGBA_ASTC_5x4:
|
||||
case TextureFormat::RGBA_ASTC_5x5:
|
||||
case TextureFormat::RGBA_ASTC_6x5:
|
||||
case TextureFormat::RGBA_ASTC_6x6:
|
||||
case TextureFormat::RGBA_ASTC_8x5:
|
||||
case TextureFormat::RGBA_ASTC_8x6:
|
||||
case TextureFormat::RGBA_ASTC_8x8:
|
||||
case TextureFormat::RGBA_ASTC_10x5:
|
||||
case TextureFormat::RGBA_ASTC_10x6:
|
||||
case TextureFormat::RGBA_ASTC_10x8:
|
||||
case TextureFormat::RGBA_ASTC_10x10:
|
||||
case TextureFormat::RGBA_ASTC_12x10:
|
||||
case TextureFormat::RGBA_ASTC_12x12:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
|
||||
return TextureType::FLOAT;
|
||||
}
|
||||
|
||||
return TextureType::FLOAT;
|
||||
}
|
||||
|
||||
size_t getFormatComponentCount(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::R8:
|
||||
|
||||
@@ -672,22 +672,8 @@ void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
// nothing to do, timer query was constructed in createTimerQueryS
|
||||
}
|
||||
|
||||
const char* toString(DescriptorType type) {
|
||||
switch (type) {
|
||||
case DescriptorType::UNIFORM_BUFFER:
|
||||
return "UNIFORM_BUFFER";
|
||||
case DescriptorType::SHADER_STORAGE_BUFFER:
|
||||
return "SHADER_STORAGE_BUFFER";
|
||||
case DescriptorType::SAMPLER:
|
||||
return "SAMPLER";
|
||||
case DescriptorType::INPUT_ATTACHMENT:
|
||||
return "INPUT_ATTACHMENT";
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
return "SAMPLER_EXTERNAL";
|
||||
}
|
||||
}
|
||||
|
||||
const char* toString(ShaderStageFlags flags) {
|
||||
UTILS_UNUSED
|
||||
static const char* toString(ShaderStageFlags flags) {
|
||||
std::vector<const char*> stages;
|
||||
if (any(flags & ShaderStageFlags::VERTEX)) {
|
||||
stages.push_back("VERTEX");
|
||||
|
||||
@@ -185,6 +185,7 @@ inline MTLPixelFormat getMetalFormat(PixelDataFormat format, PixelDataType type)
|
||||
CONVERT(RGBA_INTEGER, UINT, RGBA32Uint);
|
||||
CONVERT(RGBA_INTEGER, INT, RGBA32Sint);
|
||||
CONVERT(RGBA, FLOAT, RGBA32Float);
|
||||
CONVERT(DEPTH_COMPONENT, FLOAT, Depth32Float);
|
||||
#undef CONVERT
|
||||
|
||||
return MTLPixelFormatInvalid;
|
||||
|
||||
@@ -1356,7 +1356,25 @@ id<MTLArgumentEncoder> MetalDescriptorSetLayout::getArgumentEncoderSlow(id<MTLDe
|
||||
[arguments addObject:bufferArgument];
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor];
|
||||
textureArgument.index = binding.binding * 2;
|
||||
@@ -1472,7 +1490,25 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
|
||||
atIndex:binding.binding * 2];
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
auto found = textures.find(binding.binding);
|
||||
if (found == textures.end()) {
|
||||
|
||||
@@ -45,9 +45,6 @@ PlatformMetal::~PlatformMetal() noexcept {
|
||||
}
|
||||
|
||||
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
|
||||
? DrawableFailureBehavior::ABORT_FRAME
|
||||
: DrawableFailureBehavior::PANIC;
|
||||
return MetalDriverFactory::create(this, driverConfig);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,9 +69,8 @@ public:
|
||||
assert_invariant(set < MAX_DESCRIPTOR_SET_COUNT);
|
||||
assert_invariant(binding < MAX_DESCRIPTOR_COUNT);
|
||||
assert_invariant(entry.binding < 128); // we reserve 1 bit for the type right now
|
||||
mStorage[set][binding] = { (uint8_t)entry.binding,
|
||||
entry.type == DescriptorType::SAMPLER ||
|
||||
entry.type == DescriptorType::SAMPLER_EXTERNAL };
|
||||
mStorage[set][binding] = { uint8_t(entry.binding),
|
||||
DescriptorSetLayoutBinding::isSampler(entry.type) };
|
||||
mActiveDescriptors[set].set(binding);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,26 @@ GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle ds
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
if (UTILS_UNLIKELY(gl.isES2())) {
|
||||
desc.emplace<SamplerGLES2>();
|
||||
|
||||
@@ -175,7 +175,25 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
if (!entry.name.empty()) {
|
||||
GLint const loc = glGetUniformLocation(program, entry.name.c_str());
|
||||
|
||||
@@ -142,6 +142,10 @@ public:
|
||||
return mPortabilitySubsetFeatures.imageView2DOn3DImage == VK_TRUE;
|
||||
}
|
||||
|
||||
inline bool isUnifiedMemoryArchitecture() const noexcept {
|
||||
return mIsUnifiedMemoryArchitecture;
|
||||
}
|
||||
|
||||
private:
|
||||
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
|
||||
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
|
||||
@@ -164,6 +168,7 @@ private:
|
||||
bool mDebugUtilsSupported = false;
|
||||
bool mLazilyAllocatedMemorySupported = false;
|
||||
bool mProtectedMemorySupported = false;
|
||||
bool mIsUnifiedMemoryArchitecture = false;
|
||||
|
||||
fvkutils::VkFormatList mDepthStencilFormats;
|
||||
fvkutils::VkFormatList mBlittableDepthStencilFormats;
|
||||
|
||||
@@ -93,7 +93,26 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.externalSampler);
|
||||
UTILS_FALLTHROUGH;
|
||||
case DescriptorType::SAMPLER: {
|
||||
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT: {
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -631,6 +631,21 @@ fvkutils::VkFormatList findBlittableDepthStencilFormats(VkPhysicalDevice device)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the GPU has a unified memory architecture.
|
||||
*/
|
||||
bool hasUnifiedMemoryArchitecture(VkPhysicalDeviceMemoryProperties memoryProperties) noexcept {
|
||||
// Try to identify if the platform is running on a Unified Memory Architecture by inspecting the
|
||||
// memory heap flags, if they are all VK_MEMORY_HEAP_DEVICE_LOCAL_BIT it's UMA, otherwise not
|
||||
// enough information to make a decision, so default to false.
|
||||
for (uint32_t i = 0; i < memoryProperties.memoryHeapCount; ++i) {
|
||||
if ((memoryProperties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
using SwapChainPtr = VulkanPlatform::SwapChainPtr;
|
||||
@@ -864,6 +879,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
}
|
||||
}
|
||||
|
||||
context.mIsUnifiedMemoryArchitecture = hasUnifiedMemoryArchitecture(context.mMemoryProperties);
|
||||
|
||||
#ifdef NDEBUG
|
||||
// If we are in release build, we should not have turned on debug extensions
|
||||
FILAMENT_CHECK_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@@ -125,21 +126,64 @@ namespace {
|
||||
return module;
|
||||
}
|
||||
|
||||
// This is a 1 to 1 mapping of the ReservedSpecializationConstants enum in EngineEnums.h
|
||||
// The _hack is a workaround until https://issues.chromium.org/issues/42250586 is resolved
|
||||
// This workaround is the same one being used on the generateSpecializationConstant() function
|
||||
wgpu::StringView getSpecConstantStringId(uint32_t id) {
|
||||
switch (id) {
|
||||
case 0:
|
||||
return "0";// BACKEND_FEATURE_LEVEL_hack
|
||||
case 1:
|
||||
return "1";// CONFIG_MAX_INSTANCES_hack
|
||||
case 2:
|
||||
return "2";// ONFIG_STATIC_TEXTURE_TARGET_WORKAROUND_hack
|
||||
case 3:
|
||||
return "3";// CONFIG_SRGB_SWAPCHAIN_EMULATION_hack
|
||||
case 4:
|
||||
return "4";// CONFIG_FROXEL_BUFFER_HEIGHT_hack
|
||||
case 5:
|
||||
return "5";// CONFIG_POWER_VR_SHADER_WORKAROUNDS_hack
|
||||
case 6:
|
||||
return "6";// CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP_hack
|
||||
case 7:
|
||||
return "7";// CONFIG_DEBUG_FROXEL_VISUALIZATION_hack
|
||||
case 8:
|
||||
return "8";// CONFIG_STEREO_EYE_COUNT_hack
|
||||
case 9:
|
||||
return "9";// CONFIG_SH_BANDS_COUNT_hack
|
||||
case 10:
|
||||
return "10";// CONFIG_SHADOW_SAMPLING_METHOD_hack
|
||||
default:
|
||||
PANIC_POSTCONDITION("Unknown/unhandled spec constant key/id: %d", id);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<wgpu::ConstantEntry> convertConstants(
|
||||
utils::FixedCapacityVector<filament::backend::Program::SpecializationConstant> const&
|
||||
constantsInfo) {
|
||||
std::vector<wgpu::ConstantEntry> constants(constantsInfo.size());
|
||||
for (size_t i = 0; i < constantsInfo.size(); i++) {
|
||||
filament::backend::Program::SpecializationConstant const& specConstant = constantsInfo[i];
|
||||
wgpu::ConstantEntry& constantEntry = constants[i];
|
||||
constantEntry.key = wgpu::StringView(std::to_string(specConstant.id));
|
||||
if (auto* v = std::get_if<int32_t>(&specConstant.value)) {
|
||||
constantEntry.value = static_cast<double>(*v);
|
||||
} else if (auto* f = std::get_if<float>(&specConstant.value)) {
|
||||
constantEntry.value = static_cast<double>(*f);
|
||||
} else if (auto* b = std::get_if<bool>(&specConstant.value)) {
|
||||
constantEntry.value = *b ? 0.0 : 1.0;
|
||||
std::vector<wgpu::ConstantEntry> constants;
|
||||
constants.reserve(constantsInfo.size());
|
||||
for (filament::backend::Program::SpecializationConstant const& constant: constantsInfo) {
|
||||
// CONFIG_MAX_INSTANCES (1) and CONFIG_FROXEL_BUFFER_HEIGHT (4) will not be present
|
||||
// as constant overrides in the generated WGSL, because WGSL doesn't support specialization
|
||||
// constants as an array length
|
||||
// More information at https://github.com/gpuweb/gpuweb/issues/572#issuecomment-649760005
|
||||
// CONFIG_SRGB_SWAPCHAIN_EMULATION (3) is being skipped all together since it's only
|
||||
// included for the case of mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0, which should
|
||||
// not be possible for WebGPU
|
||||
if (constant.id == 1 || constant.id == 3 || constant.id == 4) {
|
||||
continue;
|
||||
}
|
||||
double value = 0.0;
|
||||
if (auto* v = std::get_if<int32_t>(&constant.value)) {
|
||||
value = static_cast<double>(*v);
|
||||
} else if (auto* f = std::get_if<float>(&constant.value)) {
|
||||
value = static_cast<double>(*f);
|
||||
} else if (auto* b = std::get_if<bool>(&constant.value)) {
|
||||
value = *b ? 0.0 : 1.0;
|
||||
}
|
||||
constants.push_back(
|
||||
wgpu::ConstantEntry{ .key = getSpecConstantStringId(constant.id), .value = value });
|
||||
}
|
||||
return constants;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <utils/Hash.h>
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
|
||||
#include "WebGPUPipelineCreation.h"
|
||||
@@ -389,9 +389,7 @@ Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept { return allocHandle<WGPUTexture>(); }
|
||||
|
||||
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
||||
return allocHandle<WGPUProgram>();
|
||||
@@ -410,7 +408,7 @@ Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||
@@ -438,7 +436,7 @@ Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||
@@ -450,15 +448,15 @@ Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcep
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImage2S() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
@@ -470,6 +468,8 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
|
||||
mDevice, flags);
|
||||
assert_invariant(mSwapChain);
|
||||
WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(mDevice,
|
||||
mSwapChain->getColorFormat(), mSwapChain->getDepthFormat());
|
||||
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
|
||||
"background components have been instantiated/selected, such as surface/screen, "
|
||||
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
||||
@@ -512,30 +512,46 @@ void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byte
|
||||
|
||||
void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage) {}
|
||||
TextureUsage usage) {
|
||||
constructHandle<WGPUTexture>(th, target, levels, format, samples, w, h, depth, usage, mDevice);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount) {}
|
||||
uint8_t baseLevel, uint8_t levelCount) {
|
||||
auto source = handleCast<WGPUTexture>(srch);
|
||||
|
||||
constructHandle<WGPUTexture>(th, source, baseLevel, levelCount);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||
backend::TextureSwizzle a) {}
|
||||
backend::TextureSwizzle a) {
|
||||
PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {}
|
||||
Platform::ExternalImageHandleRef externalImage) {
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* externalImage) {}
|
||||
void* externalImage) {
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* image, uint32_t plane) {}
|
||||
void* image, uint32_t plane) {
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType target,
|
||||
uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h,
|
||||
uint32_t depth, TextureUsage usage) {}
|
||||
uint32_t depth, TextureUsage usage) {
|
||||
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
||||
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {
|
||||
@@ -575,7 +591,7 @@ void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh
|
||||
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||
Handle<HwDescriptorSetLayout> dslh) {
|
||||
auto layout = handleCast<WebGPUDescriptorSetLayout>(dslh);
|
||||
constructHandle<WebGPUDescriptorSet>(dsh, layout->getLayout(), layout->getLayoutSize());
|
||||
constructHandle<WebGPUDescriptorSet>(dsh, layout->getLayout(), layout->getBindGroupEntries(), layout->label);
|
||||
}
|
||||
|
||||
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
|
||||
@@ -612,7 +628,7 @@ FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fh) {
|
||||
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
|
||||
// the GPU supports the given texture format with non-zero optimal tiling features.
|
||||
bool WebGPUDriver::isTextureFormatSupported(TextureFormat format) {
|
||||
return true;
|
||||
return WGPUTexture::fToWGPUTextureFormat(format) != wgpu::TextureFormat::Undefined;
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureSwizzleSupported() {
|
||||
@@ -760,8 +776,31 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
|
||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) {
|
||||
assert_invariant(mCommandEncoder);
|
||||
|
||||
auto* renderTarget = handleCast<WGPURenderTarget>(rth);
|
||||
// if (renderTarget == mDefaultRenderTarget) {
|
||||
// FWGPU_LOGW << "Default render target"
|
||||
// << utils::io::endl;
|
||||
// } else {
|
||||
// FWGPU_LOGW << "Non Default render target"
|
||||
// << utils::io::endl;
|
||||
// }
|
||||
wgpu::RenderPassDescriptor renderPassDescriptor2;
|
||||
wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{
|
||||
.view = mSwapChain->getDepthTextureView(),
|
||||
.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH),
|
||||
.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH),
|
||||
.depthClearValue = static_cast<float>(params.clearDepth),
|
||||
.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0,
|
||||
.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL),
|
||||
.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL),
|
||||
.stencilClearValue = params.clearStencil,
|
||||
.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0
|
||||
};
|
||||
renderTarget->setUpRenderPassAttachments(renderPassDescriptor2, mTextureView, params);
|
||||
renderPassDescriptor2.depthStencilAttachment = &depthStencilAttachment;
|
||||
// TODO: Remove this code once WebGPU pipeline is implemented
|
||||
static float red = 1.0f;
|
||||
if (red - 0.01 > 0) {
|
||||
@@ -786,7 +825,7 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
.timestampWrites = nullptr,
|
||||
};
|
||||
|
||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor2);
|
||||
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
|
||||
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
|
||||
}
|
||||
@@ -877,6 +916,14 @@ void WebGPUDriver::blit(
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
// TODO Investigate implications of this hash more closely. Vulkan has a whole class
|
||||
// VulkanPipelineCache to handle this, may be missing nuance
|
||||
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
|
||||
auto hash = pipleineStateHasher(pipelineState);
|
||||
if(mPipelineMap.find(hash) != mPipelineMap.end()){
|
||||
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
|
||||
return;
|
||||
}
|
||||
const auto* program = handleCast<WGPUProgram>(pipelineState.program);
|
||||
assert_invariant(program);
|
||||
assert_invariant(program->computeShaderModule == nullptr &&
|
||||
@@ -913,8 +960,8 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
*vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState,
|
||||
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
|
||||
mSwapChain->getDepthFormat());
|
||||
// TODO: uncomment once we have a valid pipeline to set
|
||||
// mRenderPassEncoder.SetPipeline(pipeline);
|
||||
mPipelineMap[hash] = pipeline;
|
||||
mRenderPassEncoder.SetPipeline(pipeline);
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
@@ -934,10 +981,18 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
// Calling DrawIndexed with "firstInstance = 0" results in a NON spinning triangle
|
||||
// mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
|
||||
// Calling DrawIndexed with "firstInstance = 1" results in a spinning triangle
|
||||
mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 1);
|
||||
// Calling Draw with "firstInstance = 0" results in a NON spinning triangle
|
||||
// Calling Draw with "firstInstance = 1" results in a spinning triangle
|
||||
// mRenderPassEncoder.Draw(indexCount, instanceCount, 0, 1);
|
||||
}
|
||||
|
||||
void WebGPUDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
void WebGPUDriver::draw(PipelineState, Handle<HwRenderPrimitive>, uint32_t indexOffset,
|
||||
uint32_t indexCount, uint32_t instanceCount) {
|
||||
draw2(indexOffset, indexCount, instanceCount);
|
||||
}
|
||||
|
||||
void WebGPUDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
@@ -973,34 +1028,157 @@ void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> dsh,
|
||||
|
||||
void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
|
||||
backend::descriptor_binding_t binding, Handle<HwTexture> th, SamplerParams params) {
|
||||
/*
|
||||
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
|
||||
auto texture = handleCast<WGPUTexture>(th);
|
||||
|
||||
// TODO very high odds badd assumptions are in here about handling HwTexture. Revisit with more
|
||||
// understanding. Right now assuming there is a wgpu::TextureView filled in
|
||||
if (!bindGroup->getIsLocked()) {
|
||||
// Dawn will cache duplicate samplers, so we don't strictly need to maintain a cache.
|
||||
// Making a cache might save us minor perf by reducing param translation
|
||||
auto sampler = makeSampler(params);
|
||||
// TODO making assumptions that size and offset mean the same thing here.
|
||||
wgpu::BindGroupEntry tEntry{ .binding = static_cast<uint32_t>(binding * 2),
|
||||
.textureView = texture->texView };
|
||||
.textureView = texture->getTexView() };
|
||||
bindGroup->addEntry(tEntry.binding, std::move(tEntry));
|
||||
|
||||
wgpu::BindGroupEntry sEntry{ .binding = static_cast<uint32_t>(binding * 2 + 1),
|
||||
.sampler = texture->sampler };
|
||||
.sampler = sampler };
|
||||
bindGroup->addEntry(sEntry.binding, std::move(sEntry));
|
||||
}
|
||||
//TODO Just the setup, this function stilll needs the rest of logic implemented
|
||||
*/
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh, backend::descriptor_set_t set,
|
||||
backend::DescriptorSetOffsetArray&& offsets) {
|
||||
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
|
||||
// TODO: presume we need this, use it. Probably Encoder::SetBindGroup
|
||||
auto wbg = bindGroup->lockAndReturn(mDevice);
|
||||
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh,
|
||||
backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) {
|
||||
const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
|
||||
const auto wbg = bindGroup->lockAndReturn(mDevice);
|
||||
assert_invariant(mRenderPassEncoder);
|
||||
// TODO is this how we should be getting the dynamic offsets?
|
||||
// should we add offsets for unused entries or is the input already have them?
|
||||
// this implementation assumes unused entries are not provided, and adds dummy values.
|
||||
// The count also includes unused entities, as not doing so produces errors
|
||||
const size_t dynamicOffsetCount = bindGroup->countEntitiesWithDynamicOffsets();
|
||||
uint32_t const* const dynamicOffsetsWithUnused = bindGroup->setDynamicOffsets(offsets.data());
|
||||
mRenderPassEncoder.SetBindGroup(setIndex, wbg, dynamicOffsetCount, dynamicOffsetsWithUnused);
|
||||
}
|
||||
|
||||
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
}
|
||||
wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
|
||||
wgpu::SamplerDescriptor desc;
|
||||
|
||||
desc.label = "TODO";
|
||||
desc.addressModeU = fWrapModeToWAddressMode(params.wrapS);
|
||||
desc.addressModeV = fWrapModeToWAddressMode(params.wrapR);
|
||||
desc.addressModeW = fWrapModeToWAddressMode(params.wrapT);
|
||||
if (params.compareMode == SamplerCompareMode::COMPARE_TO_TEXTURE) {
|
||||
switch (params.filterMag) {
|
||||
case SamplerMagFilter::NEAREST: {
|
||||
desc.magFilter = wgpu::FilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMagFilter::LINEAR: {
|
||||
desc.magFilter = wgpu::FilterMode::Linear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (params.filterMin) {
|
||||
case SamplerMinFilter::NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
|
||||
// suffice
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Undefined;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
|
||||
// suffice
|
||||
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Undefined;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
|
||||
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (params.compareFunc) {
|
||||
case SamplerCompareFunc::LE: {
|
||||
desc.compare = wgpu::CompareFunction::LessEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::GE: {
|
||||
desc.compare = wgpu::CompareFunction::GreaterEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::L: {
|
||||
desc.compare = wgpu::CompareFunction::Less;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::G: {
|
||||
desc.compare = wgpu::CompareFunction::Greater;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::E: {
|
||||
desc.compare = wgpu::CompareFunction::Equal;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::NE: {
|
||||
desc.compare = wgpu::CompareFunction::NotEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::A: {
|
||||
desc.compare = wgpu::CompareFunction::Always;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::N: {
|
||||
desc.compare = wgpu::CompareFunction::Never;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
desc.maxAnisotropy = 1u << params.anisotropyLog2;
|
||||
|
||||
|
||||
// Unused: WGPU lodMinClamp/lodMaxClamp
|
||||
|
||||
return mDevice.CreateSampler(&desc);
|
||||
}
|
||||
wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& fWrapMode) {
|
||||
switch (fWrapMode) {
|
||||
case SamplerWrapMode::CLAMP_TO_EDGE: {
|
||||
return wgpu::AddressMode::ClampToEdge;
|
||||
break;
|
||||
}
|
||||
case SamplerWrapMode::REPEAT: {
|
||||
return wgpu::AddressMode::Repeat;
|
||||
break;
|
||||
}
|
||||
case SamplerWrapMode::MIRRORED_REPEAT: {
|
||||
return wgpu::AddressMode::MirrorRepeat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return wgpu::AddressMode::Undefined;
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -56,7 +56,8 @@ private:
|
||||
explicit WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
|
||||
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
|
||||
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
|
||||
[[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage);
|
||||
template<typename GPUBufferObject>
|
||||
void updateGPUBuffer(GPUBufferObject* gpuBufferObject, BufferDescriptor&& bufferDescriptor,
|
||||
uint32_t byteOffset) {
|
||||
@@ -92,6 +93,8 @@ private:
|
||||
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
|
||||
wgpu::CommandBuffer mCommandBuffer = nullptr;
|
||||
WGPURenderTarget* mDefaultRenderTarget = nullptr;
|
||||
|
||||
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
|
||||
/*
|
||||
* Driver interface
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,16 @@
|
||||
|
||||
#include "WebGPUHandles.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
constexpr wgpu::BufferUsage getBufferObjectUsage(
|
||||
@@ -110,6 +119,44 @@ wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool nor
|
||||
}
|
||||
}
|
||||
|
||||
wgpu::StringView getUserTextureLabel(filament::backend::SamplerType target) {
|
||||
// TODO will be helpful to get more useful info than this
|
||||
using filament::backend::SamplerType;
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
return "a_2D_user_texture";
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
return "a_2D_array_user_texture";
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return "a_cube_map_user_texture";
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
return "an_external_user_texture";
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return "a_3D_user_texture";
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return "a_cube_mape_array_user_texture";
|
||||
}
|
||||
}
|
||||
|
||||
wgpu::StringView getUserTextureViewLabel(filament::backend::SamplerType target) {
|
||||
// TODO will be helpful to get more useful info than this
|
||||
using filament::backend::SamplerType;
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
return "a_2D_user_texture_view";
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
return "a_2D_array_user_texture_view";
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return "a_cube_map_user_texture_view";
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
return "an_external_user_texture_view";
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return "a_3D_user_texture_view";
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return "a_cube_mape_array_user_texture_view";
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -137,7 +184,7 @@ WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attribut
|
||||
mAttributes[attrib.buffer].push_back({
|
||||
.format = vertexFormat,
|
||||
.offset = attrib.offset,
|
||||
.shaderLocation = static_cast<uint32_t>(mAttributes[attrib.buffer].size()),
|
||||
.shaderLocation = attribIndex,
|
||||
});
|
||||
|
||||
mVertexBufferLayout[attrib.buffer].stepMode = wgpu::VertexStepMode::Vertex;
|
||||
@@ -192,99 +239,387 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
wgpu::Device const& device) {
|
||||
assert_invariant(device);
|
||||
|
||||
std::string baseLabel;
|
||||
if (std::holds_alternative<utils::StaticString>(layout.label)) {
|
||||
const auto& temp = std::get_if<utils::StaticString>(&layout.label);
|
||||
baseLabel = temp->c_str();
|
||||
} else if (std::holds_alternative<utils::CString>(layout.label)) {
|
||||
const auto& temp = std::get_if<utils::CString>(&layout.label);
|
||||
baseLabel = temp->c_str();
|
||||
}
|
||||
label = baseLabel;
|
||||
|
||||
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
|
||||
// debugging. For now, hack an incrementing value.
|
||||
static int layoutNum = 0;
|
||||
|
||||
uint samplerCount =
|
||||
unsigned int samplerCount =
|
||||
std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) {
|
||||
return fEntry.type == DescriptorType::SAMPLER ||
|
||||
fEntry.type == DescriptorType::SAMPLER_EXTERNAL;
|
||||
return DescriptorSetLayoutBinding::isSampler(fEntry.type);
|
||||
});
|
||||
|
||||
|
||||
std::vector<wgpu::BindGroupLayoutEntry> wEntries;
|
||||
wEntries.reserve(layout.bindings.size() + samplerCount);
|
||||
mBindGroupEntries.reserve(wEntries.capacity());
|
||||
|
||||
for (auto fEntry: layout.bindings) {
|
||||
auto& wEntry = wEntries.emplace_back();
|
||||
auto& entryInfo = mBindGroupEntries.emplace_back();
|
||||
wEntry.visibility = filamentStageToWGPUStage(fEntry.stageFlags);
|
||||
wEntry.binding = fEntry.binding * 2;
|
||||
entryInfo.binding = wEntry.binding;
|
||||
wgpu::BindGroupLayoutEntry* sEntry;
|
||||
|
||||
switch (fEntry.type) {
|
||||
// TODO Metal treats these the same. Is this fine?
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
case DescriptorType::SAMPLER: {
|
||||
// Sampler binding is 2n+1 due to split.
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT: {
|
||||
auto& samplerEntry = wEntries.emplace_back();
|
||||
auto& samplerEntryInfo = mBindGroupEntries.emplace_back();
|
||||
sEntry = &samplerEntry;
|
||||
samplerEntry.binding = fEntry.binding * 2 + 1;
|
||||
samplerEntryInfo.binding = samplerEntry.binding;
|
||||
samplerEntryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::SAMPLER;
|
||||
samplerEntry.visibility = wEntry.visibility;
|
||||
// We are simply hoping that undefined and defaults suffices here.
|
||||
samplerEntry.sampler.type = wgpu::SamplerBindingType::Undefined;
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Undefined;
|
||||
samplerEntry.sampler.type = wgpu::SamplerBindingType::NonFiltering; // Example default
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Float; // Example default
|
||||
break;
|
||||
}
|
||||
case DescriptorType::UNIFORM_BUFFER: {
|
||||
wEntry.buffer.hasDynamicOffset =
|
||||
any(fEntry.flags & DescriptorFlags::DYNAMIC_OFFSET);
|
||||
entryInfo.hasDynamicOffset = wEntry.buffer.hasDynamicOffset;
|
||||
wEntry.buffer.type = wgpu::BufferBindingType::Uniform;
|
||||
// TODO: Ideally we fill minBindingSize
|
||||
break;
|
||||
}
|
||||
|
||||
case DescriptorType::INPUT_ATTACHMENT: {
|
||||
// TODO: support INPUT_ATTACHMENT. Metal does not currently.
|
||||
PANIC_POSTCONDITION("Input Attachment is not supported");
|
||||
break;
|
||||
}
|
||||
|
||||
case DescriptorType::SHADER_STORAGE_BUFFER: {
|
||||
// TODO: Vulkan does not support this, can we?
|
||||
PANIC_POSTCONDITION("Shader storage is not supported");
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
PANIC_POSTCONDITION("External Sampler is not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Currently flags are only used to specify dynamic offset.
|
||||
|
||||
// UNUSED
|
||||
// fEntry.count
|
||||
switch (fEntry.type) {
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::e2D;
|
||||
entryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW_2D;
|
||||
break;
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::e2DArray;
|
||||
entryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW_2DA;
|
||||
break;
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::Cube;
|
||||
entryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW_CUBE;
|
||||
break;
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::CubeArray;
|
||||
entryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW_CUBEA;
|
||||
break;
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::e3D;
|
||||
entryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW_3D;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (fEntry.type) {
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
|
||||
break;
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Sint;
|
||||
break;
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Uint;
|
||||
break;
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
//Dummies complain if you set this correctly without having an associated dummy
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Depth;
|
||||
sEntry->sampler.type = wgpu::SamplerBindingType::Comparison;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// fEntry.count is unused currently
|
||||
}
|
||||
|
||||
std::string label = "layout_" + baseLabel + std::to_string(++layoutNum) ;
|
||||
wgpu::BindGroupLayoutDescriptor layoutDescriptor{
|
||||
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
|
||||
// debugging. For now, hack an incrementing value.
|
||||
.label{ "layout_" + std::to_string(++layoutNum) },
|
||||
.label{label.c_str()}, // Use .c_str() if label needs to be const char*
|
||||
.entryCount = wEntries.size(),
|
||||
.entries = wEntries.data()
|
||||
};
|
||||
// TODO Do we need to defer this until we have more info on textures and samplers??
|
||||
mLayoutSize = wEntries.size();
|
||||
mLayout = device.CreateBindGroupLayout(&layoutDescriptor);
|
||||
}
|
||||
|
||||
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
|
||||
|
||||
WebGPUDescriptorSet::WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize)
|
||||
wgpu::Buffer WebGPUDescriptorSet::sDummyUniformBuffer = nullptr;
|
||||
std::unordered_map<wgpu::TextureDimension, wgpu::Texture> WebGPUDescriptorSet::sDummyTextureMap;
|
||||
std::unordered_map<wgpu::TextureViewDimension, wgpu::TextureView> WebGPUDescriptorSet::sDummyTextureViewMap;
|
||||
wgpu::Sampler WebGPUDescriptorSet::sDummySampler = nullptr;
|
||||
|
||||
void WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(wgpu::Device const& device,
|
||||
wgpu::TextureFormat aColorFormat, wgpu::TextureFormat aDepthFormat) {
|
||||
if (!sDummyUniformBuffer) {
|
||||
wgpu::BufferDescriptor bufferDescriptor{
|
||||
.label = "dummy_uniform_not_to_be_used",
|
||||
.usage = wgpu::BufferUsage::Uniform,
|
||||
.size = 4
|
||||
};
|
||||
sDummyUniformBuffer = device.CreateBuffer(&bufferDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer)
|
||||
<< "Failed to create dummy uniform buffer?";
|
||||
}
|
||||
if (sDummyTextureMap.empty() || sDummyTextureViewMap.empty()) {
|
||||
wgpu::TextureDescriptor textureDescriptor{
|
||||
.label = "dummy_texture_not_to_be_used",
|
||||
.usage = wgpu::TextureUsage::TextureBinding,
|
||||
.dimension = wgpu::TextureDimension::e2D,
|
||||
.size = wgpu::Extent3D{ .width = 4, .height = 4, .depthOrArrayLayers = 1 },
|
||||
.format = aColorFormat,
|
||||
// .viewFormatCount = 1,
|
||||
// .viewFormats = &aDepthFormat
|
||||
};
|
||||
sDummyTextureMap[wgpu::TextureDimension::e2D] = device.CreateTexture(&textureDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyTextureMap[wgpu::TextureDimension::e2D])
|
||||
<< "Failed to create dummy texture 2D?";
|
||||
textureDescriptor.dimension = wgpu::TextureDimension::e3D;
|
||||
sDummyTextureMap[wgpu::TextureDimension::e3D] = device.CreateTexture(&textureDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyTextureMap[wgpu::TextureDimension::e3D])
|
||||
<< "Failed to create dummy texture 3D?";
|
||||
|
||||
//Hacking as an experiment, treating 1D as cube arbitrarilly since my map key was bad idea
|
||||
textureDescriptor.dimension = wgpu::TextureDimension::e2D;
|
||||
textureDescriptor.size.depthOrArrayLayers = 6;
|
||||
sDummyTextureMap[wgpu::TextureDimension::e1D] = device.CreateTexture(&textureDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyTextureMap[wgpu::TextureDimension::e1D])
|
||||
<< "Failed to create dummy texture 3D?";
|
||||
|
||||
|
||||
wgpu::TextureViewDescriptor textureViewDescriptor{
|
||||
.label = "dummy_texture_view_not_to_be_used",
|
||||
};
|
||||
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
|
||||
sDummyTextureViewMap[wgpu::TextureViewDimension::e2D] =
|
||||
sDummyTextureMap[wgpu::TextureDimension::e2D].CreateView(&textureViewDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyTextureViewMap[wgpu::TextureViewDimension::e2D])
|
||||
<< "Failed to create dummy texture view?";
|
||||
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2DArray;
|
||||
sDummyTextureViewMap[wgpu::TextureViewDimension::e2DArray] =
|
||||
sDummyTextureMap[wgpu::TextureDimension::e2D].CreateView(&textureViewDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyTextureViewMap[wgpu::TextureViewDimension::e2D])
|
||||
<< "Failed to create dummy texture view?";
|
||||
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2DArray;
|
||||
sDummyTextureViewMap[wgpu::TextureViewDimension::e2DArray] =
|
||||
sDummyTextureMap[wgpu::TextureDimension::e2D].CreateView(&textureViewDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyTextureViewMap[wgpu::TextureViewDimension::e2DArray])
|
||||
<< "Failed to create dummy texture view?";
|
||||
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e3D;
|
||||
sDummyTextureViewMap[wgpu::TextureViewDimension::e3D] =
|
||||
sDummyTextureMap[wgpu::TextureDimension::e3D].CreateView(&textureViewDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyTextureViewMap[wgpu::TextureViewDimension::e3D])
|
||||
<< "Failed to create dummy texture view?";
|
||||
|
||||
textureViewDescriptor.arrayLayerCount = 6;
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::Cube;
|
||||
sDummyTextureViewMap[wgpu::TextureViewDimension::Cube] =
|
||||
sDummyTextureMap[wgpu::TextureDimension::e1D].CreateView(&textureViewDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyTextureViewMap[wgpu::TextureViewDimension::Cube])
|
||||
<< "Failed to create dummy texture view?";
|
||||
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::CubeArray;
|
||||
sDummyTextureViewMap[wgpu::TextureViewDimension::CubeArray] =
|
||||
sDummyTextureMap[wgpu::TextureDimension::e1D].CreateView(&textureViewDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyTextureViewMap[wgpu::TextureViewDimension::CubeArray])
|
||||
<< "Failed to create dummy texture view?";
|
||||
}
|
||||
if (!sDummySampler) {
|
||||
wgpu::SamplerDescriptor samplerDescriptor{ .label = "dummy_sampler_not_to_be_used" , .addressModeU = wgpu::AddressMode::MirrorRepeat};
|
||||
sDummySampler = device.CreateSampler(&samplerDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer) << "Failed to create dummy sampler?";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<wgpu::BindGroupEntry> WebGPUDescriptorSet::createDummyEntriesSortedByBinding(
|
||||
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&
|
||||
bindGroupEntries) {
|
||||
assert_invariant(WebGPUDescriptorSet::sDummyUniformBuffer &&
|
||||
"Dummy uniform buffer must have been created before "
|
||||
"creating dummy bind group entries.");
|
||||
/* assert_invariant(
|
||||
WebGPUDescriptorSet::sDummyTexture &&
|
||||
"Dummy texture must have been created before creating dummy bind group entries.");
|
||||
assert_invariant(
|
||||
WebGPUDescriptorSet::sDummyTextureView &&
|
||||
"Dummy texture view must have been created before creating dummy bind group entries.");*/
|
||||
assert_invariant(
|
||||
WebGPUDescriptorSet::sDummySampler &&
|
||||
"Dummy sampler must have been created before creating dummy bind group entries.");
|
||||
using filament::backend::WebGPUDescriptorSetLayout;
|
||||
std::vector<wgpu::BindGroupEntry> entries;
|
||||
entries.reserve(bindGroupEntries.size());
|
||||
for (auto const& entryInfo: bindGroupEntries) {
|
||||
auto& entry = entries.emplace_back();
|
||||
entry.binding = entryInfo.binding;
|
||||
switch (entryInfo.type) {
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::UNIFORM_BUFFER:
|
||||
//entry.buffer = WebGPUDescriptorSet::sDummyUniformBuffer;
|
||||
break;
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::SAMPLER:
|
||||
// entry.sampler = WebGPUDescriptorSet::sDummySampler;
|
||||
break;
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW_2D:
|
||||
// entry.textureView = WebGPUDescriptorSet::sDummyTextureViewMap[wgpu::TextureViewDimension::e2D];
|
||||
break;
|
||||
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW_2DA:
|
||||
// entry.textureView = WebGPUDescriptorSet::sDummyTextureViewMap[wgpu::TextureViewDimension::e2DArray];
|
||||
break;
|
||||
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW_CUBE:
|
||||
// entry.textureView = WebGPUDescriptorSet::sDummyTextureViewMap[wgpu::TextureViewDimension::Cube];
|
||||
break;
|
||||
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW_CUBEA:
|
||||
// entry.textureView = WebGPUDescriptorSet::sDummyTextureViewMap[wgpu::TextureViewDimension::CubeArray];
|
||||
break;
|
||||
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW_3D:
|
||||
// entry.textureView = WebGPUDescriptorSet::sDummyTextureViewMap[wgpu::TextureViewDimension::e3D];
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
std::sort(entries.begin(), entries.end(),
|
||||
[](wgpu::BindGroupEntry const& a, wgpu::BindGroupEntry const& b) {
|
||||
return a.binding < b.binding;
|
||||
});
|
||||
return entries;
|
||||
}
|
||||
|
||||
WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
|
||||
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries, std::string label)
|
||||
: mLayout(layout),
|
||||
entries(layoutSize, wgpu::BindGroupEntry{}) {
|
||||
mEntriesSortedByBinding(createDummyEntriesSortedByBinding(bindGroupEntries)) {
|
||||
bglLabel = label;
|
||||
// Establish the size of entries based on the layout. This should be reliable and efficient.
|
||||
assert_invariant(INVALID_INDEX > mEntryIndexByBinding.size());
|
||||
for (size_t i = 0; i < mEntryIndexByBinding.size(); i++) {
|
||||
mEntryIndexByBinding[i] = INVALID_INDEX;
|
||||
}
|
||||
for (size_t index = 0; index < mEntriesSortedByBinding.size(); index++) {
|
||||
wgpu::BindGroupEntry const& entry = mEntriesSortedByBinding[index];
|
||||
assert_invariant(entry.binding < mEntryIndexByBinding.size());
|
||||
mEntryIndexByBinding[entry.binding] = static_cast<uint8_t>(index);
|
||||
}
|
||||
for (auto const& entry : bindGroupEntries) {
|
||||
if (entry.hasDynamicOffset) {
|
||||
assert_invariant(entry.binding < mEntriesByBindingWithDynamicOffsets.size());
|
||||
mEntriesByBindingWithDynamicOffsets[entry.binding] = true;
|
||||
}
|
||||
}
|
||||
mDynamicOffsets.reserve(mEntriesSortedByBinding.size());
|
||||
}
|
||||
|
||||
WebGPUDescriptorSet::~WebGPUDescriptorSet() {
|
||||
mBindGroup = nullptr;
|
||||
mLayout = nullptr;
|
||||
}
|
||||
WebGPUDescriptorSet::~WebGPUDescriptorSet() {}
|
||||
|
||||
wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
|
||||
if (mBindGroup) {
|
||||
return mBindGroup;
|
||||
}
|
||||
// TODO label? Should we just copy layout label?
|
||||
wgpu::BindGroupDescriptor desc{ .layout = mLayout,
|
||||
.entryCount = entries.size(),
|
||||
.entries = entries.data() };
|
||||
wgpu::BindGroupDescriptor desc{
|
||||
.layout = mLayout,
|
||||
.entryCount = mEntriesSortedByBinding.size(),
|
||||
.entries = mEntriesSortedByBinding.data()
|
||||
};
|
||||
for(auto e : mEntriesSortedByBinding){
|
||||
if(e.sampler.Get() == sDummySampler.Get()){
|
||||
printf("===R Dummy %s UBO %i\n", bglLabel.c_str(), e.binding/2);
|
||||
}
|
||||
for(auto dt : sDummyTextureViewMap){
|
||||
if(e.textureView.Get() == dt.second.Get())
|
||||
{
|
||||
printf("===R Dummy %s Tex %i\n", bglLabel.c_str(), e.binding/2);
|
||||
}
|
||||
}
|
||||
if(e.buffer.Get() == sDummyUniformBuffer.Get()){
|
||||
printf("===R Dummy %s Sam %i\n", bglLabel.c_str(),(e.binding-1)/2);
|
||||
}
|
||||
}
|
||||
mBindGroup = device.CreateBindGroup(&desc);
|
||||
FILAMENT_CHECK_POSTCONDITION(mBindGroup) << "Failed to create bind group?";
|
||||
// once we have created the bind group itself we should no longer need any other state
|
||||
mLayout = nullptr;
|
||||
mEntriesSortedByBinding.clear();
|
||||
mEntriesSortedByBinding.shrink_to_fit();
|
||||
return mBindGroup;
|
||||
}
|
||||
|
||||
void WebGPUDescriptorSet::addEntry(uint index, wgpu::BindGroupEntry&& entry) {
|
||||
void WebGPUDescriptorSet::addEntry(unsigned int index, wgpu::BindGroupEntry&& entry) {
|
||||
if (mBindGroup) {
|
||||
// We will keep getting hits from future updates, but shouldn't do anything
|
||||
// Filament guarantees this won't change after things have locked.
|
||||
@@ -292,6 +627,490 @@ void WebGPUDescriptorSet::addEntry(uint index, wgpu::BindGroupEntry&& entry) {
|
||||
}
|
||||
// TODO: Putting some level of trust that Filament is not going to reuse indexes or go past the
|
||||
// layout index for efficiency. Add guards if wrong.
|
||||
entries[index] = std::move(entry);
|
||||
FILAMENT_CHECK_POSTCONDITION(index < mEntryIndexByBinding.size())
|
||||
<< "impossible/invalid index for a descriptor/binding (our of range or >= "
|
||||
"MAX_DESCRIPTOR_COUNT) "
|
||||
<< index;
|
||||
uint8_t entryIndex = mEntryIndexByBinding[index];
|
||||
FILAMENT_CHECK_POSTCONDITION(
|
||||
entryIndex != INVALID_INDEX && entryIndex < mEntriesSortedByBinding.size())
|
||||
<< "Invalid binding " << index;
|
||||
entry.binding = index;
|
||||
mEntriesSortedByBinding[entryIndex] = std::move(entry);
|
||||
mEntriesByBindingAdded[index] = true;
|
||||
}
|
||||
|
||||
uint32_t const* WebGPUDescriptorSet::setDynamicOffsets(uint32_t const* offsets) {
|
||||
// mDynamicOffsets already reserves enough memory for the number of entries in the set
|
||||
mDynamicOffsets.clear();
|
||||
// this implementation copies the offsets to mDynamicOffsets, but also adds values for
|
||||
// unused entries TODO: is this necessary?
|
||||
size_t inputIndex = 0;
|
||||
size_t outputIndex = 0;
|
||||
for (auto const& entry : mEntriesSortedByBinding) {
|
||||
if (mEntriesByBindingWithDynamicOffsets[entry.binding]) {
|
||||
if (mEntriesByBindingAdded[entry.binding]) {
|
||||
mDynamicOffsets[outputIndex++] = offsets[inputIndex++];
|
||||
} else {
|
||||
mDynamicOffsets[outputIndex++] = 0; // dummy offset, as it was never added
|
||||
}
|
||||
}
|
||||
}
|
||||
return mDynamicOffsets.data();
|
||||
}
|
||||
|
||||
size_t WebGPUDescriptorSet::countEntitiesWithDynamicOffsets() const {
|
||||
return mEntriesByBindingWithDynamicOffsets.count();
|
||||
}
|
||||
|
||||
// From createTextureR
|
||||
WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
|
||||
wgpu::Device const& device) noexcept {
|
||||
assert_invariant(
|
||||
samples == 1 ||
|
||||
samples == 4 &&
|
||||
"An invalid number of samples were requested, as WGPU requires the sample "
|
||||
"count to either be 1 (no multisampling) or 4, at least as of April 2025 of "
|
||||
"the spec. See https://www.w3.org/TR/webgpu/#texture-creation or "
|
||||
"https://gpuweb.github.io/gpuweb/#multisample-state");
|
||||
// First, the texture aspect, starting with the defaults/basic configuration
|
||||
mUsage = fToWGPUTextureUsage(usage);
|
||||
mFormat = fToWGPUTextureFormat(format);
|
||||
wgpu::TextureDescriptor textureDescriptor{
|
||||
.label = getUserTextureLabel(target),
|
||||
.usage = mUsage,
|
||||
.dimension = target == SamplerType::SAMPLER_3D ? wgpu::TextureDimension::e3D
|
||||
: wgpu::TextureDimension::e2D,
|
||||
.size = { .width = width, .height = height, .depthOrArrayLayers = depth },
|
||||
.format = mFormat,
|
||||
.mipLevelCount = levels,
|
||||
.sampleCount = samples,
|
||||
// TODO Is this fine? Could do all-the-things, a naive mapping or get something from
|
||||
// Filament
|
||||
.viewFormatCount = 0,
|
||||
.viewFormats = nullptr,
|
||||
};
|
||||
// adjust for specific cases
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
mArrayLayerCount = 1;
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
textureDescriptor.size.depthOrArrayLayers = 6;
|
||||
mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
case SamplerType::SAMPLER_3D:
|
||||
mArrayLayerCount = 1;
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
textureDescriptor.size.depthOrArrayLayers = depth * 6;
|
||||
mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
|
||||
break;
|
||||
}
|
||||
assert_invariant(textureDescriptor.format != wgpu::TextureFormat::Undefined &&
|
||||
"Could not find appropriate WebGPU format");
|
||||
mTexture = device.CreateTexture(&textureDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(mTexture)
|
||||
<< "Failed to create texture for " << textureDescriptor.label;
|
||||
// Second, the texture view aspect
|
||||
mTexView = makeTextureView(0, levels, target);
|
||||
}
|
||||
|
||||
// From createTextureViewR
|
||||
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
|
||||
mTexture = src->mTexture;
|
||||
mTexView = makeTextureView(baseLevel, levelCount, target);
|
||||
}
|
||||
|
||||
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage) {
|
||||
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
|
||||
|
||||
// Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini
|
||||
// TODO Validate assumptions, revisit if issues.
|
||||
if (any(TextureUsage::BLIT_SRC & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::CopySrc;
|
||||
}
|
||||
if (any((TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE) & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::CopyDst;
|
||||
}
|
||||
if (any(TextureUsage::SAMPLEABLE & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::TextureBinding;
|
||||
}
|
||||
// WGPU Render attachment covers either color or stencil situation dependant
|
||||
// NOTE: Depth attachment isn't used this way in Vulkan but logically maps to WGPU docs. If
|
||||
// issues, investigate here.
|
||||
if (any((TextureUsage::COLOR_ATTACHMENT | TextureUsage::STENCIL_ATTACHMENT |
|
||||
TextureUsage::DEPTH_ATTACHMENT) &
|
||||
fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
}
|
||||
|
||||
// This is from Vulkan logic- if there are any issues try disabling this first, allows perf
|
||||
// benefit though
|
||||
const bool useTransientAttachment =
|
||||
// Usage consists of attachment flags only.
|
||||
none(fUsage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(fUsage & TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Depth resolve cannot use transient attachment because it uses a custom shader.
|
||||
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
|
||||
// restriction.
|
||||
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
|
||||
// and above to be able to support stencil resolve (along with depth).
|
||||
!(any(fUsage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
|
||||
if (useTransientAttachment) {
|
||||
retUsage |= wgpu::TextureUsage::TransientAttachment;
|
||||
}
|
||||
// NOTE: Unused wgpu flags:
|
||||
// StorageBinding
|
||||
// StorageAttachment
|
||||
|
||||
// NOTE: Unused Filament flags:
|
||||
// SUBPASS_INPUT VK goes to input attachment which we don't support right now
|
||||
// PROTECTED
|
||||
return retUsage;
|
||||
}
|
||||
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsage) {
|
||||
switch (fUsage) {
|
||||
case filament::backend::TextureFormat::R8:
|
||||
return wgpu::TextureFormat::R8Unorm;
|
||||
case filament::backend::TextureFormat::R8_SNORM:
|
||||
return wgpu::TextureFormat::R8Snorm;
|
||||
case filament::backend::TextureFormat::R8UI:
|
||||
return wgpu::TextureFormat::R8Uint;
|
||||
case filament::backend::TextureFormat::R8I:
|
||||
return wgpu::TextureFormat::R8Sint;
|
||||
case filament::backend::TextureFormat::STENCIL8:
|
||||
return wgpu::TextureFormat::Stencil8;
|
||||
case filament::backend::TextureFormat::R16F:
|
||||
return wgpu::TextureFormat::R16Float;
|
||||
case filament::backend::TextureFormat::R16UI:
|
||||
return wgpu::TextureFormat::R16Uint;
|
||||
case filament::backend::TextureFormat::R16I:
|
||||
return wgpu::TextureFormat::R16Sint;
|
||||
case filament::backend::TextureFormat::RG8:
|
||||
return wgpu::TextureFormat::RG8Unorm;
|
||||
case filament::backend::TextureFormat::RG8_SNORM:
|
||||
return wgpu::TextureFormat::RG8Snorm;
|
||||
case filament::backend::TextureFormat::RG8UI:
|
||||
return wgpu::TextureFormat::RG8Uint;
|
||||
case filament::backend::TextureFormat::RG8I:
|
||||
return wgpu::TextureFormat::RG8Sint;
|
||||
case filament::backend::TextureFormat::R32F:
|
||||
return wgpu::TextureFormat::R32Float;
|
||||
case filament::backend::TextureFormat::R32UI:
|
||||
return wgpu::TextureFormat::R32Uint;
|
||||
case filament::backend::TextureFormat::R32I:
|
||||
return wgpu::TextureFormat::R32Sint;
|
||||
case filament::backend::TextureFormat::RG16F:
|
||||
return wgpu::TextureFormat::RG16Float;
|
||||
case filament::backend::TextureFormat::RG16UI:
|
||||
return wgpu::TextureFormat::RG16Uint;
|
||||
case filament::backend::TextureFormat::RG16I:
|
||||
return wgpu::TextureFormat::RG16Sint;
|
||||
case filament::backend::TextureFormat::RGBA8:
|
||||
return wgpu::TextureFormat::RGBA8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_A8:
|
||||
return wgpu::TextureFormat::RGBA8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA8_SNORM:
|
||||
return wgpu::TextureFormat::RGBA8Snorm;
|
||||
case filament::backend::TextureFormat::RGBA8UI:
|
||||
return wgpu::TextureFormat::RGBA8Uint;
|
||||
case filament::backend::TextureFormat::RGBA8I:
|
||||
return wgpu::TextureFormat::RGBA8Sint;
|
||||
case filament::backend::TextureFormat::DEPTH16:
|
||||
return wgpu::TextureFormat::Depth16Unorm;
|
||||
case filament::backend::TextureFormat::DEPTH24:
|
||||
return wgpu::TextureFormat::Depth24Plus;
|
||||
case filament::backend::TextureFormat::DEPTH32F:
|
||||
return wgpu::TextureFormat::Depth32Float;
|
||||
case filament::backend::TextureFormat::DEPTH24_STENCIL8:
|
||||
return wgpu::TextureFormat::Depth24PlusStencil8;
|
||||
case filament::backend::TextureFormat::DEPTH32F_STENCIL8:
|
||||
return wgpu::TextureFormat::Depth32FloatStencil8;
|
||||
case filament::backend::TextureFormat::RG32F:
|
||||
return wgpu::TextureFormat::RG32Float;
|
||||
case filament::backend::TextureFormat::RG32UI:
|
||||
return wgpu::TextureFormat::RG32Uint;
|
||||
case filament::backend::TextureFormat::RG32I:
|
||||
return wgpu::TextureFormat::RG32Sint;
|
||||
case filament::backend::TextureFormat::RGBA16F:
|
||||
return wgpu::TextureFormat::RGBA16Float;
|
||||
case filament::backend::TextureFormat::RGBA16UI:
|
||||
return wgpu::TextureFormat::RGBA16Uint;
|
||||
case filament::backend::TextureFormat::RGBA16I:
|
||||
return wgpu::TextureFormat::RGBA16Sint;
|
||||
case filament::backend::TextureFormat::RGBA32F:
|
||||
return wgpu::TextureFormat::RGBA32Float;
|
||||
case filament::backend::TextureFormat::RGBA32UI:
|
||||
return wgpu::TextureFormat::RGBA32Uint;
|
||||
case filament::backend::TextureFormat::RGBA32I:
|
||||
return wgpu::TextureFormat::RGBA32Sint;
|
||||
case filament::backend::TextureFormat::EAC_R11:
|
||||
return wgpu::TextureFormat::EACR11Unorm;
|
||||
case filament::backend::TextureFormat::EAC_R11_SIGNED:
|
||||
return wgpu::TextureFormat::EACR11Snorm;
|
||||
case filament::backend::TextureFormat::EAC_RG11:
|
||||
return wgpu::TextureFormat::EACRG11Unorm;
|
||||
case filament::backend::TextureFormat::EAC_RG11_SIGNED:
|
||||
return wgpu::TextureFormat::EACRG11Snorm;
|
||||
case filament::backend::TextureFormat::ETC2_RGB8:
|
||||
return wgpu::TextureFormat::ETC2RGB8Unorm;
|
||||
case filament::backend::TextureFormat::ETC2_SRGB8:
|
||||
return wgpu::TextureFormat::ETC2RGB8UnormSrgb;
|
||||
case filament::backend::TextureFormat::ETC2_RGB8_A1:
|
||||
return wgpu::TextureFormat::ETC2RGB8A1Unorm;
|
||||
case filament::backend::TextureFormat::ETC2_SRGB8_A1:
|
||||
return wgpu::TextureFormat::ETC2RGB8A1UnormSrgb;
|
||||
case filament::backend::TextureFormat::ETC2_EAC_RGBA8:
|
||||
return wgpu::TextureFormat::ETC2RGBA8Unorm;
|
||||
case filament::backend::TextureFormat::ETC2_EAC_SRGBA8:
|
||||
return wgpu::TextureFormat::ETC2RGBA8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_4x4:
|
||||
return wgpu::TextureFormat::ASTC4x4Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
|
||||
return wgpu::TextureFormat::ASTC4x4UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_5x4:
|
||||
return wgpu::TextureFormat::ASTC5x4Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
|
||||
return wgpu::TextureFormat::ASTC5x4UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_5x5:
|
||||
return wgpu::TextureFormat::ASTC5x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
|
||||
return wgpu::TextureFormat::ASTC5x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_6x5:
|
||||
return wgpu::TextureFormat::ASTC6x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
|
||||
return wgpu::TextureFormat::ASTC6x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_6x6:
|
||||
return wgpu::TextureFormat::ASTC6x6Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
|
||||
return wgpu::TextureFormat::ASTC6x6UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_8x5:
|
||||
return wgpu::TextureFormat::ASTC8x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
|
||||
return wgpu::TextureFormat::ASTC8x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_8x6:
|
||||
return wgpu::TextureFormat::ASTC8x6Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
|
||||
return wgpu::TextureFormat::ASTC8x6UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_8x8:
|
||||
return wgpu::TextureFormat::ASTC8x8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
|
||||
return wgpu::TextureFormat::ASTC8x8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x5:
|
||||
return wgpu::TextureFormat::ASTC10x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
|
||||
return wgpu::TextureFormat::ASTC10x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x6:
|
||||
return wgpu::TextureFormat::ASTC10x6Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
|
||||
return wgpu::TextureFormat::ASTC10x6UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x8:
|
||||
return wgpu::TextureFormat::ASTC10x8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
|
||||
return wgpu::TextureFormat::ASTC10x8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x10:
|
||||
return wgpu::TextureFormat::ASTC10x10Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
|
||||
return wgpu::TextureFormat::ASTC10x10UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_12x10:
|
||||
return wgpu::TextureFormat::ASTC12x10Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
|
||||
return wgpu::TextureFormat::ASTC12x10UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_12x12:
|
||||
return wgpu::TextureFormat::ASTC12x12Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
|
||||
return wgpu::TextureFormat::ASTC12x12UnormSrgb;
|
||||
case filament::backend::TextureFormat::RED_RGTC1:
|
||||
return wgpu::TextureFormat::BC4RUnorm;
|
||||
case filament::backend::TextureFormat::SIGNED_RED_RGTC1:
|
||||
return wgpu::TextureFormat::BC4RSnorm;
|
||||
case filament::backend::TextureFormat::RED_GREEN_RGTC2:
|
||||
return wgpu::TextureFormat::BC5RGUnorm;
|
||||
case filament::backend::TextureFormat::SIGNED_RED_GREEN_RGTC2:
|
||||
return wgpu::TextureFormat::BC5RGSnorm;
|
||||
case filament::backend::TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
|
||||
return wgpu::TextureFormat::BC6HRGBUfloat;
|
||||
case filament::backend::TextureFormat::RGB_BPTC_SIGNED_FLOAT:
|
||||
return wgpu::TextureFormat::BC6HRGBFloat;
|
||||
case filament::backend::TextureFormat::RGBA_BPTC_UNORM:
|
||||
return wgpu::TextureFormat::BC7RGBAUnorm;
|
||||
case filament::backend::TextureFormat::SRGB_ALPHA_BPTC_UNORM:
|
||||
return wgpu::TextureFormat::BC7RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::RGB565:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and discard the alpha and lower precision.
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGB9_E5:
|
||||
return wgpu::TextureFormat::RGB9E5Ufloat;
|
||||
case filament::backend::TextureFormat::RGB5_A1:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and handle the packing/unpacking in shaders.
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGBA4:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and handle the packing/unpacking in shaders.
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGB8:
|
||||
// No direct sRGB equivalent in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8:
|
||||
// No direct sRGB equivalent in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGB8_SNORM:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Snorm;
|
||||
case filament::backend::TextureFormat::RGB8UI:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Uint;
|
||||
case filament::backend::TextureFormat::RGB8I:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Sint;
|
||||
case filament::backend::TextureFormat::R11F_G11F_B10F:
|
||||
return wgpu::TextureFormat::RG11B10Ufloat;
|
||||
case filament::backend::TextureFormat::UNUSED:
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGB10_A2:
|
||||
return wgpu::TextureFormat::RGB10A2Unorm;
|
||||
case filament::backend::TextureFormat::RGB16F:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA16Float;
|
||||
case filament::backend::TextureFormat::RGB16UI:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA16Uint;
|
||||
case filament::backend::TextureFormat::RGB16I:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA16Sint;
|
||||
case filament::backend::TextureFormat::RGB32F:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA32Float;
|
||||
case filament::backend::TextureFormat::RGB32UI:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA32Uint;
|
||||
case filament::backend::TextureFormat::RGB32I:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA32Sint;
|
||||
case filament::backend::TextureFormat::DXT1_RGB:
|
||||
return wgpu::TextureFormat::BC1RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT1_RGBA:
|
||||
return wgpu::TextureFormat::BC1RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT3_RGBA:
|
||||
return wgpu::TextureFormat::BC2RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT5_RGBA:
|
||||
return wgpu::TextureFormat::BC3RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT1_SRGB:
|
||||
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::DXT1_SRGBA:
|
||||
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::DXT3_SRGBA:
|
||||
return wgpu::TextureFormat::BC2RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::DXT5_SRGBA:
|
||||
return wgpu::TextureFormat::BC3RGBAUnormSrgb;
|
||||
}
|
||||
}
|
||||
|
||||
wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
|
||||
SamplerType target) {
|
||||
// starting with the defaults/basic configuration
|
||||
wgpu::TextureViewDescriptor textureViewDescriptor{
|
||||
.label = getUserTextureViewLabel(target),
|
||||
.format = mFormat,
|
||||
// dimension depends on target and is set below
|
||||
.baseMipLevel = baseLevel,
|
||||
.mipLevelCount = levelCount,
|
||||
// baseArrayLayer is required, making a guess
|
||||
.baseArrayLayer = 0,
|
||||
.arrayLayerCount = mArrayLayerCount,
|
||||
// Have not found an analog to aspect in other drivers, but ALL should be unrestrictive.
|
||||
// TODO Can we make this better?
|
||||
.aspect = wgpu::TextureAspect::All,
|
||||
.usage = mUsage
|
||||
};
|
||||
// adjust for specific cases
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2DArray;
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::Cube;
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e3D;
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::CubeArray;
|
||||
break;
|
||||
}
|
||||
wgpu::TextureView textureView = mTexture.CreateView(&textureViewDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(textureView)
|
||||
<< "Failed to create texture view " << textureViewDescriptor.label;
|
||||
return textureView;
|
||||
}
|
||||
|
||||
WGPURenderTarget::Attachment WGPURenderTarget::getDrawColorAttachment(size_t index) {
|
||||
assert_invariant( index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||
auto result = color[index];
|
||||
if (index == 0 && defaultRenderTarget) {
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params,
|
||||
TargetBufferFlags buffer) {
|
||||
auto clearFlags = params.flags.clear;
|
||||
auto discardStartFlags = params.flags.discardStart;
|
||||
if (any(clearFlags & buffer)) {
|
||||
return wgpu::LoadOp::Clear;
|
||||
} else if (any(discardStartFlags & buffer)) {
|
||||
return wgpu::LoadOp::Clear;
|
||||
}
|
||||
return wgpu::LoadOp::Load;
|
||||
}
|
||||
|
||||
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
|
||||
TargetBufferFlags buffer) {
|
||||
const auto discardEndFlags = params.flags.discardEnd;
|
||||
if (any(discardEndFlags & buffer)) {
|
||||
return wgpu::StoreOp::Discard;
|
||||
}
|
||||
return wgpu::StoreOp::Store;
|
||||
}
|
||||
void WGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
|
||||
wgpu::TextureView const& textureView, RenderPassParams const& params) {
|
||||
// auto discardFlags = params.flags.discardEnd;
|
||||
// (void) discardFlags;
|
||||
// std::vector<wgpu::RenderPassColorAttachment> colorAttachments;
|
||||
colorAttachments.clear();
|
||||
for (size_t i = 0; i < 1/*MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT*/; i++) {
|
||||
// auto attachment = getDrawColorAttachment(i);
|
||||
// if (attachment) {
|
||||
wgpu::RenderPassColorAttachment colorAttachment;
|
||||
colorAttachment.view = textureView;
|
||||
colorAttachment.loadOp = getLoadOperation(params, getTargetBufferFlagsAt(i));
|
||||
colorAttachment.storeOp = getStoreOperation(params, getTargetBufferFlagsAt(i));
|
||||
colorAttachment.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b, params.clearColor.a };
|
||||
colorAttachments.emplace_back(colorAttachment);
|
||||
// }
|
||||
}
|
||||
descriptor.colorAttachments = colorAttachments.data();
|
||||
descriptor.colorAttachmentCount = colorAttachments.size();
|
||||
descriptor.depthStencilAttachment = nullptr;
|
||||
descriptor.timestampWrites = nullptr;
|
||||
}
|
||||
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
@@ -98,80 +100,131 @@ struct WGPUBufferObject : HwBufferObject {
|
||||
wgpu::Buffer buffer = nullptr;
|
||||
const BufferObjectBinding bufferObjectBinding;
|
||||
};
|
||||
|
||||
class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {
|
||||
public:
|
||||
std::string label;
|
||||
|
||||
enum class BindGroupEntryType : uint8_t {
|
||||
UNIFORM_BUFFER,
|
||||
TEXTURE_VIEW_2D,
|
||||
TEXTURE_VIEW_2DA,
|
||||
TEXTURE_VIEW_CUBE,
|
||||
TEXTURE_VIEW_CUBEA,
|
||||
TEXTURE_VIEW_3D,
|
||||
SAMPLER
|
||||
};
|
||||
|
||||
struct BindGroupEntryInfo final {
|
||||
uint8_t binding = 0;
|
||||
BindGroupEntryType type = BindGroupEntryType::UNIFORM_BUFFER;
|
||||
bool hasDynamicOffset = false;
|
||||
};
|
||||
|
||||
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const& device);
|
||||
~WebGPUDescriptorSetLayout();
|
||||
[[nodiscard]] const wgpu::BindGroupLayout& getLayout() const { return mLayout; }
|
||||
[[nodiscard]] uint getLayoutSize() const { return mLayoutSize; }
|
||||
[[nodiscard]] std::vector<BindGroupEntryInfo> const& getBindGroupEntries() const {
|
||||
return mBindGroupEntries;
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO: If this is useful elsewhere, remove it from this class
|
||||
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
|
||||
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
|
||||
uint mLayoutSize;
|
||||
std::vector<BindGroupEntryInfo> mBindGroupEntries;
|
||||
wgpu::BindGroupLayout mLayout;
|
||||
};
|
||||
|
||||
class WebGPUDescriptorSet final : public HwDescriptorSet {
|
||||
public:
|
||||
WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize);
|
||||
std::string bglLabel;
|
||||
static void initializeDummyResourcesIfNotAlready(wgpu::Device const&,
|
||||
wgpu::TextureFormat aColorFormat,
|
||||
wgpu::TextureFormat aDepthFormat);
|
||||
|
||||
WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
|
||||
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries, std::string label);
|
||||
~WebGPUDescriptorSet();
|
||||
|
||||
wgpu::BindGroup lockAndReturn(wgpu::Device const& device);
|
||||
void addEntry(uint index, wgpu::BindGroupEntry&& entry);
|
||||
wgpu::BindGroup lockAndReturn(wgpu::Device const&);
|
||||
void addEntry(unsigned int index, wgpu::BindGroupEntry&& entry);
|
||||
[[nodiscard]] uint32_t const* setDynamicOffsets(uint32_t const* offsets);
|
||||
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
|
||||
|
||||
[[nodiscard]] size_t countEntitiesWithDynamicOffsets() const;
|
||||
private:
|
||||
|
||||
static wgpu::Buffer sDummyUniformBuffer;
|
||||
static std::unordered_map<wgpu::TextureDimension, wgpu::Texture> sDummyTextureMap;
|
||||
static std::unordered_map<wgpu::TextureViewDimension, wgpu::TextureView> sDummyTextureViewMap;
|
||||
static wgpu::Sampler sDummySampler;
|
||||
|
||||
static std::vector<wgpu::BindGroupEntry> createDummyEntriesSortedByBinding(
|
||||
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&);
|
||||
|
||||
// TODO: Consider storing what we used to make the layout. However we need to essentially
|
||||
// Recreate some of the info (Sampler in slot X with the actual sampler) so letting Dawn confirm
|
||||
// there isn't a mismatch may be easiest.
|
||||
// Also storing the wgpu ObjectBase takes care of ownership challenges in theory
|
||||
wgpu::BindGroupLayout mLayout;
|
||||
std::vector<wgpu::BindGroupEntry> entries;
|
||||
wgpu::BindGroup mBindGroup;
|
||||
wgpu::BindGroupLayout mLayout = nullptr;
|
||||
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
|
||||
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding {};
|
||||
std::vector<wgpu::BindGroupEntry> mEntriesSortedByBinding;
|
||||
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingWithDynamicOffsets {};
|
||||
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingAdded {};
|
||||
std::vector<uint32_t> mDynamicOffsets;
|
||||
wgpu::BindGroup mBindGroup = nullptr;
|
||||
};
|
||||
|
||||
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
|
||||
// Update the struct when used by WebGPU driver.
|
||||
struct WGPUTexture : public HwTexture {
|
||||
class WGPUTexture : public HwTexture {
|
||||
public:
|
||||
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
|
||||
wgpu::Device const& device) noexcept;
|
||||
|
||||
// constructors for creating texture views
|
||||
WGPUTexture(WGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
|
||||
wgpu::Texture texture = nullptr;
|
||||
// TODO: Adding this but not yet setting it up. Filament "Textures" are combined image samplers,
|
||||
// rep both.
|
||||
wgpu::Sampler sampler = nullptr;
|
||||
//TODO: Not sure all the ways HwTexture is used. Overloading like this might be entirely wrong.
|
||||
wgpu::TextureView texView = nullptr;
|
||||
const wgpu::Texture& getTexture() const { return mTexture; }
|
||||
const wgpu::TextureView& getTexView() const { return mTexView; }
|
||||
|
||||
// Public to allow checking for support of a texture format
|
||||
static wgpu::TextureFormat fToWGPUTextureFormat(const filament::backend::TextureFormat& fUsage);
|
||||
|
||||
private:
|
||||
wgpu::TextureView makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
|
||||
SamplerType target);
|
||||
// CreateTextureR has info for a texture and sampler. Texture Views are needed for binding,
|
||||
// along with a sampler Current plan: Inherit the sampler and Texture to always exist (It is a
|
||||
// ref counted pointer) when making views. View is optional
|
||||
wgpu::Texture mTexture = nullptr;
|
||||
wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
|
||||
wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
|
||||
uint32_t mArrayLayerCount = 1;
|
||||
wgpu::TextureView mTexView = nullptr;
|
||||
wgpu::TextureUsage fToWGPUTextureUsage(const filament::backend::TextureUsage& fUsage);
|
||||
};
|
||||
|
||||
struct WGPURenderPrimitive : public HwRenderPrimitive {
|
||||
WGPURenderPrimitive() {}
|
||||
|
||||
void setBuffers(WGPUVertexBufferInfo const* const vbi,
|
||||
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer);
|
||||
|
||||
WGPUVertexBuffer* vertexBuffer = nullptr;
|
||||
WGPUIndexBuffer* indexBuffer = nullptr;
|
||||
};
|
||||
|
||||
// TODO: Currently WGPURenderTarget is not used by WebGPU for useful task.
|
||||
// Update the struct when used by WebGPU driver.
|
||||
struct WGPURenderTarget : public HwRenderTarget {
|
||||
class WGPURenderTarget : public HwRenderTarget {
|
||||
public:
|
||||
class Attachment {
|
||||
public:
|
||||
friend struct WGPURenderTarget;
|
||||
friend class WGPURenderTarget;
|
||||
|
||||
Attachment() = default;
|
||||
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
|
||||
: level(level),
|
||||
layer(layer),
|
||||
texture(gpuTexture->texture),
|
||||
texture(gpuTexture->getTexture()),
|
||||
mWGPUTexture(gpuTexture) {}
|
||||
operator bool() const {
|
||||
return mWGPUTexture != nullptr;
|
||||
}
|
||||
|
||||
uint8_t level = 0;
|
||||
uint16_t layer = 0;
|
||||
@@ -187,8 +240,8 @@ struct WGPURenderTarget : public HwRenderTarget {
|
||||
: HwRenderTarget(0, 0),
|
||||
defaultRenderTarget(true) {}
|
||||
|
||||
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor* descriptor,
|
||||
const RenderPassParams& params);
|
||||
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
|
||||
wgpu::TextureView const& textureView, RenderPassParams const& params);
|
||||
|
||||
math::uint2 getAttachmentSize() noexcept;
|
||||
|
||||
@@ -198,15 +251,15 @@ struct WGPURenderTarget : public HwRenderTarget {
|
||||
Attachment getDrawColorAttachment(size_t index);
|
||||
Attachment getReadColorAttachment(size_t index);
|
||||
|
||||
static wgpu::LoadOp getLoadOperation(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
private:
|
||||
static wgpu::LoadOp getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
static wgpu::LoadOp getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
|
||||
bool defaultRenderTarget = false;
|
||||
uint8_t samples = 1;
|
||||
|
||||
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
|
||||
math::uint2 attachmentSize = {};
|
||||
std::vector<wgpu::RenderPassColorAttachment> colorAttachments {};
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -235,6 +235,7 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
||||
fragmentState.constantCount = program.constants.size(),
|
||||
fragmentState.constants = program.constants.data(),
|
||||
fragmentState.targetCount = 1; // TODO need to get this from the render target
|
||||
fragmentState.targets = colorTargets.data();
|
||||
assert_invariant(fragmentState.targetCount <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||
for (size_t targetIndex = 0; targetIndex < fragmentState.targetCount; targetIndex++) {
|
||||
auto& colorTarget = colorTargets[targetIndex];
|
||||
|
||||
@@ -105,7 +105,7 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||
}
|
||||
#endif
|
||||
|
||||
constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||
[[nodiscard]] constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
|
||||
const std::array expectedColorFormats =
|
||||
useSRGBColorSpace ?
|
||||
@@ -123,7 +123,7 @@ constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||
return *firstFoundColorFormat;
|
||||
}
|
||||
|
||||
constexpr wgpu::TextureFormat selectDepthFormat(bool depth32FloatStencil8Enabled,
|
||||
[[nodiscard]] constexpr wgpu::TextureFormat selectDepthFormat(bool depth32FloatStencil8Enabled,
|
||||
bool needStencil) {
|
||||
if (needStencil) {
|
||||
if (depth32FloatStencil8Enabled) {
|
||||
@@ -137,7 +137,7 @@ constexpr wgpu::TextureFormat selectDepthFormat(bool depth32FloatStencil8Enabled
|
||||
}
|
||||
}
|
||||
|
||||
constexpr wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
|
||||
[[nodiscard]] constexpr wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
|
||||
wgpu::PresentMode const* availablePresentModes) {
|
||||
// Verify that our chosen present mode is supported. In practice all devices support the FIFO
|
||||
// mode, but we check for it anyway for completeness. (and to avoid validation warnings)
|
||||
@@ -151,7 +151,7 @@ constexpr wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
|
||||
return desiredPresentMode;
|
||||
}
|
||||
|
||||
constexpr wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
|
||||
[[nodiscard]] constexpr wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
|
||||
wgpu::CompositeAlphaMode const* availableAlphaModes) {
|
||||
bool autoAvailable = false;
|
||||
bool inheritAvailable = false;
|
||||
@@ -222,13 +222,58 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
|
||||
config.alphaMode = selectAlphaMode(capabilities.alphaModeCount, capabilities.alphaModes);
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::Texture createDepthTexture(wgpu::Device const& device,
|
||||
wgpu::Extent2D const& extent, wgpu::TextureFormat depthFormat) {
|
||||
wgpu::TextureDescriptor descriptor{ .label = "depth_texture",
|
||||
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment,
|
||||
.dimension = wgpu::TextureDimension::e2D,
|
||||
.size = { .width = extent.width, .height = extent.height, .depthOrArrayLayers = 1 },
|
||||
.format = depthFormat,
|
||||
.mipLevelCount = 1,
|
||||
.sampleCount = 1,
|
||||
.viewFormatCount = 1,
|
||||
.viewFormats = &depthFormat
|
||||
};
|
||||
wgpu::Texture depthTexture = device.CreateTexture(&descriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(depthTexture) << "Failed to create depth texture with width "
|
||||
<< extent.width << " and height " << extent.height;
|
||||
return depthTexture;
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::TextureView createDepthTextureView(wgpu::Texture const& depthTexture,
|
||||
wgpu::TextureFormat depthFormat, bool needStencil) {
|
||||
wgpu::TextureViewDescriptor descriptor{
|
||||
.label = "depth_texture_view",
|
||||
.format = depthFormat,
|
||||
.dimension = wgpu::TextureViewDimension::e2D,
|
||||
.baseMipLevel = 0,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.arrayLayerCount = 1,
|
||||
.aspect = wgpu::TextureAspect::DepthOnly,
|
||||
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment
|
||||
};
|
||||
if (needStencil) {
|
||||
descriptor.aspect = wgpu::TextureAspect::All;
|
||||
}
|
||||
wgpu::TextureView depthTextureView = depthTexture.CreateView(&descriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(depthTextureView) << "Failed to create depth texture view";
|
||||
return depthTextureView;
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
|
||||
wgpu::Adapter const& adapter, wgpu::Device const& device, uint64_t flags)
|
||||
: mSurface(surface) {
|
||||
: mDevice(device),
|
||||
mSurface(surface),
|
||||
mNeedStencil((flags & SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0),
|
||||
mDepthFormat(selectDepthFormat(device.HasFeature(wgpu::FeatureName::Depth32FloatStencil8),
|
||||
mNeedStencil)),
|
||||
mDepthTexture(createDepthTexture(device, surfaceSize, mDepthFormat)),
|
||||
mDepthTextureView(createDepthTextureView(mDepthTexture, mDepthFormat, mNeedStencil)) {
|
||||
wgpu::SurfaceCapabilities capabilities = {};
|
||||
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
|
||||
@@ -238,19 +283,14 @@ WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const&
|
||||
#endif
|
||||
}
|
||||
const bool useSRGBColorSpace = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0;
|
||||
const bool needStencil = (flags & SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0;
|
||||
initConfig(mConfig, device, capabilities, surfaceSize, useSRGBColorSpace);
|
||||
mDepthFormat = selectDepthFormat(device.HasFeature(wgpu::FeatureName::Depth32FloatStencil8),
|
||||
needStencil);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printSurfaceConfiguration(mConfig, mDepthFormat);
|
||||
#endif
|
||||
mSurface.Configure(&mConfig);
|
||||
}
|
||||
|
||||
WebGPUSwapChain::~WebGPUSwapChain() {
|
||||
mSurface.Unconfigure();
|
||||
}
|
||||
WebGPUSwapChain::~WebGPUSwapChain() { mSurface.Unconfigure(); }
|
||||
|
||||
void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
|
||||
FILAMENT_CHECK_POSTCONDITION(currentSurfaceSize.width > 0 || currentSurfaceSize.height > 0)
|
||||
@@ -267,6 +307,8 @@ void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
|
||||
// TODO we may need to ensure no surface texture is in flight when we do this. some
|
||||
// synchronization may be necessary
|
||||
mSurface.Configure(&mConfig);
|
||||
mDepthTexture = createDepthTexture(mDevice, currentSurfaceSize, mDepthFormat);
|
||||
mDepthTextureView = createDepthTextureView(mDepthTexture, mDepthFormat, mNeedStencil);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,14 +38,20 @@ public:
|
||||
|
||||
[[nodiscard]] wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
|
||||
|
||||
[[nodiscard]] wgpu::TextureView getDepthTextureView() const { return mDepthTextureView; }
|
||||
|
||||
void present();
|
||||
|
||||
private:
|
||||
void setExtent(wgpu::Extent2D const&);
|
||||
|
||||
wgpu::Device mDevice = nullptr;
|
||||
wgpu::Surface mSurface = {};
|
||||
wgpu::SurfaceConfiguration mConfig = {};
|
||||
bool mNeedStencil = false;
|
||||
wgpu::TextureFormat mDepthFormat = wgpu::TextureFormat::Undefined;
|
||||
wgpu::Texture mDepthTexture = nullptr;
|
||||
wgpu::TextureView mDepthTextureView = nullptr;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -99,22 +99,45 @@ wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
|
||||
|
||||
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
|
||||
// TODO consider passing limits
|
||||
constexpr std::array desiredFeatures = {
|
||||
wgpu::FeatureName::DepthClipControl,
|
||||
wgpu::FeatureName::Depth32FloatStencil8,
|
||||
wgpu::FeatureName::CoreFeaturesAndLimits };
|
||||
std::vector<wgpu::FeatureName> requiredFeatures;
|
||||
requiredFeatures.reserve(desiredFeatures.size());
|
||||
constexpr std::array optionalFeatures = { wgpu::FeatureName::DepthClipControl,
|
||||
wgpu::FeatureName::Depth32FloatStencil8, wgpu::FeatureName::CoreFeaturesAndLimits };
|
||||
|
||||
constexpr std::array requiredFeatures = { wgpu::FeatureName::TransientAttachments };
|
||||
|
||||
wgpu::SupportedFeatures supportedFeatures;
|
||||
adapter.GetFeatures(&supportedFeatures);
|
||||
|
||||
std::vector<wgpu::FeatureName> enabledFeatures;
|
||||
enabledFeatures.reserve(requiredFeatures.size() + optionalFeatures.size());
|
||||
|
||||
std::set_intersection(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount, desiredFeatures.begin(),
|
||||
desiredFeatures.end(), std::back_inserter(requiredFeatures));
|
||||
supportedFeatures.features + supportedFeatures.featureCount, requiredFeatures.begin(),
|
||||
requiredFeatures.end(), std::back_inserter(enabledFeatures));
|
||||
|
||||
if (enabledFeatures.size() != requiredFeatures.size()) {
|
||||
std::vector<wgpu::FeatureName> missingFeatures;
|
||||
std::set_difference(requiredFeatures.begin(), requiredFeatures.end(),
|
||||
supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
std::back_inserter(missingFeatures));
|
||||
|
||||
std::stringstream missingFeaturesStream{};
|
||||
for (const auto& entry: missingFeatures) {
|
||||
missingFeaturesStream << std::to_string(static_cast<uint32_t>(entry)) << " ";
|
||||
}
|
||||
PANIC_POSTCONDITION("Some required features are not available %s/n",
|
||||
missingFeaturesStream.str().c_str());
|
||||
}
|
||||
|
||||
std::set_intersection(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount, optionalFeatures.begin(),
|
||||
optionalFeatures.end(), std::back_inserter(enabledFeatures));
|
||||
|
||||
wgpu::DeviceDescriptor deviceDescriptor{};
|
||||
deviceDescriptor.label = "graphics_device";
|
||||
deviceDescriptor.defaultQueue.label = "default_queue";
|
||||
deviceDescriptor.requiredFeatureCount = requiredFeatures.size();
|
||||
deviceDescriptor.requiredFeatures = requiredFeatures.data();
|
||||
deviceDescriptor.requiredFeatureCount = enabledFeatures.size();
|
||||
deviceDescriptor.requiredFeatures = enabledFeatures.data();
|
||||
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
|
||||
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
|
||||
wgpu::StringView message) {
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace test {
|
||||
Backend BackendTest::sBackend = Backend::NOOP;
|
||||
OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER;
|
||||
bool BackendTest::sIsMobilePlatform = false;
|
||||
std::vector<std::string> BackendTest::sFailedImages;
|
||||
|
||||
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) {
|
||||
sBackend = backend;
|
||||
@@ -63,11 +64,12 @@ BackendTest::~BackendTest() {
|
||||
flushAndWait();
|
||||
mImageExpectations->evaluate();
|
||||
// Note: Don't terminate the driver for OpenGL, as it wipes away the context and removes the buffer from the screen.
|
||||
if (sBackend == Backend::OPENGL) {
|
||||
return;
|
||||
if (sBackend != Backend::OPENGL) {
|
||||
driver->terminate();
|
||||
delete driver;
|
||||
}
|
||||
driver->terminate();
|
||||
delete driver;
|
||||
|
||||
recordFailedImages();
|
||||
}
|
||||
|
||||
void BackendTest::initializeDriver() {
|
||||
@@ -167,8 +169,24 @@ bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem) {
|
||||
return sOperatingSystem == operatingSystem;
|
||||
}
|
||||
|
||||
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem, Backend backend) {
|
||||
return matchesEnvironment(operatingSystem) && matchesEnvironment(backend);
|
||||
void BackendTest::markImageAsFailure(std::string failedImageName) {
|
||||
sFailedImages.emplace_back(std::move(failedImageName));
|
||||
}
|
||||
|
||||
void BackendTest::recordFailedImages() {
|
||||
if (!sFailedImages.empty()) {
|
||||
std::string failedImages;
|
||||
for (auto& failedTestImageName: sFailedImages) {
|
||||
if (failedImages.empty()) {
|
||||
failedImages = failedTestImageName;
|
||||
} else {
|
||||
failedImages.append(",");
|
||||
failedImages.append(failedTestImageName);
|
||||
}
|
||||
}
|
||||
RecordProperty("FailedImages", failedImages);
|
||||
}
|
||||
sFailedImages.clear();
|
||||
}
|
||||
|
||||
class Environment : public ::testing::Environment {
|
||||
|
||||
@@ -38,6 +38,9 @@ public:
|
||||
static OperatingSystem sOperatingSystem;
|
||||
static bool sIsMobilePlatform;
|
||||
|
||||
// Takes the name of the image that wasn't correct, without the .png suffix
|
||||
static void markImageAsFailure(std::string failedImageName);
|
||||
|
||||
protected:
|
||||
|
||||
BackendTest();
|
||||
@@ -73,8 +76,13 @@ protected:
|
||||
|
||||
static bool matchesEnvironment(Backend backend);
|
||||
static bool matchesEnvironment(OperatingSystem operatingSystem);
|
||||
static bool matchesEnvironment(OperatingSystem operatingSystem, Backend backend);
|
||||
private:
|
||||
// Adds all the images that failed an ImageExpectation to the XML metadata for the current tests
|
||||
// case. Add --gtest_output=xml as a command line argument to generate a test_detail.xml file in
|
||||
// the directory where the tests are run.
|
||||
static void recordFailedImages();
|
||||
|
||||
static std::vector<std::string> sFailedImages;
|
||||
|
||||
filament::backend::Driver* driver = nullptr;
|
||||
filament::backend::CommandBufferQueue commandBufferQueue;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "utils/Hash.h"
|
||||
#include <fstream>
|
||||
|
||||
#include "BackendTest.h"
|
||||
#include "backend/PixelBufferDescriptor.h"
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
@@ -32,6 +33,8 @@
|
||||
|
||||
#endif
|
||||
|
||||
namespace test {
|
||||
|
||||
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
|
||||
uint32_t expectedHash, bool isSrgb)
|
||||
: mWidth(width),
|
||||
@@ -80,6 +83,10 @@ std::string ScreenshotParams::expectedFilePath() const {
|
||||
return absl::StrFormat("%s/%s", expectedDirectoryPath(), expectedFileName());
|
||||
}
|
||||
|
||||
const std::string ScreenshotParams::filePrefix() const {
|
||||
return mFileName;
|
||||
}
|
||||
|
||||
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
|
||||
filament::backend::DriverApi& api, ScreenshotParams params,
|
||||
filament::backend::RenderTargetHandle renderTarget)
|
||||
@@ -113,7 +120,11 @@ void ImageExpectation::compareImage() const {
|
||||
#ifndef FILAMENT_IOS
|
||||
LoadedPng loadedImage(mParams.expectedFilePath());
|
||||
uint32_t loadedImageHash = loadedImage.hash();
|
||||
EXPECT_THAT(actualHash, testing::Eq(loadedImageHash)) << mParams.expectedFileName();
|
||||
auto compareToImageMatcher = testing::Eq(loadedImageHash);
|
||||
if (!testing::Matches(compareToImageMatcher)(actualHash)) {
|
||||
BackendTest::markImageAsFailure(mParams.filePrefix());
|
||||
}
|
||||
EXPECT_THAT(actualHash, compareToImageMatcher) << mParams.expectedFileName();
|
||||
#endif
|
||||
// For builds that can't load PNGs (currently iOS only) use the expected hash.
|
||||
EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash())) << mParams.expectedFileName();
|
||||
@@ -237,3 +248,5 @@ uint32_t LoadedPng::hash() const {
|
||||
const std::vector<unsigned char>& LoadedPng::bytes() const {
|
||||
return mBytes;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -35,6 +35,8 @@ do { \
|
||||
screenshotParams); \
|
||||
} while (0)
|
||||
|
||||
namespace test {
|
||||
|
||||
/**
|
||||
* Stores user-provided configuration values for an image expectation
|
||||
*/
|
||||
@@ -54,6 +56,7 @@ public:
|
||||
static std::string expectedDirectoryPath();
|
||||
std::string expectedFileName() const;
|
||||
std::string expectedFilePath() const;
|
||||
const std::string filePrefix() const;
|
||||
|
||||
private:
|
||||
int mWidth;
|
||||
@@ -153,4 +156,6 @@ private:
|
||||
std::vector<std::unique_ptr<ImageExpectation>> mExpectations;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif //TNT_IMAGE_EXPECTATIONS_H
|
||||
|
||||
@@ -57,7 +57,7 @@ Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup
|
||||
|
||||
if (!kLayouts.empty()) {
|
||||
mDescriptorSetLayout =
|
||||
cleanup.add(api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
|
||||
cleanup.add(api.createDescriptorSetLayout(DescriptorSetLayout{ .bindings = kLayouts }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ std::vector<UniformConfig> GetUniformConfig(ShaderUniformType type) {
|
||||
"backend_test", "test_tex", 0,
|
||||
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||
return {{
|
||||
"test_tex", DescriptorType::SAMPLER, samplerInfo
|
||||
"test_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo
|
||||
}};
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -1,62 +1,143 @@
|
||||
import os, shutil, argparse, typing
|
||||
|
||||
def match_sufffix(file_name: str, suffix: str, accepted_prefixes: typing.List[str]) -> str:
|
||||
"""
|
||||
Check if the file name is one of the searched for ones with the given suffix and if so return it.
|
||||
:param accepted_prefixes: If None accepts any prefix
|
||||
:return: file_name with the suffix removed or "" if it doesn't match. This does mean a string that
|
||||
is just the suffix is considered to not match as it will return the empty string.
|
||||
"""
|
||||
if file_name.endswith(suffix):
|
||||
prefix = file_name.removesuffix(suffix)
|
||||
if accepted_prefixes is None or prefix in accepted_prefixes:
|
||||
return prefix
|
||||
return ""
|
||||
import os, shutil, argparse, typing, xml.etree.ElementTree, subprocess, platform
|
||||
|
||||
|
||||
def replace_file_names(path: str, removed: str, replacement: str = "", output_path: str = "",
|
||||
prefixes: typing.List[str] = None):
|
||||
if not output_path:
|
||||
output_path = path
|
||||
for file_name in os.listdir(path=path):
|
||||
prefix = match_sufffix(file_name, removed, prefixes)
|
||||
if prefix:
|
||||
# Remove the prefix from the list so that prefixes is the list of intended but not yet found
|
||||
# files.
|
||||
if prefixes is not None:
|
||||
prefixes.remove(prefix)
|
||||
new_file_name = prefix + replacement
|
||||
new_file_path = os.path.join(output_path, new_file_name)
|
||||
old_file_path = os.path.join(path, file_name)
|
||||
print(f'{old_file_path} to {new_file_path}')
|
||||
shutil.move(old_file_path, new_file_path)
|
||||
if prefixes is not None:
|
||||
for unfound_prefix in prefixes:
|
||||
print(f'Failed to find {unfound_prefix}_actual.png')
|
||||
class TestResults(object):
|
||||
ACTUAL_SUFFIX = '_actual.png'
|
||||
EXPECTED_SUFFIX = '.png'
|
||||
|
||||
def __init__(self, results_directory: str, source_expected_directory: str):
|
||||
self.results_directory = results_directory
|
||||
self.actual_directory = os.path.join(self.results_directory, 'images', 'actual_images')
|
||||
self.expected_directory = os.path.join(self.results_directory, 'images', 'expected_images')
|
||||
self.source_expected_directory = source_expected_directory
|
||||
|
||||
def get_latest_failed_images(self) -> typing.List[str]:
|
||||
failed_images = []
|
||||
xml_tree = xml.etree.ElementTree.parse(
|
||||
os.path.join(self.results_directory, 'test_detail.xml'))
|
||||
testsuites = xml_tree.getroot()
|
||||
for testsuite in testsuites.findall('testsuite'):
|
||||
for testcase in testsuite.findall('testcase'):
|
||||
for properties in testcase.findall('properties'):
|
||||
for property in properties.findall('property'):
|
||||
if property.get('name') == 'FailedImages':
|
||||
failed_images.extend(property.get('value').split(','))
|
||||
|
||||
return failed_images
|
||||
|
||||
def handle_failed_image(self, failed_image):
|
||||
self.show_images(failed_image)
|
||||
print(f'Update {failed_image}\'s expected image? y/n')
|
||||
while True:
|
||||
user_input = input()
|
||||
if user_input == 'y':
|
||||
self.move_actual_to_source([failed_image])
|
||||
break
|
||||
elif user_input == 'n':
|
||||
break
|
||||
|
||||
def handle_all_failed_images(self):
|
||||
for failed_image in self.get_latest_failed_images():
|
||||
self.handle_failed_image(failed_image)
|
||||
|
||||
def show_images(self, failed_image):
|
||||
# TODO: Test more on non-mac systems
|
||||
open_command: str
|
||||
os_name = platform.system().lower()
|
||||
if 'windows' in os_name:
|
||||
open_command = 'start'
|
||||
elif 'osx' in os_name or 'darwin' in os_name:
|
||||
open_command = 'open'
|
||||
else:
|
||||
open_command = 'xdg-open'
|
||||
|
||||
subprocess.run(
|
||||
[open_command,
|
||||
os.path.join(self.actual_directory, failed_image + TestResults.ACTUAL_SUFFIX)])
|
||||
subprocess.run(
|
||||
[open_command,
|
||||
os.path.join(self.expected_directory, failed_image + TestResults.EXPECTED_SUFFIX)])
|
||||
|
||||
def move_actual_to_source(self, file_prefixes: typing.List[str]):
|
||||
replace_file_names(path=self.actual_directory, removed=TestResults.ACTUAL_SUFFIX,
|
||||
replacement=TestResults.EXPECTED_SUFFIX,
|
||||
output_path=self.source_expected_directory, prefixes=file_prefixes)
|
||||
|
||||
def batch_move(self, prefixes: typing.Optional[typing.List[str]] = None):
|
||||
replace_file_names(path=self.actual_directory, removed=TestResults.ACTUAL_SUFFIX,
|
||||
replacement=TestResults.EXPECTED_SUFFIX,
|
||||
output_path=self.source_expected_directory, prefixes=prefixes)
|
||||
|
||||
|
||||
def match_suffix(file_name: str, suffix: str, accepted_prefixes: typing.List[str]) -> str:
|
||||
"""
|
||||
Check if the file name is one of the searched for ones with the given suffix and if so return
|
||||
it.
|
||||
:param accepted_prefixes: If None accepts any prefix
|
||||
:return: file_name with the suffix removed or "" if it doesn't match. This does mean a string
|
||||
that is just the suffix is considered to not match as it will return the empty string.
|
||||
"""
|
||||
if file_name.endswith(suffix):
|
||||
prefix = file_name.removesuffix(suffix)
|
||||
if accepted_prefixes is None or prefix in accepted_prefixes:
|
||||
return prefix
|
||||
return ''
|
||||
|
||||
|
||||
def replace_file_names(path: str, removed: str, replacement: str = '', output_path: str = '',
|
||||
prefixes: typing.Optional[typing.List[str]] = None):
|
||||
if not output_path:
|
||||
output_path = path
|
||||
for file_name in os.listdir(path=path):
|
||||
prefix = match_suffix(file_name, removed, prefixes)
|
||||
if prefix:
|
||||
# Remove the prefix from the list so that prefixes is the list of intended but not yet
|
||||
# found files.
|
||||
if prefixes is not None:
|
||||
prefixes.remove(prefix)
|
||||
new_file_name = prefix + replacement
|
||||
new_file_path = os.path.join(output_path, new_file_name)
|
||||
old_file_path = os.path.join(path, file_name)
|
||||
print(f'{old_file_path} to {new_file_path}')
|
||||
shutil.copyfile(old_file_path, new_file_path)
|
||||
if prefixes is not None:
|
||||
for unfound_prefix in prefixes:
|
||||
print(f'Failed to find {unfound_prefix}_actual.png')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(prog='Backend Test File Renamer',
|
||||
description='Moves actual generated test images to the expected '
|
||||
'images directory, to update the test requirements. '
|
||||
'test_cases accepts multiple arguments that should '
|
||||
'be the name of the expected image file without the '
|
||||
'.png suffix. Also --all can be passed to copy all '
|
||||
'images.\n'
|
||||
'Remember to sync CMake after running this to move '
|
||||
'the new expected images to the binary directory.')
|
||||
parser.add_argument('-i', '--input_path')
|
||||
parser.add_argument('-o', '--output_path', default="./expected_images")
|
||||
parser.add_argument('-t', '--test_cases', action='extend', nargs='*')
|
||||
parser.add_argument('-a', '--all', action='store_true')
|
||||
parser = argparse.ArgumentParser(prog='Backend Test File Renamer',
|
||||
description='Moves actual generated test images to the '
|
||||
'expected images directory, to update the test '
|
||||
'requirements. test_cases accepts multiple '
|
||||
'arguments that should be the name of the '
|
||||
'expected image file without the .png suffix. '
|
||||
'Also --all can be passed to copy all images.\n'
|
||||
'Remember to sync CMake after running this to '
|
||||
'move the new expected images to the binary '
|
||||
'directory.')
|
||||
parser.add_argument('-r', '--results_path')
|
||||
parser.add_argument('-s', '--source_expected_path', default="./expected_images")
|
||||
# The mutually exclusive options for how to process the actual images
|
||||
parser.add_argument('-b', '--batch', action='extend', nargs='*')
|
||||
parser.add_argument('-a', '--all', action='store_true')
|
||||
parser.add_argument('-t', '--tests', action='store_true')
|
||||
parser.add_argument('-c', '--compare', action='extend', nargs='*')
|
||||
|
||||
args = parser.parse_args()
|
||||
input_path = "."
|
||||
if args.input_path:
|
||||
input_path = args.input_path
|
||||
args = parser.parse_args()
|
||||
if not args.results_path:
|
||||
raise AssertionError("No result path provided")
|
||||
results_path = args.results_path
|
||||
|
||||
prefixes = args.test_cases
|
||||
if args.all:
|
||||
prefixes = None
|
||||
results = TestResults(results_directory=results_path,
|
||||
source_expected_directory=args.source_expected_path)
|
||||
|
||||
replace_file_names(path=input_path, output_path=args.output_path, removed="_actual.png",
|
||||
replacement=".png", prefixes=prefixes)
|
||||
if args.all:
|
||||
results.batch_move()
|
||||
elif args.tests:
|
||||
results.handle_all_failed_images()
|
||||
elif args.compare:
|
||||
for file_prefix in args.compare:
|
||||
results.show_images(file_prefix)
|
||||
else:
|
||||
results.batch_move(args.batch)
|
||||
|
||||
@@ -118,7 +118,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
Shader shader = Shader(api, cleanup, ShaderConfig {
|
||||
.vertexShader = fullscreenVs,
|
||||
.fragmentShader = fullscreenFs,
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}, {"Params"}}
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo}, {"Params"}}
|
||||
});
|
||||
|
||||
TrianglePrimitive const triangle(getDriverApi());
|
||||
|
||||
@@ -308,7 +308,7 @@ TEST_F(LoadImageTest, UpdateImage2D) {
|
||||
Shader shader(api, cleanup, ShaderConfig{
|
||||
.vertexShader = mVertexShader,
|
||||
.fragmentShader= fragment,
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo}}
|
||||
});
|
||||
|
||||
// Create a Texture.
|
||||
@@ -372,7 +372,7 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||
getSamplerTypeName(textureFormat), fragmentTemplate);
|
||||
Shader shader(api, cleanup, ShaderConfig{
|
||||
.vertexShader = mVertexShader, .fragmentShader = fragment, .uniforms = {{
|
||||
"test_tex", DescriptorType::SAMPLER, samplerInfo
|
||||
"test_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo
|
||||
}}});
|
||||
|
||||
// Create a texture.
|
||||
@@ -447,7 +447,7 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
Shader shader(api, cleanup, ShaderConfig {
|
||||
.vertexShader = mVertexShader,
|
||||
.fragmentShader = fragment,
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo}}
|
||||
});
|
||||
|
||||
// Create a texture with 3 mip levels.
|
||||
@@ -508,7 +508,7 @@ TEST_F(LoadImageTest, UpdateImage3D) {
|
||||
Shader shader(api, cleanup, ShaderConfig {
|
||||
.vertexShader = mVertexShader,
|
||||
.fragmentShader = fragment,
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER_2D_ARRAY_FLOAT, samplerInfo}}
|
||||
});
|
||||
|
||||
// Create a texture.
|
||||
|
||||
@@ -82,7 +82,7 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
.vertexShader = vertexShader,
|
||||
.fragmentShader = fragmentTexturedLod,
|
||||
.uniforms = {{
|
||||
"backend_test_sib_tex", DescriptorType::SAMPLER, samplerInfo
|
||||
"backend_test_sib_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo
|
||||
}}
|
||||
});
|
||||
|
||||
|
||||
@@ -21,13 +21,17 @@
|
||||
|
||||
#include <filament/FilamentAPI.h>
|
||||
#include <filament/ToneMapper.h>
|
||||
#include <filament/ColorSpace.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -201,15 +205,14 @@ public:
|
||||
*
|
||||
* The default tone mapping operator is ACESLegacyToneMapper.
|
||||
*
|
||||
* The specified tone mapper must have a lifecycle that exceeds the lifetime of
|
||||
* this builder. Since the build(Engine&) method is synchronous, it is safe to
|
||||
* delete the tone mapper object after that finishes executing.
|
||||
* The ownership of the specified tone mapper is shared with the builder and built
|
||||
* ColorGrading object.
|
||||
*
|
||||
* @param toneMapper The tone mapping operator to apply to the HDR color buffer
|
||||
*
|
||||
* @return This Builder, for chaining calls
|
||||
*/
|
||||
Builder& toneMapper(ToneMapper const* UTILS_NULLABLE toneMapper) noexcept;
|
||||
Builder& toneMapper(std::shared_ptr<ToneMapper> toneMapper) noexcept;
|
||||
|
||||
/**
|
||||
* Selects the tone mapping operator to apply to the HDR color buffer as the last
|
||||
@@ -487,7 +490,118 @@ public:
|
||||
friend class FColorGrading;
|
||||
};
|
||||
|
||||
protected:
|
||||
/** Returns the quality level used to create this ColorGrading object. */
|
||||
QualityLevel getQuality() const noexcept;
|
||||
|
||||
/** Returns the LUT format used to create this ColorGrading object. */
|
||||
LutFormat getLutFormat() const noexcept;
|
||||
|
||||
/** Returns the LUT dimensions used to create this ColorGrading object. */
|
||||
uint8_t getLutDimensions() const noexcept;
|
||||
|
||||
/** Returns the tone mapper used to create this ColorGrading object. */
|
||||
const ToneMapper& getToneMapper() const noexcept;
|
||||
|
||||
/** Returns whether luminance scaling was enabled during creation. */
|
||||
bool isLuminanceScalingEnabled() const noexcept;
|
||||
|
||||
/** Returns whether gamut mapping was enabled during creation. */
|
||||
bool isGamutMappingEnabled() const noexcept;
|
||||
|
||||
/** Returns the exposure value used to create this ColorGrading object. */
|
||||
float getExposure() const noexcept;
|
||||
|
||||
/** Returns the night adaptation value used to create this ColorGrading object. */
|
||||
float getNightAdaptation() const noexcept;
|
||||
|
||||
/** Returns the white balance temperature used to create this ColorGrading object. */
|
||||
float getWhiteBalanceTemperature() const noexcept;
|
||||
|
||||
/** Returns the white balance tint used to create this ColorGrading object. */
|
||||
float getWhiteBalanceTint() const noexcept;
|
||||
|
||||
/** Returns the channel mixer output for the red channel. */
|
||||
math::float3 getChannelMixerOutRed() const noexcept;
|
||||
|
||||
/** Returns the channel mixer output for the green channel. */
|
||||
math::float3 getChannelMixerOutGreen() const noexcept;
|
||||
|
||||
/** Returns the channel mixer output for the blue channel. */
|
||||
math::float3 getChannelMixerOutBlue() const noexcept;
|
||||
|
||||
/** Returns the shadows adjustment used to create this ColorGrading object. */
|
||||
math::float3 getShadows() const noexcept;
|
||||
|
||||
/** Returns the midtones adjustment used to create this ColorGrading object. */
|
||||
math::float3 getMidtones() const noexcept;
|
||||
|
||||
/** Returns the highlights adjustment used to create this ColorGrading object. */
|
||||
math::float3 getHighlights() const noexcept;
|
||||
|
||||
/** Returns the shadow/midtones/highlights ranges used to create this ColorGrading object. */
|
||||
math::float4 getShadowMidtonesHighlightsRanges() const noexcept;
|
||||
|
||||
/** Returns the slope adjustment used to create this ColorGrading object. */
|
||||
math::float3 getSlope() const noexcept;
|
||||
|
||||
/** Returns the offset adjustment used to create this ColorGrading object. */
|
||||
math::float3 getOffset() const noexcept;
|
||||
|
||||
/** Returns the power adjustment used to create this ColorGrading object. */
|
||||
math::float3 getPower() const noexcept;
|
||||
|
||||
/** Returns the contrast value used to create this ColorGrading object. */
|
||||
float getContrast() const noexcept;
|
||||
|
||||
/** Returns the vibrance value used to create this ColorGrading object. */
|
||||
float getVibrance() const noexcept;
|
||||
|
||||
/** Returns the saturation value used to create this ColorGrading object. */
|
||||
float getSaturation() const noexcept;
|
||||
|
||||
/** Returns the shadow gamma curve adjustment used to create this ColorGrading object. */
|
||||
math::float3 getCurvesShadowGamma() const noexcept;
|
||||
|
||||
/** Returns the mid-point curve adjustment used to create this ColorGrading object. */
|
||||
math::float3 getCurvesMidPoint() const noexcept;
|
||||
|
||||
/** Returns the highlight scale curve adjustment used to create this ColorGrading object. */
|
||||
math::float3 getCurvesHighlightScale() const noexcept;
|
||||
|
||||
/** Returns the output color space used to create this ColorGrading object. */
|
||||
const color::ColorSpace& getOutputColorSpace() const noexcept;
|
||||
|
||||
protected :
|
||||
struct Settings {
|
||||
LutFormat lutFormat = LutFormat::INTEGER;
|
||||
uint8_t lutDimensions = 32;
|
||||
std::shared_ptr<ToneMapper> toneMapper = std::make_shared<ACESLegacyToneMapper>();
|
||||
bool luminanceScaling = false;
|
||||
bool gummapMapping = false;
|
||||
float exposure = 0.0f;
|
||||
float nightAdaptation = 0.0f;
|
||||
float whiteBalanceTemperature = 0.0f;
|
||||
float whiteBalanceTint = 0.0f;
|
||||
math::float3 channelMixerOutRed{1.0f, 0.0f, 0.0f};
|
||||
math::float3 channelMixerOutGreen{0.0f, 1.0f, 0.0f};
|
||||
math::float3 channelMixerOutBlue{0.0f, 0.0f, 1.0f};
|
||||
math::float3 shadows{1.0f, 1.0f, 1.0f};
|
||||
math::float3 midtones{1.0f, 1.0f, 1.0f};
|
||||
math::float3 highlights{1.0f, 1.0f, 1.0f};
|
||||
math::float4 ShadowMidtonesHighlightsRanges{0.0f, 0.333f, 0.55f, 1.0f};
|
||||
math::float3 slope{1.0f};
|
||||
math::float3 offset{0.0f};
|
||||
math::float3 power{1.0f};
|
||||
float contrast = 1.0f;
|
||||
float vibrance = 1.0f;
|
||||
float saturation = 1.0f;
|
||||
math::float3 curvesShadowGamma{1.0f, 1.0f, 1.0f};
|
||||
math::float3 curvesMidPoint{1.0f, 1.0f, 1.0f};
|
||||
math::float3 curvesHighlightScale{1.0f, 1.0f, 1.0f};
|
||||
color::ColorSpace colorSpace = color::Rec709 - color::sRGB - color::D65;
|
||||
};
|
||||
Settings mSettings;
|
||||
|
||||
// prevent heap allocation
|
||||
~ColorGrading() = default;
|
||||
};
|
||||
|
||||
@@ -317,15 +317,6 @@ public:
|
||||
*/
|
||||
size_t metalUploadBufferSizeBytes = 512 * 1024;
|
||||
|
||||
/**
|
||||
* The action to take if a Drawable cannot be acquired.
|
||||
*
|
||||
* Each frame rendered requires a CAMetalDrawable texture, which is
|
||||
* presented on-screen at the completion of each frame. These are
|
||||
* limited and provided round-robin style by the system.
|
||||
*/
|
||||
bool metalDisablePanicOnDrawableFailure = false;
|
||||
|
||||
/**
|
||||
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||
* Currently only honored by the GL and Metal backends.
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace filament {
|
||||
struct UTILS_PUBLIC ToneMapper {
|
||||
ToneMapper() noexcept;
|
||||
virtual ~ToneMapper() noexcept;
|
||||
ToneMapper(ToneMapper const&) noexcept;
|
||||
ToneMapper& operator=(ToneMapper const&) noexcept = default;
|
||||
|
||||
/**
|
||||
* Maps an open domain (or "scene referred" values) color value to display
|
||||
@@ -69,6 +71,13 @@ struct UTILS_PUBLIC ToneMapper {
|
||||
*/
|
||||
virtual math::float3 operator()(math::float3 c) const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Creates a copy of this tone mapper instance.
|
||||
*
|
||||
* @return A pointer to a new ToneMapper instance that is a copy of this instance.
|
||||
*/
|
||||
virtual ToneMapper* clone() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* If true, then this function holds that f(x) = vec3(f(x.r), f(x.g), f(x.b))
|
||||
*
|
||||
@@ -92,8 +101,10 @@ struct UTILS_PUBLIC ToneMapper {
|
||||
struct UTILS_PUBLIC LinearToneMapper final : public ToneMapper {
|
||||
LinearToneMapper() noexcept;
|
||||
~LinearToneMapper() noexcept final;
|
||||
LinearToneMapper(LinearToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 c) const noexcept override;
|
||||
LinearToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return true; }
|
||||
bool isLDR() const noexcept override { return true; }
|
||||
};
|
||||
@@ -106,8 +117,10 @@ struct UTILS_PUBLIC LinearToneMapper final : public ToneMapper {
|
||||
struct UTILS_PUBLIC ACESToneMapper final : public ToneMapper {
|
||||
ACESToneMapper() noexcept;
|
||||
~ACESToneMapper() noexcept final;
|
||||
ACESToneMapper(ACESToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 c) const noexcept override;
|
||||
ACESToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return false; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
};
|
||||
@@ -121,8 +134,10 @@ struct UTILS_PUBLIC ACESToneMapper final : public ToneMapper {
|
||||
struct UTILS_PUBLIC ACESLegacyToneMapper final : public ToneMapper {
|
||||
ACESLegacyToneMapper() noexcept;
|
||||
~ACESLegacyToneMapper() noexcept final;
|
||||
ACESLegacyToneMapper(ACESLegacyToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 c) const noexcept override;
|
||||
ACESLegacyToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return false; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
};
|
||||
@@ -136,8 +151,10 @@ struct UTILS_PUBLIC ACESLegacyToneMapper final : public ToneMapper {
|
||||
struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
|
||||
FilmicToneMapper() noexcept;
|
||||
~FilmicToneMapper() noexcept final;
|
||||
FilmicToneMapper(FilmicToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 x) const noexcept override;
|
||||
FilmicToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return true; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
};
|
||||
@@ -150,8 +167,10 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
|
||||
struct UTILS_PUBLIC PBRNeutralToneMapper final : public ToneMapper {
|
||||
PBRNeutralToneMapper() noexcept;
|
||||
~PBRNeutralToneMapper() noexcept final;
|
||||
PBRNeutralToneMapper(PBRNeutralToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 x) const noexcept override;
|
||||
virtual PBRNeutralToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return false; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
};
|
||||
@@ -173,8 +192,10 @@ struct UTILS_PUBLIC AgxToneMapper final : public ToneMapper {
|
||||
*/
|
||||
explicit AgxToneMapper(AgxLook look = AgxLook::NONE) noexcept;
|
||||
~AgxToneMapper() noexcept final;
|
||||
AgxToneMapper(AgxToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 x) const noexcept override;
|
||||
virtual AgxToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return false; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
|
||||
@@ -215,12 +236,13 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
|
||||
) noexcept;
|
||||
~GenericToneMapper() noexcept final;
|
||||
|
||||
GenericToneMapper(GenericToneMapper const&) = delete;
|
||||
GenericToneMapper& operator=(GenericToneMapper const&) = delete;
|
||||
GenericToneMapper(GenericToneMapper const& rhs) noexcept;
|
||||
GenericToneMapper& operator=(GenericToneMapper const& rhs) noexcept;
|
||||
GenericToneMapper(GenericToneMapper&& rhs) noexcept;
|
||||
GenericToneMapper& operator=(GenericToneMapper&& rhs) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 x) const noexcept override;
|
||||
GenericToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return true; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
|
||||
@@ -283,8 +305,10 @@ private:
|
||||
struct UTILS_PUBLIC DisplayRangeToneMapper final : public ToneMapper {
|
||||
DisplayRangeToneMapper() noexcept;
|
||||
~DisplayRangeToneMapper() noexcept override;
|
||||
DisplayRangeToneMapper(DisplayRangeToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 c) const noexcept override;
|
||||
DisplayRangeToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return false; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
};
|
||||
|
||||
@@ -648,44 +648,32 @@ bool ChunkAttributeInfo::unflatten(Unflattener& unflattener,
|
||||
bool ChunkDescriptorBindingsInfo::unflatten(Unflattener& unflattener,
|
||||
MaterialParser::DescriptorBindingsContainer* container) {
|
||||
|
||||
uint8_t setCount;
|
||||
if (!unflattener.read(&setCount)) {
|
||||
static_assert(sizeof(DescriptorSetBindingPoints) == sizeof(uint8_t));
|
||||
|
||||
uint8_t descriptorCount;
|
||||
if (!unflattener.read(&descriptorCount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < setCount; j++) {
|
||||
static_assert(sizeof(DescriptorSetBindingPoints) == sizeof(uint8_t));
|
||||
|
||||
DescriptorSetBindingPoints set;
|
||||
if (!unflattener.read(reinterpret_cast<uint8_t*>(&set))) {
|
||||
auto& descriptors = (*container)[+DescriptorSetBindingPoints::PER_MATERIAL];
|
||||
descriptors.reserve(descriptorCount);
|
||||
for (size_t i = 0; i < descriptorCount; i++) {
|
||||
CString name;
|
||||
if (!unflattener.read(&name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t descriptorCount;
|
||||
if (!unflattener.read(&descriptorCount)) {
|
||||
uint8_t type;
|
||||
if (!unflattener.read(&type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& descriptors = (*container)[+set];
|
||||
descriptors.reserve(descriptorCount);
|
||||
for (size_t i = 0; i < descriptorCount; i++) {
|
||||
CString name;
|
||||
if (!unflattener.read(&name)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t type;
|
||||
if (!unflattener.read(&type)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t binding;
|
||||
if (!unflattener.read(&binding)) {
|
||||
return false;
|
||||
}
|
||||
descriptors.push_back({
|
||||
std::move(name),
|
||||
DescriptorType(type),
|
||||
descriptor_binding_t(binding)});
|
||||
uint8_t binding;
|
||||
if (!unflattener.read(&binding)) {
|
||||
return false;
|
||||
}
|
||||
descriptors.push_back({
|
||||
std::move(name),
|
||||
DescriptorType(type),
|
||||
descriptor_binding_t(binding)});
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -693,42 +681,40 @@ bool ChunkDescriptorBindingsInfo::unflatten(Unflattener& unflattener,
|
||||
|
||||
bool ChunkDescriptorSetLayoutInfo::unflatten(Unflattener& unflattener,
|
||||
MaterialParser::DescriptorSetLayoutContainer* container) {
|
||||
for (size_t j = 0; j < 2; j++) {
|
||||
uint8_t descriptorCount;
|
||||
if (!unflattener.read(&descriptorCount)) {
|
||||
uint8_t descriptorCount;
|
||||
if (!unflattener.read(&descriptorCount)) {
|
||||
return false;
|
||||
}
|
||||
auto& descriptors = container->bindings;
|
||||
descriptors.reserve(descriptorCount);
|
||||
for (size_t i = 0; i < descriptorCount; i++) {
|
||||
uint8_t type;
|
||||
if (!unflattener.read(&type)) {
|
||||
return false;
|
||||
}
|
||||
auto& descriptors = (*container)[j].bindings;
|
||||
descriptors.reserve(descriptorCount);
|
||||
for (size_t i = 0; i < descriptorCount; i++) {
|
||||
uint8_t type;
|
||||
if (!unflattener.read(&type)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t stageFlags;
|
||||
if (!unflattener.read(&stageFlags)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t binding;
|
||||
if (!unflattener.read(&binding)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t flags;
|
||||
if (!unflattener.read(&flags)) {
|
||||
return false;
|
||||
}
|
||||
uint16_t count;
|
||||
if (!unflattener.read(&count)) {
|
||||
return false;
|
||||
}
|
||||
descriptors.push_back({
|
||||
DescriptorType(type),
|
||||
ShaderStageFlags(stageFlags),
|
||||
descriptor_binding_t(binding),
|
||||
DescriptorFlags(flags),
|
||||
count,
|
||||
});
|
||||
uint8_t stageFlags;
|
||||
if (!unflattener.read(&stageFlags)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t binding;
|
||||
if (!unflattener.read(&binding)) {
|
||||
return false;
|
||||
}
|
||||
uint8_t flags;
|
||||
if (!unflattener.read(&flags)) {
|
||||
return false;
|
||||
}
|
||||
uint16_t count;
|
||||
if (!unflattener.read(&count)) {
|
||||
return false;
|
||||
}
|
||||
descriptors.push_back({
|
||||
DescriptorType(type),
|
||||
ShaderStageFlags(stageFlags),
|
||||
descriptor_binding_t(binding),
|
||||
DescriptorFlags(flags),
|
||||
count,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
using DescriptorBindingsContainer = backend::Program::DescriptorSetInfo;
|
||||
bool getDescriptorBindings(DescriptorBindingsContainer* container) const noexcept;
|
||||
|
||||
using DescriptorSetLayoutContainer = std::array<backend::DescriptorSetLayout, 2>;
|
||||
using DescriptorSetLayoutContainer = backend::DescriptorSetLayout;
|
||||
bool getDescriptorSetLayout(DescriptorSetLayoutContainer* container) const noexcept;
|
||||
|
||||
bool getDepthWriteSet(bool* value) const noexcept;
|
||||
|
||||
@@ -232,7 +232,7 @@ PostProcessManager::~PostProcessManager() noexcept = default;
|
||||
void PostProcessManager::setFrameUniforms(DriverApi& driver,
|
||||
TypedUniformBuffer<PerViewUib>& uniforms) noexcept {
|
||||
mPostProcessDescriptorSet.setFrameUniforms(driver, uniforms);
|
||||
mSsrPassDescriptorSet.setFrameUniforms(uniforms);
|
||||
mSsrPassDescriptorSet.setFrameUniforms(mEngine, uniforms);
|
||||
}
|
||||
|
||||
void PostProcessManager::bindPostProcessDescriptorSet(DriverApi& driver) const noexcept {
|
||||
@@ -504,6 +504,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
if (config.picking) {
|
||||
// FIXME: the DescriptorSetLayout must specify SAMPLER_FLOAT
|
||||
data.picking = builder.createTexture("Picking Buffer", {
|
||||
.width = width, .height = height,
|
||||
.format = isES2 ? TextureFormat::RGBA8 : TextureFormat::RG32F });
|
||||
@@ -653,7 +654,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
|
||||
options, passBuilder = passBuilder]
|
||||
(FrameGraphResources const& resources, auto const& data, DriverApi& driver) mutable {
|
||||
// set structure sampler
|
||||
mSsrPassDescriptorSet.prepareStructure(data.structure ?
|
||||
mSsrPassDescriptorSet.prepareStructure(mEngine, data.structure ?
|
||||
resources.getTexture(data.structure) : getOneTexture());
|
||||
|
||||
// set screen-space reflections and screen-space refractions
|
||||
@@ -664,7 +665,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
|
||||
// the history sampler is a regular texture2D
|
||||
TextureHandle const history = data.history ?
|
||||
resources.getTexture(data.history) : getZeroTexture();
|
||||
mSsrPassDescriptorSet.prepareHistorySSR(history, reprojection, uvFromViewMatrix, options);
|
||||
mSsrPassDescriptorSet.prepareHistorySSR(mEngine, history, reprojection, uvFromViewMatrix, options);
|
||||
|
||||
mSsrPassDescriptorSet.commit(mEngine);
|
||||
|
||||
|
||||
@@ -921,7 +921,6 @@ backend::Viewport RenderPass::Executor::applyScissorViewport(
|
||||
UTILS_NOINLINE // no need to be inlined
|
||||
void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
|
||||
Command const* first, Command const* last) const noexcept {
|
||||
|
||||
SYSTRACE_CALL();
|
||||
SYSTRACE_CONTEXT();
|
||||
|
||||
@@ -942,6 +941,13 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
|
||||
driver.scissor(mScissor);
|
||||
}
|
||||
|
||||
// If we have a mColorPassDescriptorSet, we need to use its idea of "VSM" to select
|
||||
// the descriptor set layout. Materials always offer both.
|
||||
// If we don't have a mColorPassDescriptorSet, it doesn't matter because the layout
|
||||
// are chosen via the variant only.
|
||||
bool const useVsmDescriptorSetLayout =
|
||||
mColorPassDescriptorSet ? mColorPassDescriptorSet->isVSM() : false;
|
||||
|
||||
bool const polygonOffsetOverride = mPolygonOffsetOverride;
|
||||
PipelineState pipeline{
|
||||
// initialize with polygon offset override
|
||||
@@ -1051,8 +1057,13 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
|
||||
|
||||
// Each material has its own version of the per-view descriptor-set layout,
|
||||
// because it depends on the material features (e.g. lit/unlit)
|
||||
// TODO: QUESTION: are
|
||||
// Variant::isValidDepthVariant(info.materialVariant) and
|
||||
// Variant::isSSRVariant(info.materialVariant)
|
||||
// constant? If so we could precompute ma->getPerViewDescriptorSetLayout()
|
||||
pipeline.pipelineLayout.setLayout[+DescriptorSetBindingPoints::PER_VIEW] =
|
||||
ma->getPerViewDescriptorSetLayout(info.materialVariant).getHandle();
|
||||
ma->getPerViewDescriptorSetLayout(info.materialVariant,
|
||||
useVsmDescriptorSetLayout).getHandle();
|
||||
|
||||
// Each material has a per-material descriptor-set layout which encodes the
|
||||
// material's parameters (ubo and samplers)
|
||||
|
||||
@@ -205,11 +205,13 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
|
||||
view.prepareSSAO(data.ssao ?
|
||||
resources.getTexture(data.ssao) : engine.getOneTextureArray());
|
||||
|
||||
view.prepareShadowMapping(view.getVsmShadowOptions().highPrecision);
|
||||
view.prepareShadowMapping(engine, view.getVsmShadowOptions().highPrecision);
|
||||
|
||||
// set shadow sampler
|
||||
view.prepareShadow(data.shadows ?
|
||||
resources.getTexture(data.shadows) : engine.getOneTextureArray());
|
||||
resources.getTexture(data.shadows) :
|
||||
(view.getShadowType() != ShadowType::PCF ?
|
||||
engine.getOneTextureArray() : engine.getOneTextureArrayDepth()));
|
||||
|
||||
// set structure sampler
|
||||
view.prepareStructure(data.structure ?
|
||||
|
||||
@@ -188,7 +188,8 @@ float3 ACES(float3 color, float brightness) noexcept {
|
||||
|
||||
#define DEFAULT_CONSTRUCTORS(A) \
|
||||
A::A() noexcept = default; \
|
||||
A::~A() noexcept = default;
|
||||
A::~A() noexcept = default; \
|
||||
A::A(A const&) noexcept = default;
|
||||
|
||||
DEFAULT_CONSTRUCTORS(ToneMapper)
|
||||
|
||||
@@ -202,6 +203,10 @@ float3 LinearToneMapper::operator()(float3 const v) const noexcept {
|
||||
return saturate(v);
|
||||
}
|
||||
|
||||
LinearToneMapper* LinearToneMapper::clone() const noexcept {
|
||||
return new LinearToneMapper(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ACES tone mappers
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -212,12 +217,20 @@ float3 ACESToneMapper::operator()(float3 const c) const noexcept {
|
||||
return aces::ACES(c, 1.0f);
|
||||
}
|
||||
|
||||
ACESToneMapper* ACESToneMapper::clone() const noexcept {
|
||||
return new ACESToneMapper(*this);
|
||||
}
|
||||
|
||||
DEFAULT_CONSTRUCTORS(ACESLegacyToneMapper)
|
||||
|
||||
float3 ACESLegacyToneMapper::operator()(float3 const c) const noexcept {
|
||||
return aces::ACES(c, 1.0f / 0.6f);
|
||||
}
|
||||
|
||||
ACESLegacyToneMapper* ACESLegacyToneMapper::clone() const noexcept {
|
||||
return new ACESLegacyToneMapper(*this);
|
||||
}
|
||||
|
||||
DEFAULT_CONSTRUCTORS(FilmicToneMapper)
|
||||
|
||||
float3 FilmicToneMapper::operator()(float3 const x) const noexcept {
|
||||
@@ -230,6 +243,10 @@ float3 FilmicToneMapper::operator()(float3 const x) const noexcept {
|
||||
return (x * (a * x + b)) / (x * (c * x + d) + e);
|
||||
}
|
||||
|
||||
FilmicToneMapper* FilmicToneMapper::clone() const noexcept {
|
||||
return new FilmicToneMapper(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PBR Neutral tone mapper
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -256,12 +273,17 @@ float3 PBRNeutralToneMapper::operator()(float3 color) const noexcept {
|
||||
return mix(color, float3(newPeak), g);
|
||||
}
|
||||
|
||||
PBRNeutralToneMapper* PBRNeutralToneMapper::clone() const noexcept {
|
||||
return new PBRNeutralToneMapper(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// AgX tone mapper
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
AgxToneMapper::AgxToneMapper(AgxLook const look) noexcept : look(look) {}
|
||||
AgxToneMapper::~AgxToneMapper() noexcept = default;
|
||||
AgxToneMapper::AgxToneMapper(AgxToneMapper const& rhs) noexcept = default;
|
||||
|
||||
// These matrices taken from Blender's implementation of AgX, which works with Rec.2020 primaries.
|
||||
// https://github.com/EaryChow/AgX_LUT_Gen/blob/main/AgXBaseRec2020.py
|
||||
@@ -356,6 +378,10 @@ float3 AgxToneMapper::operator()(float3 v) const noexcept {
|
||||
return v;
|
||||
}
|
||||
|
||||
AgxToneMapper* AgxToneMapper::clone() const noexcept {
|
||||
return new AgxToneMapper(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Display range tone mapper
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -394,6 +420,10 @@ float3 DisplayRangeToneMapper::operator()(float3 const c) const noexcept {
|
||||
return mix(debugColors[index], debugColors[index + 1], saturate(v - float(index)));
|
||||
}
|
||||
|
||||
DisplayRangeToneMapper* DisplayRangeToneMapper::clone() const noexcept {
|
||||
return new DisplayRangeToneMapper(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Generic tone mapper
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -454,7 +484,19 @@ GenericToneMapper::~GenericToneMapper() noexcept {
|
||||
delete mOptions;
|
||||
}
|
||||
|
||||
GenericToneMapper::GenericToneMapper(GenericToneMapper&& rhs) noexcept : mOptions(rhs.mOptions) {
|
||||
GenericToneMapper::GenericToneMapper(GenericToneMapper const& rhs) noexcept {
|
||||
mOptions = new Options(*rhs.mOptions);
|
||||
}
|
||||
|
||||
GenericToneMapper& GenericToneMapper::operator=(GenericToneMapper const& rhs) noexcept {
|
||||
if (this != &rhs) {
|
||||
delete mOptions;
|
||||
mOptions = new Options(*rhs.mOptions);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
GenericToneMapper::GenericToneMapper(GenericToneMapper&& rhs) noexcept : mOptions(rhs.mOptions) {
|
||||
rhs.mOptions = nullptr;
|
||||
}
|
||||
|
||||
@@ -469,6 +511,10 @@ float3 GenericToneMapper::operator()(float3 x) const noexcept {
|
||||
return mOptions->outputScale * x / (x + mOptions->inputScale);
|
||||
}
|
||||
|
||||
GenericToneMapper* GenericToneMapper::clone() const noexcept {
|
||||
return new GenericToneMapper(*this);
|
||||
}
|
||||
|
||||
float GenericToneMapper::getContrast() const noexcept { return mOptions->contrast; }
|
||||
float GenericToneMapper::getMidGrayIn() const noexcept { return mOptions->midGrayIn; }
|
||||
float GenericToneMapper::getMidGrayOut() const noexcept { return mOptions->midGrayOut; }
|
||||
|
||||
@@ -49,7 +49,7 @@ using namespace backend;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct ColorGrading::BuilderDetails {
|
||||
const ToneMapper* toneMapper = nullptr;
|
||||
std::shared_ptr<ToneMapper> toneMapper = nullptr;
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
@@ -173,7 +173,7 @@ ColorGrading::Builder& ColorGrading::Builder::dimensions(uint8_t const dim) noex
|
||||
return *this;
|
||||
}
|
||||
|
||||
ColorGrading::Builder& ColorGrading::Builder::toneMapper(const ToneMapper* toneMapper) noexcept {
|
||||
ColorGrading::Builder& ColorGrading::Builder::toneMapper(std::shared_ptr<ToneMapper> toneMapper) noexcept {
|
||||
mImpl->toneMapper = toneMapper;
|
||||
return *this;
|
||||
}
|
||||
@@ -271,6 +271,110 @@ ColorGrading::Builder& ColorGrading::Builder::outputColorSpace(
|
||||
return *this;
|
||||
}
|
||||
|
||||
ColorGrading::LutFormat ColorGrading::getLutFormat() const noexcept {
|
||||
return mSettings.lutFormat;
|
||||
}
|
||||
|
||||
uint8_t ColorGrading::getLutDimensions() const noexcept {
|
||||
return mSettings.lutDimensions;
|
||||
}
|
||||
|
||||
const ToneMapper& ColorGrading::getToneMapper() const noexcept {
|
||||
return *mSettings.toneMapper;
|
||||
}
|
||||
|
||||
bool ColorGrading::isLuminanceScalingEnabled() const noexcept {
|
||||
return mSettings.luminanceScaling;
|
||||
}
|
||||
|
||||
bool ColorGrading::isGamutMappingEnabled() const noexcept {
|
||||
return mSettings.gummapMapping;
|
||||
}
|
||||
|
||||
float ColorGrading::getExposure() const noexcept {
|
||||
return mSettings.exposure;
|
||||
}
|
||||
|
||||
float ColorGrading::getNightAdaptation() const noexcept {
|
||||
return mSettings.nightAdaptation;
|
||||
}
|
||||
|
||||
float ColorGrading::getWhiteBalanceTemperature() const noexcept {
|
||||
return mSettings.whiteBalanceTemperature;
|
||||
}
|
||||
|
||||
float ColorGrading::getWhiteBalanceTint() const noexcept {
|
||||
return mSettings.whiteBalanceTint;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getChannelMixerOutRed() const noexcept {
|
||||
return mSettings.channelMixerOutRed;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getChannelMixerOutGreen() const noexcept {
|
||||
return mSettings.channelMixerOutGreen;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getChannelMixerOutBlue() const noexcept {
|
||||
return mSettings.channelMixerOutBlue;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getShadows() const noexcept {
|
||||
return mSettings.shadows;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getMidtones() const noexcept {
|
||||
return mSettings.midtones;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getHighlights() const noexcept {
|
||||
return mSettings.highlights;
|
||||
}
|
||||
|
||||
math::float4 ColorGrading::getShadowMidtonesHighlightsRanges() const noexcept {
|
||||
return mSettings.ShadowMidtonesHighlightsRanges;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getSlope() const noexcept {
|
||||
return mSettings.slope;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getOffset() const noexcept {
|
||||
return mSettings.offset;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getPower() const noexcept {
|
||||
return mSettings.power;
|
||||
}
|
||||
|
||||
float ColorGrading::getContrast() const noexcept {
|
||||
return mSettings.contrast;
|
||||
}
|
||||
|
||||
float ColorGrading::getVibrance() const noexcept {
|
||||
return mSettings.vibrance;
|
||||
}
|
||||
|
||||
float ColorGrading::getSaturation() const noexcept {
|
||||
return mSettings.saturation;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getCurvesShadowGamma() const noexcept {
|
||||
return mSettings.curvesShadowGamma;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getCurvesMidPoint() const noexcept {
|
||||
return mSettings.curvesMidPoint;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getCurvesHighlightScale() const noexcept {
|
||||
return mSettings.curvesHighlightScale;
|
||||
}
|
||||
|
||||
const color::ColorSpace& ColorGrading::getOutputColorSpace() const noexcept {
|
||||
return mSettings.colorSpace;
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
@@ -287,30 +391,25 @@ ColorGrading* ColorGrading::Builder::build(Engine& engine) {
|
||||
if (needToneMapper) {
|
||||
switch (mImpl->toneMapping) {
|
||||
case ToneMapping::LINEAR:
|
||||
mImpl->toneMapper = new LinearToneMapper();
|
||||
mImpl->toneMapper = std::make_shared<LinearToneMapper>();
|
||||
break;
|
||||
case ToneMapping::ACES_LEGACY:
|
||||
mImpl->toneMapper = new ACESLegacyToneMapper();
|
||||
mImpl->toneMapper = std::make_shared<ACESLegacyToneMapper>();
|
||||
break;
|
||||
case ToneMapping::ACES:
|
||||
mImpl->toneMapper = new ACESToneMapper();
|
||||
mImpl->toneMapper = std::make_shared<ACESToneMapper>();
|
||||
break;
|
||||
case ToneMapping::FILMIC:
|
||||
mImpl->toneMapper = new FilmicToneMapper();
|
||||
mImpl->toneMapper = std::make_shared<FilmicToneMapper>();
|
||||
break;
|
||||
case ToneMapping::DISPLAY_RANGE:
|
||||
mImpl->toneMapper = new DisplayRangeToneMapper();
|
||||
mImpl->toneMapper = std::make_shared<DisplayRangeToneMapper>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FColorGrading* colorGrading = downcast(engine).createColorGrading(*this);
|
||||
|
||||
if (needToneMapper) {
|
||||
delete mImpl->toneMapper;
|
||||
mImpl->toneMapper = nullptr;
|
||||
}
|
||||
|
||||
return colorGrading;
|
||||
}
|
||||
|
||||
@@ -886,6 +985,9 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
|
||||
data, lutElementCount * elementSize, format, type,
|
||||
[](void* buffer, size_t, void*) { free(buffer); }
|
||||
});
|
||||
|
||||
// Initialize settings from builder
|
||||
initializeSettings(builder, mSettings);
|
||||
}
|
||||
|
||||
FColorGrading::~FColorGrading() noexcept = default;
|
||||
@@ -895,4 +997,33 @@ void FColorGrading::terminate(FEngine& engine) {
|
||||
driver.destroyTexture(mLutHandle);
|
||||
}
|
||||
|
||||
void FColorGrading::initializeSettings(const Builder& builder, Settings& settings) noexcept {
|
||||
settings.lutFormat = builder->format;
|
||||
settings.lutDimensions = builder->dimension;
|
||||
settings.toneMapper = builder->toneMapper;
|
||||
settings.luminanceScaling = builder->luminanceScaling;
|
||||
settings.gummapMapping = builder->gamutMapping;
|
||||
settings.exposure = builder->exposure;
|
||||
settings.nightAdaptation = builder->nightAdaptation;
|
||||
settings.whiteBalanceTemperature = builder->whiteBalance.x;
|
||||
settings.whiteBalanceTint = builder->whiteBalance.y;
|
||||
settings.channelMixerOutRed = builder->outRed;
|
||||
settings.channelMixerOutGreen = builder->outGreen;
|
||||
settings.channelMixerOutBlue = builder->outBlue;
|
||||
settings.shadows = builder->shadows;
|
||||
settings.midtones = builder->midtones;
|
||||
settings.highlights = builder->highlights;
|
||||
settings.ShadowMidtonesHighlightsRanges = builder->tonalRanges;
|
||||
settings.slope = builder->slope;
|
||||
settings.offset = builder->offset;
|
||||
settings.power = builder->power;
|
||||
settings.contrast = builder->contrast;
|
||||
settings.vibrance = builder->vibrance;
|
||||
settings.saturation = builder->saturation;
|
||||
settings.curvesShadowGamma = builder->shadowGamma;
|
||||
settings.curvesMidPoint = builder->midPoint;
|
||||
settings.curvesHighlightScale = builder->highlightScale;
|
||||
settings.colorSpace = builder->outputColorSpace;
|
||||
}
|
||||
|
||||
} //namespace filament
|
||||
|
||||
@@ -51,6 +51,8 @@ private:
|
||||
uint32_t mDimension;
|
||||
bool mIsOneDimensional;
|
||||
bool mIsLDR;
|
||||
|
||||
static void initializeSettings(const Builder& builder, Settings& settings) noexcept;
|
||||
};
|
||||
|
||||
FILAMENT_DOWNCAST(ColorGrading)
|
||||
|
||||
@@ -138,7 +138,6 @@ Engine* FEngine::create(Builder const& builder) {
|
||||
.forceGLES2Context = instance->getConfig().forceGLES2Context,
|
||||
.stereoscopicType = instance->getConfig().stereoscopicType,
|
||||
.assertNativeWindowIsValid = instance->features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = instance->getConfig().metalDisablePanicOnDrawableFailure,
|
||||
};
|
||||
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
|
||||
|
||||
@@ -362,8 +361,6 @@ void FEngine::init() {
|
||||
}
|
||||
|
||||
// initialize the dummy textures so that their contents are not undefined
|
||||
static constexpr uint32_t zeroes[6] = {};
|
||||
static constexpr uint32_t ones = 0xffffffff;
|
||||
|
||||
mDefaultIblTexture = downcast(Texture::Builder()
|
||||
.width(1).height(1).levels(1)
|
||||
@@ -372,7 +369,8 @@ void FEngine::init() {
|
||||
.build(*this));
|
||||
|
||||
driverApi.update3DImage(mDefaultIblTexture->getHwHandle(), 0, 0, 0, 0, 1, 1, 6,
|
||||
{ zeroes, sizeof(zeroes), Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||
{ std::array<uint8_t, 6>{}.data(), 6,
|
||||
Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||
|
||||
// 3 bands = 9 float3
|
||||
constexpr float sh[9 * 3] = { 0.0f };
|
||||
@@ -394,10 +392,12 @@ void FEngine::init() {
|
||||
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
|
||||
|
||||
driverApi.update3DImage(mDummyOneTexture, 0, 0, 0, 0, 1, 1, 1,
|
||||
{ &ones, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||
{ std::array<uint8_t, 4>{0xff, 0xff, 0xff, 0xff}.data(), 4,
|
||||
Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||
|
||||
driverApi.update3DImage(mDummyZeroTexture, 0, 0, 0, 0, 1, 1, 1,
|
||||
{ zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||
{ std::array<uint8_t, 4>{}.data(), 4,
|
||||
Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||
|
||||
|
||||
mPerViewDescriptorSetLayoutSsrVariant = {
|
||||
@@ -424,25 +424,28 @@ void FEngine::init() {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this));
|
||||
|
||||
FMaterial::DefaultMaterialBuilder defaultMaterialBuilder;
|
||||
switch (mConfig.stereoscopicType) {
|
||||
case StereoscopicType::NONE:
|
||||
case StereoscopicType::INSTANCED:
|
||||
defaultMaterialBuilder.package(
|
||||
MATERIALS_DEFAULTMATERIAL_DATA, MATERIALS_DEFAULTMATERIAL_SIZE);
|
||||
MATERIALS_DEFAULTMATERIAL_DATA, MATERIALS_DEFAULTMATERIAL_SIZE);
|
||||
break;
|
||||
case StereoscopicType::MULTIVIEW:
|
||||
#ifdef FILAMENT_ENABLE_MULTIVIEW
|
||||
defaultMaterialBuilder.package(
|
||||
MATERIALS_DEFAULTMATERIAL_MULTIVIEW_DATA, MATERIALS_DEFAULTMATERIAL_MULTIVIEW_SIZE);
|
||||
MATERIALS_DEFAULTMATERIAL_MULTIVIEW_DATA,
|
||||
MATERIALS_DEFAULTMATERIAL_MULTIVIEW_SIZE);
|
||||
#else
|
||||
assert_invariant(false);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*this));
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(getSupportedFeatureLevel() >= FeatureLevel::FEATURE_LEVEL_1)) {
|
||||
mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this));
|
||||
|
||||
constexpr float3 dummyPositions[1] = {};
|
||||
constexpr short4 dummyTangents[1] = {};
|
||||
@@ -452,14 +455,26 @@ void FEngine::init() {
|
||||
mDummyOneTextureArray = driverApi.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1,
|
||||
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
|
||||
|
||||
mDummyOneTextureArrayDepth = driverApi.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1,
|
||||
TextureFormat::DEPTH32F, 1, 1, 1, 1, TextureUsage::DEFAULT);
|
||||
|
||||
mDummyZeroTextureArray = driverApi.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1,
|
||||
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
|
||||
|
||||
driverApi.update3DImage(mDummyOneTextureArray, 0, 0, 0, 0, 1, 1, 1,
|
||||
{ &ones, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||
{ std::array<uint8_t, 4>{0xff, 0xff, 0xff, 0xff}.data(), 4,
|
||||
Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||
|
||||
driverApi.update3DImage(mDummyOneTextureArrayDepth, 0, 0, 0, 0, 1, 1, 1,
|
||||
{ std::array<float, 1>{1.0f}.data(), 4,
|
||||
Texture::Format::DEPTH_COMPONENT, Texture::Type::FLOAT });
|
||||
|
||||
driverApi.update3DImage(mDummyZeroTextureArray, 0, 0, 0, 0, 1, 1, 1,
|
||||
{ zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||
{ std::array<uint8_t, 4>{}.data(), 4,
|
||||
Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||
|
||||
mDummyUniformBuffer = driverApi.createBufferObject(CONFIG_MINSPEC_UBO_SIZE,
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||
|
||||
mLightManager.init(*this);
|
||||
mDFG.init(*this);
|
||||
@@ -609,6 +624,8 @@ void FEngine::shutdown() {
|
||||
driver.destroyTexture(std::move(mDummyZeroTexture));
|
||||
driver.destroyTexture(std::move(mDummyZeroTextureArray));
|
||||
|
||||
driver.destroyBufferObject(std::move(mDummyUniformBuffer));
|
||||
|
||||
driver.destroyRenderTarget(std::move(mDefaultRenderTarget));
|
||||
|
||||
/*
|
||||
@@ -713,8 +730,7 @@ int FEngine::loop() {
|
||||
if (mPlatform == nullptr) {
|
||||
mPlatform = PlatformFactory::create(&mBackend);
|
||||
mOwnPlatform = true;
|
||||
const char* const backend = backendToString(mBackend);
|
||||
slog.i << "FEngine resolved backend: " << backend << io::endl;
|
||||
slog.i << "FEngine resolved backend: " << to_string(mBackend) << io::endl;
|
||||
if (mPlatform == nullptr) {
|
||||
slog.e << "Selected backend not supported in this build." << io::endl;
|
||||
mDriverBarrier.latch();
|
||||
@@ -734,7 +750,6 @@ int FEngine::loop() {
|
||||
.forceGLES2Context = mConfig.forceGLES2Context,
|
||||
.stereoscopicType = mConfig.stereoscopicType,
|
||||
.assertNativeWindowIsValid = features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = mConfig.metalDisablePanicOnDrawableFailure,
|
||||
};
|
||||
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
|
||||
|
||||
|
||||
@@ -486,10 +486,29 @@ public:
|
||||
return mPerRenderableDescriptorSetLayout;
|
||||
}
|
||||
|
||||
backend::Handle<backend::HwTexture> getOneTexture() const { return mDummyOneTexture; }
|
||||
backend::Handle<backend::HwTexture> getZeroTexture() const { return mDummyZeroTexture; }
|
||||
backend::Handle<backend::HwTexture> getOneTextureArray() const { return mDummyOneTextureArray; }
|
||||
backend::Handle<backend::HwTexture> getZeroTextureArray() const { return mDummyZeroTextureArray; }
|
||||
backend::Handle<backend::HwTexture> getOneTexture() const {
|
||||
return mDummyOneTexture;
|
||||
}
|
||||
|
||||
backend::Handle<backend::HwTexture> getOneTextureArray() const {
|
||||
return mDummyOneTextureArray;
|
||||
}
|
||||
|
||||
backend::Handle<backend::HwTexture> getOneTextureArrayDepth() const {
|
||||
return mDummyOneTextureArrayDepth;
|
||||
}
|
||||
|
||||
backend::Handle<backend::HwTexture> getZeroTexture() const {
|
||||
return mDummyZeroTexture;
|
||||
}
|
||||
|
||||
backend::Handle<backend::HwTexture> getZeroTextureArray() const {
|
||||
return mDummyZeroTextureArray;
|
||||
}
|
||||
|
||||
backend::Handle<backend::HwBufferObject> getDummyUniformBuffer() const {
|
||||
return mDummyUniformBuffer;
|
||||
}
|
||||
|
||||
static constexpr size_t MiB = 1024u * 1024u;
|
||||
size_t getMinCommandBufferSize() const noexcept { return mConfig.minCommandBufferSizeMB * MiB; }
|
||||
@@ -628,8 +647,10 @@ private:
|
||||
|
||||
backend::Handle<backend::HwTexture> mDummyOneTexture;
|
||||
backend::Handle<backend::HwTexture> mDummyOneTextureArray;
|
||||
backend::Handle<backend::HwTexture> mDummyOneTextureArrayDepth;
|
||||
backend::Handle<backend::HwTexture> mDummyZeroTextureArray;
|
||||
backend::Handle<backend::HwTexture> mDummyZeroTexture;
|
||||
backend::Handle<backend::HwBufferObject> mDummyUniformBuffer;
|
||||
|
||||
std::thread::id mMainThreadId{};
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ static std::unique_ptr<MaterialParser> createParser(Backend const backend,
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND)
|
||||
<< "the material was not built for any of the " << backendToString(backend)
|
||||
<< "the material was not built for any of the " << to_string(backend)
|
||||
<< " backend's supported shader languages (" << languageNames.c_str() << ")\n";
|
||||
}
|
||||
|
||||
@@ -329,11 +329,12 @@ FMaterial::FMaterial(FEngine& engine, const Builder& builder,
|
||||
|
||||
parser->hasCustomDepthShader(&mHasCustomDepthShader);
|
||||
|
||||
mPerViewLayoutIndex = ColorPassDescriptorSet::getIndex(
|
||||
mIsVariantLit,
|
||||
mReflectionMode == ReflectionMode::SCREEN_SPACE ||
|
||||
mRefractionMode == RefractionMode::SCREEN_SPACE,
|
||||
!(mVariantFilterMask & +UserVariantFilterBit::FOG));
|
||||
bool const isLit = mIsVariantLit || mHasShadowMultiplier;
|
||||
bool const isSSR = mReflectionMode == ReflectionMode::SCREEN_SPACE ||
|
||||
mRefractionMode == RefractionMode::SCREEN_SPACE;
|
||||
bool const hasFog = !(mVariantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
|
||||
mPerViewLayoutIndex = ColorPassDescriptorSet::getIndex(isLit, isSSR, hasFog);
|
||||
|
||||
processBlendingMode(parser);
|
||||
processSpecializationConstants(engine, builder, parser);
|
||||
@@ -407,6 +408,7 @@ void FMaterial::terminate(FEngine& engine) {
|
||||
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
mPerViewDescriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
mPerViewDescriptorSetLayoutVsm.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
mDescriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
}
|
||||
|
||||
@@ -609,9 +611,12 @@ Program FMaterial::getProgramWithVariants(
|
||||
program.attributes(mAttributeInfo);
|
||||
}
|
||||
|
||||
program.descriptorBindings(0, mProgramDescriptorBindings[0]);
|
||||
program.descriptorBindings(1, mProgramDescriptorBindings[1]);
|
||||
program.descriptorBindings(2, mProgramDescriptorBindings[2]);
|
||||
program.descriptorBindings(+DescriptorSetBindingPoints::PER_VIEW,
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_VIEW]);
|
||||
program.descriptorBindings(+DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_RENDERABLE]);
|
||||
program.descriptorBindings(+DescriptorSetBindingPoints::PER_MATERIAL,
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_MATERIAL]);
|
||||
program.specializationConstants(mSpecializationConstants);
|
||||
|
||||
program.pushConstants(ShaderStage::VERTEX, mPushConstants[uint8_t(ShaderStage::VERTEX)]);
|
||||
@@ -1143,17 +1148,52 @@ void FMaterial::processDescriptorSets(FEngine& engine, MaterialParser const* con
|
||||
success = parser->getDescriptorBindings(&mProgramDescriptorBindings);
|
||||
assert_invariant(success);
|
||||
|
||||
std::array<backend::DescriptorSetLayout, 2> descriptorSetLayout;
|
||||
backend::DescriptorSetLayout descriptorSetLayout;
|
||||
success = parser->getDescriptorSetLayout(&descriptorSetLayout);
|
||||
assert_invariant(success);
|
||||
|
||||
// get the PER_VIEW descriptor binding info
|
||||
bool const isLit = mIsVariantLit || mHasShadowMultiplier;
|
||||
bool const isSSR = mReflectionMode == ReflectionMode::SCREEN_SPACE ||
|
||||
mRefractionMode == RefractionMode::SCREEN_SPACE;
|
||||
bool const hasFog = !(mVariantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
|
||||
auto perViewDescriptorSetLayout = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
mMaterialDomain, isLit, isSSR, hasFog, false);
|
||||
|
||||
auto perViewDescriptorSetLayoutVsm = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
mMaterialDomain, isLit, isSSR, hasFog, true);
|
||||
|
||||
// set the labels
|
||||
descriptorSetLayout.label = CString{ mName }.append("_perMat");
|
||||
perViewDescriptorSetLayout.label = CString{ mName }.append("_perView");
|
||||
perViewDescriptorSetLayoutVsm.label = CString{ mName }.append("_perViewVsm");
|
||||
|
||||
// get the PER_RENDERABLE and PER_VIEW descriptor binding info
|
||||
for (auto&& [bindingPoint, descriptorSetLayout] : {
|
||||
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||
descriptor_sets::getPerRenderableLayout() },
|
||||
std::pair{ DescriptorSetBindingPoints::PER_VIEW,
|
||||
perViewDescriptorSetLayout }}) {
|
||||
Program::DescriptorBindingsInfo& descriptors = mProgramDescriptorBindings[+bindingPoint];
|
||||
descriptors.reserve(descriptorSetLayout.bindings.size());
|
||||
for (auto const& entry: descriptorSetLayout.bindings) {
|
||||
auto const& name = descriptor_sets::getDescriptorName(bindingPoint, entry.binding);
|
||||
descriptors.push_back({ name, entry.type, entry.binding });
|
||||
}
|
||||
}
|
||||
|
||||
mDescriptorSetLayout = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(descriptorSetLayout[0]) };
|
||||
engine.getDriverApi(), std::move(descriptorSetLayout) };
|
||||
|
||||
mPerViewDescriptorSetLayout = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(descriptorSetLayout[1]) };
|
||||
engine.getDriverApi(), std::move(perViewDescriptorSetLayout) };
|
||||
|
||||
mPerViewDescriptorSetLayoutVsm = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(perViewDescriptorSetLayoutVsm) };
|
||||
}
|
||||
|
||||
descriptor_binding_t FMaterial::getSamplerBinding(
|
||||
|
||||
@@ -93,7 +93,8 @@ public:
|
||||
return mPerViewDescriptorSetLayout;
|
||||
}
|
||||
|
||||
DescriptorSetLayout const& getPerViewDescriptorSetLayout(Variant const variant) const noexcept {
|
||||
DescriptorSetLayout const& getPerViewDescriptorSetLayout(
|
||||
Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept {
|
||||
if (Variant::isValidDepthVariant(variant)) {
|
||||
assert_invariant(mMaterialDomain == MaterialDomain::SURFACE);
|
||||
return mEngine.getPerViewDescriptorSetLayoutDepthVariant();
|
||||
@@ -102,6 +103,10 @@ public:
|
||||
assert_invariant(mMaterialDomain == MaterialDomain::SURFACE);
|
||||
return mEngine.getPerViewDescriptorSetLayoutSsrVariant();
|
||||
}
|
||||
if (useVsmDescriptorSetLayout) {
|
||||
assert_invariant(mMaterialDomain == MaterialDomain::SURFACE);
|
||||
return mPerViewDescriptorSetLayoutVsm;
|
||||
}
|
||||
return mPerViewDescriptorSetLayout;
|
||||
}
|
||||
|
||||
@@ -312,6 +317,7 @@ private:
|
||||
// try to order by frequency of use
|
||||
mutable std::array<backend::Handle<backend::HwProgram>, VARIANT_COUNT> mCachedPrograms;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayout;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayoutVsm;
|
||||
DescriptorSetLayout mDescriptorSetLayout;
|
||||
backend::Program::DescriptorSetInfo mProgramDescriptorBindings;
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
using namespace filament::math;
|
||||
using namespace utils;
|
||||
|
||||
@@ -74,15 +76,17 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
if (!material->getUniformInterfaceBlock().isEmpty()) {
|
||||
mUniforms = UniformBuffer(material->getUniformInterfaceBlock().getSize());
|
||||
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||
driver.setDebugTag(mUbHandle.getId(), material->getName());
|
||||
}
|
||||
// even if the material doesn't have any parameters, we allocate a small UBO because it's
|
||||
// expected by the per-material descriptor-set layout
|
||||
size_t const uboSize = std::max(size_t(16), material->getUniformInterfaceBlock().getSize());
|
||||
mUniforms = UniformBuffer(uboSize);
|
||||
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||
driver.setDebugTag(mUbHandle.getId(), material->getName());
|
||||
|
||||
// set the UBO, always descriptor 0
|
||||
mDescriptorSet.setBuffer(0, mUbHandle, 0, mUniforms.getSize());
|
||||
mDescriptorSet.setBuffer(material->getDescriptorSetLayout(),
|
||||
0, mUbHandle, 0, mUniforms.getSize());
|
||||
|
||||
const RasterState& rasterState = material->getRasterState();
|
||||
// At the moment, only MaterialInstances have a stencil state, but in the future it should be
|
||||
@@ -142,15 +146,15 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
FMaterial const* const material = other->getMaterial();
|
||||
|
||||
if (!material->getUniformInterfaceBlock().isEmpty()) {
|
||||
mUniforms.setUniforms(other->getUniformBuffer());
|
||||
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
|
||||
driver.setDebugTag(mUbHandle.getId(), material->getName());
|
||||
}
|
||||
size_t const uboSize = std::max(size_t(16), material->getUniformInterfaceBlock().getSize());
|
||||
mUniforms = UniformBuffer(uboSize);
|
||||
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
|
||||
driver.setDebugTag(mUbHandle.getId(), material->getName());
|
||||
|
||||
// set the UBO, always descriptor 0
|
||||
mDescriptorSet.setBuffer(0, mUbHandle, 0, mUniforms.getSize());
|
||||
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(),
|
||||
0, mUbHandle, 0, mUniforms.getSize());
|
||||
|
||||
if (material->hasDoubleSidedCapability()) {
|
||||
setDoubleSided(mIsDoubleSided);
|
||||
@@ -223,7 +227,8 @@ void FMaterialInstance::commit(DriverApi& driver) const {
|
||||
<< "Invalid texture still bound to MaterialInstance: '" << getName() << "'\n";
|
||||
Handle<HwTexture> const handle = p.texture->getHwHandleForSampling();
|
||||
assert_invariant(handle);
|
||||
mDescriptorSet.setSampler(binding, handle, p.params);
|
||||
mDescriptorSet.setSampler(mMaterial->getDescriptorSetLayout(),
|
||||
binding, handle, p.params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +244,9 @@ void FMaterialInstance::commit(DriverApi& driver) const {
|
||||
void FMaterialInstance::setParameter(std::string_view const name,
|
||||
Handle<HwTexture> texture, SamplerParams const params) {
|
||||
auto const binding = mMaterial->getSamplerBinding(name);
|
||||
mDescriptorSet.setSampler(binding, texture, params);
|
||||
|
||||
mDescriptorSet.setSampler(mMaterial->getDescriptorSetLayout(),
|
||||
binding, texture, params);
|
||||
}
|
||||
|
||||
void FMaterialInstance::setParameterImpl(std::string_view const name,
|
||||
@@ -266,6 +273,21 @@ void FMaterialInstance::setParameterImpl(std::string_view const name,
|
||||
#endif
|
||||
|
||||
auto const binding = mMaterial->getSamplerBinding(name);
|
||||
|
||||
if (texture) {
|
||||
auto const& descriptorSetLayout = mMaterial->getDescriptorSetLayout();
|
||||
DescriptorType const descriptorType = descriptorSetLayout.getDescriptorType(binding);
|
||||
TextureType const textureType = texture->getTextureType();
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
DescriptorSet::isTextureCompatibleWithDescriptor(textureType, descriptorType))
|
||||
<< "Texture format " << int(texture->getFormat())
|
||||
<< " of type " << to_string(textureType)
|
||||
<< " is not compatible with material \"" << getMaterial()->getName().c_str() << "\""
|
||||
<< " parameter \"" << name << "\""
|
||||
<< " of type " << to_string(descriptorType);
|
||||
}
|
||||
|
||||
if (texture && texture->textureHandleCanMutate()) {
|
||||
mTextureParameters[binding] = { texture, sampler.getSamplerParams() };
|
||||
} else {
|
||||
@@ -278,7 +300,8 @@ void FMaterialInstance::setParameterImpl(std::string_view const name,
|
||||
handle = texture->getHwHandleForSampling();
|
||||
assert_invariant(handle == texture->getHwHandle());
|
||||
}
|
||||
mDescriptorSet.setSampler(binding, handle, sampler.getSamplerParams());
|
||||
mDescriptorSet.setSampler(mMaterial->getDescriptorSetLayout(),
|
||||
binding, handle, sampler.getSamplerParams());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,29 +418,30 @@ void FMaterialInstance::fixMissingSamplers() const {
|
||||
auto const pos = std::find_if(list.begin(), list.end(), [binding](const auto& item) {
|
||||
return item.binding == binding;
|
||||
});
|
||||
|
||||
FEngine const& engine = mMaterial->getEngine();
|
||||
|
||||
// just safety-check, should never fail
|
||||
if (UTILS_LIKELY(pos != list.end())) {
|
||||
switch (pos->type) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
mDescriptorSet.setSampler(binding,
|
||||
engine.getZeroTexture(), {});
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
mDescriptorSet.setSampler(binding,
|
||||
engine.getZeroTextureArray(), {});
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
mDescriptorSet.setSampler(binding,
|
||||
engine.getDummyCubemap()->getHwHandle(), {});
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
case SamplerType::SAMPLER_3D:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
// we're currently not able to fix-up those
|
||||
break;
|
||||
FEngine const& engine = mMaterial->getEngine();
|
||||
filament::DescriptorSetLayout const& layout = mMaterial->getDescriptorSetLayout();
|
||||
|
||||
if (pos->format != SamplerFormat::FLOAT) {
|
||||
// TODO: we only handle missing samplers that are FLOAT
|
||||
switch (pos->type) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
mDescriptorSet.setSampler(layout,
|
||||
binding, engine.getZeroTexture(), {});
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
mDescriptorSet.setSampler(layout,
|
||||
binding, engine.getZeroTextureArray(), {});
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
mDescriptorSet.setSampler(layout,
|
||||
binding, engine.getDummyCubemap()->getHwHandle(), {});
|
||||
break;
|
||||
default:
|
||||
// we're currently not able to fix-up other sampler types
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -114,7 +114,8 @@ FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder)
|
||||
: mVertexCount(builder->mVertexCount),
|
||||
mCount(builder->mCount) {
|
||||
|
||||
if (UTILS_UNLIKELY(engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0)) {
|
||||
if (UTILS_UNLIKELY(engine.getSupportedFeatureLevel() <= FeatureLevel::FEATURE_LEVEL_0)) {
|
||||
// feature level 0 doesn't support morph target buffers
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -977,7 +977,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
// this descriptor-set is also used for ssr/picking/structure and these could be stale
|
||||
// it would be better to use a separate desriptor-set for those two cases so that we don't
|
||||
// have to do this
|
||||
view.unbindSamplers(driver);
|
||||
view.unbindSamplers(engine);
|
||||
view.commitUniformsAndSamplers(driver);
|
||||
});
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ using namespace math;
|
||||
|
||||
// this is a hack to be able to create a std::function<> with a non-copyable closure
|
||||
template<class F>
|
||||
auto make_copyable_function(F&& f) {
|
||||
static auto make_copyable_function(F&& f) {
|
||||
using dF = std::decay_t<F>;
|
||||
auto spf = std::make_shared<dF>(std::forward<F>(f));
|
||||
return [spf](auto&& ... args) -> decltype(auto) {
|
||||
@@ -274,7 +274,9 @@ Texture* Texture::Builder::build(Engine& engine) {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
FTexture::FTexture(FEngine& engine, const Builder& builder) {
|
||||
FTexture::FTexture(FEngine& engine, const Builder& builder)
|
||||
: mHasBlitSrc(false),
|
||||
mExternal(false) {
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
mDriver = &driver; // this is unfortunately needed for getHwHandleForSampling()
|
||||
mWidth = static_cast<uint32_t>(builder->mWidth);
|
||||
@@ -288,6 +290,7 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) {
|
||||
mTextureIsSwizzled = builder->mTextureIsSwizzled;
|
||||
mHasBlitSrc = builder->mHasBlitSrc;
|
||||
mExternal = builder->mExternal;
|
||||
mTextureType = backend::getTextureType(mFormat);
|
||||
|
||||
bool const isImported = builder->mImportedId != 0;
|
||||
if (mExternal && !isImported) {
|
||||
@@ -716,6 +719,10 @@ size_t FTexture::getFormatSize(InternalFormat const format) noexcept {
|
||||
return backend::getFormatSize(format);
|
||||
}
|
||||
|
||||
TextureType FTexture::getTextureType() const noexcept {
|
||||
return mTextureType;
|
||||
}
|
||||
|
||||
|
||||
void FTexture::generatePrefilterMipmap(FEngine& engine,
|
||||
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -105,18 +108,20 @@ public:
|
||||
// Size a of a pixel in bytes for the given format
|
||||
static size_t getFormatSize(InternalFormat format) noexcept;
|
||||
|
||||
backend::TextureType getTextureType() const noexcept;
|
||||
|
||||
// Returns the with or height for a given mipmap level from the base value.
|
||||
static inline size_t valueForLevel(uint8_t const level, size_t const baseLevelValue) {
|
||||
static size_t valueForLevel(uint8_t const level, size_t const baseLevelValue) {
|
||||
return std::max(size_t(1), baseLevelValue >> level);
|
||||
}
|
||||
|
||||
// Returns the max number of levels for a texture of given max dimensions
|
||||
static inline uint8_t maxLevelCount(uint32_t const maxDimension) noexcept {
|
||||
static uint8_t maxLevelCount(uint32_t const maxDimension) noexcept {
|
||||
return std::max(1, std::ilogbf(float(maxDimension)) + 1);
|
||||
}
|
||||
|
||||
// Returns the max number of levels for a texture of given dimensions
|
||||
static inline uint8_t maxLevelCount(uint32_t const width, uint32_t const height) noexcept {
|
||||
static uint8_t maxLevelCount(uint32_t const width, uint32_t const height) noexcept {
|
||||
uint32_t const maxDimension = std::max(width, height);
|
||||
return maxLevelCount(maxDimension);
|
||||
}
|
||||
@@ -132,7 +137,7 @@ public:
|
||||
void updateLodRange(uint8_t level) noexcept;
|
||||
|
||||
// TODO: remove in a future filament release. See below for description.
|
||||
inline bool hasBlitSrcUsage() const noexcept {
|
||||
bool hasBlitSrcUsage() const noexcept {
|
||||
return mHasBlitSrc;
|
||||
}
|
||||
|
||||
@@ -166,23 +171,25 @@ private:
|
||||
uint32_t mWidth = 1;
|
||||
uint32_t mHeight = 1;
|
||||
uint32_t mDepth = 1;
|
||||
|
||||
InternalFormat mFormat = InternalFormat::RGBA8;
|
||||
Sampler mTarget = Sampler::SAMPLER_2D;
|
||||
uint8_t mLevelCount = 1;
|
||||
uint8_t mSampleCount = 1;
|
||||
|
||||
std::array<Swizzle, 4> mSwizzle = {
|
||||
Swizzle::CHANNEL_0, Swizzle::CHANNEL_1,
|
||||
Swizzle::CHANNEL_2, Swizzle::CHANNEL_3 };
|
||||
bool mTextureIsSwizzled;
|
||||
|
||||
Usage mUsage = Usage::DEFAULT;
|
||||
backend::TextureType mTextureType;
|
||||
|
||||
// TODO: remove in a future filament release.
|
||||
// Indicates whether the user has set the TextureUsage::BLIT_SRC usage. This will be used to
|
||||
// temporarily validate whether this texture can be used for readPixels.
|
||||
bool mHasBlitSrc = false;
|
||||
bool mExternal = false;
|
||||
// there is 4 bytes of padding here
|
||||
bool mHasBlitSrc : 1;
|
||||
bool mExternal : 1;
|
||||
bool mTextureIsSwizzled : 1;
|
||||
|
||||
FStream* mStream = nullptr; // only needed for streaming textures
|
||||
};
|
||||
|
||||
+105
-44
@@ -68,10 +68,36 @@ FView::FView(FEngine& engine)
|
||||
mFogEntity(engine.getEntityManager().create()),
|
||||
mIsStereoSupported(engine.getDriverApi().isStereoSupported()),
|
||||
mUniforms(engine.getDriverApi()),
|
||||
mColorPassDescriptorSet(engine, mUniforms) {
|
||||
|
||||
mColorPassDescriptorSet{
|
||||
{ engine, false, mUniforms },
|
||||
{ engine, true, mUniforms } }
|
||||
{
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
auto const& layout = engine.getPerRenderableDescriptorSetLayout();
|
||||
|
||||
// initialize the common descriptor set with dummy descriptors
|
||||
mCommonRenderableDescriptorSet.setBuffer(layout,
|
||||
+PerRenderableBindingPoints::BONES_UNIFORMS,
|
||||
engine.getDummyUniformBuffer(), 0, sizeof(PerRenderableBoneUib));
|
||||
|
||||
mCommonRenderableDescriptorSet.setBuffer(layout,
|
||||
+PerRenderableBindingPoints::MORPHING_UNIFORMS,
|
||||
engine.getDummyUniformBuffer(), 0, sizeof(PerRenderableMorphingUib));
|
||||
|
||||
mCommonRenderableDescriptorSet.setSampler(layout,
|
||||
+PerRenderableBindingPoints::MORPH_TARGET_POSITIONS,
|
||||
engine.getDummyMorphTargetBuffer()->getPositionsHandle(), {});
|
||||
|
||||
mCommonRenderableDescriptorSet.setSampler(layout,
|
||||
+PerRenderableBindingPoints::MORPH_TARGET_TANGENTS,
|
||||
engine.getDummyMorphTargetBuffer()->getTangentsHandle(), {});
|
||||
|
||||
mCommonRenderableDescriptorSet.setSampler(layout,
|
||||
+PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS,
|
||||
engine.getZeroTextureArray(), {});
|
||||
|
||||
|
||||
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
|
||||
|
||||
debugRegistry.registerProperty("d.view.camera_at_origin",
|
||||
@@ -125,11 +151,13 @@ FView::FView(FEngine& engine)
|
||||
|
||||
mDefaultColorGrading = mColorGrading = engine.getDefaultColorGrading();
|
||||
|
||||
mColorPassDescriptorSet.init(
|
||||
engine,
|
||||
mLightUbh,
|
||||
mFroxelizer.getRecordBuffer(),
|
||||
mFroxelizer.getFroxelBuffer());
|
||||
for (auto&& colorPassDescriptorSet : mColorPassDescriptorSet) {
|
||||
colorPassDescriptorSet.init(
|
||||
engine,
|
||||
mLightUbh,
|
||||
mFroxelizer.getRecordBuffer(),
|
||||
mFroxelizer.getFroxelBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
FView::~FView() noexcept = default;
|
||||
@@ -146,7 +174,9 @@ void FView::terminate(FEngine& engine) {
|
||||
|
||||
ShadowMapManager::terminate(engine, mShadowMapManager);
|
||||
mUniforms.terminate(driver);
|
||||
mColorPassDescriptorSet.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
for (auto&& colorPassDescriptorSet : mColorPassDescriptorSet) {
|
||||
colorPassDescriptorSet.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
}
|
||||
mFroxelizer.terminate(driver);
|
||||
mCommonRenderableDescriptorSet.terminate(driver);
|
||||
|
||||
@@ -429,7 +459,7 @@ void FView::prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexc
|
||||
*/
|
||||
|
||||
const float exposure = Exposure::exposure(cameraInfo.ev100);
|
||||
mColorPassDescriptorSet.prepareExposure(cameraInfo.ev100);
|
||||
getColorPassDescriptorSet().prepareExposure(cameraInfo.ev100);
|
||||
|
||||
/*
|
||||
* Indirect light (IBL)
|
||||
@@ -446,7 +476,7 @@ void FView::prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexc
|
||||
FSkybox const* const skybox = scene->getSkybox();
|
||||
intensity = skybox ? skybox->getIntensity() : FIndirectLight::DEFAULT_INTENSITY;
|
||||
}
|
||||
mColorPassDescriptorSet.prepareAmbientLight(engine, *ibl, intensity, exposure);
|
||||
getColorPassDescriptorSet().prepareAmbientLight(engine, *ibl, intensity, exposure);
|
||||
|
||||
/*
|
||||
* Directional light (always at index 0)
|
||||
@@ -454,7 +484,7 @@ void FView::prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexc
|
||||
|
||||
FLightManager::Instance const directionalLight = lightData.elementAt<FScene::LIGHT_INSTANCE>(0);
|
||||
const float3 sceneSpaceDirection = lightData.elementAt<FScene::DIRECTION>(0); // guaranteed normalized
|
||||
mColorPassDescriptorSet.prepareDirectionalLight(engine, exposure, sceneSpaceDirection, directionalLight);
|
||||
getColorPassDescriptorSet().prepareDirectionalLight(engine, exposure, sceneSpaceDirection, directionalLight);
|
||||
}
|
||||
|
||||
CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
|
||||
@@ -606,7 +636,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
|
||||
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) {
|
||||
// TODO: might be more consistent to do this in prepareLighting(), but it's not
|
||||
// strictly necessary
|
||||
mColorPassDescriptorSet.prepareDynamicLights(mFroxelizer);
|
||||
getColorPassDescriptorSet().prepareDynamicLights(mFroxelizer);
|
||||
}
|
||||
// We need to pass viewMatrix by value here because it extends the scope of this
|
||||
// function.
|
||||
@@ -711,6 +741,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
|
||||
scene->updateUBOs(merged, mRenderableUbh);
|
||||
|
||||
mCommonRenderableDescriptorSet.setBuffer(
|
||||
engine.getPerRenderableDescriptorSetLayout(),
|
||||
+PerRenderableBindingPoints::OBJECT_UNIFORMS, mRenderableUbh,
|
||||
0, sizeof(PerRenderableUib));
|
||||
|
||||
@@ -734,37 +765,65 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
|
||||
FRenderableManager& rcm = engine.getRenderableManager();
|
||||
auto& descriptorSet = rcm.getDescriptorSet(ci);
|
||||
|
||||
auto const& layout = engine.getPerRenderableDescriptorSetLayout();
|
||||
|
||||
// initialize the descriptor set the first time it's needed
|
||||
if (UTILS_UNLIKELY(!descriptorSet.getHandle())) {
|
||||
descriptorSet = DescriptorSet{ engine.getPerRenderableDescriptorSetLayout() };
|
||||
descriptorSet = DescriptorSet{ layout };
|
||||
}
|
||||
|
||||
descriptorSet.setBuffer(+PerRenderableBindingPoints::OBJECT_UNIFORMS,
|
||||
descriptorSet.setBuffer(layout,
|
||||
+PerRenderableBindingPoints::OBJECT_UNIFORMS,
|
||||
instance.handle ? instance.handle : mRenderableUbh,
|
||||
0, sizeof(PerRenderableUib));
|
||||
|
||||
if (UTILS_UNLIKELY(skinning.handle || morphing.handle)) {
|
||||
descriptorSet.setBuffer(layout,
|
||||
+PerRenderableBindingPoints::BONES_UNIFORMS,
|
||||
engine.getDummyUniformBuffer(), 0, sizeof(PerRenderableBoneUib));
|
||||
|
||||
descriptorSet.setBuffer(+PerRenderableBindingPoints::BONES_UNIFORMS,
|
||||
descriptorSet.setBuffer(layout,
|
||||
+PerRenderableBindingPoints::MORPHING_UNIFORMS,
|
||||
engine.getDummyUniformBuffer(), 0, sizeof(PerRenderableMorphingUib));
|
||||
|
||||
descriptorSet.setSampler(layout,
|
||||
+PerRenderableBindingPoints::MORPH_TARGET_POSITIONS,
|
||||
engine.getDummyMorphTargetBuffer()->getPositionsHandle(), {});
|
||||
|
||||
descriptorSet.setSampler(layout,
|
||||
+PerRenderableBindingPoints::MORPH_TARGET_TANGENTS,
|
||||
engine.getDummyMorphTargetBuffer()->getTangentsHandle(), {});
|
||||
|
||||
descriptorSet.setSampler(layout,
|
||||
+PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS,
|
||||
engine.getZeroTextureArray(), {});
|
||||
|
||||
if (UTILS_UNLIKELY(skinning.handle || morphing.handle)) {
|
||||
descriptorSet.setBuffer(layout,
|
||||
+PerRenderableBindingPoints::BONES_UNIFORMS,
|
||||
skinning.handle, 0, sizeof(PerRenderableBoneUib));
|
||||
|
||||
descriptorSet.setSampler(+PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS,
|
||||
descriptorSet.setSampler(layout,
|
||||
+PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS,
|
||||
skinning.boneIndicesAndWeightHandle, {});
|
||||
|
||||
descriptorSet.setBuffer(+PerRenderableBindingPoints::MORPHING_UNIFORMS,
|
||||
descriptorSet.setBuffer(layout,
|
||||
+PerRenderableBindingPoints::MORPHING_UNIFORMS,
|
||||
morphing.handle, 0, sizeof(PerRenderableMorphingUib));
|
||||
|
||||
descriptorSet.setSampler(+PerRenderableBindingPoints::MORPH_TARGET_POSITIONS,
|
||||
descriptorSet.setSampler(layout,
|
||||
+PerRenderableBindingPoints::MORPH_TARGET_POSITIONS,
|
||||
morphing.morphTargetBuffer->getPositionsHandle(), {});
|
||||
|
||||
descriptorSet.setSampler(+PerRenderableBindingPoints::MORPH_TARGET_TANGENTS,
|
||||
descriptorSet.setSampler(layout,
|
||||
+PerRenderableBindingPoints::MORPH_TARGET_TANGENTS,
|
||||
morphing.morphTargetBuffer->getTangentsHandle(), {});
|
||||
}
|
||||
|
||||
descriptorSet.commit(engine.getPerRenderableDescriptorSetLayout(), driver);
|
||||
descriptorSet.commit(layout, driver);
|
||||
|
||||
// write the descriptor-set handle to the sceneData array for access later
|
||||
sceneData.elementAt<FScene::DESCRIPTOR_SET_HANDLE>(i) = descriptorSet.getHandle();
|
||||
sceneData.elementAt<FScene::DESCRIPTOR_SET_HANDLE>(i) =
|
||||
descriptorSet.getHandle();
|
||||
} else {
|
||||
// use the shared descriptor-set
|
||||
sceneData.elementAt<FScene::DESCRIPTOR_SET_HANDLE>(i) =
|
||||
@@ -788,12 +847,13 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
|
||||
auto const& tcm = engine.getTransformManager();
|
||||
auto const fogTransform = tcm.getWorldTransformAccurate(tcm.getInstance(mFogEntity));
|
||||
|
||||
mColorPassDescriptorSet.prepareTime(engine, userTime);
|
||||
mColorPassDescriptorSet.prepareFog(engine, cameraInfo, fogTransform, mFogOptions,
|
||||
auto& colorPassDescriptorSet = getColorPassDescriptorSet();
|
||||
colorPassDescriptorSet.prepareTime(engine, userTime);
|
||||
colorPassDescriptorSet.prepareFog(engine, cameraInfo, fogTransform, mFogOptions,
|
||||
scene->getIndirectLight());
|
||||
mColorPassDescriptorSet.prepareTemporalNoise(engine, mTemporalAntiAliasingOptions);
|
||||
mColorPassDescriptorSet.prepareBlending(needsAlphaChannel);
|
||||
mColorPassDescriptorSet.prepareMaterialGlobals(mMaterialGlobals);
|
||||
colorPassDescriptorSet.prepareTemporalNoise(engine, mTemporalAntiAliasingOptions);
|
||||
colorPassDescriptorSet.prepareBlending(needsAlphaChannel);
|
||||
colorPassDescriptorSet.prepareMaterialGlobals(mMaterialGlobals);
|
||||
}
|
||||
|
||||
void FView::computeVisibilityMasks(
|
||||
@@ -854,12 +914,12 @@ void FView::prepareUpscaler(float2 const scale,
|
||||
derivativesScale = 0.5f;
|
||||
}
|
||||
}
|
||||
mColorPassDescriptorSet.prepareLodBias(bias, derivativesScale);
|
||||
getColorPassDescriptorSet().prepareLodBias(bias, derivativesScale);
|
||||
}
|
||||
|
||||
void FView::prepareCamera(FEngine& engine, const CameraInfo& cameraInfo) const noexcept {
|
||||
SYSTRACE_CALL();
|
||||
mColorPassDescriptorSet.prepareCamera(engine, cameraInfo);
|
||||
getColorPassDescriptorSet().prepareCamera(engine, cameraInfo);
|
||||
}
|
||||
|
||||
void FView::prepareViewport(
|
||||
@@ -868,23 +928,23 @@ void FView::prepareViewport(
|
||||
SYSTRACE_CALL();
|
||||
// TODO: we should pass viewport.{left|bottom} to the backend, so it can offset the
|
||||
// scissor properly.
|
||||
mColorPassDescriptorSet.prepareViewport(physicalViewport, logicalViewport);
|
||||
getColorPassDescriptorSet().prepareViewport(physicalViewport, logicalViewport);
|
||||
}
|
||||
|
||||
void FView::prepareSSAO(Handle<HwTexture> ssao) const noexcept {
|
||||
mColorPassDescriptorSet.prepareSSAO(ssao, mAmbientOcclusionOptions);
|
||||
getColorPassDescriptorSet().prepareSSAO(ssao, mAmbientOcclusionOptions);
|
||||
}
|
||||
|
||||
void FView::prepareSSR(Handle<HwTexture> ssr,
|
||||
bool const disableSSR,
|
||||
float const refractionLodOffset,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept {
|
||||
mColorPassDescriptorSet.prepareSSR(ssr, disableSSR, refractionLodOffset, ssrOptions);
|
||||
getColorPassDescriptorSet().prepareSSR(ssr, disableSSR, refractionLodOffset, ssrOptions);
|
||||
}
|
||||
|
||||
void FView::prepareStructure(Handle<HwTexture> structure) const noexcept {
|
||||
// sampler must be NEAREST
|
||||
mColorPassDescriptorSet.prepareStructure(structure);
|
||||
getColorPassDescriptorSet().prepareStructure(structure);
|
||||
}
|
||||
|
||||
void FView::prepareShadow(Handle<HwTexture> texture) const noexcept {
|
||||
@@ -896,37 +956,38 @@ void FView::prepareShadow(Handle<HwTexture> texture) const noexcept {
|
||||
}
|
||||
switch (mShadowType) {
|
||||
case ShadowType::PCF:
|
||||
mColorPassDescriptorSet.prepareShadowPCF(texture, uniforms);
|
||||
getColorPassDescriptorSet().prepareShadowPCF(texture, uniforms);
|
||||
break;
|
||||
case ShadowType::VSM:
|
||||
mColorPassDescriptorSet.prepareShadowVSM(texture, uniforms, mVsmShadowOptions);
|
||||
getColorPassDescriptorSet().prepareShadowVSM(texture, uniforms, mVsmShadowOptions);
|
||||
break;
|
||||
case ShadowType::DPCF:
|
||||
mColorPassDescriptorSet.prepareShadowDPCF(texture, uniforms, mSoftShadowOptions);
|
||||
getColorPassDescriptorSet().prepareShadowDPCF(texture, uniforms, mSoftShadowOptions);
|
||||
break;
|
||||
case ShadowType::PCSS:
|
||||
mColorPassDescriptorSet.prepareShadowPCSS(texture, uniforms, mSoftShadowOptions);
|
||||
getColorPassDescriptorSet().prepareShadowPCSS(texture, uniforms, mSoftShadowOptions);
|
||||
break;
|
||||
case ShadowType::PCFd:
|
||||
mColorPassDescriptorSet.prepareShadowPCFDebug(texture, uniforms);
|
||||
getColorPassDescriptorSet().prepareShadowPCFDebug(texture, uniforms);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FView::prepareShadowMapping(bool const highPrecision) const noexcept {
|
||||
void FView::prepareShadowMapping(FEngine const& engine, bool const highPrecision) const noexcept {
|
||||
BufferObjectHandle ubo{ engine.getDummyUniformBuffer() };
|
||||
if (mHasShadowing) {
|
||||
assert_invariant(mShadowMapManager);
|
||||
mColorPassDescriptorSet.prepareShadowMapping(
|
||||
mShadowMapManager->getShadowUniformsHandle(), highPrecision);
|
||||
ubo = mShadowMapManager->getShadowUniformsHandle();
|
||||
}
|
||||
getColorPassDescriptorSet().prepareShadowMapping(ubo, highPrecision);
|
||||
}
|
||||
|
||||
void FView::commitUniformsAndSamplers(DriverApi& driver) const noexcept {
|
||||
mColorPassDescriptorSet.commit(driver);
|
||||
getColorPassDescriptorSet().commit(driver);
|
||||
}
|
||||
|
||||
void FView::unbindSamplers(DriverApi& driver) noexcept {
|
||||
mColorPassDescriptorSet.unbindSamplers(driver);
|
||||
void FView::unbindSamplers(FEngine& engine) noexcept {
|
||||
getColorPassDescriptorSet().unbindSamplers(engine);
|
||||
}
|
||||
|
||||
void FView::commitFroxels(DriverApi& driverApi) const noexcept {
|
||||
|
||||
@@ -178,11 +178,11 @@ public:
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept;
|
||||
void prepareStructure(backend::Handle<backend::HwTexture> structure) const noexcept;
|
||||
void prepareShadow(backend::Handle<backend::HwTexture> structure) const noexcept;
|
||||
void prepareShadowMapping(bool highPrecision) const noexcept;
|
||||
void prepareShadowMapping(FEngine const& engine, bool highPrecision) const noexcept;
|
||||
|
||||
void commitFroxels(backend::DriverApi& driverApi) const noexcept;
|
||||
void commitUniformsAndSamplers(backend::DriverApi& driver) const noexcept;
|
||||
void unbindSamplers(backend::DriverApi& driver) noexcept;
|
||||
void unbindSamplers(FEngine& engine) noexcept;
|
||||
|
||||
utils::JobSystem::Job* getFroxelizerSync() const noexcept { return mFroxelizerSync; }
|
||||
void setFroxelizerSync(utils::JobSystem::Job* sync) noexcept { mFroxelizerSync = sync; }
|
||||
@@ -440,7 +440,9 @@ public:
|
||||
static void cullRenderables(utils::JobSystem& js, FScene::RenderableSoa& renderableData,
|
||||
Frustum const& frustum, size_t bit) noexcept;
|
||||
|
||||
ColorPassDescriptorSet& getColorPassDescriptorSet() noexcept { return mColorPassDescriptorSet; }
|
||||
ColorPassDescriptorSet& getColorPassDescriptorSet() const noexcept {
|
||||
return mColorPassDescriptorSet[mShadowType == ShadowType::PCF ? 0 : 1];
|
||||
}
|
||||
|
||||
// Returns the frame history FIFO. This is typically used by the FrameGraph to access
|
||||
// previous frame data.
|
||||
@@ -587,7 +589,7 @@ private:
|
||||
RenderQuality mRenderQuality;
|
||||
|
||||
mutable TypedUniformBuffer<PerViewUib> mUniforms;
|
||||
mutable ColorPassDescriptorSet mColorPassDescriptorSet;
|
||||
mutable ColorPassDescriptorSet mColorPassDescriptorSet[2];
|
||||
|
||||
mutable FrameHistory mFrameHistory{};
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ uint8_t ColorPassDescriptorSet::getIndex(
|
||||
index |= 0x2;
|
||||
}
|
||||
|
||||
if (!fog) {
|
||||
if (fog) {
|
||||
// this will remove samplers needed for fog
|
||||
index |= 0x4;
|
||||
}
|
||||
@@ -88,27 +88,18 @@ uint8_t ColorPassDescriptorSet::getIndex(
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
ColorPassDescriptorSet::ColorPassDescriptorSet(FEngine& engine,
|
||||
ColorPassDescriptorSet::ColorPassDescriptorSet(FEngine& engine, bool const vsm,
|
||||
TypedUniformBuffer<PerViewUib>& uniforms) noexcept
|
||||
: mUniforms(uniforms) {
|
||||
|
||||
constexpr UserVariantFilterMask filterFog = UserVariantFilterMask(UserVariantFilterBit::FOG);
|
||||
constexpr UserVariantFilterMask keepFog = UserVariantFilterMask(0);
|
||||
|
||||
: mUniforms(uniforms), mIsVsm(vsm) {
|
||||
for (bool const lit: { false, true }) {
|
||||
for (bool const ssr: { false, true }) {
|
||||
for (bool const fog: { false, true }) {
|
||||
auto index = getIndex(lit, ssr, fog);
|
||||
auto const index = getIndex(lit, ssr, fog);
|
||||
mDescriptorSetLayout[index] = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(),
|
||||
descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
MaterialDomain::SURFACE,
|
||||
fog ? keepFog : filterFog,
|
||||
lit,
|
||||
ssr ? ReflectionMode::SCREEN_SPACE : ReflectionMode::DEFAULT,
|
||||
ssr ? RefractionMode::SCREEN_SPACE : RefractionMode::NONE)
|
||||
MaterialDomain::SURFACE, lit, ssr, fog, vsm)
|
||||
};
|
||||
mDescriptorSet[index] = DescriptorSet{ mDescriptorSetLayout[index] };
|
||||
}
|
||||
@@ -118,11 +109,11 @@ ColorPassDescriptorSet::ColorPassDescriptorSet(FEngine& engine,
|
||||
setBuffer(+PerViewBindingPoints::FRAME_UNIFORMS,
|
||||
uniforms.getUboHandle(), 0, uniforms.getSize());
|
||||
|
||||
if (engine.getDFG().isValid()) {
|
||||
TextureSampler const sampler(TextureSampler::MagFilter::LINEAR);
|
||||
setSampler(+PerViewBindingPoints::IBL_DFG_LUT,
|
||||
engine.getDFG().getTexture(), sampler.getSamplerParams());
|
||||
}
|
||||
setSampler(+PerViewBindingPoints::IBL_DFG_LUT,
|
||||
engine.getDFG().isValid() ?
|
||||
engine.getDFG().getTexture() : engine.getZeroTexture(), {
|
||||
.filterMag = SamplerMagFilter::LINEAR
|
||||
});
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::init(
|
||||
@@ -130,12 +121,14 @@ void ColorPassDescriptorSet::init(
|
||||
BufferObjectHandle lights,
|
||||
BufferObjectHandle recordBuffer,
|
||||
BufferObjectHandle froxelBuffer) noexcept {
|
||||
for (auto&& descriptorSet: mDescriptorSet) {
|
||||
descriptorSet.setBuffer(+PerViewBindingPoints::LIGHTS,
|
||||
for (size_t i = 0; i < DESCRIPTOR_LAYOUT_COUNT; i++) {
|
||||
auto& descriptorSet = mDescriptorSet[i];
|
||||
auto const& layout = mDescriptorSetLayout[i];
|
||||
descriptorSet.setBuffer(layout, +PerViewBindingPoints::LIGHTS,
|
||||
lights, 0, CONFIG_MAX_LIGHT_COUNT * sizeof(LightsUib));
|
||||
descriptorSet.setBuffer(+PerViewBindingPoints::RECORD_BUFFER,
|
||||
descriptorSet.setBuffer(layout, +PerViewBindingPoints::RECORD_BUFFER,
|
||||
recordBuffer, 0, sizeof(FroxelRecordUib));
|
||||
descriptorSet.setBuffer(+PerViewBindingPoints::FROXEL_BUFFER,
|
||||
descriptorSet.setBuffer(layout, +PerViewBindingPoints::FROXEL_BUFFER,
|
||||
froxelBuffer, 0, Froxelizer::getFroxelBufferByteCount(engine.getDriverApi()));
|
||||
}
|
||||
}
|
||||
@@ -305,6 +298,7 @@ void ColorPassDescriptorSet::prepareFog(FEngine& engine, const CameraInfo& camer
|
||||
|
||||
void ColorPassDescriptorSet::prepareSSAO(Handle<HwTexture> ssao,
|
||||
AmbientOcclusionOptions const& options) noexcept {
|
||||
|
||||
// High quality sampling is enabled only if AO itself is enabled and upsampling quality is at
|
||||
// least set to high and of course only if upsampling is needed.
|
||||
const bool highQualitySampling = options.upsampling >= QualityLevel::HIGH
|
||||
@@ -537,20 +531,28 @@ void ColorPassDescriptorSet::commit(DriverApi& driver) noexcept {
|
||||
driver.updateBufferObject(mUniforms.getUboHandle(),
|
||||
mUniforms.toBufferDescriptor(driver), 0);
|
||||
}
|
||||
|
||||
|
||||
for (size_t i = 0; i < DESCRIPTOR_LAYOUT_COUNT; i++) {
|
||||
mDescriptorSet[i].commit(mDescriptorSetLayout[i], driver);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::unbindSamplers(DriverApi&) noexcept {
|
||||
void ColorPassDescriptorSet::unbindSamplers(FEngine& engine) noexcept {
|
||||
// this needs to reset the sampler that are only set in RendererUtils::colorPass(), because
|
||||
// this descriptor-set is also used for ssr/picking/structure and these could be stale
|
||||
// it would be better to use a separate descriptor-set for those two cases so that we don't
|
||||
// have to do this
|
||||
setSampler(+PerViewBindingPoints::STRUCTURE, {}, {});
|
||||
setSampler(+PerViewBindingPoints::SHADOW_MAP, {}, {});
|
||||
setSampler(+PerViewBindingPoints::SSAO, {}, {});
|
||||
setSampler(+PerViewBindingPoints::SSR, {}, {});
|
||||
setBuffer(+PerViewBindingPoints::SHADOWS,
|
||||
engine.getDummyUniformBuffer(), 0, sizeof(ShadowUib));
|
||||
setSampler(+PerViewBindingPoints::STRUCTURE,
|
||||
engine.getZeroTexture(), {});
|
||||
setSampler(+PerViewBindingPoints::SHADOW_MAP,
|
||||
engine.getOneTextureArrayDepth(), {});
|
||||
setSampler(+PerViewBindingPoints::SSAO,
|
||||
engine.getZeroTextureArray(), {});
|
||||
setSampler(+PerViewBindingPoints::SSR,
|
||||
engine.getZeroTextureArray(), {});
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::setSampler(descriptor_binding_t const binding,
|
||||
@@ -558,7 +560,7 @@ void ColorPassDescriptorSet::setSampler(descriptor_binding_t const binding,
|
||||
for (size_t i = 0; i < DESCRIPTOR_LAYOUT_COUNT; i++) {
|
||||
auto samplers = mDescriptorSetLayout[i].getSamplerDescriptors();
|
||||
if (samplers[binding]) {
|
||||
mDescriptorSet[i].setSampler(binding, th, params);
|
||||
mDescriptorSet[i].setSampler(mDescriptorSetLayout[i], binding, th, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -566,9 +568,10 @@ void ColorPassDescriptorSet::setSampler(descriptor_binding_t const binding,
|
||||
void ColorPassDescriptorSet::setBuffer(descriptor_binding_t const binding,
|
||||
BufferObjectHandle boh, uint32_t const offset, uint32_t const size) noexcept {
|
||||
for (size_t i = 0; i < DESCRIPTOR_LAYOUT_COUNT; i++) {
|
||||
auto ubos = mDescriptorSetLayout[i].getUniformBufferDescriptors();
|
||||
auto const& layout = mDescriptorSetLayout[i];
|
||||
auto ubos = layout.getUniformBufferDescriptors();
|
||||
if (ubos[binding]) {
|
||||
mDescriptorSet[i].setBuffer(binding, boh, offset, size);
|
||||
mDescriptorSet[i].setBuffer(layout, binding, boh, offset, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,16 +66,16 @@ class ColorPassDescriptorSet {
|
||||
using LightManagerInstance = utils::EntityInstance<LightManager>;
|
||||
using TextureHandle = backend::Handle<backend::HwTexture>;
|
||||
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCF = 0u;
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_EVSM = 1u;
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_DPCF = 2u;
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCSS = 3u;
|
||||
static constexpr uint32_t SHADOW_SAMPLING_RUNTIME_PCF = 0u;
|
||||
static constexpr uint32_t SHADOW_SAMPLING_RUNTIME_EVSM = 1u;
|
||||
static constexpr uint32_t SHADOW_SAMPLING_RUNTIME_DPCF = 2u;
|
||||
static constexpr uint32_t SHADOW_SAMPLING_RUNTIME_PCSS = 3u;
|
||||
|
||||
public:
|
||||
|
||||
static uint8_t getIndex(bool lit, bool ssr, bool fog) noexcept;
|
||||
|
||||
ColorPassDescriptorSet(FEngine& engine,
|
||||
ColorPassDescriptorSet(FEngine& engine, bool vsm,
|
||||
TypedUniformBuffer<PerViewUib>& uniforms) noexcept;
|
||||
|
||||
void init(
|
||||
@@ -153,13 +153,15 @@ public:
|
||||
// update local data into GPU UBO
|
||||
void commit(backend::DriverApi& driver) noexcept;
|
||||
|
||||
void unbindSamplers(backend::DriverApi& driver) noexcept;
|
||||
void unbindSamplers(FEngine& engine) noexcept;
|
||||
|
||||
// bind this UBO
|
||||
void bind(backend::DriverApi& driver, uint8_t const index) const noexcept {
|
||||
mDescriptorSet[index].bind(driver, DescriptorSetBindingPoints::PER_VIEW);
|
||||
}
|
||||
|
||||
bool isVSM() const noexcept { return mIsVsm; }
|
||||
|
||||
private:
|
||||
static constexpr size_t DESCRIPTOR_LAYOUT_COUNT = 8;
|
||||
|
||||
@@ -169,11 +171,13 @@ private:
|
||||
void setBuffer(backend::descriptor_binding_t binding,
|
||||
backend::BufferObjectHandle boh, uint32_t offset, uint32_t size) noexcept;
|
||||
|
||||
static void prepareShadowSampling(PerViewUib& uniforms,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;
|
||||
|
||||
TypedUniformBuffer<PerViewUib>& mUniforms;
|
||||
std::array<DescriptorSetLayout, DESCRIPTOR_LAYOUT_COUNT> mDescriptorSetLayout;
|
||||
std::array<DescriptorSet, DESCRIPTOR_LAYOUT_COUNT> mDescriptorSet;
|
||||
static void prepareShadowSampling(PerViewUib& uniforms,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;
|
||||
bool const mIsVsm;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <utility>
|
||||
#include <limits>
|
||||
@@ -86,6 +87,7 @@ void DescriptorSet::commitSlow(DescriptorSetLayout const& layout,
|
||||
mValid.forEachSetBit([&layout, &driver,
|
||||
dsh = mDescriptorSetHandle, descriptors = mDescriptors.data()]
|
||||
(backend::descriptor_binding_t const binding) {
|
||||
assert_invariant(layout.isValid(binding));
|
||||
if (layout.isSampler(binding)) {
|
||||
driver.updateDescriptorSetTexture(dsh, binding,
|
||||
descriptors[binding].texture.th,
|
||||
@@ -113,7 +115,7 @@ void DescriptorSet::bind(FEngine::DriverApi& driver, DescriptorSetBindingPoints
|
||||
// within the renderpass. We have to comment the assert out since it crashed a client's debug
|
||||
// build.
|
||||
// assert_invariant(mDirty.none());
|
||||
if (mDirty.any() && !mSetAfterCommitWarning) {
|
||||
if (UTILS_VERY_UNLIKELY(mDirty.any() && !mSetAfterCommitWarning)) {
|
||||
mDirty.forEachSetBit([&](uint8_t const binding) {
|
||||
utils::slog.w << "Descriptor set (handle=" << mDescriptorSetHandle.getId()
|
||||
<< ") binding=" << (int) binding
|
||||
@@ -124,35 +126,109 @@ void DescriptorSet::bind(FEngine::DriverApi& driver, DescriptorSetBindingPoints
|
||||
driver.bindDescriptorSet(mDescriptorSetHandle, +set, std::move(dynamicOffsets));
|
||||
}
|
||||
|
||||
void DescriptorSet::setBuffer(
|
||||
void DescriptorSet::setBuffer(DescriptorSetLayout const& layout,
|
||||
backend::descriptor_binding_t const binding,
|
||||
backend::Handle<backend::HwBufferObject> boh, uint32_t const offset, uint32_t const size) noexcept {
|
||||
// TODO: validate it's the right kind of descriptor
|
||||
backend::Handle<backend::HwBufferObject> boh, uint32_t const offset, uint32_t const size) {
|
||||
|
||||
// Validate it's the right kind of descriptor
|
||||
using DSLB = backend::DescriptorSetLayoutBinding;
|
||||
FILAMENT_CHECK_PRECONDITION(DSLB::isBuffer(layout.getDescriptorType(binding)))
|
||||
<< "descriptor " << +binding << "is not a buffer";
|
||||
|
||||
if (mDescriptors[binding].buffer.boh != boh || mDescriptors[binding].buffer.size != size) {
|
||||
// we don't set the dirty bit if only offset changes
|
||||
mDirty.set(binding);
|
||||
}
|
||||
mDescriptors[binding].buffer = { boh, offset, size };
|
||||
mValid.set(binding, (bool)boh);
|
||||
mValid.set(binding, bool(boh));
|
||||
}
|
||||
|
||||
void DescriptorSet::setSampler(
|
||||
DescriptorSetLayout const& layout,
|
||||
backend::descriptor_binding_t const binding,
|
||||
backend::Handle<backend::HwTexture> th, backend::SamplerParams const params) noexcept {
|
||||
// TODO: validate it's the right kind of descriptor
|
||||
backend::Handle<backend::HwTexture> th, backend::SamplerParams const params) {
|
||||
|
||||
using namespace backend;
|
||||
using DSLB = DescriptorSetLayoutBinding;
|
||||
|
||||
// Validate it's the right kind of descriptor
|
||||
auto type = layout.getDescriptorType(binding);
|
||||
FILAMENT_CHECK_PRECONDITION(DSLB::isSampler(type))
|
||||
<< "descriptor " << +binding << " is not a sampler";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
!(params.compareMode == SamplerCompareMode::COMPARE_TO_TEXTURE && !isDepthDescriptor(type)))
|
||||
<< "descriptor " << +binding
|
||||
<< " is not of type DEPTH, but sampler is in COMPARE_TO_TEXTURE mode";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
!(params.isFiltered() &&
|
||||
isDepthDescriptor(type) &&
|
||||
params.compareMode != SamplerCompareMode::COMPARE_TO_TEXTURE))
|
||||
<< "descriptor " << +binding
|
||||
<< " is of type filtered DEPTH, but sampler not in COMPARE_TO_TEXTURE mode";
|
||||
|
||||
if (mDescriptors[binding].texture.th != th || mDescriptors[binding].texture.params != params) {
|
||||
mDirty.set(binding);
|
||||
}
|
||||
mDescriptors[binding].texture = { th, params };
|
||||
mValid.set(binding, (bool)th);
|
||||
mValid.set(binding, bool(th));
|
||||
}
|
||||
|
||||
DescriptorSet DescriptorSet::duplicate(DescriptorSetLayout const& layout) const noexcept {
|
||||
DescriptorSet set{layout};
|
||||
set.mDescriptors = mDescriptors; // Use the vector's assignment operator
|
||||
set.mDirty = mDirty;
|
||||
set.mDirty = mValid | mDirty; // Dirty all valid descriptors so they're updated during commit
|
||||
set.mValid = mValid;
|
||||
return set;
|
||||
}
|
||||
bool DescriptorSet::isTextureCompatibleWithDescriptor(
|
||||
backend::TextureType t, backend::DescriptorType d) noexcept {
|
||||
using namespace backend;
|
||||
|
||||
switch (d) {
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
// DEPTH_STENCIL is treated as accessing the depth component. OpenGL 4.3
|
||||
// allows to specify which one, but not filament.
|
||||
// Depth textures can be used as an unfiltered float sampler
|
||||
return t == TextureType::FLOAT || t == TextureType::DEPTH || t == TextureType::DEPTH_STENCIL;
|
||||
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
return t == TextureType::INT;
|
||||
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
return t == TextureType::UINT;
|
||||
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
// DEPTH_STENCIL is treated as accessing the depth component. OpenGL 4.3
|
||||
// allows to specify which one, but not filament.
|
||||
return t == TextureType::DEPTH || t == TextureType::DEPTH_STENCIL;
|
||||
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
case DescriptorType::UNIFORM_BUFFER:
|
||||
case DescriptorType::SHADER_STORAGE_BUFFER:
|
||||
case DescriptorType::INPUT_ATTACHMENT:
|
||||
return false;
|
||||
}
|
||||
|
||||
// should never happen
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -62,14 +62,16 @@ public:
|
||||
backend::DescriptorSetOffsetArray dynamicOffsets) const noexcept;
|
||||
|
||||
// sets a ubo/ssbo descriptor
|
||||
void setBuffer(backend::descriptor_binding_t binding,
|
||||
void setBuffer(DescriptorSetLayout const& layout,
|
||||
backend::descriptor_binding_t binding,
|
||||
backend::Handle<backend::HwBufferObject> boh,
|
||||
uint32_t offset, uint32_t size) noexcept;
|
||||
uint32_t offset, uint32_t size);
|
||||
|
||||
// sets a sampler descriptor
|
||||
void setSampler(backend::descriptor_binding_t binding,
|
||||
void setSampler(DescriptorSetLayout const& layout,
|
||||
backend::descriptor_binding_t binding,
|
||||
backend::Handle<backend::HwTexture> th,
|
||||
backend::SamplerParams params) noexcept;
|
||||
backend::SamplerParams params);
|
||||
|
||||
// Used for duplicating material
|
||||
DescriptorSet duplicate(DescriptorSetLayout const& layout) const noexcept;
|
||||
@@ -82,6 +84,9 @@ public:
|
||||
return mValid;
|
||||
}
|
||||
|
||||
static bool isTextureCompatibleWithDescriptor(
|
||||
backend::TextureType t, backend::DescriptorType d) noexcept;
|
||||
|
||||
private:
|
||||
struct Desc {
|
||||
Desc() noexcept { }
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -35,11 +38,15 @@ DescriptorSetLayout::DescriptorSetLayout(
|
||||
backend::DescriptorSetLayout descriptorSetLayout) noexcept {
|
||||
for (auto&& desc : descriptorSetLayout.bindings) {
|
||||
mMaxDescriptorBinding = std::max(mMaxDescriptorBinding, desc.binding);
|
||||
mSamplers.set(desc.binding,
|
||||
desc.type == backend::DescriptorType::SAMPLER ||
|
||||
desc.type == backend::DescriptorType::SAMPLER_EXTERNAL);
|
||||
mUniformBuffers.set(desc.binding,
|
||||
desc.type == backend::DescriptorType::UNIFORM_BUFFER);
|
||||
mSamplers.set(desc.binding, backend::DescriptorSetLayoutBinding::isSampler(desc.type));
|
||||
mUniformBuffers.set(desc.binding, desc.type == backend::DescriptorType::UNIFORM_BUFFER);
|
||||
}
|
||||
|
||||
assert_invariant(mMaxDescriptorBinding < utils::bitset64::BIT_COUNT);
|
||||
|
||||
mDescriptorTypes = utils::FixedCapacityVector<backend::DescriptorType>(mMaxDescriptorBinding + 1);
|
||||
for (auto&& desc : descriptorSetLayout.bindings) {
|
||||
mDescriptorTypes[desc.binding] = desc.type;
|
||||
}
|
||||
|
||||
mDescriptorSetLayoutHandle = factory.create(driver,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -56,6 +57,10 @@ public:
|
||||
return mMaxDescriptorBinding;
|
||||
}
|
||||
|
||||
bool isValid(backend::descriptor_binding_t const binding) const noexcept {
|
||||
return mSamplers[binding] || mUniformBuffers[binding];
|
||||
}
|
||||
|
||||
bool isSampler(backend::descriptor_binding_t const binding) const noexcept {
|
||||
return mSamplers[binding];
|
||||
}
|
||||
@@ -68,11 +73,17 @@ public:
|
||||
return mUniformBuffers;
|
||||
}
|
||||
|
||||
backend::DescriptorType getDescriptorType(
|
||||
backend::descriptor_binding_t const binding) const noexcept {
|
||||
return mDescriptorTypes[binding];
|
||||
}
|
||||
|
||||
private:
|
||||
backend::DescriptorSetLayoutHandle mDescriptorSetLayoutHandle;
|
||||
utils::bitset64 mSamplers;
|
||||
utils::bitset64 mUniformBuffers;
|
||||
uint8_t mMaxDescriptorBinding = 0;
|
||||
utils::FixedCapacityVector<backend::DescriptorType> mDescriptorTypes;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,8 @@ void PostProcessDescriptorSet::terminate(HwDescriptorSetLayoutFactory& factory,
|
||||
void PostProcessDescriptorSet::setFrameUniforms(DriverApi& driver,
|
||||
TypedUniformBuffer<PerViewUib>& uniforms) noexcept {
|
||||
// initialize the descriptor-set
|
||||
mDescriptorSet.setBuffer(+PerViewBindingPoints::FRAME_UNIFORMS,
|
||||
mDescriptorSet.setBuffer(mDescriptorSetLayout,
|
||||
+PerViewBindingPoints::FRAME_UNIFORMS,
|
||||
uniforms.getUboHandle(), 0, uniforms.getSize());
|
||||
|
||||
mDescriptorSet.commit(mDescriptorSetLayout, driver);
|
||||
|
||||
@@ -46,8 +46,8 @@ ShadowMapDescriptorSet::ShadowMapDescriptorSet(FEngine& engine) noexcept {
|
||||
mDescriptorSet = DescriptorSet{ engine.getPerViewDescriptorSetLayoutDepthVariant() };
|
||||
|
||||
// initialize the descriptor-set
|
||||
mDescriptorSet.setBuffer(+PerViewBindingPoints::FRAME_UNIFORMS,
|
||||
mUniformBufferHandle, 0, sizeof(PerViewUib));
|
||||
mDescriptorSet.setBuffer(engine.getPerViewDescriptorSetLayoutDepthVariant(),
|
||||
+PerViewBindingPoints::FRAME_UNIFORMS, mUniformBufferHandle, 0, sizeof(PerViewUib));
|
||||
}
|
||||
|
||||
void ShadowMapDescriptorSet::terminate(DriverApi& driver) {
|
||||
|
||||
@@ -53,28 +53,32 @@ void SsrPassDescriptorSet::terminate(DriverApi& driver) {
|
||||
driver.destroyBufferObject(mShadowUbh);
|
||||
}
|
||||
|
||||
void SsrPassDescriptorSet::setFrameUniforms(TypedUniformBuffer<PerViewUib>& uniforms) noexcept {
|
||||
void SsrPassDescriptorSet::setFrameUniforms(FEngine const& engine,
|
||||
TypedUniformBuffer<PerViewUib>& uniforms) noexcept {
|
||||
// initialize the descriptor-set
|
||||
mDescriptorSet.setBuffer(+PerViewBindingPoints::FRAME_UNIFORMS,
|
||||
mDescriptorSet.setBuffer(engine.getPerViewDescriptorSetLayoutSsrVariant(),
|
||||
+PerViewBindingPoints::FRAME_UNIFORMS,
|
||||
uniforms.getUboHandle(), 0, uniforms.getSize());
|
||||
|
||||
// This is actually not used for the SSR variants, but the descriptor-set layout needs
|
||||
// to have this UBO because the fragment shader used is the "generic" one. Both Metal
|
||||
// and GL would be okay without this, but Vulkan's validation layer would complain.
|
||||
mDescriptorSet.setBuffer(+PerViewBindingPoints::SHADOWS, mShadowUbh, 0, sizeof(ShadowUib));
|
||||
mDescriptorSet.setBuffer(engine.getPerViewDescriptorSetLayoutSsrVariant(),
|
||||
+PerViewBindingPoints::SHADOWS, mShadowUbh, 0, sizeof(ShadowUib));
|
||||
|
||||
mUniforms = std::addressof(uniforms);
|
||||
}
|
||||
|
||||
void SsrPassDescriptorSet::prepareHistorySSR(Handle<HwTexture> ssr,
|
||||
void SsrPassDescriptorSet::prepareHistorySSR(FEngine const& engine, Handle<HwTexture> ssr,
|
||||
mat4f const& historyProjection,
|
||||
mat4f const& uvFromViewMatrix,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept {
|
||||
|
||||
mDescriptorSet.setSampler(+PerViewBindingPoints::SSR, ssr, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
mDescriptorSet.setSampler(engine.getPerViewDescriptorSetLayoutSsrVariant(),
|
||||
+PerViewBindingPoints::SSR, ssr, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
|
||||
assert_invariant(mUniforms);
|
||||
auto& s = mUniforms->edit();
|
||||
@@ -86,9 +90,11 @@ void SsrPassDescriptorSet::prepareHistorySSR(Handle<HwTexture> ssr,
|
||||
s.ssrStride = ssrOptions.stride;
|
||||
}
|
||||
|
||||
void SsrPassDescriptorSet::prepareStructure(Handle<HwTexture> structure) noexcept {
|
||||
void SsrPassDescriptorSet::prepareStructure(FEngine const& engine,
|
||||
Handle<HwTexture> structure) noexcept {
|
||||
// sampler must be NEAREST
|
||||
mDescriptorSet.setSampler(+PerViewBindingPoints::STRUCTURE, structure, {});
|
||||
mDescriptorSet.setSampler(engine.getPerViewDescriptorSetLayoutSsrVariant(),
|
||||
+PerViewBindingPoints::STRUCTURE, structure, {});
|
||||
}
|
||||
|
||||
void SsrPassDescriptorSet::commit(FEngine& engine) noexcept {
|
||||
|
||||
@@ -47,11 +47,11 @@ public:
|
||||
|
||||
void terminate(backend::DriverApi& driver);
|
||||
|
||||
void setFrameUniforms(TypedUniformBuffer<PerViewUib>& uniforms) noexcept;
|
||||
void setFrameUniforms(FEngine const& engine, TypedUniformBuffer<PerViewUib>& uniforms) noexcept;
|
||||
|
||||
void prepareStructure(TextureHandle structure) noexcept;
|
||||
void prepareStructure(FEngine const& engine, TextureHandle structure) noexcept;
|
||||
|
||||
void prepareHistorySSR(TextureHandle ssr,
|
||||
void prepareHistorySSR(FEngine const& engine, TextureHandle ssr,
|
||||
math::mat4f const& historyProjection,
|
||||
math::mat4f const& uvFromViewMatrix,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
|
||||
|
||||
@@ -102,7 +102,8 @@ project in Xcode to see changes take effect.
|
||||
|
||||
## Building iOS Samples with ASan / UBSan
|
||||
|
||||
1. Turn on ASan / UBSan in Filament's top-level CMakeLists.txt by uncommenting the following line:
|
||||
1. Turn on ASan / UBSan in Filament's top-level CMakeLists.txt by passing
|
||||
`-DFILAMENT_ENABLE_ASAN_UBSAN=1` to trigger the following line:
|
||||
|
||||
```
|
||||
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=undefined -fsanitize=address")
|
||||
|
||||
@@ -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 = 59;
|
||||
static constexpr size_t MATERIAL_VERSION = 60;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
@@ -34,15 +35,20 @@ backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept;
|
||||
|
||||
backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
MaterialDomain domain,
|
||||
UserVariantFilterMask variantFilter,
|
||||
bool isLit,
|
||||
ReflectionMode reflectionMode,
|
||||
RefractionMode refractionMode) noexcept;
|
||||
bool isLit, bool isSSR, bool hasFog,
|
||||
bool isVSM) noexcept;
|
||||
|
||||
backend::DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
|
||||
Variant variant,
|
||||
MaterialDomain domain,
|
||||
bool isLit, bool isSSR, bool hasFog) noexcept;
|
||||
|
||||
utils::CString getDescriptorName(
|
||||
filament::DescriptorSetBindingPoints set,
|
||||
DescriptorSetBindingPoints set,
|
||||
backend::descriptor_binding_t binding) noexcept;
|
||||
|
||||
backend::DescriptorType getDescriptorType(backend::SamplerType type, backend::SamplerFormat format);
|
||||
|
||||
} // namespace filament::descriptor_sets
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <private/filament/DescriptorSets.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <unordered_map>
|
||||
#include <string_view>
|
||||
@@ -128,6 +130,9 @@ public:
|
||||
|
||||
static utils::CString generateUniformName(const char* group, const char* sampler) noexcept;
|
||||
|
||||
static SamplerInfoList filterSamplerList(SamplerInfoList list,
|
||||
backend::DescriptorSetLayout const& descriptorSetLayout);
|
||||
|
||||
private:
|
||||
friend class Builder;
|
||||
|
||||
|
||||
@@ -17,65 +17,151 @@
|
||||
#include "private/filament/DescriptorSets.h"
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/StaticString.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace filament::descriptor_sets {
|
||||
|
||||
using namespace backend;
|
||||
|
||||
static DescriptorSetLayout const postProcessDescriptorSetLayout{{
|
||||
// used for post-processing passes
|
||||
static constexpr std::initializer_list<DescriptorSetLayoutBinding> postProcessDescriptorSetLayoutList = {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
}};
|
||||
};
|
||||
|
||||
static DescriptorSetLayout const depthVariantDescriptorSetLayout{{
|
||||
// used to generate shadow-maps
|
||||
static constexpr std::initializer_list<DescriptorSetLayoutBinding> depthVariantDescriptorSetLayoutList = {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
}};
|
||||
};
|
||||
|
||||
// ssrVariantDescriptorSetLayout must match perViewDescriptorSetLayout's vertex stage. This is
|
||||
// because the SSR variant is always using the "standard" vertex shader (i.e. there is no
|
||||
// dedicated SSR vertex shader), which uses perViewDescriptorSetLayout.
|
||||
// This means that PerViewBindingPoints::SHADOWS must be in the layout even though it's not used
|
||||
// by the SSR variant.
|
||||
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{{
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
|
||||
}};
|
||||
static constexpr std::initializer_list<DescriptorSetLayoutBinding> ssrVariantDescriptorSetLayoutList = {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
||||
{ DescriptorType::SAMPLER_2D_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
|
||||
};
|
||||
|
||||
static DescriptorSetLayout perViewDescriptorSetLayout = {{
|
||||
// Used for generating the color pass (i.e. the main pass). This is in fact a template that gets
|
||||
// declined into 8 different layouts, based on variants.
|
||||
//
|
||||
// Note about the SHADOW_MAP binding points:
|
||||
// This descriptor can either be a SAMPLER_FLOAT or a SAMPLER_DEPTH,
|
||||
// and there are 3 cases to consider:
|
||||
//
|
||||
// | TextureType | CompareMode | Filtered | SamplerType | Variant |
|
||||
// ---------+-------------+-------------+----------+-------------+---------+
|
||||
// PCF | DEPTH | COMPARE | Yes | DEPTH | - |
|
||||
// VSM | FLOAT | NONE | Yes | FLOAT | VSM |
|
||||
// OTHER | DEPTH | NONE | No | FLOAT | VSM |
|
||||
//
|
||||
// The SamplerType to use depends on the Variant. Variant::VSM is set for all cases except PCM.
|
||||
//
|
||||
static constexpr std::initializer_list<DescriptorSetLayoutBinding> perViewDescriptorSetLayoutList = {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::LIGHTS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::RECORD_BUFFER },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FROXEL_BUFFER },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOW_MAP },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::IBL_DFG_LUT },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::IBL_SPECULAR },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSAO },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG },
|
||||
}};
|
||||
{ DescriptorType::SAMPLER_2D_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_DEPTH, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOW_MAP },
|
||||
{ DescriptorType::SAMPLER_2D_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::IBL_DFG_LUT },
|
||||
{ DescriptorType::SAMPLER_CUBE_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::IBL_SPECULAR },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSAO },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
|
||||
{ DescriptorType::SAMPLER_CUBE_FLOAT, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG },
|
||||
};
|
||||
|
||||
static DescriptorSetLayout perRenderableDescriptorSetLayout = {{
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_POSITIONS },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_TANGENTS },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS },
|
||||
}};
|
||||
static constexpr std::initializer_list<DescriptorSetLayoutBinding> perRenderableDescriptorSetLayoutList = {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_POSITIONS },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_INT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_TANGENTS },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS },
|
||||
};
|
||||
|
||||
struct PairSamplerTypeFormatHasher {
|
||||
std::size_t operator()(const std::pair<SamplerType, SamplerFormat>& p) const {
|
||||
using UnderlyingSamplerType = std::underlying_type_t<SamplerType>;
|
||||
using UnderlyingSamplerFormat = std::underlying_type_t<SamplerFormat>;
|
||||
std::size_t seed = 0;
|
||||
std::size_t const hash1 = std::hash<UnderlyingSamplerType>{}(
|
||||
static_cast<UnderlyingSamplerType>(p.first)
|
||||
);
|
||||
std::size_t const hash2 = std::hash<UnderlyingSamplerFormat>{}(
|
||||
static_cast<UnderlyingSamplerFormat>(p.second)
|
||||
);
|
||||
seed ^= hash1 + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
seed ^= hash2 + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
static const std::unordered_map<
|
||||
std::pair<SamplerType, SamplerFormat>, DescriptorType, PairSamplerTypeFormatHasher> sDescriptorTypeMap{
|
||||
{{ SamplerType::SAMPLER_2D, SamplerFormat::INT }, DescriptorType::SAMPLER_2D_INT },
|
||||
{{ SamplerType::SAMPLER_2D, SamplerFormat::UINT }, DescriptorType::SAMPLER_2D_UINT },
|
||||
{{ SamplerType::SAMPLER_2D, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_2D_FLOAT },
|
||||
{{ SamplerType::SAMPLER_2D, SamplerFormat::SHADOW }, DescriptorType::SAMPLER_2D_DEPTH },
|
||||
{{ SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::INT }, DescriptorType::SAMPLER_2D_ARRAY_INT },
|
||||
{{ SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::UINT }, DescriptorType::SAMPLER_2D_ARRAY_UINT },
|
||||
{{ SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_2D_ARRAY_FLOAT },
|
||||
{{ SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::SHADOW }, DescriptorType::SAMPLER_2D_ARRAY_DEPTH },
|
||||
{{ SamplerType::SAMPLER_CUBEMAP, SamplerFormat::INT }, DescriptorType::SAMPLER_CUBE_INT },
|
||||
{{ SamplerType::SAMPLER_CUBEMAP, SamplerFormat::UINT }, DescriptorType::SAMPLER_CUBE_UINT },
|
||||
{{ SamplerType::SAMPLER_CUBEMAP, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_CUBE_FLOAT },
|
||||
{{ SamplerType::SAMPLER_CUBEMAP, SamplerFormat::SHADOW }, DescriptorType::SAMPLER_CUBE_DEPTH },
|
||||
{{ SamplerType::SAMPLER_CUBEMAP_ARRAY, SamplerFormat::INT }, DescriptorType::SAMPLER_CUBE_ARRAY_INT },
|
||||
{{ SamplerType::SAMPLER_CUBEMAP_ARRAY, SamplerFormat::UINT }, DescriptorType::SAMPLER_CUBE_ARRAY_UINT },
|
||||
{{ SamplerType::SAMPLER_CUBEMAP_ARRAY, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT },
|
||||
{{ SamplerType::SAMPLER_CUBEMAP_ARRAY, SamplerFormat::SHADOW }, DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH },
|
||||
{{ SamplerType::SAMPLER_3D, SamplerFormat::INT }, DescriptorType::SAMPLER_3D_INT },
|
||||
{{ SamplerType::SAMPLER_3D, SamplerFormat::UINT }, DescriptorType::SAMPLER_3D_UINT },
|
||||
{{ SamplerType::SAMPLER_3D, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_3D_FLOAT },
|
||||
{{ SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_EXTERNAL }
|
||||
};
|
||||
|
||||
// used for post-processing passes
|
||||
static DescriptorSetLayout const postProcessDescriptorSetLayout{ utils::StaticString("postProcess"),
|
||||
postProcessDescriptorSetLayoutList };
|
||||
|
||||
// used to generate shadow-maps
|
||||
static DescriptorSetLayout const depthVariantDescriptorSetLayout{
|
||||
utils::StaticString("depthVariant"), depthVariantDescriptorSetLayoutList
|
||||
};
|
||||
|
||||
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{ utils::StaticString("ssrVariant"),
|
||||
ssrVariantDescriptorSetLayoutList };
|
||||
|
||||
// Used for generating the color pass (i.e. the main pass). This is in fact a template that gets
|
||||
// declined into 8 different layouts, based on variants.
|
||||
static DescriptorSetLayout perViewDescriptorSetLayout = { utils::StaticString("perView"),
|
||||
perViewDescriptorSetLayoutList };
|
||||
|
||||
static DescriptorSetLayout perRenderableDescriptorSetLayout = {
|
||||
utils::StaticString("perRenderable"), perRenderableDescriptorSetLayoutList
|
||||
};
|
||||
|
||||
DescriptorSetLayout const& getPostProcessLayout() noexcept {
|
||||
return postProcessDescriptorSetLayout;
|
||||
@@ -93,8 +179,8 @@ DescriptorSetLayout const& getPerRenderableLayout() noexcept {
|
||||
return perRenderableDescriptorSetLayout;
|
||||
}
|
||||
|
||||
utils::CString getDescriptorName(DescriptorSetBindingPoints set,
|
||||
descriptor_binding_t binding) noexcept {
|
||||
utils::CString getDescriptorName(DescriptorSetBindingPoints const set,
|
||||
descriptor_binding_t const binding) noexcept {
|
||||
using namespace std::literals;
|
||||
|
||||
static std::unordered_map<descriptor_binding_t, std::string_view> const set0{{
|
||||
@@ -123,12 +209,12 @@ utils::CString getDescriptorName(DescriptorSetBindingPoints set,
|
||||
|
||||
switch (set) {
|
||||
case DescriptorSetBindingPoints::PER_VIEW: {
|
||||
auto pos = set0.find(binding);
|
||||
auto const pos = set0.find(binding);
|
||||
assert_invariant(pos != set0.end());
|
||||
return { pos->second.data(), pos->second.size() };
|
||||
}
|
||||
case DescriptorSetBindingPoints::PER_RENDERABLE: {
|
||||
auto pos = set1.find(binding);
|
||||
auto const pos = set1.find(binding);
|
||||
assert_invariant(pos != set1.end());
|
||||
return { pos->second.data(), pos->second.size() };
|
||||
}
|
||||
@@ -137,17 +223,13 @@ utils::CString getDescriptorName(DescriptorSetBindingPoints set,
|
||||
return "MaterialParams";
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
MaterialDomain domain,
|
||||
UserVariantFilterMask variantFilter,
|
||||
bool isLit,
|
||||
ReflectionMode reflectionMode,
|
||||
RefractionMode refractionMode) noexcept {
|
||||
|
||||
bool const ssr = reflectionMode == ReflectionMode::SCREEN_SPACE ||
|
||||
refractionMode == RefractionMode::SCREEN_SPACE;
|
||||
MaterialDomain const domain,
|
||||
bool const isLit, bool const isSSR, bool const hasFog,
|
||||
bool const isVSM) noexcept {
|
||||
|
||||
switch (domain) {
|
||||
case MaterialDomain::SURFACE: {
|
||||
@@ -166,7 +248,7 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
layout.bindings.end());
|
||||
}
|
||||
// remove descriptors not needed for SSRs
|
||||
if (!ssr) {
|
||||
if (!isSSR) {
|
||||
layout.bindings.erase(
|
||||
std::remove_if(layout.bindings.begin(), layout.bindings.end(),
|
||||
[](auto const& entry) {
|
||||
@@ -176,7 +258,7 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
|
||||
}
|
||||
// remove fog descriptor if filtered out
|
||||
if (variantFilter & (UserVariantFilterMask)UserVariantFilterBit::FOG) {
|
||||
if (!hasFog) {
|
||||
layout.bindings.erase(
|
||||
std::remove_if(layout.bindings.begin(), layout.bindings.end(),
|
||||
[](auto const& entry) {
|
||||
@@ -184,14 +266,82 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
}),
|
||||
layout.bindings.end());
|
||||
}
|
||||
|
||||
// change the SHADOW_MAP descriptor type for VSM
|
||||
if (isVSM) {
|
||||
auto const pos = std::find_if(layout.bindings.begin(), layout.bindings.end(),
|
||||
[](auto const& v) {
|
||||
return v.binding == PerViewBindingPoints::SHADOW_MAP;
|
||||
});
|
||||
if (pos != layout.bindings.end()) {
|
||||
pos->type = DescriptorType::SAMPLER_2D_ARRAY_FLOAT;
|
||||
}
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
case MaterialDomain::POST_PROCESS:
|
||||
return descriptor_sets::getPostProcessLayout();
|
||||
return postProcessDescriptorSetLayout;
|
||||
case MaterialDomain::COMPUTE:
|
||||
// TODO: what's the layout for compute?
|
||||
return descriptor_sets::getPostProcessLayout();
|
||||
return postProcessDescriptorSetLayout;
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
|
||||
Variant const variant,
|
||||
MaterialDomain const domain,
|
||||
bool const isLit, bool const isSSR, bool const hasFog) noexcept {
|
||||
if (Variant::isValidDepthVariant(variant)) {
|
||||
return depthVariantDescriptorSetLayout;
|
||||
}
|
||||
if (Variant::isSSRVariant(variant)) {
|
||||
return ssrVariantDescriptorSetLayout;
|
||||
}
|
||||
// We need to filter out all the descriptors not included in the "resolved" layout below
|
||||
return getPerViewDescriptorSetLayout(domain, isLit, isSSR, hasFog,
|
||||
Variant::isVSMVariant(variant));
|
||||
}
|
||||
|
||||
DescriptorType getDescriptorType(SamplerType const type, SamplerFormat const format) {
|
||||
auto const pos = sDescriptorTypeMap.find({ type, format });
|
||||
FILAMENT_CHECK_PRECONDITION(pos != sDescriptorTypeMap.end())
|
||||
<< "Incompatible Sampler Format " << to_string(format)
|
||||
<< " and Type " << to_string(type);
|
||||
return pos->second;
|
||||
}
|
||||
|
||||
template<class ITERATOR, class PREDICATE>
|
||||
constexpr static ITERATOR find_if(ITERATOR first, ITERATOR last, PREDICATE pred) {
|
||||
for (; first != last; ++first)
|
||||
if (pred(*first)) break;
|
||||
return first;
|
||||
}
|
||||
|
||||
constexpr static bool checkConsistency() noexcept {
|
||||
// check that all descriptors that apply to the vertex stage in perViewDescriptorSetLayout
|
||||
// are present in ssrVariantDescriptorSetLayout; meaning that the latter is compatible
|
||||
// with the former.
|
||||
for (auto const& r: perViewDescriptorSetLayoutList) {
|
||||
if (hasShaderType(r.stageFlags, ShaderStage::VERTEX)) {
|
||||
auto const pos = find_if(
|
||||
ssrVariantDescriptorSetLayoutList.begin(),
|
||||
ssrVariantDescriptorSetLayoutList.end(),
|
||||
[r](auto const& l) {
|
||||
return l.count == r.count &&
|
||||
l.type == r.type &&
|
||||
l.binding == r.binding &&
|
||||
l.flags == r.flags &&
|
||||
l.stageFlags == r.stageFlags;
|
||||
});
|
||||
if (pos == ssrVariantDescriptorSetLayoutList.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert(checkConsistency(), "ssrVariantDescriptorSetLayout is not compatible with "
|
||||
"perViewDescriptorSetLayout");
|
||||
|
||||
} // namespace filament::descriptor_sets
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "private/filament/SamplerInterfaceBlock.h"
|
||||
|
||||
#include <private/filament/DescriptorSets.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -102,7 +103,7 @@ const SamplerInterfaceBlock::SamplerInfo* SamplerInterfaceBlock::getSamplerInfo(
|
||||
return &mSamplersInfoList[pos->second];
|
||||
}
|
||||
|
||||
utils::CString SamplerInterfaceBlock::generateUniformName(const char* group, const char* sampler) noexcept {
|
||||
CString SamplerInterfaceBlock::generateUniformName(const char* group, const char* sampler) noexcept {
|
||||
char uniformName[256];
|
||||
|
||||
// sampler interface block name
|
||||
@@ -117,9 +118,27 @@ utils::CString SamplerInterfaceBlock::generateUniformName(const char* group, con
|
||||
std::min(sizeof(uniformName) / 2 - 2, strlen(sampler)),
|
||||
prefix + 1);
|
||||
*last++ = 0; // null terminator
|
||||
assert(last <= std::end(uniformName));
|
||||
assert_invariant(last <= std::end(uniformName));
|
||||
|
||||
return CString{ uniformName, size_t(last - uniformName) - 1u };
|
||||
}
|
||||
|
||||
SamplerInterfaceBlock::SamplerInfoList SamplerInterfaceBlock::filterSamplerList(
|
||||
SamplerInfoList list, backend::DescriptorSetLayout const& descriptorSetLayout) {
|
||||
// remove all the samplers that are not included in the descriptor-set layout
|
||||
list.erase(
|
||||
std::remove_if(list.begin(), list.end(),
|
||||
[&](auto const& entry) {
|
||||
auto pos = std::find_if(
|
||||
descriptorSetLayout.bindings.begin(),
|
||||
descriptorSetLayout.bindings.end(),
|
||||
[&entry](const auto& item) {
|
||||
return item.binding == entry.binding;
|
||||
});
|
||||
return pos == descriptorSetLayout.bindings.end();
|
||||
}), list.end());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -603,7 +603,7 @@ public:
|
||||
* extension will be derived from the shader stage. For example, mymaterial_0x0e.frag,
|
||||
* mymaterial_0x18.vert, etc.
|
||||
*/
|
||||
MaterialBuilder& saveRawVariants(bool saveVariants) noexcept;
|
||||
MaterialBuilder& saveRawVariants(bool saveRawVariants) noexcept;
|
||||
|
||||
//! If true, will include debugging information in generated SPIRV.
|
||||
MaterialBuilder& generateDebugInfo(bool generateDebugInfo) noexcept;
|
||||
@@ -635,7 +635,7 @@ public:
|
||||
* Build the material. If you are using the Filament engine with this library, you should use
|
||||
* the job system provided by Engine.
|
||||
*/
|
||||
Package build(utils::JobSystem& jobSystem) noexcept;
|
||||
Package build(utils::JobSystem& jobSystem);
|
||||
|
||||
public:
|
||||
// The methods and types below are for internal use
|
||||
@@ -809,7 +809,7 @@ private:
|
||||
// Multiple calls to findProperties accumulate the property sets across fragment
|
||||
// and vertex shaders in mProperties.
|
||||
bool findProperties(filament::backend::ShaderStage type,
|
||||
MaterialBuilder::PropertyList& allProperties,
|
||||
MaterialBuilder::PropertyList const& allProperties,
|
||||
CodeGenParams const& semanticCodeGenParams) noexcept;
|
||||
|
||||
bool runSemanticAnalysis(MaterialInfo* inOutInfo,
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <spirv_glsl.hpp>
|
||||
#include <spirv_msl.hpp>
|
||||
|
||||
#include "backend/DriverEnums.h"
|
||||
#include "private/filament/DescriptorSets.h"
|
||||
#include "sca/builtinResource.h"
|
||||
#include "sca/GLSLTools.h"
|
||||
@@ -34,16 +33,26 @@
|
||||
|
||||
#include "MetalArgumentBuffer.h"
|
||||
#include "SpirvFixup.h"
|
||||
#include "utils/ostream.h"
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_WEBGPU
|
||||
#include <tint/tint.h>
|
||||
#endif
|
||||
@@ -66,22 +75,7 @@ using BindingIndexMap = std::unordered_map<std::string, uint16_t>;
|
||||
#define DEBUG_LOG_DESCRIPTOR_SETS 0
|
||||
#endif
|
||||
|
||||
const char* toString(DescriptorType type) {
|
||||
switch (type) {
|
||||
case DescriptorType::UNIFORM_BUFFER:
|
||||
return "UNIFORM_BUFFER";
|
||||
case DescriptorType::SHADER_STORAGE_BUFFER:
|
||||
return "SHADER_STORAGE_BUFFER";
|
||||
case DescriptorType::SAMPLER:
|
||||
return "SAMPLER";
|
||||
case DescriptorType::INPUT_ATTACHMENT:
|
||||
return "INPUT_ATTACHMENT";
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
return "SAMPLER_EXTERNAL";
|
||||
}
|
||||
}
|
||||
|
||||
const char* toString(ShaderStageFlags flags) {
|
||||
static const char* toString(ShaderStageFlags const flags) {
|
||||
std::vector<const char*> stages;
|
||||
if (any(flags & ShaderStageFlags::VERTEX)) {
|
||||
stages.push_back("VERTEX");
|
||||
@@ -106,14 +100,14 @@ const char* toString(ShaderStageFlags flags) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char* prettyDescriptorFlags(DescriptorFlags flags) {
|
||||
static const char* prettyDescriptorFlags(DescriptorFlags const flags) {
|
||||
if (flags == DescriptorFlags::DYNAMIC_OFFSET) {
|
||||
return "DYNAMIC_OFFSET";
|
||||
}
|
||||
return "NONE";
|
||||
}
|
||||
|
||||
const char* prettyPrintSamplerType(SamplerType type) {
|
||||
static const char* prettyPrintSamplerType(SamplerType const type) {
|
||||
switch (type) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
return "SAMPLER_2D";
|
||||
@@ -140,35 +134,37 @@ DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib
|
||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::NONE, 0 });
|
||||
|
||||
for (auto const& sampler : samplers) {
|
||||
layout.bindings.push_back(DescriptorSetLayoutBinding {
|
||||
(sampler.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL) ?
|
||||
DescriptorType::SAMPLER_EXTERNAL : DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, sampler.binding,
|
||||
DescriptorFlags::NONE, 0 });
|
||||
for (auto const& sampler: samplers) {
|
||||
DescriptorSetLayoutBinding layoutBinding{
|
||||
DescriptorType::SAMPLER_EXTERNAL,
|
||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||
sampler.binding,
|
||||
DescriptorFlags::NONE,
|
||||
0
|
||||
};
|
||||
if (sampler.type != SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL) {
|
||||
layoutBinding.type = descriptor_sets::getDescriptorType(sampler.type, sampler.format);
|
||||
}
|
||||
layout.bindings.push_back(layoutBinding);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
static void collectDescriptorsForSet(filament::DescriptorSetBindingPoints set,
|
||||
static void collectDescriptorsForSet(DescriptorSetBindingPoints set,
|
||||
const GLSLPostProcessor::Config& config, DescriptorSetInfo& descriptors) {
|
||||
const MaterialInfo& material = *config.materialInfo;
|
||||
|
||||
DescriptorSetLayout const info = [&]() {
|
||||
// get the descriptor set layout for the given pinding point
|
||||
DescriptorSetLayout const descriptorSetLayout = [&] {
|
||||
switch (set) {
|
||||
case DescriptorSetBindingPoints::PER_VIEW: {
|
||||
if (filament::Variant::isValidDepthVariant(config.variant)) {
|
||||
return descriptor_sets::getDepthVariantLayout();
|
||||
}
|
||||
if (filament::Variant::isSSRVariant(config.variant)) {
|
||||
return descriptor_sets::getSsrVariantLayout();
|
||||
}
|
||||
return descriptor_sets::getPerViewDescriptorSetLayout(config.domain,
|
||||
config.variantFilter,
|
||||
material.isLit || material.hasShadowMultiplier,
|
||||
material.reflectionMode,
|
||||
material.refractionMode);
|
||||
bool const isLit = material.isLit || material.hasShadowMultiplier;
|
||||
bool const isSSR = material.reflectionMode == ReflectionMode::SCREEN_SPACE ||
|
||||
material.refractionMode == RefractionMode::SCREEN_SPACE;
|
||||
bool const hasFog = !(config.variantFilter & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
return descriptor_sets::getPerViewDescriptorSetLayoutWithVariant(
|
||||
config.variant, config.domain, isLit, isSSR, hasFog);
|
||||
}
|
||||
case DescriptorSetBindingPoints::PER_RENDERABLE:
|
||||
return descriptor_sets::getPerRenderableLayout();
|
||||
@@ -179,7 +175,8 @@ static void collectDescriptorsForSet(filament::DescriptorSetBindingPoints set,
|
||||
}
|
||||
}();
|
||||
|
||||
auto samplerList = [&]() {
|
||||
// get the sampler list for this binding point
|
||||
auto samplerList = [&] {
|
||||
switch (set) {
|
||||
case DescriptorSetBindingPoints::PER_VIEW:
|
||||
return SibGenerator::getPerViewSib(config.variant).getSamplerInfoList();
|
||||
@@ -192,42 +189,33 @@ static void collectDescriptorsForSet(filament::DescriptorSetBindingPoints set,
|
||||
}
|
||||
}();
|
||||
|
||||
// remove all the samplers that are not included in the descriptor-set layout
|
||||
samplerList.erase(std::remove_if(samplerList.begin(), samplerList.end(),
|
||||
[&info](auto const& entry) {
|
||||
auto pos = std::find_if(info.bindings.begin(),
|
||||
info.bindings.end(), [&entry](const auto& item) {
|
||||
return item.binding == entry.binding;
|
||||
});
|
||||
return pos == info.bindings.end();
|
||||
}),
|
||||
samplerList.end());
|
||||
// filter the list with the descriptor set layout
|
||||
auto const descriptorSetSamplerList =
|
||||
SamplerInterfaceBlock::filterSamplerList(std::move(samplerList), descriptorSetLayout);
|
||||
|
||||
auto getDescriptorName = [&](DescriptorSetBindingPoints set, descriptor_binding_t binding) {
|
||||
// helper to get the name of a descriptor for this set, given a binding.
|
||||
auto getDescriptorName = [set, &descriptorSetSamplerList](descriptor_binding_t binding) {
|
||||
if (set == DescriptorSetBindingPoints::PER_MATERIAL) {
|
||||
auto pos = std::find_if(samplerList.begin(), samplerList.end(),
|
||||
auto pos = std::find_if(descriptorSetSamplerList.begin(), descriptorSetSamplerList.end(),
|
||||
[&](const auto& entry) { return entry.binding == binding; });
|
||||
if (pos == samplerList.end()) {
|
||||
if (pos == descriptorSetSamplerList.end()) {
|
||||
return descriptor_sets::getDescriptorName(set, binding);
|
||||
}
|
||||
SamplerInterfaceBlock::SamplerInfo& sampler = *pos;
|
||||
return sampler.uniformName;
|
||||
return pos->uniformName;
|
||||
}
|
||||
return descriptor_sets::getDescriptorName(set, binding);
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < info.bindings.size(); i++) {
|
||||
backend::descriptor_binding_t binding = info.bindings[i].binding;
|
||||
auto name = getDescriptorName(set, binding);
|
||||
if (info.bindings[i].type == DescriptorType::SAMPLER ||
|
||||
info.bindings[i].type == DescriptorType::SAMPLER_EXTERNAL) {
|
||||
auto pos = std::find_if(samplerList.begin(), samplerList.end(),
|
||||
for (auto const& layoutBinding : descriptorSetLayout.bindings) {
|
||||
descriptor_binding_t binding = layoutBinding.binding;
|
||||
auto name = getDescriptorName(binding);
|
||||
if (DescriptorSetLayoutBinding::isSampler(layoutBinding.type)) {
|
||||
auto const pos = std::find_if(descriptorSetSamplerList.begin(), descriptorSetSamplerList.end(),
|
||||
[&](const auto& entry) { return entry.binding == binding; });
|
||||
assert_invariant(pos != samplerList.end());
|
||||
SamplerInterfaceBlock::SamplerInfo& sampler = *pos;
|
||||
descriptors.emplace_back(name, info.bindings[i], sampler);
|
||||
assert_invariant(pos != descriptorSetSamplerList.end());
|
||||
descriptors.emplace_back(name, layoutBinding, *pos);
|
||||
} else {
|
||||
descriptors.emplace_back(name, info.bindings[i], std::nullopt);
|
||||
descriptors.emplace_back(name, layoutBinding, std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,8 +224,8 @@ static void collectDescriptorsForSet(filament::DescriptorSetBindingPoints set,
|
||||
});
|
||||
}
|
||||
|
||||
void prettyPrintDescriptorSetInfoVector(DescriptorSets const& sets) {
|
||||
auto getName = [](uint8_t set) {
|
||||
static void prettyPrintDescriptorSetInfoVector(DescriptorSets const& sets) noexcept {
|
||||
auto getName = [](uint8_t const set) {
|
||||
switch (set) {
|
||||
case +DescriptorSetBindingPoints::PER_VIEW:
|
||||
return "perViewDescriptorSetLayout";
|
||||
@@ -254,18 +242,23 @@ void prettyPrintDescriptorSetInfoVector(DescriptorSets const& sets) {
|
||||
printf("[DS] info (%s) = [\n", getName(setIndex));
|
||||
for (auto const& descriptor : descriptors) {
|
||||
auto const& [name, info, sampler] = descriptor;
|
||||
if (info.type == DescriptorType::SAMPLER ||
|
||||
info.type == DescriptorType::SAMPLER_EXTERNAL) {
|
||||
if (DescriptorSetLayoutBinding::isSampler(info.type)) {
|
||||
assert_invariant(sampler.has_value());
|
||||
printf(" {name = %s, binding = %d, type = %s, count = %d, stage = %s, flags = "
|
||||
printf(" {name = %s, binding = %d, type = %.*s, count = %d, stage = %s, flags = "
|
||||
"%s, samplerType = %s}",
|
||||
name.c_str_safe(), info.binding, toString(info.type), info.count,
|
||||
name.c_str_safe(), info.binding,
|
||||
int(to_string(info.type).size()),
|
||||
to_string(info.type).data(),
|
||||
info.count,
|
||||
toString(info.stageFlags), prettyDescriptorFlags(info.flags),
|
||||
prettyPrintSamplerType(sampler->type));
|
||||
} else {
|
||||
printf(" {name = %s, binding = %d, type = %s, count = %d, stage = %s, flags = "
|
||||
printf(" {name = %s, binding = %d, type = %.*s, count = %d, stage = %s, flags = "
|
||||
"%s}",
|
||||
name.c_str_safe(), info.binding, toString(info.type), info.count,
|
||||
name.c_str_safe(), info.binding,
|
||||
int(to_string(info.type).size()),
|
||||
to_string(info.type).data(),
|
||||
info.count,
|
||||
toString(info.stageFlags), prettyDescriptorFlags(info.flags));
|
||||
}
|
||||
printf(",\n");
|
||||
@@ -365,7 +358,7 @@ static std::string stringifySpvOptimizerMessage(spv_message_level_t level, const
|
||||
}
|
||||
|
||||
void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
|
||||
filament::backend::ShaderStage stage, filament::backend::ShaderModel shaderModel,
|
||||
ShaderStage stage, ShaderModel shaderModel,
|
||||
bool useFramebufferFetch, const DescriptorSets& descriptorSets,
|
||||
const ShaderMinifier* minifier) {
|
||||
using namespace msl;
|
||||
@@ -479,7 +472,25 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
|
||||
break;
|
||||
}
|
||||
|
||||
case DescriptorType::SAMPLER:
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
assert_invariant(sampler.has_value());
|
||||
const std::string samplerName = std::string(name.c_str()) + "Smplr";
|
||||
@@ -575,7 +586,7 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
|
||||
//After splitting the image samplers, we need to remap the bindings to separate them.
|
||||
rebindImageSamplerForWGSL(*spirv);
|
||||
|
||||
//Allow non-uniform derivitives due to our nested shaders. See https://github.com/gpuweb/gpuweb/issues/3479
|
||||
//Allow non-uniform derivatives due to our nested shaders. See https://github.com/gpuweb/gpuweb/issues/3479
|
||||
const tint::spirv::reader::Options readerOpts{true};
|
||||
tint::wgsl::writer::Options writerOpts{};
|
||||
|
||||
@@ -614,7 +625,9 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
|
||||
*outWsl = wgslOut->wgsl;
|
||||
return true;
|
||||
#else
|
||||
slog.i << "Trying to emit WGSL without including WebGPU dependencies, please set CMake arg FILAMENT_SUPPORTS_WEBGPU and FILAMENT_SUPPORTS_WEBGPU" << io::endl;
|
||||
slog.i << "Trying to emit WGSL without including WebGPU dependencies,"
|
||||
" please set CMake arg FILAMENT_SUPPORTS_WEBGPU and FILAMENT_SUPPORTS_WEBGPU"
|
||||
<< io::endl;
|
||||
return false;
|
||||
#endif
|
||||
|
||||
@@ -674,7 +687,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
|
||||
// SpvRules should be enough.
|
||||
// I think this could cause the compilation to fail on gl_VertexID.
|
||||
using Type = std::underlying_type_t<EShMessages>;
|
||||
msg = EShMessages(Type(msg) | Type(EShMessages::EShMsgVulkanRules));
|
||||
msg = EShMessages(Type(msg) | Type(EShMsgVulkanRules));
|
||||
}
|
||||
|
||||
bool const ok = tShader.parse(&DefaultTBuiltInResource, internalConfig.langVersion, false, msg);
|
||||
@@ -684,7 +697,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
|
||||
}
|
||||
|
||||
// add texture lod bias
|
||||
if (config.shaderType == backend::ShaderStage::FRAGMENT &&
|
||||
if (config.shaderType == ShaderStage::FRAGMENT &&
|
||||
config.domain == MaterialDomain::SURFACE) {
|
||||
GLSLTools::textureLodBias(tShader);
|
||||
}
|
||||
@@ -760,8 +773,8 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
|
||||
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const {
|
||||
bool GLSLPostProcessor::preprocessOptimization(TShader& tShader,
|
||||
Config const& config, InternalConfig& internalConfig) const {
|
||||
using TargetApi = MaterialBuilder::TargetApi;
|
||||
assert_invariant(bool(internalConfig.spirvOutput) == (config.targetApi != TargetApi::OPENGL));
|
||||
|
||||
@@ -832,7 +845,7 @@ bool GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
|
||||
}
|
||||
|
||||
bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
|
||||
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const {
|
||||
Config const& config, InternalConfig& internalConfig) const {
|
||||
SpirvBlob spirv;
|
||||
|
||||
bool const optimizeForSize = mOptimization == MaterialBuilderBase::Optimization::SIZE;
|
||||
@@ -928,7 +941,7 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
|
||||
#else
|
||||
try {
|
||||
*internalConfig.glslOutput = glslCompiler.compile();
|
||||
} catch (spirv_cross::CompilerError e) {
|
||||
} catch (CompilerError e) {
|
||||
slog.e << "ERROR: " << e.what() << io::endl;
|
||||
return false;
|
||||
}
|
||||
@@ -948,8 +961,8 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createEmptyOptimizer() {
|
||||
auto optimizer = std::make_shared<spvtools::Optimizer>(SPV_ENV_UNIVERSAL_1_3);
|
||||
std::shared_ptr<Optimizer> GLSLPostProcessor::createEmptyOptimizer() {
|
||||
auto optimizer = std::make_shared<Optimizer>(SPV_ENV_UNIVERSAL_1_3);
|
||||
optimizer->SetMessageConsumer([](spv_message_level_t level,
|
||||
const char* source, const spv_position_t& position, const char* message) {
|
||||
if (!filterSpvOptimizerMessage(level)) {
|
||||
@@ -961,7 +974,7 @@ std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createEmptyOptimizer() {
|
||||
return optimizer;
|
||||
}
|
||||
|
||||
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
|
||||
std::shared_ptr<Optimizer> GLSLPostProcessor::createOptimizer(
|
||||
MaterialBuilder::Optimization optimization, Config const& config) {
|
||||
auto optimizer = createEmptyOptimizer();
|
||||
|
||||
@@ -1000,7 +1013,7 @@ void GLSLPostProcessor::optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv)
|
||||
}
|
||||
|
||||
void GLSLPostProcessor::fixupClipDistance(
|
||||
SpirvBlob& spirv, GLSLPostProcessor::Config const& config) const {
|
||||
SpirvBlob& spirv, Config const& config) const {
|
||||
if (!config.usesClipDistance) {
|
||||
return;
|
||||
}
|
||||
@@ -1040,7 +1053,7 @@ void GLSLPostProcessor::fixupClipDistance(
|
||||
|
||||
|
||||
void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config const& config) {
|
||||
auto RegisterPass = [&](spvtools::Optimizer::PassToken&& pass,
|
||||
auto RegisterPass = [&](Optimizer::PassToken&& pass,
|
||||
MaterialBuilder::TargetApi apiFilter = MaterialBuilder::TargetApi::ALL) {
|
||||
if (!(config.targetApi & apiFilter)) {
|
||||
return;
|
||||
@@ -1085,7 +1098,7 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
|
||||
}
|
||||
|
||||
void GLSLPostProcessor::registerSizePasses(Optimizer& optimizer, Config const& config) {
|
||||
auto RegisterPass = [&](spvtools::Optimizer::PassToken&& pass,
|
||||
auto RegisterPass = [&](Optimizer::PassToken&& pass,
|
||||
MaterialBuilder::TargetApi apiFilter = MaterialBuilder::TargetApi::ALL) {
|
||||
if (!(config.targetApi & apiFilter)) {
|
||||
return;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user