Compare commits

..
Author SHA1 Message Date
Benjamin Doherty 18dab53920 All changes 2022-11-14 22:20:59 -08:00
Benjamin Doherty b754b81e6f Almost all the changes 2022-11-14 22:16:58 -08:00
Benjamin Doherty 65bb66f90c Even more shader changes 2022-11-14 22:00:10 -08:00
Benjamin Doherty a0af84d034 Some more shader changes 2022-11-14 21:56:50 -08:00
Benjamin Doherty 3cdaf3feeb ShadowMapManager.h changes 2022-11-14 17:18:00 -08:00
Benjamin Doherty da7d1b6fa9 Update Scene.cpp 2022-11-14 17:09:55 -08:00
Benjamin Doherty 3129226839 Fix literal 0 2022-11-14 17:07:55 -08:00
Benjamin Doherty 6935acafff Changes to ShadowMapManager 2022-11-14 17:07:25 -08:00
Benjamin Doherty d8103f4fd6 Update ShaderGenerator.cpp 2022-11-14 16:55:50 -08:00
Benjamin Doherty 09a13f4015 Some shadowing changes 2022-11-14 15:25:08 -08:00
Benjamin Doherty e888023102 Some shader changes 2022-11-14 15:00:09 -08:00
Benjamin Doherty f62f4736f0 Adjust LightsUib 2022-11-14 13:39:08 -08:00
Benjamin Doherty a378272d56 Remove some more uniforms 2022-11-14 13:34:26 -08:00
Benjamin Doherty 3d61938fcf Remove lightFromWorldMatrix from PerViewUniforms 2022-11-14 13:30:54 -08:00
Benjamin Doherty a9f3937da6 Trivial changes 2022-11-14 13:05:52 -08:00
Mathias Agopian 2dc8d6bcc0 minor cleanups, comment fixes
- update comments
- remove unused or unneeded methods
2022-11-08 15:25:33 -08:00
Balazs Vegh 08dc398452 Fix skipped depth bias set on Metal (#6276) 2022-11-07 11:26:53 -08:00
Benjamin Doherty cd180c9a44 Update Python requirements.txt to fix CI 2022-11-04 11:35:10 -07:00
daemyung jang 0a7fe52124 Trim a whitespace (#6272) 2022-11-04 08:36:36 -07:00
Mathias Agopian b71faf47cc fix shadow cascades
breakage was due to an uninitialized variable at the beginning of the
frame, caused by a recent change.

shadows would disappear when cascades were used with 'stable' shadows.
2022-11-03 13:48:09 -07:00
Ben Doherty 67babceb73 Fix backend_test to work with Metal arg buffers (#6135) 2022-11-03 11:04:29 -07:00
Ben Doherty 8e13fc86d5 Expose missing ColorGrading::Builder methods to JS (#6262) 2022-11-03 11:04:06 -07:00
Mathias Agopian 351c8e0972 fix spotlight attenuation
Spotlight attenuation was computed only when shadowing was enabled.
2022-11-03 10:34:39 -07:00
Benjamin Doherty dc44e20be0 Fix RELEASE_NOTES 2022-11-03 09:57:21 -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
238 changed files with 45804 additions and 3699 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.28.1'
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.28.1'
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
+17
View File
@@ -5,6 +5,22 @@ A new header is inserted each time a *tag* is created.
## main branch
- gltfio: calculate primitive's AABB correctly.
- gltfio: recompute bounding boxes with morph targets
- engine: add missing getters on `MaterialInstance`
- WebGL: add missing `ColorGrading` JS bindings
- engine: improvements/cleanup of Shadow mapping code [⚠️ **Recompile Materials**]
## v1.28.3
- backend: add support for GGP platform
- engine: primitives with `CullingMode::FRONT_AND_BACK` are now skipped.
## 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
@@ -35,6 +51,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
@@ -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();
}
@@ -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);
}
+6 -1
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)
@@ -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)
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.28.1
VERSION_NAME=1.28.2
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+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
+10 -10
View File
@@ -1,14 +1,14 @@
setuptools==40.6.2
setuptools==58.0.4
wheel==0.37.1
certifi==2021.10.8
cffi==1.15.0
charset-normalizer==2.0.12
certifi==2022.9.24
cffi==1.15.1
charset-normalizer==2.1.1
Deprecated==1.2.13
idna==3.3
idna==3.4
pycparser==2.21
PyGithub==1.55
PyJWT==2.4.0
PyGithub==1.56
PyJWT==2.6.0
PyNaCl==1.5.0
requests==2.27.1
urllib3==1.26.9
wrapt==1.14.0
requests==2.28.1
urllib3==1.26.12
wrapt==1.14.1
+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;
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+3
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
+1
View File
@@ -405,6 +405,7 @@ if (APPLE)
backend
getopt
gtest
filamat
SPIRV
spirv-cross-glsl)
@@ -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
};
/**
@@ -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
* -----------------------
+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
+11 -7
View File
@@ -34,7 +34,8 @@ 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;
@@ -61,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);
+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];
}
}
}
+5 -1
View File
@@ -64,6 +64,7 @@ struct MetalContext {
// Supported features.
bool supportsTextureSwizzling = false;
bool supportsAutoDepthResolve = false;
bool supportsMemorylessRenderTargets = false;
uint8_t maxColorRenderTargets = 4;
struct {
@@ -84,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,6 +96,8 @@ struct MetalContext {
SamplerStateCache samplerStateCache;
ArgumentEncoderCache argumentEncoderCache;
PolygonOffset currentPolygonOffset = {0.0f, 0.0f};
MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {};
// Keeps track of sampler groups we've finalized for the current render pass.
+3 -3
View File
@@ -39,7 +39,7 @@ 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
@@ -129,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);
};
+190 -30
View File
@@ -92,6 +92,13 @@ 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.
@@ -243,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,
@@ -481,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);
}
@@ -686,7 +703,7 @@ bool MetalDriver::isFrameTimeSupported() {
}
bool MetalDriver::isAutoDepthResolveSupported() {
return true;
return mContext->supportsAutoDepthResolve;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
@@ -988,6 +1005,7 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
mContext->depthStencilState.invalidate();
mContext->cullModeState.invalidate();
mContext->windingState.invalidate();
mContext->currentPolygonOffset = {0.0f, 0.0f};
mContext->finalizedSamplerGroups.clear();
}
@@ -1051,7 +1069,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
@@ -1064,24 +1082,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) {
@@ -1109,8 +1184,12 @@ void MetalDriver::startCapture(int) {
if (@available(iOS 13, *)) {
MTLCaptureDescriptor* descriptor = [MTLCaptureDescriptor new];
descriptor.captureObject = mContext->device;
#if defined(IOS)
descriptor.destination = MTLCaptureDestinationDeveloperTools;
#else
descriptor.destination = MTLCaptureDestinationGPUTraceDocument;
descriptor.outputURL = [[NSURL alloc] initFileURLWithPath:@"filament.gputrace"];
#endif
NSError* error = nil;
[[MTLCaptureManager sharedCaptureManager] startCaptureWithDescriptor:descriptor
error:&error];
@@ -1512,10 +1591,12 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
[mContext->currentRenderPassEncoder setDepthStencilState:state];
}
if (ps.polygonOffset.constant != 0.0 || ps.polygonOffset.slope != 0.0) {
if (ps.polygonOffset.constant != mContext->currentPolygonOffset.constant ||
ps.polygonOffset.slope != mContext->currentPolygonOffset.slope) {
[mContext->currentRenderPassEncoder setDepthBias:ps.polygonOffset.constant
slopeScale:ps.polygonOffset.slope
clamp:0.0];
mContext->currentPolygonOffset = ps.polygonOffset;
}
// Set scissor-rectangle.
@@ -1552,8 +1633,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;
});
@@ -1635,7 +1717,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) {
@@ -1652,17 +1794,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.");
}
}
+6 -2
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;
+24 -11
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;
@@ -1046,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;
@@ -1078,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;
}
}
}
}
+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]);
@@ -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);
-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
@@ -2012,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>;
@@ -263,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;
+13 -37
View File
@@ -22,8 +22,12 @@
#include <spirv_glsl.hpp>
#include <spirv_msl.hpp>
#include "../src/GLSLPostProcessor.h"
#include "builtinResource.h"
#include <utils/FixedCapacityVector.h>
#include <iostream>
#include <utility>
#include <vector>
@@ -59,38 +63,6 @@ void SpvToGlsl(const SpirvBlob* spirv, std::string* outGlsl) {
*outGlsl = glslCompiler.compile();
}
void SpvToMsl(const SpirvBlob* spirv, std::string* outMsl) {
CompilerMSL mslCompiler(*spirv);
mslCompiler.set_msl_options(CompilerMSL::Options {
.msl_version = CompilerMSL::Options::make_msl_version(1, 1)
});
auto executionModel = mslCompiler.get_execution_model();
auto duplicateResourceBinding = [executionModel, &mslCompiler](const auto& resource) {
auto set = mslCompiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
auto binding = mslCompiler.get_decoration(resource.id, spv::DecorationBinding);
MSLResourceBinding newBinding;
newBinding.stage = executionModel;
newBinding.desc_set = set;
newBinding.binding = binding;
newBinding.msl_texture = binding;
newBinding.msl_sampler = binding;
newBinding.msl_buffer = binding;
mslCompiler.add_msl_resource_binding(newBinding);
};
auto resources = mslCompiler.get_shader_resources();
for (const auto& resource : resources.sampled_images) {
duplicateResourceBinding(resource);
}
for (const auto& resource : resources.uniform_buffers) {
duplicateResourceBinding(resource);
}
*outMsl = mslCompiler.compile();
}
} // anonymous namespace
void ShaderGenerator::init() {
@@ -102,15 +74,16 @@ void ShaderGenerator::shutdown() {
}
ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment,
Backend backend, bool isMobile) noexcept
Backend backend, bool isMobile, const filament::SamplerInterfaceBlock* sib) noexcept
: mBackend(backend),
mVertexBlob(transpileShader(ShaderStage::VERTEX, std::move(vertex), backend, isMobile)),
mVertexBlob(transpileShader(ShaderStage::VERTEX, std::move(vertex), backend, isMobile, sib)),
mFragmentBlob(transpileShader(ShaderStage::FRAGMENT, std::move(fragment), backend,
isMobile)) {
isMobile, sib)) {
}
ShaderGenerator::Blob ShaderGenerator::transpileShader(
ShaderStage stage, std::string shader, Backend backend, bool isMobile) noexcept {
ShaderStage stage, std::string shader, Backend backend, bool isMobile,
const filament::SamplerInterfaceBlock* sib) noexcept {
TProgram program;
const EShLanguage language = stage == ShaderStage::VERTEX ? EShLangVertex : EShLangFragment;
TShader tShader(language);
@@ -173,7 +146,10 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(
}
return { result.c_str(), result.c_str() + result.length() + 1 };
} else if (backend == Backend::METAL) {
SpvToMsl(&spirv, &result);
const auto sm = isMobile ? ShaderModel::MOBILE : ShaderModel::DESKTOP;
filamat::SibVector sibs = filamat::SibVector::with_capacity(1);
if (sib) { sibs.emplace_back(0, sib); }
filamat::GLSLPostProcessor::spirvToMsl(&spirv, &result, sm, false, sibs, nullptr);
return { result.c_str(), result.c_str() + result.length() + 1 };
} else if (backend == Backend::VULKAN) {
return { (uint8_t*)spirv.data(), (uint8_t*)(spirv.data() + spirv.size()) };
+4 -2
View File
@@ -19,6 +19,7 @@
#include "PlatformRunner.h"
#include "private/filament/SamplerInterfaceBlock.h"
#include "private/backend/DriverApi.h"
#include "backend/Program.h"
@@ -38,7 +39,8 @@ public:
* @param vertex The vertex shader, written in GLSL 450 core.
* @param fragment The fragment shader, written in GLSL 450 core.
*/
ShaderGenerator(std::string vertex, std::string fragment, Backend backend, bool isMobile) noexcept;
ShaderGenerator(std::string vertex, std::string fragment, Backend backend, bool isMobile,
const filament::SamplerInterfaceBlock* sib = nullptr) noexcept;
ShaderGenerator(const ShaderGenerator& rhs) = delete;
ShaderGenerator& operator=(const ShaderGenerator& rhs) = delete;
@@ -50,7 +52,7 @@ private:
using Blob = std::vector<char>;
static Blob transpileShader(ShaderStage stage, std::string shader, Backend backend,
bool isMobile) noexcept;
bool isMobile, const filament::SamplerInterfaceBlock* sib = nullptr) noexcept;
Backend mBackend;
+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();
+6 -1
View File
@@ -133,7 +133,12 @@ TEST_F(BackendTest, FeedbackLoops) {
// Create a program.
ProgramHandle program;
{
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform);
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
+31 -12
View File
@@ -19,6 +19,7 @@
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "private/filament/SamplerInterfaceBlock.h"
#include "private/backend/SamplerGroup.h"
#include <math/half.h>
@@ -54,7 +55,7 @@ layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform {samplerType} tex;
layout(location = 0, set = 1) uniform {samplerType} test_tex;
void main() {
vec2 fbsize = vec2(512);
@@ -62,7 +63,7 @@ void main() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
uv.y = 1.0 - uv.y;
#endif
fragColor = vec4(texture(tex, uv).rgb, 1.0f);
fragColor = vec4(texture(test_tex, uv).rgb, 1.0f);
}
)");
@@ -72,8 +73,7 @@ std::string fragmentUpdateImage3DTemplate (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform {samplerType} tex;
layout(location = 0, set = 1) uniform {samplerType} test_tex;
float getLayer(in sampler3D s) { return 2.5f / 4.0f; }
float getLayer(in sampler2DArray s) { return 2.0f; }
@@ -84,7 +84,7 @@ void main() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
uv.y = 1.0 - uv.y;
#endif
fragColor = vec4(texture(tex, vec3(uv, getLayer(tex))).rgb, 1.0f);
fragColor = vec4(texture(test_tex, vec3(uv, getLayer(test_tex))).rgb, 1.0f);
}
)");
@@ -94,8 +94,7 @@ std::string fragmentUpdateImageMip (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform sampler2D tex;
layout(location = 0, set = 1) uniform sampler2D test_tex;
void main() {
vec2 fbsize = vec2(512);
@@ -103,7 +102,7 @@ void main() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
uv.y = 1.0 - uv.y;
#endif
fragColor = vec4(textureLod(tex, uv, 1.0f).rgb, 1.0f);
fragColor = vec4(textureLod(test_tex, uv, 1.0f).rgb, 1.0f);
}
)");
@@ -345,10 +344,15 @@ TEST_F(BackendTest, UpdateImage2D) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ProgramHandle program;
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(t.textureFormat), fragmentTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -425,9 +429,14 @@ TEST_F(BackendTest, UpdateImageSRGB) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(textureFormat), fragmentTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -511,9 +520,14 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -583,9 +597,14 @@ TEST_F(BackendTest, UpdateImage3D) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -45,7 +45,7 @@ std::string fragment (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
layout(location = 0) in vec2 uv;
layout(set = 1, binding = 6) uniform sampler2D tex;
layout(location = 0, set = 1) uniform sampler2D tex;
void main() {
fragColor = texture(tex, uv);
@@ -65,11 +65,16 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
auto swapChain = createSwapChain();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
// Create a program that samples a texture.
Program p = shaderGen.getProgram(getDriverApi());
Program::Sampler sampler { utils::CString("tex"), 6 };
Program::Sampler sampler { utils::CString("tex"), 0 };
p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1);
backend::Handle<HwProgram> program = getDriverApi().createProgram(std::move(p));
@@ -139,11 +144,16 @@ TEST_F(BackendTest, RenderExternalImage) {
auto swapChain = createSwapChain();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
// Create a program that samples a texture.
Program p = shaderGen.getProgram(getDriverApi());
Program::Sampler sampler { utils::CString("tex"), 6 };
Program::Sampler sampler { utils::CString("tex"), 0 };
p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1);
auto program = getDriverApi().createProgram(std::move(p));
+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
+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:
@@ -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).
*
+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
+6
View File
@@ -273,4 +273,10 @@ FeatureLevel Engine::getActiveFeatureLevel() const noexcept {
return downcast(this)->getActiveFeatureLevel();
}
#if defined(__EMSCRIPTEN__)
void Engine::resetBackendState() noexcept {
downcast(this)->resetBackendState();
}
#endif
} // namespace filament
+40
View File
@@ -292,4 +292,44 @@ MaterialInstance* MaterialInstance::duplicate(MaterialInstance const* other, con
}
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
+130
View File
@@ -0,0 +1,130 @@
/*
* 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::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
+91
View File
@@ -0,0 +1,91 @@
/*
* 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 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
+17 -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.
@@ -297,14 +290,10 @@ void PerViewUniforms::prepareShadowMapping(bool highPrecision) noexcept {
void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
uniforms.lightFromWorldMatrix = shadowMappingUniforms.lightFromWorldMatrix;
uniforms.cascadeSplits = shadowMappingUniforms.cascadeSplits;
uniforms.shadowBulbRadiusLs = shadowMappingUniforms.shadowBulbRadiusLs;
uniforms.shadowBias = shadowMappingUniforms.shadowBias;
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,
+12 -9
View File
@@ -48,6 +48,11 @@ 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>;
@@ -63,8 +68,8 @@ public:
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;
@@ -95,10 +100,11 @@ public:
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;
};
+7 -7
View File
@@ -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;
@@ -2476,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);
});
}
+58 -56
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;
@@ -542,11 +544,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
*/
for (size_t pi = 0, c = primitives.size(); pi < c; ++pi) {
auto const& primitive = primitives[pi];
// handle the case where this primitive is empty / no-op
if (primitive.getPrimitiveType() == PrimitiveType::NONE) {
continue;
}
auto const& morphTargets = morphing.targets[pi];
FMaterialInstance const* const mi = primitive.getMaterialInstance();
FMaterial const* const ma = mi->getMaterial();
@@ -609,6 +606,9 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
// 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;
@@ -635,6 +635,10 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
}
*curr = cmdColor;
// cancel command if both front and back faces are culled
curr->key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
++curr;
}
@@ -659,11 +663,16 @@ RenderPass::Command* 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;
// cancel command if both front and back faces are culled
curr->key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
++curr;
}
}
@@ -686,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) {
@@ -724,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) {
@@ -737,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;
}
@@ -745,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;
@@ -831,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());
}
+41 -47
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
@@ -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
+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] = {};
};
+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.getVsmShadowOptions().highPrecision);
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
+66 -3
View File
@@ -41,8 +41,10 @@ using namespace backend;
static constexpr bool USE_DEPTH_CLAMP = false;
ShadowMap::ShadowMap(FEngine& engine) noexcept
: mShadowType(ShadowType::DIRECTIONAL),
mHasVisibleShadows(false) {
: mPerShadowMapUniforms(engine),
mShadowType(ShadowType::DIRECTIONAL),
mHasVisibleShadows(false),
mFace(0) {
Entity entities[2];
engine.getEntityManager().create(2, entities);
mCamera = engine.createCamera(entities[0]);
@@ -60,16 +62,19 @@ void ShadowMap::terminate(FEngine& engine) {
engine.destroyCameraComponent(e);
}
engine.getEntityManager().destroy(sizeof(entities) / sizeof(Entity), entities);
mPerShadowMapUniforms.terminate(engine.getDriverApi());
}
ShadowMap::~ShadowMap() = default;
void ShadowMap::initialize(size_t lightIndex, ShadowType shadowType, uint16_t shadowIndex,
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 {
@@ -1179,6 +1184,8 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
void ShadowMap::initSceneInfo(ShadowMap::SceneInfo& sceneInfo,
uint8_t visibleLayers, FScene const& scene, mat4f const& viewMatrix) {
sceneInfo.csNearFar = { -1.0f, 1.0f };
sceneInfo.lsNearFar = {};
sceneInfo.visibleLayers = visibleLayers;
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
@@ -1238,4 +1245,60 @@ void ShadowMap::updateSceneInfoSpot(mat4f const& 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::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::bind(backend::DriverApi& driver) const noexcept {
mPerShadowMapUniforms.bind(driver);
}
} // namespace filament
+31 -8
View File
@@ -19,12 +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 "math/mat4.h"
#include "private/backend/DriverApi.h"
#include <filament/Viewport.h>
#include <math/mat4.h>
#include <math/vec4.h>
@@ -122,7 +124,7 @@ public:
static math::mat4f getPointLightViewMatrix(backend::TextureCubemapFace face,
math::float3 position) noexcept;
void initialize(size_t lightIndex, ShadowType shadowType, uint16_t shadowIndex,
void initialize(size_t lightIndex, ShadowType shadowType, uint16_t shadowIndex, uint8_t face,
LightManager::ShadowOptions const* options);
struct ShaderParameters {
@@ -172,6 +174,8 @@ public:
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; }
@@ -181,6 +185,22 @@ public:
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 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 bind(backend::DriverApi& driver) const noexcept;
private:
struct Segment {
@@ -278,8 +298,10 @@ private:
{ 2, 6, 7, 3 }, // top
};
FCamera* mCamera = nullptr; // 8
FCamera* mDebugCamera = nullptr; // 8
mutable PerShadowMapUniforms mPerShadowMapUniforms; // 4
FCamera* mCamera = nullptr; // 8
FCamera* mDebugCamera = nullptr; // 8
// 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.
@@ -287,8 +309,9 @@ private:
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;
bool mHasVisibleShadows : 2;
ShadowType mShadowType : 2; // :2
bool mHasVisibleShadows : 2; // :2
uint8_t mFace : 3; // :3
};
} // namespace filament
+113 -116
View File
@@ -97,23 +97,39 @@ void ShadowMapManager::setDirectionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept {
assert_invariant(options->shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
for (size_t c = 0; c < options->shadowCascades; c++) {
auto* pShadowMap = getCascadeShadowMap(c);
pShadowMap->initialize(lightIndex, ShadowType::DIRECTIONAL, c, options);
const size_t i = c;
assert_invariant(i < CONFIG_MAX_SHADOW_CASCADES);
auto* pShadowMap = getCascadeShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::DIRECTIONAL, i, 0, options);
mCascadeShadowMaps.push_back(pShadowMap);
}
}
void ShadowMapManager::addShadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept {
const size_t c = mSpotShadowMaps.size();
assert_invariant(c < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
auto* pShadowMap = getPointOrSpotShadowMap(c);
pShadowMap->initialize(lightIndex, spotlight ? ShadowType::SPOT : ShadowType::POINT, c, options);
mSpotShadowMaps.push_back(pShadowMap);
if (spotlight) {
const size_t c = mSpotShadowMaps.size();
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
auto* pShadowMap = getPointOrSpotShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::SPOT, i, 0, options);
mSpotShadowMaps.push_back(pShadowMap);
} else {
// point-light, generate 6 independent shadowmaps
for (size_t face = 0; face < 6; face++) {
const size_t c = mSpotShadowMaps.size();
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
auto* pShadowMap = getPointOrSpotShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::POINT, i, face, options);
mSpotShadowMaps.push_back(pShadowMap);
}
}
}
FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine& engine,
RenderPass const& pass, FView& view, CameraInfo const& mainCameraInfo) noexcept {
FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameGraph& fg,
RenderPass const& pass, FView& view, CameraInfo const& mainCameraInfo,
float4 const& userTime) noexcept {
const float moment2 = std::numeric_limits<half>::max();
const float moment1 = std::sqrt(moment2);
@@ -132,12 +148,10 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
struct PrepareShadowPassData {
struct ShadowPass {
mutable RenderPass pass;
mutable CameraInfo cameraInfo;
mutable RenderPass::Executor executor;
ShadowMap* shadowMap;
utils::Range<uint32_t> range;
FScene::VisibleMaskType visibilityMask;
uint8_t face = 0;
};
// the actual shadow map atlas (currently a 2D texture array)
FrameGraphId<FrameGraphTexture> shadows;
@@ -147,7 +161,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
auto& prepareShadowPass = fg.addPass<PrepareShadowPassData>("Prepare Shadow Pass",
[&](FrameGraph::Builder& builder, auto& data) {
data.passList.reserve(CONFIG_MAX_SHADOW_LAYERS);
data.passList.reserve(CONFIG_MAX_SHADOWMAPS);
data.shadows = builder.createTexture("Shadowmap", {
.width = textureRequirements.size, .height = textureRequirements.size,
.depth = textureRequirements.layers,
@@ -166,7 +180,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
// for the directional light, we already know if it has visible shadows.
if (pShadowMap->hasVisibleShadows()) {
passList.push_back({
pass, {}, pShadowMap, directionalShadowCastersRange,
{}, pShadowMap, directionalShadowCastersRange,
VISIBLE_DIR_SHADOW_RENDERABLE });
}
}
@@ -178,16 +192,8 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
for (auto* pShadowMap : mSpotShadowMaps) {
assert_invariant(!pShadowMap->isDirectionalShadow());
passList.push_back({
pass, {}, pShadowMap, spotShadowCastersRange,
{}, pShadowMap, spotShadowCastersRange,
VISIBLE_DYN_SHADOW_RENDERABLE });
if (pShadowMap->isPointShadow()) {
// add the 5 extra faces
for (uint8_t face = 1; face < 6; face++) {
passList.push_back({
pass, {}, pShadowMap, spotShadowCastersRange,
VISIBLE_DYN_SHADOW_RENDERABLE, face });
}
}
}
}
@@ -197,10 +203,8 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
// "read" from one of its resource (only writes), so the FrameGraph culls it.
builder.sideEffect();
},
[this, &engine, &view, scene, &mainCameraInfo](
[this, &engine, &view, scene, mainCameraInfo, userTime, passTemplate = pass](
FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
// set uniforms needed to render this ShadowMap
view.prepareShadowMap(view.getVsmShadowOptions().highPrecision);
// Note: we could almost parallel_for the loop below, the problem currently is
// that updatePrimitivesLod() updates temporary global state.
@@ -215,7 +219,6 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
// for spot shadow map, we need to do the culling
switch (shadowMap.getShadowType()) {
case ShadowType::DIRECTIONAL:
// nothing to do, it's been done already
break;
case ShadowType::SPOT:
prepareSpotShadowMap(shadowMap, engine, view, mainCameraInfo,
@@ -225,51 +228,60 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
case ShadowType::POINT:
preparePointShadowMap(shadowMap, engine, view, mainCameraInfo,
scene->getRenderableData(), entry.range,
scene->getLightData(), entry.face, mSceneInfo);
scene->getLightData(), mSceneInfo);
break;
}
if (shadowMap.hasVisibleShadows()) {
// TODO: this loop can generate a lot of commands. investigate if
// we could compact or reduce the number of commands.
// - we could move this into the execute of each pass, but
// then we would have to update the UBO each time.
// - we could also always use the same commands for all
// shadow maps, but then we'd generate a lot of
// unneeded draw call.
// Note: this loop can generate a lot of commands that come out of the
// "per frame command arena". The allocation persists until the
// end of the frame.
// One way to possibly mitigate this, would be to always use the
// same command buffer for all shadow map, but then we'd generate
// a lot of unneeded draw calls.
// To do this efficiently, we'd need a way to cull draw calls already
// recorded in the command buffer, per shadow map.
// generate and sort the commands for rendering the shadow map
// cameraInfo only valid after calling update
const CameraInfo cameraInfo{ shadowMap.getCamera() };
auto transaction = ShadowMap::open(driver);
ShadowMap::prepareCamera(transaction, engine, cameraInfo);
ShadowMap::prepareViewport(transaction, shadowMap.getViewport());
ShadowMap::prepareTime(transaction, engine, userTime);
ShadowMap::prepareShadowMapping(transaction,
view.getVsmShadowOptions().highPrecision);
shadowMap.commit(transaction, driver);
// updatePrimitivesLod must be run before RenderPass::appendCommands.
view.updatePrimitivesLod(engine,
cameraInfo, scene->getRenderableData(), entry.range);
// generate and sort the commands for rendering the shadow map
RenderPass pass(passTemplate);
pass.setCamera(cameraInfo);
pass.setVisibilityMask(entry.visibilityMask);
pass.setGeometry(scene->getRenderableData(),
entry.range, scene->getRenderableUBO());
pass.appendCommands(engine, RenderPass::SHADOW);
pass.sortCommands(engine);
entry.executor = pass.getExecutor();
if (!view.hasVSM()) {
auto li = scene->getLightData().elementAt<FScene::LIGHT_INSTANCE>(
shadowMap.getLightIndex());
auto const& params = engine.getLightManager().getShadowParams(li);
auto const* options = shadowMap.getShadowOptions();
const PolygonOffset polygonOffset = { // handle reversed Z
.slope = -params.options.polygonOffsetSlope,
.constant = -params.options.polygonOffsetConstant
.slope = -options->polygonOffsetSlope,
.constant = -options->polygonOffsetConstant
};
entry.pass.overridePolygonOffset(&polygonOffset);
entry.executor.overridePolygonOffset(&polygonOffset);
}
constexpr const backend::Viewport disabledScissor{ 0, 0,
(uint32_t)std::numeric_limits<int32_t>::max(),
(uint32_t)std::numeric_limits<int32_t>::max() };
entry.pass.setCamera(cameraInfo);
entry.pass.setVisibilityMask(entry.visibilityMask);
entry.pass.setGeometry(scene->getRenderableData(),
entry.range, scene->getRenderableUBO());
entry.pass.overrideScissor(&disabledScissor);
entry.pass.appendCommands(RenderPass::SHADOW);
entry.pass.sortCommands();
entry.cameraInfo = cameraInfo;
entry.executor.overrideScissor(&disabledScissor);
}
}
@@ -287,8 +299,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
struct ShadowPassData {
FrameGraphId<FrameGraphTexture> tempBlurSrc{}; // temporary shadowmap when blurring
FrameGraphId<FrameGraphTexture> output;
uint32_t blurRt{};
uint32_t shadowRt{};
uint32_t rt{};
};
auto const& passList = prepareShadowPass.getData().passList;
@@ -297,7 +308,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
continue;
}
const uint8_t layer = entry.shadowMap->getLayer() + entry.face;
const uint8_t layer = entry.shadowMap->getLayer();
const auto* options = entry.shadowMap->getShadowOptions();
auto& shadowPass = fg.addPass<ShadowPassData>("Shadow Pass",
@@ -346,7 +357,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
data.tempBlurSrc = builder.write(data.tempBlurSrc,
FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.blurRt = builder.declareRenderPass("Temp Shadow RT", {
data.rt = builder.declareRenderPass("Temp Shadow RT", {
.attachments = {
.color = { data.tempBlurSrc },
.depth = depth },
@@ -365,53 +376,30 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
}
// finally, create the shadowmap render target -- one per layer.
data.shadowRt = builder.declareRenderPass("Shadow RT", renderTargetDesc);
auto rt = builder.declareRenderPass("Shadow RT", renderTargetDesc);
// render either directly into the shadowmap, or to the temporary texture for
// blurring.
data.rt = blur ? data.rt : rt;
},
[=, &entry, &view](FrameGraphResources const& resources,
[=, &engine, &entry](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
// Note: we capture entry by reference here. That's actually okay because
// `entry` lives in `PrepareShadowPassData` which is guaranteed to still
// be alive when we execute here (all passes stay alive until the FrameGraph
// is destroyed).
// It wouldn't work to capture by copy because entry.pass wouldn't be
// It wouldn't work to capture by copy because entry.executor wouldn't be
// initialized, as this happens in an `execute` block.
CameraInfo const& cameraInfo = entry.cameraInfo;
auto rt = resources.getRenderPassInfo(data.rt);
rt.params.viewport = entry.shadowMap->getViewport();
const auto& executor = entry.pass.getExecutor();
const bool blur = view.hasVSM() && options->vsm.blurWidth > 0.0f;
// set uniforms relative to the camera
view.prepareCamera(cameraInfo);
// 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 = options->mapSize;
filament::Viewport viewport{ 1, 1, dim - 2, dim - 2 };
if (entry.shadowMap->isPointShadow()) {
// for point-light we don't have a border
viewport = { 0, 0, dim, dim };
}
view.prepareViewport(viewport, 0, 0);
view.commitUniforms(driver);
// render either directly into the shadowmap, or to the temporary texture for
// blurring.
auto rt = resources.getRenderPassInfo(blur ? data.blurRt : data.shadowRt);
rt.params.viewport = viewport;
executor.execute("Shadow Pass", rt.target, rt.params);
engine.flush();
driver.beginRenderPass(rt.target, rt.params);
entry.shadowMap->bind(driver);
entry.executor.execute(engine, "Shadow Pass");
driver.endRenderPass();
});
@@ -471,7 +459,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// entire camera frustum, as if we only had a single cascade.
ShadowMap& shadowMap = *mCascadeShadowMaps[0];
auto shaderParameters = shadowMap.updateDirectional(mEngine,
shadowMap.updateDirectional(mEngine,
lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
hasVisibleShadows = shadowMap.hasVisibleShadows();
@@ -480,18 +468,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
VISIBLE_DIR_SHADOW_RENDERABLE_BIT);
// Set shadowBias, using the first directional cascade.
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
// Texel size is constant for directional light (although that's not true when LISPSM
// is used, but in that case we're pretending it is).
const float wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
mShadowMappingUniforms.shadowBias = normalBias * wsTexelSize;
mShadowMappingUniforms.shadowBulbRadiusLs =
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
mShadowMappingUniforms.elvsm = options.vsm.elvsm;
}
}
@@ -544,6 +520,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
for (size_t i = 0, c = mCascadeShadowMaps.size(); i < c; i++) {
assert_invariant(mCascadeShadowMaps[i]);
@@ -557,7 +537,22 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
if (shadowMap.hasVisibleShadows()) {
mShadowMappingUniforms.lightFromWorldMatrix[i] = shaderParameters.lightSpace;
const size_t shadowIndex = shadowMap.getShadowIndex();
assert_invariant(shadowIndex == i);
// Texel size is constant for directional light (although that's not true when LISPSM
// is used, but in that case we're pretending it is).
const float wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
auto& s = mShadowUb.edit();
s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSize;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSize;
s.shadows[shadowIndex].elvsm = options.vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
cascadeHasVisibleShadows |= 0x1u << i;
}
@@ -685,6 +680,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
auto& s = mShadowUb.edit();
const double n = shadowMap.getCamera().getNear();
const double f = shadowMap.getCamera().getCullingFar();
s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
s.shadows[shadowIndex].direction = direction;
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
@@ -695,15 +691,17 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
/ wsTexelSizeAtOneMeter;
}
}
void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
FScene::LightSoa& lightData, uint8_t face,
FScene::LightSoa& lightData,
ShadowMap::SceneInfo const& sceneInfo) noexcept {
const uint8_t face = shadowMap.getFace();
const size_t lightIndex = shadowMap.getLightIndex();
FLightManager::ShadowOptions const* const options = shadowMap.getShadowOptions();
@@ -755,7 +753,6 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
// and if we need to generate it, update all the UBO data
// Note: this below is done for all six faces even if it sets identical values each time
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
@@ -765,7 +762,7 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
auto& s = mShadowUb.edit();
const double n = shadowMap.getCamera().getNear();
const double f = shadowMap.getCamera().getCullingFar();
s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = {}; // no texture matrix for point lights
s.shadows[shadowIndex].direction = {}; // no direction of point lights
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
@@ -794,9 +791,13 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
for (auto const* pShadowMap : mSpotShadowMaps) {
const size_t lightIndex = pShadowMap->getLightIndex();
shadowInfo[lightIndex].castsShadows = true; // FIXME: is that set correctly?
shadowInfo[lightIndex].index = pShadowMap->getShadowIndex();
shadowInfo[lightIndex].layer = pShadowMap->getLayer();
// gather the per-light (not per shadow map) information. For point lights we will
// "see" 6 shadowmaps (one per face), we must use the first face one, the shader
// knows how to find the entry for other faces (they're guaranteed to be sequential).
if (pShadowMap->getFace() == 0) {
shadowInfo[lightIndex].castsShadows = true; // FIXME: is that set correctly?
shadowInfo[lightIndex].index = pShadowMap->getShadowIndex();
}
}
}
@@ -837,10 +838,6 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
maxDimension = std::max(maxDimension, options->mapSize);
elvsm = elvsm || options->vsm.elvsm;
pShadowMap->setLayer(layer++);
if (pShadowMap->isPointShadow()) {
// FIXME: we need to make sure we don't exceed 256
layer += 5;
}
}
const uint8_t layersNeeded = layer;
+9 -13
View File
@@ -45,14 +45,10 @@ class FrameGraph;
class RenderPass;
struct ShadowMappingUniforms {
std::array<math::mat4f, CONFIG_MAX_SHADOW_CASCADES> lightFromWorldMatrix;
math::float4 cascadeSplits;
float shadowBulbRadiusLs;
float shadowBias;
float ssContactShadowDistance;
uint32_t directionalShadows;
uint32_t cascades;
bool elvsm;
};
class ShadowMapManager {
@@ -90,8 +86,8 @@ public:
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept;
// Renders all the shadow maps.
FrameGraphId<FrameGraphTexture> render(FrameGraph& fg, FEngine& engine,
RenderPass const& pass, FView& view, CameraInfo const& mainCameraInfo) 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);
@@ -103,9 +99,9 @@ public:
}
ShadowMap* getPointOrSpotShadowMap(size_t index) noexcept {
assert_invariant(index < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
assert_invariant(index < CONFIG_MAX_SHADOWMAPS);
return std::launder(reinterpret_cast<ShadowMap*>(
&mShadowMapCache[CONFIG_MAX_SHADOW_CASCADES + index]));
&mShadowMapCache[index]));
}
ShadowMap const* getPointOrSpotShadowMap(size_t spot) const noexcept {
@@ -140,7 +136,7 @@ private:
void preparePointShadowMap(ShadowMap& map,
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
FScene::LightSoa& lightData, uint8_t face,
FScene::LightSoa& lightData,
ShadowMap::SceneInfo const& sceneInfo) noexcept;
static void updateSpotVisibilityMasks(
@@ -205,7 +201,7 @@ private:
mutable TypedUniformBuffer<ShadowUib> mShadowUb;
backend::Handle<backend::HwBufferObject> mShadowUbh;
ShadowMappingUniforms mShadowMappingUniforms;
ShadowMappingUniforms mShadowMappingUniforms = {};
ShadowMap::SceneInfo mSceneInfo;
@@ -215,13 +211,13 @@ private:
utils::FixedCapacityVector<ShadowMap*> mSpotShadowMaps{
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
CONFIG_MAX_SHADOWMAP_PUNCTUAL) };
CONFIG_MAX_SHADOWMAPS - CONFIG_MAX_SHADOW_CASCADES) };
// inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly.
// because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations.
// Each ShadowMap is currently 32 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_LAYERS> mShadowMapCache;
std::array<ShadowMapStorage, CONFIG_MAX_SHADOWMAPS> mShadowMapCache;
};
} // namespace filament
+1 -1
View File
@@ -46,7 +46,7 @@ public:
// return if any uniform has been changed
bool isDirty() const noexcept { return mSomethingDirty; }
// mark the whole buffer as clean (no modified uniforms)
// mark the whole buffer as "clean" (no modified uniforms)
void clean() const noexcept { mSomethingDirty = false; }
// helper functions
@@ -747,4 +747,8 @@ bool FRenderableManager::getLightChannel(Instance ci, unsigned int channel) cons
return false;
}
size_t FRenderableManager::getPrimitiveCount(Instance instance, uint8_t level) const noexcept {
return getRenderPrimitives(instance, level).size();
}
} // namespace filament
+1 -5
View File
@@ -160,7 +160,7 @@ public:
}
inline size_t getLevelCount(Instance) const noexcept { return 1; }
inline size_t getPrimitiveCount(Instance instance, uint8_t level) const noexcept;
size_t getPrimitiveCount(Instance instance, uint8_t level) const noexcept;
void setMaterialInstanceAt(Instance instance, uint8_t level,
size_t primitiveIndex, FMaterialInstance const* materialInstance);
MaterialInstance* getMaterialInstanceAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept;
@@ -411,10 +411,6 @@ utils::Slice<FRenderableManager::MorphTargets>& FRenderableManager::getMorphTarg
return mManager[instance].morphTargets;
}
size_t FRenderableManager::getPrimitiveCount(Instance instance, uint8_t level) const noexcept {
return getRenderPrimitives(instance, level).size();
}
} // namespace filament
#endif // TNT_FILAMENT_COMPONENTS_RENDERABLEMANAGER_H
+6
View File
@@ -1072,4 +1072,10 @@ Engine::FeatureLevel FEngine::setActiveFeatureLevel(FeatureLevel featureLevel) {
return (mActiveFeatureLevel = std::max(mActiveFeatureLevel, featureLevel));
}
#if defined(__EMSCRIPTEN__)
void FEngine::resetBackendState() noexcept {
getDriverApi().resetState();
}
#endif
} // namespace filament
+4
View File
@@ -374,6 +374,10 @@ public:
return FEngine::getActiveFeatureLevel() >= neededFeatureLevel;
}
#if defined(__EMSCRIPTEN__)
void resetBackendState() noexcept;
#endif
private:
static Config validateConfig(const Config* pConfig) noexcept;
+55 -5
View File
@@ -35,17 +35,30 @@ namespace filament {
using namespace backend;
FMaterialInstance::FMaterialInstance() noexcept = default;
FMaterialInstance::FMaterialInstance() noexcept
: mCulling(CullingMode::BACK),
mDepthFunc(RasterState::DepthFunc::LE),
mColorWrite(false),
mDepthWrite(false),
mHasScissor(false),
mIsDoubleSided(false),
mTransparencyMode(TransparencyMode::DEFAULT) {
}
FMaterialInstance::FMaterialInstance(FEngine& engine,
FMaterialInstance const* other, const char* name)
: mMaterial(other->mMaterial),
mPolygonOffset(other->mPolygonOffset),
mStencilState(other->mStencilState),
mMaskThreshold(other->mMaskThreshold),
mSpecularAntiAliasingVariance(other->mSpecularAntiAliasingVariance),
mSpecularAntiAliasingThreshold(other->mSpecularAntiAliasingThreshold),
mCulling(other->mCulling),
mDepthFunc(other->mDepthFunc),
mColorWrite(other->mColorWrite),
mDepthWrite(other->mDepthWrite),
mStencilState(other->mStencilState),
mDepthFunc(other->mDepthFunc),
mHasScissor(false),
mIsDoubleSided(other->mIsDoubleSided),
mScissorRect(other->mScissorRect),
mName(name ? CString(name) : other->mName) {
@@ -63,10 +76,23 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mSbHandle = driver.createSamplerGroup(mSamplers.getSize());
}
mMaterialSortingKey = RenderPass::makeMaterialSortingKey(
material->getId(), material->generateMaterialInstanceId());
if (material->hasDoubleSidedCapability()) {
setDoubleSided(mIsDoubleSided);
}
if (material->getBlendingMode() == BlendingMode::MASKED) {
setMaskThreshold(mMaskThreshold);
}
if (material->hasSpecularAntiAliasing()) {
setSpecularAntiAliasingThreshold(mSpecularAntiAliasingThreshold);
setSpecularAntiAliasingVariance(mSpecularAntiAliasingVariance);
}
setTransparencyMode(material->getTransparencyMode());
mMaterialSortingKey = RenderPass::makeMaterialSortingKey(
material->getId(), material->generateMaterialInstanceId());
}
FMaterialInstance* FMaterialInstance::duplicate(
@@ -183,14 +209,29 @@ void FMaterialInstance::setParameterImpl(std::string_view name,
void FMaterialInstance::setMaskThreshold(float threshold) noexcept {
setParameter("_maskThreshold", math::saturate(threshold));
mMaskThreshold = math::saturate(threshold);
}
float FMaterialInstance::getMaskThreshold() const noexcept {
return mMaskThreshold;
}
void FMaterialInstance::setSpecularAntiAliasingVariance(float variance) noexcept {
setParameter("_specularAntiAliasingVariance", math::saturate(variance));
mSpecularAntiAliasingVariance = math::saturate(variance);
}
float FMaterialInstance::getSpecularAntiAliasingVariance() const noexcept {
return mSpecularAntiAliasingVariance;
}
void FMaterialInstance::setSpecularAntiAliasingThreshold(float threshold) noexcept {
setParameter("_specularAntiAliasingThreshold", math::saturate(threshold * threshold));
mSpecularAntiAliasingThreshold = std::sqrt(math::saturate(threshold * threshold));
}
float FMaterialInstance::getSpecularAntiAliasingThreshold() const noexcept {
return mSpecularAntiAliasingThreshold;
}
void FMaterialInstance::setDoubleSided(bool doubleSided) noexcept {
@@ -202,6 +243,11 @@ void FMaterialInstance::setDoubleSided(bool doubleSided) noexcept {
if (doubleSided) {
setCullingMode(CullingMode::NONE);
}
mIsDoubleSided = doubleSided;
}
bool FMaterialInstance::isDoubleSided() const noexcept {
return mIsDoubleSided;
}
void FMaterialInstance::setTransparencyMode(TransparencyMode mode) noexcept {
@@ -212,6 +258,10 @@ void FMaterialInstance::setDepthCulling(bool enable) noexcept {
mDepthFunc = enable ? RasterState::DepthFunc::GE : RasterState::DepthFunc::A;
}
bool FMaterialInstance::isDepthCullingEnabled() const noexcept {
return mDepthFunc != RasterState::DepthFunc::A;
}
const char* FMaterialInstance::getName() const noexcept {
// To decide whether to use the parent material name as a fallback, we check for the nullness of
// the instance's CString rather than calling empty(). This allows instances to override the
+30 -11
View File
@@ -89,9 +89,11 @@ public:
backend::CullingMode getCullingMode() const noexcept { return mCulling; }
bool getColorWrite() const noexcept { return mColorWrite; }
bool isColorWriteEnabled() const noexcept { return mColorWrite; }
bool getDepthWrite() const noexcept { return mDepthWrite; }
bool isDepthWriteEnabled() const noexcept { return mDepthWrite; }
bool isStencilWriteEnabled() const noexcept { return mStencilState.stencilWrite; }
backend::StencilState getStencilState() const noexcept { return mStencilState; }
@@ -108,12 +110,20 @@ public:
void setMaskThreshold(float threshold) noexcept;
float getMaskThreshold() const noexcept;
void setSpecularAntiAliasingVariance(float variance) noexcept;
float getSpecularAntiAliasingVariance() const noexcept;
void setSpecularAntiAliasingThreshold(float threshold) noexcept;
float getSpecularAntiAliasingThreshold() const noexcept;
void setDoubleSided(bool doubleSided) noexcept;
bool isDoubleSided() const noexcept;
void setTransparencyMode(TransparencyMode mode) noexcept;
void setCullingMode(CullingMode culling) noexcept { mCulling = culling; }
@@ -126,6 +136,8 @@ public:
void setDepthCulling(bool enable) noexcept;
bool isDepthCullingEnabled() const noexcept;
void setStencilCompareFunction(StencilCompareFunc func, StencilFace face) noexcept {
if (any(face & StencilFace::FRONT)) {
mStencilState.front.stencilFunc = func;
@@ -222,19 +234,26 @@ private:
// keep these grouped, they're accessed together in the render-loop
FMaterial const* mMaterial = nullptr;
backend::Handle<backend::HwBufferObject> mUbHandle;
backend::Handle<backend::HwSamplerGroup> mSbHandle;
UniformBuffer mUniforms;
backend::SamplerGroup mSamplers;
backend::PolygonOffset mPolygonOffset;
backend::CullingMode mCulling;
bool mColorWrite;
bool mDepthWrite;
bool mHasScissor = false;
backend::StencilState mStencilState = {};
backend::RasterState::DepthFunc mDepthFunc;
TransparencyMode mTransparencyMode;
backend::PolygonOffset mPolygonOffset{};
backend::StencilState mStencilState{};
float mMaskThreshold = 0.0f;
float mSpecularAntiAliasingVariance = 0.0f;
float mSpecularAntiAliasingThreshold = 0.0f;
backend::CullingMode mCulling : 2;
backend::RasterState::DepthFunc mDepthFunc : 3;
bool mColorWrite : 1;
bool mDepthWrite : 1;
bool mHasScissor : 1;
bool mIsDoubleSided : 1;
TransparencyMode mTransparencyMode : 2;
uint64_t mMaterialSortingKey = 0;
+10 -7
View File
@@ -656,7 +656,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
RenderPass shadowPass(pass);
shadowPass.setVariant(shadowVariant);
auto shadows = view.renderShadowMaps(fg, engine, cameraInfo, shadowPass);
auto shadows = view.renderShadowMaps(engine, fg, cameraInfo, mShaderUserTime, shadowPass);
blackboard["shadows"] = shadows;
}
@@ -740,8 +740,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// view set-ups that need to happen before rendering
fg.addTrivialSideEffectPass("Prepare View Uniforms",
[=, &uniforms = view.getPerViewUniforms()](DriverApi& driver) {
uniforms.prepareCamera(cameraInfo);
[=, &view, &engine](DriverApi& driver) {
view.prepareCamera(engine, cameraInfo);
// The code here is a little fragile. In theory, we need to call prepareViewport()
// for each render pass, because the viewport parameters depend on the resolution.
@@ -761,11 +761,14 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// The reason why this bug is acceptable is that the viewport parameters are
// currently only used for generating noise, so it's not too bad.
uniforms.prepareViewport(svp,
view.prepareViewport(svp,
xvp.left * aoOptions.resolution,
xvp.bottom * aoOptions.resolution);
uniforms.commit(driver);
view.commitUniforms(driver);
// set uniforms and samplers for the color passes
view.bindPerViewUniformsAndSamplers(driver);
});
// --------------------------------------------------------------------------------------------
@@ -850,8 +853,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// This one doesn't need to be a FrameGraph pass because it always happens by construction
// (i.e. it won't be culled, unless everything is culled), so no need to complexify things.
pass.setVariant(variant);
pass.appendCommands(RenderPass::COLOR);
pass.sortCommands();
pass.appendCommands(engine, RenderPass::COLOR);
pass.sortCommands(engine);
FrameGraphTexture::Descriptor desc = {
.width = config.width,
+4 -3
View File
@@ -343,9 +343,10 @@ void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootAren
lp[gpuIndex].typeShadow = LightsUib::packTypeShadow(
lcm.isPointLight(li) ? 0u : 1u,
shadowInfo[i].contactShadows,
shadowInfo[i].index,
shadowInfo[i].layer);
lp[gpuIndex].channels = LightsUib::packChannels(lcm.getLightChannels(li), shadowInfo[i].castsShadows);
shadowInfo[i].index);
lp[gpuIndex].channels = LightsUib::packChannels(
lcm.getLightChannels(li),
shadowInfo[i].castsShadows);
}
driver.updateBufferObject(lightUbh, { lp, positionalLightCount * sizeof(LightsUib) }, 0);
+2 -2
View File
@@ -155,7 +155,6 @@ public:
bool castsShadows = false; // whether this light casts shadows
bool contactShadows = false; // whether this light casts contact shadows
uint8_t index = 0; // an index into the arrays in the Shadows uniform buffer
uint8_t layer = 0; // which layer of the shadow texture array to sample from
};
enum {
@@ -179,7 +178,8 @@ public:
LightSoa const& getLightData() const noexcept { return mLightData; }
LightSoa& getLightData() noexcept { return mLightData; }
void updateUBOs(utils::Range<uint32_t> visibleRenderables, backend::Handle<backend::HwBufferObject> renderableUbh) noexcept;
void updateUBOs(utils::Range<uint32_t> visibleRenderables,
backend::Handle<backend::HwBufferObject> renderableUbh) noexcept;
bool hasContactShadows() const noexcept;
+31 -25
View File
@@ -294,10 +294,11 @@ void FView::prepareShadowing(FEngine& engine, DriverApi& driver,
}
// Find all shadow-casting spotlights.
size_t shadowLayerCount = 0;
size_t shadowMapCount = 0;
// We allow a max of CONFIG_MAX_SHADOW_CASTING_SPOTS spotlight shadows. Any additional
// We allow a max of CONFIG_MAX_SHADOWMAPS point/spotlight shadows. Any additional
// shadow-casting spotlights are ignored.
// Note that pointlight shadows cost 6 shadowmaps, reducing the total count.
for (size_t l = FScene::DIRECTIONAL_LIGHTS_COUNT; l < lightData.size(); l++) {
// when we get here all the lights should be visible
@@ -317,12 +318,12 @@ void FView::prepareShadowing(FEngine& engine, DriverApi& driver,
mShadowMapManager.addShadowMap(l, lcm.isSpotLight(li), &shadowOptions);
if (lcm.isSpotLight(li)) {
shadowLayerCount += 6;
shadowMapCount += 6;
} else {
shadowLayerCount += 1;
shadowMapCount += 1;
}
if (shadowLayerCount > CONFIG_MAX_SHADOWMAP_PUNCTUAL - 1) {
if (shadowMapCount > CONFIG_MAX_SHADOWMAPS - 1) {
break; // we ran out of spotlight shadow casting
}
}
@@ -383,7 +384,7 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc
intensity = skybox ? skybox->getIntensity() : FIndirectLight::DEFAULT_INTENSITY;
}
mPerViewUniforms.prepareAmbientLight(*ibl, intensity, exposure);
mPerViewUniforms.prepareAmbientLight(engine, *ibl, intensity, exposure);
/*
* Directional light (always at index 0)
@@ -391,7 +392,7 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc
FLightManager::Instance directionalLight = lightData.elementAt<FScene::LIGHT_INSTANCE>(0);
const float3 sceneSpaceDirection = lightData.elementAt<FScene::DIRECTION>(0); // guaranteed normalized
mPerViewUniforms.prepareDirectionalLight(exposure, sceneSpaceDirection, directionalLight);
mPerViewUniforms.prepareDirectionalLight(engine, exposure, sceneSpaceDirection, directionalLight);
mHasDirectionalLight = directionalLight.isValid();
}
@@ -602,13 +603,23 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
* Update driver state
*/
mPerViewUniforms.prepareTime(userTime);
mPerViewUniforms.prepareTime(engine, userTime);
mPerViewUniforms.prepareFog(cameraInfo.getPosition(), mFogOptions);
mPerViewUniforms.prepareTemporalNoise(mTemporalAntiAliasingOptions);
mPerViewUniforms.prepareTemporalNoise(engine, mTemporalAntiAliasingOptions);
mPerViewUniforms.prepareBlending(needsAlphaChannel);
}
// set uniforms and samplers
bindPerViewUniformsAndSamplers(driver);
void FView::bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept {
mPerViewUniforms.bind(driver);
driver.bindUniformBuffer(+UniformBindingPoints::LIGHTS,
mLightUbh);
driver.bindUniformBuffer(+UniformBindingPoints::SHADOW,
mShadowMapManager.getShadowUniformsHandle());
driver.bindUniformBuffer(+UniformBindingPoints::FROXEL_RECORDS,
mFroxelizer.getRecordBuffer());
}
void FView::computeVisibilityMasks(
@@ -656,12 +667,14 @@ UTILS_NOINLINE
void FView::prepareUpscaler(float2 scale) const noexcept {
SYSTRACE_CALL();
mPerViewUniforms.prepareUpscaler(scale, mDynamicResolution);
const float bias = (mDynamicResolution.quality >= QualityLevel::HIGH) ?
std::log2(std::min(scale.x, scale.y)) : 0.0f;
mPerViewUniforms.prepareLodBias(bias);
}
void FView::prepareCamera(const CameraInfo& cameraInfo) const noexcept {
void FView::prepareCamera(FEngine& engine, const CameraInfo& cameraInfo) const noexcept {
SYSTRACE_CALL();
mPerViewUniforms.prepareCamera(cameraInfo);
mPerViewUniforms.prepareCamera(engine, cameraInfo);
}
void FView::prepareViewport(const filament::Viewport& viewport,
@@ -681,13 +694,6 @@ void FView::prepareSSR(Handle<HwTexture> ssr, float refractionLodOffset,
mPerViewUniforms.prepareSSR(ssr, refractionLodOffset, ssrOptions);
}
void FView::prepareHistorySSR(Handle<HwTexture> ssr,
mat4f const& historyProjection, mat4f const& uvFromViewMatrix,
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept {
mPerViewUniforms.prepareHistorySSR(ssr,
historyProjection, uvFromViewMatrix, ssrOptions);
}
void FView::prepareStructure(Handle<HwTexture> structure) const noexcept {
// sampler must be NEAREST
mPerViewUniforms.prepareStructure(structure);
@@ -711,7 +717,7 @@ void FView::prepareShadow(Handle<HwTexture> texture) const noexcept {
}
}
void FView::prepareShadowMap(bool highPrecision) const noexcept {
void FView::prepareShadowMapping(bool highPrecision) const noexcept {
mPerViewUniforms.prepareShadowMapping(highPrecision);
}
@@ -894,9 +900,9 @@ void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&,
}
}
FrameGraphId<FrameGraphTexture> FView::renderShadowMaps(FrameGraph& fg, FEngine& engine,
CameraInfo const& cameraInfo, RenderPass const& pass) noexcept {
return mShadowMapManager.render(fg, engine, pass, *this, cameraInfo);
FrameGraphId<FrameGraphTexture> FView::renderShadowMaps(FEngine& engine, FrameGraph& fg,
CameraInfo const& cameraInfo, float4 const& userTime, RenderPass const& pass) noexcept {
return mShadowMapManager.render(engine, fg, pass, *this, cameraInfo, userTime);
}
void FView::commitFrameHistory(FEngine& engine) noexcept {
+7 -14
View File
@@ -92,6 +92,8 @@ public:
filament::Viewport const& viewport, CameraInfo const& cameraInfo,
math::float4 const& userTime, bool needsAlphaChannel) noexcept;
void bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept;
void setScene(FScene* scene) { mScene = scene; }
FScene const* getScene() const noexcept { return mScene; }
FScene* getScene() noexcept { return mScene; }
@@ -132,7 +134,7 @@ public:
}
void prepareUpscaler(math::float2 scale) const noexcept;
void prepareCamera(const CameraInfo& cameraInfo) const noexcept;
void prepareCamera(FEngine& engine, const CameraInfo& cameraInfo) const noexcept;
void prepareViewport(const Viewport& viewport, uint32_t xoffset, uint32_t yoffset) const noexcept;
void prepareShadowing(FEngine& engine, backend::DriverApi& driver,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
@@ -143,12 +145,9 @@ public:
void prepareSSAO(backend::Handle<backend::HwTexture> ssao) const noexcept;
void prepareSSR(backend::Handle<backend::HwTexture> ssr, float refractionLodOffset,
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept;
void prepareHistorySSR(backend::Handle<backend::HwTexture> ssr,
math::mat4f const& historyProjection, math::mat4f const& uvFromViewMatrix,
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept;
void prepareStructure(backend::Handle<backend::HwTexture> structure) const noexcept;
void prepareShadow(backend::Handle<backend::HwTexture> structure) const noexcept;
void prepareShadowMap(bool highPrecision) const noexcept;
void prepareShadowMapping(bool highPrecision) const noexcept;
void cleanupRenderPasses() const noexcept;
void froxelize(FEngine& engine, math::mat4f const& viewMatrix) const noexcept;
@@ -165,8 +164,9 @@ public:
bool hasPCSS() const noexcept { return mShadowType == ShadowType::PCSS; }
bool hasPicking() const noexcept { return mActivePickingQueriesList != nullptr; }
FrameGraphId<FrameGraphTexture> renderShadowMaps(FrameGraph& fg, FEngine& engine,
CameraInfo const& cameraInfo, RenderPass const& pass) noexcept;
FrameGraphId<FrameGraphTexture> renderShadowMaps(FEngine& engine, FrameGraph& fg,
CameraInfo const& cameraInfo, math::float4 const& userTime,
RenderPass const& pass) noexcept;
void updatePrimitivesLod(
FEngine& engine, const CameraInfo& camera,
@@ -451,13 +451,6 @@ private:
Culler::result_type* visibleMask,
size_t count);
void bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept {
mPerViewUniforms.bind(driver);
driver.bindUniformBuffer(+UniformBindingPoints::LIGHTS, mLightUbh);
driver.bindUniformBuffer(+UniformBindingPoints::SHADOW, mShadowMapManager.getShadowUniformsHandle());
driver.bindUniformBuffer(+UniformBindingPoints::FROXEL_RECORDS, mFroxelizer.getRecordBuffer());
}
// Clean-up the whole history, free all resources. This is typically called when the View is
// being terminated.
void drainFrameHistory(FEngine& engine) noexcept;
+1
View File
@@ -42,6 +42,7 @@ list(APPEND RESGEN_SOURCE ${DUMMY_SRC})
# away in Release builds
if (TNT_DEV)
add_executable(test_${TARGET}
filament_AtlasAllocator_test.cpp
filament_test_exposure.cpp
filament_rendering_test.cpp
filament_framegraph_test.cpp
@@ -0,0 +1,149 @@
/*
* 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 <gtest/gtest.h>
#include "AtlasAllocator.h"
using namespace filament;
TEST(AtlasAllocator, AllocateFirstLevel) {
AtlasAllocator allocator;
auto a = allocator.allocateInLayer(0);
EXPECT_TRUE(a.l == 0 && a.code == 0);
auto b = allocator.allocateInLayer(1);
EXPECT_TRUE(b.l < 0);
auto c = allocator.allocateInLayer(2);
EXPECT_TRUE(c.l < 0);
auto d = allocator.allocateInLayer(5);
EXPECT_TRUE(d.l < 0);
}
TEST(AtlasAllocator, AllocateSecondLevel) {
AtlasAllocator allocator;
auto d0 = allocator.allocateInLayer(1);
EXPECT_TRUE(d0.l == 1 && d0.code == 0);
auto d1 = allocator.allocateInLayer(1);
EXPECT_TRUE(d1.l == 1 && d1.code == 1);
auto d2 = allocator.allocateInLayer(1);
EXPECT_TRUE(d2.l == 1 && d2.code == 2);
auto d3 = allocator.allocateInLayer(1);
EXPECT_TRUE(d3.l == 1 && d3.code == 3);
}
TEST(AtlasAllocator, AllocateMixed0) {
AtlasAllocator allocator;
auto e0 = allocator.allocateInLayer(1);
EXPECT_TRUE(e0.l == 1 && e0.code == 0);
auto e1 = allocator.allocateInLayer(1);
EXPECT_TRUE(e1.l == 1 && e1.code == 1);
auto e2 = allocator.allocateInLayer(1);
EXPECT_TRUE(e2.l == 1 && e2.code == 2);
auto e3 = allocator.allocateInLayer(2);
EXPECT_TRUE(e3.l == 2 && e3.code == 12);
auto e4 = allocator.allocateInLayer(1);
EXPECT_TRUE(e4.l < 0);
}
TEST(AtlasAllocator, AllocateMixed1) {
AtlasAllocator allocator;
auto e0 = allocator.allocateInLayer(1);
EXPECT_TRUE(e0.l == 1 && e0.code == 0);
auto e1 = allocator.allocateInLayer(1);
EXPECT_TRUE(e1.l == 1 && e1.code == 1);
auto e2 = allocator.allocateInLayer(2);
EXPECT_TRUE(e2.l == 2 && e2.code == 8);
auto e3 = allocator.allocateInLayer(1);
EXPECT_TRUE(e3.l == 1 && e3.code == 3);
}
TEST(AtlasAllocator, AllocateMixed2) {
AtlasAllocator allocator;
auto e0 = allocator.allocateInLayer(1);
EXPECT_TRUE(e0.l == 1 && e0.code == 0);
auto e1 = allocator.allocateInLayer(1);
EXPECT_TRUE(e1.l == 1 && e1.code == 1);
auto c0 = allocator.allocateInLayer(2);
EXPECT_TRUE(c0.l == 2 && c0.code == 8);
auto c1 = allocator.allocateInLayer(2);
EXPECT_TRUE(c1.l == 2 && c1.code == 9);
auto c2 = allocator.allocateInLayer(2);
EXPECT_TRUE(c2.l == 2 && c2.code == 10);
auto c3 = allocator.allocateInLayer(2);
EXPECT_TRUE(c3.l == 2 && c3.code == 11);
auto c4 = allocator.allocateInLayer(2);
EXPECT_TRUE(c4.l == 2 && c4.code == 12);
auto c5 = allocator.allocateInLayer(2);
EXPECT_TRUE(c5.l == 2 && c5.code == 13);
auto c6 = allocator.allocateInLayer(2);
EXPECT_TRUE(c6.l == 2 && c6.code == 14);
auto c7 = allocator.allocateInLayer(2);
EXPECT_TRUE(c7.l == 2 && c7.code == 15);
}
TEST(AtlasAllocator, AllocateBySize) {
AtlasAllocator allocator(256);
Viewport vp(0,0,256,256);
auto vp0 = allocator.allocate(256);
EXPECT_EQ(vp0, vp);
auto vp1 = allocator.allocate(128);
EXPECT_TRUE(vp1.empty());
}
TEST(AtlasAllocator, AllocateBySizeOneOfEach) {
AtlasAllocator allocator(256);
Viewport r0(0,0,128,128);
Viewport r1(128,0,64,64);
Viewport r2(192,0,32,32);
auto vp0 = allocator.allocate(128);
auto vp1 = allocator.allocate(64);
auto vp2 = allocator.allocate(32);
auto vp3 = allocator.allocate(16);
EXPECT_EQ(vp0, r0);
EXPECT_EQ(vp1, r1);
EXPECT_EQ(vp2, r2);
EXPECT_TRUE(vp3.empty());
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.28.1"
spec.version = "1.28.2"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.28.1/filament-v1.28.1-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.28.2/filament-v1.28.2-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:backend-test.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:backend-test.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -123,7 +123,7 @@
};
};
buildConfigurationList = 90327DE42AE5DF12C4BD2113 /* Build configuration list for PBXProject "gltf-viewer" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -175,7 +175,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "#/usr/bin/env/bash\n\nset -e\n\n# Compile resources.\n\n# The gltf-viewer app requires two resources:\n# 1. The IBL image\n# 2. The skybox image\n\nHOST_TOOLS_PATH=\"${HOST_TOOLS_PATH:-../../../out/release/filament/bin}\"\n\ncmgen_path=`find ${HOST_TOOLS_PATH} -name cmgen -type f | head -n 1`\n\n# Ensure that the required tools are present in the out/ directory.\n# These can be built by running ./build.sh -p desktop -i release at Filament's root directory.\n\nif [[ ! -e \"${cmgen_path}\" ]]; then\n echo \"No cmgen binary could be found in ${HOST_TOOLS_PATH}.\"\n echo \"Ensure Filament has been built/installed before building this app.\"\n exit 1\nfi\n\n# cmgen consumes an HDR environment map and generates two mipmapped KTX files (IBL and skybox)\n\"${cmgen_path}\" \\\n --deploy=\"${PROJECT_DIR}/generated/default_env\" \\\n --format=ktx --size=256 --extract-blur=0.1 \\\n \"${PROJECT_DIR}/../../../third_party/environments/lightroom_14b.hdr\"\n";
shellScript = "#/usr/bin/env/bash\n\nset -e\n\n# Compile resources.\n\n# The gltf-viewer app requires two resources:\n# 1. The IBL image\n# 2. The skybox image\n\nHOST_TOOLS_PATH=\"${HOST_TOOLS_PATH:-../../../out/release/filament/bin}\"\n\ncmgen_path=`find ${HOST_TOOLS_PATH} -name cmgen -type f | head -n 1`\n\n# Ensure that the required tools are present in the out/ directory.\n# These can be built by running ./build.sh -p desktop -i release at Filament's root directory.\n\nif [[ ! -e \"${cmgen_path}\" ]]; then\n echo \"No cmgen binary could be found in ${HOST_TOOLS_PATH}.\"\n echo \"Ensure Filament has been built/installed before building this app.\"\n exit 1\nfi\n\n# cmgen consumes an HDR environment map and generates two mipmapped KTX files (IBL and skybox)\n\"${cmgen_path}\" \\\n --quiet \\\n --deploy=\"${PROJECT_DIR}/generated/default_env\" \\\n --format=ktx --size=256 --extract-blur=0.1 \\\n \"${PROJECT_DIR}/../../../third_party/environments/lightroom_14b.hdr\"\n";
};
/* End PBXShellScriptBuildPhase section */
@@ -429,6 +429,7 @@
"-lzstd",
"-lcamutils",
"-ldracodec",
"-lmeshoptimizer",
"-lviewer",
"-lcivetweb",
"-lktxreader",
@@ -485,6 +486,7 @@
"-lzstd",
"-lcamutils",
"-ldracodec",
"-lmeshoptimizer",
"-lviewer",
"-lcivetweb",
"-lktxreader",
@@ -603,6 +605,7 @@
"-lzstd",
"-lcamutils",
"-ldracodec",
"-lmeshoptimizer",
"-lviewer",
"-lcivetweb",
"-lktxreader",
@@ -659,6 +662,7 @@
"-lzstd",
"-lcamutils",
"-ldracodec",
"-lmeshoptimizer",
"-lviewer",
"-lcivetweb",
"-lktxreader",
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:gltf-viewer.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:gltf-viewer.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
+2 -1
View File
@@ -20,7 +20,8 @@ targets:
settings:
base:
OTHER_LDFLAGS: ["-lgltfio_core", "-luberarchive", "-limage", "-lgeometry", "-lstb", "-lbasis_transcoder",
"-lzstd", "-lcamutils", "-ldracodec", "-lviewer", "-lcivetweb", "-lktxreader", "-luberzlib"]
"-lzstd", "-lcamutils", "-ldracodec", "-lmeshoptimizer", "-lviewer", "-lcivetweb", "-lktxreader",
"-luberzlib"]
preBuildScripts:
- path: build-resources.sh
name: Build Resources
@@ -165,7 +165,7 @@
};
};
buildConfigurationList = 281E763643B2F1F3B903864B /* Build configuration list for PBXProject "hello-ar" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-ar.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-ar.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -138,7 +138,7 @@
};
};
buildConfigurationList = C7514DDCFDA3C5F803E239F9 /* Build configuration list for PBXProject "hello-gltf" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -328,6 +328,8 @@
"-limage",
"-lgeometry",
"-ldracodec",
"-lmeshoptimizer",
"-lbasis_transcoder",
"-lktxreader",
"-lstb",
"-lzstd",
@@ -380,6 +382,8 @@
"-limage",
"-lgeometry",
"-ldracodec",
"-lmeshoptimizer",
"-lbasis_transcoder",
"-lktxreader",
"-lstb",
"-lzstd",
@@ -604,6 +608,8 @@
"-limage",
"-lgeometry",
"-ldracodec",
"-lmeshoptimizer",
"-lbasis_transcoder",
"-lktxreader",
"-lstb",
"-lzstd",
@@ -656,6 +662,8 @@
"-limage",
"-lgeometry",
"-ldracodec",
"-lmeshoptimizer",
"-lbasis_transcoder",
"-lktxreader",
"-lstb",
"-lzstd",
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-gltf.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-gltf.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -23,6 +23,7 @@
#include <ktxreader/Ktx1Reader.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/ResourceLoader.h>
#include <gltfio/TextureProvider.h>
#include <gltfio/materials/uberarchive.h>
#include <fstream>
@@ -139,10 +140,21 @@ void App::setupMesh() {
}
app.asset = app.assetLoader->createAsset(buffer.data(), static_cast<uint32_t>(size));
filament::gltfio::ResourceLoader({
auto resourceLoader = new filament::gltfio::ResourceLoader({
.engine = engine,
.normalizeSkinningWeights = true
}).loadResources(app.asset);
});
auto stbDecoder = filament::gltfio::createStbProvider(engine);
auto ktxDecoder = filament::gltfio::createKtx2Provider(engine);
resourceLoader->addTextureProvider("image/png", stbDecoder);
resourceLoader->addTextureProvider("image/jpeg", stbDecoder);
resourceLoader->addTextureProvider("image/ktx2", ktxDecoder);
resourceLoader->loadResources(app.asset);
delete resourceLoader;
delete stbDecoder;
delete ktxDecoder;
scene->addEntities(app.asset->getEntities(), app.asset->getEntityCount());
}
+2 -1
View File
@@ -12,7 +12,8 @@ targets:
- FilamentApp
settings:
base:
OTHER_LDFLAGS: ["-lgltfio_core", "-luberarchive", "-limage", "-lgeometry", "-ldracodec", "-lktxreader", "-lstb", "-lzstd", "-luberzlib"]
OTHER_LDFLAGS: ["-lgltfio_core", "-luberarchive", "-limage", "-lgeometry", "-ldracodec", "-lmeshoptimizer",
"-lbasis_transcoder", "-lktxreader", "-lstb", "-lzstd", "-luberzlib"]
preBuildScripts:
- path: build-resources.sh
name: Build Resources
@@ -120,7 +120,7 @@
};
};
buildConfigurationList = 0479354FB8D9A411347D91A6 /* Build configuration list for PBXProject "hello-pbr" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-pbr.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-pbr.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-triangle.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-triangle.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:transparent-rendering.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:transparent-rendering.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
+17
View File
@@ -0,0 +1,17 @@
## Updating Vulkan headers
To update the Vulkan headers, perform the following steps.
First, find the latest version of the Vulkan headers here:
https://github.com/KhronosGroup/Vulkan-Headers/tags
Replace `v1.3.232` with the latest version of the headers in the following commands.
```
cd libs/bluevk
curl -OL https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v1.3.232.zip
unzip v1.3.232.zip
rsync -r Vulkan-Headers-1.3.232/include/vulkan/ include/vulkan --delete
rsync -r Vulkan-Headers-1.3.232/include/vk_video/ include/vk_video --delete
rm include/vulkan/*.hpp
rm -r Vulkan-Headers-1.3.232 v1.3.232.zip
```

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