Compare commits

..
78 Commits
Author SHA1 Message Date
Benjamin Doherty d3be6a99e3 Test commit 2024-09-16 12:09:26 -07:00
Powei Feng 3e556588fc Merge branch 'rc/1.54.3' into release 2024-09-10 12:26:56 -07:00
Powei Feng 32b0625f36 Revert "reenable the SimplificationPass in spirv-opt"
This reverts commit 30387af61c.

Causes breakage on Pixel 8pro for 1P apps.
2024-09-09 10:37:11 -07:00
Sungun Park bb1d1c7349 Merge branch 'rc/1.54.2' into release 2024-09-04 18:55:59 +00:00
Sungun Park c967fb7860 Bump version to 1.54.3 2024-09-04 18:55:59 +00:00
Sungun Park 1b4afbab51 Release Filament 1.54.2 2024-09-04 18:55:52 +00:00
Powei Feng c677607353 github: Split Android CI to per ABI builds
- Pulled the android continuous workflow into an action
 - Split the android continuous build into 3 ABIs armv7, armv8a,
   and x86_64 (note that x86 is not present).
 - Remove the upload artifacts step from previous workflow.
   This was meant for letting client try a tip-of-tree Android
   build.  We can revisit this later. (Also removed mention
   in README.md)
 - Split the android continous into debug build and a release
   build commands, enabling deletion of intermediate output
   directory.
