Compare commits

...
Author SHA1 Message Date
Benjamin Doherty 6dbee125b3 Make some headers public so JobSystem.h can be used 2022-11-03 09:39:01 -07:00
daemyung jang e64b498d6e Update a comment about the range of morph target weight (#6265) 2022-11-03 09:19:01 -07:00
Mathias Agopian 31ab4a13c9 fix recently introduced crash on init
certain material properties can only be set if supported by the 
material.
2022-11-01 13:23:52 -07:00
Benjamin Doherty e9728dcc8d Fix iOS build 2022-11-01 15:03:44 -04:00
Mathias Agopian 40a3644086 Reduce the size of ShadowMap to 40 bytes from 2KB+
ShadowMap had recently inflated due to its internal use of 
PerViewUniforms, which keeps a shadow copy of the uniform data. This
copy is not needed for ShadowMaps.
2022-11-01 11:58:28 -07:00
Mathias Agopian 41ef71abdf Add MaterialInstance getters for overridable properties (#6243)
In addition to the new getters, this change fixes the duplication of
a MaterialInstance, which didn't carry along the following states:
  - alpha mask threshold
  - specular AA threshold
  - specular AA variance
  - double sidedness

TODO: stencil state, polygon offset and scissor are stil not queryable
2022-11-01 11:22:01 -07:00
daemyung jangandRomain Guy 3b40e52c98 Recompute bounding boxes with morph targets (#6246)
* Recompute bounding boxes with morph targets

* Reduce the number of heap allocation while recomputing bounding boxes

Co-authored-by: Romain Guy <romainguy@curious-creature.com>
2022-11-01 11:20:28 -07:00
daemyung jang de8c7969f3 Return the correct permit type corresponding to the component type (#6255) 2022-11-01 11:03:27 -07:00
Benjamin Doherty f2c049222b Release Filament 1.28.2 2022-11-01 13:01:13 -04:00
Ben Doherty be30ac1fcb Fix G3 reported ubsan warning (#6257) 2022-11-01 12:50:27 -04:00
Ben Doherty c5cc7fe26a Metal: generate argument buffer structures ourselves (#6242) 2022-11-01 12:44:42 -04:00
Ben Doherty d04b0a5c73 Update Vulkan headers to v1.3.232 (#6256) 2022-11-01 12:38:23 -04:00
Mathias Agopian 72ab2aef0a fix UBO handle leak with ShadowMap
This wasn't a big issue because the "leak" would be cleaned when the
engine is destroyed.
2022-10-31 11:29:46 -07:00
Koncz Levente 1527bb7ad3 Preserve iOS stack traces (#6240) 2022-10-31 13:58:31 -04:00
daemyung jang 8cb26fc9ac Fix a build error on Android
The size of struct could be different on each architecture.
2022-10-30 20:52:56 -07:00
Matt Vera c21794d0a0 Add Vulkan GGP platform. (#6235)
* Add GGP vulkan platform.

* Add release note for GGP support
2022-10-28 11:53:29 -07:00
daemyung jangandMathias Agopian 1a6338569a Calculate primitive's AABB correctly (#6232)
AABB of morph target is displacement data so it should be added to
primitive's base AABB.

Co-authored-by: Mathias Agopian <mathias@google.com>
2022-10-28 11:51:02 -07:00
Mathias Agopian c5ebe1a88a skip primitives with FRONT_AND_BACK face culling 2022-10-28 11:50:21 -07:00
Mathias Agopian a3f2028ae4 Reduce the size of PerViewUniforms by 24 bytes
This seems a bit pointless because PerViewUniforms has a 2KB field,
however, it adds up now that it's used by ShadowMap.
2022-10-28 11:49:55 -07:00
Mathias Agopian c393ee4591 Each shadowmap now manages its own UBO
Instead of constantly reusing the View's UBO, each shadowmap now
manages it own. All UBOs for all shadow maps are updated together, 
then later during rendering, we're simply binding the correct UBO.
This change will be needed later so that we can update several
shadow maps in a single texture.

There are two main changes to achieve this:
1. ShadowMap now has PerViewUniforms object
2. For point lights, each face now has a ShadowMap object

One consequence is that ShadowMap is now very large (over 2KB). This
will be addressed later.
2022-10-28 11:49:55 -07:00
Mathias Agopian 83a82ae10e shadowing: store Executor instead of whole RenderPass
We didn't need to store the actual RenderPass for each shadowmap pass,
the executor is enough. This might prevent some mistakes later.
2022-10-28 11:47:12 -07:00
daemyung jang 676694e458 Fix glTF breaking issue (#6239) 2022-10-28 09:56:01 -07:00
Mathias Agopian 2ca218928f utils: get rid of GrowingSlice
it's just not used anywhere. change the implementation of Slice<> to
use two pointers.
2022-10-27 22:48:45 -07:00
Philip Rideout 776ce223fc Fix incorrect Vulkan documentation wrt layouts. 2022-10-26 19:18:32 -07:00
Philip Rideout 0d4d1f55c9 Updates to the Vulkan architecture doc. 2022-10-26 19:18:32 -07:00
Romain Guy d6dba71ace Update docs 2022-10-26 12:28:56 -07:00
Mathias AgopianandRomain Guy 61ee11ae6c fix the doubleSided property documentation (#6228)
* fix the `doubleSided` property documentation

* Update Materials.md.html

Co-authored-by: Romain Guy <romainguy@curious-creature.com>
2022-10-26 12:28:03 -07:00
Mathias Agopian 90f32e7277 A basic POT 2D allocator
This 2D allocator only supports power-of-two, square allocations. It
uses a quadtree to store the allocated regions.

The quadtree implementation currently has a fixed depth (templated).

The allocator does a best-fit match, but currently doesn't implement a
free method.
2022-10-26 11:02:17 -07:00
daemyung jang b85d9afdb3 Remove an useless whitespace (#6226) 2022-10-26 00:10:33 -07:00
Mathias Agopian 4297d43d9a more improvements to RenderPass
- RenderPass::Executor now only executes the commands, it doesn't 
  create a backend render pass anymore. This is still done in the
  RenderPass::execute() helper.

- scissor and poloyon offset overrides are now set on Executor because
  they are not needed to generate the commands.
2022-10-25 14:38:56 -07:00
Mathias Agopian cfcf714c21 Get rid of PrimitiveType::NONE 2022-10-25 14:38:08 -07:00
Benjamin Doherty f7cfc6a65e Release Filament 1.28.1 2022-10-25 13:07:56 -04:00
Benjamin Doherty 4ead3bcebf Force spirv-cross to keep Metal argument buffer equal sized 2022-10-25 12:07:24 -04:00
Balazs Vegh 21e0677039 Check GPU support for depth resolve on Metal (#6220) 2022-10-25 11:55:17 -04:00
Ben Doherty 107b5a4854 Metal: fix microshadowing artifacts (#6221) 2022-10-24 19:12:14 -04:00
Balazs Vegh 9761b65137 Fix Metal unused variable warning (#6217) 2022-10-24 12:41:58 -04:00
Ben Doherty b1831d2786 Fix, don't use encoder.device (#6212) 2022-10-20 13:05:07 -04:00
Benjamin Doherty 977d369e40 Fix unused variable warnings 2022-10-19 12:44:07 -04:00
Josh Faust 5cf5b25175 Add a function to reset internal GL backend state (#6105)
This allows bug-free integration with external renderers in WebGL

See https://github.com/google/filament/issues/6091
2022-10-19 09:31:11 -07:00
Ben Doherty e75c3020c8 Initial support for Metal compute shaders / SSBOs (#6189) 2022-10-18 16:21:57 -04:00
k1rnt aeb0a4008d fix(ci): replace set-output to GITHUB_OUTPUT (#6195) 2022-10-18 13:41:25 -04:00
Benjamin Doherty 2d43ad59ec Revert Android API_LEVEL change
glDispatchCompute requires Android API level 21, however bumping our
required minimum from 19 to 21 caused some clients' builds to fail.
Commenting-out that line for now to proceed with the 1.28.0 upgrade.
2022-10-18 13:34:50 -04:00
daemyung jang f86ec07c45 Fix ios hello-gltf runtime errors 2022-10-18 10:19:22 -07:00
Ben Doherty fc012d6d65 Metal: fix use-after-free ASAN error (#6203) 2022-10-17 19:13:36 -04:00
Philip Rideout 6b71232e38 matc / matinfo / filamesh: add vulnerability message 2022-10-17 15:49:35 -07:00
Philip Rideout e157023fc1 Use mField style for fields in FFilamentInstance 2022-10-17 15:49:19 -07:00
Philip Rideout 6e38db882f Fix installed IBL location for desktop.
Fixes #6193
2022-10-17 12:30:11 -07:00
daemyung jang 09600699cb Update README.md 2022-10-17 09:57:58 -07:00
daemyung jang 6a5e74f031 Update README for meshoptimizer 2022-10-17 09:57:58 -07:00
daemyung jang 795bd1e995 Fix build errors in ios samples 2022-10-17 09:57:58 -07:00
daemyung jang 784416f658 Support glTF EXT_meshopt_compression 2022-10-17 09:57:58 -07:00
daemyung jang b151afe567 Update meshoptimizer to version 0.18 2022-10-17 09:57:58 -07:00
Mathias Agopian 117ae1d648 RenderPass doesn't store an FEngine anymore 2022-10-14 15:59:03 -07:00
Mathias Agopian 7165d8aa54 fix a directional light shadowing issue
the shadow frustum could be be smaller than expected.
2022-10-14 13:16:14 -07:00
Ben Doherty 6b461b2d89 Metal: fix simulator regression due to arg buffers (#6181) 2022-10-13 14:34:33 -04:00
Benjamin Doherty affc5be840 Release Filament 1.28.0 2022-10-13 12:35:45 -04:00
Ben Doherty e3ff5616ef G3 fixes for 1.28.0 (#6174) 2022-10-11 17:02:46 -07:00
Mathias Agopian d9a401d57e Reduce size of ShadowMap object
- remove mEngine from ShadowMap
- remove storage of ShadowMapInfo in ShadowMap
- ShadowMap::render should be on ShadowMapManager
- remove more attributes from ShadowMap

ShadowMap is now reduced to 32 bytes.
2022-10-10 13:03:36 -07:00
Mathias Agopian acd5823953 Add support for point light shadows
[experimental]
2022-10-10 13:03:36 -07:00
Mathias Agopian 9b13565750 spotlight shadow map skipped if the scene is empty. 2022-10-10 13:03:36 -07:00
Mathias Agopian 734d8ff85c rename upcast() to downcast()
it was down casting all along!
2022-10-07 17:50:56 -07:00
Mathias Agopian a02ef1a6c7 Remove the limit to 14 spot shadow maps, now 64.
Spotlight shadow maps are now limited only by h/w limits (UBO size and
2d array maximum layers), which works out to about 146 shadows.

In practice we hardcode the limite to 64, but this can be changed at
compile time. A higher maximum increases memory usage.

There used to be a bitfield per renderable with a bit for each shadow
map indicating if the renderable was "visible" from this light. The
limit to 14 came from that bitfield. All shadow maps used to be culled
first, setting the bitfield appropriately.

Instead, we are now processing spotlight shadows one at a time,
performing culling as we go. For this reason we don't need a bit per
shadow map.
2022-10-07 14:07:51 -07:00
Mathias Agopian f13bf11d0f simplify command generation
No need to generate a sentinel command in the main loop when a 
command is canceled, this is now done automatically at a higher level.
The main benefit is that we can reject renderables that are not visible
a bit faster.
2022-10-07 14:07:51 -07:00
Ben Doherty 7d0b5fed8f Fix Metal argument buffer encoding (#6150) 2022-10-07 13:53:26 -07:00
Philip Rideout e1f51f04c1 Vulkan: fix black screen regression
The VulkanProgram constructor was bailing out early and emitting a
warning because it saw that one of the stages wasn't fulfilled.
However it's okay for a pipeline to be missing a compute program.

Fixes regression that started with fabba73b1.
2022-10-04 10:47:16 -07:00
Philip Rideout f6d74be123 Fix windows build. 2022-10-03 12:35:35 -07:00
Philip Rideout 50a1935b68 Vulkan performance improvements for readPixels. 2022-10-03 09:54:35 -07:00
Mathias Agopian 694ecce150 fix shadowing when the scene is outside the "shadow" view frustum
this case was causing uninitialized variables to be used creating a
false positive "shadows on" in the first cascade.

The main change here is to bail out as soon as we know there is no shadowing.
2022-09-30 16:12:36 -07:00
Mathias Agopian 3e3bd2722f fix lispsm when camera and light direction are aligned
in that case we have to revert to the ortho projection
2022-09-30 16:12:15 -07:00
Mathias Agopian 44fdf83e4c fix multiview mode in gltf_viewer
The "shadow" camera now works again.
2022-09-30 16:12:15 -07:00
Benjamin Doherty 99c4ce67e4 Print warning when Metal reaches memoryless geo limit 2022-09-30 13:59:49 -07:00
Balazs Vegh 71aceec4cf Memoryless storage mode with fallback (Metal) (#6124) 2022-09-30 13:54:54 -07:00
Philip Rideout 98e4cdbd4c gltfio ubershader mode: fix re-loading.
Fixes #6128
2022-09-30 13:54:36 -07:00
Mathias Agopian 86ef0109f4 use the correct gaussian blur shader
we attempted to select a shader which processed the required number of
component, but were always selecting the 4-components version.

this should improve blur performance for VSM.
2022-09-30 13:30:15 -07:00
Mathias Agopian b4142261c2 trivial typo and ide warnings fixes 2022-09-30 13:30:15 -07:00
Mathias Agopian 359624e8eb Fix gaussian blur kernel computation
This improves significantly VSM shadows with blur,
 but will also improve SSR and the flare.
2022-09-30 13:30:15 -07:00
Mathias Agopian 5bcc11d9e8 fix typo that broke the VSM blur radius setting 2022-09-30 13:30:15 -07:00
Mathias Agopian 5aa414dd24 support for ELVSM and 32 bits xVSM
This basically adds two settings:
- highPrecision at the View level, which controls the bit depth
  of the vsm shadow texture used. This affects all shadowmaps.
- elvsm per shadowmap, which enables Exponential Layered VSM.

the main change in the shaders is that we now always output the 
"negative" EVSM, even when it's not enabled. If no shadowmap uses
ELVSM, then the texture is stil an RG texture and the calculation is
lost (with some luck culled by the shader compiler), either way it's
not a lot of math and it's done only once per shadow texel.

During the color pass, on the other hand, we compute the "negative"
EVSM only if enabled.
2022-09-30 12:04:27 -07:00
Mathias Agopian 1e51b03971 fix VSM 2nd moment calculation
the linear approximation only works with a linear depth, it fails
with our warped depth.
2022-09-30 12:04:27 -07:00
Mathias Agopian fc484bd319 fix "stable" shadows
We were always adjusting the near/far of the view volume based on the
scene content for shadowing, which defeated the "stable" shadows.

Also changed the default cascade splits from a linear split to a
log2 split, because due to the perspective projection, the log2 split
actually looks linear. Also intuitively, it makes more sense to give
more resolution to the shadows close to the camera.
2022-09-30 12:00:02 -07:00
Mathias Agopian 9c4783142e matc: minor cleanup 2022-09-29 19:34:07 -07:00
Mathias Agopian 4dc03318e8 break circular dependency between Package and Flattener 2022-09-29 19:34:07 -07:00
Philip Rideout 15ba54c4c5 Update README wrt Android Studio Version. 2022-09-29 16:28:50 -07:00
Philip Rideout 342138aecb gltfio: Update Java API. 2022-09-29 16:28:50 -07:00
Philip Rideout 9f53c62e06 gltfio: Update Web API.
Java API changes will come next.
2022-09-29 16:28:50 -07:00
Philip Rideout 2d996033e3 gltfio API change part 2: remove asset-level animator 2022-09-29 16:28:50 -07:00
Philip Rideout 9fa6dc4131 gltfio API change part 1: move all MaterialInstance methods to FilamentInstance 2022-09-29 16:28:50 -07:00
daemyung jangandPhilip Rideout 4930227743 Fix a crash by setting not exist material parameters on ubershader (#6122)
* Fix a crash by setting not exist material parameters on ubershader

* gltfio: add getFeatureMap, remove logic for dummy textures

Co-authored-by: Philip Rideout <philiprideout@gmail.com>
2022-09-29 15:51:35 -07:00
343 changed files with 48171 additions and 5374 deletions
+10 -10
View File
@@ -39,8 +39,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -69,8 +69,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -97,8 +97,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -143,8 +143,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -171,8 +171,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
shell: bash
- uses: actions/checkout@v2.0.0
with:
+10 -6
View File
@@ -337,19 +337,23 @@ endif()
# ==================================================================================================
# Release compiler flags
# ==================================================================================================
if (NOT MSVC)
if (NOT MSVC AND NOT IOS)
# Omitting stack frame pointers prevents the generation of readable stack traces in crash reports on iOS
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer")
# These aren't compatible with -fembed-bitcode (and seem to have no effect on Apple platforms anyway)
if (NOT IOS)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffunction-sections -fdata-sections")
endif()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffunction-sections -fdata-sections")
endif()
# On Android RELEASE builds, we disable exceptions and RTTI to save some space (about 75 KiB
# saved by -fno-exception and 10 KiB saved by -fno-rtti).
if (ANDROID OR IOS OR WEBGL)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-rtti")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions -fno-rtti")
if (ANDROID OR WEBGL)
# Omitting unwind info prevents the generation of readable stack traces in crash reports on iOS
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-unwind-tables -fno-asynchronous-unwind-tables")
endif()
endif()
# With WebGL, we disable RTTI even for debug builds because we pass emscripten::val back and forth
@@ -666,7 +670,7 @@ add_subdirectory(${EXTERNAL}/imgui/tnt)
add_subdirectory(${EXTERNAL}/robin-map/tnt)
add_subdirectory(${EXTERNAL}/smol-v/tnt)
add_subdirectory(${EXTERNAL}/benchmark/tnt)
add_subdirectory(${EXTERNAL}/meshoptimizer)
add_subdirectory(${EXTERNAL}/meshoptimizer/tnt)
add_subdirectory(${EXTERNAL}/cgltf/tnt)
add_subdirectory(${EXTERNAL}/draco/tnt)
add_subdirectory(${EXTERNAL}/jsmn/tnt)
+3 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.27.2'
implementation 'com.google.android.filament:filament-android:1.28.2'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.27.2'
pod 'Filament', '~> 1.28.2'
```
### Snapshots
@@ -179,6 +179,7 @@ steps:
- [x] KHR_mesh_quantization
- [x] KHR_texture_basisu
- [x] KHR_texture_transform
- [x] EXT_meshopt_compression
## Rendering with Filament
+26
View File
@@ -5,6 +5,30 @@ A new header is inserted each time a *tag* is created.
## main branch
- backend: add support for GGP platform
- gltfio: calculate primitive's AABB correctly.
- gltfio: fix glTF breaking issue
- gltfio: recompute bounding boxes with morph targets
- engine: primitives with `CullingMode::FRONT_AND_BACK` are now skipped.
- engine: add missing getters on `MaterialInstance`
## v1.28.2
- gltfio: support EXT_meshopt_compression
- release packaging: fixed location of default IBL file
## v1.28.1
- gltfio: fix reloading crash in ubershader mode
- Vulkan: improve performance in the readPixels path
- engine: raise the spot shadows limit to 64, from 14.
- engine: add experimental support for point light shadows.
- gltfio: fix ubershader issues with assignment of dummy textures
- gltfio: material instances and variants are now accessed via `FilamentInstance` [⚠️ **API Change**]
- gltfio: the animator can now only be accessed via `FilamentInstance` [⚠️ **API Change**]
- engine: fix "stable" shadows and make the default cascade splits logarithmic.
- engine: Add new quality options to EVSM shadows + rendering fixes
## v1.28.0
- engine: LiSPSM is now a user settable option
@@ -13,6 +37,7 @@ A new header is inserted each time a *tag* is created.
- Metal: newer devices are no longer limited to 16 samplers per Material.
- gltfio: fix interpretation of occlusion strength
- engine: minsdk is now 21 instead of 19. This allows the use of OpenGL ES 3.1
- Vulkan: fix black screen regression
## v1.27.2
@@ -22,6 +47,7 @@ A new header is inserted each time a *tag* is created.
- gltfio: fix regression with meshes that have no material
- gltfio: fix regression with recomputeBoundingBoxes()
- filamesh / matinfo: fix minor ASAN issues
- engine: Added `Engine::resetBackendState()`, available only in WebGL builds
## v1.27.1
+1 -1
View File
@@ -72,7 +72,7 @@ buildscript {
}
ext.versions = [
'minSdk': 21,
'minSdk': 19,
'targetSdk': 33,
'compileSdk': 33,
'kotlin': '1.7.10',
@@ -79,7 +79,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
jfloat shadowFarHint, jboolean stable, jboolean lispsm,
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
jboolean screenSpaceContactShadows, jint stepCount,
jfloat maxShadowDistance, jint vsmMsaaSamples, jfloat blurWidth, jfloat shadowBulbRadius) {
jfloat maxShadowDistance, jint vsmMsaaSamples, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
LightManager::ShadowOptions shadowOptions {
.mapSize = (uint32_t)mapSize,
@@ -98,6 +98,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
.maxShadowDistance = maxShadowDistance,
.vsm = {
.msaaSamples = (uint8_t) vsmMsaaSamples,
.elvsm = (bool)elvsm,
.blurWidth = blurWidth
},
.shadowBulbRadius = shadowBulbRadius
@@ -452,3 +452,84 @@ Java_com_google_android_filament_MaterialInstance_nDuplicate(JNIEnv* env, jclass
}
return (jlong)mi;
}
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_MaterialInstance_nGetMaskThreshold(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nGetMaskThreshold()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->getMaskThreshold();
}
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingVariance(JNIEnv* env,
jclass clazz, jlong nativeMaterialInstance) {
// TODO: implement nGetSpecularAntiAliasingVariance()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->getSpecularAntiAliasingVariance();
}
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingThreshold(JNIEnv* env,
jclass clazz, jlong nativeMaterialInstance) {
// TODO: implement nGetSpecularAntiAliasingThreshold()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->getSpecularAntiAliasingThreshold();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsDoubleSided(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nIsDoubleSided()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->isDoubleSided();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_MaterialInstance_nGetCullingMode(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nGetCullingMode()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return (jint)instance->getCullingMode();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsColorWriteEnabled(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nIsColorWriteEnabled()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->isColorWriteEnabled();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsDepthWriteEnabled(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nIsDepthWriteEnabled()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->isDepthWriteEnabled();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsStencilWriteEnabled(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nIsStencilWriteEnabled()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->isStencilWriteEnabled();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsDepthCullingEnabled(JNIEnv* env, jclass clazz,
jlong nativeMaterialInstance) {
// TODO: implement nIsDepthCullingEnabled()
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return instance->isDepthCullingEnabled();
}
@@ -149,12 +149,13 @@ Java_com_google_android_filament_View_nSetShadowType(JNIEnv*, jclass, jlong nati
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetVsmShadowOptions(JNIEnv*, jclass, jlong nativeView,
jint anisotropy, jboolean mipmapping, jfloat minVarianceScale,
jint anisotropy, jboolean mipmapping, jboolean highPrecision, jfloat minVarianceScale,
jfloat lightBleedReduction) {
View* view = (View*) nativeView;
View::VsmShadowOptions options;
options.anisotropy = (uint8_t)anisotropy;
options.mipmapping = (bool)mipmapping;
options.highPrecision = (bool)highPrecision;
options.minVarianceScale = minVarianceScale;
options.lightBleedReduction = lightBleedReduction;
view->setVsmShadowOptions(options);
@@ -240,7 +240,7 @@ public class LightManager {
*/
@NonNull
@Size(min = 3)
public float[] cascadeSplitPositions = { 0.25f, 0.50f, 0.75f };
public float[] cascadeSplitPositions = { 0.125f, 0.25f, 0.50f };
/** Constant bias in world units (e.g. meters) by which shadows are moved away from the
* light. 1mm by default.
@@ -362,6 +362,15 @@ public class LightManager {
@IntRange(from = 1)
public int vsmMsaaSamples = 1;
/**
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
* an improvement to the default EVSM which suffers important light leaks. Enabling
* ELVSM for a single shadowmap doubles the memory usage of all shadow maps.
* ELVSM is mostly useful when large blurs are used.
*/
public boolean elvsm = false;
/**
* Blur width for the VSM blur. Zero do disable.
* The maximum value is 125.
@@ -486,11 +495,6 @@ public class LightManager {
/**
* Whether this Light casts shadows (disabled by default)
*
* <p>
* <b>warning:</b>
* {@link Type#POINT} lights cannot cast shadows.
* </p>
*
* @param enable Enables or disables casting shadows from this Light.
*
* @return This Builder, for chaining calls.
@@ -516,7 +520,7 @@ public class LightManager {
options.polygonOffsetConstant, options.polygonOffsetSlope,
options.screenSpaceContactShadows,
options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples,
options.blurWidth, options.shadowBulbRadius);
options.elvsm, options.blurWidth, options.shadowBulbRadius);
return this;
}
@@ -1181,7 +1185,7 @@ public class LightManager {
boolean stable, boolean lispsm,
float polygonOffsetConstant, float polygonOffsetSlope,
boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance,
int vsmMsaaSamples, float blurWidth, float shadowBulbRadius);
int vsmMsaaSamples, boolean elvsm, float blurWidth, float shadowBulbRadius);
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);
@@ -24,6 +24,7 @@ import com.google.android.filament.proguard.UsedByNative;
@UsedByNative("AssetLoader.cpp")
public class MaterialInstance {
private static final Material.CullingMode[] sCullingModeValues = Material.CullingMode.values();
private Material mMaterial;
private String mName;
private long mNativeObject;
@@ -473,6 +474,14 @@ public class MaterialInstance {
nSetMaskThreshold(getNativeObject(), threshold);
}
/**
* Gets the minimum alpha value a fragment must have to not be discarded when the blend
* mode is MASKED
*/
public float getMaskThreshold() {
return nGetMaskThreshold(getNativeObject());
}
/**
* Sets the screen space variance of the filter kernel used when applying specular
* anti-aliasing. The default value is set to 0.15. The specified value should be between
@@ -486,6 +495,14 @@ public class MaterialInstance {
nSetSpecularAntiAliasingVariance(getNativeObject(), variance);
}
/**
* Gets the screen space variance of the filter kernel used when applying specular
* anti-aliasing.
*/
public float getSpecularAntiAliasingVariance() {
return nGetSpecularAntiAliasingVariance(getNativeObject());
}
/**
* Sets the clamping threshold used to suppress estimation errors when applying specular
* anti-aliasing. The default value is set to 0.2. The specified value should be between 0
@@ -499,6 +516,14 @@ public class MaterialInstance {
nSetSpecularAntiAliasingThreshold(getNativeObject(), threshold);
}
/**
* Gets the clamping threshold used to suppress estimation errors when applying specular
* anti-aliasing.
*/
public float getSpecularAntiAliasingThreshold() {
return nGetSpecularAntiAliasingThreshold(getNativeObject());
}
/**
* Enables or disables double-sided lighting if the parent Material has double-sided capability,
* otherwise prints a warning. If double-sided lighting is enabled, backface culling is
@@ -512,6 +537,14 @@ public class MaterialInstance {
nSetDoubleSided(getNativeObject(), doubleSided);
}
/**
* Returns whether double-sided lighting is enabled when the parent Material has double-sided
* capability.
*/
public boolean isDoubleSided() {
return nIsDoubleSided(getNativeObject());
}
/**
* Overrides the default triangle culling state that was set on the material.
*
@@ -519,10 +552,18 @@ public class MaterialInstance {
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:culling">
* Rasterization: culling</a>
*/
public void setCullingMode(Material.CullingMode mode) {
public void setCullingMode(@NonNull Material.CullingMode mode) {
nSetCullingMode(getNativeObject(), mode.ordinal());
}
/**
* Returns the face culling mode.
*/
@NonNull
public Material.CullingMode getCullingMode() {
return sCullingModeValues[nGetCullingMode(getNativeObject())];
}
/**
* Overrides the default color-buffer write state that was set on the material.
*
@@ -534,6 +575,13 @@ public class MaterialInstance {
nSetColorWrite(getNativeObject(), enable);
}
/**
* Returns whether color write is enabled.
*/
public boolean isColorWriteEnabled() {
return nIsColorWriteEnabled(getNativeObject());
}
/**
* Overrides the default depth-buffer write state that was set on the material.
*
@@ -545,10 +593,27 @@ public class MaterialInstance {
nSetDepthWrite(getNativeObject(), enable);
}
/**
* Returns whether depth write is enabled.
*/
public boolean isDepthWriteEnabled() {
return nIsDepthWriteEnabled(getNativeObject());
}
/**
* Enables or Disable stencil writes
*/
public void setStencilWrite(boolean enable) {
nSetStencilWrite(getNativeObject(), enable);
}
/**
* Returns whether stencil write is enabled.
*/
public boolean isStencilWriteEnabled() {
return nIsStencilWriteEnabled(getNativeObject());
}
/**
* Overrides the default depth testing state that was set on the material.
*
@@ -560,6 +625,13 @@ public class MaterialInstance {
nSetDepthCulling(getNativeObject(), enable);
}
/**
* Returns whether depth culling is enabled.
*/
public boolean isDepthCullingEnabled() {
return nIsDepthCullingEnabled(getNativeObject());
}
/**
* Sets the stencil comparison function (default is {@link TextureSampler.CompareFunction#ALWAYS}).
*
@@ -856,4 +928,15 @@ public class MaterialInstance {
private static native long nGetMaterial(long nativeMaterialInstance);
private static native long nDuplicate(long otherNativeMaterialInstance, String name);
private static native float nGetMaskThreshold(long nativeMaterialInstance);
private static native float nGetSpecularAntiAliasingVariance(long nativeMaterialInstance);
private static native float nGetSpecularAntiAliasingThreshold(long nativeMaterialInstance);
private static native boolean nIsDoubleSided(long nativeMaterialInstance);
private static native int nGetCullingMode(long nativeMaterialInstance);
private static native boolean nIsColorWriteEnabled(long nativeMaterialInstance);
private static native boolean nIsDepthWriteEnabled(long nativeMaterialInstance);
private static native boolean nIsStencilWriteEnabled(long nativeMaterialInstance);
private static native boolean nIsDepthCullingEnabled(long nativeMaterialInstance);
}
@@ -793,7 +793,7 @@ public class View {
public void setVsmShadowOptions(@NonNull VsmShadowOptions options) {
mVsmShadowOptions = options;
nSetVsmShadowOptions(getNativeObject(), options.anisotropy, options.mipmapping,
options.minVarianceScale, options.lightBleedReduction);
options.highPrecision, options.minVarianceScale, options.lightBleedReduction);
}
/**
@@ -1144,7 +1144,7 @@ public class View {
private static native void nSetRenderQuality(long nativeView, int hdrColorBufferQuality);
private static native void nSetDynamicLightingOptions(long nativeView, float zLightNear, float zLightFar);
private static native void nSetShadowType(long nativeView, int type);
private static native void nSetVsmShadowOptions(long nativeView, int anisotropy, boolean mipmapping, float minVarianceScale, float lightBleedReduction);
private static native void nSetVsmShadowOptions(long nativeView, int anisotropy, boolean mipmapping, boolean highPrecision, float minVarianceScale, float lightBleedReduction);
private static native void nSetSoftShadowOptions(long nativeView, float penumbraScale, float penumbraRatioScale);
private static native void nSetColorGrading(long nativeView, long nativeColorGrading);
private static native void nSetPostProcessingEnabled(long nativeView, boolean enabled);
@@ -1817,6 +1817,13 @@ public class View {
* Whether to generate mipmaps for all VSM shadow maps.
*/
public boolean mipmapping = false;
/**
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
* precision is rarely needed, but it does reduces light leaks as well as "fading"
* of the shadows in some situations. Setting highPrecision to true for a single
* shadow map will double the memory usage of all shadow maps.
*/
public boolean highPrecision = false;
/**
* VSM minimum variance scale, must be positive.
*/
@@ -179,7 +179,7 @@ class ModelViewer(
asset = assetLoader.createAsset(buffer)
asset?.let { asset ->
resourceLoader.asyncBeginLoad(asset)
animator = asset.animator
animator = asset.getInstance().animator
asset.releaseSourceData()
}
}
@@ -202,7 +202,7 @@ class ModelViewer(
resourceLoader.addResourceData(uri, resourceBuffer)
}
resourceLoader.asyncBeginLoad(asset)
animator = asset.animator
animator = asset.getInstance().animator
asset.releaseSourceData()
}
}
@@ -359,7 +359,7 @@ class ModelViewer(
resourceLoader.addResourceData(uri, buffer)
}
resourceLoader.asyncBeginLoad(asset)
animator = asset.animator
animator = asset.getInstance().animator
asset.releaseSourceData()
}
}
+7 -2
View File
@@ -11,6 +11,10 @@ add_library(dracodec STATIC IMPORTED)
set_target_properties(dracodec PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libdracodec.a)
add_library(meshoptimizer STATIC IMPORTED)
set_target_properties(meshoptimizer PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libmeshoptimizer.a)
add_library(ktxreader STATIC IMPORTED)
set_target_properties(ktxreader PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libktxreader.a)
@@ -76,7 +80,7 @@ set(GLTFIO_SRCS
${GLTFIO_DIR}/src/UbershaderProvider.cpp
${GLTFIO_DIR}/src/Wireframe.cpp
${GLTFIO_DIR}/src/Wireframe.h
${GLTFIO_DIR}/src/upcast.h
${GLTFIO_DIR}/src/downcast.h
src/main/cpp/Animator.cpp
src/main/cpp/AssetLoader.cpp
@@ -99,6 +103,7 @@ set(GLTFIO_INCLUDE_DIRS
../../libs/gltfio/include
../../third_party/basisu/zstd
../../third_party/cgltf
../../third_party/meshoptimizer/src
../../third_party/robin-map
../../third_party/hat-trie
../../third_party/stb
@@ -111,7 +116,7 @@ target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
target_link_libraries(gltfio-jni filament-jni utils uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
target_link_libraries(gltfio-jni dracodec)
target_link_libraries(gltfio-jni dracodec meshoptimizer)
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/tnt)
@@ -172,27 +172,6 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetCameraEntityCount(JNIE
return asset->getCameraEntityCount();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialInstanceCount(JNIEnv*, jclass,
jlong nativeAsset) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
return asset->getMaterialInstanceCount();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialInstances(JNIEnv* env, jclass,
jlong nativeAsset, jlongArray result) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
jsize available = env->GetArrayLength(result);
jsize count = std::min(available, (jsize) asset->getMaterialInstanceCount());
jlong* dst = env->GetLongArrayElements(result, nullptr);
const MaterialInstance * const* src = asset->getMaterialInstances();
for (jsize i = 0; i < count; i++) {
dst[i] = (jlong) src[i];
}
env->ReleaseLongArrayElements(result, dst, 0);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetBoundingBox(JNIEnv* env, jclass,
jlong nativeAsset, jfloatArray result) {
@@ -229,10 +208,10 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetExtras(JNIEnv* env, jc
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetAnimator(JNIEnv* , jclass,
Java_com_google_android_filament_gltfio_FilamentAsset_nGetInstance(JNIEnv* , jclass,
jlong nativeAsset) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
return (jlong) asset->getAnimator();
return (jlong) asset->getInstance();
}
extern "C" JNIEXPORT jint JNICALL
@@ -272,30 +251,6 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetMorphTargetNames(JNIEn
}
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialVariantCount(JNIEnv*, jclass,
jlong nativeAsset) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
return (jint) asset->getMaterialVariantCount();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialVariantNames(JNIEnv* env, jclass,
jlong nativeAsset, jobjectArray result) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
for (int i = 0; i < asset->getMaterialVariantCount(); ++i) {
const char* name = asset->getMaterialVariantName(i);
env->SetObjectArrayElement(result, (jsize) i, env->NewStringUTF(name));
}
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nApplyMaterialVariant(JNIEnv* env, jclass,
jlong nativeAsset, jint variantIndex) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
asset->applyMaterialVariant(variantIndex);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nReleaseSourceData(JNIEnv* env, jclass,
jlong nativeAsset) {
@@ -20,6 +20,7 @@
#include <algorithm>
using namespace filament;
using namespace filament::gltfio;
using namespace utils;
@@ -62,6 +63,44 @@ Java_com_google_android_filament_gltfio_FilamentInstance_nApplyMaterialVariant(J
instance->applyMaterialVariant(variantIndex);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialVariantCount(JNIEnv*, jclass,
jlong nativeInstance) {
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
return (jint) instance->getMaterialVariantCount();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialVariantNames(JNIEnv* env, jclass,
jlong nativeInstance, jobjectArray result) {
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
for (int i = 0; i < instance->getMaterialVariantCount(); ++i) {
const char* name = instance->getMaterialVariantName(i);
env->SetObjectArrayElement(result, (jsize) i, env->NewStringUTF(name));
}
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialInstanceCount(JNIEnv*, jclass,
jlong nativeInstance) {
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
return instance->getMaterialInstanceCount();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialInstances(JNIEnv* env, jclass,
jlong nativeInstance, jlongArray result) {
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
jsize available = env->GetArrayLength(result);
jsize count = std::min(available, (jsize) instance->getMaterialInstanceCount());
jlong* dst = env->GetLongArrayElements(result, nullptr);
const MaterialInstance * const* src = instance->getMaterialInstances();
for (jsize i = 0; i < count; i++) {
dst[i] = (jlong) src[i];
}
env->ReleaseLongArrayElements(result, dst, 0);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentInstance_nAttachSkin(JNIEnv* env, jclass,
jlong nativeInstance, jint skinIndex, jint targetEntity) {
@@ -23,7 +23,6 @@ import androidx.annotation.Nullable;
import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.Entity;
import com.google.android.filament.MaterialInstance;
/**
* Owns a bundle of Filament objects that have been created by <code>AssetLoader</code>.
@@ -35,25 +34,32 @@ import com.google.android.filament.MaterialInstance;
* <code>NameComponentManager</code>, <code>RenderableManager</code>, and others.</p>
*
* <p>In addition to the aforementioned entities, an asset has strong ownership over a list of
* <code>VertexBuffer</code>, <code>IndexBuffer</code>, <code>MaterialInstance</code>, and
* <code>Texture</code>.</p>
* <code>VertexBuffer</code>, <code>IndexBuffer</code>, and <code>Texture</code>.</p>
*
* <p>Clients can use {@link ResourceLoader} to create textures, compute tangent quaternions, and
* upload data into vertex buffers and index buffers.</p>
*
* @see ResourceLoader
* @see Animator
* @see FilamentInstance
* @see AssetLoader
*/
public class FilamentAsset {
private long mNativeObject;
private Animator mAnimator;
private FilamentInstance mPrimaryInstance;
private Engine mEngine;
FilamentAsset(Engine engine, long nativeObject) {
mEngine = engine;
mNativeObject = nativeObject;
mAnimator = null;
}
public FilamentInstance getInstance() {
if (mPrimaryInstance != null) {
return mPrimaryInstance;
}
long nativeInstance = nGetInstance(getNativeObject());
mPrimaryInstance = new FilamentInstance(this, nativeInstance);
return mPrimaryInstance;
}
long getNativeObject() {
@@ -169,17 +175,6 @@ public class FilamentAsset {
return result;
}
public @NonNull MaterialInstance[] getMaterialInstances() {
final int count = nGetMaterialInstanceCount(mNativeObject);
MaterialInstance[] result = new MaterialInstance[count];
long[] natives = new long[count];
nGetMaterialInstances(mNativeObject, natives);
for (int i = 0; i < count; i++) {
result[i] = new MaterialInstance(mEngine, natives[i]);
}
return result;
}
/**
* Gets the bounding box computed from the supplied min / max values in glTF accessors.
*
@@ -209,25 +204,6 @@ public class FilamentAsset {
return nGetExtras(mNativeObject, entity);
}
/**
* Retrieves the <code>Animator</code> interface for this asset.
*
* <p>When calling this for the first time, this must be called after
* {@link ResourceLoader#loadResources} or {@link ResourceLoader#asyncBeginLoad}. When the asset
* is destroyed, its animator becomes invalid.</p>
*/
public @NonNull Animator getAnimator() {
if (mAnimator != null) {
return mAnimator;
}
long nativeAnimator = nGetAnimator(getNativeObject());
if (nativeAnimator == 0) {
throw new IllegalStateException("Unable to create animator");
}
mAnimator = new Animator(nativeAnimator);
return mAnimator;
}
/**
* Gets the names of all morph targets in the given entity.
*/
@@ -246,31 +222,6 @@ public class FilamentAsset {
return uris;
}
/**
* Returns the names of all material variants.
*/
public @NonNull String[] getMaterialVariantNames() {
String[] names = new String[nGetMaterialVariantCount(mNativeObject)];
nGetMaterialVariantNames(mNativeObject, names);
return names;
}
/**
* Applies the given material variant to all primitives that it affects.
*
* This is efficient because it merely swaps around persistent MaterialInstances. If you change
* a material parameter while a certain variant is active, the updated value will be remembered
* after you re-apply that variant.
*
* If the asset is instanced, this affects all instances in the same way.
* To set the variant on an individual instance, use FilamentInstance#applyMaterialVariant.
*
* Ignored if variantIndex is out of bounds.
*/
public void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
nApplyMaterialVariant(getNativeObject(), variantIndex);
}
/**
* Reclaims CPU-side memory for URI strings, binding lists, and raw animation data.
*
@@ -282,8 +233,10 @@ public class FilamentAsset {
nReleaseSourceData(mNativeObject);
}
public Engine getEngine() { return mEngine; }
void clearNativeObject() {
if (mAnimator != null) mAnimator.clearNativeObject();
mPrimaryInstance = null;
mNativeObject = 0;
}
@@ -307,21 +260,17 @@ public class FilamentAsset {
private static native int nGetCameraEntityCount(long nativeAsset);
private static native void nGetCameraEntities(long nativeAsset, int[] result);
private static native int nGetMaterialInstanceCount(long nativeAsset);
private static native void nGetMaterialInstances(long nativeAsset, long[] nativeResults);
private static native int nGetMaterialVariantCount(long nativeAsset);
private static native void nGetMaterialVariantNames(long nativeAsset, String[] result);
private static native int nGetMorphTargetCount(long nativeAsset, int entity);
private static native void nGetMorphTargetNames(long nativeAsset, int entity, String[] result);
private static native void nGetBoundingBox(long nativeAsset, float[] box);
private static native String nGetName(long nativeAsset, int entity);
private static native String nGetExtras(long nativeAsset, int entity);
private static native long nGetAnimator(long nativeAsset);
private static native void nApplyMaterialVariant(long nativeAsset, int variantIndex);
private static native long nGetInstance(long nativeAsset);
private static native int nGetResourceUriCount(long nativeAsset);
private static native void nGetResourceUris(long nativeAsset, String[] result);
private static native void nReleaseSourceData(long nativeAsset);
}
@@ -19,7 +19,9 @@ package com.google.android.filament.gltfio;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import com.google.android.filament.Engine;
import com.google.android.filament.Entity;
import com.google.android.filament.MaterialInstance;
/**
* Provides access to a hierarchy of entities that have been instanced from a glTF asset.
@@ -146,11 +148,39 @@ public class FilamentInstance {
nApplyMaterialVariant(mNativeObject, variantIndex);
}
public @NonNull MaterialInstance[] getMaterialInstances() {
final int count = nGetMaterialInstanceCount(mNativeObject);
MaterialInstance[] result = new MaterialInstance[count];
long[] natives = new long[count];
nGetMaterialInstances(mNativeObject, natives);
Engine engine = mAsset.getEngine();
for (int i = 0; i < count; i++) {
result[i] = new MaterialInstance(engine, natives[i]);
}
return result;
}
/**
* Returns the names of all material variants.
*/
public @NonNull String[] getMaterialVariantNames() {
String[] names = new String[nGetMaterialVariantCount(mNativeObject)];
nGetMaterialVariantNames(mNativeObject, names);
return names;
}
private static native int nGetRoot(long nativeInstance);
private static native int nGetEntityCount(long nativeInstance);
private static native void nGetEntities(long nativeInstance, int[] result);
private static native long nGetAnimator(long nativeInstance);
private static native int nGetMaterialInstanceCount(long nativeAsset);
private static native void nGetMaterialInstances(long nativeAsset, long[] nativeResults);
private static native void nApplyMaterialVariant(long nativeInstance, int variantIndex);
private static native int nGetMaterialVariantCount(long nativeAsset);
private static native void nGetMaterialVariantNames(long nativeAsset, String[] result);
private static native void nGetJointsAt(long nativeInstance, int skinIndex, int[] result);
private static native int nGetSkinCount(long nativeInstance);
private static native void nGetSkinNames(long nativeInstance, String[] result);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.27.2
VERSION_NAME=1.28.2
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+3 -3
View File
@@ -112,9 +112,9 @@ the Android SDK.
## Android Studio
You must use Android Studio 3.6 RC 1 or higher. To open the project, point Studio to the `android`
folder. After opening the project and syncing to gradle, select the sample of your choice using the
drop-down widget in the toolbar.
You must use the latest stable release of Android Studio. To open the project, point Studio to the
`android` folder. After opening the project and syncing to gradle, select the sample of your choice
using the drop-down widget in the toolbar.
## Compiling
@@ -30,7 +30,7 @@ android {
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.gltf"
minSdkVersion 21
minSdkVersion 19
targetSdkVersion versions.targetSdk
missingDimensionStrategy 'functionality', 'full'
}
@@ -417,7 +417,7 @@ class MainActivity : Activity() {
// Just for testing purposes, this releases the current model and reloads the default model.
inner class DoubleTapListener : GestureDetector.SimpleOnGestureListener() {
override fun onDoubleTap(e: MotionEvent?): Boolean {
override fun onDoubleTap(e: MotionEvent): Boolean {
modelViewer.destroyModel()
createDefaultRenderables()
return super.onDoubleTap(e)
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
set -e
function print_help {
local self_name=$(basename "$0")
echo "This tool verifies that every Metal shader in the given Filament material file(s) successfully compiles."
echo "Requires the matinfo tool and xcrun to be on the system PATH."
echo ""
echo "Usage:"
echo " $self_name [options] [<material> ...]"
echo ""
echo "Options:"
echo " -h"
echo " Print this help message."
echo " -w"
echo " Pass the -w flag to the Metal compiler, to disable warnings."
echo ""
}
COMP_FLAGS=""
while getopts ":hw" opt; do
case ${opt} in
h)
print_help
exit 1
;;
w)
COMP_FLAGS="${COMP_FLAGS} -w"
;;
esac
done
shift $((OPTIND - 1))
if [[ "$#" == "0" ]]; then
print_help
exit 1
fi
# Make sure matinfo and xcrun are available.
if [[ ! $(command -v matinfo) ]]; then
echo "Error: matinfo not on PATH."
exit 1
fi
if [[ ! $(command -v xcrun) ]]; then
echo "Error: xcrun not on PATH."
exit 1
fi
function check_material {
local material="$1"
tmpdir="$(mktemp -d /tmp/check_metal_shaders.XXXXX)"
# First check that the material is valid.
if [[ ! $(matinfo "${material}") ]]; then
echo "Invalid material file: ${material}"
exit 1
fi
echo "Checking that Metal shaders compile: ${material}"
set +e
local i=0
while true; do
# The file must have a .metal extension.
metalFile="${tmpdir}/${i}.metal"
# Extract shader i. matinfo will return a non-zero exit code if the shader doesn't exist,
# which means we're finished with this material.
matinfo --print-metal=${i} "${material}" > "${metalFile}" 2> /dev/null
if [[ "$?" -ne 0 ]]; then
break
fi
echo "Testing Metal shader ${i}"
# Attempt to compile the Metal shader.
xcrun -sdk macosx metal ${COMP_FLAGS} -c "${metalFile}" -o /dev/null
if [[ "$?" -ne 0 ]]; then
echo "Error compiling Metal shader: ${metalFile}"
exit 1
fi
((i++))
done
set -e
rm -r "${tmpdir}"
}
for material in "$@"
do
check_material "${material}"
done
+1 -1
View File
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
# android
set(API_LEVEL 21)
set(API_LEVEL 19)
# architecture
set(ARCH armv7a-linux-androideabi)
+1 -1
View File
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
# android
set(API_LEVEL 21)
set(API_LEVEL 19)
# architecture
set(ARCH i686-linux-android)
+1 -1
View File
File diff suppressed because one or more lines are too long
+11 -12
View File
@@ -1432,12 +1432,13 @@ non-shader data.
</p><dl><table><tbody><tr valign="top"><td><dt>Type</dt></td><td><dd><p> <code>number</code>
</p></dd></td></tr><tr valign="top"><td><dt>Value</dt></td><td><dd><p> An integer value, either 1 or 2. Defaults to 1.
</p></dd></td></tr><tr valign="top"><td><dt>Value</dt></td><td><dd><p> An integer value, either 1, 2 or 3. Defaults to 1.
</p></dd></td></tr></tbody></table></dl><div class="table">
<table class="table"><tbody><tr><th style="text-align:left"> Feature Level </th><th style="text-align:left"> Guaranteed features </th></tr>
<tr><td style="text-align:left"> 1 </td><td style="text-align:left"> 9 textures per material </td></tr>
<tr><td style="text-align:left"> 2 </td><td style="text-align:left"> 12 textures per material, cubemap arrays, ESSL 3.10 </td></tr>
<tr><td style="text-align:left"> 2 </td><td style="text-align:left"> 9 textures per material, cubemap arrays, ESSL 3.10 </td></tr>
<tr><td style="text-align:left"> 3 </td><td style="text-align:left"> 12 textures per material, cubemap arrays, ESSL 3.10 </td></tr>
</tbody></table><center><div class="tablecaption"><a class="target" name="table_featurelevels">&nbsp;</a><b style="font-style:normal;">Table&nbsp;13:</b> Feature levels</div></center></div>
<p></p><p>
@@ -2036,23 +2037,21 @@ and sorting issues are minimized or eliminated</span></center></div></center>
</p></dd><dt>Value</dt><dd><p> <code>true</code> or <code>false</code>. Defaults to <code>false</code>.
</p></dd><dt>Description</dt><dd><p> Only available in the <code>unlit</code> shading model. If this property is enabled, the final color
computed by the material is multiplied by the shadowing factor (or visibility). This allows to
create transparent shadow-receiving objects (for instance an invisible ground plane in AR).
This is only supported with shadows from directional lights.
</p></dd><dt>Description</dt><dd><p> Enables two-sided rendering and its capability to be toggled at run time. When set to <code>true</code>,
<code>culling</code> is automatically set to <code>none</code>; if the triangle is back-facing, the triangle's
normal is flipped to become front-facing. When explicitly set to <code>false</code>, this allows the
double-sidedness to be toggled at run time.
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> name : <span class="hljs-string">"Invisible shadow plane"</span>,</span>
<span class="line"> shadingModel : unlit,</span>
<span class="line"> shadowMultiplier : <span class="hljs-literal">true</span>,</span>
<span class="line"> blending : transparent</span>
<span class="line"> name : <span class="hljs-string">"Double sided material"</span>,</span>
<span class="line"> shadingModel : lit,</span>
<span class="line"> doubleSided : <span class="hljs-literal">true</span></span>
<span class="line">}</span>
<span class="line"></span>
<span class="line">fragment {</span>
<span class="line"> void material(inout MaterialInputs material) {</span>
<span class="line"> prepareMaterial(material);</span>
<span class="line"> <span class="hljs-comment">// baseColor defines the color and opacity of the final shadow</span></span>
<span class="line"> material.baseColor = vec4(0.0, 0.0, 0.0, 0.7);</span>
<span class="line"> material.baseColor = materialParams.albedo;</span>
<span class="line"> }</span>
<span class="line">}</span></code></pre>
<a class="target" name="lighting:reflections">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:reflections">&nbsp;</a><a class="target" name="toc4.2.25">&nbsp;</a><h3>Lighting: reflections</h3>
+8 -10
View File
@@ -1582,24 +1582,22 @@ Value
: `true` or `false`. Defaults to `false`.
Description
: Only available in the `unlit` shading model. If this property is enabled, the final color
computed by the material is multiplied by the shadowing factor (or visibility). This allows to
create transparent shadow-receiving objects (for instance an invisible ground plane in AR).
This is only supported with shadows from directional lights.
: Enables two-sided rendering and its capability to be toggled at run time. When set to `true`,
`culling` is automatically set to `none`; if the triangle is back-facing, the triangle's
normal is flipped to become front-facing. When explicitly set to `false`, this allows the
double-sidedness to be toggled at run time.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
name : "Invisible shadow plane",
shadingModel : unlit,
shadowMultiplier : true,
blending : transparent
name : "Double sided material",
shadingModel : lit,
doubleSided : true
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
// baseColor defines the color and opacity of the final shadow
material.baseColor = vec4(0.0, 0.0, 0.0, 0.7);
material.baseColor = materialParams.albedo;
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+4 -1
View File
@@ -48,6 +48,7 @@ set(PUBLIC_HDRS
)
set(SRCS
src/AtlasAllocator.cpp
src/Box.cpp
src/BufferObject.cpp
src/Camera.cpp
@@ -72,6 +73,7 @@ set(SRCS
src/MaterialParser.cpp
src/MorphTargetBuffer.cpp
src/PerViewUniforms.cpp
src/PerShadowMapUniforms.cpp
src/PostProcessManager.cpp
src/RenderPass.cpp
src/RenderPrimitive.cpp
@@ -145,6 +147,7 @@ set(PRIVATE_HDRS
src/Intersections.h
src/MaterialParser.h
src/PerViewUniforms.h
src/PerShadowMapUniforms.h
src/PIDController.h
src/PostProcessManager.h
src/RendererUtils.h
@@ -198,7 +201,7 @@ set(PRIVATE_HDRS
src/materials/fsr/ffx_a.h
src/materials/fsr/ffx_fsr1.h
src/materials/fsr/ffx_fsr1_mobile.fs
src/upcast.h
src/downcast.h
)
set(MATERIAL_SRCS
@@ -220,8 +220,7 @@ enum class PrimitiveType : uint8_t {
LINES = 1, //!< lines
LINE_STRIP = 3, //!< line strip
TRIANGLES = 4, //!< triangles
TRIANGLE_STRIP = 5, //!< triangle strip
NONE = 0xFF
TRIANGLE_STRIP = 5 //!< triangle strip
};
/**
@@ -49,6 +49,11 @@ void removeGoogleLineDirectives(char* shader, size_t length) noexcept;
*/
size_t getFormatSize(TextureFormat format) noexcept;
/**
* Returns the number of component (1 to 4) for the given format.
*/
size_t getFormatComponentCount(TextureFormat format) noexcept;
/**
* For compressed texture formats, returns the number of horizontal texels per block. Otherwise
* returns 0.
@@ -158,6 +158,9 @@ DECL_DRIVER_API_0(flush)
// flush and wait for the effects to be done
DECL_DRIVER_API_0(finish)
// reset state tracking, if the driver does any state tracking (e.g. GL)
DECL_DRIVER_API_0(resetState)
/*
* Creating driver objects
* -----------------------
+139 -16
View File
@@ -209,6 +209,129 @@ size_t getFormatSize(TextureFormat format) noexcept {
}
}
size_t getFormatComponentCount(TextureFormat format) noexcept {
switch (format) {
case TextureFormat::R8:
case TextureFormat::R8_SNORM:
case TextureFormat::R8UI:
case TextureFormat::R8I:
case TextureFormat::R16F:
case TextureFormat::R16UI:
case TextureFormat::R16I:
case TextureFormat::R32F:
case TextureFormat::R32I:
case TextureFormat::R32UI:
case TextureFormat::STENCIL8:
case TextureFormat::DEPTH16:
case TextureFormat::DEPTH24:
case TextureFormat::DEPTH32F:
return 1;
case TextureFormat::RG8:
case TextureFormat::RG8_SNORM:
case TextureFormat::RG8UI:
case TextureFormat::RG8I:
case TextureFormat::RG16F:
case TextureFormat::RG16UI:
case TextureFormat::RG16I:
case TextureFormat::RG32F:
case TextureFormat::RG32UI:
case TextureFormat::RG32I:
case TextureFormat::DEPTH24_STENCIL8:
case TextureFormat::DEPTH32F_STENCIL8:
return 2;
case TextureFormat::RGB565:
case TextureFormat::RGB8:
case TextureFormat::SRGB8:
case TextureFormat::RGB8_SNORM:
case TextureFormat::RGB8UI:
case TextureFormat::RGB8I:
case TextureFormat::R11F_G11F_B10F:
case TextureFormat::RGB16F:
case TextureFormat::RGB16UI:
case TextureFormat::RGB16I:
case TextureFormat::RGB32F:
case TextureFormat::RGB32UI:
case TextureFormat::RGB32I:
return 3;
case TextureFormat::RGB5_A1:
case TextureFormat::RGBA4:
case TextureFormat::RGB9_E5:
case TextureFormat::RGBA8:
case TextureFormat::SRGB8_A8:
case TextureFormat::RGBA8_SNORM:
case TextureFormat::RGB10_A2:
case TextureFormat::RGBA8UI:
case TextureFormat::RGBA8I:
case TextureFormat::RGBA16F:
case TextureFormat::RGBA16UI:
case TextureFormat::RGBA16I:
case TextureFormat::RGBA32F:
case TextureFormat::RGBA32UI:
case TextureFormat::RGBA32I:
return 4;
// Compressed formats ---------------------------------------------------------------------
case TextureFormat::EAC_R11:
case TextureFormat::EAC_R11_SIGNED:
return 1;
case TextureFormat::EAC_RG11:
case TextureFormat::EAC_RG11_SIGNED:
return 2;
case TextureFormat::ETC2_RGB8:
case TextureFormat::ETC2_SRGB8:
case TextureFormat::DXT1_RGB:
case TextureFormat::DXT1_SRGB:
return 3;
case TextureFormat::ETC2_EAC_RGBA8:
case TextureFormat::ETC2_EAC_SRGBA8:
case TextureFormat::ETC2_RGB8_A1:
case TextureFormat::ETC2_SRGB8_A1:
case TextureFormat::DXT1_RGBA:
case TextureFormat::DXT1_SRGBA:
case TextureFormat::DXT3_RGBA:
case TextureFormat::DXT3_SRGBA:
case TextureFormat::DXT5_RGBA:
case TextureFormat::DXT5_SRGBA:
case TextureFormat::RGBA_ASTC_4x4:
case TextureFormat::RGBA_ASTC_5x4:
case TextureFormat::RGBA_ASTC_5x5:
case TextureFormat::RGBA_ASTC_6x5:
case TextureFormat::RGBA_ASTC_6x6:
case TextureFormat::RGBA_ASTC_8x5:
case TextureFormat::RGBA_ASTC_8x6:
case TextureFormat::RGBA_ASTC_8x8:
case TextureFormat::RGBA_ASTC_10x5:
case TextureFormat::RGBA_ASTC_10x6:
case TextureFormat::RGBA_ASTC_10x8:
case TextureFormat::RGBA_ASTC_10x10:
case TextureFormat::RGBA_ASTC_12x10:
case TextureFormat::RGBA_ASTC_12x12:
case TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
case TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
case TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
return 4;
case TextureFormat::UNUSED:
return 0;
}
}
size_t getBlockWidth(TextureFormat format) noexcept {
switch (format) {
case TextureFormat::EAC_RG11:
@@ -384,53 +507,53 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
namespace utils {
template<>
CString to_string<filament::backend::TextureUsage>(filament::backend::TextureUsage usage) noexcept {
CString to_string<filament::backend::TextureUsage>(filament::backend::TextureUsage value) noexcept {
using namespace filament::backend;
char string[7] = {'-', '-', '-', '-', '-', '-', 0};
if (any(usage & TextureUsage::UPLOADABLE)) {
if (any(value & TextureUsage::UPLOADABLE)) {
string[0]='U';
}
if (any(usage & TextureUsage::SAMPLEABLE)) {
if (any(value & TextureUsage::SAMPLEABLE)) {
string[1]='S';
}
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
if (any(value & TextureUsage::COLOR_ATTACHMENT)) {
string[2]='c';
}
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
if (any(value & TextureUsage::DEPTH_ATTACHMENT)) {
string[3]='d';
}
if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) {
if (any(value & TextureUsage::STENCIL_ATTACHMENT)) {
string[4] = 's';
}
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
if (any(value & TextureUsage::SUBPASS_INPUT)) {
string[5]='f';
}
return CString(string, 6);
return { string, 6 };
}
template<>
CString to_string<filament::backend::TargetBufferFlags>(filament::backend::TargetBufferFlags flags) noexcept {
CString to_string<filament::backend::TargetBufferFlags>(filament::backend::TargetBufferFlags value) noexcept {
using namespace filament::backend;
char string[7] = {'-', '-', '-', '-', '-', '-', 0};
if (any(flags & TargetBufferFlags::COLOR0)) {
if (any(value & TargetBufferFlags::COLOR0)) {
string[0]='0';
}
if (any(flags & TargetBufferFlags::COLOR1)) {
if (any(value & TargetBufferFlags::COLOR1)) {
string[1]='1';
}
if (any(flags & TargetBufferFlags::COLOR2)) {
if (any(value & TargetBufferFlags::COLOR2)) {
string[2]='2';
}
if (any(flags & TargetBufferFlags::COLOR3)) {
if (any(value & TargetBufferFlags::COLOR3)) {
string[3]='3';
}
if (any(flags & TargetBufferFlags::DEPTH)) {
if (any(value & TargetBufferFlags::DEPTH)) {
string[4]='D';
}
if (any(flags & TargetBufferFlags::STENCIL)) {
if (any(value & TargetBufferFlags::STENCIL)) {
string[5]='S';
}
return CString(string, 6);
return { string, 6 };
}
} // namespace utils
+22 -1
View File
@@ -59,6 +59,21 @@ public:
}
}
static void copyImage(uint8_t* UTILS_RESTRICT dest,
const uint8_t* UTILS_RESTRICT src,
size_t srcBytesPerRow, size_t /*srcChannelCount*/,
size_t dstBytesPerRow, size_t /*dstChannelCount*/,
size_t /*width*/, size_t height, bool /*swizzle*/) {
if (srcBytesPerRow == dstBytesPerRow) {
std::memcpy(dest, src, height * srcBytesPerRow);
return;
}
const size_t minBytesPerRow = std::min(srcBytesPerRow, dstBytesPerRow);
for (size_t i = 0; i < height; ++i, src += srcBytesPerRow, dest += dstBytesPerRow) {
std::memcpy(dest, src, minBytesPerRow);
}
}
// Converts a n-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
template<typename dstComponentType, typename srcComponentType>
static void reshapeImage(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
@@ -119,7 +134,13 @@ public:
switch (dst->type) {
case UBYTE:
switch (srcType) {
case UBYTE: reshaper = reshapeImage<uint8_t, uint8_t>; break;
case UBYTE:
reshaper = reshapeImage<uint8_t, uint8_t>;
if (dst->format == PixelDataFormat::RGBA &&
dstChannelCount == srcChannelCount && !swizzle) {
reshaper = copyImage;
}
break;
case FLOAT: reshaper = reshapeImage<uint8_t, float>; break;
case INT: reshaper = reshapeImage<uint8_t, int32_t>; break;
case UINT: reshaper = reshapeImage<uint8_t, uint32_t>; break;
+6 -2
View File
@@ -42,7 +42,9 @@
#include "vulkan/PlatformVkCocoa.h"
#endif
#elif defined(__linux__)
#if defined(FILAMENT_SUPPORTS_WAYLAND)
#if defined(FILAMENT_SUPPORTS_GGP)
#include "vulkan/PlatformVkLinuxGGP.h"
#elif defined(FILAMENT_SUPPORTS_WAYLAND)
#if defined (FILAMENT_DRIVER_SUPPORTS_VULKAN)
#include "vulkan/PlatformVkLinuxWayland.h"
#endif
@@ -120,7 +122,9 @@ DefaultPlatform* DefaultPlatform::create(Backend* backend) noexcept {
#elif defined(IOS)
return new PlatformVkCocoaTouch();
#elif defined(__linux__)
#if defined(FILAMENT_SUPPORTS_WAYLAND)
#if defined(FILAMENT_SUPPORTS_GGP)
return new PlatformVkLinuxGGP();
#elif defined(FILAMENT_SUPPORTS_WAYLAND)
return new PlatformVkLinuxWayland();
#elif defined(FILAMENT_SUPPORTS_X11)
return new PlatformVkLinuxX11();
@@ -91,12 +91,14 @@ blitterFrag(VertexOut in [[stage_in]],
{
FragmentOut out = {};
#if defined(BLIT_COLOR) || defined(BLIT_DEPTH)
// These coordinates match the Vulkan vkCmdBlitImage spec:
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdBlitImage.html
float2 uvbase = in.position.xy; // unnormalized coordinates at center of texel: (1.5, 2.5, etc)
float2 uvoffset = uvbase - args->dstOffset;
float2 uvscaled = uvoffset * args->scale;
float2 uv = uvscaled + args->srcOffset;
#endif
#ifdef BLIT_COLOR
#ifdef MSAA_COLOR_SOURCE
+27 -11
View File
@@ -27,13 +27,15 @@
#include <utils/compiler.h>
#include <utility>
#include <memory>
namespace filament::backend {
class MetalBuffer {
public:
MetalBuffer(MetalContext& context, BufferUsage usage, size_t size, bool forceGpuBuffer = false);
MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer = false);
~MetalBuffer();
MetalBuffer(const MetalBuffer& rhs) = delete;
@@ -60,18 +62,21 @@ public:
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
enum Stage {
VERTEX = 1,
FRAGMENT = 2
enum Stage : uint8_t {
VERTEX = 1u << 0u,
FRAGMENT = 1u << 1u,
COMPUTE = 1u << 2u
};
/**
* Bind multiple buffers to pipeline stages.
*
* bindBuffers binds an array of buffers to the given stage(s) of a MTLRenderCommandEncoder's
* pipeline.
* bindBuffers binds an array of buffers to the given stage(s) of a MTLCommandEncoders's
* pipeline. The encoder must be either a MTLRenderCommandEncoder or a MTLComputeCommandEncoder.
* For MTLRenderCommandEncoders, only the VERTEX and FRAGMENT stages may be specified.
* For MTLComputeCommandEncoders, only the COMPUTE stage may be specified.
*/
static void bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderCommandEncoder> encoder,
static void bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
size_t bufferStart, uint8_t stages, MetalBuffer* const* buffers, size_t const* offsets,
size_t count);
@@ -106,7 +111,12 @@ public:
// address space. Constant buffers have specific alignment requirements when specifying an
// offset.
#if defined(IOS)
#if TARGET_OS_SIMULATOR
// The iOS simulator has differing alignment requirements.
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 256;
#else
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 4;
#endif // TARGET_OS_SIMULATOR
#else
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 32;
#endif
@@ -132,7 +142,7 @@ public:
* @return the id<MTLBuffer> and offset for the new allocation
*/
std::pair<id<MTLBuffer>, NSUInteger> createNewAllocation(id<MTLCommandBuffer> cmdBuffer) {
const auto occupiedSlots = mOccupiedSlots.load(std::memory_order_relaxed);
const auto occupiedSlots = mOccupiedSlots->load(std::memory_order_relaxed);
assert_invariant(occupiedSlots <= mSlotCount);
if (UTILS_UNLIKELY(occupiedSlots == mSlotCount)) {
// We don't have any room left, so we fall back to creating a one-off aux buffer.
@@ -144,14 +154,19 @@ public:
return {mAuxBuffer, 0};
}
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
mOccupiedSlots.fetch_add(1, std::memory_order_relaxed);
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
// Release the previous allocation.
if (UTILS_UNLIKELY(mAuxBuffer)) {
mAuxBuffer = nil;
} else {
// Capture the mOccupiedSlots var via a weak_ptr because the MetalRingBuffer could be
// destructed before the block executes.
std::weak_ptr<AtomicCounterType> slots = mOccupiedSlots;
[cmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
mOccupiedSlots.fetch_sub(1, std::memory_order_relaxed);
if (auto s = slots.lock()) {
s->fetch_sub(1, std::memory_order_relaxed);
}
}];
}
return getCurrentAllocation();
@@ -185,7 +200,8 @@ private:
NSUInteger mSlotCount;
NSUInteger mCurrentSlot = 0;
std::atomic<NSUInteger> mOccupiedSlots = 1;
using AtomicCounterType = std::atomic<NSUInteger>;
std::shared_ptr<AtomicCounterType> mOccupiedSlots = std::make_shared<AtomicCounterType>(1);
};
} // namespace filament::backend
+50 -20
View File
@@ -21,15 +21,17 @@
namespace filament {
namespace backend {
MetalBuffer::MetalBuffer(MetalContext& context, BufferUsage usage, size_t size, bool forceGpuBuffer)
: mBufferSize(size), mContext(context) {
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
// buffer. Instead, we use immediate command encoder methods like setVertexBytes:length:atIndex:.
if (size <= 4 * 1024 && usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
mBuffer = nil;
mCpuBuffer = malloc(size);
return;
}
// This won't work for SSBOs, since they are read/write.
if (size <= 4 * 1024 && bindingType != BufferObjectBinding::SHADER_STORAGE &&
usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
mBuffer = nil;
mCpuBuffer = malloc(size);
return;
}
// Otherwise, we allocate a private GPU buffer.
mBuffer = [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate];
@@ -93,9 +95,24 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
return mBuffer;
}
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderCommandEncoder> encoder,
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
size_t bufferStart, uint8_t stages, MetalBuffer* const* buffers, size_t const* offsets,
size_t count) {
// Ensure we were given the correct type of encoder:
// either a MTLRenderCommandEncoder or a MTLComputeCommandEncoder.
if (stages & MetalBuffer::Stage::VERTEX || stages & MetalBuffer::Stage::FRAGMENT) {
assert_invariant([encoder respondsToSelector:@selector(setVertexBuffers:offsets:withRange:)]);
assert_invariant([encoder respondsToSelector:@selector(setFragmentBuffers:offsets:withRange:)]);
assert_invariant([encoder respondsToSelector:@selector(setVertexBytes:length:atIndex:)]);
assert_invariant([encoder respondsToSelector:@selector(setFragmentBytes:length:atIndex:)]);
assert_invariant(!(stages & MetalBuffer::Stage::COMPUTE));
}
if (stages & MetalBuffer::Stage::COMPUTE) {
assert_invariant([encoder respondsToSelector:@selector(setBuffers:offsets:withRange:)]);
assert_invariant([encoder respondsToSelector:@selector(setBytes:length:atIndex:)]);
assert_invariant(!(stages & (MetalBuffer::Stage::FRAGMENT | MetalBuffer::Stage::VERTEX)));
}
const NSRange bufferRange = NSMakeRange(bufferStart, count);
constexpr size_t MAX_BUFFERS = 16;
@@ -120,14 +137,19 @@ void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderComman
}
if (stages & Stage::VERTEX) {
[encoder setVertexBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
[(id<MTLRenderCommandEncoder>) encoder setVertexBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
}
if (stages & Stage::FRAGMENT) {
[encoder setFragmentBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
[(id<MTLRenderCommandEncoder>) encoder setFragmentBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
}
if (stages & Stage::COMPUTE) {
[(id<MTLComputeCommandEncoder>) encoder setBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
}
for (size_t b = 0; b < count; b++) {
@@ -146,14 +168,22 @@ void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderComman
auto* bytes = static_cast<const uint8_t*>(cpuBuffer);
if (stages & Stage::VERTEX) {
[encoder setVertexBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
[(id<MTLRenderCommandEncoder>) encoder setVertexBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
if (stages & Stage::FRAGMENT) {
[encoder setFragmentBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
[(id<MTLRenderCommandEncoder>) encoder setFragmentBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
if (stages & Stage::COMPUTE) {
// TODO: using setBytes means the data is read-only, which currently isn't enforced.
// In practice this won't be an issue since MetalBuffer ensures all SSBOs are realized
// through actual id<MTLBuffer> allocations.
[(id<MTLComputeCommandEncoder>) encoder setBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
}
}
+10 -6
View File
@@ -25,6 +25,7 @@
#include <QuartzCore/QuartzCore.h>
#include <array>
#include <atomic>
#include <stack>
#if defined(FILAMENT_METAL_PROFILING)
@@ -43,6 +44,7 @@ class MetalBufferPool;
class MetalRenderTarget;
class MetalSamplerGroup;
class MetalSwapChain;
class MetalTexture;
class MetalTimerQueryInterface;
struct MetalUniformBuffer;
struct MetalIndexBuffer;
@@ -58,8 +60,12 @@ struct MetalContext {
id<MTLCommandBuffer> pendingCommandBuffer = nullptr;
id<MTLRenderCommandEncoder> currentRenderPassEncoder = nullptr;
std::atomic<bool> memorylessLimitsReached = false;
// Supported features.
bool supportsTextureSwizzling = false;
bool supportsAutoDepthResolve = false;
bool supportsMemorylessRenderTargets = false;
uint8_t maxColorRenderTargets = 4;
struct {
uint8_t common;
@@ -79,7 +85,8 @@ struct MetalContext {
// State trackers.
PipelineStateTracker pipelineState;
DepthStencilStateTracker depthStencilState;
UniformBufferState uniformState[Program::UNIFORM_BINDING_COUNT];
std::array<BufferState, Program::UNIFORM_BINDING_COUNT> uniformState;
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
@@ -94,8 +101,9 @@ struct MetalContext {
// Keeps track of sampler groups we've finalized for the current render pass.
tsl::robin_set<MetalSamplerGroup*> finalizedSamplerGroups;
// Keeps track of all alive sampler groups.
// Keeps track of all alive sampler groups, textures.
tsl::robin_set<MetalSamplerGroup*> samplerGroups;
tsl::robin_set<MetalTexture*> textures;
MetalBufferPool* bufferPool;
@@ -127,10 +135,6 @@ struct MetalContext {
os_log_t log;
os_signpost_id_t signpostId;
#endif
#ifndef NDEBUG
tsl::robin_set<HandleBase::HandleId> aliveTextures;
#endif
};
void initializeSupportedGpuFamilies(MetalContext* context);
@@ -103,6 +103,15 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
// all frames and their completion handlers finish before context is deallocated.
[context->pendingCommandBuffer addCompletedHandler:^(id <MTLCommandBuffer> buffer) {
context->resourceTracker.clearResources((__bridge void*) buffer);
auto errorCode = (MTLCommandBufferError)buffer.error.code;
if (@available(macOS 11.0, *)) {
if (errorCode == MTLCommandBufferErrorMemoryless) {
utils::slog.w << "Metal: memoryless geometry limit reached. "
"Continuing with private storage mode." << utils::io::endl;
context->memorylessLimitsReached = true;
}
}
}];
ASSERT_POSTCONDITION(context->pendingCommandBuffer, "Could not obtain command buffer.");
return context->pendingCommandBuffer;
+4 -3
View File
@@ -35,10 +35,11 @@ class MetalPlatform;
class MetalBuffer;
class MetalSamplerGroup;
class MetalTexture;
struct MetalUniformBuffer;
struct MetalContext;
struct MetalProgram;
struct UniformBufferState;
struct BufferState;
#ifndef FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB
#define FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB 8
@@ -128,8 +129,8 @@ private:
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
void finalizeSamplerGroup(MetalSamplerGroup* sg);
void enumerateBoundUniformBuffers(const std::function<void(const UniformBufferState&,
MetalBuffer*, uint32_t)>& f);
void enumerateBoundBuffers(BufferObjectBinding bindingType,
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
};
+215 -59
View File
@@ -92,6 +92,18 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
mContext->highestSupportedGpuFamily.mac >= 2; // newer macOS GPUs
}
// In order to support resolve store action on depth attachment, the GPU needs to support it.
// Note that support for depth resolve implies support for stencil resolve using .sample0 resolve filter.
// (Other resolve filters are supported starting .apple5 and .mac2 families).
mContext->supportsAutoDepthResolve =
mContext->highestSupportedGpuFamily.apple >= 3 ||
mContext->highestSupportedGpuFamily.mac >= 2;
// In order to support memoryless render targets, an Apple GPU is needed.
// Available starting macOS 11.0, the first version to run on Apple GPUs.
// On iOS, it's available on all OS versions.
mContext->supportsMemorylessRenderTargets = mContext->highestSupportedGpuFamily.apple >= 1;
mContext->maxColorRenderTargets = 4;
if (mContext->highestSupportedGpuFamily.apple >= 2 ||
mContext->highestSupportedGpuFamily.mac >= 1) {
@@ -191,6 +203,18 @@ void MetalDriver::endFrame(uint32_t frameId) {
assert_invariant(mContext->groupMarkers.empty());
// If we exceeded memoryless limits, turn it off for the rest of the lifetime of the driver.
if (mContext->supportsMemorylessRenderTargets && mContext->memorylessLimitsReached) {
for (MetalTexture* texture : mContext->textures) {
// Release memoryless MTLTexture-s, which are currently only the MSAA sidecars.
// Creation of new render targets is going to trigger the re-allocation of sidecars,
// with private storage mode from now on. Here, at the end of the frame,
// all render targets that have textures with sidecars are assumed to be destroyed.
texture->msaaSidecar = nil;
}
mContext->supportsMemorylessRenderTargets = false;
}
#if defined(FILAMENT_METAL_PROFILING)
os_signpost_interval_end(mContext->log, mContext->signpostId, "Frame encoding");
#endif
@@ -226,7 +250,7 @@ void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elem
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
construct_handle<MetalBufferObject>(boh, *mContext, usage, byteCount);
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
}
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -236,13 +260,10 @@ void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
width, height, depth, usage, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1,
TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3);
#ifndef NDEBUG
mContext->aliveTextures.insert(th.getId());
#endif
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
target, levels, format, samples, width, height, depth, usage,
TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1,
TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3));
}
void MetalDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -253,12 +274,8 @@ void MetalDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType targe
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
width, height, depth, usage, r, g, b, a);
#ifndef NDEBUG
mContext->aliveTextures.insert(th.getId());
#endif
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
target, levels, format, samples, width, height, depth, usage, r, g, b, a));
}
void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
@@ -279,12 +296,8 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
ASSERT_PRECONDITION(metalTexture.textureType == filamentMetalType,
"Imported id<MTLTexture> type (%d) != Filament texture type (%d)",
metalTexture.textureType, filamentMetalType);
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
width, height, depth, usage, metalTexture);
#ifndef NDEBUG
mContext->aliveTextures.insert(th.getId());
#endif
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
target, levels, format, samples, width, height, depth, usage, metalTexture));
}
void MetalDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t size) {
@@ -475,10 +488,20 @@ void MetalDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
return;
}
auto* bo = handle_cast<MetalBufferObject>(boh);
// Unbind this buffer object from any uniform slots it's still bound to.
// Unbind this buffer object from any uniform / SSBO slots it's still bound to.
bo->boundUniformBuffers.forEachSetBit([this](size_t index) {
mContext->uniformState[index].buffer = nullptr;
mContext->uniformState[index].bound = false;
mContext->uniformState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
});
bo->boundSsbos.forEachSetBit([this](size_t index) {
mContext->ssboState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
});
destruct_handle<MetalBufferObject>(boh);
}
@@ -515,10 +538,7 @@ void MetalDriver::destroyTexture(Handle<HwTexture> th) {
return;
}
#ifndef NDEBUG
mContext->aliveTextures.erase(th.getId());
#endif
mContext->textures.erase(handle_cast<MetalTexture>(th));
destruct_handle<MetalTexture>(th);
}
@@ -683,7 +703,7 @@ bool MetalDriver::isFrameTimeSupported() {
}
bool MetalDriver::isAutoDepthResolveSupported() {
return true;
return mContext->supportsAutoDepthResolve;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
@@ -847,13 +867,13 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
if (!samplers[s].t) {
continue;
}
auto iter = mContext->aliveTextures.find(samplers[s].t.getId());
if (iter == mContext->aliveTextures.end()) {
auto iter = mContext->textures.find(handle_cast<MetalTexture>(samplers[s].t));
if (iter == mContext->textures.end()) {
utils::slog.e << "updateSamplerGroup: texture #"
<< (int) s << " is dead, texture handle = "
<< samplers[s].t << utils::io::endl;
}
assert_invariant(iter != mContext->aliveTextures.end());
assert_invariant(iter != mContext->textures.end());
}
#endif
@@ -880,7 +900,7 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
auto& encoderCache = mContext->argumentEncoderCache;
id<MTLArgumentEncoder> encoder =
encoderCache.getOrCreateState(ArgumentEncoderState(std::move(textureTypes)));
sb->reset(getPendingCommandBuffer(mContext), encoder);
sb->reset(getPendingCommandBuffer(mContext), encoder, mContext->device);
// In a perfect world, all the MTLTexture bindings would be known at updateSamplerGroup time.
// However, there are two special cases preventing this:
@@ -1048,7 +1068,7 @@ void MetalDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh)
currentBo->boundUniformBuffers.unset(index);
}
bo->boundUniformBuffers.set(index);
mContext->uniformState[index] = UniformBufferState{
mContext->uniformState[index] = BufferState{
.buffer = bo,
.offset = 0,
.bound = true
@@ -1061,24 +1081,81 @@ void MetalDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t inde
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
// TODO: implement BufferObjectBinding::SHADER_STORAGE case
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
auto* bo = handle_cast<MetalBufferObject>(boh);
auto* currentBo = mContext->uniformState[index].buffer;
if (currentBo) {
currentBo->boundUniformBuffers.unset(index);
switch (bindingType) {
default:
case BufferObjectBinding::UNIFORM: {
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
auto* currentBo = mContext->uniformState[index].buffer;
if (currentBo) {
currentBo->boundUniformBuffers.unset(index);
}
bo->boundUniformBuffers.set(index);
mContext->uniformState[index] = BufferState {
.buffer = bo,
.offset = offset,
.bound = true
};
break;
}
case BufferObjectBinding::SHADER_STORAGE: {
assert_invariant(index < MAX_SSBO_COUNT);
auto* currentBo = mContext->ssboState[index].buffer;
if (currentBo) {
currentBo->boundSsbos.unset(index);
}
bo->boundSsbos.set(index);
mContext->ssboState[index] = BufferState {
.buffer = bo,
.offset = offset,
.bound = true
};
break;
}
}
bo->boundUniformBuffers.set(index);
mContext->uniformState[index] = UniformBufferState{
.buffer = bo,
.offset = offset,
.bound = true
};
}
void MetalDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
// TODO: implement unbindBuffer()
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
switch (bindingType) {
default:
case BufferObjectBinding::UNIFORM: {
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
auto* currentBo = mContext->uniformState[index].buffer;
if (currentBo) {
currentBo->boundUniformBuffers.unset(index);
}
mContext->uniformState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
break;
}
case BufferObjectBinding::SHADER_STORAGE: {
assert_invariant(index < MAX_SSBO_COUNT);
auto* currentBo = mContext->ssboState[index].buffer;
if (currentBo) {
currentBo->boundSsbos.unset(index);
}
mContext->ssboState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
break;
}
}
}
void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
@@ -1307,13 +1384,13 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
if (!handles[s]) {
continue;
}
auto iter = mContext->aliveTextures.find(handles[s].getId());
if (iter == mContext->aliveTextures.end()) {
auto iter = mContext->textures.find(handle_cast<MetalTexture>(handles[s]));
if (iter == mContext->textures.end()) {
utils::slog.e << "finalizeSamplerGroup: texture #"
<< (int) s << " is dead, texture handle = "
<< handles[s] << utils::io::endl;
}
assert_invariant(iter != mContext->aliveTextures.end());
assert_invariant(iter != mContext->textures.end());
}
#endif
@@ -1549,8 +1626,9 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundUniformBuffers([&uniformsToBind, &offsets](const UniformBufferState& state,
MetalBuffer* buffer, uint32_t index) {
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
offsets[index] = state.offset;
});
@@ -1632,7 +1710,67 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
}
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"dispatchCompute must be called outside of a render pass.");
auto mtlProgram = handle_cast<MetalProgram>(program);
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!mtlProgram->isValid)) {
return;
}
assert_invariant(mtlProgram->isValid && mtlProgram->computeFunction);
id<MTLComputeCommandEncoder> computeEncoder =
[getPendingCommandBuffer(mContext) computeCommandEncoder];
NSError* error = nil;
id<MTLComputePipelineState> computePipelineState =
[mContext->device newComputePipelineStateWithFunction:mtlProgram->computeFunction
error:&error];
if (error) {
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
utils::slog.e << description << utils::io::endl;
}
assert_invariant(!error);
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger uniformOffsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &uniformOffsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
uniformOffsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), computeEncoder,
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::COMPUTE, uniformsToBind,
uniformOffsets, Program::UNIFORM_BINDING_COUNT);
// Bind SSBOs.
MetalBuffer* ssbosToBind[MAX_SSBO_COUNT] = { nil };
NSUInteger ssboOffsets[MAX_SSBO_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::SHADER_STORAGE,
[&ssbosToBind, &ssboOffsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
ssbosToBind[index] = buffer;
ssboOffsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), computeEncoder, SSBO_BINDING_START,
MetalBuffer::Stage::COMPUTE, ssbosToBind, ssboOffsets, MAX_SSBO_COUNT);
[computeEncoder setComputePipelineState:computePipelineState];
MTLSize threadgroupsPerGrid = MTLSizeMake(workGroupCount.x, workGroupCount.y, workGroupCount.z);
// FIXME: the threadgroup size should be specified in the Program
MTLSize threadsPerThreadgroup = MTLSizeMake(16u, 1u, 1u);
[computeEncoder dispatchThreadgroups:threadgroupsPerGrid
threadsPerThreadgroup:threadsPerThreadgroup];
[computeEncoder endEncoding];
}
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
@@ -1649,17 +1787,35 @@ void MetalDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
mContext->timerQueryImpl->endTimeElapsedQuery(tq);
}
void MetalDriver::enumerateBoundUniformBuffers(
const std::function<void(const UniformBufferState&, MetalBuffer*, uint32_t)>& f) {
for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) {
auto& thisUniform = mContext->uniformState[i];
if (!thisUniform.bound) {
continue;
void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f) {
assert_invariant(bindingType == BufferObjectBinding::UNIFORM ||
bindingType == BufferObjectBinding::SHADER_STORAGE);
auto enumerate = [&](auto arrayType){
for (auto i = 0u; i < arrayType.size(); i++) {
const auto& thisBuffer = arrayType[i];
if (!thisBuffer.bound) {
continue;
}
f(thisBuffer, thisBuffer.buffer->getBuffer(), i);
}
f(thisUniform, thisUniform.buffer->getBuffer(), i);
};
switch (bindingType) {
default:
case (BufferObjectBinding::UNIFORM):
enumerate(mContext->uniformState);
break;
case (BufferObjectBinding::SHADER_STORAGE):
enumerate(mContext->ssboState);
break;
}
}
void MetalDriver::resetState(int) {
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<MetalDriver>;
-2
View File
@@ -337,8 +337,6 @@ constexpr inline MTLPrimitiveType getMetalPrimitiveType(PrimitiveType type) noex
case PrimitiveType::LINE_STRIP: return MTLPrimitiveTypeLineStrip;
case PrimitiveType::TRIANGLES: return MTLPrimitiveTypeTriangle;
case PrimitiveType::TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip;
case PrimitiveType::NONE:
ASSERT_POSTCONDITION(false, "NONE is not a valid primitive type.");
}
}
+9 -10
View File
@@ -118,15 +118,18 @@ private:
class MetalBufferObject : public HwBufferObject {
public:
MetalBufferObject(MetalContext& context, BufferUsage usage, uint32_t byteCount);
MetalBufferObject(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
uint32_t byteCount);
void updateBuffer(void* data, size_t size, uint32_t byteOffset);
void updateBufferUnsynchronized(void* data, size_t size, uint32_t byteOffset);
MetalBuffer* getBuffer() { return &buffer; }
// Tracks which uniform buffers this buffer object is bound into.
// Tracks which uniform/ssbo buffers this buffer object is bound into.
static_assert(Program::UNIFORM_BINDING_COUNT <= 32);
static_assert(MAX_SSBO_COUNT <= 32);
utils::bitset32 boundUniformBuffers;
utils::bitset32 boundSsbos;
private:
MetalBuffer buffer;
@@ -163,6 +166,7 @@ struct MetalProgram : public HwProgram {
id<MTLFunction> vertexFunction;
id<MTLFunction> fragmentFunction;
id<MTLFunction> computeFunction;
Program::SamplerGroupInfo samplerGroupInfo;
@@ -279,24 +283,20 @@ public:
// Encode a MTLTexture into this SamplerGroup at the given index.
inline void setFinalizedTexture(size_t index, id<MTLTexture> t) {
assert_invariant(encoder);
assert_invariant(!finalized);
[encoder setTexture:t atIndex:(index * 2 + 0)];
textures[index] = t;
}
// Encode a MTLSamplerState into this SamplerGroup at the given index.
inline void setFinalizedSampler(size_t index, id<MTLSamplerState> s) {
assert_invariant(encoder);
assert_invariant(!finalized);
[encoder setSamplerState:s atIndex:(index * 2 + 1)];
samplers[index] = s;
}
// A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in
// a draw call.
// Once a SamplerGroup is finalized, it must be reset or mutated to be written into again.
void finalize() { finalized = true; }
void finalize();
bool isFinalized() const noexcept { return finalized; }
// Both of these methods "unfinalize" a SamplerGroup, allowing it to be updated via calls to
@@ -305,7 +305,7 @@ public:
// the texture types have changed.
// Mutate re-encodes the current set of samplers/textures into the new argument
// buffer.
void reset(id<MTLCommandBuffer> cmdBuffer, id<MTLArgumentEncoder> e);
void reset(id<MTLCommandBuffer> cmdBuffer, id<MTLArgumentEncoder> e, id<MTLDevice> device);
void mutate(id<MTLCommandBuffer> cmdBuffer);
id<MTLBuffer> getArgumentBuffer() const {
@@ -433,8 +433,7 @@ private:
static MTLLoadAction getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
static MTLStoreAction getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
static id<MTLTexture> createMultisampledTexture(id<MTLDevice> device, MTLPixelFormat format,
uint32_t width, uint32_t height, uint8_t samples);
id<MTLTexture> createMultisampledTexture(MTLPixelFormat format, uint32_t width, uint32_t height, uint8_t samples) const;
MetalContext* context;
bool defaultRenderTarget = false;
+58 -31
View File
@@ -26,6 +26,7 @@
#include "private/backend/BackendUtils.h"
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <utils/trap.h>
#include <utils/debug.h>
@@ -270,8 +271,9 @@ void MetalSwapChain::scheduleFrameCompletedCallback() {
}];
}
MetalBufferObject::MetalBufferObject(MetalContext& context, BufferUsage usage, uint32_t byteCount)
: HwBufferObject(byteCount), buffer(context, usage, byteCount) {}
MetalBufferObject::MetalBufferObject(MetalContext& context, BufferObjectBinding bindingType,
BufferUsage usage, uint32_t byteCount)
: HwBufferObject(byteCount), buffer(context, bindingType, usage, byteCount) {}
void MetalBufferObject::updateBuffer(void* data, size_t size, uint32_t byteOffset) {
buffer.copyIntoBuffer(data, size, byteOffset);
@@ -287,7 +289,7 @@ MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, usage, elementSize * indexCount, true) { }
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer*
indexBuffer) {
@@ -338,13 +340,12 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcept
: HwProgram(program.getName()), vertexFunction(nil), fragmentFunction(nil),
isValid(false) {
computeFunction(nil), isValid(false) {
using MetalFunctionPtr = __strong id<MTLFunction>*;
// FIXME: rework this to handle compute
//static_assert(Program::SHADER_TYPE_COUNT == 2, "Only vertex and fragment shaders expected.");
MetalFunctionPtr shaderFunctions[2] = { &vertexFunction, &fragmentFunction };
static_assert(Program::SHADER_TYPE_COUNT == 3, "Only vertex, fragment, and/or compute shaders expected.");
MetalFunctionPtr shaderFunctions[3] = { &vertexFunction, &fragmentFunction, &computeFunction };
const auto& sources = program.getShadersSource();
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
@@ -387,12 +388,19 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
}, sc.value);
}
*shaderFunctions[i] = [library newFunctionWithName:@"main0"
constantValues:constants
error:&error];
id<MTLFunction> function = [library newFunctionWithName:@"main0"
constantValues:constants
error:&error];
assert_invariant(function);
*shaderFunctions[i] = function;
}
UTILS_UNUSED_IN_RELEASE const bool isRasterizationProgram =
vertexFunction != nil && fragmentFunction != nil;
UTILS_UNUSED_IN_RELEASE const bool isComputeProgram = computeFunction != nil;
// The program must be either a rasterization program XOR a compute program.
assert_invariant(isRasterizationProgram != isComputeProgram);
// All stages of the program have compiled successfully, this is a valid program.
isValid = true;
@@ -802,7 +810,26 @@ void MetalTexture::updateLodRange(uint32_t min, uint32_t max) {
lodTextureView = nil;
}
void MetalSamplerGroup::reset(id<MTLCommandBuffer> cmdBuffer, id<MTLArgumentEncoder> e) {
void MetalSamplerGroup::finalize() {
assert_invariant(encoder);
// TODO: we should be able to encode textures and samplers inside setFinalizedTexture and
// setFinalizedSampler as they become available, but Metal doesn't seem to like this; the arg
// buffer gets encoded incorrectly. This warrants more investigation.
auto [buffer, offset] = argBuffer->getCurrentAllocation();
[encoder setArgumentBuffer:buffer offset:offset];
// Encode all textures and samplers.
for (size_t s = 0; s < size; s++) {
[encoder setTexture:textures[s] atIndex:(s * 2 + 0)];
[encoder setSamplerState:samplers[s] atIndex:(s * 2 + 1)];
}
finalized = true;
}
void MetalSamplerGroup::reset(id<MTLCommandBuffer> cmdBuffer, id<MTLArgumentEncoder> e,
id<MTLDevice> device) {
encoder = e;
// The number of slots in the ring buffer we use to manage argument buffer allocations.
@@ -820,15 +847,12 @@ void MetalSamplerGroup::reset(id<MTLCommandBuffer> cmdBuffer, id<MTLArgumentEnco
// Chances are, even though the MTLArgumentEncoder might change, the required size and alignment
// probably won't. So we can re-use the previous ring buffer.
if (UTILS_UNLIKELY(!argBuffer || !argBuffer->canAccomodateLayout(argBufferLayout))) {
argBuffer = std::make_unique<MetalRingBuffer>(encoder.device, MTLResourceStorageModeShared,
argBuffer = std::make_unique<MetalRingBuffer>(device, MTLResourceStorageModeShared,
argBufferLayout, METAL_ARGUMENT_BUFFER_SLOTS);
} else {
argBuffer->createNewAllocation(cmdBuffer);
}
auto [buffer, offset] = argBuffer->getCurrentAllocation();
[encoder setArgumentBuffer:buffer offset:offset];
// Clear all textures and samplers.
assert_invariant(textureHandles.size() == textures.size());
assert_invariant(textures.size() == samplers.size());
@@ -844,15 +868,7 @@ void MetalSamplerGroup::reset(id<MTLCommandBuffer> cmdBuffer, id<MTLArgumentEnco
void MetalSamplerGroup::mutate(id<MTLCommandBuffer> cmdBuffer) {
assert_invariant(finalized); // only makes sense to mutate if this sampler group is finalized
assert_invariant(argBuffer);
auto [buffer, offset] = argBuffer->createNewAllocation(cmdBuffer);
[encoder setArgumentBuffer:buffer offset:offset];
// Re-encode all textures and samplers.
for (size_t s = 0; s < size; s++) {
[encoder setTexture:textures[s] atIndex:(s * 2 + 0)];
[encoder setSamplerState:samplers[s] atIndex:(s * 2 + 1)];
}
argBuffer->createNewAllocation(cmdBuffer);
finalized = false;
}
@@ -901,7 +917,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
if (samples > 1 && color[i].getSampleCount() == 1) {
auto& sidecar = color[i].metalTexture->msaaSidecar;
if (!sidecar) {
sidecar = createMultisampledTexture(context->device, color[i].getPixelFormat(),
sidecar = createMultisampledTexture(color[i].getPixelFormat(),
color[i].metalTexture->width, color[i].metalTexture->height, samples);
}
}
@@ -926,7 +942,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
if (samples > 1 && depth.getSampleCount() == 1) {
auto& sidecar = depth.metalTexture->msaaSidecar;
if (!sidecar) {
sidecar = createMultisampledTexture(context->device, depth.getPixelFormat(),
sidecar = createMultisampledTexture(depth.getPixelFormat(),
depth.metalTexture->width, depth.metalTexture->height, samples);
}
}
@@ -951,7 +967,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
if (samples > 1 && stencil.getSampleCount() == 1) {
auto& sidecar = stencil.metalTexture->msaaSidecar;
if (!sidecar) {
sidecar = createMultisampledTexture(context->device, stencil.getPixelFormat(),
sidecar = createMultisampledTexture(stencil.getPixelFormat(),
stencil.metalTexture->width, stencil.metalTexture->height, samples);
}
}
@@ -1038,6 +1054,7 @@ void MetalRenderTarget::setUpRenderPassAttachments(MTLRenderPassDescriptor* desc
descriptor.depthAttachment.slice = 0;
const bool discard = any(discardFlags & TargetBufferFlags::DEPTH);
if (!discard) {
assert_invariant(context->supportsAutoDepthResolve);
descriptor.depthAttachment.resolveTexture = depthAttachment.getTexture();
descriptor.depthAttachment.resolveLevel = depthAttachment.level;
descriptor.depthAttachment.resolveSlice = depthAttachment.layer;
@@ -1070,10 +1087,14 @@ void MetalRenderTarget::setUpRenderPassAttachments(MTLRenderPassDescriptor* desc
descriptor.stencilAttachment.slice = 0;
const bool discard = any(discardFlags & TargetBufferFlags::STENCIL);
if (!discard) {
assert_invariant(context->supportsAutoDepthResolve);
descriptor.stencilAttachment.resolveTexture = stencilAttachment.getTexture();
descriptor.stencilAttachment.resolveLevel = stencilAttachment.level;
descriptor.stencilAttachment.resolveSlice = stencilAttachment.layer;
descriptor.stencilAttachment.storeAction = MTLStoreActionMultisampleResolve;
if (@available(iOS 12.0, *)) {
descriptor.stencilAttachment.stencilResolveFilter = MTLMultisampleStencilResolveFilterSample0;
}
}
}
}
@@ -1135,8 +1156,8 @@ MTLStoreAction MetalRenderTarget::getStoreAction(const RenderPassParams& params,
return MTLStoreActionStore;
}
id<MTLTexture> MetalRenderTarget::createMultisampledTexture(id<MTLDevice> device,
MTLPixelFormat format, uint32_t width, uint32_t height, uint8_t samples) {
id<MTLTexture> MetalRenderTarget::createMultisampledTexture(MTLPixelFormat format,
uint32_t width, uint32_t height, uint8_t samples) const {
MTLTextureDescriptor* descriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
width:width
@@ -1147,7 +1168,13 @@ id<MTLTexture> MetalRenderTarget::createMultisampledTexture(id<MTLDevice> device
descriptor.usage = MTLTextureUsageRenderTarget;
descriptor.resourceOptions = MTLResourceStorageModePrivate;
return [device newTextureWithDescriptor:descriptor];
if (context->supportsMemorylessRenderTargets) {
if (@available(macOS 11.0, *)) {
descriptor.resourceOptions = MTLResourceStorageModeMemoryless;
}
}
return [context->device newTextureWithDescriptor:descriptor];
}
math::uint2 MetalRenderTarget::getAttachmentSize() noexcept {
+14 -16
View File
@@ -37,6 +37,8 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
return lhs.u == rhs.u;
}
// Rasterization Bindings
// ----------------------
// Bindings Buffer name Count
// ------------------------------------------------------
// 0 Zero buffer (placeholder vertex buffer) 1
@@ -46,6 +48,16 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
//
// Total 31
// Compute Bindings
// ----------------------
// Bindings Buffer name Count
// ------------------------------------------------------
// 0-3 SSBO buffers 4 MAX_SSBO_COUNT
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
//
// Total 18
// The total number of vertex buffer "slots" that the Metal backend can bind.
// + 1 to account for the zero buffer, a placeholder buffer used internally by the Metal backend.
// MAX_VERTEX_BUFFER_COUNT represents the max number of vertex buffers Filament can bind.
@@ -59,6 +71,7 @@ static constexpr uint32_t USER_VERTEX_BUFFER_BINDING_START = 1u;
// These constants must match the equivalent in CodeGenerator.h.
static constexpr uint32_t UNIFORM_BUFFER_BINDING_START = 17u;
static constexpr uint32_t SSBO_BINDING_START = 0u;
static constexpr uint32_t SAMPLER_GROUP_BINDING_START = 27u;
// Forward declarations necessary here, definitions at end of file.
@@ -335,27 +348,12 @@ using DepthStencilStateCache = StateCache<DepthStencilState, id<MTLDepthStencilS
class MetalBufferObject;
struct UniformBufferState {
struct BufferState {
MetalBufferObject* buffer = nullptr; // 8 bytes
uint32_t offset = 0; // 4 bytes
bool bound = false; // 1 byte
char padding[3] = { 0 }; // 3 bytes
bool operator==(const UniformBufferState& rhs) const noexcept {
return this->bound == rhs.bound &&
this->buffer == rhs.buffer &&
this->offset == rhs.offset;
}
bool operator!=(const UniformBufferState& rhs) const noexcept {
return !operator==(rhs);
}
};
static_assert(sizeof(UniformBufferState) == 16, "UniformBufferState unexpected size.");
using UniformBufferStateTracker = StateTracker<UniformBufferState>;
// Sampler states
struct SamplerState {
+3
View File
@@ -328,4 +328,7 @@ void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
void NoopDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
}
void NoopDriver::resetState(int) {
}
} // namespace filament
+204 -29
View File
@@ -61,6 +61,8 @@ OpenGLContext::OpenGLContext() noexcept {
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &gets.max_draw_buffers);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units);
glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &gets.max_transform_feedback_separate_attribs);
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &gets.max_uniform_buffer_bindings);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &gets.uniform_buffer_offset_alignment);
constexpr auto const caps3 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_3];
@@ -247,31 +249,7 @@ OpenGLContext::OpenGLContext() noexcept {
flush(slog.v);
#endif
/*
* Set our default state
*/
// We need to make sure our internal state matches the GL state when we start.
// (some of these calls may be unneeded as they might be the gl defaults)
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_DITHER);
glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
glDisable(GL_SAMPLE_COVERAGE);
glDisable(GL_POLYGON_OFFSET_FILL);
// Point sprite size and seamless cubemap filtering are disabled by default in desktop GL.
// In OpenGL ES, these flags do not exist because they are always on.
#if BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL
enable(GL_PROGRAM_POINT_SIZE);
#endif
#ifdef GL_ARB_seamless_cube_map
enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
#endif
setDefaultState();
#ifdef GL_EXT_texture_filter_anisotropic
if (ext.EXT_texture_filter_anisotropic) {
@@ -290,10 +268,6 @@ OpenGLContext::OpenGLContext() noexcept {
}
#endif
#ifdef GL_FRAGMENT_SHADER_DERIVATIVE_HINT
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
#endif
#if !defined(NDEBUG) && defined(GL_KHR_debug)
if (ext.KHR_debug) {
auto cb = [](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
@@ -325,6 +299,50 @@ OpenGLContext::OpenGLContext() noexcept {
glDebugMessageCallback(cb, nullptr);
}
#endif
}
void OpenGLContext::setDefaultState() noexcept {
// We need to make sure our internal state matches the GL state when we start.
// (some of these calls may be unneeded as they might be the gl defaults)
GLenum caps[] = {
GL_BLEND,
GL_CULL_FACE,
GL_SCISSOR_TEST,
GL_DEPTH_TEST,
GL_STENCIL_TEST,
GL_DITHER,
GL_SAMPLE_ALPHA_TO_COVERAGE,
GL_SAMPLE_COVERAGE,
GL_POLYGON_OFFSET_FILL,
};
size_t capsCount = sizeof(caps) / sizeof(caps[0]);
UTILS_NOUNROLL
for (GLenum capi = 0; capi < capsCount; ++capi) {
size_t capIndex = getIndexForCap(caps[capi]);
GLenum cap = caps[capi];
if (state.enables.caps[capIndex]) {
glEnable(cap);
} else {
glDisable(cap);
}
}
// Point sprite size and seamless cubemap filtering are disabled by default in desktop GL.
// In OpenGL ES, these flags do not exist because they are always on.
#if BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL
glEnable(GL_PROGRAM_POINT_SIZE);
enable(GL_PROGRAM_POINT_SIZE);
#endif
#ifdef GL_ARB_seamless_cube_map
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
#endif
#ifdef GL_FRAGMENT_SHADER_DERIVATIVE_HINT
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
#endif
#if defined(GL_EXT_clip_control) || defined(GL_ARB_clip_control) || defined(GL_VERSION_4_5)
if (ext.EXT_clip_control) {
@@ -521,4 +539,161 @@ void OpenGLContext::deleteVertexArrays(GLsizei n, const GLuint* arrays) noexcept
}
}
void OpenGLContext::resetState() noexcept {
// Force GL state to match the Filament state
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, state.draw_fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, state.read_fbo);
// state.program
glUseProgram(state.program.use);
// state.vao
if (state.vao.p) {
glBindVertexArray(state.vao.p->vao);
} else {
bindVertexArray(nullptr);
}
// state.raster
glFrontFace(state.raster.frontFace);
glCullFace(state.raster.cullFace);
glBlendEquationSeparate(state.raster.blendEquationRGB, state.raster.blendEquationA);
glBlendFuncSeparate(
state.raster.blendFunctionSrcRGB,
state.raster.blendFunctionDstRGB,
state.raster.blendFunctionSrcA,
state.raster.blendFunctionDstA
);
glColorMask(
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask
);
glDepthMask(state.raster.depthMask);
glDepthFunc(state.raster.depthFunc);
// state.stencil
glStencilFuncSeparate(
GL_FRONT,
state.stencil.front.func.func,
state.stencil.front.func.ref,
state.stencil.front.func.mask
);
glStencilFuncSeparate(
GL_BACK,
state.stencil.back.func.func,
state.stencil.back.func.ref,
state.stencil.back.func.mask
);
glStencilOpSeparate(
GL_FRONT,
state.stencil.front.op.sfail,
state.stencil.front.op.dpfail,
state.stencil.front.op.dppass
);
glStencilOpSeparate(
GL_BACK,
state.stencil.back.op.sfail,
state.stencil.back.op.dpfail,
state.stencil.back.op.dppass
);
glStencilMaskSeparate(GL_FRONT, state.stencil.front.stencilMask);
glStencilMaskSeparate(GL_BACK, state.stencil.back.stencilMask);
// state.polygonOffset
glPolygonOffset(state.polygonOffset.factor, state.polygonOffset.units);
// state.enables
setDefaultState();
// state.buffers
// Reset state.buffers to its default state to avoid the complexity and error-prone
// nature of resetting the GL state to its existing state
state.buffers = {};
GLenum bufferTargets[] = {
GL_UNIFORM_BUFFER,
GL_TRANSFORM_FEEDBACK_BUFFER,
#if !defined(__EMSCRIPTEN__)
GL_SHADER_STORAGE_BUFFER,
#endif
GL_ARRAY_BUFFER,
GL_COPY_READ_BUFFER,
GL_COPY_WRITE_BUFFER,
GL_ELEMENT_ARRAY_BUFFER,
GL_PIXEL_PACK_BUFFER,
GL_PIXEL_UNPACK_BUFFER,
};
size_t bufferTargetCount = sizeof(bufferTargets) / sizeof(bufferTargets[0]);
for (size_t targetArrayIndex = 0; targetArrayIndex < bufferTargetCount; ++targetArrayIndex) {
GLenum target = bufferTargets[targetArrayIndex];
glBindBuffer(target, 0);
}
for (size_t bufferIndex = 0; bufferIndex < MAX_BUFFER_BINDINGS; ++bufferIndex) {
if (bufferIndex < (size_t)gets.max_uniform_buffer_bindings) {
glBindBufferBase(GL_UNIFORM_BUFFER, bufferIndex, 0);
}
if (bufferIndex < (size_t)gets.max_transform_feedback_separate_attribs) {
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, bufferIndex, 0);
}
}
// state.textures
// Reset state.textures to its default state to avoid the complexity and error-prone
// nature of resetting the GL state to its existing state
state.textures = {};
const GLuint textureTargets[] = {
GL_TEXTURE_2D,
GL_TEXTURE_2D_ARRAY,
GL_TEXTURE_CUBE_MAP,
GL_TEXTURE_3D,
#if !defined(__EMSCRIPTEN__)
GL_TEXTURE_2D_MULTISAMPLE,
GL_TEXTURE_EXTERNAL_OES,
GL_TEXTURE_CUBE_MAP_ARRAY,
#endif
};
const size_t textureTargetCount = sizeof(textureTargets) / sizeof(textureTargets[0]);
for (GLint unit = 0; unit < gets.max_combined_texture_image_units; ++unit) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindSampler(unit, 0);
for (size_t textureTargetArrayIndex = 0; textureTargetArrayIndex < textureTargetCount; ++textureTargetArrayIndex) {
GLuint target = textureTargets[textureTargetArrayIndex];
glBindTexture(target, 0);
}
}
glActiveTexture(GL_TEXTURE0 + state.textures.active);
// state.unpack
glPixelStorei(GL_UNPACK_ALIGNMENT, state.unpack.alignment);
glPixelStorei(GL_UNPACK_ROW_LENGTH, state.unpack.row_length);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, state.unpack.skip_pixels);
glPixelStorei(GL_UNPACK_SKIP_ROWS, state.unpack.skip_row);
// state.pack
glPixelStorei(GL_PACK_ALIGNMENT, state.pack.alignment);
glPixelStorei(GL_PACK_ROW_LENGTH, state.pack.row_length);
glPixelStorei(GL_PACK_SKIP_PIXELS, state.pack.skip_pixels);
glPixelStorei(GL_PACK_SKIP_ROWS, state.pack.skip_row);
// state.window
glScissor(
state.window.scissor.x,
state.window.scissor.y,
state.window.scissor.z,
state.window.scissor.w
);
glViewport(
state.window.viewport.x,
state.window.viewport.y,
state.window.viewport.z,
state.window.viewport.w
);
glDepthRangef(state.window.depthRange.x, state.window.depthRange.y);
}
} // namesapce filament
@@ -73,6 +73,7 @@ public:
ShaderModel getShaderModel() const noexcept { return mShaderModel; }
void resetState() noexcept;
inline void useProgram(GLuint program) noexcept;
@@ -129,6 +130,8 @@ public:
GLint max_uniform_block_size;
GLint max_texture_image_units;
GLint max_combined_texture_image_units;
GLint max_transform_feedback_separate_attribs;
GLint max_uniform_buffer_bindings;
GLint uniform_buffer_offset_alignment;
} gets = {};
@@ -409,6 +412,8 @@ private:
}
}
void setDefaultState() noexcept;
static constexpr const size_t TEXTURE_TARGET_COUNT =
sizeof(state.textures.units[0].targets) / sizeof(state.textures.units[0].targets[0]);
+5 -1
View File
@@ -235,6 +235,9 @@ ShaderModel OpenGLDriver::getShaderModel() const noexcept {
// ------------------------------------------------------------------------------------------------
// Change and track GL state
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::resetState(int) {
mContext.resetState();
}
void OpenGLDriver::bindSampler(GLuint unit, GLuint sampler) noexcept {
mContext.bindSampler(unit, sampler);
@@ -3185,7 +3188,8 @@ void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
useProgram(p);
#if defined(GL_ES_VERSION_3_1) || defined(GL_VERSION_4_3)
glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z);
// FIXME: this line is commented-out for now due to issues switching G3 clients to API 21.
// glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z);
#endif
#ifdef FILAMENT_ENABLE_MATDBG
+9 -1
View File
@@ -93,8 +93,11 @@
*/
#if defined(GL_ES_VERSION_2_0)
#ifdef GL_EXT_disjoint_timer_query
# define GL_TIME_ELAPSED GL_TIME_ELAPSED_EXT
# ifndef GL_TIME_ELAPSED
# define GL_TIME_ELAPSED GL_TIME_ELAPSED_EXT
# endif
#endif
#ifdef GL_EXT_clip_control
@@ -165,7 +168,12 @@
#define GL_COMPUTE_SHADER 0x91B9
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
// FIXME: The GL_TIME_ELAPSED define is used unconditionally in Filament, but
// requires extension support.
#ifndef GL_TIME_ELAPSED
#define GL_TIME_ELAPSED 0x88BF
#endif
#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
@@ -35,9 +35,11 @@
#include <jni.h>
// We require filament to be built with a API 21 toolchain, before that, OpenGLES 3.1 didn't exist
#if __ANDROID_API__ < 21
# error "__ANDROID_API__ must be at least 21"
// We require filament to be built with a API 19 toolchain, before that, OpenGLES 3.0 didn't exist
// Actually, OpenGL ES 3.0 was added to API 18, but API 19 is the better target and
// the minimum for Jetpack at the time of this comment.
#if __ANDROID_API__ < 19
# error "__ANDROID_API__ must be at least 19"
#endif
using namespace utils;
-1
View File
@@ -54,7 +54,6 @@ io::ostream& operator<<(io::ostream& out, PrimitiveType type) {
CASE(PrimitiveType, LINES)
CASE(PrimitiveType, LINE_STRIP)
CASE(PrimitiveType, POINTS)
CASE(PrimitiveType, NONE)
}
return out;
}
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2018 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 "vulkan/PlatformVkLinuxGGP.h"
#include <bluevk/BlueVK.h>
#if defined(FILAMENT_SUPPORTS_GGP)
#include <ggp_c/ggp.h>
#endif
#include <utils/Panic.h>
#include "VulkanDriverFactory.h"
using namespace bluevk;
namespace filament::backend {
Driver* PlatformVkLinuxGGP::createDriver(
void* const sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
#if defined(FILAMENT_SUPPORTS_GGP)
ASSERT_PRECONDITION(sharedContext == nullptr,
"Vulkan does not support shared contexts.");
const char* requiredInstanceExtensions[] = {
VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME};
return VulkanDriverFactory::create(this, requiredInstanceExtensions,
sizeof(requiredInstanceExtensions) /
sizeof(requiredInstanceExtensions[0]),
driverConfig);
#else
PANIC_PRECONDITION("Filament does not support GGP.");
return nullptr;
#endif
}
void* PlatformVkLinuxGGP::createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept {
#if defined(FILAMENT_SUPPORTS_GGP)
VkStreamDescriptorSurfaceCreateInfoGGP surface_create_info = {
VK_STRUCTURE_TYPE_STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP};
surface_create_info.streamDescriptor = kGgpPrimaryStreamDescriptor;
PFN_vkCreateStreamDescriptorSurfaceGGP fpCreateStreamDescriptorSurfaceGGP =
reinterpret_cast<PFN_vkCreateStreamDescriptorSurfaceGGP>(
vkGetInstanceProcAddr(static_cast<VkInstance>(instance),
"vkCreateStreamDescriptorSurfaceGGP"));
ASSERT_PRECONDITION(fpCreateStreamDescriptorSurfaceGGP != nullptr,
"Error getting VkInstance "
"function vkCreateStreamDescriptorSurfaceGGP");
VkSurfaceKHR surface = nullptr;
VkResult res = fpCreateStreamDescriptorSurfaceGGP(
static_cast<VkInstance>(instance), &surface_create_info, nullptr,
&surface);
ASSERT_PRECONDITION(res == VK_SUCCESS, "Error in vulkan: %d", res);
return surface;
#else
PANIC_PRECONDITION("Filament does not support GGP.");
return nullptr;
#endif
}
} // namespace filament::backend
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKAN_PLATFORM_VK_LINUX_GGP_H
#define TNT_FILAMENT_BACKEND_VULKAN_PLATFORM_VK_LINUX_GGP_H
#include <backend/DriverEnums.h>
#include <stdint.h>
#include "VulkanPlatform.h"
namespace filament::backend {
class PlatformVkLinuxGGP final : public VulkanPlatform {
public:
Driver* createDriver(
void* const sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
void* createVkSurfaceKHR(void* nativeWindow, void* instance,
uint64_t flags) noexcept override;
int getOSVersion() const noexcept override { return 0; }
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKAN_PLATFORM_VK_LINUX_GGP_H
+5 -1
View File
@@ -1583,7 +1583,8 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memReqs.size,
.memoryTypeIndex = mContext.selectMemoryType(memReqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
};
vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory);
@@ -2011,6 +2012,9 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
#endif
}
void VulkanDriver::resetState(int) {
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<VulkanDriver>;
+2 -14
View File
@@ -49,14 +49,10 @@ VulkanProgram::VulkanProgram(VulkanContext& context, const Program& builder) noe
HwProgram(builder.getName()), context(context) {
auto const& blobs = builder.getShadersSource();
VkShaderModule* modules[2] = { &bundle.vertex, &bundle.fragment };
bool missing = false;
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
// TODO: handle compute shaders.
for (size_t i = 0; i < 2; i++) {
const auto& blob = blobs[i];
VkShaderModule* module = modules[i];
if (blob.empty()) {
missing = true;
continue;
}
VkShaderModuleCreateInfo moduleInfo = {};
moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
moduleInfo.codeSize = blob.size();
@@ -106,13 +102,6 @@ VulkanProgram::VulkanProgram(VulkanContext& context, const Program& builder) noe
bundle.specializationInfos = pInfo;
}
// Output a warning because it's okay to encounter empty blobs, but it's not okay to use
// this program handle in a draw call.
if (missing) {
utils::slog.w << "Missing SPIR-V shader: " << this->name.c_str() << utils::io::endl;
return;
}
// Make a copy of the binding map
samplerGroupInfo = builder.getSamplerGroupInfo();
if constexpr (FILAMENT_VULKAN_VERBOSE) {
@@ -274,7 +263,6 @@ VulkanBufferObject::VulkanBufferObject(VulkanContext& context, VulkanStagePool&
void VulkanRenderPrimitive::setPrimitiveType(PrimitiveType pt) {
this->type = pt;
switch (pt) {
case PrimitiveType::NONE:
case PrimitiveType::POINTS:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
break;
@@ -58,7 +58,9 @@ VulkanPipelineCache::getUsageFlags(uint16_t binding, ShaderStageFlags flags, Usa
if (any(flags & ShaderStageFlags::FRAGMENT)) {
src.set(MAX_SAMPLER_COUNT + binding);
}
assert_invariant(!any(flags & ~(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)));
// TODO: add support for compute by extending SHADER_MODULE_COUNT and ensuring UsageFlags
// has 186 bits (MAX_SAMPLER_COUNT * 3)
// assert_invariant(!any(flags & ~(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)));
return src;
}
+34 -12
View File
@@ -22,25 +22,29 @@ using namespace filament::backend;
TEST_F(ComputeTest, basic) {
auto& driver = getDriverApi();
std::string_view shader_gles310 = {R"(
std::string shader_gles310 = {R"(
#version 310 es
layout(local_size_x = 16) in;
void main() {
}
)"};
std::string_view shader_gl450 = {R"(
std::string shader_gl450 = {R"(
#version 450 core
layout(local_size_x = 16) in;
void main() {
}
)"};
std::string_view shader_msl = {R"(
// TODO: msl test
std::string shader_msl = {R"(
#include <simd/simd.h>
#include <metal_stdlib>
using namespace metal;
constant uint3 WorkGroupSize [[maybe_unused]] = uint3(16u, 1u, 1u);
kernel void main0() {}
)"};
std::string_view shader_spirv = {R"(
std::string shader_spirv = {R"(
// TODO: spirv test
)"};
@@ -54,7 +58,7 @@ void main() {
}
Program program;
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size());
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size() + 1);
Handle<HwProgram> ph = driver.createProgram(std::move(program));
driver.dispatchCompute(ph, { 1, 1, 1 });
@@ -68,7 +72,7 @@ void main() {
TEST_F(ComputeTest, copy) {
auto& driver = getDriverApi();
std::string_view shader_gles310 = {R"(
std::string shader_gles310 = {R"(
#version 310 es
layout(local_size_x = 16) in;
layout(std430) buffer;
@@ -80,7 +84,7 @@ void main() {
}
)"};
std::string_view shader_gl450 = {R"(
std::string shader_gl450 = {R"(
#version 450 core
layout(local_size_x = 16) in;
layout(std430) buffer;
@@ -92,11 +96,25 @@ void main() {
}
)"};
std::string_view shader_msl = {R"(
// TODO: msl test
std::string shader_msl = {R"(
#include <simd/simd.h>
#include <metal_stdlib>
using namespace metal;
struct Output_data {
float elements[1];
};
struct Input_data {
float elements[1];
};
constant uint3 WorkGroupSize [[maybe_unused]] = uint3(16u, 1u, 1u);
kernel void main0(device Output_data& output_data [[buffer(0)]],
device Input_data& input_data [[buffer(1)]],
uint3 GlobalInvocationID [[thread_position_in_grid]]) {
output_data.elements[GlobalInvocationID.x] = input_data.elements[GlobalInvocationID.x];
}
)"};
std::string_view shader_spirv = {R"(
std::string shader_spirv = {R"(
// TODO: spirv test
)"};
@@ -119,12 +137,14 @@ void main() {
return v;
});
driver.startCapture(0);
auto output_data = driver.createBufferObject(size, BufferObjectBinding::SHADER_STORAGE, BufferUsage::STATIC);
auto input_data = driver.createBufferObject(size, BufferObjectBinding::SHADER_STORAGE, BufferUsage::STATIC);
driver.updateBufferObject(input_data, { data.data(), size }, 0);
Program program;
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size());
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size() + 1);
Handle<HwProgram> ph = driver.createProgram(std::move(program));
@@ -144,6 +164,8 @@ void main() {
driver.destroyBufferObject(output_data);
driver.finish();
driver.stopCapture(0);
executeCommands();
getDriver().purge();
+76 -9
View File
@@ -11,7 +11,7 @@
</style>
**Vulkan Backend**
*Last updated: 11 June 2021*
*Last updated: 26 October 2022*
![](../../docs/images/filament_logo.png)
@@ -47,7 +47,7 @@ single `VkDevice` and `VkInstance`.
* | | | | *
* |------------------------------------------| |-----------------------------------------| *
* | | | | *
* | Helpers VulkanContext | | SwapContext SwapContext | *
* | Helpers VulkanContext | | VulkanTexture VulkanTexture | *
* | .--------------. .---------------. | | .----------------+----------------. | *
* | | PipelineCache| | VkDevice | | | | VkImage | VkImage | | *
* | +--------------+ | VkInstance | | | | VkImageView | VkImageView | | *
@@ -91,13 +91,14 @@ that are defined in `DriverBase.h`.
Most of the handle types do not contain much logic, but they often do more than mere wrapping of
Vk objects. Notably, `VulkanRenderTarget` does not have a straightforward mapping to any one Vk
object. See its class comment for details.
object. See its class comment for details. Also, `VulkanTexture` and `VulkanSwapChain` have their
own cpps file because of complexity around layouts and subresources.
VulkanProgram
: Descends from `HwProgram`, owns a pair of `VkShaderModule`.
VulkanRenderTarget
: Descends from `HwRenderTarget`, contains references to `VkImage` objects.
: Descends from `HwRenderTarget`, contains references to `VulkanTexture` objects.
VulkanSwapChain
: Descends from `HwSwapChain`, has a reference to `VkSurfaceKHR` and `VkSwapchainKHR`.
@@ -127,14 +128,16 @@ they do follow a common set of conventions. For example, most of them implement
that is called once per frame to free unused objects.
VulkanPipelineCache
: Cache of VkDescriptorSet and VkPipeline.
: Cache of VkDescriptorSet, VkPipeline, and VkPipelineLayout.
VulkanDisposer
: Holds reference counts to Vulkan objects in order to defer their destruction.
VulkanFboCache
: Cache of VkRenderPass and VkFramebuffer. Contains weak references to VkImage handles, but does not
own any pixel data.
own any pixel data. The cache key for render passes is quite complex because it needs to include
image layout information in a compact way. For details see the comments in the `RenderPassKey`
declaration.
VulkanStagePool
: Pool of VkBuffer objects that can be memory mapped (shared between host and device).
@@ -149,6 +152,71 @@ no subfolders. All backend-specific types and functions live directly in the `fi
namespace, and we simply prefix our names with "Vulkan" when necessary to avoid ambiguation with
OpenGL.
# Image Layout Strategy
Adhering to a coherent strategy for image layout transitions is crucial. Without this, validation
errors (and actual black screens with some GPU vendors) occur far too easily and can feel like
whack-a-mole.
Vulkan provides several mechanisms for transitioning layout (render passes, barriers) so the
strategy outlined below specifies which mechanism should be used in various scenarios.
The current image layouts are tracked at the subresource level in `VulkanTexture` using a
specialized STL-ish container that we designed for this purpose:
[RangeMap](https://github.com/google/filament/blob/main/libs/utils/include/utils/RangeMap.h).
In the past we tried to avoid tracking since it theoritically duplicates state that exists in the
GPU vendor's driver. However in practice this was impossible to manage, because complex logic was
required to deduce the current layout. Also, tracking lets us use `assert_invariant` in many places
to ensure that the behavior described in this document is implemented properly. It is much easier
to catch errors with asserts than with validation.
As of this writing, the current rules are:
(1) **Read-Only Color Textures**
These should be created in an UNDEFINED state and stay that way until data gets uploaded into them,
at which point the appropriate subresources are transitioned to TRANSFER_DST_OPTIMAL for the upload,
then to SHADER_READ_ONLY_OPTIMAL, both times using a barrier. Note that a subresource might be
updated several times; each time it undergoes these two transitions.
If a shader samples from a texture whose mipmaps are only partially loaded, we might see validation
warnings about how some subresources are in an UNDEFINED layout. However if we are properly using
the `setMinMaxLevels` driver API, then the Vulkan backend will not bind those particular
subresources, so validation should not complain.
(2) **Writeable Color Textures**
During construction of the HwTexture, these are transitioned to GENERAL using a barrier.
GENERAL layout is not recommended by AMD best practices but in our case we wish to avoid thrashing
the layout between COLOR_ATTACHMENT_OPTIMAL and SHADER_READ_ONLY_OPTIMAL for post-processing
effects. Moreover we do not have a priori knowledge about blitting and read pixels usage.
(3) **Depth Textures**
During construction of the HwTexture, these are transitioned to GENERAL using a barrier.
Note that some post-processing passes (e.g. SSAO) sample from the depth buffer while also enabling
depth testing. Using GENERAL makes this legal.
(4) **Swap Chain Textures**
During construction, these are transitioned to GENERAL using a barrier. They are then transitioned
to PRESENT at the end of the frame using a barrier, and transitioned back to GENERAL using the first
render pass of the frame.
Note that we transition to PRESENT using a barrier, but with some GPU vendors it might be preferable
to transition them using the last render pass. However we have no way of knowing ahead of time which
pass is last.
Note that Filament allows swap chains to be used as blit targets and blit sources (e.g. for
ReadPixels). Using GENERAL makes this easier to manage.
(5) **Headless Swap Chain Textures**
These follow the same rules as writeable color texture and depth textures.
# VulkanPipelineCache
Uniforms, samplers, and shaders are not directly bindable in Vulkan; instead they are bound through
@@ -161,8 +229,6 @@ public methods. See its header file for details.
The pipeline cache also manages the *layout objects*, which constrain the number of uniform buffers
and samplers that can be bound simultaneously (see `VkDescriptorSetLayout` and `VkPipelineLayout`).
Here we take advantage of Filament's opinionated shading model in order to keep things simple, which
allows us to create only one layout for the entire lifetime of the app.
# Memory Allocation
@@ -196,7 +262,8 @@ Here we list some of the most significant issues and to-be-done items for the Vu
- Improve the UBO strategy
- Currently we use staging buffers and perform lots of copies (host-to-host, then host-to-device)
- Consider an approach similar to MetalDriver, which effectively implements double-buffering using a pool of shared mem buffers
- Consider an approach similar to MetalDriver, which effectively implements double-buffering
using a pool of shared mem buffers
- Consider using subpasses for the depth prepass
- We lazily create pipeline objects
1. This is notoriously slow in Vulkan
-1
View File
@@ -281,7 +281,6 @@ public:
* By default, this is an identity matrix.
*
* @param shift x and y translation added to the projection matrix, specified in NDC
* @param shift x and y translation added to the projection matrix, specified in NDC
* coordinates, that is, if the translation must be specified in pixels,
* shift must be scaled by 1.0 / { viewport.width, viewport.height }.
*
+16
View File
@@ -725,6 +725,22 @@ public:
*/
utils::JobSystem& getJobSystem() noexcept;
#if defined(__EMSCRIPTEN__)
/**
* WebGL only: Tells the driver to reset any internal state tracking if necessary.
*
* This is only useful when integrating an external renderer into Filament on platforms
* like WebGL, where share contexts do not exist. Filament keeps track of the GL
* state it has set (like which texture is bound), and does not re-set that state if
* it does not think it needs to. However, if an external renderer has set different
* state in the mean time, Filament will use that new state unknowingly.
*
* If you are in this situation, call this function - ideally only once per frame,
* immediately after calling Engine::execute().
*/
void resetBackendState() noexcept;
#endif
DebugRegistry& getDebugRegistry() noexcept;
protected:
+9 -4
View File
@@ -226,7 +226,7 @@ public:
* @see ShadowCascades::computeLogSplits
* @see ShadowCascades::computePracticalSplits
*/
float cascadeSplitPositions[3] = { 0.25f, 0.50f, 0.75f };
float cascadeSplitPositions[3] = { 0.125f, 0.25f, 0.50f };
/** Constant bias in world units (e.g. meters) by which shadows are moved away from the
* light. 1mm by default.
@@ -348,6 +348,14 @@ public:
*/
uint8_t msaaSamples = 1;
/**
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
* an improvement to the default EVSM which suffers important light leaks. Enabling
* ELVSM for a single shadowmap doubles the memory usage of all shadow maps.
* ELVSM is mostly useful when large blurs are used.
*/
bool elvsm = false;
/**
* Blur width for the VSM blur. Zero do disable.
* The maximum value is 125.
@@ -438,9 +446,6 @@ public:
* @param enable Enables or disables casting shadows from this Light.
*
* @return This Builder, for chaining calls.
*
* @warning
* - Only a Type.DIRECTIONAL, Type.SUN, Type.SPOT, or Type.FOCUSED_SPOT light can cast shadows
*/
Builder& castShadows(bool enable) noexcept;
@@ -277,6 +277,12 @@ public:
*/
void setMaskThreshold(float threshold) noexcept;
/**
* Gets the minimum alpha value a fragment must have to not be discarded when the blend
* mode is MASKED
*/
float getMaskThreshold() const noexcept;
/**
* Sets the screen space variance of the filter kernel used when applying specular
* anti-aliasing. The default value is set to 0.15. The specified value should be between
@@ -284,6 +290,12 @@ public:
*/
void setSpecularAntiAliasingVariance(float variance) noexcept;
/**
* Gets the screen space variance of the filter kernel used when applying specular
* anti-aliasing.
*/
float getSpecularAntiAliasingVariance() const noexcept;
/**
* Sets the clamping threshold used to suppress estimation errors when applying specular
* anti-aliasing. The default value is set to 0.2. The specified value should be between 0
@@ -291,6 +303,12 @@ public:
*/
void setSpecularAntiAliasingThreshold(float threshold) noexcept;
/**
* Gets the clamping threshold used to suppress estimation errors when applying specular
* anti-aliasing.
*/
float getSpecularAntiAliasingThreshold() const noexcept;
/**
* Enables or disables double-sided lighting if the parent Material has double-sided capability,
* otherwise prints a warning. If double-sided lighting is enabled, backface culling is
@@ -298,36 +316,72 @@ public:
*/
void setDoubleSided(bool doubleSided) noexcept;
/**
* Returns whether double-sided lighting is enabled when the parent Material has double-sided
* capability.
*/
bool isDoubleSided() const noexcept;
/**
* Specifies how transparent objects should be rendered (default is DEFAULT).
*/
void setTransparencyMode(TransparencyMode mode) noexcept;
/**
* Returns the transparency mode.
*/
TransparencyMode getTransparencyMode() const noexcept;
/**
* Overrides the default triangle culling state that was set on the material.
*/
void setCullingMode(CullingMode culling) noexcept;
/**
* Returns the face culling mode.
*/
CullingMode getCullingMode() const noexcept;
/**
* Overrides the default color-buffer write state that was set on the material.
*/
void setColorWrite(bool enable) noexcept;
/**
* Returns whether color write is enabled.
*/
bool isColorWriteEnabled() const noexcept;
/**
* Overrides the default depth-buffer write state that was set on the material.
*/
void setDepthWrite(bool enable) noexcept;
/**
* Returns whether depth write is enabled.
*/
bool isDepthWriteEnabled() const noexcept;
/**
* Overrides the default depth testing state that was set on the material.
*/
void setDepthCulling(bool enable) noexcept;
/**
* Returns whether depth culling is enabled.
*/
bool isDepthCullingEnabled() const noexcept;
/**
* Overrides the default stencil-buffer write state that was set on the material.
*/
void setStencilWrite(bool enable) noexcept;
/**
* Returns whether stencil write is enabled.
*/
bool isStencilWriteEnabled() const noexcept;
/**
* Sets the stencil comparison function (default is StencilCompareFunc::A).
*
+8
View File
@@ -390,6 +390,14 @@ struct VsmShadowOptions {
*/
bool mipmapping = false;
/**
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
* precision is rarely needed, but it does reduces light leaks as well as "fading"
* of the shadows in some situations. Setting highPrecision to true for a single
* shadow map will double the memory usage of all shadow maps.
*/
bool highPrecision = false;
/**
* VSM minimum variance scale, must be positive.
*/
+161
View File
@@ -0,0 +1,161 @@
/*
* Copyright (C) 2022 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 "AtlasAllocator.h"
#include <utils/debug.h>
namespace filament {
using namespace utils;
static inline constexpr std::pair<uint8_t, uint8_t> unmorton(uint16_t m) noexcept {
uint32_t r = (m | (uint32_t(m) << 15u)) & 0x55555555u;
r = (r | (r >> 1u)) & 0x33333333u;
r = (r | (r >> 2u)) & 0x0f0f0f0fu;
r = r | (r >> 4u);
return { uint8_t(r), uint8_t(r >> 16u) };
}
AtlasAllocator::AtlasAllocator(size_t maxTextureSize) noexcept {
// round to power-of-two immediately inferior or equal to the size specified.
mMaxTextureSizePot = (sizeof(maxTextureSize) * 8 - 1u) - utils::clz(maxTextureSize);
}
Viewport AtlasAllocator::allocate(size_t textureSize) noexcept {
Viewport result{};
const size_t powerOfTwo = (sizeof(textureSize) * 8 - 1u) - utils::clz(textureSize);
// asked for a texture size too large
if (UTILS_UNLIKELY(powerOfTwo > mMaxTextureSizePot)) {
return result;
}
// asked for a texture size too small
if (UTILS_UNLIKELY(mMaxTextureSizePot - powerOfTwo >= QuadTree::height())) {
return result;
}
const size_t layer = mMaxTextureSizePot - powerOfTwo;
const NodeId loc = allocateInLayer(layer);
if (loc.l >= 0) {
assert_invariant(loc.l == int8_t(layer));
size_t dimension = 1u << powerOfTwo;
// find the location of in the texture from the morton code (quadtree position)
auto [x, y] = unmorton(loc.code);
// scale to our maximum allocation size
result.left = int32_t(x) << powerOfTwo;
result.bottom = int32_t(y) << powerOfTwo;
result.width = dimension;
result.height = dimension;
}
return result;
}
void AtlasAllocator::clear(size_t maxTextureSize) noexcept {
std::fill(mQuadTree.begin(), mQuadTree.end(), Node{});
mMaxTextureSizePot = (sizeof(maxTextureSize) * 8 - 1u) - utils::clz(maxTextureSize);
}
AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t maxHeight) noexcept {
using namespace QuadTreeUtils;
NodeId candidate{ -1, 0 };
if (UTILS_UNLIKELY(maxHeight > QuadTree::height())) {
return candidate;
}
const int8_t n = int8_t(maxHeight);
QuadTree::traverse(0, 0, n,
[this, n, &candidate](NodeId const& curr) -> QuadTree::TraversalResult {
// we should never reach a level higher than n
assert_invariant(curr.l <= n);
// get the node being processed
const size_t i = index(curr.l, curr.code);
Node const& node = mQuadTree[i];
// if the node is allocated, the whole tree below it is unavailable
if (node.isAllocated()) {
// if a node is allocated it can't have children
assert_invariant(!node.hasChildren());
return QuadTree::TraversalResult::SKIP_CHILDREN;
}
// we're looking for the `deepest` unallocated node that has no children,
// up to the depth we're trying to allocate
if (curr.l > candidate.l && !node.hasChildren()) {
candidate = curr;
// Special case where we found a fitting node, just exit.
if (curr.l == n) {
return QuadTree::TraversalResult::EXIT;
}
}
// We only want to find a fitting node that already has siblings, in order
// to accomplish a "best fit" allocation. So if we're a parent of a 'fitting'
// node and don't have children, we skip the children recursion.
if (curr.l == n - 1 && !node.hasChildren()) {
return QuadTree::TraversalResult::SKIP_CHILDREN;
}
// continue going down the tree
return QuadTree::TraversalResult::RECURSE;
});
if (candidate.l >= 0) {
const size_t i = index(candidate.l, candidate.code);
Node& allocation = mQuadTree[i];
if (candidate.l == n) {
allocation.allocated = true;
if (UTILS_LIKELY(n > 0)) {
// the root doesn't have a parent
const size_t p = parent(candidate.l, candidate.code);
Node& parent = mQuadTree[p];
assert_invariant(!parent.isAllocated());
assert_invariant(parent.hasChildren());
assert_invariant(!parent.hasAllChildren());
parent.children++;
}
} else if (candidate.l < int8_t(QuadTree::height())) {
// we need to create the hierarchy down to the level we need
assert_invariant(!allocation.isAllocated());
assert_invariant(!allocation.hasChildren());
NodeId found{};
QuadTree::traverse(candidate.l, candidate.code,
[this, n, &found](NodeId const& curr) -> QuadTree::TraversalResult {
size_t i = index(curr.l, curr.code);
Node& node = mQuadTree[i];
if (curr.l == n) {
found = curr;
node.allocated = true;
return QuadTree::TraversalResult::EXIT;
}
node.children++;
return QuadTree::TraversalResult::RECURSE;
});
candidate = found;
}
}
return candidate;
}
} // namespace filament
+95
View File
@@ -0,0 +1,95 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_ATLASALLOCATOR_H
#define TNT_FILAMENT_ATLASALLOCATOR_H
#include <utils/QuadTree.h>
#include <filament/Viewport.h>
class AtlasAllocator_AllocateFirstLevel_Test;
class AtlasAllocator_AllocateSecondLevel_Test;
class AtlasAllocator_AllocateMixed0_Test;
class AtlasAllocator_AllocateMixed1_Test;
class AtlasAllocator_AllocateMixed2_Test;
namespace filament {
/*
* A 2D allocator. Allocations must be square and have a power-of-two size.
* AtlasAllocator hard codes a depth of 4, that is only 4 allocation sizes are permitted.
* This doesn't actually allocate memory, just manages space within an abstract 2D image.
*/
class AtlasAllocator {
/*
* A quadtree is used to track the allocated regions. Each node of the quadtree stores a
* `Node` data structure below.
* The `Node` tracks if it is allocated as well as the number of children it has. It doesn't
* track which children though.
*/
struct Node {
// whether this node is allocated. Implies no children.
constexpr bool isAllocated() const noexcept { return allocated; }
// whether this node has children. Implies it's not allocated.
constexpr bool hasChildren() const noexcept { return children != 0; }
// whether this node has all four children. Implies hasChildren().
constexpr bool hasAllChildren() const noexcept { return children == 4; }
bool allocated : 1; // true / false
uint8_t children : 3; // 0, 1, 2, 3, 4
};
static constexpr size_t QUAD_TREE_DEPTH = 4u;
using QuadTree = utils::QuadTreeArray<Node, QUAD_TREE_DEPTH>;
using NodeId = QuadTree::NodeId;
public:
/*
* Create allocator and specify the maximum texture size. Must be a power of two.
* Allocations size allowed are the four power-of-two smaller or equal to this size.
*/
explicit AtlasAllocator(size_t maxTextureSize = 1024) noexcept;
/*
* Allocates a square of size `textureSize`. Must be one of the power-of-two allowed
* (see above).
* Returns the location of the allocation within the maxTextureSize^2 square.
*/
Viewport allocate(size_t textureSize) noexcept;
/*
* Frees all allocations and reset the maximum texture size.
*/
void clear(size_t maxTextureSize = 1024) noexcept;
private:
friend AtlasAllocator_AllocateFirstLevel_Test;
friend AtlasAllocator_AllocateSecondLevel_Test;
friend AtlasAllocator_AllocateMixed0_Test;
friend AtlasAllocator_AllocateMixed1_Test;
friend AtlasAllocator_AllocateMixed2_Test;
QuadTree::NodeId allocateInLayer(size_t n) noexcept;
// quad-tree array to store the allocated list
QuadTree mQuadTree{};
uint8_t mMaxTextureSizePot = 0;
};
} // namespace filament
#endif // TNT_FILAMENT_ATLASALLOCATOR_H
+2 -2
View File
@@ -24,11 +24,11 @@ namespace filament {
void BufferObject::setBuffer(Engine& engine,
BufferObject::BufferDescriptor&& buffer, uint32_t byteOffset) {
upcast(this)->setBuffer(upcast(engine), std::move(buffer), byteOffset);
downcast(this)->setBuffer(downcast(engine), std::move(buffer), byteOffset);
}
size_t BufferObject::getByteCount() const noexcept {
return upcast(this)->getByteCount();
return downcast(this)->getByteCount();
}
} // namespace filament
+32 -32
View File
@@ -69,134 +69,134 @@ mat4 Camera::inverseProjection(const mat4 & p) noexcept {
void Camera::setProjection(Camera::Projection projection, double left, double right, double bottom,
double top, double near, double far) {
upcast(this)->setProjection(projection, left, right, bottom, top, near, far);
downcast(this)->setProjection(projection, left, right, bottom, top, near, far);
}
void Camera::setProjection(double fovInDegrees, double aspect, double near, double far,
Camera::Fov direction) {
upcast(this)->setProjection(fovInDegrees, aspect, near, far, direction);
downcast(this)->setProjection(fovInDegrees, aspect, near, far, direction);
}
void Camera::setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far) {
upcast(this)->setLensProjection(focalLengthInMillimeters, aspect, near, far);
downcast(this)->setLensProjection(focalLengthInMillimeters, aspect, near, far);
}
void Camera::setCustomProjection(mat4 const& projection, double near, double far) noexcept {
upcast(this)->setCustomProjection(projection, near, far);
downcast(this)->setCustomProjection(projection, near, far);
}
void Camera::setCustomProjection(mat4 const& projection, mat4 const& projectionForCulling,
double near, double far) noexcept {
upcast(this)->setCustomProjection(projection, projectionForCulling, near, far);
downcast(this)->setCustomProjection(projection, projectionForCulling, near, far);
}
void Camera::setScaling(double2 scaling) noexcept {
upcast(this)->setScaling(scaling);
downcast(this)->setScaling(scaling);
}
void Camera::setShift(double2 shift) noexcept {
upcast(this)->setShift(shift);
downcast(this)->setShift(shift);
}
mat4 Camera::getProjectionMatrix() const noexcept {
return upcast(this)->getUserProjectionMatrix();
return downcast(this)->getUserProjectionMatrix();
}
mat4 Camera::getCullingProjectionMatrix() const noexcept {
return upcast(this)->getUserCullingProjectionMatrix();
return downcast(this)->getUserCullingProjectionMatrix();
}
double4 Camera::getScaling() const noexcept {
return upcast(this)->getScaling();
return downcast(this)->getScaling();
}
double2 Camera::getShift() const noexcept {
return upcast(this)->getShift();
return downcast(this)->getShift();
}
double Camera::getNear() const noexcept {
return upcast(this)->getNear();
return downcast(this)->getNear();
}
double Camera::getCullingFar() const noexcept {
return upcast(this)->getCullingFar();
return downcast(this)->getCullingFar();
}
void Camera::setModelMatrix(const mat4& modelMatrix) noexcept {
upcast(this)->setModelMatrix(modelMatrix);
downcast(this)->setModelMatrix(modelMatrix);
}
void Camera::setModelMatrix(const mat4f& modelMatrix) noexcept {
upcast(this)->setModelMatrix(modelMatrix);
downcast(this)->setModelMatrix(modelMatrix);
}
void Camera::lookAt(double3 const& eye, double3 const& center, double3 const& up) noexcept {
upcast(this)->lookAt(eye, center, up);
downcast(this)->lookAt(eye, center, up);
}
mat4 Camera::getModelMatrix() const noexcept {
return upcast(this)->getModelMatrix();
return downcast(this)->getModelMatrix();
}
mat4 Camera::getViewMatrix() const noexcept {
return upcast(this)->getViewMatrix();
return downcast(this)->getViewMatrix();
}
double3 Camera::getPosition() const noexcept {
return upcast(this)->getPosition();
return downcast(this)->getPosition();
}
float3 Camera::getLeftVector() const noexcept {
return upcast(this)->getLeftVector();
return downcast(this)->getLeftVector();
}
float3 Camera::getUpVector() const noexcept {
return upcast(this)->getUpVector();
return downcast(this)->getUpVector();
}
float3 Camera::getForwardVector() const noexcept {
return upcast(this)->getForwardVector();
return downcast(this)->getForwardVector();
}
float Camera::getFieldOfViewInDegrees(Camera::Fov direction) const noexcept {
return upcast(this)->getFieldOfViewInDegrees(direction);
return downcast(this)->getFieldOfViewInDegrees(direction);
}
Frustum Camera::getFrustum() const noexcept {
return upcast(this)->getCullingFrustum();
return downcast(this)->getCullingFrustum();
}
utils::Entity Camera::getEntity() const noexcept {
return upcast(this)->getEntity();
return downcast(this)->getEntity();
}
void Camera::setExposure(float aperture, float shutterSpeed, float ISO) noexcept {
upcast(this)->setExposure(aperture, shutterSpeed, ISO);
downcast(this)->setExposure(aperture, shutterSpeed, ISO);
}
float Camera::getAperture() const noexcept {
return upcast(this)->getAperture();
return downcast(this)->getAperture();
}
float Camera::getShutterSpeed() const noexcept {
return upcast(this)->getShutterSpeed();
return downcast(this)->getShutterSpeed();
}
float Camera::getSensitivity() const noexcept {
return upcast(this)->getSensitivity();
return downcast(this)->getSensitivity();
}
void Camera::setFocusDistance(float distance) noexcept {
upcast(this)->setFocusDistance(distance);
downcast(this)->setFocusDistance(distance);
}
float Camera::getFocusDistance() const noexcept {
return upcast(this)->getFocusDistance();
return downcast(this)->getFocusDistance();
}
double Camera::getFocalLength() const noexcept {
return upcast(this)->getFocalLength();
return downcast(this)->getFocalLength();
}
double Camera::computeEffectiveFocalLength(double focalLength, double focusDistance) noexcept {
+7 -6
View File
@@ -86,7 +86,10 @@ void Culler::intersects(
visible &= fast::signbit(dot) << bit;
}
results[i] |= result_type(visible);
auto r = results[i];
r &= ~result_type(1u << bit);
r |= result_type(visible);
results[i] = r;
}
}
@@ -101,22 +104,20 @@ bool Culler::intersects(Frustum const& frustum, Box const& box) noexcept {
Culler::result_type results[MODULO];
centers[0] = box.center;
extents[0] = box.halfExtent;
results[0] = 0;
Culler::intersects(results, frustum, centers, extents, MODULO, 0);
return bool(results[0]);
return bool(results[0] & 1);
}
/*
* returns whether an sphere intersects with the frustum
* returns whether a sphere intersects with the frustum
*/
bool Culler::intersects(Frustum const& frustum, float4 const& sphere) noexcept {
// The main intersection routine assumes multiples of 8 items
float4 spheres[MODULO];
Culler::result_type results[MODULO];
spheres[0] = sphere;
results[0] = 0;
Culler::intersects(results, frustum, spheres, MODULO);
return bool(results[0]);
return bool(results[0] & 1);
}
// For testing...
+1 -1
View File
@@ -42,7 +42,7 @@ void DFG::init(FEngine& engine) noexcept {
lut->setImage(engine, 0, std::move(buffer));
mLUT = upcast(lut);
mLUT = downcast(lut);
}
void DFG::terminate(FEngine& engine) noexcept {
+15 -15
View File
@@ -25,64 +25,64 @@ namespace filament {
using namespace math;
bool DebugRegistry::hasProperty(const char* name) const noexcept {
return upcast(this)->hasProperty(name);
return downcast(this)->hasProperty(name);
}
bool DebugRegistry::setProperty(const char* name, bool v) noexcept {
return upcast(this)->setProperty(name, v);
return downcast(this)->setProperty(name, v);
}
bool DebugRegistry::setProperty(const char* name, int v) noexcept {
return upcast(this)->setProperty(name, v);
return downcast(this)->setProperty(name, v);
}
bool DebugRegistry::setProperty(const char* name, float v) noexcept {
return upcast(this)->setProperty(name, v);
return downcast(this)->setProperty(name, v);
}
bool DebugRegistry::setProperty(const char* name, float2 v) noexcept {
return upcast(this)->setProperty(name, v);
return downcast(this)->setProperty(name, v);
}
bool DebugRegistry::setProperty(const char* name, float3 v) noexcept {
return upcast(this)->setProperty(name, v);
return downcast(this)->setProperty(name, v);
}
bool DebugRegistry::setProperty(const char* name, float4 v) noexcept {
return upcast(this)->setProperty(name, v);
return downcast(this)->setProperty(name, v);
}
bool DebugRegistry::getProperty(const char* name, bool* v) const noexcept {
return upcast(this)->getProperty(name, v);
return downcast(this)->getProperty(name, v);
}
bool DebugRegistry::getProperty(const char* name, int* v) const noexcept {
return upcast(this)->getProperty(name, v);
return downcast(this)->getProperty(name, v);
}
bool DebugRegistry::getProperty(const char* name, float* v) const noexcept {
return upcast(this)->getProperty(name, v);
return downcast(this)->getProperty(name, v);
}
bool DebugRegistry::getProperty(const char* name, float2* v) const noexcept {
return upcast(this)->getProperty(name, v);
return downcast(this)->getProperty(name, v);
}
bool DebugRegistry::getProperty(const char* name, float3* v) const noexcept {
return upcast(this)->getProperty(name, v);
return downcast(this)->getProperty(name, v);
}
bool DebugRegistry::getProperty(const char* name, float4* v) const noexcept {
return upcast(this)->getProperty(name, v);
return downcast(this)->getProperty(name, v);
}
void *DebugRegistry::getPropertyAddress(const char *name) noexcept {
return upcast(this)->getPropertyAddress(name);
return downcast(this)->getPropertyAddress(name);
}
DebugRegistry::DataSource DebugRegistry::getDataSource(const char* name) const noexcept {
return upcast(this)->getDataSource(name);
return downcast(this)->getDataSource(name);
}
+56 -50
View File
@@ -51,7 +51,7 @@ Engine* Engine::create(Backend backend, Platform* platform, void* sharedGLContex
}
void Engine::destroy(Engine* engine) {
FEngine::destroy(upcast(engine));
FEngine::destroy(downcast(engine));
}
#if UTILS_HAS_THREADING
@@ -68,7 +68,7 @@ Engine* Engine::getEngine(void* token) {
void Engine::destroy(Engine** pEngine) {
if (pEngine) {
Engine* engine = *pEngine;
FEngine::destroy(upcast(engine));
FEngine::destroy(downcast(engine));
*pEngine = nullptr;
}
}
@@ -78,151 +78,151 @@ void Engine::destroy(Engine** pEngine) {
// -----------------------------------------------------------------------------------------------
const Material* Engine::getDefaultMaterial() const noexcept {
return upcast(this)->getDefaultMaterial();
return downcast(this)->getDefaultMaterial();
}
Backend Engine::getBackend() const noexcept {
return upcast(this)->getBackend();
return downcast(this)->getBackend();
}
Platform* Engine::getPlatform() const noexcept {
return upcast(this)->getPlatform();
return downcast(this)->getPlatform();
}
Renderer* Engine::createRenderer() noexcept {
return upcast(this)->createRenderer();
return downcast(this)->createRenderer();
}
View* Engine::createView() noexcept {
return upcast(this)->createView();
return downcast(this)->createView();
}
Scene* Engine::createScene() noexcept {
return upcast(this)->createScene();
return downcast(this)->createScene();
}
Camera* Engine::createCamera(Entity entity) noexcept {
return upcast(this)->createCamera(entity);
return downcast(this)->createCamera(entity);
}
Camera* Engine::getCameraComponent(utils::Entity entity) noexcept {
return upcast(this)->getCameraComponent(entity);
return downcast(this)->getCameraComponent(entity);
}
void Engine::destroyCameraComponent(utils::Entity entity) noexcept {
upcast(this)->destroyCameraComponent(entity);
downcast(this)->destroyCameraComponent(entity);
}
Fence* Engine::createFence() noexcept {
return upcast(this)->createFence(FFence::Type::SOFT);
return downcast(this)->createFence(FFence::Type::SOFT);
}
SwapChain* Engine::createSwapChain(void* nativeWindow, uint64_t flags) noexcept {
return upcast(this)->createSwapChain(nativeWindow, flags);
return downcast(this)->createSwapChain(nativeWindow, flags);
}
SwapChain* Engine::createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept {
return upcast(this)->createSwapChain(width, height, flags);
return downcast(this)->createSwapChain(width, height, flags);
}
bool Engine::destroy(const BufferObject* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const VertexBuffer* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const IndexBuffer* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const SkinningBuffer* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const MorphTargetBuffer* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const IndirectLight* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const Material* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const MaterialInstance* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const Renderer* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const View* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const Scene* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const Skybox* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const ColorGrading* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const Stream* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const Texture* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const RenderTarget* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const Fence* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
bool Engine::destroy(const SwapChain* p) {
return upcast(this)->destroy(upcast(p));
return downcast(this)->destroy(downcast(p));
}
void Engine::destroy(Entity e) {
upcast(this)->destroy(e);
downcast(this)->destroy(e);
}
void Engine::flushAndWait() {
upcast(this)->flushAndWait();
downcast(this)->flushAndWait();
}
void Engine::flush() {
upcast(this)->flush();
downcast(this)->flush();
}
utils::EntityManager& Engine::getEntityManager() noexcept {
return upcast(this)->getEntityManager();
return downcast(this)->getEntityManager();
}
RenderableManager& Engine::getRenderableManager() noexcept {
return upcast(this)->getRenderableManager();
return downcast(this)->getRenderableManager();
}
LightManager& Engine::getLightManager() noexcept {
return upcast(this)->getLightManager();
return downcast(this)->getLightManager();
}
TransformManager& Engine::getTransformManager() noexcept {
return upcast(this)->getTransformManager();
return downcast(this)->getTransformManager();
}
void Engine::enableAccurateTranslations() noexcept {
@@ -230,47 +230,53 @@ void Engine::enableAccurateTranslations() noexcept {
}
void* Engine::streamAlloc(size_t size, size_t alignment) noexcept {
return upcast(this)->streamAlloc(size, alignment);
return downcast(this)->streamAlloc(size, alignment);
}
// The external-facing execute does a flush, and is meant only for single-threaded environments.
// It also discards the boolean return value, which would otherwise indicate a thread exit.
void Engine::execute() {
ASSERT_PRECONDITION(!UTILS_HAS_THREADING, "Execute is meant for single-threaded platforms.");
upcast(this)->flush();
upcast(this)->execute();
downcast(this)->flush();
downcast(this)->execute();
}
utils::JobSystem& Engine::getJobSystem() noexcept {
return upcast(this)->getJobSystem();
return downcast(this)->getJobSystem();
}
DebugRegistry& Engine::getDebugRegistry() noexcept {
return upcast(this)->getDebugRegistry();
return downcast(this)->getDebugRegistry();
}
void Engine::pumpMessageQueues() {
upcast(this)->pumpMessageQueues();
downcast(this)->pumpMessageQueues();
}
void Engine::setAutomaticInstancingEnabled(bool enable) noexcept {
upcast(this)->setAutomaticInstancingEnabled(enable);
downcast(this)->setAutomaticInstancingEnabled(enable);
}
bool Engine::isAutomaticInstancingEnabled() const noexcept {
return upcast(this)->isAutomaticInstancingEnabled();
return downcast(this)->isAutomaticInstancingEnabled();
}
FeatureLevel Engine::getSupportedFeatureLevel() const noexcept {
return upcast(this)->getSupportedFeatureLevel();
return downcast(this)->getSupportedFeatureLevel();
}
FeatureLevel Engine::setActiveFeatureLevel(FeatureLevel featureLevel) {
return upcast(this)->setActiveFeatureLevel(featureLevel);
return downcast(this)->setActiveFeatureLevel(featureLevel);
}
FeatureLevel Engine::getActiveFeatureLevel() const noexcept {
return upcast(this)->getActiveFeatureLevel();
return downcast(this)->getActiveFeatureLevel();
}
#if defined(__EMSCRIPTEN__)
void Engine::resetBackendState() noexcept {
downcast(this)->resetBackendState();
}
#endif
} // namespace filament
+4 -4
View File
@@ -24,7 +24,7 @@ namespace filament {
namespace Exposure {
float ev100(const Camera& c) noexcept {
const FCamera& camera = upcast(c);
const FCamera& camera = downcast(c);
return ev100(camera.getAperture(), camera.getShutterSpeed(), camera.getSensitivity());
}
@@ -79,7 +79,7 @@ float ev100FromIlluminance(float illuminance) noexcept {
}
float exposure(const Camera& c) noexcept {
const FCamera& camera = upcast(c);
const FCamera& camera = downcast(c);
return exposure(camera.getAperture(), camera.getShutterSpeed(), camera.getSensitivity());
}
@@ -136,7 +136,7 @@ float exposure(float ev100) noexcept {
}
float luminance(const Camera& c) noexcept {
const FCamera& camera = upcast(c);
const FCamera& camera = downcast(c);
return luminance(camera.getAperture(), camera.getShutterSpeed(), camera.getSensitivity());
}
@@ -168,7 +168,7 @@ float luminance(float ev100) noexcept {
}
float illuminance(const Camera& c) noexcept {
const FCamera& camera = upcast(c);
const FCamera& camera = downcast(c);
return illuminance(camera.getAperture(), camera.getShutterSpeed(), camera.getSensitivity());
}
+2 -2
View File
@@ -21,11 +21,11 @@ namespace filament {
using namespace backend;
FenceStatus Fence::waitAndDestroy(Fence* fence, Mode mode) {
return FFence::waitAndDestroy(upcast(fence), mode);
return FFence::waitAndDestroy(downcast(fence), mode);
}
FenceStatus Fence::wait(Mode mode, uint64_t timeout) {
return upcast(this)->wait(mode, timeout);
return downcast(this)->wait(mode, timeout);
}
} // namespace filament
+2 -2
View File
@@ -22,11 +22,11 @@ namespace filament {
void IndexBuffer::setBuffer(Engine& engine,
IndexBuffer::BufferDescriptor&& buffer, uint32_t byteOffset) {
upcast(this)->setBuffer(upcast(engine), std::move(buffer), byteOffset);
downcast(this)->setBuffer(downcast(engine), std::move(buffer), byteOffset);
}
size_t IndexBuffer::getIndexCount() const noexcept {
return upcast(this)->getIndexCount();
return downcast(this)->getIndexCount();
}
} // namespace filament
+8 -8
View File
@@ -23,35 +23,35 @@ namespace filament {
using namespace math;
void IndirectLight::setIntensity(float intensity) noexcept {
upcast(this)->setIntensity(intensity);
downcast(this)->setIntensity(intensity);
}
float IndirectLight::getIntensity() const noexcept {
return upcast(this)->getIntensity();
return downcast(this)->getIntensity();
}
void IndirectLight::setRotation(mat3f const& rotation) noexcept {
upcast(this)->setRotation(rotation);
downcast(this)->setRotation(rotation);
}
const mat3f& IndirectLight::getRotation() const noexcept {
return upcast(this)->getRotation();
return downcast(this)->getRotation();
}
Texture const* IndirectLight::getReflectionsTexture() const noexcept {
return upcast(this)->getReflectionsTexture();
return downcast(this)->getReflectionsTexture();
}
Texture const* IndirectLight::getIrradianceTexture() const noexcept {
return upcast(this)->getIrradianceTexture();
return downcast(this)->getIrradianceTexture();
}
float3 IndirectLight::getDirectionEstimate() const noexcept {
return upcast(this)->getDirectionEstimate();
return downcast(this)->getDirectionEstimate();
}
float4 IndirectLight::getColorEstimate(float3 direction) const noexcept {
return upcast(this)->getColorEstimate(direction);
return downcast(this)->getColorEstimate(direction);
}
float3 IndirectLight::getDirectionEstimate(const float3* sh) noexcept {
+32 -32
View File
@@ -23,132 +23,132 @@ namespace filament {
using namespace math;
size_t LightManager::getComponentCount() const noexcept {
return upcast(this)->getComponentCount();
return downcast(this)->getComponentCount();
}
utils::Entity const* LightManager::getEntities() const noexcept {
return upcast(this)->getEntities();
return downcast(this)->getEntities();
}
bool LightManager::hasComponent(Entity e) const noexcept {
return upcast(this)->hasComponent(e);
return downcast(this)->hasComponent(e);
}
LightManager::Instance LightManager::getInstance(Entity e) const noexcept {
return upcast(this)->getInstance(e);
return downcast(this)->getInstance(e);
}
void LightManager::destroy(Entity e) noexcept {
return upcast(this)->destroy(e);
return downcast(this)->destroy(e);
}
void LightManager::setLightChannel(Instance i, unsigned int channel, bool enable) noexcept {
upcast(this)->setLightChannel(i, channel, enable);
downcast(this)->setLightChannel(i, channel, enable);
}
bool LightManager::getLightChannel(LightManager::Instance i, unsigned int channel) const noexcept {
return upcast(this)->getLightChannel(i, channel);
return downcast(this)->getLightChannel(i, channel);
}
void LightManager::setPosition(Instance i, const float3& position) noexcept {
upcast(this)->setLocalPosition(i, position);
downcast(this)->setLocalPosition(i, position);
}
const float3& LightManager::getPosition(Instance i) const noexcept {
return upcast(this)->getLocalPosition(i);
return downcast(this)->getLocalPosition(i);
}
void LightManager::setDirection(Instance i, const float3& direction) noexcept {
upcast(this)->setLocalDirection(i, direction);
downcast(this)->setLocalDirection(i, direction);
}
const float3& LightManager::getDirection(Instance i) const noexcept {
return upcast(this)->getLocalDirection(i);
return downcast(this)->getLocalDirection(i);
}
void LightManager::setColor(Instance i, const LinearColor& color) noexcept {
upcast(this)->setColor(i, color);
downcast(this)->setColor(i, color);
}
const float3& LightManager::getColor(Instance i) const noexcept {
return upcast(this)->getColor(i);
return downcast(this)->getColor(i);
}
void LightManager::setIntensity(Instance i, float intensity) noexcept {
upcast(this)->setIntensity(i, intensity, FLightManager::IntensityUnit::LUMEN_LUX);
downcast(this)->setIntensity(i, intensity, FLightManager::IntensityUnit::LUMEN_LUX);
}
void LightManager::setIntensityCandela(Instance i, float intensity) noexcept {
upcast(this)->setIntensity(i, intensity, FLightManager::IntensityUnit::CANDELA);
downcast(this)->setIntensity(i, intensity, FLightManager::IntensityUnit::CANDELA);
}
float LightManager::getIntensity(Instance i) const noexcept {
return upcast(this)->getIntensity(i);
return downcast(this)->getIntensity(i);
}
void LightManager::setFalloff(Instance i, float radius) noexcept {
upcast(this)->setFalloff(i, radius);
downcast(this)->setFalloff(i, radius);
}
float LightManager::getFalloff(Instance i) const noexcept {
return upcast(this)->getFalloff(i);
return downcast(this)->getFalloff(i);
}
void LightManager::setSpotLightCone(Instance i, float inner, float outer) noexcept {
upcast(this)->setSpotLightCone(i, inner, outer);
downcast(this)->setSpotLightCone(i, inner, outer);
}
float LightManager::getSpotLightOuterCone(Instance i) const noexcept {
return upcast(this)->getSpotParams(i).outerClamped;
return downcast(this)->getSpotParams(i).outerClamped;
}
float LightManager::getSpotLightInnerCone(Instance i) const noexcept {
return upcast(this)->getSpotLightInnerCone(i);
return downcast(this)->getSpotLightInnerCone(i);
}
void LightManager::setSunAngularRadius(Instance i, float angularRadius) noexcept {
upcast(this)->setSunAngularRadius(i, angularRadius);
downcast(this)->setSunAngularRadius(i, angularRadius);
}
float LightManager::getSunAngularRadius(Instance i) const noexcept {
float radius = upcast(this)->getSunAngularRadius(i);
float radius = downcast(this)->getSunAngularRadius(i);
return radius * f::RAD_TO_DEG;
}
void LightManager::setSunHaloSize(Instance i, float haloSize) noexcept {
upcast(this)->setSunHaloSize(i, haloSize);
downcast(this)->setSunHaloSize(i, haloSize);
}
float LightManager::getSunHaloSize(Instance i) const noexcept {
return upcast(this)->getSunHaloSize(i);
return downcast(this)->getSunHaloSize(i);
}
void LightManager::setSunHaloFalloff(Instance i, float haloFalloff) noexcept {
upcast(this)->setSunHaloFalloff(i, haloFalloff);
downcast(this)->setSunHaloFalloff(i, haloFalloff);
}
float LightManager::getSunHaloFalloff(Instance i) const noexcept {
return upcast(this)->getSunHaloFalloff(i);
return downcast(this)->getSunHaloFalloff(i);
}
LightManager::Type LightManager::getType(LightManager::Instance i) const noexcept {
return upcast(this)->getType(i);
return downcast(this)->getType(i);
}
const LightManager::ShadowOptions& LightManager::getShadowOptions(Instance i) const noexcept {
return upcast(this)->getShadowOptions(i);
return downcast(this)->getShadowOptions(i);
}
void LightManager::setShadowOptions(Instance i, ShadowOptions const& options) noexcept {
upcast(this)->setShadowOptions(i, options);
downcast(this)->setShadowOptions(i, options);
}
bool LightManager::isShadowCaster(Instance i) const noexcept {
return upcast(this)->isShadowCaster(i);
return downcast(this)->isShadowCaster(i);
}
void LightManager::setShadowCaster(Instance i, bool castShadows) noexcept {
upcast(this)->setShadowCaster(i, castShadows);
downcast(this)->setShadowCaster(i, castShadows);
}
} // namespace filament
+28 -28
View File
@@ -21,115 +21,115 @@ namespace filament {
using namespace backend;
MaterialInstance* Material::createInstance(const char* name) const noexcept {
return upcast(this)->createInstance(name);
return downcast(this)->createInstance(name);
}
const char* Material::getName() const noexcept {
return upcast(this)->getName().c_str();
return downcast(this)->getName().c_str();
}
Shading Material::getShading() const noexcept {
return upcast(this)->getShading();
return downcast(this)->getShading();
}
Interpolation Material::getInterpolation() const noexcept {
return upcast(this)->getInterpolation();
return downcast(this)->getInterpolation();
}
BlendingMode Material::getBlendingMode() const noexcept {
return upcast(this)->getBlendingMode();
return downcast(this)->getBlendingMode();
}
VertexDomain Material::getVertexDomain() const noexcept {
return upcast(this)->getVertexDomain();
return downcast(this)->getVertexDomain();
}
MaterialDomain Material::getMaterialDomain() const noexcept {
return upcast(this)->getMaterialDomain();
return downcast(this)->getMaterialDomain();
}
CullingMode Material::getCullingMode() const noexcept {
return upcast(this)->getCullingMode();
return downcast(this)->getCullingMode();
}
TransparencyMode Material::getTransparencyMode() const noexcept {
return upcast(this)->getTransparencyMode();
return downcast(this)->getTransparencyMode();
}
bool Material::isColorWriteEnabled() const noexcept {
return upcast(this)->isColorWriteEnabled();
return downcast(this)->isColorWriteEnabled();
}
bool Material::isDepthWriteEnabled() const noexcept {
return upcast(this)->isDepthWriteEnabled();
return downcast(this)->isDepthWriteEnabled();
}
bool Material::isDepthCullingEnabled() const noexcept {
return upcast(this)->isDepthCullingEnabled();
return downcast(this)->isDepthCullingEnabled();
}
bool Material::isDoubleSided() const noexcept {
return upcast(this)->isDoubleSided();
return downcast(this)->isDoubleSided();
}
float Material::getMaskThreshold() const noexcept {
return upcast(this)->getMaskThreshold();
return downcast(this)->getMaskThreshold();
}
bool Material::hasShadowMultiplier() const noexcept {
return upcast(this)->hasShadowMultiplier();
return downcast(this)->hasShadowMultiplier();
}
bool Material::hasSpecularAntiAliasing() const noexcept {
return upcast(this)->hasSpecularAntiAliasing();
return downcast(this)->hasSpecularAntiAliasing();
}
float Material::getSpecularAntiAliasingVariance() const noexcept {
return upcast(this)->getSpecularAntiAliasingVariance();
return downcast(this)->getSpecularAntiAliasingVariance();
}
float Material::getSpecularAntiAliasingThreshold() const noexcept {
return upcast(this)->getSpecularAntiAliasingThreshold();
return downcast(this)->getSpecularAntiAliasingThreshold();
}
size_t Material::getParameterCount() const noexcept {
return upcast(this)->getParameterCount();
return downcast(this)->getParameterCount();
}
size_t Material::getParameters(ParameterInfo* parameters, size_t count) const noexcept {
return upcast(this)->getParameters(parameters, count);
return downcast(this)->getParameters(parameters, count);
}
AttributeBitset Material::getRequiredAttributes() const noexcept {
return upcast(this)->getRequiredAttributes();
return downcast(this)->getRequiredAttributes();
}
RefractionMode Material::getRefractionMode() const noexcept {
return upcast(this)->getRefractionMode();
return downcast(this)->getRefractionMode();
}
RefractionType Material::getRefractionType() const noexcept {
return upcast(this)->getRefractionType();
return downcast(this)->getRefractionType();
}
ReflectionMode Material::getReflectionMode() const noexcept {
return upcast(this)->getReflectionMode();
return downcast(this)->getReflectionMode();
}
bool Material::hasParameter(const char* name) const noexcept {
return upcast(this)->hasParameter(name);
return downcast(this)->hasParameter(name);
}
bool Material::isSampler(const char* name) const noexcept {
return upcast(this)->isSampler(name);
return downcast(this)->isSampler(name);
}
MaterialInstance* Material::getDefaultInstance() noexcept {
return upcast(this)->getDefaultInstance();
return downcast(this)->getDefaultInstance();
}
MaterialInstance const* Material::getDefaultInstance() const noexcept {
return upcast(this)->getDefaultInstance();
return downcast(this)->getDefaultInstance();
}
} // namespace filament
+68 -28
View File
@@ -79,7 +79,7 @@ inline void FMaterialInstance::setParameterImpl(std::string_view name,
template<typename T, typename>
void MaterialInstance::setParameter(const char* name, size_t nameLength, T const& value) {
upcast(this)->setParameterImpl({ name, nameLength }, value);
downcast(this)->setParameterImpl({ name, nameLength }, value);
}
template<>
@@ -126,7 +126,7 @@ template UTILS_PUBLIC void MaterialInstance::setParameter<mat4f> (const char*
template <typename T, typename>
void MaterialInstance::setParameter(const char* name, size_t nameLength, const T* value, size_t count) {
upcast(this)->setParameterImpl({ name, nameLength }, value, count);
downcast(this)->setParameterImpl({ name, nameLength }, value, count);
}
template<>
@@ -184,112 +184,152 @@ template UTILS_PUBLIC void MaterialInstance::setParameter<mat4f> (const char*
// ------------------------------------------------------------------------------------------------
Material const* MaterialInstance::getMaterial() const noexcept {
return upcast(this)->getMaterial();
return downcast(this)->getMaterial();
}
const char* MaterialInstance::getName() const noexcept {
return upcast(this)->getName();
return downcast(this)->getName();
}
void MaterialInstance::setParameter(const char* name, size_t nameLength, Texture const* texture,
TextureSampler const& sampler) {
return upcast(this)->setParameterImpl({ name, nameLength }, upcast(texture), sampler);
return downcast(this)->setParameterImpl({ name, nameLength }, downcast(texture), sampler);
}
void MaterialInstance::setParameter(
const char* name, size_t nameLength, RgbType type, float3 color) {
upcast(this)->setParameterImpl<float3>({ name, nameLength }, Color::toLinear(type, color));
downcast(this)->setParameterImpl<float3>({ name, nameLength }, Color::toLinear(type, color));
}
void MaterialInstance::setParameter(
const char* name, size_t nameLength, RgbaType type, float4 color) {
upcast(this)->setParameterImpl<float4>({ name, nameLength }, Color::toLinear(type, color));
downcast(this)->setParameterImpl<float4>({ name, nameLength }, Color::toLinear(type, color));
}
void MaterialInstance::setScissor(
uint32_t left, uint32_t bottom, uint32_t width, uint32_t height) noexcept {
upcast(this)->setScissor(left, bottom, width, height);
downcast(this)->setScissor(left, bottom, width, height);
}
void MaterialInstance::unsetScissor() noexcept {
upcast(this)->unsetScissor();
downcast(this)->unsetScissor();
}
void MaterialInstance::setPolygonOffset(float scale, float constant) noexcept {
upcast(this)->setPolygonOffset(scale, constant);
downcast(this)->setPolygonOffset(scale, constant);
}
void MaterialInstance::setMaskThreshold(float threshold) noexcept {
upcast(this)->setMaskThreshold(threshold);
downcast(this)->setMaskThreshold(threshold);
}
void MaterialInstance::setSpecularAntiAliasingVariance(float variance) noexcept {
upcast(this)->setSpecularAntiAliasingVariance(variance);
downcast(this)->setSpecularAntiAliasingVariance(variance);
}
void MaterialInstance::setSpecularAntiAliasingThreshold(float threshold) noexcept {
upcast(this)->setSpecularAntiAliasingThreshold(threshold);
downcast(this)->setSpecularAntiAliasingThreshold(threshold);
}
void MaterialInstance::setDoubleSided(bool doubleSided) noexcept {
upcast(this)->setDoubleSided(doubleSided);
downcast(this)->setDoubleSided(doubleSided);
}
void MaterialInstance::setTransparencyMode(TransparencyMode mode) noexcept {
upcast(this)->setTransparencyMode(mode);
downcast(this)->setTransparencyMode(mode);
}
void MaterialInstance::setCullingMode(CullingMode culling) noexcept {
upcast(this)->setCullingMode(culling);
downcast(this)->setCullingMode(culling);
}
void MaterialInstance::setColorWrite(bool enable) noexcept {
upcast(this)->setColorWrite(enable);
downcast(this)->setColorWrite(enable);
}
void MaterialInstance::setDepthWrite(bool enable) noexcept {
upcast(this)->setDepthWrite(enable);
downcast(this)->setDepthWrite(enable);
}
void MaterialInstance::setDepthCulling(bool enable) noexcept {
upcast(this)->setDepthCulling(enable);
downcast(this)->setDepthCulling(enable);
}
void MaterialInstance::setStencilWrite(bool enable) noexcept {
upcast(this)->setStencilWrite(enable);
downcast(this)->setStencilWrite(enable);
}
void MaterialInstance::setStencilCompareFunction(StencilCompareFunc func, StencilFace face) noexcept {
upcast(this)->setStencilCompareFunction(func, face);
downcast(this)->setStencilCompareFunction(func, face);
}
void MaterialInstance::setStencilOpStencilFail(StencilOperation op, StencilFace face) noexcept {
upcast(this)->setStencilOpStencilFail(op, face);
downcast(this)->setStencilOpStencilFail(op, face);
}
void MaterialInstance::setStencilOpDepthFail(StencilOperation op, StencilFace face) noexcept {
upcast(this)->setStencilOpDepthFail(op, face);
downcast(this)->setStencilOpDepthFail(op, face);
}
void MaterialInstance::setStencilOpDepthStencilPass(StencilOperation op, StencilFace face) noexcept {
upcast(this)->setStencilOpDepthStencilPass(op, face);
downcast(this)->setStencilOpDepthStencilPass(op, face);
}
void MaterialInstance::setStencilReferenceValue(uint8_t value, StencilFace face) noexcept {
upcast(this)->setStencilReferenceValue(value, face);
downcast(this)->setStencilReferenceValue(value, face);
}
void MaterialInstance::setStencilReadMask(uint8_t readMask, StencilFace face) noexcept {
upcast(this)->setStencilReadMask(readMask, face);
downcast(this)->setStencilReadMask(readMask, face);
}
void MaterialInstance::setStencilWriteMask(uint8_t writeMask, StencilFace face) noexcept {
upcast(this)->setStencilWriteMask(writeMask, face);
downcast(this)->setStencilWriteMask(writeMask, face);
}
MaterialInstance* MaterialInstance::duplicate(MaterialInstance const* other, const char* name) noexcept {
return FMaterialInstance::duplicate(upcast(other), name);
return FMaterialInstance::duplicate(downcast(other), name);
}
float MaterialInstance::getMaskThreshold() const noexcept {
return downcast(this)->getMaskThreshold();
}
float MaterialInstance::getSpecularAntiAliasingVariance() const noexcept {
return downcast(this)->getSpecularAntiAliasingVariance();
}
float MaterialInstance::getSpecularAntiAliasingThreshold() const noexcept {
return downcast(this)->getSpecularAntiAliasingThreshold();
}
bool MaterialInstance::isDoubleSided() const noexcept {
return downcast(this)->isDoubleSided();
}
TransparencyMode MaterialInstance::getTransparencyMode() const noexcept {
return downcast(this)->getTransparencyMode();
}
CullingMode MaterialInstance::getCullingMode() const noexcept {
return downcast(this)->getCullingMode();
}
bool MaterialInstance::isColorWriteEnabled() const noexcept {
return downcast(this)->isColorWriteEnabled();
}
bool MaterialInstance::isDepthWriteEnabled() const noexcept {
return downcast(this)->isDepthWriteEnabled();
}
bool MaterialInstance::isStencilWriteEnabled() const noexcept {
return downcast(this)->isStencilWriteEnabled();
}
bool MaterialInstance::isDepthCullingEnabled() const noexcept {
return downcast(this)->isDepthCullingEnabled();
}
} // namespace filament
+5 -5
View File
@@ -22,25 +22,25 @@ namespace filament {
void MorphTargetBuffer::setPositionsAt(Engine& engine, size_t targetIndex,
math::float3 const* positions, size_t count, size_t offset) {
upcast(this)->setPositionsAt(upcast(engine), targetIndex, positions, count, offset);
downcast(this)->setPositionsAt(downcast(engine), targetIndex, positions, count, offset);
}
void MorphTargetBuffer::setPositionsAt(Engine& engine, size_t targetIndex,
math::float4 const* positions, size_t count, size_t offset) {
upcast(this)->setPositionsAt(upcast(engine), targetIndex, positions, count, offset);
downcast(this)->setPositionsAt(downcast(engine), targetIndex, positions, count, offset);
}
void MorphTargetBuffer::setTangentsAt(Engine& engine, size_t targetIndex,
math::short4 const* tangents, size_t count, size_t offset) {
upcast(this)->setTangentsAt(upcast(engine), targetIndex, tangents, count, offset);
downcast(this)->setTangentsAt(downcast(engine), targetIndex, tangents, count, offset);
}
size_t MorphTargetBuffer::getVertexCount() const noexcept {
return upcast(this)->getVertexCount();
return downcast(this)->getVertexCount();
}
size_t MorphTargetBuffer::getCount() const noexcept {
return upcast(this)->getCount();
return downcast(this)->getCount();
}
} // namespace filament
+169
View File
@@ -0,0 +1,169 @@
/*
* Copyright (C) 2022 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 "PerShadowMapUniforms.h"
#include "ShadowMapManager.h"
#include "details/Camera.h"
#include "details/Engine.h"
#include <private/filament/EngineEnums.h>
namespace filament {
using namespace backend;
using namespace math;
PerShadowMapUniforms::PerShadowMapUniforms(FEngine& engine) noexcept {
DriverApi& driver = engine.getDriverApi();
mUniformBufferHandle = driver.createBufferObject(sizeof(PerViewUib),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
}
void PerShadowMapUniforms::terminate(DriverApi& driver) {
driver.destroyBufferObject(mUniformBufferHandle);
}
PerViewUib& PerShadowMapUniforms::edit(Transaction const& transaction) noexcept {
assert_invariant(transaction.uniforms);
return *transaction.uniforms;
}
void PerShadowMapUniforms::prepareCamera(Transaction const& transaction,
FEngine& engine, const CameraInfo& camera) noexcept {
mat4f const& viewFromWorld = camera.view;
mat4f const& worldFromView = camera.model;
mat4f const& clipFromView = camera.projection;
const mat4f viewFromClip{ inverse((mat4)camera.projection) };
const mat4f clipFromWorld{ highPrecisionMultiply(clipFromView, viewFromWorld) };
const mat4f worldFromClip{ highPrecisionMultiply(worldFromView, viewFromClip) };
auto& s = edit(transaction);
s.viewFromWorldMatrix = viewFromWorld; // view
s.worldFromViewMatrix = worldFromView; // model
s.clipFromViewMatrix = clipFromView; // projection
s.viewFromClipMatrix = viewFromClip; // 1/projection
s.clipFromWorldMatrix = clipFromWorld; // projection * view
s.worldFromClipMatrix = worldFromClip; // 1/(projection * view)
s.clipTransform = camera.clipTransfrom;
s.cameraPosition = float3{ camera.getPosition() };
s.worldOffset = camera.getWorldOffset();
s.cameraFar = camera.zf;
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn);
// with a clip-space of [-w, w] ==> z' = -z
// with a clip-space of [0, w] ==> z' = (w - z)/2
s.clipControl = engine.getDriverApi().getClipSpaceParams();
}
void PerShadowMapUniforms::prepareLodBias(Transaction const& transaction,
float bias) noexcept {
auto& s = edit(transaction);
s.lodBias = bias;
}
void PerShadowMapUniforms::prepareViewport(Transaction const& transaction,
const filament::Viewport& viewport,
uint32_t xoffset, uint32_t yoffset) noexcept {
const float w = float(viewport.width);
const float h = float(viewport.height);
auto& s = edit(transaction);
s.resolution = float4{ w, h, 1.0f / w, 1.0f / h };
s.origin = float2{ viewport.left, viewport.bottom };
s.offset = float2{ xoffset, yoffset };
}
void PerShadowMapUniforms::prepareTime(Transaction const& transaction,
FEngine& engine, math::float4 const& userTime) noexcept {
auto& s = edit(transaction);
const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1000000000;
const float fraction = float(double(oneSecondRemainder) / 1000000000.0);
s.time = fraction;
s.userTime = userTime;
}
void PerShadowMapUniforms::prepareDirectionalLight(Transaction const& transaction,
FEngine& engine,
float exposure,
float3 const& sceneSpaceDirection,
PerShadowMapUniforms::LightManagerInstance directionalLight) noexcept {
FLightManager& lcm = engine.getLightManager();
auto& s = edit(transaction);
const float3 l = -sceneSpaceDirection; // guaranteed normalized
if (directionalLight.isValid()) {
const float4 colorIntensity = {
lcm.getColor(directionalLight), lcm.getIntensity(directionalLight) * exposure };
s.lightDirection = l;
s.lightColorIntensity = colorIntensity;
s.lightChannels = lcm.getLightChannels(directionalLight);
const bool isSun = lcm.isSunLight(directionalLight);
// The last parameter must be < 0.0f for regular directional lights
float4 sun{ 0.0f, 0.0f, 0.0f, -1.0f };
if (UTILS_UNLIKELY(isSun && colorIntensity.w > 0.0f)) {
// currently we have only a single directional light, so it's probably likely that it's
// also the Sun. However, conceptually, most directional lights won't be sun lights.
float radius = lcm.getSunAngularRadius(directionalLight);
float haloSize = lcm.getSunHaloSize(directionalLight);
float haloFalloff = lcm.getSunHaloFalloff(directionalLight);
sun.x = std::cos(radius);
sun.y = std::sin(radius);
sun.z = 1.0f / (std::cos(radius * haloSize) - sun.x);
sun.w = haloFalloff;
}
s.sun = sun;
} else {
// Disable the sun if there's no directional light
s.sun = float4{ 0.0f, 0.0f, 0.0f, -1.0f };
}
}
void PerShadowMapUniforms::prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept {
auto& s = edit(transaction);
constexpr float low = 5.54f; // ~ std::log(std::numeric_limits<math::half>::max()) * 0.5f;
constexpr float high = 42.0f; // ~ std::log(std::numeric_limits<float>::max()) * 0.5f;
s.vsmExponent = highPrecision ? high : low;
}
PerShadowMapUniforms::Transaction PerShadowMapUniforms::open(backend::DriverApi& driver) noexcept {
Transaction transaction;
// TODO: use out-of-line buffer if too large
transaction.uniforms = (PerViewUib *)driver.allocate(sizeof(PerViewUib));
assert_invariant(transaction.uniforms);
return transaction;
}
void PerShadowMapUniforms::commit(Transaction& transaction,
backend::DriverApi& driver) noexcept {
driver.updateBufferObject(mUniformBufferHandle, {
transaction.uniforms, sizeof(PerViewUib) }, 0);
transaction.uniforms = nullptr;
}
void PerShadowMapUniforms::bind(backend::DriverApi& driver) noexcept {
driver.bindUniformBuffer(+UniformBindingPoints::PER_VIEW, mUniformBufferHandle);
}
} // namespace filament
+95
View File
@@ -0,0 +1,95 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_PERSHADOWMAPUNIFORMS_H
#define TNT_FILAMENT_PERSHADOWMAPUNIFORMS_H
#include <filament/Viewport.h>
#include <private/filament/UibStructs.h>
#include <private/backend/SamplerGroup.h>
#include "TypedUniformBuffer.h"
#include <backend/Handle.h>
#include <utils/EntityInstance.h>
#include <random>
namespace filament {
struct CameraInfo;
class FEngine;
class LightManager;
/*
* PerShadowMapUniforms manages the UBO needed to generate our shadow maps. Internally it just
* holds onto a `PerViewUniform` UBO handle, but doesn't keep any shadow copy of it, instead it
* writes the data directly into the commandstream, for this reason partial update of the data
* is not possible.
*/
class PerShadowMapUniforms {
using LightManagerInstance = utils::EntityInstance<LightManager>;
public:
class Transaction {
friend PerShadowMapUniforms;
PerViewUib* uniforms = nullptr;
Transaction() = default; // disallow creation by the caller
};
explicit PerShadowMapUniforms(FEngine& engine) noexcept;
void terminate(backend::DriverApi& driver);
static void prepareCamera(Transaction const& transaction,
FEngine& engine, const CameraInfo& camera) noexcept;
static void prepareLodBias(Transaction const& transaction,
float bias) noexcept;
static void prepareViewport(Transaction const& transaction,
const filament::Viewport& viewport, uint32_t xoffset, uint32_t yoffset) noexcept;
static void prepareTime(Transaction const& transaction,
FEngine& engine, math::float4 const& userTime) noexcept;
static void prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept;
static void prepareDirectionalLight(Transaction const& transaction,
FEngine& engine, float exposure,
math::float3 const& sceneSpaceDirection, LightManagerInstance instance) noexcept;
static Transaction open(backend::DriverApi& driver) noexcept;
// update local data into GPU UBO
void commit(Transaction& transaction, backend::DriverApi& driver) noexcept;
// bind this UBO
void bind(backend::DriverApi& driver) noexcept;
private:
static PerViewUib& edit(Transaction const& transaction) noexcept;
backend::Handle<backend::HwBufferObject> mUniformBufferHandle;
};
} // namespace filament
#endif //TNT_FILAMENT_PERSHADOWMAPUNIFORMS_H
+26 -28
View File
@@ -34,15 +34,13 @@
#include <math/mat4.h>
namespace filament {
using namespace backend;
using namespace math;
PerViewUniforms::PerViewUniforms(FEngine& engine) noexcept
: mEngine(engine),
mSamplers(PerViewSib::SAMPLER_COUNT) {
: mSamplers(PerViewSib::SAMPLER_COUNT) {
DriverApi& driver = engine.getDriverApi();
mSamplerGroupHandle = driver.createSamplerGroup(mSamplers.getSize());
@@ -50,10 +48,6 @@ PerViewUniforms::PerViewUniforms(FEngine& engine) noexcept
mUniformBufferHandle = driver.createBufferObject(mUniforms.getSize(),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
// with a clip-space of [-w, w] ==> z' = -z
// with a clip-space of [0, w] ==> z' = (w - z)/2
mClipControl = driver.getClipSpaceParams();
if (engine.getDFG().isValid()) {
TextureSampler sampler(TextureSampler::MagFilter::LINEAR);
mSamplers.setSampler(PerViewSib::IBL_DFG_LUT,
@@ -66,7 +60,7 @@ void PerViewUniforms::terminate(DriverApi& driver) {
driver.destroySamplerGroup(mSamplerGroupHandle);
}
void PerViewUniforms::prepareCamera(const CameraInfo& camera) noexcept {
void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) noexcept {
mat4f const& viewFromWorld = camera.view;
mat4f const& worldFromView = camera.model;
mat4f const& clipFromView = camera.projection;
@@ -88,17 +82,15 @@ void PerViewUniforms::prepareCamera(const CameraInfo& camera) noexcept {
s.cameraFar = camera.zf;
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn);
s.clipControl = mClipControl;
// with a clip-space of [-w, w] ==> z' = -z
// with a clip-space of [0, w] ==> z' = (w - z)/2
s.clipControl = engine.getDriverApi().getClipSpaceParams();
}
void PerViewUniforms::prepareUpscaler(math::float2 scale,
DynamicResolutionOptions const& options) noexcept {
void PerViewUniforms::prepareLodBias(float bias) noexcept {
auto& s = mUniforms.edit();
if (options.quality >= QualityLevel::HIGH) {
s.lodBias = std::log2(std::min(scale.x, scale.y));
} else {
s.lodBias = 0.0f;
}
s.lodBias = bias;
}
void PerViewUniforms::prepareExposure(float ev100) noexcept {
@@ -118,17 +110,19 @@ void PerViewUniforms::prepareViewport(const filament::Viewport& viewport,
s.offset = float2{ xoffset, yoffset };
}
void PerViewUniforms::prepareTime(math::float4 const& userTime) noexcept {
void PerViewUniforms::prepareTime(FEngine& engine, math::float4 const& userTime) noexcept {
auto& s = mUniforms.edit();
const uint64_t oneSecondRemainder = mEngine.getEngineTime().count() % 1000000000;
const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1000000000;
const float fraction = float(double(oneSecondRemainder) / 1000000000.0);
s.time = fraction;
s.userTime = userTime;
}
void PerViewUniforms::prepareTemporalNoise(TemporalAntiAliasingOptions const& options) noexcept {
void PerViewUniforms::prepareTemporalNoise(FEngine& engine,
TemporalAntiAliasingOptions const& options) noexcept {
std::uniform_real_distribution<float> uniformDistribution{ 0.0f, 1.0f };
auto& s = mUniforms.edit();
const float temporalNoise = mUniformDistribution(mEngine.getRandomEngine());
const float temporalNoise = uniformDistribution(engine.getRandomEngine());
s.temporalNoise = options.enabled ? temporalNoise : 0.0f;
}
@@ -217,11 +211,11 @@ void PerViewUniforms::prepareStructure(Handle<HwTexture> structure) noexcept {
mSamplers.setSampler(PerViewSib::STRUCTURE, { structure, {}});
}
void PerViewUniforms::prepareDirectionalLight(
void PerViewUniforms::prepareDirectionalLight(FEngine& engine,
float exposure,
float3 const& sceneSpaceDirection,
PerViewUniforms::LightManagerInstance directionalLight) noexcept {
FLightManager& lcm = mEngine.getLightManager();
FLightManager& lcm = engine.getLightManager();
auto& s = mUniforms.edit();
const float3 l = -sceneSpaceDirection; // guaranteed normalized
@@ -255,9 +249,8 @@ void PerViewUniforms::prepareDirectionalLight(
}
}
void PerViewUniforms::prepareAmbientLight(FIndirectLight const& ibl,
void PerViewUniforms::prepareAmbientLight(FEngine& engine, FIndirectLight const& ibl,
float intensity, float exposure) noexcept {
auto& engine = mEngine;
auto& s = mUniforms.edit();
// Set up uniforms and sampler for the IBL, guaranteed to be non-null at this point.
@@ -288,9 +281,11 @@ void PerViewUniforms::prepareDynamicLights(Froxelizer& froxelizer) noexcept {
s.lightFarAttenuationParams = 0.5f * float2{ 10.0f, 10.0f / (f * f) };
}
void PerViewUniforms::prepareShadowMapping() noexcept {
void PerViewUniforms::prepareShadowMapping(bool highPrecision) noexcept {
auto& s = mUniforms.edit();
s.vsmExponent = 5.54f; // fp16: max 5.54f, fp32: max 42.0
constexpr float low = 5.54f; // ~ std::log(std::numeric_limits<math::half>::max()) * 0.5f;
constexpr float high = 42.0f; // ~ std::log(std::numeric_limits<float>::max()) * 0.5f;
s.vsmExponent = highPrecision ? high : low;
}
void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms,
@@ -302,11 +297,14 @@ void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms,
uniforms.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance;
uniforms.directionalShadows = shadowMappingUniforms.directionalShadows;
uniforms.cascades = shadowMappingUniforms.cascades;
uniforms.cascades |= uint32_t(shadowMappingUniforms.elvsm) << 31u;
}
void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms,
VsmShadowOptions const& options) noexcept {
constexpr float low = 5.54f; // ~ std::log(std::numeric_limits<math::half>::max()) * 0.5f;
constexpr float high = 42.0f; // ~ std::log(std::numeric_limits<float>::max()) * 0.5f;
SamplerMinFilter filterMin = SamplerMinFilter::LINEAR;
if (options.anisotropy > 0 || options.mipmapping) {
filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR;
@@ -318,8 +316,8 @@ void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
.anisotropyLog2 = options.anisotropy,
}});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_VSM;
s.vsmExponent = 5.54f; // fp16: max 5.54f, fp32: max 42.0
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_EVSM;
s.vsmExponent = options.highPrecision ? high : low;
s.vsmDepthScale = options.minVarianceScale * 0.01f * s.vsmExponent;
s.vsmLightBleedReduction = options.lightBleedReduction;
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
+17 -14
View File
@@ -48,23 +48,28 @@ class FIndirectLight;
class Froxelizer;
class LightManager;
/*
* PerViewUniforms manages the UBO and samplers needed to render the color passes. Internally it
* holds onto handles for the PER_VIEW UBO and SamplerGroup. This class maintains a shadow copy
* of the UBO/sampler data, so it is possible to partially update it between commits.
*/
class PerViewUniforms {
using LightManagerInstance = utils::EntityInstance<LightManager>;
using TextureHandle = backend::Handle<backend::HwTexture>;
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCF = 0u;
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_VSM = 1u;
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_DPCF = 2u;
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCSS = 3u;
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCF = 0u;
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_EVSM = 1u;
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_DPCF = 2u;
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCSS = 3u;
public:
explicit PerViewUniforms(FEngine& engine) noexcept;
void terminate(backend::DriverApi& driver);
void prepareCamera(const CameraInfo& camera) noexcept;
void prepareUpscaler(math::float2 scale, DynamicResolutionOptions const& options) noexcept;
void prepareCamera(FEngine& engine, const CameraInfo& camera) noexcept;
void prepareLodBias(float bias) noexcept;
/*
* @param viewport viewport (should be same as RenderPassParams::viewport)
@@ -75,8 +80,8 @@ public:
*/
void prepareViewport(const filament::Viewport& viewport, uint32_t xoffset, uint32_t yoffset) noexcept;
void prepareTime(math::float4 const& userTime) noexcept;
void prepareTemporalNoise(TemporalAntiAliasingOptions const& options) noexcept;
void prepareTime(FEngine& engine, math::float4 const& userTime) noexcept;
void prepareTemporalNoise(FEngine& engine, TemporalAntiAliasingOptions const& options) noexcept;
void prepareExposure(float ev100) noexcept;
void prepareFog(math::float3 const& cameraPosition, FogOptions const& options) noexcept;
void prepareStructure(TextureHandle structure) noexcept;
@@ -93,12 +98,13 @@ public:
math::mat4f const& uvFromViewMatrix,
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
void prepareShadowMapping() noexcept;
void prepareShadowMapping(bool highPrecision) noexcept;
void prepareDirectionalLight(float exposure,
void prepareDirectionalLight(FEngine& engine, float exposure,
math::float3 const& sceneSpaceDirection, LightManagerInstance instance) noexcept;
void prepareAmbientLight(FIndirectLight const& ibl, float intensity, float exposure) noexcept;
void prepareAmbientLight(FEngine& engine,
FIndirectLight const& ibl, float intensity, float exposure) noexcept;
void prepareDynamicLights(Froxelizer& froxelizer) noexcept;
@@ -126,13 +132,10 @@ public:
void unbindSamplers() noexcept;
private:
FEngine& mEngine;
math::float2 mClipControl{};
TypedUniformBuffer<PerViewUib> mUniforms;
backend::SamplerGroup mSamplers;
backend::Handle<backend::HwBufferObject> mUniformBufferHandle;
backend::Handle<backend::HwSamplerGroup> mSamplerGroupHandle;
std::uniform_real_distribution<float> mUniformDistribution{ 0.0f, 1.0f };
static void prepareShadowSampling(PerViewUib& uniforms,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;
};
+19 -12
View File
@@ -127,7 +127,7 @@ void PostProcessManager::PostProcessMaterial::loadMaterial(FEngine& engine) cons
// TODO: After all materials using this class have been converted to the post-process material
// domain, load both OPAQUE and TRANSPARENT variants here.
mHasMaterial = true;
mMaterial = upcast(Material::Builder().package(mData, mSize).build(engine));
mMaterial = downcast(Material::Builder().package(mData, mSize).build(engine));
}
UTILS_NOINLINE
@@ -399,9 +399,9 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
auto out = resources.getRenderPassInfo();
renderPass.setRenderFlags(structureRenderFlags);
renderPass.setVariant(structureVariant);
renderPass.appendCommands(RenderPass::CommandTypeFlags::SSAO);
renderPass.sortCommands();
renderPass.execute(resources.getPassName(), out.target, out.params);
renderPass.appendCommands(mEngine, RenderPass::CommandTypeFlags::SSAO);
renderPass.sortCommands(mEngine);
renderPass.execute(mEngine, resources.getPassName(), out.target, out.params);
});
auto depth = structurePass->depth;
@@ -544,9 +544,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
// the SCREEN_SPACE ReflectionMode.
renderPass.setVariant(Variant{Variant::SPECIAL_SSR});
// generate all our drawing commands, except blended objects.
renderPass.appendCommands(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
renderPass.sortCommands();
renderPass.execute(resources.getPassName(), out.target, out.params);
renderPass.appendCommands(mEngine, RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
renderPass.sortCommands(mEngine);
renderPass.execute(mEngine, resources.getPassName(), out.target, out.params);
});
return ssrPass->reflections;
@@ -990,8 +990,15 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
float x1 = float(i * 2);
float k0 = std::exp(-alpha * x0 * x0);
float k1 = std::exp(-alpha * x1 * x1);
// k * textureLod(..., o) with bilinear sampling is equivalent to:
// k * (s[0] * (1 - o) + s[1] * o)
// solve:
// k0 = k * (1 - o)
// k1 = k * o
float k = k0 + k1;
float o = k0 / k;
float o = k1 / k;
kernel[i].x = k;
kernel[i].y = o;
totalWeight += (k0 + k1) * 2.0f;
@@ -1039,7 +1046,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
// don't use auto for those, b/c the can't resolve them
// don't use auto for those, b/c the ide can't resolve them
using FGTD = FrameGraphTexture::Descriptor;
using FGTSD = FrameGraphTexture::SubResourceDescriptor;
@@ -1055,7 +1062,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
using namespace std::literals;
std::string_view materialName;
const bool is2dArray = inDesc.type == SamplerType::SAMPLER_2D_ARRAY;
switch (backend::getFormatSize(outDesc.format)) {
switch (backend::getFormatComponentCount(outDesc.format)) {
case 1: materialName = is2dArray ?
"separableGaussianBlur1L"sv : "separableGaussianBlur1"sv; break;
case 2: materialName = is2dArray ?
@@ -2258,7 +2265,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
if (bloomOptions.enabled) {
bloomStrength = clamp(bloomOptions.strength, 0.0f, 1.0f);
if (bloomOptions.dirt) {
FTexture* fdirt = upcast(bloomOptions.dirt);
FTexture* fdirt = downcast(bloomOptions.dirt);
FrameGraphTexture frameGraphTexture{ .handle = fdirt->getHwHandle() };
bloomDirt = fg.import("dirt", {
.width = (uint32_t)fdirt->getWidth(0u),
@@ -2469,7 +2476,7 @@ void PostProcessManager::prepareTaa(FrameGraph& fg, filament::Viewport const& sv
fg.addTrivialSideEffectPass("Jitter Camera",
[=, &uniforms] (DriverApi& driver) {
uniforms.prepareCamera(*inoutCameraInfo);
uniforms.prepareCamera(mEngine, *inoutCameraInfo);
uniforms.commit(driver);
});
}
+75 -77
View File
@@ -39,7 +39,7 @@ using namespace backend;
RenderPass::RenderPass(FEngine& engine,
RenderPass::Arena& arena) noexcept
: mEngine(engine), mCommandArena(arena),
: mCommandArena(arena),
mCustomCommands(engine.getPerRenderPassAllocator()) {
}
@@ -79,26 +79,13 @@ void RenderPass::setCamera(const CameraInfo& camera) noexcept {
mCameraForwardVector = camera.getForwardVector();
}
void RenderPass::overridePolygonOffset(backend::PolygonOffset const* polygonOffset) noexcept {
if ((mPolygonOffsetOverride = (polygonOffset != nullptr))) {
mPolygonOffset = *polygonOffset;
}
}
void RenderPass::setScissorViewport(backend::Viewport viewport) noexcept {
assert_invariant(viewport.width <= std::numeric_limits<int32_t>::max());
assert_invariant(viewport.height <= std::numeric_limits<int32_t>::max());
mScissorViewport = viewport;
}
void RenderPass::overrideScissor(backend::Viewport const* scissor) noexcept {
if ((mScissorOverride = (scissor != nullptr))) {
mScissor = *scissor;
}
}
void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcept {
void RenderPass::appendCommands(FEngine& engine, CommandTypeFlags const commandTypeFlags) noexcept {
SYSTRACE_CONTEXT();
assert_invariant(mRenderableSoa);
@@ -110,7 +97,6 @@ void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcep
return;
}
FEngine& engine = mEngine;
JobSystem& js = engine.getJobSystem();
const RenderFlags renderFlags = mFlags;
const Variant variant = mVariant;
@@ -179,7 +165,7 @@ void RenderPass::appendCustomCommand(Pass pass, CustomCommand custom, uint32_t o
curr->key = cmd;
}
void RenderPass::sortCommands() noexcept {
void RenderPass::sortCommands(FEngine& engine) noexcept {
SYSTRACE_NAME("sort and trim commands");
std::sort(mCommandBegin, mCommandEnd);
@@ -192,12 +178,28 @@ void RenderPass::sortCommands() noexcept {
resize(uint32_t(last - mCommandBegin));
if (mEngine.isAutomaticInstancingEnabled()) {
instanceify();
if (engine.isAutomaticInstancingEnabled()) {
instanceify(engine);
}
}
void RenderPass::instanceify() noexcept {
void RenderPass::execute(FEngine& engine, const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams params) const noexcept {
DriverApi& driver = engine.getDriverApi();
// this is a good time to flush the CommandStream, because we're about to potentially
// output a lot of commands. This guarantees here that we have at least
// FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB bytes (1MiB by default).
engine.flush();
driver.beginRenderPass(renderTarget, params);
getExecutor().execute(engine, name);
driver.endRenderPass();
}
void RenderPass::instanceify(FEngine& engine) noexcept {
SYSTRACE_NAME("instanceify");
// instanceify works by scanning the **sorted** command stream, looking for repeat draw
@@ -284,7 +286,7 @@ void RenderPass::instanceify() noexcept {
#endif
// we have instanced primitives
DriverApi& driver = mEngine.getDriverApi();
DriverApi& driver = engine.getDriverApi();
// TODO: maybe use a pool? so we can reuse the buffer.
// create a ubo to hold the instanced primitive data
@@ -356,8 +358,8 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
cmdDraw.primitive.rasterState.inverseFrontFaces = inverseFrontFaces;
cmdDraw.primitive.rasterState.culling = mi->getCullingMode();
cmdDraw.primitive.rasterState.colorWrite = mi->getColorWrite();
cmdDraw.primitive.rasterState.depthWrite = mi->getDepthWrite();
cmdDraw.primitive.rasterState.colorWrite = mi->isColorWriteEnabled();
cmdDraw.primitive.rasterState.depthWrite = mi->isDepthWriteEnabled();
cmdDraw.primitive.rasterState.depthFunc = mi->getDepthFunc();
cmdDraw.primitive.mi = mi;
cmdDraw.primitive.materialVariant = variant;
@@ -378,12 +380,14 @@ void RenderPass::generateCommands(uint32_t commandTypeFlags, Command* const comm
// the list twice)
// compute how much maximum storage we need
uint32_t offset = FScene::getPrimitiveCount(soa, range.first);
// double the color pass for transparent objects that need to render twice
const bool colorPass = bool(commandTypeFlags & CommandTypeFlags::COLOR);
const bool depthPass = bool(commandTypeFlags & CommandTypeFlags::DEPTH);
offset *= uint32_t(colorPass * 2 + depthPass);
Command* const curr = commands + offset;
const size_t commandsPerPrimitive = uint32_t(colorPass * 2 + depthPass);
const size_t offsetBegin = FScene::getPrimitiveCount(soa, range.first) * commandsPerPrimitive;
const size_t offsetEnd = FScene::getPrimitiveCount(soa, range.last) * commandsPerPrimitive;
Command* curr = commands + offsetBegin;
Command* const last = commands + offsetEnd;
/*
* The switch {} below is to coerce the compiler into generating different versions of
@@ -396,23 +400,31 @@ void RenderPass::generateCommands(uint32_t commandTypeFlags, Command* const comm
switch (commandTypeFlags & (CommandTypeFlags::COLOR | CommandTypeFlags::DEPTH)) {
case CommandTypeFlags::COLOR:
generateCommandsImpl<CommandTypeFlags::COLOR>(commandTypeFlags, curr,
curr = generateCommandsImpl<CommandTypeFlags::COLOR>(commandTypeFlags, curr,
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward);
break;
case CommandTypeFlags::DEPTH:
generateCommandsImpl<CommandTypeFlags::DEPTH>(commandTypeFlags, curr,
curr = generateCommandsImpl<CommandTypeFlags::DEPTH>(commandTypeFlags, curr,
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward);
break;
default:
// we should never end-up here
break;
}
assert_invariant(curr <= last);
// commands may have been skipped, cancel all of them.
while (curr != last) {
curr->key = uint64_t(Pass::SENTINEL);
++curr;
}
}
/* static */
template<uint32_t commandTypeFlags>
UTILS_NOINLINE
void RenderPass::generateCommandsImpl(uint32_t extraFlags,
RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
Command* UTILS_RESTRICT curr,
FScene::RenderableSoa const& UTILS_RESTRICT soa, Range<uint32_t> range,
Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
@@ -458,17 +470,8 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
const float cameraPositionDotCameraForward = dot(cameraPosition, cameraForward);
for (uint32_t i = range.first; i < range.last; ++i) {
// Check if this renderable passes the visibilityMask. If it doesn't, encode SENTINEL
// commands (no-op).
// Check if this renderable passes the visibilityMask.
if (UTILS_UNLIKELY(!(soaVisibilityMask[i] & visibilityMask))) {
// We need to encode a SENTINEL for each command that would have been generated
// otherwise. Color passes get 2 commands per primitive; depth passes get 1.
const Slice<FRenderPrimitive>& primitives = soaPrimitives[i];
const size_t commandsToEncode = (isColorPass * 2 + isDepthPass) * primitives.size();
for (size_t j = 0; j < commandsToEncode; j++) {
curr->key = uint64_t(Pass::SENTINEL);
++curr;
}
continue;
}
@@ -597,15 +600,15 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
// draw this command AFTER THE NEXT ONE
key |= makeField(1, BLEND_TWO_PASS_MASK, BLEND_TWO_PASS_SHIFT);
// handle the case where this primitive is empty / no-op
key |= select(primitive.getPrimitiveType() == PrimitiveType::NONE);
// correct for TransparencyMode::DEFAULT -- i.e. cancel the command
key |= select(mode == TransparencyMode::DEFAULT);
// cancel command if asked to filter translucent objects
key |= select(filterTranslucentObjects);
// cancel command if both front and back faces are culled
key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
*curr = cmdColor;
curr->key = key;
++curr;
@@ -629,14 +632,13 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
// bucketizes the depth by its log2 and in 4 linear chunks in each bucket.
cmdColor.key &= ~Z_BUCKET_MASK;
cmdColor.key |= makeField(distanceBits >> 22u, Z_BUCKET_MASK, Z_BUCKET_SHIFT);
curr->key = uint64_t(Pass::SENTINEL);
++curr;
}
*curr = cmdColor;
// handle the case where this primitive is empty / no-op
curr->key |= select(primitive.getPrimitiveType() == PrimitiveType::NONE);
// cancel command if both front and back faces are culled
curr->key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
++curr;
}
@@ -661,18 +663,21 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
// FIXME: should writeDepthForShadowCasters take precedence over mi->getDepthWrite()?
cmdDepth.primitive.rasterState.depthWrite = (1 // only keep bit 0
& (mi->getDepthWrite() | (mode == TransparencyMode::TWO_PASSES_ONE_SIDE))
& (mi->isDepthWriteEnabled() | (mode == TransparencyMode::TWO_PASSES_ONE_SIDE))
& !(filterTranslucentObjects & translucent)
& !(depthFilterAlphaMaskedObjects & rs.alphaToCoverage))
| writeDepthForShadowCasters;
*curr = cmdDepth;
// handle the case where this primitive is empty / no-op
curr->key |= select(primitive.getPrimitiveType() == PrimitiveType::NONE);
// cancel command if both front and back faces are culled
curr->key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
++curr;
}
}
}
return curr;
}
void RenderPass::updateSummedPrimitiveCounts(
@@ -690,25 +695,25 @@ void RenderPass::updateSummedPrimitiveCounts(
// ------------------------------------------------------------------------------------------------
void RenderPass::Executor::execute(const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams const& params) const noexcept {
FEngine& engine = mEngine;
DriverApi& driver = engine.getDriverApi();
void RenderPass::Executor::overridePolygonOffset(backend::PolygonOffset const* polygonOffset) noexcept {
if ((mPolygonOffsetOverride = (polygonOffset != nullptr))) {
mPolygonOffset = *polygonOffset;
}
}
// this is a good time to flush the CommandStream, because we're about to potentially
// output a lot of commands. This guarantees here that we have at least
// FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB bytes (1MiB by default).
engine.flush();
void RenderPass::Executor::overrideScissor(backend::Viewport const* scissor) noexcept {
if ((mScissorOverride = (scissor != nullptr))) {
mScissor = *scissor;
}
}
driver.beginRenderPass(renderTarget, params);
recordDriverCommands(engine, driver, mBegin, mEnd, params.readOnlyDepthStencil);
driver.endRenderPass();
void RenderPass::Executor::execute(FEngine& engine, const char* name) const noexcept {
execute(engine.getDriverApi(), mCommands.begin(), mCommands.end());
}
UTILS_NOINLINE // no need to be inlined
void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::DriverApi& driver,
const Command* first, const Command* last, uint16_t readOnlyDepthStencil) const noexcept {
void RenderPass::Executor::execute(backend::DriverApi& driver,
const Command* first, const Command* last) const noexcept {
SYSTRACE_CALL();
if (first != last) {
@@ -728,7 +733,7 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::Driver
Handle<HwBufferObject> uboHandle = mUboHandle;
FMaterialInstance const* UTILS_RESTRICT mi = nullptr;
FMaterial const* UTILS_RESTRICT ma = nullptr;
auto customCommands = mCustomCommands.data();
auto const* UTILS_RESTRICT pCustomCommands = mCustomCommands.data();
first--;
while (++first != last) {
@@ -741,7 +746,7 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::Driver
if (UTILS_UNLIKELY((first->key & CUSTOM_MASK) != uint64_t(CustomCommand::PASS))) {
uint32_t index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
assert_invariant(index < mCustomCommands.size());
customCommands[index]();
pCustomCommands[index]();
continue;
}
@@ -749,11 +754,6 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::Driver
const PrimitiveInfo info = first->primitive;
pipeline.rasterState = info.rasterState;
#ifndef NDEBUG
const bool readOnlyDepthGuaranteed = readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH;
assert_invariant(!readOnlyDepthGuaranteed || !pipeline.rasterState.depthWrite);
#endif
if (UTILS_UNLIKELY(mi != info.mi)) {
// this is always taken the first time
mi = info.mi;
@@ -835,15 +835,13 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::Driver
// ------------------------------------------------------------------------------------------------
RenderPass::Executor::Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept
: mEngine(pass->mEngine), mBegin(b), mEnd(e),
mCustomCommands(pass->mCustomCommands),
: mCommands(b, e),
mCustomCommands(pass->mCustomCommands.data(), pass->mCustomCommands.size()),
mUboHandle(pass->mUboHandle),
mInstancedUboHandle(pass->mInstancedUboHandle),
mPolygonOffset(pass->mPolygonOffset),
mScissor(pass->mScissor),
mScissorViewport(pass->mScissorViewport),
mPolygonOffsetOverride(pass->mPolygonOffsetOverride),
mScissorOverride(pass->mScissorOverride) {
mPolygonOffsetOverride(false),
mScissorOverride(false) {
assert_invariant(b >= pass->begin());
assert_invariant(e <= pass->end());
}
+42 -48
View File
@@ -276,15 +276,9 @@ public:
// allocated commands ARE NOT freed, they're owned by the Arena
~RenderPass() noexcept;
// if non-null, overrides the material's polygon offset
void overridePolygonOffset(backend::PolygonOffset const* polygonOffset) noexcept;
// a box that both offsets the viewport and clips it
void setScissorViewport(backend::Viewport viewport) noexcept;
// if non-null, overrides the material's scissor
void overrideScissor(backend::Viewport const* scissor) noexcept;
// specifies the geometry to generate commands for
void setGeometry(FScene::RenderableSoa const& soa, utils::Range<uint32_t> vr,
backend::Handle<backend::HwBufferObject> uboHandle) noexcept;
@@ -310,58 +304,69 @@ public:
// This is the main function of this class, this appends commands to the pass using
// the current camera, geometry and flags set. This can be called multiple times if needed.
void appendCommands(CommandTypeFlags commandTypeFlags) noexcept;
void appendCommands(FEngine& engine, CommandTypeFlags commandTypeFlags) noexcept;
// sorts and instancify commands then trims sentinels
void sortCommands() noexcept;
// sorts and instanceify commands then trims sentinels
void sortCommands(FEngine& engine) noexcept;
// Helper to execute all the commands generated by this RenderPass
void execute(const char* name,
void execute(FEngine& engine, const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams params) const noexcept {
getExecutor().execute(name, renderTarget, params);
}
backend::RenderPassParams params) const noexcept;
/*
* Executor holds the range of commands to execute for a given pass
*/
class Executor {
using CustomCommandFn = std::function<void()>;
using CustomCommandVector = std::vector<CustomCommandFn,
utils::STLAllocator<CustomCommandFn, LinearAllocatorArena>>;
friend class RenderPass;
FEngine& mEngine;
Command const* mBegin;
Command const* mEnd;
const CustomCommandVector mCustomCommands;
const backend::Handle<backend::HwBufferObject> mUboHandle;
const backend::Handle<backend::HwBufferObject> mInstancedUboHandle;
const backend::PolygonOffset mPolygonOffset;
const backend::Viewport mScissor;
const backend::Viewport mScissorViewport;
const bool mPolygonOffsetOverride : 1;
const bool mScissorOverride : 1;
// these fields are constant after creation
utils::Slice<Command> mCommands;
utils::Slice<CustomCommandFn> mCustomCommands;
backend::Handle<backend::HwBufferObject> mUboHandle;
backend::Handle<backend::HwBufferObject> mInstancedUboHandle;
backend::Viewport mScissorViewport;
backend::Viewport mScissor{}; // value of scissor override
backend::PolygonOffset mPolygonOffset{}; // value of the override
bool mPolygonOffsetOverride : 1; // whether to override the polygon offset setting
bool mScissorOverride : 1; // whether to override the polygon offset setting
Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept;
void recordDriverCommands(FEngine& engine, backend::DriverApi& driver,
const Command* first, const Command* last, uint16_t readOnlyDepthStencil) const noexcept;
void execute(backend::DriverApi& driver,
const Command* first, const Command* last) const noexcept;
public:
Executor() = default;
Executor(Executor const& rhs);
Executor& operator=(Executor const& rhs) = default;
~Executor() noexcept;
void execute(const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams const& params) const noexcept;
// if non-null, overrides the material's polygon offset
void overridePolygonOffset(backend::PolygonOffset const* polygonOffset) noexcept;
// if non-null, overrides the material's scissor
void overrideScissor(backend::Viewport const* scissor) noexcept;
void execute(FEngine& engine, const char* name) const noexcept;
};
// returns a new executor for this pass
Executor getExecutor() {
return { this, mCommandBegin, mCommandEnd };
}
Executor getExecutor() const {
return { this, mCommandBegin, mCommandEnd };
}
// returns a new executor for this pass with a custom range
Executor getExecutor(Command const* b, Command const* e) {
return { this, b, e };
}
Executor getExecutor(Command const* b, Command const* e) const {
return { this, b, e };
}
@@ -376,7 +381,7 @@ private:
Command* append(size_t count) noexcept;
void resize(size_t count) noexcept;
void instanceify() noexcept;
void instanceify(FEngine& engine) noexcept;
// on 64-bits systems, we process batches of 256 (64 bytes) cache-lines, or 512 (32 bytes) commands
// on 32-bits systems, we process batches of 512 (32 bytes) cache-lines, or 512 (32 bytes) commands
@@ -394,7 +399,7 @@ private:
math::float3 cameraPosition, math::float3 cameraForward) noexcept;
template<uint32_t commandTypeFlags>
static inline void generateCommandsImpl(uint32_t, Command* commands,
static inline Command* generateCommandsImpl(uint32_t extraFlags, Command* curr,
FScene::RenderableSoa const& soa, utils::Range<uint32_t> range,
Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
math::float3 cameraPosition, math::float3 cameraForward) noexcept;
@@ -406,7 +411,6 @@ private:
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
// a reference to the Engine, mostly to get to things like JobSystem
FEngine& mEngine;
// Arena where all Commands are allocated. The Arena owns the commands.
Arena& mCommandArena;
@@ -440,24 +444,14 @@ private:
// Additional visibility mask
FScene::VisibleMaskType mVisibilityMask = std::numeric_limits<FScene::VisibleMaskType>::max();
// whether to override the polygon offset setting
bool mPolygonOffsetOverride = false;
// whether to override the polygon offset setting
bool mScissorOverride = false;
// value of the override
backend::PolygonOffset mPolygonOffset{};
// value of scissor override
backend::Viewport mScissor{};
backend::Viewport mScissorViewport{ 0, 0,
std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max() };
// a vector for our custom commands
mutable Executor::CustomCommandVector mCustomCommands;
using CustomCommandVector = std::vector<Executor::CustomCommandFn,
utils::STLAllocator<Executor::CustomCommandFn, LinearAllocatorArena>>;
mutable CustomCommandVector mCustomCommands;
};
} // namespace filament
+3 -3
View File
@@ -30,12 +30,12 @@ void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverAp
assert_invariant(entry.materialInstance);
mMaterialInstance = upcast(entry.materialInstance);
mMaterialInstance = downcast(entry.materialInstance);
mBlendOrder = entry.blendOrder;
if (entry.indices && entry.vertices) {
FVertexBuffer* vertexBuffer = upcast(entry.vertices);
FIndexBuffer* indexBuffer = upcast(entry.indices);
FVertexBuffer* vertexBuffer = downcast(entry.vertices);
FIndexBuffer* indexBuffer = downcast(entry.indices);
AttributeBitset enabledAttributes = vertexBuffer->getDeclaredAttributes();
+1 -1
View File
@@ -71,7 +71,7 @@ private:
AttributeBitset mEnabledAttributes = {};
uint16_t mBlendOrder = 0;
bool mGlobalBlendOrderEnabled = false;
backend::PrimitiveType mPrimitiveType = backend::PrimitiveType::NONE;
backend::PrimitiveType mPrimitiveType = backend::PrimitiveType::TRIANGLES;
UTILS_UNUSED uint8_t reserved[4] = {};
};
+5 -5
View File
@@ -23,23 +23,23 @@ namespace filament {
using namespace backend;
Texture* RenderTarget::getTexture(AttachmentPoint attachment) const noexcept {
return upcast(this)->getAttachment(attachment).texture;
return downcast(this)->getAttachment(attachment).texture;
}
uint8_t RenderTarget::getMipLevel(AttachmentPoint attachment) const noexcept {
return upcast(this)->getAttachment(attachment).mipLevel;
return downcast(this)->getAttachment(attachment).mipLevel;
}
RenderTarget::CubemapFace RenderTarget::getFace(AttachmentPoint attachment) const noexcept {
return upcast(this)->getAttachment(attachment).face;
return downcast(this)->getAttachment(attachment).face;
}
uint32_t RenderTarget::getLayer(AttachmentPoint attachment) const noexcept {
return upcast(this)->getAttachment(attachment).layer;
return downcast(this)->getAttachment(attachment).layer;
}
uint8_t RenderTarget::getSupportedColorAttachmentsCount() const noexcept {
return upcast(this)->getSupportedColorAttachmentsCount();
return downcast(this)->getSupportedColorAttachmentsCount();
}
} // namespace filament
+32 -32
View File
@@ -28,137 +28,137 @@ using namespace backend;
using namespace math;
bool RenderableManager::hasComponent(utils::Entity e) const noexcept {
return upcast(this)->hasComponent(e);
return downcast(this)->hasComponent(e);
}
RenderableManager::Instance
RenderableManager::getInstance(utils::Entity e) const noexcept {
return upcast(this)->getInstance(e);
return downcast(this)->getInstance(e);
}
void RenderableManager::destroy(utils::Entity e) noexcept {
return upcast(this)->destroy(e);
return downcast(this)->destroy(e);
}
void RenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept {
upcast(this)->setAxisAlignedBoundingBox(instance, aabb);
downcast(this)->setAxisAlignedBoundingBox(instance, aabb);
}
void RenderableManager::setLayerMask(Instance instance, uint8_t select, uint8_t values) noexcept {
upcast(this)->setLayerMask(instance, select, values);
downcast(this)->setLayerMask(instance, select, values);
}
void RenderableManager::setPriority(Instance instance, uint8_t priority) noexcept {
upcast(this)->setPriority(instance, priority);
downcast(this)->setPriority(instance, priority);
}
void RenderableManager::setCulling(Instance instance, bool enable) noexcept {
upcast(this)->setCulling(instance, enable);
downcast(this)->setCulling(instance, enable);
}
void RenderableManager::setCastShadows(Instance instance, bool enable) noexcept {
upcast(this)->setCastShadows(instance, enable);
downcast(this)->setCastShadows(instance, enable);
}
void RenderableManager::setReceiveShadows(Instance instance, bool enable) noexcept {
upcast(this)->setReceiveShadows(instance, enable);
downcast(this)->setReceiveShadows(instance, enable);
}
void RenderableManager::setScreenSpaceContactShadows(Instance instance, bool enable) noexcept {
upcast(this)->setScreenSpaceContactShadows(instance, enable);
downcast(this)->setScreenSpaceContactShadows(instance, enable);
}
bool RenderableManager::isShadowCaster(Instance instance) const noexcept {
return upcast(this)->isShadowCaster(instance);
return downcast(this)->isShadowCaster(instance);
}
bool RenderableManager::isShadowReceiver(Instance instance) const noexcept {
return upcast(this)->isShadowReceiver(instance);
return downcast(this)->isShadowReceiver(instance);
}
const Box& RenderableManager::getAxisAlignedBoundingBox(Instance instance) const noexcept {
return upcast(this)->getAxisAlignedBoundingBox(instance);
return downcast(this)->getAxisAlignedBoundingBox(instance);
}
uint8_t RenderableManager::getLayerMask(Instance instance) const noexcept {
return upcast(this)->getLayerMask(instance);
return downcast(this)->getLayerMask(instance);
}
size_t RenderableManager::getPrimitiveCount(Instance instance) const noexcept {
return upcast(this)->getPrimitiveCount(instance, 0);
return downcast(this)->getPrimitiveCount(instance, 0);
}
void RenderableManager::setMaterialInstanceAt(Instance instance,
size_t primitiveIndex, MaterialInstance const* materialInstance) {
upcast(this)->setMaterialInstanceAt(instance, 0, primitiveIndex, upcast(materialInstance));
downcast(this)->setMaterialInstanceAt(instance, 0, primitiveIndex, downcast(materialInstance));
}
MaterialInstance* RenderableManager::getMaterialInstanceAt(
Instance instance, size_t primitiveIndex) const noexcept {
return upcast(this)->getMaterialInstanceAt(instance, 0, primitiveIndex);
return downcast(this)->getMaterialInstanceAt(instance, 0, primitiveIndex);
}
void RenderableManager::setBlendOrderAt(Instance instance, size_t primitiveIndex, uint16_t order) noexcept {
upcast(this)->setBlendOrderAt(instance, 0, primitiveIndex, order);
downcast(this)->setBlendOrderAt(instance, 0, primitiveIndex, order);
}
void RenderableManager::setGlobalBlendOrderEnabledAt(RenderableManager::Instance instance,
size_t primitiveIndex, bool enabled) noexcept {
upcast(this)->setGlobalBlendOrderEnabledAt(instance, 0, primitiveIndex, enabled);
downcast(this)->setGlobalBlendOrderEnabledAt(instance, 0, primitiveIndex, enabled);
}
AttributeBitset RenderableManager::getEnabledAttributesAt(Instance instance, size_t primitiveIndex) const noexcept {
return upcast(this)->getEnabledAttributesAt(instance, 0, primitiveIndex);
return downcast(this)->getEnabledAttributesAt(instance, 0, primitiveIndex);
}
void RenderableManager::setGeometryAt(Instance instance, size_t primitiveIndex,
PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices,
size_t offset, size_t count) noexcept {
upcast(this)->setGeometryAt(instance, 0, primitiveIndex,
type, upcast(vertices), upcast(indices), offset, count);
downcast(this)->setGeometryAt(instance, 0, primitiveIndex,
type, downcast(vertices), downcast(indices), offset, count);
}
void RenderableManager::setBones(Instance instance,
RenderableManager::Bone const* transforms, size_t boneCount, size_t offset) {
upcast(this)->setBones(instance, transforms, boneCount, offset);
downcast(this)->setBones(instance, transforms, boneCount, offset);
}
void RenderableManager::setBones(Instance instance,
mat4f const* transforms, size_t boneCount, size_t offset) {
upcast(this)->setBones(instance, transforms, boneCount, offset);
downcast(this)->setBones(instance, transforms, boneCount, offset);
}
void RenderableManager::setSkinningBuffer(Instance instance,
SkinningBuffer* skinningBuffer, size_t count, size_t offset) {
upcast(this)->setSkinningBuffer(instance, upcast(skinningBuffer), count, offset);
downcast(this)->setSkinningBuffer(instance, downcast(skinningBuffer), count, offset);
}
void RenderableManager::setMorphWeights(Instance instance, float const* weights,
size_t count, size_t offset) {
upcast(this)->setMorphWeights(instance, weights, count, offset);
downcast(this)->setMorphWeights(instance, weights, count, offset);
}
void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) {
upcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex,
upcast(morphTargetBuffer), offset, count);
downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex,
downcast(morphTargetBuffer), offset, count);
}
MorphTargetBuffer* RenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex) const noexcept {
return upcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex);
return downcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex);
}
size_t RenderableManager::getMorphTargetCount(Instance instance) const noexcept {
return upcast(this)->getMorphTargetCount(instance);
return downcast(this)->getMorphTargetCount(instance);
}
void RenderableManager::setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept {
upcast(this)->setLightChannel(instance, channel, enable);
downcast(this)->setLightChannel(instance, channel, enable);
}
bool RenderableManager::getLightChannel(Instance instance, unsigned int channel) const noexcept {
return upcast(this)->getLightChannel(instance, channel);
return downcast(this)->getLightChannel(instance, channel);
}
} // namespace filament
+14 -14
View File
@@ -25,64 +25,64 @@ using namespace math;
using namespace backend;
Engine* Renderer::getEngine() noexcept {
return &upcast(this)->getEngine();
return &downcast(this)->getEngine();
}
void Renderer::render(View const* view) {
upcast(this)->render(upcast(view));
downcast(this)->render(downcast(view));
}
void Renderer::setPresentationTime(int64_t monotonic_clock_ns) {
upcast(this)->setPresentationTime(monotonic_clock_ns);
downcast(this)->setPresentationTime(monotonic_clock_ns);
}
bool Renderer::beginFrame(SwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) {
return upcast(this)->beginFrame(upcast(swapChain), vsyncSteadyClockTimeNano);
return downcast(this)->beginFrame(downcast(swapChain), vsyncSteadyClockTimeNano);
}
void Renderer::copyFrame(SwapChain* dstSwapChain, filament::Viewport const& dstViewport,
filament::Viewport const& srcViewport, CopyFrameFlag flags) {
upcast(this)->copyFrame(upcast(dstSwapChain), dstViewport, srcViewport, flags);
downcast(this)->copyFrame(downcast(dstSwapChain), dstViewport, srcViewport, flags);
}
void Renderer::readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) {
upcast(this)->readPixels(xoffset, yoffset, width, height, std::move(buffer));
downcast(this)->readPixels(xoffset, yoffset, width, height, std::move(buffer));
}
void Renderer::readPixels(RenderTarget* renderTarget,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) {
upcast(this)->readPixels(upcast(renderTarget),
downcast(this)->readPixels(downcast(renderTarget),
xoffset, yoffset, width, height, std::move(buffer));
}
void Renderer::endFrame() {
upcast(this)->endFrame();
downcast(this)->endFrame();
}
double Renderer::getUserTime() const {
return upcast(this)->getUserTime();
return downcast(this)->getUserTime();
}
void Renderer::resetUserTime() {
upcast(this)->resetUserTime();
downcast(this)->resetUserTime();
}
void Renderer::setDisplayInfo(const DisplayInfo& info) noexcept {
upcast(this)->setDisplayInfo(info);
downcast(this)->setDisplayInfo(info);
}
void Renderer::setFrameRateOptions(FrameRateOptions const& options) noexcept {
upcast(this)->setFrameRateOptions(options);
downcast(this)->setFrameRateOptions(options);
}
void Renderer::setClearOptions(const ClearOptions& options) {
upcast(this)->setClearOptions(options);
downcast(this)->setClearOptions(options);
}
void Renderer::renderStandaloneView(View const* view) {
upcast(this)->renderStandaloneView(upcast(view));
downcast(this)->renderStandaloneView(downcast(view));
}
} // namespace filament
+10 -2
View File
@@ -173,7 +173,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
view.prepareSSAO(data.ssao ?
resources.getTexture(data.ssao) : engine.getOneTextureArray());
view.prepareShadowMap();
view.prepareShadowMapping(view.getVsmShadowOptions().highPrecision);
// set shadow sampler
view.prepareShadow(data.shadows ?
@@ -200,6 +200,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
view.prepareViewport(static_cast<filament::Viewport&>(out.params.viewport),
config.xoffset, config.yoffset);
view.commitUniforms(driver);
out.params.clearColor = data.clearColor;
@@ -217,7 +218,14 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
if (colorGradingConfig.asSubpass || colorGradingConfig.customResolve) {
out.params.subpassMask = 1;
}
passExecutor.execute(resources.getPassName(), out.target, out.params);
// this is a good time to flush the CommandStream, because we're about to potentially
// output a lot of commands. This guarantees here that we have at least
// FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB bytes (1MiB by default).
engine.flush();
driver.beginRenderPass(out.target, out.params);
passExecutor.execute(engine, resources.getPassName());
driver.endRenderPass();
// color pass is typically heavy, and we don't have much CPU work left after
// this point, so flushing now allows us to start the GPU earlier and reduce
+12 -12
View File
@@ -24,51 +24,51 @@ using namespace utils;
namespace filament {
void Scene::setSkybox(Skybox* skybox) noexcept {
upcast(this)->setSkybox(upcast(skybox));
downcast(this)->setSkybox(downcast(skybox));
}
Skybox* Scene::getSkybox() noexcept {
return upcast(this)->getSkybox();
return downcast(this)->getSkybox();
}
Skybox const* Scene::getSkybox() const noexcept {
return upcast(this)->getSkybox();
return downcast(this)->getSkybox();
}
void Scene::setIndirectLight(IndirectLight const* ibl) noexcept {
upcast(this)->setIndirectLight(upcast(ibl));
downcast(this)->setIndirectLight(downcast(ibl));
}
void Scene::addEntity(Entity entity) {
upcast(this)->addEntity(entity);
downcast(this)->addEntity(entity);
}
void Scene::addEntities(const Entity* entities, size_t count) {
upcast(this)->addEntities(entities, count);
downcast(this)->addEntities(entities, count);
}
void Scene::remove(Entity entity) {
upcast(this)->remove(entity);
downcast(this)->remove(entity);
}
void Scene::removeEntities(const Entity* entities, size_t count) {
upcast(this)->removeEntities(entities, count);
downcast(this)->removeEntities(entities, count);
}
size_t Scene::getRenderableCount() const noexcept {
return upcast(this)->getRenderableCount();
return downcast(this)->getRenderableCount();
}
size_t Scene::getLightCount() const noexcept {
return upcast(this)->getLightCount();
return downcast(this)->getLightCount();
}
bool Scene::hasEntity(Entity entity) const noexcept {
return upcast(this)->hasEntity(entity);
return downcast(this)->hasEntity(entity);
}
void Scene::forEach(Invocable<void(utils::Entity)>&& functor) const noexcept {
upcast(this)->forEach(std::move(functor));
downcast(this)->forEach(std::move(functor));
}
} // namespace filament
+404 -228
View File
@@ -40,14 +40,15 @@ using namespace backend;
// do this only if depth-clamp is available
static constexpr bool USE_DEPTH_CLAMP = false;
ShadowMap::ShadowMap(FEngine& engine) noexcept :
mEngine(engine),
mClipSpaceFlipped(engine.getBackend() == Backend::VULKAN),
mTextureSpaceFlipped(engine.getBackend() == Backend::METAL) {
ShadowMap::ShadowMap(FEngine& engine) noexcept
: mPerShadowMapUniforms(engine),
mShadowType(ShadowType::DIRECTIONAL),
mHasVisibleShadows(false),
mFace(0) {
Entity entities[2];
engine.getEntityManager().create(2, entities);
mCamera = mEngine.createCamera(entities[0]);
mDebugCamera = mEngine.createCamera((entities[1]));
mCamera = engine.createCamera(entities[0]);
mDebugCamera = engine.createCamera((entities[1]));
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.focus_shadowcasters", &engine.debug.shadowmap.focus_shadowcasters);
debugRegistry.registerProperty("d.shadowmap.far_uses_shadowcasters", &engine.debug.shadowmap.far_uses_shadowcasters);
@@ -56,52 +57,62 @@ ShadowMap::ShadowMap(FEngine& engine) noexcept :
}
void ShadowMap::terminate(FEngine& engine) {
}
ShadowMap::~ShadowMap() {
FEngine& engine = mEngine;
Entity entities[] = { mCamera->getEntity(), mDebugCamera->getEntity() };
for (Entity e : entities) {
engine.destroyCameraComponent(e);
}
engine.getEntityManager().destroy(sizeof(entities) / sizeof(Entity), entities);
mPerShadowMapUniforms.terminate(engine.getDriverApi());
}
void ShadowMap::render(FScene const& scene, utils::Range<uint32_t> range,
FScene::VisibleMaskType visibilityMask, RenderPass* const pass) noexcept {
const backend::Viewport disabledScissor{ 0, 0,
(uint32_t)std::numeric_limits<int32_t>::max(),
(uint32_t)std::numeric_limits<int32_t>::max() };
pass->setVisibilityMask(visibilityMask);
pass->setGeometry(scene.getRenderableData(), range, scene.getRenderableUBO());
pass->overridePolygonOffset(&mShadowMapInfo.polygonOffset);
pass->overrideScissor(&disabledScissor);
pass->appendCommands(RenderPass::SHADOW);
pass->sortCommands();
ShadowMap::~ShadowMap() = default;
void ShadowMap::initialize(size_t lightIndex, ShadowType shadowType,
uint16_t shadowIndex, uint8_t face,
LightManager::ShadowOptions const* options) {
mLightIndex = lightIndex;
mShadowIndex = shadowIndex;
mOptions = options;
mShadowType = shadowType;
mFace = face;
}
mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position) noexcept {
const mat4f M = mat4f::lookAt(position, position + direction, float3{ 0, 1, 0 });
return FCamera::rigidTransformInverse(M);
const mat4f Mm = mat4f::lookAt(position, position + direction, float3{ 0, 1, 0 });
return FCamera::rigidTransformInverse(Mm);
}
void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t index,
math::mat4f ShadowMap::getPointLightViewMatrix(backend::TextureCubemapFace face,
math::float3 position) noexcept {
float3 direction;
switch (TextureCubemapFace(face)) {
case TextureCubemapFace::POSITIVE_X: direction = { 1, 0, 0 }; break;
case TextureCubemapFace::NEGATIVE_X: direction = { -1, 0, 0 }; break;
case TextureCubemapFace::POSITIVE_Y: direction = { 0, 1, 0 }; break;
case TextureCubemapFace::NEGATIVE_Y: direction = { 0, -1, 0 }; break;
case TextureCubemapFace::POSITIVE_Z: direction = { 0, 0, 1 }; break;
case TextureCubemapFace::NEGATIVE_Z: direction = { 0, 0, -1 }; break;
}
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
return Mv;
}
ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
ShadowMapInfo const& shadowMapInfo, FScene const& scene,
SceneInfo& sceneInfo) noexcept {
mShadowMapInfo = shadowMapInfo;
// Note: we keep the polygon offset even with VSM as it seems to help.
auto& lcm = mEngine.getLightManager();
auto& lcm = engine.getLightManager();
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
FLightManager::ShadowParams params = lcm.getShadowParams(li);
#ifndef NDEBUG
// LISPSM debugging for directional light (works because we only have one)
const float dz = camera.zf - camera.zn;
float& dzn = mEngine.debug.shadowmap.dzn;
float& dzf = mEngine.debug.shadowmap.dzf;
float& dzn = engine.debug.shadowmap.dzn;
float& dzf = engine.debug.shadowmap.dzf;
if (dzn < 0) dzn = std::max(0.0f, params.options.shadowNearHint - camera.zn) / dz;
else params.options.shadowNearHint = dzn * dz - camera.zn;
if (dzf > 0) dzf =-std::max(0.0f, camera.zf - params.options.shadowFarHint) / dz;
@@ -109,17 +120,19 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
#endif
// Adjust the camera's projection for the light's shadowFar
mat4f cullingProjection(camera.cullingProjection);
if (params.options.shadowFar > 0.0f) {
float n = camera.zn;
float f = params.options.shadowFar;
// orthographic projection
assert_invariant(std::abs(cullingProjection[2].w) <= std::numeric_limits<float>::epsilon());
cullingProjection[2].z = 2.0f / (n - f);
cullingProjection[3].z = (f + n) / (n - f);
}
const mat4f cullingProjection{ [&](auto p) {
if (params.options.shadowFar > 0.0f) {
float n = camera.zn;
float f = params.options.shadowFar;
// orthographic projection
assert_invariant(std::abs(p[2].w) <= std::numeric_limits<float>::epsilon());
p[2].z = 2.0f / (n - f);
p[3].z = (f + n) / (n - f);
}
return p;
}(camera.cullingProjection) };
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
const auto direction = lightData.elementAt<FScene::DIRECTION>(index);
/*
* Compute the light's model matrix
@@ -131,13 +144,13 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
const mat4f MvAtOrigin = getDirectionalLightViewMatrix(direction);
// Compute scene-dependent values shared across all cascades
ShadowMap::updateSceneInfo(MvAtOrigin, scene, sceneInfo);
ShadowMap::updateSceneInfoDirectional(MvAtOrigin, scene, sceneInfo);
const Aabb wsShadowCastersVolume = sceneInfo.wsShadowCastersVolume;
const Aabb wsShadowReceiversVolume = sceneInfo.wsShadowReceiversVolume;
if (wsShadowCastersVolume.isEmpty() || wsShadowReceiversVolume.isEmpty()) {
mHasVisibleShadows = false;
return;
return {};
}
// view frustum vertices in world-space
@@ -155,6 +168,11 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
size_t vertexCount = intersectFrustumWithBox(wsClippedShadowReceiverVolume,
wsFrustum, wsViewFrustumVertices, wsShadowReceiversVolume);
if (UTILS_UNLIKELY(vertexCount < 4)) {
mHasVisibleShadows = false;
return {};
}
/*
* compute scene 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).
@@ -183,7 +201,7 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
lsLightFrustumBounds.max.z = std::max(lsLightFrustumBounds.max.z, v.z);
}
}
if (mEngine.debug.shadowmap.far_uses_shadowcasters) {
if (engine.debug.shadowmap.far_uses_shadowcasters) {
// far: closest of the farthest shadow casters and receivers
lsLightFrustumBounds.min.z = std::max(lsLightFrustumBounds.min.z, sceneInfo.lsNearFar[1]);
}
@@ -200,11 +218,11 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
// if znear >= zfar, it means we don't have any shadow caster in front of a shadow receiver
if (UTILS_UNLIKELY(znear >= zfar)) {
mHasVisibleShadows = false;
return;
return {};
}
float4 viewVolumeBoundingSphere = {};
if (params.options.stable) {
if (UTILS_UNLIKELY(params.options.stable)) {
// In stable mode, the light frustum size must be fixed, so we can choose either the
// whole view frustum, or the whole scene bounding volume. We simply pick whichever
// is smaller.
@@ -217,177 +235,193 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
viewVolumeBoundingSphere = computeBoundingSphere(wsViewFrustumVertices, 8);
if (shadowReceiverVolumeBoundingSphere.w < viewVolumeBoundingSphere.w) {
// When using the shadowReceiver volume, we don't have to use its enclosing sphere
// because (we assume) the scene volume doesn't change. Seen from the light it only
// changes when the light moves or rotates, and it is acceptable in that case to have
// non "stable" shadows (the shadow will never be stable when the light moves).
//
// On the other hand, when using the view volume, we must use a sphere because otherwise
// its projection's bounds in light space change with the camera, leading to unstable
// shadows with camera movement.
viewVolumeBoundingSphere.w = 0;
std::copy_n(wsShadowReceiversVolume.getCorners().data(), 8,
wsClippedShadowReceiverVolume.data());
}
}
mHasVisibleShadows = vertexCount >= 4;
if (mHasVisibleShadows) {
// We can't use LISPSM in stable mode
const bool useLispsm = params.options.lispsm && !params.options.stable;
mHasVisibleShadows = true;
/*
* Compute the light's projection matrix
* (directional/point lights, i.e. projection to use, including znear/zfar clip planes)
*/
// We can't use LISPSM in stable mode
const bool useLispsm = params.options.lispsm && !params.options.stable;
// The light's projection, ortho for directional lights, perspective otherwise
const mat4f Mp = directionalLightFrustum(znear, zfar);
/*
* Compute the light's projection matrix
* (directional/point lights, i.e. projection to use, including znear/zfar clip planes)
*/
const mat4f MpMv(Mp * Mv);
// The light's projection, ortho for directional lights, perspective otherwise
const mat4f Mp = directionalLightFrustum(znear, zfar);
/*
* Compute warping (optional, improve quality)
*/
const mat4f MpMv(Mp * Mv);
mat4f LMpMv = MpMv;
/*
* Compute warping (optional, improve quality)
*/
// Compute the LiSPSM warping
mat4f W, Wp;
mat4f L; // Rotation matrix in light space
if (useLispsm) {
// Orient the shadow map in the direction of the view vector by constructing a
// rotation matrix in light space around the z-axis, that aligns the y-axis with the camera's
// forward vector (V) -- this gives the wrap direction, vp, for LiSPSM.
const float3 wsCameraFwd = camera.getForwardVector();
const float3 lsCameraFwd = Mv.upperLeft() * wsCameraFwd;
// If the light and view vector are parallel, this rotation becomes
// meaningless. Just use identity.
// (LdotV == (Mv*V).z, because L = {0,0,1} in light-space)
if (UTILS_LIKELY(std::abs(lsCameraFwd.z) < 0.9997f)) { // this is |dot(L, V)|
const float3 vp{ normalize(lsCameraFwd.xy), 0 }; // wrap direction in light-space
L[0].xyz = cross(vp, float3{ 0, 0, 1 });
L[1].xyz = vp;
L[2].xyz = { 0, 0, 1 };
L = transpose(L);
}
mat4f LMpMv = MpMv;
LMpMv = L * MpMv;
W = applyLISPSM(Wp, camera, params, LMpMv,
wsClippedShadowReceiverVolume, vertexCount, direction);
// Compute the LiSPSM warping
mat4f W, Wp;
mat4f L; // Rotation matrix in light space
if (useLispsm) {
// Orient the shadow map in the direction of the view vector by constructing a
// rotation matrix in light space around the z-axis, that aligns the y-axis with the camera's
// forward vector (V) -- this gives the wrap direction, vp, for LiSPSM.
const float3 wsCameraFwd = camera.getForwardVector();
const float3 lsCameraFwd = Mv.upperLeft() * wsCameraFwd;
// If the light and view vector are parallel, this rotation becomes
// meaningless. Just use identity.
// (LdotV == (Mv*V).z, because L = {0,0,1} in light-space)
if (UTILS_LIKELY(std::abs(lsCameraFwd.z) < 0.9997f)) { // this is |dot(L, V)|
const float3 vp{ normalize(lsCameraFwd.xy), 0 }; // wrap direction in light-space
L[0].xyz = cross(vp, float3{ 0, 0, 1 });
L[1].xyz = vp;
L[2].xyz = { 0, 0, 1 };
L = transpose(L);
}
/*
* Compute focusing matrix (optional, greatly improves quality)
*/
LMpMv = L * MpMv;
// construct the warped light-space
const mat4f WLMpMv = W * LMpMv;
// 2) Now we find the x-y bounds of our convex-hull (view volume & shadow receivers)
// in light space, so we can "focus" the shadow map to the interesting area.
// This is the most important step to increase the quality of the shadow map.
//
// In LiPSM mode, we're using the warped space here.
Aabb bounds;
if (params.options.stable && viewVolumeBoundingSphere.w > 0) {
bounds = compute2DBounds(Mv, viewVolumeBoundingSphere);
} else {
bounds = compute2DBounds(WLMpMv, wsClippedShadowReceiverVolume.data(), vertexCount);
}
lsLightFrustumBounds.min.xy = bounds.min.xy;
lsLightFrustumBounds.max.xy = bounds.max.xy;
if (params.options.stable) {
// in stable mode we can't do anything that can change the scaling of the texture
} else {
// For directional lights, we further constraint the light frustum to the
// intersection of the shadow casters & shadow receivers in light-space.
// ** This relies on the 1-texel shadow map border **
if (mEngine.debug.shadowmap.focus_shadowcasters) {
intersectWithShadowCasters(lsLightFrustumBounds, WLMpMv, wsShadowCastersVolume);
}
}
if (UTILS_UNLIKELY((lsLightFrustumBounds.min.x >= lsLightFrustumBounds.max.x) ||
(lsLightFrustumBounds.min.y >= lsLightFrustumBounds.max.y))) {
// this could happen if the only thing visible is a perfectly horizontal or
// vertical thin line
mHasVisibleShadows = false;
return;
}
assert_invariant(lsLightFrustumBounds.min.x < lsLightFrustumBounds.max.x);
assert_invariant(lsLightFrustumBounds.min.y < lsLightFrustumBounds.max.y);
// compute focus scale and offset
float2 s = 2.0f / float2(lsLightFrustumBounds.max.xy - lsLightFrustumBounds.min.xy);
float2 o = -s * float2(lsLightFrustumBounds.max.xy + lsLightFrustumBounds.min.xy) * 0.5f;
if (params.options.stable) {
// Use the world origin as reference point, fixed w.r.t. the camera
snapLightFrustum(s, o, Mv, -camera.getWorldOffset(),
1.0f / mShadowMapInfo.shadowDimension);
}
const mat4f F(mat4f::row_major_init {
s.x, 0.0f, 0.0f, o.x,
0.0f, s.y, 0.0f, o.y,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
});
/*
* Final shadow map transform
*/
// Final shadow transform
const mat4f S = F * WLMpMv;
// Computes St the transform to use in the shader to access the shadow map texture
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
const mat4 MbMt = getTextureCoordsMapping();
const mat4f St = mat4f(MbMt * S);
// note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
// if Wp[3][1] is 0, then LISPSM was cancelled.
if (useLispsm && Wp[3][1] != 0.0f) {
mTexelSizeAtOneMeterWs = texelSizeWorldSpace(Wp, mat4f(MbMt * F));
} else {
// We know we're using an ortho projection
mTexelSizeAtOneMeterWs = texelSizeWorldSpace(St.upperLeft());
}
if (!mShadowMapInfo.vsm) {
mLightSpace = St;
} else {
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar);
}
// We apply the constant bias in world space (as opposed to light-space) to account
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
// by baking it in the shadow-map itself.
const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(direction * constantBias);
// It's important to set the light camera's model matrix separately from its projection, so
// that the cameraPosition uniform gets set correctly.
// mLightSpace is used in the shader to access the shadow map texture, and has the model
// matrix baked in.
// The model matrix below is in fact inverted to get the view matrix and passed to the
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
mCamera->setCustomProjection(mat4(F * W * L * Mp), znear, zfar);
// for the debug camera, we need to undo the world origin
mDebugCamera->setCustomProjection(mat4(S * b * camera.worldOrigin), znear, zfar);
W = applyLISPSM(Wp, camera, params, LMpMv,
wsClippedShadowReceiverVolume, vertexCount, direction);
}
/*
* Compute focusing matrix (optional, greatly improves quality)
*/
// construct the warped light-space
const mat4f WLMpMv = W * LMpMv;
// 2) Now we find the x-y bounds of our convex-hull (view volume & shadow receivers)
// in light space, so we can "focus" the shadow map to the interesting area.
// This is the most important step to increase the quality of the shadow map.
//
// In LiPSM mode, we're using the warped space here.
Aabb bounds;
if (params.options.stable && viewVolumeBoundingSphere.w > 0) {
bounds = compute2DBounds(Mv, viewVolumeBoundingSphere);
} else {
bounds = compute2DBounds(WLMpMv, wsClippedShadowReceiverVolume.data(), vertexCount);
}
lsLightFrustumBounds.min.xy = bounds.min.xy;
lsLightFrustumBounds.max.xy = bounds.max.xy;
if (params.options.stable) {
// in stable mode we can't do anything that can change the scaling of the texture
} else {
// For directional lights, we further constraint the light frustum to the
// intersection of the shadow casters & shadow receivers in light-space.
// ** This relies on the 1-texel shadow map border **
if (engine.debug.shadowmap.focus_shadowcasters) {
intersectWithShadowCasters(lsLightFrustumBounds, WLMpMv, wsShadowCastersVolume);
}
}
if (UTILS_UNLIKELY((lsLightFrustumBounds.min.x >= lsLightFrustumBounds.max.x) ||
(lsLightFrustumBounds.min.y >= lsLightFrustumBounds.max.y))) {
// this could happen if the only thing visible is a perfectly horizontal or
// vertical thin line
mHasVisibleShadows = false;
return {};
}
assert_invariant(lsLightFrustumBounds.min.x < lsLightFrustumBounds.max.x);
assert_invariant(lsLightFrustumBounds.min.y < lsLightFrustumBounds.max.y);
// compute focus scale and offset
float2 s = 2.0f / float2(lsLightFrustumBounds.max.xy - lsLightFrustumBounds.min.xy);
float2 o = -s * float2(lsLightFrustumBounds.max.xy + lsLightFrustumBounds.min.xy) * 0.5f;
if (params.options.stable) {
// Use the world origin as reference point, fixed w.r.t. the camera
snapLightFrustum(s, o, Mv, -camera.getWorldOffset(),
1.0f / float(shadowMapInfo.shadowDimension));
}
const mat4f F(mat4f::row_major_init {
s.x, 0.0f, 0.0f, o.x,
0.0f, s.y, 0.0f, o.y,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
});
/*
* Final shadow map transform
*/
// Final shadow transform
const mat4f S = F * WLMpMv;
// Computes St the transform to use in the shader to access the shadow map texture
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
const mat4 MbMt = getTextureCoordsMapping(shadowMapInfo);
const mat4f St = mat4f(MbMt * S);
ShadowMap::ShaderParameters shaderParameters;
// note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
// if Wp[3][1] is 0, then LISPSM was cancelled.
if (useLispsm && Wp[3][1] != 0.0f) {
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(Wp, mat4f(MbMt * F),
shadowMapInfo.shadowDimension);
} else {
// We know we're using an ortho projection
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(St.upperLeft(),
shadowMapInfo.shadowDimension);
}
if (!shadowMapInfo.vsm) {
shaderParameters.lightSpace = St;
} else {
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar);
}
// We apply the constant bias in world space (as opposed to light-space) to account
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
// by baking it in the shadow-map itself.
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(direction * constantBias);
// It's important to set the light camera's model matrix separately from its projection, so
// that the cameraPosition uniform gets set correctly.
// mLightSpace is used in the shader to access the shadow map texture, and has the model
// matrix baked in.
// The model matrix below is in fact inverted to get the view matrix and passed to the
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
mCamera->setCustomProjection(mat4(F * W * L * Mp), znear, zfar);
// for the debug camera, we need to undo the world origin
mDebugCamera->setCustomProjection(mat4(S * b * camera.worldOrigin), znear, zfar);
return shaderParameters;
}
void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo,
FScene const& scene, SceneInfo& sceneInfo) noexcept {
FScene const& scene, SceneInfo sceneInfo) noexcept {
mShadowMapInfo = shadowMapInfo;
ShaderParameters shaderParameters;
auto& lcm = mEngine.getLightManager();
auto& lcm = engine.getLightManager();
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
@@ -395,9 +429,6 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
// TODO: correctly compute if this spot light has any visible shadows.
mHasVisibleShadows = true;
/*
* Compute the light model matrix.
*/
@@ -406,10 +437,14 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
// find decent near/far
ShadowMap::updateSceneInfo(Mv, scene, sceneInfo, mShadowMapInfo.spotIndex);
ShadowMap::updateSceneInfoSpot(Mv, scene, sceneInfo);
// if the scene was empty, near > far
mHasVisibleShadows = -sceneInfo.lsNearFar[0] < -sceneInfo.lsNearFar[1];
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar.x);
float farPlane = std::min(radius, -sceneInfo.lsNearFar.y);
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar[0]);
float farPlane = std::min(radius, -sceneInfo.lsNearFar[1]);
float outerConeAngleDegrees = outerConeAngle * f::RAD_TO_DEG;
const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2.0f, 1.0f, nearPlane, farPlane);
@@ -418,7 +453,7 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
// Final shadow transform
const mat4f S = MpMv;
const mat4 MbMt = getTextureCoordsMapping();
const mat4 MbMt = getTextureCoordsMapping(shadowMapInfo);
const mat4f St = mat4f(MbMt * S);
// TODO: focus projection
@@ -434,16 +469,16 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
// = zInLightSpace * texelSizeAtOneMeter
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
// Note: this would not work with LISPSM, which warps the texture space.
mTexelSizeAtOneMeterWs = (2.0f * std::tan(outerConeAngle) / float(mShadowMapInfo.shadowDimension));
mLightFromWorldZ = -transpose(Mv)[2]; // negate because camera looks in -Z
shaderParameters.texelSizeAtOneMeterWs = (2.0f * std::tan(outerConeAngle) / float(shadowMapInfo.shadowDimension));
shaderParameters.lightFromWorldZ = -transpose(Mv)[2]; // negate because camera looks in -Z
if (!mShadowMapInfo.vsm) {
mLightSpace = St;
if (!shadowMapInfo.vsm) {
shaderParameters.lightSpace = St;
} else {
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
}
const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(direction * constantBias);
const mat4f Sb = S * b;
@@ -460,6 +495,76 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
// for the debug camera, we need to undo the world origin
mDebugCamera->setCustomProjection(mat4(Sb * camera.worldOrigin), nearPlane, radius);
return shaderParameters;
}
ShadowMap::ShaderParameters ShadowMap::updatePoint(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo, FScene const& scene,
SceneInfo, uint8_t face) noexcept {
ShaderParameters shaderParameters;
// check if this shadow map has anything to render
mHasVisibleShadows = false;
FScene::RenderableSoa const& UTILS_RESTRICT soa = scene.getRenderableData();
auto const* const UTILS_RESTRICT visibleMasks = soa.data<FScene::VISIBLE_MASK>();
size_t c = soa.size();
for (size_t i = 0; i < c; i++) {
if (visibleMasks[i] & VISIBLE_DYN_SHADOW_RENDERABLE) {
mHasVisibleShadows = true;
break;
}
}
if (!mHasVisibleShadows) {
return shaderParameters;
}
auto& lcm = engine.getLightManager();
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
/*
* Compute the light model matrix.
*/
const mat4f Mv = getPointLightViewMatrix(TextureCubemapFace(face), position);
const float3 direction = -transpose(Mv)[2].xyz;
// TODO: don't hardcode near plane
// Choose a reasonable value for the near plane.
float nearPlane = 0.01f;
float farPlane = radius;
const mat4f Mp = mat4f::perspective(90.0f, 1.0f, nearPlane, farPlane);
// For calculating the point light normal bias, we need the texel size in world space at the
// sample location. Using Thales's theorem, we find:
// texelSize(zInLightSpace) = zInLightSpace * texelSizeOnTheNearPlane / near
// = zInLightSpace * texelSizeAtOneMeter
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
// Note: this would not work with LISPSM, which warps the texture space.
shaderParameters.texelSizeAtOneMeterWs =
(2.0f * std::tan(f::PI_4) / float(shadowMapInfo.shadowDimension));
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(direction * constantBias);
// It's important to set the light camera's model matrix separately from its projection, so that
// the cameraPosition uniform gets set correctly.
// mLightSpace is used in the shader to access the shadow map texture, and has the model matrix
// baked in.
// The model matrix below is in fact inverted to get the view matrix and passed to the
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane);
return shaderParameters;
}
mat4f ShadowMap::applyLISPSM(mat4f& Wp,
@@ -499,7 +604,8 @@ mat4f ShadowMap::applyLISPSM(mat4f& Wp,
mat4f W;
// see nopt1 below for an explanation about this test
// sinLV is positive since it comes from a square-root
if (sinLV > 0 && 3.0f * (dzn / (zf - zn)) < 2.0f) {
constexpr float epsilon = 0.02f; // very roughly 1 degree
if (sinLV > epsilon && 3.0f * (dzn / (zf - zn)) < 2.0f) {
// nopt is the optimal near plane distance of Wp (i.e. distance from P).
// virtual near and far planes
@@ -540,10 +646,10 @@ mat4f ShadowMap::applyLISPSM(mat4f& Wp,
// Apply these remapping in double to maintain a high precision for the depth axis
mat4 ShadowMap::getTextureCoordsMapping() const noexcept {
mat4 ShadowMap::getTextureCoordsMapping(ShadowMapInfo const& info) noexcept {
// remapping from NDC to texture coordinates (i.e. [-1,1] -> [0, 1])
// ([1, 0] for depth mapping)
const mat4f Mt(mClipSpaceFlipped ? mat4f::row_major_init{
const mat4f Mt(info.clipSpaceFlipped ? mat4f::row_major_init{
0.5f, 0.0f, 0.0f, 0.5f,
0.0f, -0.5f, 0.0f, 0.5f,
0.0f, 0.0f, -0.5f, 0.5f,
@@ -557,7 +663,7 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept {
// the shadow map texture might be larger than the shadow map dimension, so we add a scaling
// factor
const float v = (float) mShadowMapInfo.textureDimension / mShadowMapInfo.atlasDimension;
const float v = float(info.textureDimension) / float(info.atlasDimension);
const mat4f Mv(mat4f::row_major_init{
v, 0.0f, 0.0f, 0.0f,
0.0f, v, 0.0f, 0.0f,
@@ -566,8 +672,8 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept {
});
// apply the 1-texel border viewport transform
const float o = 1.0f / mShadowMapInfo.atlasDimension;
const float s = 1.0f - 2.0f * (1.0f / mShadowMapInfo.textureDimension);
const float o = 1.0f / float(info.atlasDimension);
const float s = 1.0f - 2.0f * (1.0f / float(info.textureDimension));
const mat4f Mb(mat4f::row_major_init{
s, 0.0f, 0.0f, o,
0.0f, s, 0.0f, o,
@@ -575,7 +681,7 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept {
0.0f, 0.0f, 0.0f, 1.0f
});
const mat4f Mf = mTextureSpaceFlipped ? mat4f(mat4f::row_major_init{
const mat4f Mf = info.textureSpaceFlipped ? mat4f(mat4f::row_major_init{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 0.0f,
@@ -882,8 +988,12 @@ size_t ShadowMap::intersectFrustumWithBox(
vertexCount = intersectFrustum(outVertices.data(), vertexCount,
wsFrustumCorners, wsSceneReceiversCorners.vertices);
} else {
// by construction vertexCount must be 8 here
assert_invariant(vertexCount == 8);
// by construction vertexCount should be 8 here, but it can be more because
// step (b) above can classify a point as inside the frustum that isn't quite inside.
// -> We can't blindly reset vertexCount to 8, we need to remove the "duplicate"
// vertices.
// It shouldn't cause any major issues later to have "nearly" duplicate vertices.
assert_invariant(vertexCount >= 8);
}
}
@@ -964,13 +1074,14 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p,
return hit;
}
float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture) const noexcept {
float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture,
uint16_t shadowDimension) const noexcept {
// The Jacobian of the transformation from texture-to-world is the matrix itself for
// orthographic projections. We just need to inverse worldToShadowTexture,
// which is guaranteed to be orthographic.
// The two first columns give us how a texel maps in world-space.
const float ures = 1.0f / float(mShadowMapInfo.shadowDimension);
const float vres = 1.0f / float(mShadowMapInfo.shadowDimension);
const float ures = 1.0f / float(shadowDimension);
const float vres = 1.0f / float(shadowDimension);
const mat3f shadowTextureToWorld(inverse(worldToShadowTexture));
const float3 Jx = shadowTextureToWorld[0];
const float3 Jy = shadowTextureToWorld[1];
@@ -978,7 +1089,8 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture) const no
return s;
}
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const noexcept {
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF,
uint16_t shadowDimension) const noexcept {
// Here we compute the Jacobian of inverse(MbMtF * Wp).
// The expression below has been computed with Mathematica. However, it's not very hard,
// albeit error-prone, to do it by hand because MbMtF is a linear transform.
@@ -993,8 +1105,8 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const
// It might be better to do this computation in the vertex shader.
float3 p = {0.5, 0.5, 0.0};
const float ures = 1.0f / float(mShadowMapInfo.shadowDimension);
const float vres = 1.0f / float(mShadowMapInfo.shadowDimension);
const float ures = 1.0f / float(shadowDimension);
const float vres = 1.0f / float(shadowDimension);
const float dres = 1.0f / 65536.0f;
constexpr bool JACOBIAN_ESTIMATE = false;
@@ -1070,8 +1182,9 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
}
}
void ShadowMap::initSceneInfo(FScene const& scene, mat4f const& viewMatrix,
ShadowMap::SceneInfo& sceneInfo) {
void ShadowMap::initSceneInfo(ShadowMap::SceneInfo& sceneInfo,
uint8_t visibleLayers, FScene const& scene, mat4f const& viewMatrix) {
sceneInfo.visibleLayers = visibleLayers;
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
@@ -1099,8 +1212,8 @@ void ShadowMap::initSceneInfo(FScene const& scene, mat4f const& viewMatrix,
);
}
void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
ShadowMap::SceneInfo& sceneInfo) {
void ShadowMap::updateSceneInfoDirectional(mat4f const& Mv, FScene const& scene,
SceneInfo& sceneInfo) {
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
visitScene(scene, sceneInfo.visibleLayers,
[&](Aabb caster, Culler::result_type) {
@@ -1113,13 +1226,13 @@ void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
);
}
void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
ShadowMap::SceneInfo& sceneInfo, uint16_t index) {
void ShadowMap::updateSceneInfoSpot(mat4f const& Mv, FScene const& scene,
SceneInfo& sceneInfo) {
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
visitScene(scene, sceneInfo.visibleLayers,
[&](Aabb caster, Culler::result_type mask) {
if (mask & VISIBLE_SPOT_SHADOW_RENDERABLE_N(index)) {
if (mask & VISIBLE_DYN_SHADOW_RENDERABLE) {
float2 nf = ShadowMap::computeNearFar(Mv, caster);
sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near
sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far
@@ -1130,4 +1243,67 @@ void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
);
}
filament::Viewport ShadowMap::getViewport() const noexcept {
// We set a viewport with a 1-texel border for when we index outside the
// texture.
// DON'T CHANGE this unless ShadowMap::getTextureCoordsMapping() is updated too.
// see: ShadowMap::getTextureCoordsMapping()
//
// For floating-point depth textures, the 1-texel border could be set to
// FLOAT_MAX to avoid clamping in the shadow shader (see sampleDepth inside
// shadowing.fs). Unfortunately, the APIs don't seem let us clear depth
// attachments to anything greater than 1.0, so we'd need a way to do this other
// than clearing.
const uint32_t dim = mOptions->mapSize;
if (isPointShadow()) {
// for point-light we don't have a border
return { 0, 0, dim, dim };
} else {
return { 1, 1, dim - 2, dim - 2 };
}
}
// ------------------------------------------------------------------------------------------------
void ShadowMap::prepareCamera(Transaction const& transaction,
FEngine& engine, const CameraInfo& cameraInfo) noexcept {
PerShadowMapUniforms::prepareCamera(transaction, engine, cameraInfo);
PerShadowMapUniforms::prepareLodBias(transaction, 0.0f);
}
void ShadowMap::prepareViewport(Transaction const& transaction,
const filament::Viewport& viewport) noexcept {
PerShadowMapUniforms::prepareViewport(transaction, viewport, 0, 0);
}
void ShadowMap::prepareTime(Transaction const& transaction,
FEngine& engine, math::float4 const& userTime) noexcept {
PerShadowMapUniforms::prepareTime(transaction, engine, userTime);
}
void ShadowMap::prepareDirectionalLight(Transaction const& transaction,
FEngine& engine, math::float3 const& sceneSpaceDirection,
LightManager::Instance instance) noexcept {
PerShadowMapUniforms::prepareDirectionalLight(transaction,
engine, 1.0f, sceneSpaceDirection, instance);
}
void ShadowMap::prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept {
PerShadowMapUniforms::prepareShadowMapping(transaction, highPrecision);
}
PerShadowMapUniforms::Transaction ShadowMap::open(DriverApi& driver) noexcept {
return PerShadowMapUniforms::open(driver);
}
void ShadowMap::commit(Transaction& transaction,
backend::DriverApi& driver) const noexcept {
mPerShadowMapUniforms.commit(transaction, driver);
}
void ShadowMap::bindPerViewUniformsAndSamplers(backend::DriverApi& driver) const noexcept {
mPerShadowMapUniforms.bind(driver);
}
} // namespace filament
+103 -68
View File
@@ -19,11 +19,14 @@
#include "components/LightManager.h"
#include "PerShadowMapUniforms.h"
#include "details/Camera.h"
#include "details/Scene.h"
#include "backend/DriverApiForward.h"
#include "private/backend/SamplerGroup.h"
#include "private/backend/DriverApi.h"
#include <filament/Viewport.h>
#include <math/mat4.h>
#include <math/vec4.h>
@@ -40,9 +43,7 @@ class RenderPass;
// +------------------------------------------------------+
// VISIBLE_RENDERABLE X
// VISIBLE_DIR_SHADOW_RENDERABLE X
// VISIBLE_SPOT_SHADOW_RENDERABLE_0 X
// VISIBLE_SPOT_SHADOW_RENDERABLE_1 X
// ...
// VISIBLE_DYN_SHADOW_RENDERABLE X
// A "shadow renderable" is a renderable rendered to the shadow map during a shadow pass:
// PCF shadows: only shadow casters
@@ -50,25 +51,20 @@ class RenderPass;
static constexpr size_t VISIBLE_RENDERABLE_BIT = 0u;
static constexpr size_t VISIBLE_DIR_SHADOW_RENDERABLE_BIT = 1u;
static constexpr size_t VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(size_t n) { return n + 2; }
static constexpr size_t VISIBLE_DYN_SHADOW_RENDERABLE_BIT = 2u;
static constexpr Culler::result_type VISIBLE_DIR_SHADOW_RENDERABLE = 1u << VISIBLE_DIR_SHADOW_RENDERABLE_BIT;
static constexpr Culler::result_type VISIBLE_SPOT_SHADOW_RENDERABLE_N(size_t n) {
return 1u << VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(n);
}
// ORing of all the VISIBLE_SPOT_SHADOW_RENDERABLE bits
static constexpr Culler::result_type VISIBLE_SPOT_SHADOW_RENDERABLE =
(0xFFu >> (sizeof(Culler::result_type) * 8u - CONFIG_MAX_SHADOW_CASTING_SPOTS)) << 2u;
// Because we're using a uint16_t for the visibility mask, we're limited to 14 spot light shadows.
// (2 of the bits are used for visible renderables + directional light shadow casters).
static_assert(CONFIG_MAX_SHADOW_CASTING_SPOTS <= sizeof(Culler::result_type) * 8 - 2,
"CONFIG_MAX_SHADOW_CASTING_SPOTS cannot be higher than 14.");
static constexpr Culler::result_type VISIBLE_DYN_SHADOW_RENDERABLE = 1u << VISIBLE_DYN_SHADOW_RENDERABLE_BIT;
class ShadowMap {
public:
enum class ShadowType : uint8_t {
DIRECTIONAL,
SPOT,
POINT
};
explicit ShadowMap(FEngine& engine) noexcept;
// ShadowMap is not copyable for now
@@ -91,29 +87,26 @@ public:
// e.g., for a texture dimension of 512, shadowDimension would be 510
uint16_t shadowDimension = 0;
// This spot shadowmap index.
uint16_t spotIndex = 0;
// e.g. Vulkan clip-space
bool clipSpaceFlipped = false;
// e.g. metal textures
bool textureSpaceFlipped = false;
// whether we're using vsm
bool vsm = false;
// polygon offset
backend::PolygonOffset polygonOffset{};
};
struct SceneInfo {
explicit SceneInfo(uint8_t visibleLayers) noexcept : visibleLayers(visibleLayers) { }
// The near and far planes, in clip space, to use for this shadow map
// scratch data: The near and far planes, in clip space, to use for this shadow map
math::float2 csNearFar = { -1.0f, 1.0f };
// The following fields are set by computeSceneCascadeParams.
// light's near/far expressed in light-space, calculated from the scene's content
// assuming the light is at the origin.
// 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 lsNearFar{};
// Viewing camera's near/far expressed in view-space, calculated from the scene's content
// scratch data: Viewing camera's near/far expressed in view-space, calculated from the
// scene's content.
math::float2 vsNearFar{};
// World-space shadow-casters volume
@@ -128,51 +121,89 @@ public:
static math::mat4f getDirectionalLightViewMatrix(
math::float3 direction, math::float3 position = {}) noexcept;
static math::mat4f getPointLightViewMatrix(backend::TextureCubemapFace face,
math::float3 position) noexcept;
void initialize(size_t lightIndex, ShadowType shadowType, uint16_t shadowIndex, uint8_t face,
LightManager::ShadowOptions const* options);
struct ShaderParameters {
math::mat4f lightSpace{};
math::float4 lightFromWorldZ{};
float texelSizeAtOneMeterWs{};
};
// Call once per frame if the light, scene (or visible layers) or camera changes.
// This computes the light's camera.
void updateDirectional(const FScene::LightSoa& lightData, size_t index,
ShaderParameters updateDirectional(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
ShadowMapInfo const& shadowMapInfo, FScene const& scene,
SceneInfo& sceneInfo) noexcept;
void updateSpot(const FScene::LightSoa& lightData, size_t index,
ShaderParameters updateSpot(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
SceneInfo& sceneInfo) noexcept;
SceneInfo sceneInfo) noexcept;
void render(FScene const& scene, utils::Range<uint32_t> range,
FScene::VisibleMaskType visibilityMask, RenderPass* pass) noexcept;
ShaderParameters updatePoint(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo,
FScene const& scene,
SceneInfo sceneInfo, uint8_t face) noexcept;
// Do we have visible shadows. Valid after calling update().
bool hasVisibleShadows() const noexcept { return mHasVisibleShadows; }
// Computes the transform to use in the shader to access the shadow map.
// Valid after calling update().
math::mat4f const& getLightSpaceMatrix() const noexcept { return mLightSpace; }
// return the size of a texel in world space (pre-warping)
float getTexelSizAtOneMeterWs() const noexcept { return mTexelSizeAtOneMeterWs; }
math::float4 getLightFromWorldZ() const noexcept { return mLightFromWorldZ; }
// Returns the light's projection. Valid after calling update().
FCamera const& getCamera() const noexcept { return *mCamera; }
// use only for debugging
FCamera const& getDebugCamera() const noexcept { return *mDebugCamera; }
backend::PolygonOffset getPolygonOffset() const noexcept { return mShadowMapInfo.polygonOffset; }
// Call once per frame to populate the SceneInfo struct, then pass to update().
// This computes values constant across all shadow maps.
static void initSceneInfo(FScene const& scene, math::mat4f const& viewMatrix,
ShadowMap::SceneInfo& sceneInfo);
static void initSceneInfo(ShadowMap::SceneInfo& sceneInfo, uint8_t visibleLayers,
FScene const& scene, math::mat4f const& viewMatrix);
// Update SceneInfo struct for a given light
static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene,
ShadowMap::SceneInfo& sceneInfo);
static void updateSceneInfoDirectional(const math::mat4f& Mv, FScene const& scene,
SceneInfo& sceneInfo);
static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene,
ShadowMap::SceneInfo& sceneInfo, uint16_t index);
static void updateSceneInfoSpot(const math::mat4f& Mv, FScene const& scene,
SceneInfo& sceneInfo);
filament::Viewport getViewport() const noexcept;
LightManager::ShadowOptions const* getShadowOptions() const noexcept { return mOptions; }
size_t getLightIndex() const { return mLightIndex; }
uint16_t getShadowIndex() const { return mShadowIndex; }
void setLayer(uint8_t layer) noexcept { mLayer = layer; }
uint8_t getLayer() const noexcept { return mLayer; }
bool isDirectionalShadow() const noexcept { return mShadowType == ShadowType::DIRECTIONAL; }
bool isSpotShadow() const noexcept { return mShadowType == ShadowType::SPOT; }
bool isPointShadow() const noexcept { return mShadowType == ShadowType::POINT; }
ShadowType getShadowType() const noexcept { return mShadowType; }
uint8_t getFace() const noexcept { return mFace; }
using Transaction = PerShadowMapUniforms::Transaction;
static void prepareCamera(Transaction const& transaction,
FEngine& engine, const CameraInfo& cameraInfo) noexcept;
static void prepareViewport(Transaction const& transaction,
const filament::Viewport& viewport) noexcept;
static void prepareTime(Transaction const& transaction,
FEngine& engine, math::float4 const& userTime) noexcept;
static void prepareDirectionalLight(Transaction const& transaction,
FEngine& engine, math::float3 const& sceneSpaceDirection,
LightManager::Instance instance) noexcept;
static void prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept;
static PerShadowMapUniforms::Transaction open(backend::DriverApi& driver) noexcept;
void commit(Transaction& transaction,
backend::DriverApi& driver) const noexcept;
void bindPerViewUniformsAndSamplers(backend::DriverApi& driver) const noexcept;
private:
struct Segment {
@@ -245,13 +276,16 @@ private:
static math::mat4f directionalLightFrustum(float n, float f) noexcept;
math::mat4 getTextureCoordsMapping() const noexcept;
static math::mat4 getTextureCoordsMapping(ShadowMapInfo const& info) noexcept;
static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf,
const math::mat4f& Mv, float znear, float zfar) noexcept;
float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture) const noexcept;
float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF) const noexcept;
float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
uint16_t shadowDimension) const noexcept;
float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
uint16_t shadowDimension) const noexcept;
static constexpr const Segment sBoxSegments[12] = {
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
@@ -267,19 +301,20 @@ private:
{ 2, 6, 7, 3 }, // top
};
FCamera* mCamera = nullptr; // 8
FCamera* mDebugCamera = nullptr; // 8
math::mat4f mLightSpace; // 64
float mTexelSizeAtOneMeterWs = 0.0f; // 4
math::float4 mLightFromWorldZ{};
mutable PerShadowMapUniforms mPerShadowMapUniforms; // 4
// set-up in update()
ShadowMapInfo mShadowMapInfo; // 20
bool mHasVisibleShadows = false; // 1
FCamera* mCamera = nullptr; // 8
FCamera* mDebugCamera = nullptr; // 8
FEngine& mEngine; // 8
const bool mClipSpaceFlipped; // 1
const bool mTextureSpaceFlipped; // 1
// The data below technically belongs to ShadowMapManager, but it simplifies allocations
// to store it here. This data is always associated with this shadow map anyway.
LightManager::ShadowOptions const* mOptions = nullptr; // 8
uint32_t mLightIndex = 0; // which light are we shadowing // 4
uint16_t mShadowIndex = 0; // our index in the shadowMap vector // 2
uint8_t mLayer = 0; // our layer in the shadowMap texture // 1
ShadowType mShadowType : 2; // :2
bool mHasVisibleShadows : 2; // :2
uint8_t mFace : 3; // :3
};
} // namespace filament
File diff suppressed because it is too large Load Diff
+49 -55
View File
@@ -27,9 +27,7 @@
#include <private/filament/EngineEnums.h>
#include <private/backend/DriverApi.h>
#include "backend/DriverApiForward.h"
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
@@ -54,6 +52,7 @@ struct ShadowMappingUniforms {
float ssContactShadowDistance;
uint32_t directionalShadows;
uint32_t cascades;
bool elvsm;
};
class ShadowMapManager {
@@ -61,6 +60,8 @@ public:
using ShadowMappingUniforms = ShadowMappingUniforms;
using ShadowType = ShadowMap::ShadowType;
enum class ShadowTechnique : uint8_t {
NONE = 0x0u,
SHADOW_MAP = 0x1u,
@@ -76,18 +77,21 @@ public:
// Reset shadow map layout.
void reset() noexcept;
void setShadowCascades(size_t lightIndex, LightManager::ShadowOptions const* options) noexcept;
void addSpotShadowMap(size_t lightIndex, LightManager::ShadowOptions const* options) noexcept;
void setDirectionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept;
// Updates all of the shadow maps and performs culling.
void addShadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept;
// Updates all the shadow maps and performs culling.
// Returns true if any of the shadow maps have visible shadows.
ShadowMapManager::ShadowTechnique update(FEngine& engine, FView& view,
CameraInfo const& cameraInfo,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept;
// Renders all the shadow maps.
FrameGraphId<FrameGraphTexture> render(FrameGraph& fg, FEngine& engine,
RenderPass const& pass, FView& view) noexcept;
FrameGraphId<FrameGraphTexture> render(FEngine& engine, FrameGraph& fg, RenderPass const& pass,
FView& view, CameraInfo const& mainCameraInfo, math::float4 const& userTime) noexcept;
ShadowMap* getCascadeShadowMap(size_t cascade) noexcept {
assert_invariant(cascade < CONFIG_MAX_SHADOW_CASCADES);
@@ -98,14 +102,14 @@ public:
return const_cast<ShadowMapManager*>(this)->getCascadeShadowMap(cascade);
}
ShadowMap* getSpotShadowMap(size_t spot) noexcept {
assert_invariant(spot < CONFIG_MAX_SHADOW_CASTING_SPOTS);
ShadowMap* getPointOrSpotShadowMap(size_t index) noexcept {
assert_invariant(index < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
return std::launder(reinterpret_cast<ShadowMap*>(
&mShadowMapCache[CONFIG_MAX_SHADOW_CASCADES + spot]));
&mShadowMapCache[CONFIG_MAX_SHADOW_CASCADES + index]));
}
ShadowMap const* getSpotShadowMap(size_t spot) const noexcept {
return const_cast<ShadowMapManager*>(this)->getSpotShadowMap(spot);
ShadowMap const* getPointOrSpotShadowMap(size_t spot) const noexcept {
return const_cast<ShadowMapManager*>(this)->getPointOrSpotShadowMap(spot);
}
// valid after calling update() above
@@ -113,46 +117,37 @@ public:
return mShadowMappingUniforms;
}
auto& getShadowUniforms() const { return mShadowUb; }
auto& getShadowUniformsHandle() const { return mShadowUbh; }
bool hasSpotShadows() const { return !mSpotShadowMaps.empty(); }
private:
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept;
ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine,
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept;
FScene::LightSoa& lightData) noexcept;
void calculateTextureRequirements(FEngine& engine, FView& view, FScene::LightSoa& lightData) noexcept;
void calculateTextureRequirements(FEngine& engine, FView& view,
FScene::LightSoa& lightData) noexcept;
class ShadowMapEntry {
public:
ShadowMapEntry() = default;
ShadowMapEntry(ShadowMap* shadowMap, size_t light,
LightManager::ShadowOptions const* options) :
mShadowMap(shadowMap), mOptions(options), mLightIndex(light) {
}
void prepareSpotShadowMap(ShadowMap& shadowMap,
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
FScene::LightSoa& lightData, ShadowMap::SceneInfo const& sceneInfo) noexcept;
explicit operator bool() const { return mShadowMap != nullptr; }
void preparePointShadowMap(ShadowMap& map,
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
FScene::LightSoa& lightData,
ShadowMap::SceneInfo const& sceneInfo) noexcept;
void setLayer(uint8_t layer) noexcept { mLayer = layer; }
uint8_t getLayer() const noexcept { return mLayer; }
LightManager::ShadowOptions const* getShadowOptions() const noexcept { return mOptions; }
ShadowMap& getShadowMap() const { return *mShadowMap; }
size_t getLightIndex() const { return mLightIndex; }
bool hasVisibleShadows() const { return mShadowMap->hasVisibleShadows(); }
private:
ShadowMap* mShadowMap = nullptr;
LightManager::ShadowOptions const* mOptions = nullptr;
uint32_t mLightIndex = 0;
uint8_t mLayer = 0;
};
static void updateSpotVisibilityMasks(
uint8_t visibleLayers,
uint8_t const* UTILS_RESTRICT layers,
FRenderableManager::Visibility const* UTILS_RESTRICT visibility,
Culler::result_type* UTILS_RESTRICT visibleMask, size_t count);
class CascadeSplits {
public:
@@ -191,12 +186,15 @@ private:
size_t mSplitCount;
};
FEngine& mEngine;
// Atlas requirements, updated in ShadowMapManager::update(),
// consumed in ShadowMapManager::render()
struct TextureAtlasRequirements {
uint16_t size = 0;
uint8_t layers = 0;
uint8_t levels = 0;
backend::TextureFormat format = backend::TextureFormat::DEPTH16;
} mTextureAtlasRequirements;
SoftShadowOptions mSoftShadowOptions;
@@ -204,30 +202,26 @@ private:
CascadeSplits::Params mCascadeSplitParams;
CascadeSplits mCascadeSplits;
// 16-bits seems enough.
// TODO: make it an option.
// TODO: iOS does not support the DEPTH16 texture format.
backend::TextureFormat mTextureFormat = backend::TextureFormat::DEPTH16;
mutable TypedUniformBuffer<ShadowUib> mShadowUb;
backend::Handle<backend::HwBufferObject> mShadowUbh;
ShadowMappingUniforms mShadowMappingUniforms;
ShadowMappingUniforms mShadowMappingUniforms = {};
utils::FixedCapacityVector<ShadowMapEntry> mCascadeShadowMaps{
utils::FixedCapacityVector<ShadowMapEntry>::with_capacity(
ShadowMap::SceneInfo mSceneInfo;
utils::FixedCapacityVector<ShadowMap*> mCascadeShadowMaps{
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
CONFIG_MAX_SHADOW_CASCADES) };
utils::FixedCapacityVector<ShadowMapEntry> mSpotShadowMaps{
utils::FixedCapacityVector<ShadowMapEntry>::with_capacity(
CONFIG_MAX_SHADOW_CASTING_SPOTS) };
utils::FixedCapacityVector<ShadowMap*> mSpotShadowMaps{
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
CONFIG_MAX_SHADOWMAP_PUNCTUAL) };
// inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly.
// because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations.
// Each ShadowMap is currently 128 bytes.
// Each ShadowMap is currently 40 bytes (total of 2.5KB for 64 shadow maps)
using ShadowMapStorage = std::aligned_storage<sizeof(ShadowMap), alignof(ShadowMap)>::type;
std::array<ShadowMapStorage,
CONFIG_MAX_SHADOW_CASCADES + CONFIG_MAX_SHADOW_CASTING_SPOTS> mShadowMapCache;
std::array<ShadowMapStorage, CONFIG_MAX_SHADOW_LAYERS> mShadowMapCache;
};
} // namespace filament
+3 -3
View File
@@ -25,16 +25,16 @@ using namespace math;
void SkinningBuffer::setBones(Engine& engine,
RenderableManager::Bone const* transforms, size_t count, size_t offset) {
upcast(this)->setBones(upcast(engine), transforms, count, offset);
downcast(this)->setBones(downcast(engine), transforms, count, offset);
}
void SkinningBuffer::setBones(Engine& engine,
math::mat4f const* transforms, size_t count, size_t offset) {
upcast(this)->setBones(upcast(engine), transforms, count, offset);
downcast(this)->setBones(downcast(engine), transforms, count, offset);
}
size_t SkinningBuffer::getBoneCount() const noexcept {
return upcast(this)->getBoneCount();
return downcast(this)->getBoneCount();
}
} // namespace filament
+5 -5
View File
@@ -21,23 +21,23 @@
namespace filament {
void Skybox::setLayerMask(uint8_t select, uint8_t values) noexcept {
upcast(this)->setLayerMask(select, values);
downcast(this)->setLayerMask(select, values);
}
uint8_t Skybox::getLayerMask() const noexcept {
return upcast(this)->getLayerMask();
return downcast(this)->getLayerMask();
}
float Skybox::getIntensity() const noexcept {
return upcast(this)->getIntensity();
return downcast(this)->getIntensity();
}
void Skybox::setColor(math::float4 color) noexcept {
upcast(this)->setColor(color);
downcast(this)->setColor(color);
}
Texture const* Skybox::getTexture() const noexcept {
return upcast(this)->getTexture();
return downcast(this)->getTexture();
}
} // namespace filament

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