2024-09-04 11:22:43 -07:00
Powei Feng 422fcea2cf Make builderMakeName public
This call is used in the BuilderNameMixin template definition,
which is a public API.
2024-09-04 17:07:51 +00:00
Powei Feng cdb539b3cf Make builderMakeName public
This call is used in the BuilderNameMixin template definition,
which is a public API.
2024-09-04 10:04:20 -07:00
Mathias Agopian b0d3f14243 a handle with a tag would sometime return "no tag"
this is because the key used to retrieve the tag was not "truncated"
like it was when inserting the tag in the hash-map.
2024-09-03 17:13:59 +00:00
Ben Doherty 16bed4de00 Enable mmap on iOS (#8100) 2024-09-03 09:58:13 -07:00
Mathias Agopian 2b620e65fd fix potential framegraph textures use-after free
make sure to unset all textures in the per-view sampler group after
they are used, because the resource could be destroyed after the
pass is finished

- unset the fog and ibl_specular after the color pass
- move that cleanup a bit earlier
- in the case of screen-space reflection the structure pass is
  set, but might not be used in the color pass, so we also need to
  unset it after the SSR pass and before any other passes.
2024-08-30 16:24:25 -07:00
Mathias Agopian 2aa51db614 add texture tagging in the FrameGraph
Also tag user texture "FTexture" if user doesn't provide a tag, this
is so that we can distinguish them from internal textures that might
not be tagged.
2024-08-30 16:24:25 -07:00
Mathias AgopianandPowei Feng 2bbbb7f4d1 Update filament/src/details/Renderer.cpp
Co-authored-by: Powei Feng <powei@google.com>
2024-08-30 16:22:49 -07:00
Mathias Agopian 339e8da976 repair the "no buffer padding case"
with a previous change we were too aggressive in falling back to 
"no buffer padding", we need to do this only in the case where
"as subpass" would have been used if supported by the h/w.
2024-08-30 16:22:49 -07:00
Mathias Agopian c36dd955f4 make pushGroupMarker and insertEventMarker null-terminated strings
This is because we had this assumption on vk and mtl backends already
and also because SYSTRACE works this way and we didn't have non-null
terminated strings. This makes things more consistant and less bug 
prone.
2024-08-30 15:29:09 -07:00
Mathias Agopian 950be941eb disabling ResourceAllocator led to incorrect assert
this is because the "disposer" is now separate from the 
ResourceAllocator, so even if we don't use the resource allocator we
need to register the handle with the disposer.
2024-08-30 15:28:54 -07:00
Mathias Agopian 3857e3789c a handle with a tag would sometime return "no tag"
this is because the key used to retrieve the tag was not "truncated"
like it was when inserting the tag in the hash-map.
2024-08-30 15:28:37 -07:00
Mathias Agopian 2202b5ab8c Add support for depth clamp and use it for shadows
vk, metal and desktop gl all support depth clamp, GLES/android also does
with ANGLE. Add support for it in the backends.

use depth clamp to improve directional shadow quality; this allows
to render everything that's behind the camera at the same "zero" depth,
so we can reduce the depth range we need.

Fixes #6293
2024-08-30 10:56:31 -07:00
Sungun Park 639b933fd6 Rename customRenderTarget (#8093)
The currentRenderTarget is a misnomer as it's a custom RT. Rename it
properly.
2024-08-29 11:47:24 -07:00
Mathias Agopian 6653c6c08b fix a typo in RenderPassBuilder::renderFlags() 2024-08-28 21:51:03 -07:00
Maximilien Dagois 777f664b1b Refactored the handling of usage flags in the VulkanTexture constructor 2024-08-28 12:58:31 -07:00
Powei Feng f5f1e56123 Re-enable DebugRegistry::setProperty for release build (#8086)
`DebugRegistry::setProperty` is no longer just applicable to debug
builds.

This change was previously added in 6c0bd36 but then reverted
in a7317e7. We re-enable it and separate it from the shadow
changes in this commit.
2024-08-28 12:17:11 -07:00
Powei Feng 283d240409 Re-enable DebugRegistry::setProperty for release build (#8086)
`DebugRegistry::setProperty` is no longer just applicable to debug
builds.

This change was previously added in 6c0bd36 but then reverted
in a7317e7. We re-enable it and separate it from the shadow
changes in this commit.
2024-08-28 06:17:26 +00:00
Sungun Park bce91c56dd Merge branch 'rc/1.54.1' into release 2024-08-27 23:30:37 +00:00
Sungun Park f743bedef9 Bump version to 1.54.2 2024-08-27 23:30:37 +00:00
Sungun Park 0e0f3a5518 Release Filament 1.54.1 2024-08-27 23:30:26 +00:00
Powei Feng ba5413622f Decouple subpass from buffer padding (#8085)
Coupling subpass on/off with buffer padding has unintended
consequences when we need to turn subpass off for tests. We
uncouple them in this change.
2024-08-27 22:33:31 +00:00
Ben Doherty ba8d429fcb Metal: annotate memory allocation failures with buffer tag (#8082) 2024-08-27 10:49:35 -07:00
Mathias Agopian f11e5cb081 remove uses of the blackboard
the framegraph blackboard tends to create a lot of problems hard to 
debug, so we are now more explicit. here we remove usages of the
blackboard in RenderUtils.cpp.
2024-08-27 09:55:00 -07:00
Mathias Agopian c84f5d2a7f fix screen-space reflection when sub-pass colorgrading is used
the problem was that in that case, SSR was using the colorgraded
color buffer as history.
2024-08-27 09:55:00 -07:00
Sungun Park eb12e06387 Revert two depth relevant changes (#8083)
This reverts commits
- b70aa43727 "depth clamp cannot work with VSM"
- 6c0bd360b3 "Add support for depth clamp and use it for shadows"
2024-08-27 00:27:32 +00:00
Sungun Park a7317e7a99 Revert two depth relevant changes (#8083)
This reverts commits
- b70aa43727 "depth clamp cannot work with VSM"
- 6c0bd360b3 "Add support for depth clamp and use it for shadows"
2024-08-26 23:30:15 +00:00
Mathias Agopian be4391950d fix gltfio ubershaders
default parameters were not initialized which could cause them to
be incorrectly evaluated in the shader. this is actually a pretty
crazy bug that has been around since forever and which we were
"lucky" to not run into sooner.

this was exposed with the specular extension that was added recently.
2024-08-26 10:24:42 -07:00
Ben Doherty 40ce15cfbd Support tagging driver handles with a name (#8038) 2024-08-24 09:19:02 -07:00
Ben Doherty 6f20cf4b02 Support tagging driver handles with a name (#8038) 2024-08-24 09:17:05 -07:00
Mathias Agopian 9674a5ae3c add support for subresources in "blit"
This is needed for debugging, but is a pretty low-hanging fruit.
2024-08-23 22:37:01 -07:00
Balaji MandPowei Feng 485c05789b return statement moved to separate line
Co-authored-by: Powei Feng <powei@google.com>
2024-08-23 11:02:19 -07:00
Balaji M b058794dd1 decoding meshoptimized gltf just once 2024-08-23 11:02:19 -07:00
Powei Feng be22e9305d matc: initialize CustomVariable (#8076)
The default values were not provided via default construction.
Caused matc to crash on Linux.
2024-08-23 16:09:56 +00:00
Sungun Park 4ae7aa6a1b Fix compatibility warning for multiview (#8075)
Verify the compatibility between a compiled material and the engine's
setting only when the engine is set up for stereo.

Default materials are always compiled with either 'instanced' or
'multiview. Therefore Filament will emit warnings unintentionally if the
engine is set up for stereo. This commit fixes it.
2024-08-22 20:39:04 +00:00
Mathias Agopian 5e7106b521 custom variables can now have their precision specified
e.g.:

    variables : [
        {
            name : vertex,
            precision : medium,
         }
    ],
2024-08-22 13:08:48 -07:00
Mathias Agopian 30387af61c reenable the SimplificationPass in spirv-opt
Issues with it were fixed (https://github.com/KhronosGroup/SPIRV-Cross/pull/2163),
so it should be safe to reenable. The invalid glsl code seems to be resolved.
2024-08-22 13:08:23 -07:00
Mathias Agopian 1c2ffc9ed4 new screenshot debug option in gltf_viewer
screenshots are saved in the current directory as 
"screenshotXX.ppm" with XX increasing after each
screenshot.
2024-08-20 22:41:49 -07:00
Sungun Park 574518ea74 Cleanup color pass for multiview (#8062) 2024-08-20 21:44:17 +00:00
Ben Doherty d5a274fa25 Add better glShaderSource fix for IMG devices (#8061) 2024-08-20 12:28:13 -07:00
Mathias Agopian 3351db1178 improve FrameSkipper when disregarding skipping
It is legal for the app to draw a frame even when FrameSkipper 
detects that a frame should be skipped. In that case we used to
overwrite the most recent fence, but this wasn't ideal. We now
proceed as if the fence had signaled, i.e. we destroy it and
move all the fences up, creating a new one at the end of the 
delayed array.
2024-08-20 12:07:59 -07:00
Mathias Agopian b70aa43727 depth clamp cannot work with VSM
This is because the shadowmap doesn't store depth values so it doesn't 
work to "flatten" all casters behind de camera to the near plane.
The bulk of shadowing works but the filtering/edges don't always.

Disable depth-clamping when VSM is enabled.
2024-08-20 12:04:01 -07:00
Sungun Park 1f9a11802d Minor tweak (#8053)
Move the common part outside the loop.
2024-08-20 17:13:55 +00:00
Sungun Park 89835a7a67 Fix a bug for calculating distance (#8058)
This is a missing part from the change
22d99bac3d
2024-08-20 16:28:29 +00:00
Powei Feng aa5f36e1e3 Bump version to 1.54.1 2024-08-20 08:55:19 -07:00
Powei Feng c1a3450d9c Merge branch 'rc/1.54.0' into release 2024-08-20 08:55:18 -07:00
Powei Feng 28ef805e5d Release Filament 1.54.0 2024-08-20 08:55:10 -07:00
Mathias Agopian 6c0bd360b3 Add support for depth clamp and use it for shadows
vk, metal and desktop gl all support depth clamp, GLES/android also does
with ANGLE. Add support for it in the backends.

use depth clamp to improve directional shadow quality; this allows
to render everything that's behind the camera at the same "zero" depth,
so we can reduce the depth range we need.

Fixes #6293
2024-08-19 17:13:30 -07:00
Mathias Agopian 063affb612 more improvement of csm display 2024-08-19 17:13:03 -07:00
Mathias Agopian 9c857f64ae improve split-view mode
- side panel doesn't overlap with content anymore
2024-08-19 17:13:03 -07:00
Mathias Agopian 5966b5dd8f fix shadow cascade computations
shadow cascades where not calculated properly because part of the 
calculation took the cascade near/far into account, while another
part didn't. This resulted in cascades being too large. It didn't
create wrong shadows, but reduced (and in some case canceled) the
usefulness of the cascade.

We fix the problem by always  using the projection matrix only for
describing the cascade's frustum, as opposed to just passing the
near/far plane distances.

Now the calculation of each cascade is completely self contained and
identical.


We also improve the orientation of the light frustum:
We can rotate the light frustum around the light direction axis, so
it aligns with the view direction, this generally result in smaller
light frustums. This cannot be used in stable mode.
2024-08-19 16:30:16 -07:00
Mathias Agopian 1795c40591 fix typo when calculation shadowmap frustum
min() and lowest() are different!
2024-08-19 00:03:44 -07:00
Mathias Agopian 26f4239d8c fix a few issues with shadowing
A recent change broken the optional "depth clamp" as well as the 
computation of the far plane of the light frustum. There was also
a case where DEBUG builds could assert.

- The far plane was no longer being "optimized" (i.e. moved as close
as possible), which resulted in less optimal use of the shadow texture.
the far plane can be moved as close as the farthest visible shadow 
caster.

- After the camera/light frustums intersection we now see of the 2D
bounds seen from the light are empty and if so we bail, which prevents
an assertion later.

- finally, the "DEPTH_CLAMP" option is also updated for the new code
structure.
2024-08-16 16:00:37 -07:00
Mathias Agopian ad29b9c70a fix several issue with the debug datasource in View
- the last View created was always overriding previous View's datasource
- because of lazy registering of the data source it was possible that
  the registering lambda was called after the view was destroyed, leading
  to crashes
- all view would share the same PID parameters and these would be
  initialized to default value instead of the user provided value. so
  debug build would behave differently.

With this change we improve things:
- now only the first view gets to publish its data source. it's still
  not ideal, but works for our use case with gltf_Viewer
- the view can now unregister itself when it's destroyed
- only the view that successfully registered uses the debug PID values
  and publishes its data source.
- the normal parameters are used until we query the datasource (from
  imgui), so by default the behavior is now identical to release builds


This fixes a crash in gltf_viewer when opening the Debug panel.
2024-08-16 09:38:54 -07:00
Sungun Park 1c817026f2 Remove unused code (#8043) 2024-08-14 15:41:10 +00:00
Powei Feng 4396a1a776 Fix misnumbered version in RELEASE_NOTES 2024-08-13 16:30:07 -07:00
Powei Feng 44a954b559 Fix misnumbered version in RELEASE_NOTES 2024-08-13 16:28:33 -07:00
Powei Feng d88ab8d527 Bump version to 1.54.0 2024-08-13 16:22:56 -07:00
Powei Feng cdd0147a17 Release Filament 1.53.5 2024-08-13 15:59:08 -07:00
Sungun Park 9de29a475e Add warning for the incompatibility of stereo type (#8031)
Print a warning in case the sterescopic type in a compiled package is
different than what's in the engine's setting. The application may
proceed, but it could end up visual glitches when enabling stereoscopic
rendering.

This requires the stereoscopic type to be written into the package,
which needs a material version bump.
2024-08-13 01:31:10 +00:00
Mathias Agopian 7f97363d9c fix debug build 2024-08-12 15:09:06 -07:00
Mathias Agopian d88b4c1bc3 Add a way to retrieve the count of all resources
This is intended for debugging.

BUGS=[358111049]
2024-08-12 14:50:43 -07:00
Mathias Agopian 3badf9db18 read all valid pending timer queries each frame
This reduces the latency of the timer query result; with the previous
code the latency could only increase, but there is no reason to wait
a whole frame for reading the next available result.

We just loop over them until we find one that has not signaled; instead
of doing one per frame.
2024-08-12 14:50:20 -07:00
Mathias Agopian 929f793cf3 clean-up RenderPass a bit
make mCommandBegin and mCommandEnd const* const, this allows to make
sort() And resize() static, and instanceify() almost static..
2024-08-12 14:49:57 -07:00
Mathias Agopian 06bffaa650 fix a couple precondition checks logs 2024-08-12 14:49:44 -07:00
Ben Doherty 2e2f111435 Re-enable missing packing/unpacking functions in ESSL, fix IMG shader compilation (#8030) 2024-08-12 10:23:52 -07:00
Mathias Agopian 06f9626429 improve GL backend debug markers
we now have two levels of debug markers. Those that come from the "user"
(i.e. filament itself) are now always enabled and generate both 
systrace and gl markers. the 2nd level is internal and always
disabled by default. Of enabled at compile time it'll emit markers for
each driver API method.
2024-08-09 15:25:33 -07:00
Mathias Agopian fa0e20a699 always validate Engine::config 2024-08-08 22:46:57 -07:00
Mathias Agopian 59593830e5 Fix skipFrame incorrectly asserts in some cases
BUGS=[357992376]
2024-08-08 13:24:38 -07:00
Mathias Agopian 112f9d742c assert when a program compilation fails
The current behavior is to get a UB, which usually is a crash. So this
is better.
2024-08-08 13:24:08 -07:00
Ben Doherty 8ba20eb03c Metal: fix static texture target on more devices (#8022) 2024-08-08 13:04:42 -07:00
Ben Doherty 1c0370d5d7 Remove flaky assertion in ShadowMap (#8023) 2024-08-08 13:04:30 -07:00
115 changed files with 1722 additions and 658 deletions
@@ -0,0 +1,17 @@
name: 'Android Continuous'
inputs:
build-abi:
description: 'The target platform ABI'
required: true
default: 'armeabi-v7a'
runs:
using: "composite"
steps:
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Run build script
run: |
cd build/android && printf "y" | ./build.sh continuous ${{ inputs.build-abi }}
shell: bash
+25 -22
View File
@@ -8,32 +8,35 @@ on:
- rc/**
jobs:
build-android:
name: build-android
build-android-armv7:
name: build-android-armv7
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- uses: actions/setup-java@v3
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
with:
distribution: 'temurin'
java-version: '17'
- name: Run build script
run: |
cd build/android && printf "y" | ./build.sh continuous
- uses: actions/upload-artifact@v1.0.0
build-abi: armeabi-v7a
build-android-armv8a:
name: build-android-armv8a
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
with:
name: filament-android
path: out/filament-android-release.aar
- uses: actions/upload-artifact@v1.0.0
build-abi: arm64-v8a
build-android-x86_64:
name: build-android-x86_64
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
with:
name: filamat-android-full
path: out/filamat-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: gltfio-android-release
path: out/gltfio-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: filament-utils-android-release
path: out/filament-utils-android-release.aar
build-abi: x86_64
+3 -1
View File
@@ -49,8 +49,10 @@ jobs:
distribution: 'temurin'
java-version: '17'
- name: Run build script
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
# Continuous builds will build everything
run: |
cd build/android && printf "y" | ./build.sh presubmit
cd build/android && printf "y" | ./build.sh presubmit arm64-v8a
build-ios:
name: build-iOS
+1 -1
View File
@@ -120,7 +120,7 @@ jobs:
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/android && printf "y" | ./build.sh release
cd build/android && printf "y" | ./build.sh release armeabi-v7a,arm64-v8a,x86,x86_64
cd ../..
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
+2 -12
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.53.5'
implementation 'com.google.android.filament:filament-android:1.54.3'
}
```
@@ -51,19 +51,9 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.53.5'
pod 'Filament', '~> 1.54.3'
```
### Snapshots
If you prefer to live on the edge, you can download a continuous build by following the following
steps:
1. Find the [commit](https://github.com/google/filament/commits/main) you're interested in.
2. Click the green check mark under the commit message.
3. Click on the _Details_ link for the platform you're interested in.
4. On the top left click _Summary_, then in the _Artifacts_ section choose the desired artifact.
## Documentation
- [Filament](https://google.github.io/filament/Filament.html), an in-depth explanation of
+15
View File
@@ -7,6 +7,21 @@ 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.54.3
## v1.54.2
- Add a `name` API to Filament objects for debugging handle use-after-free assertions
## v1.54.1
## v1.54.0
- materials: add a new `stereoscopicType` material parameter. [⚠️ **New Material Version**]
- Fix a crash when compiling shaders on IMG devices
## v1.53.5
- engine: Fix bug causing certain sampler parameters to not be applied correctly in GLES 2.0 and on
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.53.5
VERSION_NAME=1.54.3
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+15 -7
View File
@@ -60,13 +60,7 @@ if [[ ! -d "${ANDROID_HOME}/ndk/$FILAMENT_NDK_VERSION" ]]; then
yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --licenses
${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager "ndk;$FILAMENT_NDK_VERSION"
fi
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
# Continuous builds will build everything
ANDROID_ABIS=
if [[ "$TARGET" == "presubmit" ]]; then
ANDROID_ABIS="-q arm64-v8a"
fi
# Build the Android sample-gltf-viewer APK during release.
BUILD_SAMPLES=
@@ -74,5 +68,19 @@ if [[ "$TARGET" == "release" ]]; then
BUILD_SAMPLES="-k sample-gltf-viewer"
fi
function build_android() {
local ABI=$1
# Do the following in two steps so that we do not run out of space
if [[ -n "${BUILD_DEBUG}" ]]; then
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android -q ${ABI} -c ${BUILD_SAMPLES} ${GENERATE_ARCHIVES} ${BUILD_DEBUG}
rm -rf out/cmake-android-debug-*
fi
if [[ -n "${BUILD_RELEASE}" ]]; then
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android -q ${ABI} -c ${BUILD_SAMPLES} ${GENERATE_ARCHIVES} ${BUILD_RELEASE}
rm -rf out/cmake-android-release-*
fi
}
pushd `dirname $0`/../.. > /dev/null
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android $ANDROID_ABIS -c $BUILD_SAMPLES $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
build_android $2
+11 -2
View File
@@ -1308,7 +1308,12 @@ Description
declare a variable called `eyeDirection` you can access it in the fragment shader using
`variable_eyeDirection`. In the vertex shader, the interpolant name is simply a member of
the `MaterialVertexInputs` structure (`material.eyeDirection` in your example). Each
interpolant is of type `float4` (`vec4`) in the shaders.
interpolant is of type `float4` (`vec4`) in the shaders. By default the precision of the
interpolant is `highp` in *both* the vertex and fragment shaders.
An alternate syntax can be used to specify both the name and precision of the interpolant.
In this case the specified precision is used as-is in both fragment and vertex stages, in
particular if `default` is specified the default precision is used is the fragment shader
(`mediump`) and in the vertex shader (`highp`).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
@@ -1320,7 +1325,11 @@ material {
}
],
variables : [
eyeDirection
eyeDirection,
{
name : eyeColor,
precision : medium
}
],
vertexDomain : device,
depthWrite : false,
+1
View File
@@ -61,6 +61,7 @@ set(SRCS
src/Engine.cpp
src/Exposure.cpp
src/Fence.cpp
src/FilamentBuilder.cpp
src/FrameInfo.cpp
src/FrameSkipper.cpp
src/Froxelizer.cpp
@@ -1058,7 +1058,7 @@ struct RasterState {
bool inverseFrontFaces : 1; // 31
//! padding, must be 0
uint8_t padding : 1; // 32
bool depthClamp : 1; // 32
};
uint32_t u = 0;
};
@@ -1244,7 +1244,7 @@ enum class Workaround : uint16_t {
ADRENO_UNIFORM_ARRAY_CRASH,
// Workaround a Metal pipeline compilation error with the message:
// "Could not statically determine the target of a texture". See light_indirect.fs
A8X_STATIC_TEXTURE_TARGET_ERROR,
METAL_STATIC_TEXTURE_TARGET_ERROR,
// Adreno drivers sometimes aren't able to blit into a layer of a texture array.
DISABLE_BLIT_INTO_TEXTURE_ARRAY,
// Multiple workarounds needed for PowerVR GPUs
@@ -162,6 +162,10 @@ DECL_DRIVER_API_0(finish)
// reset state tracking, if the driver does any state tracking (e.g. GL)
DECL_DRIVER_API_0(resetState)
DECL_DRIVER_API_N(setDebugTag,
backend::HandleBase::HandleId, handleId,
utils::CString, tag)
/*
* Creating driver objects
* -----------------------
@@ -305,6 +309,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isDepthStencilBlitSupported, backend::TextureFormat, format)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
@@ -439,12 +444,10 @@ DECL_DRIVER_API_N(setPushConstant,
backend::PushConstantVariant, value)
DECL_DRIVER_API_N(insertEventMarker,
const char*, string,
uint32_t, len = 0)
const char*, string)
DECL_DRIVER_API_N(pushGroupMarker,
const char*, string,
uint32_t, len = 0)
const char*, string)
DECL_DRIVER_API_0(popGroupMarker)
@@ -20,11 +20,12 @@
#include <backend/Handle.h>
#include <utils/Allocator.h>
#include <utils/CString.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <tsl/robin_map.h>
@@ -173,8 +174,10 @@ public:
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
auto const pNode = static_cast<typename Allocator::Node*>(p);
uint8_t const expectedAge = pNode[-1].age;
FILAMENT_CHECK_POSTCONDITION(expectedAge == age) <<
"use-after-free of Handle with id=" << handle.getId();
// getHandleTag() is only called if the check fails.
FILAMENT_CHECK_POSTCONDITION(expectedAge == age)
<< "use-after-free of Handle with id=" << handle.getId()
<< ", tag=" << getHandleTag(handle.getId()).c_str_safe();
}
}
@@ -201,6 +204,29 @@ public:
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
}
void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
// TODO: for now, only pool handles check for use-after-free, so we only keep tags for
// those
if (isPoolHandle(id)) {
// Truncate the age to get the debug tag
uint32_t const key = id & ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
// This line is the costly part. In the future, we could potentially use a custom
// allocator.
mDebugTags[key] = std::move(tag);
}
}
utils::CString getHandleTag(HandleBase::HandleId id) const noexcept {
if (!isPoolHandle(id)) {
return "(no tag)";
}
uint32_t const key = id & ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
if (auto pos = mDebugTags.find(key); pos != mDebugTags.end()) {
return pos->second;
}
return "(no tag)";
}
private:
template<typename D>
@@ -318,12 +344,24 @@ private:
}
}
// we handle a 4 bits age per address
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u; // pool vs heap handle
static constexpr uint32_t HANDLE_AGE_MASK = 0x78000000u; // handle's age
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu; // handle index
static constexpr uint32_t HANDLE_TAG_MASK = HANDLE_AGE_MASK;
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
// number if bits allotted to the handle's age (currently 4 max)
static constexpr uint32_t HANDLE_AGE_BIT_COUNT = 4;
// number if bits allotted to the handle's debug tag (HANDLE_AGE_BIT_COUNT max)
static constexpr uint32_t HANDLE_DEBUG_TAG_BIT_COUNT = 2;
// bit shift for both the age and debug tag
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
// mask for the heap (vs pool) flag
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u;
// mask for the age
static constexpr uint32_t HANDLE_AGE_MASK =
((1 << HANDLE_AGE_BIT_COUNT) - 1) << HANDLE_AGE_SHIFT;
// mask for the debug tag
static constexpr uint32_t HANDLE_DEBUG_TAG_MASK =
((1 << HANDLE_DEBUG_TAG_BIT_COUNT) - 1) << HANDLE_AGE_SHIFT;
// mask for the index
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu;
static_assert(HANDLE_DEBUG_TAG_BIT_COUNT <= HANDLE_AGE_BIT_COUNT);
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
return (id & HANDLE_HEAP_FLAG) == 0u;
@@ -338,7 +376,7 @@ private:
// a non-pool handle.
if (UTILS_LIKELY(isPoolHandle(id))) {
char* const base = (char*)mHandleArena.getArea().begin();
uint32_t const tag = id & HANDLE_TAG_MASK;
uint32_t const tag = id & HANDLE_AGE_MASK;
size_t const offset = (id & HANDLE_INDEX_MASK) * Allocator::getAlignment();
return { static_cast<void*>(base + offset), tag };
}
@@ -353,7 +391,7 @@ private:
size_t const offset = (char*)p - base;
assert_invariant((offset % Allocator::getAlignment()) == 0);
auto id = HandleBase::HandleId(offset / Allocator::getAlignment());
id |= tag & HANDLE_TAG_MASK;
id |= tag & HANDLE_AGE_MASK;
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
return id;
}
@@ -363,6 +401,7 @@ private:
// Below is only used when running out of space in the HandleArena
mutable utils::Mutex mLock;
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
tsl::robin_map<HandleBase::HandleId, utils::CString> mDebugTags;
HandleBase::HandleId mId = 0;
bool mUseAfterFreeCheckDisabled = false;
};
+1 -1
View File
@@ -24,7 +24,7 @@
#include <utils/debug.h>
#include <utils/ostream.h>
#if !defined(WIN32) && !defined(__EMSCRIPTEN__) && !defined(IOS)
#if !defined(WIN32) && !defined(__EMSCRIPTEN__)
# include <sys/mman.h>
# include <unistd.h>
# define HAS_MMAP 1
+3
View File
@@ -80,6 +80,9 @@ HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
bool disableUseAfterFreeCheck) noexcept
: mHandleArena(name, size, disableUseAfterFreeCheck),
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
// Reserve initial space for debug tags. This prevents excessive calls to malloc when the first
// few tags are set.
mDebugTags.reserve(512);
}
template <size_t P0, size_t P1, size_t P2>
+2
View File
@@ -160,6 +160,8 @@ public:
size_t size, bool forceGpuBuffer = false);
~MetalBuffer();
[[nodiscard]] bool wasAllocationSuccessful() const noexcept { return mBuffer || mCpuBuffer; }
MetalBuffer(const MetalBuffer& rhs) = delete;
MetalBuffer& operator=(const MetalBuffer& rhs) = delete;
+2 -2
View File
@@ -53,8 +53,8 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
TrackedMetalBuffer::Type::GENERIC };
}
FILAMENT_CHECK_POSTCONDITION(mBuffer)
<< "Could not allocate Metal buffer of size " << size << ".";
// mBuffer might fail to be allocated. Clients can check for this by calling
// wasAllocationSuccessful().
}
MetalBuffer::~MetalBuffer() {
+2 -1
View File
@@ -93,7 +93,7 @@ struct MetalContext {
} highestSupportedGpuFamily;
struct {
bool a8xStaticTextureTargetError;
bool staticTextureTargetError;
} bugs;
// sampleCountLookup[requestedSamples] gives a <= sample count supported by the device.
@@ -112,6 +112,7 @@ struct MetalContext {
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
DepthClampStateTracker depthClampState;
Handle<HwRenderPrimitive> currentRenderPrimitive;
// State caches.
+3 -2
View File
@@ -57,7 +57,6 @@ class MetalDriver final : public DriverBase {
public:
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
void runAtNextTick(const std::function<void()>& fn) noexcept;
private:
@@ -73,10 +72,12 @@ private:
/*
* Tasks run regularly on the driver thread.
* Not thread-safe; tasks are run from the driver thead and must be enqueued from the driver
* thread.
*/
void runAtNextTick(const std::function<void()>& fn) noexcept;
void executeTickOps() noexcept;
std::vector<std::function<void()>> mTickOps;
std::mutex mTickOpsLock;
/*
* Driver interface
+51 -12
View File
@@ -162,8 +162,10 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
sc[s] = [mContext->device supportsTextureSampleCount:s] ? s : sc[s - 1];
}
mContext->bugs.a8xStaticTextureTargetError =
[mContext->device.name containsString:@"Apple A8X GPU"];
mContext->bugs.staticTextureTargetError =
[mContext->device.name containsString:@"Apple A8X GPU"] ||
[mContext->device.name containsString:@"Apple A8 GPU"] ||
[mContext->device.name containsString:@"Apple A7 GPU"];
mContext->commandQueue = mPlatform.createCommandQueue(mContext->device);
mContext->pipelineStateCache.setDevice(mContext->device);
@@ -318,17 +320,40 @@ void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
// No actual GPU memory is allocated here, so no need to check for allocation success.
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
auto elementSize = (uint8_t) getElementTypeSize(elementType);
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
auto elementSize = (uint8_t)getElementTypeSize(elementType);
auto* indexBuffer =
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
auto& buffer = indexBuffer->buffer;
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!buffer.wasAllocationSuccessful())) {
const size_t byteCount = buffer.getSize();
runAtNextTick([byteCount, this, ibh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal index buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(ibh.getId()).c_str_safe();
});
}
}
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
auto* bufferObject =
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!bufferObject->getBuffer()->wasAllocationSuccessful())) {
runAtNextTick([byteCount, this, boh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(boh.getId()).c_str_safe();
});
}
}
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -832,6 +857,10 @@ bool MetalDriver::isProtectedTexturesSupported() {
return false;
}
bool MetalDriver::isDepthClampSupported() {
return true;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -840,8 +869,8 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
return true;
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
return false;
case Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR:
return mContext->bugs.a8xStaticTextureTargetError;
case Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR:
return mContext->bugs.staticTextureTargetError;
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
return false;
default:
@@ -1273,11 +1302,11 @@ void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
pushConstants.setPushConstant(value, index);
}
void MetalDriver::insertEventMarker(const char* string, uint32_t len) {
void MetalDriver::insertEventMarker(const char* string) {
}
void MetalDriver::pushGroupMarker(const char* string, uint32_t len) {
void MetalDriver::pushGroupMarker(const char* string) {
mContext->groupMarkers.push(string);
}
@@ -1749,6 +1778,13 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
[mContext->currentRenderPassEncoder setFrontFacingWinding:winding];
}
// depth clip mode
MTLDepthClipMode depthClipMode = rs.depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
mContext->depthClampState.updateState(depthClipMode);
if (mContext->depthClampState.stateChanged()) {
[mContext->currentRenderPassEncoder setDepthClipMode:depthClipMode];
}
// Set the depth-stencil state, if a state change is needed.
DepthStencilState depthState;
if (depthAttachment) {
@@ -1758,6 +1794,8 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
if (stencilAttachment) {
const auto& ss = ps.stencilState;
// Ben test
auto& front = depthState.front;
front.stencilCompare = getMetalCompareFunction(ss.front.stencilFunc);
front.stencilOperationStencilFail = getMetalStencilOperation(ss.front.stencilOpStencilFail);
@@ -2059,16 +2097,17 @@ void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
void MetalDriver::resetState(int) {
}
void MetalDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
std::lock_guard<std::mutex> const lock(mTickOpsLock);
mTickOps.push_back(fn);
}
void MetalDriver::executeTickOps() noexcept {
std::vector<std::function<void()>> ops;
mTickOpsLock.lock();
std::swap(ops, mTickOps);
mTickOpsLock.unlock();
for (const auto& f : ops) {
f();
}
@@ -257,10 +257,6 @@ void MetalSwapChain::present() {
}
}
#ifndef FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD
#define FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD 1
#endif
class PresentDrawableData {
public:
PresentDrawableData() = delete;
@@ -279,14 +275,10 @@ public:
[that->mDrawable present];
}
#if FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD == 1
// mDrawable is acquired on the driver thread. Typically, we would release this object on
// the same thread, but after receiving consistent crash reports from within
// [CAMetalDrawable dealloc], we suspect this object requires releasing on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{ cleanupAndDestroy(that); });
#else
that->mDriver->runAtNextTick([that]() { cleanupAndDestroy(that); });
#endif
}
private:
+1
View File
@@ -382,6 +382,7 @@ using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerS
using CullModeStateTracker = StateTracker<MTLCullMode>;
using WindingStateTracker = StateTracker<MTLWinding>;
using DepthClampStateTracker = StateTracker<MTLDepthClipMode>;
// Argument encoder
+9 -2
View File
@@ -202,6 +202,10 @@ bool NoopDriver::isProtectedTexturesSupported() {
return true;
}
bool NoopDriver::isDepthClampSupported() {
return false;
}
bool NoopDriver::isWorkaroundNeeded(Workaround) {
return false;
}
@@ -316,10 +320,10 @@ void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
}
void NoopDriver::insertEventMarker(char const* string, uint32_t len) {
void NoopDriver::insertEventMarker(char const* string) {
}
void NoopDriver::pushGroupMarker(char const* string, uint32_t len) {
void NoopDriver::pushGroupMarker(char const* string) {
}
void NoopDriver::popGroupMarker(int) {
@@ -388,4 +392,7 @@ void NoopDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
void NoopDriver::resetState(int) {
}
void NoopDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
}
} // namespace filament
@@ -676,6 +676,7 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
#ifndef __EMSCRIPTEN__
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
#endif
ext->EXT_depth_clamp = exts.has("GL_EXT_depth_clamp"sv);
ext->EXT_discard_framebuffer = exts.has("GL_EXT_discard_framebuffer"sv);
#ifndef __EMSCRIPTEN__
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
@@ -746,6 +747,7 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
ext->EXT_color_buffer_half_float = true; // Assumes core profile.
ext->EXT_clip_cull_distance = true;
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
ext->EXT_depth_clamp = true;
ext->EXT_discard_framebuffer = false;
ext->EXT_disjoint_timer_query = true;
ext->EXT_multisampled_render_to_texture = false;
+6 -4
View File
@@ -220,8 +220,9 @@ public:
bool EXT_color_buffer_float;
bool EXT_color_buffer_half_float;
bool EXT_debug_marker;
bool EXT_disjoint_timer_query;
bool EXT_depth_clamp;
bool EXT_discard_framebuffer;
bool EXT_disjoint_timer_query;
bool EXT_multisampled_render_to_texture2;
bool EXT_multisampled_render_to_texture;
bool EXT_protected_textures;
@@ -239,10 +240,10 @@ public:
bool KHR_parallel_shader_compile;
bool KHR_texture_compression_astc_hdr;
bool KHR_texture_compression_astc_ldr;
bool OES_depth_texture;
bool OES_depth24;
bool OES_packed_depth_stencil;
bool OES_EGL_image_external_essl3;
bool OES_depth24;
bool OES_depth_texture;
bool OES_packed_depth_stencil;
bool OES_rgb8_rgba8;
bool OES_standard_derivatives;
bool OES_texture_npot;
@@ -627,6 +628,7 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
#ifdef BACKEND_OPENGL_VERSION_GL
case GL_PROGRAM_POINT_SIZE: index = 10; break;
#endif
case GL_DEPTH_CLAMP: index = 11; break;
default: break;
}
assert_invariant(index < state.enables.caps.size());
+65 -15
View File
@@ -83,12 +83,20 @@
#define HAS_MAPBUFFERS 1
#endif
#define DEBUG_MARKER_NONE 0x00 // no debug marker
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_MARKER_ALL 0x03 // all markers
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_GROUP_MARKER_ALL 0x03 // all markers
// set to the desired debug marker level
#define DEBUG_MARKER_NONE 0x00 // no debug marker
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_MARKER_ALL 0x03 // all markers
// set to the desired debug marker level (for user markers [default: All])
#define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_ALL
// set to the desired debug level (for internal debugging [Default: None])
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
#if DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
@@ -196,11 +204,37 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
OpenGLDriver::DebugMarker::DebugMarker(OpenGLDriver& driver, const char* string) noexcept
: driver(driver) {
driver.pushGroupMarker(string, strlen(string));
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
}
#endif
#endif
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_BEGIN(string);
#endif
#endif
}
OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
driver.popGroupMarker();
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
glPopGroupMarkerEXT();
}
#endif
#endif
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_END();
#endif
#endif
}
// ------------------------------------------------------------------------------------------------
@@ -417,6 +451,14 @@ void OpenGLDriver::setRasterState(RasterState rs) noexcept {
} else {
gl.disable(GL_SAMPLE_ALPHA_TO_COVERAGE);
}
if (gl.ext.EXT_depth_clamp) {
if (rs.depthClamp) {
gl.enable(GL_DEPTH_CLAMP);
} else {
gl.disable(GL_DEPTH_CLAMP);
}
}
}
void OpenGLDriver::setStencilState(StencilState ss) noexcept {
@@ -2085,6 +2127,10 @@ bool OpenGLDriver::isProtectedTexturesSupported() {
return getContext().ext.EXT_protected_textures;
}
bool OpenGLDriver::isDepthClampSupported() {
return getContext().ext.EXT_depth_clamp;
}
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -2176,6 +2222,10 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
// Updating driver objects
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
uint32_t index, Handle<HwBufferObject> boh) {
DEBUG_MARKER()
@@ -3146,28 +3196,28 @@ void OpenGLDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
void OpenGLDriver::insertEventMarker(char const* string) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
auto& gl = mContext;
if (gl.ext.EXT_debug_marker) {
glInsertEventMarkerEXT(GLsizei(len ? len : strlen(string)), string);
glInsertEventMarkerEXT(GLsizei(strlen(string)), string);
}
#endif
#endif
}
void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
void OpenGLDriver::pushGroupMarker(char const* string) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
glPushGroupMarkerEXT(GLsizei(len ? len : strlen(string)), string);
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
}
#endif
#endif
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_BEGIN(string);
#endif
@@ -3177,14 +3227,14 @@ void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
void OpenGLDriver::popGroupMarker(int) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
glPopGroupMarkerEXT();
}
#endif
#endif
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_END();
#endif
+1 -1
View File
@@ -81,7 +81,7 @@ public:
const Platform::DriverConfig& driverConfig) noexcept;
class DebugMarker {
OpenGLDriver& driver;
UTILS_UNUSED OpenGLDriver& driver;
public:
DebugMarker(OpenGLDriver& driver, const char* string) noexcept;
~DebugMarker() noexcept;
@@ -26,16 +26,28 @@
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/Systrace.h>
#include <array>
#include <cctype>
#include <chrono>
#include <mutex>
#include <memory>
#include <string>
#include <string_view>
#include <thread>
#include <utility>
#include <variant>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
using namespace utils;
@@ -476,6 +488,12 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
// check status of program linking and shader compilation, logs error and free all resources
// in case of error.
bool const success = checkProgramStatus(token);
// Unless we have matdbg, we panic if a program is invalid. Otherwise, we'd get a UB.
// The compilation error has been logged to log.e by this point.
FILAMENT_CHECK_POSTCONDITION(FILAMENT_ENABLE_MATDBG || success)
<< "OpenGL program " << token->name.c_str_safe() << " failed to link or compile";
if (UTILS_LIKELY(success)) {
program = token->gl.program;
// no need to keep the shaders around
@@ -572,24 +590,38 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
// split shader source, so we can insert the specialization constants and the packing
// functions
auto const [prolog, body] = splitShaderSource({ shader_src, shader_len });
auto [version, prolog, body] = splitShaderSource({ shader_src, shader_len });
const std::array<const char*, 4> sources = {
prolog.data(),
specializationConstantString.c_str(),
packingFunctions.data(),
body.data()
// enable ESSL 3.10 if available
if (context.isAtLeastGLES<3, 1>()) {
version = "#version 310 es\n";
}
std::array<std::string_view, 5> sources = {
version,
prolog,
specializationConstantString,
packingFunctions,
{ body.data(), body.size() - 1 } // null-terminated
};
const std::array<GLint, 4> lengths = {
(GLint)prolog.length(),
(GLint)specializationConstantString.length(),
(GLint)packingFunctions.length(),
(GLint)body.length() - 1 // null terminated
};
// Some of the sources may be zero-length. Remove them as to avoid passing lengths of
// zero to glShaderSource(). glShaderSource should work with lengths of zero, but some
// drivers instead interpret zero as a sentinel for a null-terminated string.
auto partitionPoint = std::stable_partition(
sources.begin(), sources.end(), [](std::string_view s) { return !s.empty(); });
size_t count = std::distance(sources.begin(), partitionPoint);
std::array<const char*, 5> shaderStrings;
std::array<GLint, 5> lengths;
for (size_t i = 0; i < count; i++) {
shaderStrings[i] = sources[i].data();
lengths[i] = sources[i].size();
}
GLuint const shaderId = glCreateShader(glShaderType);
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
glShaderSource(shaderId, count, shaderStrings.data(), lengths.data());
glCompileShader(shaderId);
#ifndef NDEBUG
@@ -661,6 +693,7 @@ void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context,
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
// macOS doesn't support GL_ARB_shading_language_packing
// Also GLES3.0 didn't have the full set of packing/unpacking functions
std::string_view ShaderCompilerService::process_ARB_shading_language_packing(OpenGLContext& context) noexcept {
using namespace std::literals;
#ifdef BACKEND_OPENGL_VERSION_GL
@@ -700,31 +733,102 @@ highp uint packHalf2x16(vec2 v) {
highp uint y = fp32tou16(v.y);
return (y << 16u) | x;
}
highp uint packUnorm4x8(mediump vec4 v) {
v = round(clamp(v, 0.0, 1.0) * 255.0);
highp uint a = uint(v.x);
highp uint b = uint(v.y) << 8;
highp uint c = uint(v.z) << 16;
highp uint d = uint(v.w) << 24;
return (a|b|c|d);
}
highp uint packSnorm4x8(mediump vec4 v) {
v = round(clamp(v, -1.0, 1.0) * 127.0);
highp uint a = uint((int(v.x) & 0xff));
highp uint b = uint((int(v.y) & 0xff)) << 8;
highp uint c = uint((int(v.z) & 0xff)) << 16;
highp uint d = uint((int(v.w) & 0xff)) << 24;
return (a|b|c|d);
}
mediump vec4 unpackUnorm4x8(highp uint v) {
return vec4(float((v & 0x000000ffu) ),
float((v & 0x0000ff00u) >> 8),
float((v & 0x00ff0000u) >> 16),
float((v & 0xff000000u) >> 24)) / 255.0;
}
mediump vec4 unpackSnorm4x8(highp uint v) {
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
}
)"sv;
}
#endif // BACKEND_OPENGL_VERSION_GL
#ifdef BACKEND_OPENGL_VERSION_GLES
if (!context.isES2() && !context.isAtLeastGLES<3, 1>()) {
return R"(
highp uint packUnorm4x8(mediump vec4 v) {
v = round(clamp(v, 0.0, 1.0) * 255.0);
highp uint a = uint(v.x);
highp uint b = uint(v.y) << 8;
highp uint c = uint(v.z) << 16;
highp uint d = uint(v.w) << 24;
return (a|b|c|d);
}
highp uint packSnorm4x8(mediump vec4 v) {
v = round(clamp(v, -1.0, 1.0) * 127.0);
highp uint a = uint((int(v.x) & 0xff));
highp uint b = uint((int(v.y) & 0xff)) << 8;
highp uint c = uint((int(v.z) & 0xff)) << 16;
highp uint d = uint((int(v.w) & 0xff)) << 24;
return (a|b|c|d);
}
mediump vec4 unpackUnorm4x8(highp uint v) {
return vec4(float((v & 0x000000ffu) ),
float((v & 0x0000ff00u) >> 8),
float((v & 0x00ff0000u) >> 16),
float((v & 0xff000000u) >> 24)) / 255.0;
}
mediump vec4 unpackSnorm4x8(highp uint v) {
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
}
)"sv;
}
#endif // BACKEND_OPENGL_VERSION_GLES
return ""sv;
}
// split shader source code in two, the first section goes from the start to the line after the
// last #extension, and the 2nd part goes from there to the end.
std::array<std::string_view, 2> ShaderCompilerService::splitShaderSource(std::string_view source) noexcept {
auto start = source.find("#version");
assert_invariant(start != std::string_view::npos);
// split shader source code in three:
// - the version line
// - extensions
// - everything else
std::array<std::string_view, 3> ShaderCompilerService::splitShaderSource(std::string_view source) noexcept {
auto version_start = source.find("#version");
assert_invariant(version_start != std::string_view::npos);
auto pos = source.rfind("\n#extension");
if (pos == std::string_view::npos) {
pos = start;
auto version_eol = source.find('\n', version_start) + 1;
assert_invariant(version_eol != std::string_view::npos);
auto prolog_start = version_eol;
auto prolog_eol = source.rfind("\n#extension"); // last #extension line
if (prolog_eol == std::string_view::npos) {
prolog_eol = prolog_start;
} else {
++pos;
prolog_eol = source.find('\n', prolog_eol + 1) + 1;
}
auto body_start = prolog_eol;
auto eol = source.find('\n', pos) + 1;
assert_invariant(eol != std::string_view::npos);
std::string_view const version = source.substr(start, eol - start);
std::string_view const body = source.substr(version.length(), source.length() - version.length());
return { version, body };
std::string_view const version = source.substr(version_start, version_eol - version_start);
std::string_view const prolog = source.substr(prolog_start, prolog_eol - prolog_start);
std::string_view const body = source.substr(body_start, source.length() - body_start);
return { version, prolog, body };
}
/*
@@ -146,7 +146,7 @@ private:
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
static std::array<std::string_view, 2> splitShaderSource(std::string_view source) noexcept;
static std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept;
static GLuint linkProgram(OpenGLContext& context,
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
+6
View File
@@ -201,6 +201,12 @@ using namespace glext;
# define GL_CLIP_DISTANCE1 0x3001
#endif
#if defined(GL_EXT_depth_clamp)
# define GL_DEPTH_CLAMP GL_DEPTH_CLAMP_EXT
#else
# define GL_DEPTH_CLAMP 0x864F
#endif
#if defined(GL_KHR_debug)
# define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
# define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
@@ -125,6 +125,10 @@ public:
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
}
inline bool isDepthClampSupported() const noexcept {
return mPhysicalDeviceFeatures.depthClamp == VK_TRUE;
}
inline bool isDebugMarkersSupported() const noexcept {
return mDebugMarkersSupported;
}
+12 -3
View File
@@ -944,6 +944,10 @@ bool VulkanDriver::isProtectedTexturesSupported() {
return false;
}
bool VulkanDriver::isDepthClampSupported() {
return mContext.isDepthClampSupported();
}
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU: {
@@ -1602,13 +1606,13 @@ void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
value);
}
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
void VulkanDriver::insertEventMarker(char const* string) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.insertEventMarker(string, len);
mCommands.insertEventMarker(string, strlen(string));
#endif
}
void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
void VulkanDriver::pushGroupMarker(char const* string) {
// Turns out all the markers are 0-terminated, so we can just pass it without len.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.pushGroupMarker(string);
@@ -1816,6 +1820,7 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha),
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
.rasterizationSamples = rt->getSamples(),
.depthClamp = rasterState.depthClamp,
.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass),
.colorBlendOp = rasterState.blendEquationRGB,
.alphaBlendOp = rasterState.blendEquationAlpha,
@@ -2032,6 +2037,10 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
void VulkanDriver::resetState(int) {
}
void VulkanDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mResourceAllocator.associateHandle(handleId, std::move(tag));
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<VulkanDriver>;
@@ -16,7 +16,6 @@
#include "VulkanHandles.h"
#include "VulkanDriver.h"
#include "VulkanConstants.h"
#include "VulkanDriver.h"
#include "VulkanMemory.h"
@@ -184,6 +184,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
vkRaster.polygonMode = VK_POLYGON_MODE_FILL;
vkRaster.cullMode = raster.cullMode;
vkRaster.frontFace = raster.frontFace;
vkRaster.depthClampEnable = raster.depthClamp;
vkRaster.depthBiasEnable = raster.depthBiasEnable;
vkRaster.depthBiasConstantFactor = raster.depthBiasConstantFactor;
vkRaster.depthBiasClamp = 0.0f;
@@ -90,7 +90,8 @@ public:
VkBlendFactor srcAlphaBlendFactor : 5;
VkBlendFactor dstAlphaBlendFactor : 5;
VkColorComponentFlags colorWriteMask : 4;
uint8_t rasterizationSamples; // offset = 4 bytes
uint8_t rasterizationSamples : 4;// offset = 4 bytes
uint8_t depthClamp : 4;
uint8_t colorTargetCount; // offset = 5 bytes
BlendEquation colorBlendOp : 4; // offset = 6 bytes
BlendEquation alphaBlendOp : 4;
@@ -105,6 +105,10 @@ public:
mHandleAllocatorImpl.deallocate(handle, obj);
}
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
}
private:
AllocatorImpl mHandleAllocatorImpl;
+7 -10
View File
@@ -97,13 +97,11 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.extent.depth = 1;
}
// Filament expects blit() to work with any texture, so we almost always set these usage flags.
// TODO: investigate performance implications of setting these flags.
constexpr VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::BLIT_SRC))) {
imageInfo.usage |= blittable;
if (any(usage & TextureUsage::BLIT_SRC)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & TextureUsage::BLIT_DST)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
if (any(usage & TextureUsage::SAMPLEABLE)) {
@@ -121,7 +119,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | blittable;
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
imageInfo.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
@@ -130,10 +128,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
if (any(usage & TextureUsage::UPLOADABLE)) {
imageInfo.usage |= blittable;
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
imageInfo.usage |= blittable;
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
// Depth resolves uses a custom shader and therefore needs to be sampleable.
@@ -172,7 +172,7 @@ public:
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Failed to allocate descriptor set code=" << result << " size=" << mSize
<< " capacity=" << mCapacity << " count=" << "%s";
<< " capacity=" << mCapacity << " count=" << mUnusedCount;
mSize++;
return createSet(layout->bitmask, vkSet);
}
@@ -325,6 +325,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
// We could simply enable all supported features, but since that may have performance
// consequences let's just enable the features we need.
VkPhysicalDeviceFeatures enabledFeatures{
.depthClamp = features.depthClamp,
.samplerAnisotropy = features.samplerAnisotropy,
.textureCompressionETC2 = features.textureCompressionETC2,
.textureCompressionBC = features.textureCompressionBC,
+16 -1
View File
@@ -54,7 +54,7 @@ public:
using BufferDescriptor = backend::BufferDescriptor;
using BindingType = backend::BufferObjectBinding;
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -78,6 +78,21 @@ public:
*/
Builder& bindingType(BindingType bindingType) noexcept;
/**
* Associate an optional name with this BufferObject for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this BufferObject
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
+23
View File
@@ -896,6 +896,29 @@ public:
/** Tells whether an InstanceBuffer object is valid */
bool isValid(const InstanceBuffer* UTILS_NULLABLE p) const;
/**
* Retrieve the count of each resource tracked by Engine.
* This is intended for debugging.
* @{
*/
size_t getBufferObjectCount() const noexcept;
size_t getViewCount() const noexcept;
size_t getSceneCount() const noexcept;
size_t getSwapChainCount() const noexcept;
size_t getStreamCount() const noexcept;
size_t getIndexBufferCount() const noexcept;
size_t getSkinningBufferCount() const noexcept;
size_t getMorphTargetBufferCount() const noexcept;
size_t getInstanceBufferCount() const noexcept;
size_t getVertexBufferCount() const noexcept;
size_t getIndirectLightCount() const noexcept;
size_t getMaterialCount() const noexcept;
size_t getTextureCount() const noexcept;
size_t getSkyboxeCount() const noexcept;
size_t getColorGradingCount() const noexcept;
size_t getRenderTargetCount() const noexcept;
/** @} */
/**
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
* all commands to this point are executed. Note that does guarantee that the
+18
View File
@@ -19,6 +19,7 @@
#include <utils/compiler.h>
#include <utils/PrivateImplementation.h>
#include <utils/CString.h>
#include <stddef.h>
@@ -54,6 +55,23 @@ public:
template<typename T>
using BuilderBase = utils::PrivateImplementation<T>;
// This needs to be public because it is used in the following template.
UTILS_PUBLIC void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept;
template <typename Builder>
class UTILS_PUBLIC BuilderNameMixin {
public:
Builder& name(const char* name, size_t len) noexcept {
builderMakeName(mName, name, len);
return static_cast<Builder&>(*this);
}
utils::CString const& getName() const noexcept { return mName; }
private:
utils::CString mName;
};
} // namespace filament
#endif // TNT_FILAMENT_FILAMENTAPI_H
+16 -1
View File
@@ -59,7 +59,7 @@ public:
UINT = uint8_t(backend::ElementType::UINT), //!< 32-bit indices
};
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -83,6 +83,21 @@ public:
*/
Builder& bufferType(IndexType indexType) noexcept;
/**
* Associate an optional name with this IndexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this IndexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
+16 -1
View File
@@ -38,7 +38,7 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
@@ -70,6 +70,21 @@ public:
*/
Builder& localTransforms(math::mat4f const* UTILS_NULLABLE localTransforms) noexcept;
/**
* Associate an optional name with this InstanceBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this InstanceBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the InstanceBuffer object and returns a pointer to it.
*/
+16 -1
View File
@@ -39,7 +39,7 @@ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -63,6 +63,21 @@ public:
*/
Builder& count(size_t count) noexcept;
/**
* Associate an optional name with this MorphTargetBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this MorphTargetBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the MorphTargetBuffer object and returns a pointer to it.
*
+16 -1
View File
@@ -39,7 +39,7 @@ class UTILS_PUBLIC SkinningBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -69,6 +69,21 @@ public:
*/
Builder& initialize(bool initialize = true) noexcept;
/**
* Associate an optional name with this SkinningBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this SkinningBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the SkinningBuffer object and returns a pointer to it.
*
+16 -1
View File
@@ -94,7 +94,7 @@ public:
*
* To create a NATIVE stream, call the <pre>stream</pre> method on the builder.
*/
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -136,6 +136,21 @@ public:
*/
Builder& height(uint32_t height) noexcept;
/**
* Associate an optional name with this Stream for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this Stream
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the Stream object and returns a pointer to it.
*
+16 -1
View File
@@ -112,7 +112,7 @@ public:
//! Use Builder to construct a Texture object instance
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -202,6 +202,21 @@ public:
*/
Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept;
/**
* Associate an optional name with this Texture for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this Texture
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the Texture object and returns a pointer to it.
*
+16 -1
View File
@@ -61,7 +61,7 @@ public:
using AttributeType = backend::ElementType;
using BufferDescriptor = backend::BufferDescriptor;
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -158,6 +158,21 @@ public:
*/
Builder& advancedSkinning(bool enabled) noexcept;
/**
* Associate an optional name with this VertexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this VertexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the VertexBuffer object and returns a pointer to it.
*
+2 -1
View File
@@ -24,6 +24,7 @@
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/FixedCapacityVector.h>
#include <math/mathfwd.h>
@@ -727,7 +728,7 @@ public:
void setDebugCamera(Camera* UTILS_NULLABLE camera) noexcept;
//! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing.
Camera const* UTILS_NULLABLE getDirectionalShadowCamera() const noexcept;
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
/** Result of a picking query */
+65
View File
@@ -268,6 +268,71 @@ bool Engine::isValid(const InstanceBuffer* p) const {
return downcast(this)->isValid(downcast(p));
}
size_t Engine::getBufferObjectCount() const noexcept {
return downcast(this)->getBufferObjectCount();
}
size_t Engine::getViewCount() const noexcept {
return downcast(this)->getViewCount();
}
size_t Engine::getSceneCount() const noexcept {
return downcast(this)->getSceneCount();
}
size_t Engine::getSwapChainCount() const noexcept {
return downcast(this)->getSwapChainCount();
}
size_t Engine::getStreamCount() const noexcept {
return downcast(this)->getStreamCount();
}
size_t Engine::getIndexBufferCount() const noexcept {
return downcast(this)->getIndexBufferCount();
}
size_t Engine::getSkinningBufferCount() const noexcept {
return downcast(this)->getSkinningBufferCount();
}
size_t Engine::getMorphTargetBufferCount() const noexcept {
return downcast(this)->getMorphTargetBufferCount();
}
size_t Engine::getInstanceBufferCount() const noexcept {
return downcast(this)->getInstanceBufferCount();
}
size_t Engine::getVertexBufferCount() const noexcept {
return downcast(this)->getVertexBufferCount();
}
size_t Engine::getIndirectLightCount() const noexcept {
return downcast(this)->getIndirectLightCount();
}
size_t Engine::getMaterialCount() const noexcept {
return downcast(this)->getMaterialCount();
}
size_t Engine::getTextureCount() const noexcept {
return downcast(this)->getTextureCount();
}
size_t Engine::getSkyboxeCount() const noexcept {
return downcast(this)->getSkyboxeCount();
}
size_t Engine::getColorGradingCount() const noexcept {
return downcast(this)->getColorGradingCount();
}
size_t Engine::getRenderTargetCount() const noexcept {
return downcast(this)->getRenderTargetCount();
}
void Engine::flushAndWait() {
downcast(this)->flushAndWait();
}
+31
View File
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <filament/FilamentAPI.h>
#include <algorithm>
namespace filament {
void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept {
if (!name) {
return;
}
size_t const length = std::min(len, size_t { 128u });
outName = utils::CString(name, length);
}
} // namespace filament
+26 -19
View File
@@ -25,6 +25,7 @@
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Systrace.h>
#include <algorithm>
#include <atomic>
@@ -78,25 +79,33 @@ void FrameInfoManager::beginFrame(DriverApi& driver, Config const& config, uint3
});
// now is a good time to check the oldest active query
uint64_t elapsed = 0;
TimerQueryResult const result = driver.getTimerQueryValue(mQueries[mLast].handle, &elapsed);
switch (result) {
case TimerQueryResult::NOT_READY:
// nothing to do
break;
case TimerQueryResult::ERROR:
mLast = (mLast + 1) % POOL_COUNT;
break;
case TimerQueryResult::AVAILABLE:
// conversion to our duration happens here
pFront = mQueries[mLast].pInfo;
pFront->frameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
mLast = (mLast + 1) % POOL_COUNT;
denoiseFrameTime(config);
while (mLast != mIndex) {
uint64_t elapsed = 0;
TimerQueryResult const result = driver.getTimerQueryValue(mQueries[mLast].handle, &elapsed);
switch (result) {
case TimerQueryResult::NOT_READY:
// nothing to do
break;
case TimerQueryResult::ERROR:
mLast = (mLast + 1) % POOL_COUNT;
break;
case TimerQueryResult::AVAILABLE: {
SYSTRACE_CONTEXT();
SYSTRACE_VALUE32("FrameInfo::elapsed", uint32_t(elapsed));
// conversion to our duration happens here
pFront = mQueries[mLast].pInfo;
pFront->frameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
mLast = (mLast + 1) % POOL_COUNT;
denoiseFrameTime(history, config);
break;
}
}
if (result != TimerQueryResult::AVAILABLE) {
break;
}
// read the pending timer queries until we find one that's not ready
}
// keep this just for debugging
if constexpr (false) {
using namespace utils;
@@ -129,15 +138,13 @@ void FrameInfoManager::endFrame(DriverApi& driver) noexcept {
mIndex = (mIndex + 1) % POOL_COUNT;
}
void FrameInfoManager::denoiseFrameTime(Config const& config) noexcept {
auto& history = mFrameTimeHistory;
void FrameInfoManager::denoiseFrameTime(FrameHistoryQueue& history, Config const& config) noexcept {
assert_invariant(!history.empty());
// find the first slot that has a valid frame duration
size_t first = history.size();
for (size_t i = 0, c = history.size(); i < c; ++i) {
if (history[i].frameTime != duration(0)) {
assert_invariant(std::addressof(history[i]) == pFront);
first = i;
break;
}
+3 -2
View File
@@ -171,7 +171,8 @@ public:
utils::FixedCapacityVector<Renderer::FrameInfo> getFrameInfoHistory(size_t historySize) const noexcept;
private:
void denoiseFrameTime(Config const& config) noexcept;
using FrameHistoryQueue = CircularQueue<FrameInfoImpl, MAX_FRAMETIME_HISTORY>;
static void denoiseFrameTime(FrameHistoryQueue& history, Config const& config) noexcept;
struct Query {
backend::Handle<backend::HwTimerQuery> handle{};
FrameInfoImpl* pInfo = nullptr;
@@ -180,7 +181,7 @@ private:
uint32_t mIndex = 0; // index of current query
uint32_t mLast = 0; // index of oldest query still active
FrameInfoImpl* pFront = nullptr; // the most recent slot with a valid frame time
CircularQueue<FrameInfoImpl, MAX_FRAMETIME_HISTORY> mFrameTimeHistory;
FrameHistoryQueue mFrameTimeHistory;
};
+25 -18
View File
@@ -16,16 +16,22 @@
#include "FrameSkipper.h"
#include <utils/Log.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <algorithm>
#include <stddef.h>
namespace filament {
using namespace utils;
using namespace backend;
FrameSkipper::FrameSkipper(size_t latency) noexcept
: mLast(latency - 1) {
: mLast(std::max(latency, MAX_FRAME_LATENCY) - 1) {
assert_invariant(latency <= MAX_FRAME_LATENCY);
}
@@ -41,31 +47,32 @@ void FrameSkipper::terminate(DriverApi& driver) noexcept {
bool FrameSkipper::beginFrame(DriverApi& driver) noexcept {
auto& fences = mDelayedFences;
auto fence = fences.front();
if (fence) {
auto status = driver.getFenceStatus(fence);
if (status == FenceStatus::TIMEOUT_EXPIRED) {
// Sync not ready, skip frame
if (fences.front()) {
// Do we have a latency old fence?
auto status = driver.getFenceStatus(fences.front());
if (UTILS_UNLIKELY(status == FenceStatus::TIMEOUT_EXPIRED)) {
// The fence hasn't signaled yet, skip this frame
return false;
}
assert_invariant(status == FenceStatus::CONDITION_SATISFIED);
driver.destroyFence(fence);
}
// shift all fences down by 1
std::move(fences.begin() + 1, fences.end(), fences.begin());
fences.back() = {};
return true;
}
void FrameSkipper::endFrame(DriverApi& driver) noexcept {
// If the user produced a new frame despite the fact that the previous one wasn't finished
// (i.e. FrameSkipper::beginFrame() returned false), we need to make sure to replace
// a fence that might be here already)
auto& fence = mDelayedFences[mLast];
if (fence) {
driver.destroyFence(fence);
auto& fences = mDelayedFences;
size_t const last = mLast;
// pop the oldest fence and advance the other ones
if (fences.front()) {
driver.destroyFence(fences.front());
}
fence = driver.createFence();
std::move(fences.begin() + 1, fences.end(), fences.begin());
// add a new fence to the end
assert_invariant(!fences[last]);
fences[last] = driver.createFence();
}
} // namespace filament
+4 -1
View File
@@ -22,6 +22,9 @@
#include <array>
#include <stddef.h>
#include <stdint.h>
namespace filament {
/*
@@ -60,7 +63,7 @@ public:
private:
using Container = std::array<backend::Handle<backend::HwFence>, MAX_FRAME_LATENCY>;
mutable Container mDelayedFences{};
size_t mLast;
uint8_t const mLast;
};
} // namespace filament
+6
View File
@@ -354,6 +354,12 @@ bool MaterialParser::getSpecularAntiAliasingThreshold(float* value) const noexce
return mImpl.getFromSimpleChunk(ChunkType::MaterialSpecularAntiAliasingThreshold, value);
}
bool MaterialParser::getStereoscopicType(backend::StereoscopicType* value) const noexcept {
static_assert(sizeof(StereoscopicType) == sizeof(uint8_t),
"StereoscopicType expected size is wrong");
return mImpl.getFromSimpleChunk(ChunkType::MaterialStereoscopicType, reinterpret_cast<uint8_t*>(value));
}
bool MaterialParser::getRequiredAttributes(AttributeBitset* value) const noexcept {
uint32_t rawAttributes = 0;
if (!mImpl.getFromSimpleChunk(ChunkType::MaterialRequiredAttributes, &rawAttributes)) {
+1
View File
@@ -122,6 +122,7 @@ public:
bool hasSpecularAntiAliasing(bool* value) const noexcept;
bool getSpecularAntiAliasingVariance(float* value) const noexcept;
bool getSpecularAntiAliasingThreshold(float* value) const noexcept;
bool getStereoscopicType(backend::StereoscopicType*) const noexcept;
bool getShader(filaflat::ShaderContent& shader, backend::ShaderModel shaderModel,
Variant variant, backend::ShaderStage stage) noexcept;
+3 -1
View File
@@ -459,10 +459,12 @@ void PerViewUniforms::bind(backend::DriverApi& driver) noexcept {
void PerViewUniforms::unbindSamplers() noexcept {
auto& samplerGroup = mSamplers;
samplerGroup.clearSampler(PerViewSib::SHADOW_MAP);
samplerGroup.clearSampler(PerViewSib::IBL_SPECULAR);
samplerGroup.clearSampler(PerViewSib::SSAO);
samplerGroup.clearSampler(PerViewSib::SSR);
samplerGroup.clearSampler(PerViewSib::STRUCTURE);
samplerGroup.clearSampler(PerViewSib::SHADOW_MAP);
samplerGroup.clearSampler(PerViewSib::FOG);
}
} // namespace filament
+14 -6
View File
@@ -494,7 +494,6 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
struct StructureMipmapData {
FrameGraphId<FrameGraphTexture> depth;
uint32_t rt[8];
};
fg.addPass<StructureMipmapData>("StructureMipmap",
@@ -505,7 +504,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
.level = uint8_t(i)
});
out = builder.write(out, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
data.rt[i - 1] = builder.declareRenderPass("Structure mip target", {
builder.declareRenderPass("Structure mip target", {
.attachments = { .depth = out }
});
}
@@ -2946,6 +2945,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
1.0f / outputDesc.height});
}
if (blitterNames[index] == "blitLow") {
mi->setParameter("levelOfDetail", 0.0f);
}
mi->setParameter("viewport", float4{
(float)vp.left / inputDesc.width,
(float)vp.bottom / inputDesc.height,
@@ -3004,9 +3007,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
SamplerMagFilter filterMag,
SamplerMinFilter filterMin) noexcept {
// TODO: add support for sub-resources
assert_invariant(fg.getSubResourceDescriptor(input).layer == 0);
assert_invariant(fg.getSubResourceDescriptor(input).level == 0);
uint32_t const layer = fg.getSubResourceDescriptor(input).layer;
float const levelOfDetail = fg.getSubResourceDescriptor(input).level;
struct QuadBlitData {
FrameGraphId<FrameGraphTexture> input;
@@ -3032,7 +3034,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
// --------------------------------------------------------------------------------
// set uniforms
PostProcessMaterial const& material = getPostProcessMaterial("blitLow");
PostProcessMaterial const& material =
getPostProcessMaterial(layer ? "blitArray" : "blitLow");
auto* mi = material.getMaterialInstance(mEngine);
mi->setParameter("color", color, {
.filterMag = filterMag,
@@ -3044,6 +3047,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->setParameter("levelOfDetail", levelOfDetail);
if (layer) {
mi->setParameter("layerIndex", layer);
}
mi->commit(driver);
mi->use(driver);
@@ -3186,6 +3193,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
assert_invariant(dst);
assert_invariant(srcDesc.format == dstDesc.format);
assert_invariant(srcDesc.width == dstDesc.width && srcDesc.height == dstDesc.height);
assert_invariant(srcDesc.samples > 1 && dstDesc.samples <= 1);
driver.resolve(
dst, dstSubDesc.level, dstSubDesc.layer,
src, srcSubDesc.level, srcSubDesc.layer);
+41 -31
View File
@@ -118,10 +118,11 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
uint32_t const customCommandCount =
builder.mCustomCommands.has_value() ? builder.mCustomCommands->size() : 0;
Command* const curr = builder.mArena.alloc<Command>(commandCount + customCommandCount);
assert_invariant(curr);
Command* const commandBegin = builder.mArena.alloc<Command>(commandCount + customCommandCount);
Command* commandEnd = commandBegin + (commandCount + customCommandCount);
assert_invariant(commandBegin);
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(curr))) {
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(commandBegin))) {
static bool sLogOnce = true;
if (UTILS_UNLIKELY(sLogOnce)) {
sLogOnce = false;
@@ -130,10 +131,7 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
}
}
mCommandBegin = curr;
mCommandEnd = curr + commandCount + customCommandCount;
appendCommands(engine, { curr, commandCount },
appendCommands(engine, { commandBegin, commandCount },
builder.mUboHandle,
builder.mVisibleRenderables,
builder.mCommandTypeFlags,
@@ -144,32 +142,36 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
builder.mCameraForwardVector);
if (builder.mCustomCommands.has_value()) {
Command* p = curr + commandCount;
Command* p = commandBegin + commandCount;
for (auto [channel, passId, command, order, fn]: builder.mCustomCommands.value()) {
appendCustomCommand(p++, channel, passId, command, order, fn);
}
}
// sort commands once we're done adding commands
sortCommands(builder.mArena);
commandEnd = resize(builder.mArena,
RenderPass::sortCommands(commandBegin, commandEnd));
if (engine.isAutomaticInstancingEnabled()) {
int32_t stereoscopicEyeCount = 1;
if (builder.mFlags & IS_INSTANCED_STEREOSCOPIC) {
stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount;
}
instanceify(engine, builder.mArena, stereoscopicEyeCount);
commandEnd = resize(builder.mArena,
instanceify(engine, commandBegin, commandEnd, stereoscopicEyeCount));
}
// these are `const` from this point on...
mCommandBegin = commandBegin;
mCommandEnd = commandEnd;
}
// this destructor is actually heavy because it inlines ~vector<>
RenderPass::~RenderPass() noexcept = default;
void RenderPass::resize(Arena& arena, size_t count) noexcept {
if (mCommandBegin) {
mCommandEnd = mCommandBegin + count;
arena.rewind(mCommandEnd);
}
RenderPass::Command* RenderPass::resize(Arena& arena, Command* const last) noexcept {
arena.rewind(last);
return last;
}
void RenderPass::appendCommands(FEngine& engine,
@@ -260,18 +262,19 @@ void RenderPass::appendCustomCommand(Command* commands,
commands->key = cmd;
}
void RenderPass::sortCommands(Arena& arena) noexcept {
SYSTRACE_NAME("sort and trim commands");
RenderPass::Command* RenderPass::sortCommands(
Command* const begin, Command* const end) noexcept {
SYSTRACE_NAME("sort commands");
std::sort(mCommandBegin, mCommandEnd);
std::sort(begin, end);
// find the last command
Command const* const last = std::partition_point(mCommandBegin, mCommandEnd,
Command* const last = std::partition_point(begin, end,
[](Command const& c) {
return c.key != uint64_t(Pass::SENTINEL);
});
resize(arena, uint32_t(last - mCommandBegin));
return last;
}
void RenderPass::execute(RenderPass const& pass,
@@ -284,7 +287,9 @@ void RenderPass::execute(RenderPass const& pass,
driver.endRenderPass();
}
void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept {
RenderPass::Command* RenderPass::instanceify(FEngine& engine,
Command* curr, Command* const last,
int32_t eyeCount) const noexcept {
SYSTRACE_NAME("instanceify");
// instanceify works by scanning the **sorted** command stream, looking for repeat draw
@@ -297,14 +302,12 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) no
UTILS_UNUSED uint32_t drawCallsSavedCount = 0;
Command* curr = mCommandBegin;
Command* const last = mCommandEnd;
Command* firstSentinel = nullptr;
PerRenderableData const* uboData = nullptr;
PerRenderableData* stagingBuffer = nullptr;
uint32_t stagingBufferSize = 0;
uint32_t instancedPrimitiveOffset = 0;
size_t const count = last - curr;
// TODO: for the case of instancing we could actually use 128 instead of 64 instances
constexpr size_t maxInstanceCount = CONFIG_MAX_INSTANCES;
@@ -343,7 +346,6 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) no
if (UTILS_UNLIKELY(!stagingBuffer)) {
// create a temporary UBO for instancing
size_t const count = mCommandEnd - mCommandBegin;
mInstancedUboHandle = BufferObjectSharedHandle{
engine.getDriverApi().createBufferObject(
count * sizeof(PerRenderableData) + sizeof(PerRenderableUib),
@@ -385,7 +387,7 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) no
if (UTILS_UNLIKELY(firstSentinel)) {
//slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of "
// << mCommandEnd - mCommandBegin << io::endl;
// << count << io::endl;
// we have instanced primitives
DriverApi& driver = engine.getDriverApi();
@@ -401,14 +403,15 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) no
stagingBuffer = nullptr;
// remove all the canceled commands
auto lastCommand = std::remove_if(firstSentinel, mCommandEnd, [](auto const& command) {
auto lastCommand = std::remove_if(firstSentinel, last, [](auto const& command) {
return command.key == uint64_t(Pass::SENTINEL);
});
resize(arena, uint32_t(lastCommand - mCommandBegin));
return lastCommand;
}
assert_invariant(stagingBuffer == nullptr);
return last;
}
@@ -416,7 +419,8 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) no
UTILS_ALWAYS_INLINE // This function exists only to make the code more readable. we want it inlined.
inline // and we don't need it in the compilation unit
void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
FMaterialInstance const* const UTILS_RESTRICT mi, bool inverseFrontFaces) noexcept {
FMaterialInstance const* const UTILS_RESTRICT mi,
bool inverseFrontFaces, bool hasDepthClamp) noexcept {
FMaterial const * const UTILS_RESTRICT ma = mi->getMaterial();
variant = Variant::filterVariant(variant, ma->isVariantLit());
@@ -457,6 +461,7 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
cmdDraw.info.rasterState.colorWrite = mi->isColorWriteEnabled();
cmdDraw.info.rasterState.depthWrite = mi->isDepthWriteEnabled();
cmdDraw.info.rasterState.depthFunc = mi->getDepthFunc();
cmdDraw.info.rasterState.depthClamp = hasDepthClamp;
cmdDraw.info.materialVariant = variant;
// we keep "RasterState::colorWrite" to the value set by material (could be disabled)
}
@@ -555,6 +560,9 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
bool const hasInstancedStereo =
renderFlags & IS_INSTANCED_STEREOSCOPIC;
bool const hasDepthClamp =
renderFlags & HAS_DEPTH_CLAMP;
float const cameraPositionDotCameraForward = dot(cameraPosition, cameraForward);
auto const* const UTILS_RESTRICT soaWorldAABBCenter = soa.data<FScene::WORLD_AABB_CENTER>();
@@ -574,6 +582,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.rasterState.depthWrite = true;
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
cmd.info.rasterState.alphaToCoverage = false;
cmd.info.rasterState.depthClamp = hasDepthClamp;
}
for (uint32_t i = range.first; i < range.last; ++i) {
@@ -607,7 +616,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
// Here, objects close to the camera (but behind) will be drawn first.
// An alternative that keeps the mathematical ordering is given here:
// distanceBits ^= ((int32_t(distanceBits) >> 31) | 0x80000000u);
float const distance = -dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward;
float const distance = -(dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward);
uint32_t const distanceBits = reinterpret_cast<uint32_t const&>(distance);
// calculate the per-primitive face winding order inversion
@@ -688,7 +697,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.morphingOffset = primitive.getMorphingBufferOffset();
if constexpr (isColorPass) {
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
RenderPass::setupColorCommand(cmd, renderableVariant, mi,
inverseFrontFaces, hasDepthClamp);
const bool blendPass = Pass(cmd.key & PASS_MASK) == Pass::BLENDED;
if (blendPass) {
// TODO: at least for transparent objects, AABB should be per primitive
+14 -8
View File
@@ -284,6 +284,7 @@ public:
static constexpr RenderFlags HAS_SHADOWING = 0x01;
static constexpr RenderFlags HAS_INVERSE_FRONT_FACES = 0x02;
static constexpr RenderFlags IS_INSTANCED_STEREOSCOPIC = 0x04;
static constexpr RenderFlags HAS_DEPTH_CLAMP = 0x08;
// Arena used for commands
using Arena = utils::Arena<
@@ -408,13 +409,16 @@ private:
uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
Executor::CustomCommandFn command);
void resize(Arena& arena, size_t count) noexcept;
static Command* resize(Arena& arena, Command* const last) noexcept;
// sorts commands then trims sentinels
void sortCommands(Arena& arena) noexcept;
static Command* sortCommands(
Command* begin, Command* end) noexcept;
// instanceify commands then trims sentinels
void instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept;
RenderPass::Command* instanceify(FEngine& engine,
Command* begin, Command* end,
int32_t eyeCount) const noexcept;
// We choose the command count per job to minimize JobSystem overhead.
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 128;
@@ -441,17 +445,17 @@ private:
uint8_t instancedStereoEyeCount) noexcept;
static void setupColorCommand(Command& cmdDraw, Variant variant,
FMaterialInstance const* mi, bool inverseFrontFaces) noexcept;
FMaterialInstance const* mi, bool inverseFrontFaces, bool hasDepthClamp) noexcept;
static void updateSummedPrimitiveCounts(
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
FScene::RenderableSoa const& mRenderableSoa;
backend::Viewport const mScissorViewport;
Command* mCommandBegin = nullptr; // Pointer to the first command
Command* mCommandEnd = nullptr; // Pointer to one past the last command
Command const* /* const */ mCommandBegin = nullptr; // Pointer to the first command
Command const* /* const */ mCommandEnd = nullptr; // Pointer to one past the last command
// a UBO for instanced primitives
BufferObjectSharedHandle mInstancedUboHandle;
mutable BufferObjectSharedHandle mInstancedUboHandle;
// a vector for our custom commands
using CustomCommandVector = std::vector<Executor::CustomCommandFn,
utils::STLAllocator<Executor::CustomCommandFn, LinearAllocatorArena>>;
@@ -525,7 +529,9 @@ public:
// like above but allows to set specific flags
RenderPassBuilder& renderFlags(
RenderPass::RenderFlags mask, RenderPass::RenderFlags value) noexcept {
mFlags = (mFlags & mask) | (value & mask);
value &= mask;
mFlags &= ~mask;
mFlags |= value;
return *this;
}
+43 -45
View File
@@ -40,6 +40,7 @@
#include <utils/Panic.h>
#include <algorithm>
#include <optional>
#include <utility>
#include <stddef.h>
@@ -50,8 +51,9 @@ namespace filament {
using namespace backend;
using namespace math;
FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
RendererUtils::ColorPassOutput RendererUtils::colorPass(
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
ColorPassInput const& colorPassInput,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor passExecutor) noexcept {
@@ -67,31 +69,28 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
FrameGraphId<FrameGraphTexture> structure;
};
Blackboard& blackboard = fg.getBlackboard();
auto& colorPass = fg.addPass<ColorPassData>(name,
[&](FrameGraph::Builder& builder, ColorPassData& data) {
TargetBufferFlags const clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
uint8_t layerCount = 1;
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
data.color = blackboard.get<FrameGraphTexture>("color");
data.depth = blackboard.get<FrameGraphTexture>("depth");
data.color = colorPassInput.linearColor;
data.depth = colorPassInput.depth;
data.shadows = colorPassInput.shadows;
data.ssao = colorPassInput.ssao;
// Screen-space reflection or refractions
if (config.hasScreenSpaceReflectionsOrRefractions) {
data.ssr = blackboard.get<FrameGraphTexture>("ssr");
data.ssr = colorPassInput.ssr;
if (data.ssr) {
data.ssr = builder.sample(data.ssr);
}
}
if (config.hasContactShadows) {
data.structure = blackboard.get<FrameGraphTexture>("structure");
data.structure = colorPassInput.structure;
assert_invariant(data.structure);
data.structure = builder.sample(data.structure);
}
@@ -154,6 +153,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
}
if (colorGradingConfig.asSubpass) {
assert_invariant(config.msaa <= 1);
assert_invariant(colorBufferDesc.samples <= 1);
data.output = builder.createTexture("Tonemapped Buffer", {
.width = colorBufferDesc.width,
.height = colorBufferDesc.height,
@@ -172,9 +173,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
if (view.hasStereo() && engine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW) {
layerCount = engine.getConfig().stereoscopicEyeCount;
}
/*
* There is a bit of magic happening here regarding the viewport used.
@@ -196,9 +194,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
.stencil = data.stencil },
.clearColor = config.clearColor,
.samples = config.msaa,
.layerCount = layerCount,
.layerCount = static_cast<uint8_t>(colorBufferDesc.depth),
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
blackboard["depth"] = data.depth;
},
[=, passExecutor = std::move(passExecutor), &view, &engine](FrameGraphResources const& resources,
ColorPassData const& data, DriverApi& driver) {
@@ -264,24 +261,21 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
}
);
// when color grading is done as a subpass, the output of the color-pass is the ldr buffer
auto output = colorGradingConfig.asSubpass ? colorPass->output : colorPass->color;
blackboard["color"] = output;
return output;
return {
.linearColor = colorPass->color,
.tonemappedColor = colorPass->output, // can be null
.depth = colorPass->depth
};
}
std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
std::optional<RendererUtils::ColorPassOutput> RendererUtils::refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassInput colorPassInput,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass const& pass) noexcept {
auto& blackboard = fg.getBlackboard();
auto input = blackboard.get<FrameGraphTexture>("color");
FrameGraphId<FrameGraphTexture> output;
// find the first refractive object in channel 2
RenderPass::Command const* const refraction = std::partition_point(pass.begin(), pass.end(),
[](auto const& command) {
@@ -296,34 +290,35 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
// if there wasn't any refractive object, just skip everything below.
if (UTILS_UNLIKELY(hasScreenSpaceRefraction)) {
PostProcessManager& ppm = engine.getPostProcessManager();
// clear the color/depth buffers, which will orphan (and cull) the color pass
input.clear();
blackboard.remove("color");
blackboard.remove("depth");
assert_invariant(!colorPassInput.linearColor);
assert_invariant(!colorPassInput.depth);
config.hasScreenSpaceReflectionsOrRefractions = true;
input = RendererUtils::colorPass(fg, "Color Pass (opaque)", engine, view, {
PostProcessManager& ppm = engine.getPostProcessManager();
auto const opaquePassOutput = RendererUtils::colorPass(fg,
"Color Pass (opaque)", engine, view, colorPassInput, {
// When rendering the opaques, we need to conserve the sample buffer,
// so create a config that specifies the sample count.
.width = config.physicalViewport.width,
.height = config.physicalViewport.height,
.samples = config.msaa,
.format = config.hdrFormat
}, config, { .asSubpass = false },
},
config, { .asSubpass = false, .customResolve = false },
pass.getExecutor(pass.begin(), refraction));
// generate the mipmap chain
PostProcessManager::generateMipmapSSR(ppm, fg,
input, ssrConfig.refraction, true, ssrConfig);
opaquePassOutput.linearColor,
ssrConfig.refraction,
true, ssrConfig);
// Now we're doing the refraction pass proper.
// This uses the same framebuffer (color and depth) used by the opaque pass. This happens
// automatically because these are set in the Blackboard (they were set by the opaque
// pass). For this reason, `desc` below is only used in colorPass() for the width and
// height.
// This uses the same framebuffer (color and depth) used by the opaque pass.
// For this reason, the `colorBufferDesc` parameter of colorPass() below is only used for
// the width and height.
colorPassInput.linearColor = opaquePassOutput.linearColor;
colorPassInput.depth = opaquePassOutput.depth;
// Since we're reusing the existing target we don't want to clear any of its buffer.
// Important: if this target ended up being an imported target, then the clearFlags
@@ -331,22 +326,25 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
// and we'd end up clearing the opaque pass. This scenario never happens because it is
// prevented in Renderer.cpp's final blit.
config.clearFlags = TargetBufferFlags::NONE;
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view, {
auto transparentPassOutput = RendererUtils::colorPass(fg, "Color Pass (transparent)",
engine, view, colorPassInput, {
.width = config.physicalViewport.width,
.height = config.physicalViewport.height },
config, colorGradingConfig, pass.getExecutor(refraction, pass.end()));
config, colorGradingConfig,
pass.getExecutor(refraction, pass.end()));
if (config.msaa > 1 && !colorGradingConfig.asSubpass) {
// We need to do a resolve here because later passes (such as color grading or DoF) will
// need to sample from 'output'. However, because we have MSAA, we know we're not
// sampleable. And this is because in the SSR case, we had to use a renderbuffer to
// conserve the multi-sample buffer.
output = ppm.resolve(fg, "Resolved Color Buffer", output, { .levels = 1 });
transparentPassOutput.linearColor = ppm.resolve(fg, "Resolved Color Buffer",
transparentPassOutput.linearColor, { .levels = 1 });
}
} else {
output = input;
return transparentPassOutput;
}
return { output, hasScreenSpaceRefraction };
return std::nullopt;
}
UTILS_NOINLINE
+24 -2
View File
@@ -21,14 +21,19 @@
#include "RenderPass.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphTexture.h"
#include <filament/Viewport.h>
#include <backend/DriverEnums.h>
#include <backend/PixelBufferDescriptor.h>
#include <math/vec2.h>
#include <math/vec4.h>
#include <stdint.h>
#include <optional>
#include <utility>
namespace filament {
@@ -71,15 +76,32 @@ public:
bool screenSpaceReflectionHistoryNotReady;
};
static FrameGraphId<FrameGraphTexture> colorPass(
struct ColorPassInput {
FrameGraphId<FrameGraphTexture> linearColor;
FrameGraphId<FrameGraphTexture> tonemappedColor;
FrameGraphId<FrameGraphTexture> depth;
FrameGraphId<FrameGraphTexture> shadows;
FrameGraphId<FrameGraphTexture> ssao;
FrameGraphId<FrameGraphTexture> ssr;
FrameGraphId<FrameGraphTexture> structure;
};
struct ColorPassOutput {
FrameGraphId<FrameGraphTexture> linearColor;
FrameGraphId<FrameGraphTexture> tonemappedColor;
FrameGraphId<FrameGraphTexture> depth;
};
static ColorPassOutput colorPass(
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
ColorPassInput const& colorPassInput,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor passExecutor) noexcept;
static std::pair<FrameGraphId<FrameGraphTexture>, bool> refractionPass(
static std::optional<RendererUtils::ColorPassOutput> refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassInput colorPassInput,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
+8 -5
View File
@@ -143,12 +143,14 @@ void ResourceAllocator::terminate() noexcept {
}
}
RenderTargetHandle ResourceAllocator::createRenderTarget(const char*,
RenderTargetHandle ResourceAllocator::createRenderTarget(const char* name,
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, uint8_t layerCount, MRT color, TargetBufferInfo depth,
TargetBufferInfo stencil) noexcept {
return mBackend.createRenderTarget(targetBufferFlags,
auto handle = mBackend.createRenderTarget(targetBufferFlags,
width, height, samples ? samples : 1u, layerCount, color, depth, stencil);
mBackend.setDebugTag(handle.getId(), CString{ name });
return handle;
}
void ResourceAllocator::destroyRenderTarget(RenderTargetHandle h) noexcept {
@@ -170,9 +172,9 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
// do we have a suitable texture in the cache?
TextureHandle handle;
TextureKey const key{ name, target, levels, format, samples, width, height, depth, usage, swizzle };
if constexpr (mEnabled) {
auto& textureCache = mTextureCache;
const TextureKey key{ name, target, levels, format, samples, width, height, depth, usage, swizzle };
auto it = textureCache.find(key);
if (UTILS_LIKELY(it != textureCache.end())) {
// we do, move the entry to the in-use list, and remove from the cache
@@ -190,7 +192,6 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
}
}
mDisposer->checkout(handle, key);
} else {
if (swizzle == defaultSwizzle) {
handle = mBackend.createTexture(
@@ -201,12 +202,14 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
}
}
mDisposer->checkout(handle, key);
mBackend.setDebugTag(handle.getId(), CString{ name });
return handle;
}
void ResourceAllocator::destroyTexture(TextureHandle h) noexcept {
auto const key = mDisposer->checkin(h);
if constexpr (mEnabled) {
auto const key = mDisposer->checkin(h);
if (UTILS_LIKELY(key.has_value())) {
uint32_t const size = key.value().getSize();
mTextureCache.emplace(key.value(), TextureCachePayload{ h, mAge, size });
+9
View File
@@ -28,6 +28,7 @@ namespace filament {
class ResourceListBase {
public:
using iterator = typename tsl::robin_set<void*>::iterator;
using const_iterator = typename tsl::robin_set<void*>::const_iterator;
explicit ResourceListBase(const char* typeName);
ResourceListBase(ResourceListBase&& rhs) noexcept = default;
@@ -58,6 +59,14 @@ public:
return mList.end();
}
const_iterator begin() const noexcept {
return mList.begin();
}
const_iterator end() const noexcept {
return mList.end();
}
protected:
void forEach(void(*f)(void* user, void *p), void* user) const noexcept;
tsl::robin_set<void*> mList;
+68 -50
View File
@@ -57,9 +57,6 @@ namespace filament {
using namespace math;
using namespace backend;
// do this only if depth-clamp is available
static constexpr bool USE_DEPTH_CLAMP = false;
ShadowMap::ShadowMap(FEngine& engine) noexcept
: mPerShadowMapUniforms(engine),
mShadowType(ShadowType::DIRECTIONAL),
@@ -126,7 +123,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
FScene::LightSoa const& lightData, size_t index,
filament::CameraInfo const& camera,
ShadowMapInfo const& shadowMapInfo,
SceneInfo const& sceneInfo) noexcept {
SceneInfo const& sceneInfo,
bool useDepthClamp) noexcept {
// reset the visible shadow status
mHasVisibleShadows = false;
@@ -137,13 +135,20 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(index);
auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo);
auto const [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo,
useDepthClamp);
if (UTILS_UNLIKELY(!visibleShadows)) {
return {};
}
// if the 2D bounds of the intersection is empty seen from the light, we have no shadow
Aabb const bounds = compute2DBounds(Mv, lsClippedShadowVolume.data(), vertexCount);
if (UTILS_UNLIKELY(!(bounds.min.x < bounds.max.x && bounds.min.y < bounds.max.y))) {
return {};
}
/*
* Compute the light's projection matrix
* (directional/point lights, i.e. projection to use, including znear/zfar clip planes)
@@ -161,12 +166,17 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
mat4f W, Wp, L;
// We can't use LISPSM in stable mode
const bool useLispsm = params.options.lispsm && !params.options.stable;
if (useLispsm) {
if (useLispsm ||
(!params.options.stable && !engine.debug.shadowmap.disable_light_frustum_align)) {
// Orient the shadow map in the direction of the view vector by constructing a
const float3 lsCameraFwd = Mv.upperLeft() * camera.getForwardVector();
L = computeLightRotation(lsCameraFwd);
LMp = L * Mp;
LMpMv = LMp * Mv;
}
if (useLispsm) {
W = applyLISPSM(Wp, camera, params,
LMp, Mv, LMpMv, lsClippedShadowVolume, vertexCount, direction);
}
@@ -184,8 +194,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// In LiPSM mode, we're using the warped space here.
float4 f = computeFocusParams(LMpMv, WLMp,
lsClippedShadowVolume, vertexCount,
camera, sceneInfo.csNearFar,
params.options.shadowFar, params.options.stable);
camera, params.options.shadowFar, params.options.stable);
if (params.options.stable) {
const auto lsRef = lightData.elementAt<FScene::SHADOW_REF>(index);
@@ -386,7 +395,8 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
float3 const direction,
FLightManager::ShadowParams params,
filament::CameraInfo const& camera,
SceneInfo const& sceneInfo) noexcept {
SceneInfo const& sceneInfo,
bool useDepthClamp) noexcept {
// we use aligned_storage<> here to avoid the default initialization of std::array<>
using Storage = std::aligned_storage<sizeof(FrustumBoxIntersection)>::type;
@@ -448,36 +458,49 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
// Compute the intersection of the view volume with the intersection of receivers and casters
// in light space. This returns a set of points on the convex-hull of the intersection.
mat4f const projection = camera.cullingProjection *
(camera.view * FCamera::rigidTransformInverse(MvAtOrigin));
size_t const vertexCount = intersectFrustumWithBox(lsClippedShadowVolume,
camera.cullingProjection * camera.view * FCamera::rigidTransformInverse(MvAtOrigin),
sceneInfo.csNearFar, lsLightFrustumBounds);
projection, lsLightFrustumBounds);
if (UTILS_UNLIKELY(vertexCount < 4)) {
return {};
}
/*
* Adjust the scene's zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
* (near/far correspond to max/min because the light looks down the -z axis).
* - The Near plane is set to the shadow casters max z (i.e. closest to the light)
* - The Far plane is set to the closest of the farthest shadow casters and receivers
* i.e.: shadow casters behind the last receivers can't cast any shadows
*
* If "depth clamp" is supported, we can further tighten the near plane to the
* shadow receiver.
*
* Note: L has no influence here, since we're only interested in z values
* (L is a rotation around z)
*/
* Adjust the scene's zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
* (near/far correspond to max/min because the light looks down the -z axis).
*
* - For the Far plane, the light frustum is already set to the closest of receivers and
* casters (i.e.: shadow casters behind the last receivers can't cast any shadows).
* But it can be moved closer up to the farthest point in the camera frustum & light
* frustum intersection.
*
* - The Near plane is already set to the shadow casters max z (i.e. closest to the light)
*
* If "depth clamp" is supported, we can further tighten the near plane to the
* shadow receiver (i.e. to the closest point of camera frustum & light frustum intersection).
*
* Note: L has no influence here, since we're only interested in z values
* (L is a rotation around z)
*/
float lsClippedShadowVolumeFarthest = std::numeric_limits<float>::max();
float lsClippedShadowVolumeNearest = std::numeric_limits<float>::lowest();
for (size_t i = 0; i < vertexCount; ++i) {
float3 const v = lsClippedShadowVolume[i];
// far: figure out the farthest shadow receivers
lsLightFrustumBounds.min.z = min(lsLightFrustumBounds.min.z, v.z);
if constexpr (USE_DEPTH_CLAMP) {
lsLightFrustumBounds.max.z = max(lsLightFrustumBounds.max.z, v.z);
}
lsClippedShadowVolumeFarthest = std::min(lsClippedShadowVolumeFarthest, v.z);
lsClippedShadowVolumeNearest = std::max(lsClippedShadowVolumeNearest, v.z);
}
lsLightFrustumBounds.min.z =
std::max(lsLightFrustumBounds.min.z, lsClippedShadowVolumeFarthest);
if (useDepthClamp) {
lsLightFrustumBounds.max.z =
std::min(lsLightFrustumBounds.max.z, lsClippedShadowVolumeNearest);
}
if (engine.debug.shadowmap.far_uses_shadowcasters) {
// far: closest of the farthest shadow casters and receivers
lsLightFrustumBounds.min.z =
@@ -611,7 +634,7 @@ math::float4 ShadowMap::computeFocusParams(
mat4f const& LMpMv,
mat4f const& WLMp,
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
filament::CameraInfo const& camera, float2 const& csNearFar,
filament::CameraInfo const& camera,
float shadowFar, bool stable) noexcept {
float2 s, o;
if (stable) {
@@ -626,7 +649,7 @@ math::float4 ShadowMap::computeFocusParams(
return wsViewVolumeBoundingSphere;
} else {
mat4f const viewFromClip = inverse(camera.cullingProjection);
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar);
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip);
float4 const wsViewVolumeBoundingSphere =
computeBoundingSphere(wsFrustumVertices.vertices, 8);
return wsViewVolumeBoundingSphere;
@@ -794,26 +817,25 @@ Aabb ShadowMap::compute2DBounds(const mat4f& lightView,
}
ShadowMap::Corners ShadowMap::computeFrustumCorners(
mat4f const& projectionInverse,
float2 csNearFar) noexcept {
mat4f const& projectionInverse) noexcept {
Corners out;
// compute view frustum in world space (from its NDC)
// matrix to convert: ndc -> camera -> world
float const near = csNearFar.x;
float const far = csNearFar.y;
float const near = -1.0f;
float const far = 1.0f;
Corners const csViewFrustumCorners = {
.vertices = {
{ -1, -1, far },
{ 1, -1, far },
{ -1, 1, far },
{ 1, 1, far },
{ 1, -1, far },
{ -1, 1, far },
{ 1, 1, far },
{ -1, -1, near },
{ 1, -1, near },
{ -1, 1, near },
{ 1, 1, near },
{ 1, -1, near },
{ -1, 1, near },
{ 1, 1, near },
}
};
@@ -895,11 +917,11 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o,
size_t ShadowMap::intersectFrustumWithBox(
FrustumBoxIntersection& UTILS_RESTRICT outVertices,
mat4f const& UTILS_RESTRICT projection, math::float2 const& UTILS_RESTRICT csNearFar,
mat4f const& UTILS_RESTRICT projection,
Aabb const& UTILS_RESTRICT box)
{
Frustum const frustum{ projection };
Corners const frustumVertices{ computeFrustumCorners(inverse(projection), csNearFar) };
Corners const frustumVertices{ computeFrustumCorners(inverse(projection)) };
constexpr const float EPSILON = 1.0f / 8192.0f; // ~0.012 mm
size_t vertexCount = 0;
@@ -1203,12 +1225,11 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
}
ShadowMap::SceneInfo::SceneInfo(
FScene const& scene, uint8_t visibleLayers, mat4f const& viewMatrix) noexcept
: vsNearFar(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max()),
visibleLayers(visibleLayers) {
FScene const& scene, uint8_t visibleLayers) noexcept
: visibleLayers(visibleLayers) {
// the code below only works with affine transforms
assert_invariant(transpose(viewMatrix)[3] == float4(0, 0, 0, 1));
// Filament's API requires viewMatrix to be rigid (and thus affine).
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
// computeShadowCameraDirectional() which takes this into account.
@@ -1224,9 +1245,6 @@ ShadowMap::SceneInfo::SceneInfo(
[&](Aabb receiver, Culler::result_type) {
wsShadowReceiversVolume.min = min(wsShadowReceiversVolume.min, receiver.min);
wsShadowReceiversVolume.max = max(wsShadowReceiversVolume.max, receiver.max);
auto r = Aabb::transform(viewMatrix.upperLeft(), viewMatrix[3].xyz, receiver);
vsNearFar.x = std::max(vsNearFar.x, r.max.z);
vsNearFar.y = std::min(vsNearFar.y, r.min.z);
}
);
}
+8 -14
View File
@@ -109,20 +109,13 @@ public:
struct SceneInfo {
SceneInfo() noexcept = default;
SceneInfo(FScene const& scene, uint8_t visibleLayers, math::mat4f const& viewMatrix) noexcept;
// scratch data: The near and far planes, in clip space, to use for this shadow map
math::float2 csNearFar = { -1.0f, 1.0f };
SceneInfo(FScene const& scene, uint8_t visibleLayers) noexcept;
// scratch data: light's near/far expressed in light-space, calculated from the scene's
// content assuming the light is at the origin.
math::float2 lsCastersNearFar;
math::float2 lsReceiversNearFar;
// Viewing camera's near/far expressed in view-space, calculated from the
// scene's content.
math::float2 vsNearFar;
// World-space shadow-casters volume
Aabb wsShadowCastersVolume;
@@ -154,7 +147,8 @@ public:
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
ShadowMapInfo const& shadowMapInfo,
SceneInfo const& sceneInfo) noexcept;
SceneInfo const& sceneInfo,
bool useDepthClamp) noexcept;
ShaderParameters updateSpot(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
@@ -244,7 +238,8 @@ private:
math::float3 direction,
FLightManager::ShadowParams params,
filament::CameraInfo const& camera,
SceneInfo const& sceneInfo) noexcept;
SceneInfo const& sceneInfo,
bool useDepthClamp) noexcept;
static math::mat4f applyLISPSM(math::mat4f& Wp,
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
@@ -260,7 +255,7 @@ private:
math::mat4f const& LMpMv,
math::mat4f const& WLMp,
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
filament::CameraInfo const& camera, math::float2 const& csNearFar,
filament::CameraInfo const& camera,
float shadowFar, bool stable) noexcept;
static inline void snapLightFrustum(math::float2& s, math::float2& o,
@@ -271,8 +266,7 @@ private:
SceneInfo const& sceneInfo,
bool stable, bool focusShadowCasters, bool farUsesShadowCasters) noexcept;
static Corners computeFrustumCorners(const math::mat4f& projectionInverse,
math::float2 csNearFar = { -1.0f, 1.0f }) noexcept;
static Corners computeFrustumCorners(const math::mat4f& projectionInverse) noexcept;
static inline math::float2 computeNearFar(math::mat4f const& view,
math::float3 const* wsVertices, size_t count) noexcept;
@@ -307,7 +301,7 @@ private:
static size_t intersectFrustumWithBox(
FrustumBoxIntersection& outVertices,
math::mat4f const& projection, math::float2 const& csNearFar,
math::mat4f const& projection,
Aabb const& box);
static math::mat4f warpFrustum(float n, float f) noexcept;
+117 -84
View File
@@ -39,17 +39,20 @@
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/BitmaskEnum.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <math/half.h>
#include <math/mat4.h>
#include <math/vec4.h>
#include <math/scalar.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <limits>
#include <new>
@@ -63,12 +66,15 @@ namespace filament {
using namespace backend;
using namespace math;
ShadowMapManager::ShadowMapManager(FEngine& engine) {
ShadowMapManager::ShadowMapManager(FEngine& engine)
: mIsDepthClampSupported(engine.getDriverApi().isDepthClampSupported()) {
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
&engine.debug.shadowmap.visualize_cascades);
debugRegistry.registerProperty("d.shadowmap.tightly_bound_scene",
&engine.debug.shadowmap.tightly_bound_scene);
debugRegistry.registerProperty("d.shadowmap.disable_light_frustum_align",
&engine.debug.shadowmap.disable_light_frustum_align);
debugRegistry.registerProperty("d.shadowmap.depth_clamp",
&engine.debug.shadowmap.depth_clamp);
}
ShadowMapManager::~ShadowMapManager() {
@@ -147,7 +153,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
calculateTextureRequirements(engine, view, lightData);
// Compute scene-dependent values shared across all shadow maps
ShadowMap::SceneInfo const info{ *view.getScene(), view.getVisibleLayers(), cameraInfo.view };
ShadowMap::SceneInfo const info{ *view.getScene(), view.getVisibleLayers() };
shadowTechnique |= updateCascadeShadowMaps(
engine, view, cameraInfo, renderableData, lightData, info);
@@ -361,7 +367,20 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// generate and sort the commands for rendering the shadow map
RenderPass::RenderFlags renderPassFlags{};
bool const canUseDepthClamp =
shadowMap.getShadowType() == ShadowType::DIRECTIONAL &&
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
if (canUseDepthClamp) {
renderPassFlags |= RenderPass::HAS_DEPTH_CLAMP;
}
RenderPass const pass = passBuilder
.renderFlags(RenderPass::HAS_DEPTH_CLAMP, renderPassFlags)
.camera(cameraInfo)
.visibilityMask(entry.visibilityMask)
.geometry(scene->getRenderableData(),
@@ -543,65 +562,9 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
// Adjust the camera's projection for the light's shadowFar
cameraInfo.zf = params.options.shadowFar > 0.0f ? params.options.shadowFar : cameraInfo.zf;
if (UTILS_UNLIKELY(params.options.shadowFar > 0.0f)) {
cameraInfo.zf = params.options.shadowFar;
float const n = cameraInfo.zn;
float const f = cameraInfo.zf;
/*
* Updating a projection matrix near and far planes:
*
* We assume that the near and far plane equations are of the form:
* N = { 0, 0, 1, n }
* F = { 0, 0, -1, -f }
*
* We also assume that the lower-left 2x2 of the projection is all 0:
* P = A 0 C 0
* 0 B D 0
* 0 0 E F
* 0 0 G H
*
* It result that we need to calculate E and F while leaving all other parameter unchanged.
*
* We know that:
* with N, F the near/far normalized plane equation parameters
* sn, sf arbitrary scale factors (they don't affect the planes)
* m is the transpose of projection (see Frustum.cpp)
*
* sn * N == -m[3] - m[2]
* sf * F == -m[3] + m[2]
*
* sn * N + sf * F == -2 * m[3]
* sn * N - sf * F == -2 * m[2]
*
* sn * N.z + sf * F.z == -2 * m[3].z
* sn * N.w + sf * F.w == -2 * m[3].w
* sn * N.z - sf * F.z == -2 * m[2].z
* sn * N.w - sf * F.w == -2 * m[2].w
*
* sn * N.z + sf * F.z == -2 * p[2].w
* sn * N.w + sf * F.w == -2 * p[3].w
* sn * N.z - sf * F.z == -2 * p[2].z
* sn * N.w - sf * F.w == -2 * p[3].z
*
* We now need to solve for { p[2].z, p[3].z, sn, sf } :
*
* sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
* sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
* p[2].z == (sf * F.z - sn * N.z) / 2
* p[3].z == (sf * F.w - sn * N.w) / 2
*/
auto& p = cameraInfo.cullingProjection;
float4 const N = { 0, 0, 1, n }; // near plane equation
float4 const F = { 0, 0, -1, -f }; // far plane equation
// near plane equation scale factor
float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
// far plane equation scale factor
float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
// New values for the projection
p[2].z = (sf * F.z - sn * N.z) * 0.5f;
p[3].z = (sf * F.w - sn * N.w) * 0.5f;
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
}
const ShadowMap::ShadowMapInfo shadowMapInfo{
@@ -631,8 +594,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// Compute scene-dependent values shared across all cascades
ShadowMap::updateSceneInfoDirectional(MvAtOrigin, *scene, sceneInfo);
// we always do culling without depth clamp, because objects behind the camera
// must be rendered regardless
shadowMap.updateDirectional(engine,
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo, false);
hasVisibleShadows = shadowMap.hasVisibleShadows();
@@ -648,15 +613,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
uint32_t cascadeHasVisibleShadows = 0;
if (hasVisibleShadows) {
// Adjust the near and far planes to tightly bound the scene.
float vsNear = -cameraInfo.zn;
float vsFar = -cameraInfo.zf;
if (engine.debug.shadowmap.tightly_bound_scene && !params.options.stable) {
vsNear = std::min(vsNear, sceneInfo.vsNearFar.x);
vsFar = std::max(vsFar, sceneInfo.vsNearFar.y);
}
const size_t cascadeCount = cascadedShadowMaps.size();
uint32_t const cascadeCount = cascadedShadowMaps.size();
// We divide the camera frustum into N cascades. This gives us N + 1 split positions.
// The first split position is the near plane; the last split position is the far plane.
@@ -667,9 +624,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
}
const CascadeSplits splits({
.proj = cameraInfo.cullingProjection,
.near = vsNear,
.far = vsFar,
.near = -cameraInfo.zn,
.far = -cameraInfo.zf,
.cascadeCount = cascadeCount,
.splitPositions = splitPercentages
});
@@ -679,10 +635,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
static_assert(CONFIG_MAX_SHADOW_CASCADES <= 5,
"At most, a float4 can fit 4 split positions for 5 shadow cascades");
float4 wsSplitPositionUniform{ -std::numeric_limits<float>::infinity() };
std::copy(splits.beginWs() + 1, splits.endWs(), &wsSplitPositionUniform[0]);
float csSplitPosition[CONFIG_MAX_SHADOW_CASCADES + 1];
std::copy(splits.beginCs(), splits.endCs(), csSplitPosition);
std::copy(splits.begin() + 1, splits.end(), &wsSplitPositionUniform[0]);
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
@@ -695,10 +648,20 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
ShadowMap& shadowMap = cascadedShadowMaps[i];
assert_invariant(shadowMap.getLightIndex() == 0);
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
// update cameraInfo culling projection for the cascade
float const* nearFarPlanes = splits.begin();
cameraInfo.zn = -nearFarPlanes[i];
cameraInfo.zf = -nearFarPlanes[i + 1];
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
bool const canUseDepthClamp =
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
auto shaderParameters = shadowMap.updateDirectional(engine,
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo,
canUseDepthClamp);
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
@@ -1069,8 +1032,78 @@ ShadowMapManager::CascadeSplits::CascadeSplits(Params const& params) noexcept
: mSplitCount(params.cascadeCount + 1) {
for (size_t s = 0; s < mSplitCount; s++) {
mSplitsWs[s] = params.near + (params.far - params.near) * params.splitPositions[s];
mSplitsCs[s] = mat4f::project(params.proj, float3(0.0f, 0.0f, mSplitsWs[s])).z;
}
}
void ShadowMapManager::updateNearFarPlanes(mat4f* projection,
float nearDistance, float farDistance) noexcept {
float const n = nearDistance;
float const f = farDistance;
/*
* Updating a projection matrix near and far planes:
*
* We assume that the near and far plane equations are of the form:
* N = { 0, 0, 1, n }
* F = { 0, 0, -1, -f }
*
* We also assume that the lower-left 2x2 of the projection is all 0:
* P = A 0 C 0
* 0 B D 0
* 0 0 E F
* 0 0 G H
*
* It result that we need to calculate E and F while leaving all other parameter unchanged.
*
* We know that:
* with N, F the near/far normalized plane equation parameters
* sn, sf arbitrary scale factors (they don't affect the planes)
* m is the transpose of projection (see Frustum.cpp)
*
* sn * N == -m[3] - m[2]
* sf * F == -m[3] + m[2]
*
* sn * N + sf * F == -2 * m[3]
* sn * N - sf * F == -2 * m[2]
*
* sn * N.z + sf * F.z == -2 * m[3].z
* sn * N.w + sf * F.w == -2 * m[3].w
* sn * N.z - sf * F.z == -2 * m[2].z
* sn * N.w - sf * F.w == -2 * m[2].w
*
* sn * N.z + sf * F.z == -2 * p[2].w
* sn * N.w + sf * F.w == -2 * p[3].w
* sn * N.z - sf * F.z == -2 * p[2].z
* sn * N.w - sf * F.w == -2 * p[3].z
*
* We now need to solve for { p[2].z, p[3].z, sn, sf } :
*
* sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
* sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
* p[2].z == (sf * F.z - sn * N.z) / 2
* p[3].z == (sf * F.w - sn * N.w) / 2
*/
auto& p = *projection;
float4 const N = { 0, 0, 1, n }; // near plane equation
float4 const F = { 0, 0, -1, -f }; // far plane equation
// near plane equation scale factor
float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
// far plane equation scale factor
float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
// New values for the projection
p[2].z = (sf * F.z - sn * N.z) * 0.5f;
p[3].z = (sf * F.w - sn * N.w) * 0.5f;
}
utils::FixedCapacityVector<Camera const*>
ShadowMapManager::getDirectionalShadowCameras() const noexcept {
if (!mInitialized) return {};
auto const csm = getCascadedShadowMap();
auto result = utils::FixedCapacityVector<Camera const*>::with_capacity(csm.size());
for (ShadowMap const& sm : csm) {
result.push_back(sm.hasVisibleShadows() ? sm.getDebugCamera() : nullptr);
}
return result;
}
} // namespace filament
+16 -15
View File
@@ -40,6 +40,7 @@
#include <utils/BitmaskEnum.h>
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <utils/debug.h>
#include <utils/Range.h>
#include <utils/Slice.h>
@@ -58,6 +59,7 @@
namespace filament {
class Camera;
class FCamera;
class FView;
class FrameGraph;
@@ -138,16 +140,16 @@ public:
bool hasSpotShadows() const { return !mSpotShadowMapCount; }
// for debugging only
FCamera const* getDirectionalShadowCamera() const noexcept {
if (!mInitialized) return nullptr;
return getShadowMap(0).getDebugCamera();
}
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
private:
explicit ShadowMapManager(FEngine& engine);
void terminate(FEngine& engine);
static void updateNearFarPlanes(math::mat4f* projection,
float nearDistance, float farDistance) noexcept;
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept;
@@ -186,27 +188,21 @@ private:
constexpr static size_t SPLIT_COUNT = CONFIG_MAX_SHADOW_CASCADES + 1;
struct Params {
math::mat4f proj;
float near = 0.0f;
float far = 0.0f;
size_t cascadeCount = 1;
uint32_t cascadeCount = 1;
std::array<float, SPLIT_COUNT> splitPositions = { 0.0f };
};
explicit CascadeSplits(Params const& params) noexcept;
// Split positions in world-space.
const float* beginWs() const { return mSplitsWs; }
const float* endWs() const { return mSplitsWs + mSplitCount; }
// Split positions in clip-space.
const float* beginCs() const { return mSplitsCs; }
const float* endCs() const { return mSplitsCs + mSplitCount; }
const float* begin() const { return mSplitsWs; }
const float* end() const { return mSplitsWs + mSplitCount; }
private:
float mSplitsWs[SPLIT_COUNT];
float mSplitsCs[SPLIT_COUNT];
size_t mSplitCount;
uint32_t mSplitCount;
};
// Atlas requirements, updated in ShadowMapManager::update(),
@@ -236,6 +232,7 @@ private:
ShadowMapCacheContainer mShadowMapCache;
uint32_t mDirectionalShadowMapCount = 0;
uint32_t mSpotShadowMapCount = 0;
bool const mIsDepthClampSupported;
bool mInitialized = false;
ShadowMap& getShadowMap(size_t index) noexcept {
@@ -248,10 +245,14 @@ private:
}
utils::Slice<ShadowMap> getCascadedShadowMap() noexcept {
ShadowMap* const p = &getShadowMap(0);
ShadowMap const* const p = &getShadowMap(0);
return { p, mDirectionalShadowMapCount };
}
utils::Slice<ShadowMap> getCascadedShadowMap() const noexcept {
return const_cast<ShadowMapManager*>(this)->getCascadedShadowMap();
}
utils::Slice<ShadowMap> getSpotShadowMaps() noexcept {
ShadowMap* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES);
return { p, mSpotShadowMapCount };
+2 -2
View File
@@ -73,8 +73,8 @@ const char* View::getName() const noexcept {
return downcast(this)->getName();
}
Camera const* View::getDirectionalShadowCamera() const noexcept {
return downcast(this)->getDirectionalShadowCamera();
utils::FixedCapacityVector<Camera const*> View::getDirectionalShadowCameras() const noexcept {
return downcast(this)->getDirectionalShadowCameras();
}
void View::setShadowingEnabled(bool enabled) noexcept {
@@ -588,6 +588,9 @@ void FRenderableManager::create(
// full size of the UBO.
instances.handle = driver.createBufferObject(sizeof(PerRenderableUib),
BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC);
if (auto name = instances.buffer->getName(); !name.empty()) {
driver.setDebugTag(instances.handle.getId(), std::move(name));
}
}
const uint32_t boneCount = builder->mSkinningBoneCount;
+5
View File
@@ -20,6 +20,8 @@
#include "FilamentAPI-impl.h"
#include <utils/CString.h>
namespace filament {
struct BufferObject::BuilderDetails {
@@ -56,6 +58,9 @@ FBufferObject::FBufferObject(FEngine& engine, const BufferObject::Builder& build
FEngine::DriverApi& driver = engine.getDriverApi();
mHandle = driver.createBufferObject(builder->mByteCount, builder->mBindingType,
backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
}
}
void FBufferObject::terminate(FEngine& engine) {
+25 -20
View File
@@ -28,12 +28,6 @@
#include <string_view>
#include <utility>
#ifndef NDEBUG
# define DEBUG_PROPERTIES_WRITABLE true
#else
# define DEBUG_PROPERTIES_WRITABLE false
#endif
using namespace filament::math;
using namespace utils;
@@ -79,17 +73,15 @@ bool FDebugRegistry::hasProperty(const char* name) const noexcept {
template<typename T>
bool FDebugRegistry::setProperty(const char* name, T v) noexcept {
if constexpr (DEBUG_PROPERTIES_WRITABLE) {
auto info = getPropertyInfo(name);
T* const addr = static_cast<T*>(info.first);
if (addr) {
auto old = *addr;
*addr = v;
if (info.second && old != v) {
info.second();
}
return true;
auto info = getPropertyInfo(name);
T* const addr = static_cast<T*>(info.first);
if (addr) {
auto old = *addr;
*addr = v;
if (info.second && old != v) {
info.second();
}
return true;
}
return false;
}
@@ -118,19 +110,32 @@ template bool FDebugRegistry::getProperty<float2>(const char* name, float2* v) c
template bool FDebugRegistry::getProperty<float3>(const char* name, float3* v) const noexcept;
template bool FDebugRegistry::getProperty<float4>(const char* name, float4* v) const noexcept;
void FDebugRegistry::registerDataSource(std::string_view name,
bool FDebugRegistry::registerDataSource(std::string_view name,
void const* data, size_t count) noexcept {
auto& dataSourceMap = mDataSourceMap;
if (dataSourceMap.find(name) == dataSourceMap.end()) {
bool const found = dataSourceMap.find(name) == dataSourceMap.end();
if (found) {
dataSourceMap[name] = { data, count };
}
return found;
}
void FDebugRegistry::registerDataSource(std::string_view name,
bool FDebugRegistry::registerDataSource(std::string_view name,
utils::Invocable<DataSource()>&& creator) noexcept {
mDataSourceCreatorMap[name] = std::move(creator);
auto& dataSourceCreatorMap = mDataSourceCreatorMap;
bool const found = dataSourceCreatorMap.find(name) == dataSourceCreatorMap.end();
if (found) {
dataSourceCreatorMap[name] = std::move(creator);
}
return found;
}
void FDebugRegistry::unregisterDataSource(std::string_view name) noexcept {
mDataSourceCreatorMap.erase(name);
mDataSourceMap.erase(name);
}
DebugRegistry::DataSource FDebugRegistry::getDataSource(const char* name) const noexcept {
std::string_view const key{ name };
auto& dataSourceMap = mDataSourceMap;
+4 -2
View File
@@ -101,12 +101,14 @@ public:
}
// registers a DataSource directly
void registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
bool registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
// registers a DataSource lazily
void registerDataSource(std::string_view name,
bool registerDataSource(std::string_view name,
utils::Invocable<DataSource()>&& creator) noexcept;
void unregisterDataSource(std::string_view name) noexcept;
#if !defined(_MSC_VER)
private:
#endif
+20 -11
View File
@@ -71,7 +71,7 @@ struct Engine::BuilderDetails {
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
void* mSharedContext = nullptr;
bool mPaused = false;
static Config validateConfig(const Config* pConfig) noexcept;
static Config validateConfig(Config config) noexcept;
};
Engine* FEngine::create(Engine::Builder const& builder) {
@@ -1197,6 +1197,22 @@ bool FEngine::isValid(const FInstanceBuffer* p) const {
return isValid(p, mInstanceBuffers);
}
size_t FEngine::getBufferObjectCount() const noexcept { return mBufferObjects.size(); }
size_t FEngine::getViewCount() const noexcept { return mViews.size(); }
size_t FEngine::getSceneCount() const noexcept { return mScenes.size(); }
size_t FEngine::getSwapChainCount() const noexcept { return mSwapChains.size(); }
size_t FEngine::getStreamCount() const noexcept { return mStreams.size(); }
size_t FEngine::getIndexBufferCount() const noexcept { return mIndexBuffers.size(); }
size_t FEngine::getSkinningBufferCount() const noexcept { return mSkinningBuffers.size(); }
size_t FEngine::getMorphTargetBufferCount() const noexcept { return mMorphTargetBuffers.size(); }
size_t FEngine::getInstanceBufferCount() const noexcept { return mInstanceBuffers.size(); }
size_t FEngine::getVertexBufferCount() const noexcept { return mVertexBuffers.size(); }
size_t FEngine::getIndirectLightCount() const noexcept { return mIndirectLights.size(); }
size_t FEngine::getMaterialCount() const noexcept { return mMaterials.size(); }
size_t FEngine::getTextureCount() const noexcept { return mTextures.size(); }
size_t FEngine::getSkyboxeCount() const noexcept { return mSkyboxes.size(); }
size_t FEngine::getColorGradingCount() const noexcept { return mColorGradings.size(); }
size_t FEngine::getRenderTargetCount() const noexcept { return mRenderTargets.size(); }
void* FEngine::streamAlloc(size_t size, size_t alignment) noexcept {
// we allow this only for small allocations
@@ -1286,7 +1302,7 @@ Engine::Builder& Engine::Builder::platform(Platform* platform) noexcept {
}
Engine::Builder& Engine::Builder::config(Engine::Config const* config) noexcept {
mImpl->mConfig = BuilderDetails::validateConfig(config);
mImpl->mConfig = config ? *config : Engine::Config{};
return *this;
}
@@ -1314,22 +1330,15 @@ void Engine::Builder::build(Invocable<void(void*)>&& callback) const {
#endif
Engine* Engine::Builder::build() const {
mImpl->mConfig = BuilderDetails::validateConfig(mImpl->mConfig);
return FEngine::create(*this);
}
Engine::Config Engine::BuilderDetails::validateConfig(const Config* const pConfig) noexcept {
Engine::Config Engine::BuilderDetails::validateConfig(Config config) noexcept {
// Rule of thumb: perRenderPassArenaMB must be roughly 1 MB larger than perFrameCommandsMB
constexpr uint32_t COMMAND_ARENA_OVERHEAD = 1;
constexpr uint32_t CONCURRENT_FRAME_COUNT = 3;
Config config;
if (!pConfig) {
return config;
}
// make sure to copy all the fields
config = *pConfig;
// Use at least the defaults set by the build system
config.minCommandBufferSizeMB = std::max(
config.minCommandBufferSizeMB,
+21 -2
View File
@@ -351,6 +351,23 @@ public:
bool isValid(const FView* p) const;
bool isValid(const FInstanceBuffer* p) const;
size_t getBufferObjectCount() const noexcept;
size_t getViewCount() const noexcept;
size_t getSceneCount() const noexcept;
size_t getSwapChainCount() const noexcept;
size_t getStreamCount() const noexcept;
size_t getIndexBufferCount() const noexcept;
size_t getSkinningBufferCount() const noexcept;
size_t getMorphTargetBufferCount() const noexcept;
size_t getInstanceBufferCount() const noexcept;
size_t getVertexBufferCount() const noexcept;
size_t getIndirectLightCount() const noexcept;
size_t getMaterialCount() const noexcept;
size_t getTextureCount() const noexcept;
size_t getSkyboxeCount() const noexcept;
size_t getColorGradingCount() const noexcept;
size_t getRenderTargetCount() const noexcept;
void destroy(utils::Entity e);
bool isPaused() const noexcept;
@@ -573,7 +590,8 @@ private:
Config mConfig;
public:
// these are the debug properties used by FDebug. They're accessed directly by modules who need them.
// These are the debug properties used by FDebug.
// They're accessed directly by modules who need them.
struct {
struct {
bool debug_directional_shadowmap = false;
@@ -581,7 +599,8 @@ public:
bool far_uses_shadowcasters = true;
bool focus_shadowcasters = true;
bool visualize_cascades = false;
bool tightly_bound_scene = true;
bool disable_light_frustum_align = false;
bool depth_clamp = true;
float dzn = -1.0f;
float dzf = 1.0f;
float display_shadow_texture_scale = 0.25f;
+5
View File
@@ -20,6 +20,8 @@
#include "FilamentAPI-impl.h"
#include <utils/CString.h>
namespace filament {
struct IndexBuffer::BuilderDetails {
@@ -58,6 +60,9 @@ FIndexBuffer::FIndexBuffer(FEngine& engine, const IndexBuffer::Builder& builder)
(backend::ElementType)builder->mIndexType,
uint32_t(builder->mIndexCount),
backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
}
}
void FIndexBuffer::terminate(FEngine& engine) {
+2 -1
View File
@@ -61,7 +61,8 @@ InstanceBuffer* InstanceBuffer::Builder::build(Engine& engine) {
// ------------------------------------------------------------------------------------------------
FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder) {
FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder)
: mName(builder.getName()) {
mInstanceCount = builder->mInstanceCount;
mLocalTransforms.reserve(mInstanceCount);
+4
View File
@@ -25,6 +25,7 @@
#include <math/mat4.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
namespace filament {
@@ -46,10 +47,13 @@ public:
void prepare(FEngine& engine, math::mat4f rootTransform, const PerRenderableData& ubo,
backend::Handle<backend::HwBufferObject> handle);
utils::CString const& getName() const noexcept { return mName; }
private:
friend class RenderableManager;
utils::FixedCapacityVector<math::mat4f> mLocalTransforms;
utils::CString mName;
size_t mInstanceCount;
};
+27 -3
View File
@@ -192,6 +192,30 @@ Material* Material::Builder::build(Engine& engine) {
return nullptr;
}
// Print a warning if the material's stereo type doesn't align with the engine's setting.
MaterialDomain materialDomain;
UserVariantFilterMask variantFilterMask;
materialParser->getMaterialDomain(&materialDomain);
materialParser->getMaterialVariantFilterMask(&variantFilterMask);
bool const hasStereoVariants = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::STE));
if (materialDomain == MaterialDomain::SURFACE && hasStereoVariants) {
StereoscopicType const engineStereoscopicType = engine.getConfig().stereoscopicType;
// Default materials are always compiled with either 'instanced' or 'multiview'.
// So, we only verify compatibility if the engine is set up for stereo.
if (engineStereoscopicType != StereoscopicType::NONE) {
StereoscopicType materialStereoscopicType = StereoscopicType::NONE;
materialParser->getStereoscopicType(&materialStereoscopicType);
if (materialStereoscopicType != engineStereoscopicType) {
CString name;
materialParser->getName(&name);
slog.w << "The stereoscopic type in the compiled material '" << name.c_str_safe()
<< "' is " << (int)materialStereoscopicType
<< ", which is not compatiable with the engine's setting "
<< (int)engineStereoscopicType << "." << io::endl;
}
}
}
return downcast(engine).createMaterial(*this, std::move(materialParser));
}
@@ -200,8 +224,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
: mIsDefaultMaterial(builder->mDefaultMaterial),
mEngine(engine),
mMaterialId(engine.getMaterialId()),
mMaterialParser(std::move(materialParser))
{
mMaterialParser(std::move(materialParser)) {
MaterialParser* const parser = mMaterialParser.get();
UTILS_UNUSED_IN_RELEASE bool const nameOk = parser->getName(&mName);
@@ -595,6 +618,7 @@ Program FMaterial::getProgramWithVariants(
void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexcept {
auto program = mEngine.getDriverApi().createProgram(std::move(p));
mEngine.getDriverApi().setDebugTag(program.getId(), mName);
assert_invariant(program);
mCachedPrograms[variant.key] = program;
}
@@ -873,7 +897,7 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Material::Builde
engine.getDriverApi().getMaxUniformBufferSize() / 16u);
bool const staticTextureWorkaround =
engine.getDriverApi().isWorkaroundNeeded(Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR);
engine.getDriverApi().isWorkaroundNeeded(Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR);
bool const powerVrShaderWorkarounds =
engine.getDriverApi().isWorkaroundNeeded(Workaround::POWER_VR_SHADER_WORKAROUNDS);
@@ -51,6 +51,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material)
mUniforms = UniformBuffer(material->getUniformInterfaceBlock().getSize());
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
BufferObjectBinding::UNIFORM, backend::BufferUsage::STATIC);
driver.setDebugTag(mUbHandle.getId(), material->getName());
}
if (!material->getSamplerInterfaceBlock().isEmpty()) {
@@ -116,6 +117,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mUniforms.setUniforms(other->getUniformBuffer());
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC);
driver.setDebugTag(mUbHandle.getId(), material->getName());
}
if (!material->getSamplerInterfaceBlock().isEmpty()) {
@@ -25,6 +25,8 @@
#include <math/mat4.h>
#include <math/norm.h>
#include <utils/CString.h>
namespace filament {
using namespace backend;
@@ -124,6 +126,11 @@ FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder)
mCount,
TextureUsage::DEFAULT);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mPbHandle.getId(), name);
driver.setDebugTag(mTbHandle.getId(), std::move(name));
}
// create and update sampler group
mSbHandle = driver.createSamplerGroup(PerRenderPrimitiveMorphingSib::SAMPLER_COUNT,
utils::FixedSizeString<32>("Morph target samplers"));
+82 -40
View File
@@ -344,10 +344,14 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
* to ignore the return value and render the frame anyway -- which is perfectly fine.
* The remaining work will be done when the first render() call is made.
*/
auto beginFrameInternal = [this, appVsync]() {
auto beginFrameInternal = [this, appVsync, swapChain]() {
FEngine& engine = mEngine;
FEngine::DriverApi& driver = engine.getDriverApi();
// we need to re-set mSwapChain here because if a frame was marked as "skip" but the
// user ignored us, we'd still want mSwapChain to be set.
mSwapChain = swapChain;
driver.beginFrame(
appVsync.time_since_epoch().count(),
mDisplayInfo.refreshRate == 0.0 ? 0 : int64_t(
@@ -376,6 +380,10 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
// so we need to delay this work until that happens.
mBeginFrameInternal = beginFrameInternal;
// clear mSwapChain because the frame should be skipped (it will be re-set if the user
// ignores the skip)
mSwapChain = nullptr;
// we need to flush in this case, to make sure the tick() call is executed at some point
engine.flush();
@@ -534,8 +542,6 @@ void FRenderer::renderStandaloneView(FView const* view) {
void FRenderer::render(FView const* view) {
SYSTRACE_CALL();
assert_invariant(mSwapChain);
if (UTILS_UNLIKELY(mBeginFrameInternal)) {
// this should not happen, the user should not call render() if we returned false from
// beginFrame(). But because this is allowed, we handle it gracefully.
@@ -543,6 +549,9 @@ void FRenderer::render(FView const* view) {
mBeginFrameInternal = {};
}
// after beginFrame() is called, mSwapChain should be true
assert_invariant(mSwapChain);
if (UTILS_LIKELY(view && view->getScene() && view->hasCamera())) {
if (mViewRenderedCount) {
// This is a good place to kick the GPU, since we've rendered a View before,
@@ -642,20 +651,25 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
const bool isProtectedContent = mSwapChain && mSwapChain->isProtected();
// Conditions to meet to be able to use the sub-pass rendering path. This is regardless of
// whether the backend supports subpasses (or if they are disabled in the debugRegistry).
const bool isSubpassPossible =
msaaSampleCount <= 1 &&
hasColorGrading &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled;
// asSubpass is disabled with TAA (although it's supported) because performance was degraded
// on qualcomm hardware -- we might need a backend dependent toggle at some point
const PostProcessManager::ColorGradingConfig colorGradingConfig{
.asSubpass =
hasColorGrading &&
msaaSampleCount <= 1 &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled &&
isSubpassPossible &&
driver.isFrameBufferFetchSupported() &&
!engine.debug.renderer.disable_subpasses,
.customResolve =
msaaOptions.customResolve &&
msaaSampleCount > 1 &&
hasColorGrading &&
driver.isFrameBufferFetchMultiSampleSupported() &&
msaaOptions.customResolve &&
hasColorGrading &&
!engine.debug.renderer.disable_subpasses,
.translucent = needsAlphaChannel,
.fxaa = hasFXAA,
@@ -664,6 +678,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
TextureFormat::RGBA8 : getLdrFormat(needsAlphaChannel)
};
// by construction (msaaSampleCount) both asSubpass and customResolve can't be true
assert_invariant(colorGradingConfig.asSubpass + colorGradingConfig.customResolve < 2);
// whether we're scaled at all
bool scaled = any(notEqual(scale, float2(1.0f)));
@@ -686,13 +703,12 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
CameraInfo cameraInfo = view.computeCameraInfo(engine);
// when colorgrading-as-subpass is active, we know that many other effects are disabled
// such as dof, bloom. Moreover, if fxaa and scaling are not enabled, we're essentially in
// a very fast rendering path -- in this case, we would need an extra blit to "resolve" the
// buffer padding (because there are no other pass that can do it as a side effect).
// In this case, it is better to skip the padding, which won't be helping much.
const bool noBufferPadding = (colorGradingConfig.asSubpass && !hasFXAA && !scaled)
|| engine.debug.renderer.disable_buffer_padding;
// If fxaa and scaling are not enabled, we're essentially in a very fast rendering path -- in
// this case, we would need an extra blit to "resolve" the buffer padding (because there are no
// other pass that can do it as a side effect). In this case, it is better to skip the padding,
// which won't be helping much.
const bool noBufferPadding = (isSubpassPossible &&
!hasFXAA && !scaled) || engine.debug.renderer.disable_buffer_padding;
// guardBand must be a multiple of 16 to guarantee the same exact rendering up to 4 mip levels.
float const guardBand = guardBandOptions.enabled ? 16.0f : 0.0f;
@@ -807,14 +823,14 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
blackboard["shadows"] = shadows;
}
// When we don't have a custom RenderTarget, currentRenderTarget below is nullptr and is
// When we don't have a custom RenderTarget, customRenderTarget below is nullptr and is
// recorded in the list of targets already rendered into -- this ensures that
// initializeClearFlags() is called only once for the default RenderTarget.
auto& previousRenderTargets = mPreviousRenderTargets;
FRenderTarget* const currentRenderTarget = downcast(view.getRenderTarget());
FRenderTarget* const customRenderTarget = downcast(view.getRenderTarget());
if (UTILS_LIKELY(
previousRenderTargets.find(currentRenderTarget) == previousRenderTargets.end())) {
previousRenderTargets.insert(currentRenderTarget);
previousRenderTargets.find(customRenderTarget) == previousRenderTargets.end())) {
previousRenderTargets.insert(customRenderTarget);
initializeClearFlags();
}
@@ -831,10 +847,10 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
const TargetBufferFlags keepOverrideStartFlags = TargetBufferFlags::ALL & ~discardStartFlags;
TargetBufferFlags keepOverrideEndFlags = TargetBufferFlags::NONE;
if (currentRenderTarget) {
if (customRenderTarget) {
// For custom RenderTarget, we look at each attachment flag and if they have their
// SAMPLEABLE usage bit set, we assume they must not be discarded after the render pass.
keepOverrideEndFlags |= currentRenderTarget->getSampleableAttachmentsMask();
keepOverrideEndFlags |= customRenderTarget->getSampleableAttachmentsMask();
}
// Renderer's ClearOptions apply once at the beginning of the frame (not for each View),
@@ -943,7 +959,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
.scale = aoOptions.resolution,
.picking = view.hasPicking()
});
blackboard["structure"] = structure;
const auto picking = picking_;
@@ -991,7 +1006,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
ssReflectionsOptions.enabled ? TextureFormat::RGBA16F : TextureFormat::R11F_G11F_B10F,
view.getCameraUser().getFieldOfView(Camera::Fov::VERTICAL), config.scale);
config.ssrLodOffset = ssrConfig.lodOffset;
blackboard["ssr"] = ssrConfig.ssr;
// --------------------------------------------------------------------------------------------
// screen-space reflections pass
@@ -1005,6 +1019,10 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
{ .width = svp.width, .height = svp.height });
if (UTILS_LIKELY(reflections)) {
fg.addTrivialSideEffectPass("SSR Cleanup", [&view](DriverApi& driver) {
view.getPerViewUniforms().prepareStructure({});
view.commitUniforms(driver);
});
// generate the mipchain
PostProcessManager::generateMipmapSSR(ppm, fg,
reflections, ssrConfig.reflection, false, ssrConfig);
@@ -1095,24 +1113,45 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
);
// the color pass itself + color-grading as subpass if needed
auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view,
auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view, {
.shadows = blackboard.get<FrameGraphTexture>("shadows"),
.ssao = blackboard.get<FrameGraphTexture>("ssao"),
.ssr = ssrConfig.ssr,
.structure = structure
},
colorBufferDesc, config, colorGradingConfigForColor, pass.getExecutor());
if (view.isScreenSpaceRefractionEnabled() && !pass.empty()) {
// this cancels the colorPass() call above if refraction is active.
// the color pass + refraction + color-grading as subpass if needed
const auto [output, enabled] = RendererUtils::refractionPass(fg, mEngine, view,
// This cancels the colorPass() call above if refraction is active.
// The color pass + refraction + color-grading as subpass if needed
auto const output = RendererUtils::refractionPass(fg, mEngine, view, {
.shadows = blackboard.get<FrameGraphTexture>("shadows"),
.ssao = blackboard.get<FrameGraphTexture>("ssao"),
.ssr = ssrConfig.ssr,
.structure = structure
},
config, ssrConfig, colorGradingConfigForColor, pass);
colorPassOutput = output;
hasScreenSpaceRefraction = enabled;
hasScreenSpaceRefraction = output.has_value();
if (hasScreenSpaceRefraction) {
colorPassOutput = output.value();
}
}
fg.addTrivialSideEffectPass("Finish Color Passes", [&view](DriverApi& driver) {
// Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass.
view.cleanupRenderPasses();
view.commitUniforms(driver);
});
if (colorGradingConfig.customResolve) {
assert_invariant(fg.getDescriptor(colorPassOutput.linearColor).samples <= 1);
// TODO: we have to "uncompress" (i.e. detonemap) the color buffer here because it's used
// by many other passes (Bloom, TAA, DoF, etc...). We could make this more
// efficient by using ARM_shader_framebuffer_fetch. We use a load/store (i.e.
// subpass) here because it's more convenient.
colorPassOutput = ppm.customResolveUncompressPass(fg, colorPassOutput);
colorPassOutput.linearColor =
ppm.customResolveUncompressPass(fg, colorPassOutput.linearColor);
}
// export the color buffer if screen-space reflections are enabled
@@ -1128,7 +1167,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// The "output" of this pass is going to be used during the next frame as
// an "import".
builder.sideEffect();
data.history = builder.sample(colorPassOutput); // FIXME: an access must be declared for detach(), why?
// we can't use colorPassOutput here because it could be tonemapped
data.history = builder.sample(colorPassOutput.linearColor); // FIXME: an access must be declared for detach(), why?
}, [&view, projection](FrameGraphResources const& resources, auto const& data,
backend::DriverApi&) {
auto& history = view.getFrameHistory();
@@ -1138,19 +1179,20 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
});
}
FrameGraphId<FrameGraphTexture> input = colorPassOutput;
fg.addTrivialSideEffectPass("Finish Color Passes", [&view](DriverApi& driver) {
// Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass.
view.cleanupRenderPasses();
view.commitUniforms(driver);
});
// this is the output of the color pass / input to post processing,
// this is only used later for comparing it with the output after post-processing
FrameGraphId<FrameGraphTexture> const postProcessInput = colorGradingConfig.asSubpass ?
colorPassOutput.tonemappedColor :
colorPassOutput.linearColor;
// input can change below
FrameGraphId<FrameGraphTexture> input = postProcessInput;
// Resolve depth -- which might be needed because of TAA or DoF. This pass will be culled
// if the depth is not used below or if the depth is not MS (e.g. it could have been
// auto-resolved).
// In practice, this is used on Vulkan and older Metal devices.
auto depth = blackboard.get<FrameGraphTexture>("depth");
depth = ppm.resolve(fg, "Resolved Depth Buffer", depth, { .levels = 1 });
auto depth = ppm.resolve(fg, "Resolved Depth Buffer", colorPassOutput.depth, { .levels = 1 });
// Debug: CSM visualisation
if (UTILS_UNLIKELY(engine.debug.shadowmap.visualize_cascades &&
@@ -1266,7 +1308,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// The intermediate buffer is accomplished with a "fake" opaqueBlit (i.e. blit) operation.
const bool outputIsSwapChain =
(input == colorPassOutput) && (viewRenderTarget == mRenderTargetHandle);
(input == postProcessInput) && (viewRenderTarget == mRenderTargetHandle);
if (mightNeedFinalBlit) {
if (blendModeTranslucent ||
xvp != svp ||
+7
View File
@@ -27,6 +27,8 @@
#include <math/half.h>
#include <math/mat4.h>
#include <utils/CString.h>
#include <cstring>
namespace filament {
@@ -80,6 +82,11 @@ FSkinningBuffer::FSkinningBuffer(FEngine& engine, const Builder& builder)
BufferObjectBinding::UNIFORM,
BufferUsage::DYNAMIC);
if (auto name = builder.getName(); !name.empty()) {
// TODO: We should also tag the texture created inside createIndicesAndWeightsHandle.
driver.setDebugTag(mHandle.getId(), std::move(name));
}
if (builder->mInitialize) {
// initialize the bones to identity (before rounding up)
auto* out = driver.allocatePod<PerRenderableBoneUib::BoneData>(mBoneCount);
+5 -1
View File
@@ -23,10 +23,10 @@
#include <backend/PixelBufferDescriptor.h>
#include <utils/CString.h>
#include <utils/Panic.h>
#include <filament/Stream.h>
namespace filament {
using namespace backend;
@@ -80,6 +80,10 @@ FStream::FStream(FEngine& engine, const Builder& builder) noexcept
} else {
mStreamHandle = engine.getDriverApi().createStreamAcquired();
}
if (auto name = builder.getName(); !name.empty()) {
engine.getDriverApi().setDebugTag(mStreamHandle.getId(), std::move(name));
}
}
void FStream::terminate(FEngine& engine) noexcept {
+5
View File
@@ -243,6 +243,11 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) {
mHandle = driver.importTexture(builder->mImportedId,
mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage);
}
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
} else {
driver.setDebugTag(mHandle.getId(), CString{"FTexture"});
}
}
// frees driver resources, object becomes invalid
+10 -1
View File
@@ -29,6 +29,7 @@
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
@@ -259,7 +260,9 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
mBufferCount, mDeclaredAttributes.count(), mAttributes);
mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), name);
}
// calculate buffer sizes
size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {};
@@ -287,6 +290,9 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
if (!mBufferObjects[i]) {
BufferObjectHandle bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(bo.getId(), name);
}
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
@@ -303,6 +309,9 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
if (!mBufferObjects[i]) {
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(bo.getId(), name);
}
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
+51 -27
View File
@@ -31,15 +31,17 @@
#include "details/Skybox.h"
#include <filament/Exposure.h>
#include <filament/DebugRegistry.h>
#include <filament/TextureSampler.h>
#include <filament/View.h>
#include <private/filament/UibStructs.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Profiler.h>
#include <utils/Slice.h>
#include <utils/Systrace.h>
#include <utils/debug.h>
#include <utils/Zip2Iterator.h>
#include <math/scalar.h>
@@ -47,6 +49,7 @@
#include <array>
#include <memory>
#include <tuple>
using namespace utils;
@@ -78,21 +81,29 @@ FView::FView(FEngine& engine)
mPidController.setOutputDeadBand(-0.01f, 0.05f);
#ifndef NDEBUG
debugRegistry.registerDataSource("d.view.frame_info",
[this]() -> DebugRegistry::DataSource {
assert_invariant(!mDebugFrameHistory);
mDebugFrameHistory = std::make_unique<std::array<DebugRegistry::FrameHistory, 5*60>>();
return { mDebugFrameHistory->data(), mDebugFrameHistory->size() };
// This can fail if another view has already registered this data source
mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info",
[weak = std::weak_ptr<DebugState>(mDebugState)]() -> DebugRegistry::DataSource {
// the View could have been destroyed by the time we do this
auto const state = weak.lock();
if (!state) {
return { nullptr, 0 };
}
// Lazily allocate the buffer for the debug data source, and mark this
// data source as active. It can never go back to inactive.
assert_invariant(!state->debugFrameHistory);
state->active = true;
state->debugFrameHistory =
std::make_unique<std::array<DebugRegistry::FrameHistory, 5 * 60>>();
return { state->debugFrameHistory->data(), state->debugFrameHistory->size() };
});
debugRegistry.registerProperty("d.view.pid.kp", &engine.debug.view.pid.kp);
debugRegistry.registerProperty("d.view.pid.ki", &engine.debug.view.pid.ki);
debugRegistry.registerProperty("d.view.pid.kd", &engine.debug.view.pid.kd);
// default parameters for debugging UI
engine.debug.view.pid.kp = 1.0f - std::exp(-1.0f / 8.0f);
engine.debug.view.pid.ki = PID_CONTROLLER_Ki;
engine.debug.view.pid.kd = PID_CONTROLLER_Kd;
mPidController.setParallelGains(
engine.debug.view.pid.kp, engine.debug.view.pid.ki, engine.debug.view.pid.kd);
if (UTILS_UNLIKELY(mDebugState->owner)) {
// publish the properties (they will be initialized in the main loop)
debugRegistry.registerProperty("d.view.pid.kp", &engine.debug.view.pid.kp);
debugRegistry.registerProperty("d.view.pid.ki", &engine.debug.view.pid.ki);
debugRegistry.registerProperty("d.view.pid.kd", &engine.debug.view.pid.kd);
}
#endif
// allocate UBOs
@@ -126,6 +137,12 @@ void FView::terminate(FEngine& engine) {
mFroxelizer.terminate(driver);
engine.getEntityManager().destroy(mFogEntity);
#ifndef NDEBUG
if (UTILS_UNLIKELY(mDebugState->owner)) {
engine.getDebugRegistry().unregisterDataSource("d.view.frame_info");
}
#endif
}
void FView::setViewport(filament::Viewport const& viewport) noexcept {
@@ -167,6 +184,16 @@ float2 FView::updateScale(FEngine& engine,
Renderer::FrameRateOptions const& frameRateOptions,
Renderer::DisplayInfo const& displayInfo) noexcept {
#ifndef NDEBUG
if (UTILS_LIKELY(!mDebugState->active)) {
// if we're not active, update the debug properties with the normal values
// and use that for configuring the PID controller.
engine.debug.view.pid.kp = 1.0f - std::exp(-frameRateOptions.scaleRate);
engine.debug.view.pid.ki = PID_CONTROLLER_Ki;
engine.debug.view.pid.kd = PID_CONTROLLER_Kd;
}
#endif
DynamicResolutionOptions const& options = mDynamicResolution;
if (options.enabled) {
if (!UTILS_UNLIKELY(info.valid)) {
@@ -250,14 +277,15 @@ float2 FView::updateScale(FEngine& engine,
#ifndef NDEBUG
// only for debugging...
if (mDebugFrameHistory) {
if (UTILS_UNLIKELY(mDebugState->active && mDebugState->debugFrameHistory)) {
auto* const debugFrameHistory = mDebugState->debugFrameHistory.get();
using namespace std::chrono;
using duration_ms = duration<float, std::milli>;
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
std::move(mDebugFrameHistory->begin() + 1,
mDebugFrameHistory->end(), mDebugFrameHistory->begin());
mDebugFrameHistory->back() = {
std::move(debugFrameHistory->begin() + 1,
debugFrameHistory->end(), debugFrameHistory->begin());
debugFrameHistory->back() = {
.target = target,
.targetWithHeadroom = targetWithHeadroom,
.frameTime = duration_cast<duration_ms>(info.frameTime).count(),
@@ -1012,10 +1040,8 @@ void FView::commitFrameHistory(FEngine& engine) noexcept {
auto& frameHistory = mFrameHistory;
FrameHistoryEntry& last = frameHistory.back();
disposer.destroy(last.taa.color.handle);
disposer.destroy(last.ssr.color.handle);
last.taa.color.handle.clear();
last.ssr.color.handle.clear();
disposer.destroy(std::move(last.taa.color.handle));
disposer.destroy(std::move(last.ssr.color.handle));
// and then push the new history entry to the history stack
frameHistory.commit();
@@ -1027,10 +1053,8 @@ void FView::clearFrameHistory(FEngine& engine) noexcept {
auto& frameHistory = mFrameHistory;
for (size_t i = 0; i < frameHistory.size(); ++i) {
FrameHistoryEntry& last = frameHistory[i];
disposer.destroy(last.taa.color.handle);
disposer.destroy(last.ssr.color.handle);
last.taa.color.handle.clear();
last.ssr.color.handle.clear();
disposer.destroy(std::move(last.taa.color.handle));
disposer.destroy(std::move(last.ssr.color.handle));
}
}
+9 -4
View File
@@ -201,9 +201,9 @@ public:
void setStereoscopicOptions(StereoscopicOptions const& options) noexcept;
FCamera const* getDirectionalShadowCamera() const noexcept {
if (!mShadowMapManager) return nullptr;
return mShadowMapManager->getDirectionalShadowCamera();
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept {
if (!mShadowMapManager) return {};
return mShadowMapManager->getDirectionalShadowCameras();
}
void setRenderTarget(FRenderTarget* renderTarget) noexcept {
@@ -576,7 +576,12 @@ private:
}};
#ifndef NDEBUG
std::unique_ptr<std::array<DebugRegistry::FrameHistory, 5*60>> mDebugFrameHistory;
struct DebugState {
std::unique_ptr<std::array<DebugRegistry::FrameHistory, 5*60>> debugFrameHistory{};
bool owner = false;
bool active = false;
};
std::shared_ptr<DebugState> mDebugState{ new DebugState };
#endif
};
+3 -3
View File
@@ -190,10 +190,10 @@ void RenderPassNode::resolve() noexcept {
minHeight = std::min(minHeight, h);
maxHeight = std::max(maxHeight, h);
}
// additionally, clear implies discardStart
rt.backend.params.flags.discardStart |= (
rt.descriptor.clearFlags & rt.targetBufferFlags);
}
// additionally, clear implies discardStart
rt.backend.params.flags.discardStart |= (
rt.descriptor.clearFlags & rt.targetBufferFlags);
assert_invariant(minWidth == maxWidth);
assert_invariant(minHeight == maxHeight);
+5 -1
View File
@@ -10,6 +10,10 @@ material {
type : int,
name : layerIndex
},
{
type : float,
name : levelOfDetail,
},
{
type : float4,
name : viewport,
@@ -33,6 +37,6 @@ vertex {
fragment {
void postProcess(inout PostProcessInputs postProcess) {
postProcess.color = textureLod(materialParams_color, vec3(variable_vertex.xy, materialParams.layerIndex), 0.0);
postProcess.color = textureLod(materialParams_color, vec3(variable_vertex.xy, materialParams.layerIndex), materialParams.levelOfDetail);
}
}
+5 -1
View File
@@ -10,6 +10,10 @@ material {
type : float4,
name : viewport,
precision: high
},
{
type : float,
name : levelOfDetail,
}
],
variables : [
@@ -33,7 +37,7 @@ fragment {
#if FILAMENT_EFFECTIVE_VERSION == 100
postProcess.color = texture2D(materialParams_color, variable_vertex.xy);
#else
postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0);
postProcess.color = textureLod(materialParams_color, variable_vertex.xy, materialParams.levelOfDetail);
#endif
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.53.5"
spec.version = "1.54.3"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.53.5/filament-v1.53.5-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.54.3/filament-v1.54.3-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -92,6 +92,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialVertexDomain = charTo64bitNum("MAT_VEDO"),
MaterialInterpolation = charTo64bitNum("MAT_INTR"),
MaterialStereoscopicType = charTo64bitNum("MAT_STER"),
DictionaryText = charTo64bitNum("DIC_TEXT"),
DictionarySpirv = charTo64bitNum("DIC_SPIR"),
@@ -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 = 53;
static constexpr size_t MATERIAL_VERSION = 54;
/**
* Supported shading models
+2 -1
View File
@@ -166,7 +166,8 @@ ssize_t BufferInterfaceBlock::getFieldOffset(std::string_view name, size_t index
BufferInterfaceBlock::FieldInfo const* BufferInterfaceBlock::getFieldInfo(
std::string_view name) const {
auto pos = mInfoMap.find(name);
FILAMENT_CHECK_PRECONDITION(pos != mInfoMap.end()) << "uniform named \"%.*s\" not found";
FILAMENT_CHECK_PRECONDITION(pos != mInfoMap.end()) << "uniform named \""
<< name << "\" not found";
return &mFieldInfoList[pos->second];
}
+8 -2
View File
@@ -40,14 +40,20 @@ endif()
file(MAKE_DIRECTORY ${MATERIAL_DIR})
set (MATC_FLAGS ${MATC_BASE_FLAGS})
if (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced")
set (MATC_FLAGS ${MATC_FLAGS} -PstereoscopicType=instanced)
elseif (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
set (MATC_FLAGS ${MATC_FLAGS} -PstereoscopicType=multiview)
endif ()
foreach (mat_src ${MATERIAL_SRCS})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
COMMAND matc ${MATC_FLAGS} -o ${output_path} ${fullname}
DEPENDS ${mat_src} matc
COMMENT "Compiling material ${mat_src} to ${output_path}"
)
+10 -1
View File
@@ -323,6 +323,9 @@ public:
//! Custom variables (all float4).
MaterialBuilder& variable(Variable v, const char* name) noexcept;
MaterialBuilder& variable(Variable v, const char* name,
ParameterPrecision precision) noexcept;
/**
* Require a specified attribute.
*
@@ -708,11 +711,17 @@ public:
ShaderStage stage;
};
struct CustomVariable {
utils::CString name;
Precision precision = Precision::DEFAULT;
bool hasPrecision = false;
};
static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT;
using Property = filament::Property;
using PropertyList = bool[MATERIAL_PROPERTIES_COUNT];
using VariableList = utils::CString[MATERIAL_VARIABLES_COUNT];
using VariableList = CustomVariable[MATERIAL_VARIABLES_COUNT];
using OutputList = std::vector<Output>;
static constexpr size_t MAX_COLOR_OUTPUT = filament::backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
+17 -2
View File
@@ -234,7 +234,21 @@ MaterialBuilder& MaterialBuilder::variable(Variable v, const char* name) noexcep
case Variable::CUSTOM2:
case Variable::CUSTOM3:
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
mVariables[size_t(v)] = CString(name);
mVariables[size_t(v)] = { CString(name), Precision::DEFAULT, false };
break;
}
return *this;
}
MaterialBuilder& MaterialBuilder::variable(Variable v,
const char* name, ParameterPrecision precision) noexcept {
switch (v) {
case Variable::CUSTOM0:
case Variable::CUSTOM1:
case Variable::CUSTOM2:
case Variable::CUSTOM3:
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
mVariables[size_t(v)] = { CString(name), precision, true };
break;
}
return *this;
@@ -1383,7 +1397,7 @@ bool MaterialBuilder::checkMaterialLevelFeatures(MaterialInfo const& info) const
bool MaterialBuilder::hasCustomVaryings() const noexcept {
for (const auto& variable : mVariables) {
if (!variable.empty()) {
if (!variable.name.empty()) {
return true;
}
}
@@ -1609,6 +1623,7 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
}
}
container.emplace<uint64_t>(ChunkType::MaterialProperties, properties);
container.emplace<uint8_t>(ChunkType::MaterialStereoscopicType, static_cast<uint8_t>(mStereoscopicType));
}
// create a unique material id

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