Compare commits
60
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e72e67bce | ||
|
|
a0c486f608 | ||
|
|
eb7447fd17 | ||
|
|
a870d01475 | ||
|
|
83e747b732 | ||
|
|
ca5cc32dc1 | ||
|
|
5c03e290c2 | ||
|
|
e4953ada3f | ||
|
|
10022f983b | ||
|
|
977d369e40 | ||
|
|
5cf5b25175 | ||
|
|
e75c3020c8 | ||
|
|
aeb0a4008d | ||
|
|
2d43ad59ec | ||
|
|
f86ec07c45 | ||
|
|
fc012d6d65 | ||
|
|
6b71232e38 | ||
|
|
e157023fc1 | ||
|
|
6e38db882f | ||
|
|
09600699cb | ||
|
|
6a5e74f031 | ||
|
|
795bd1e995 | ||
|
|
784416f658 | ||
|
|
b151afe567 | ||
|
|
117ae1d648 | ||
|
|
7165d8aa54 | ||
|
|
6b461b2d89 | ||
|
|
affc5be840 | ||
|
|
e3ff5616ef | ||
|
|
d9a401d57e | ||
|
|
acd5823953 | ||
|
|
9b13565750 | ||
|
|
734d8ff85c | ||
|
|
a02ef1a6c7 | ||
|
|
f13bf11d0f | ||
|
|
7d0b5fed8f | ||
|
|
e1f51f04c1 | ||
|
|
f6d74be123 | ||
|
|
50a1935b68 | ||
|
|
694ecce150 | ||
|
|
3e3bd2722f | ||
|
|
44fdf83e4c | ||
|
|
99c4ce67e4 | ||
|
|
71aceec4cf | ||
|
|
98e4cdbd4c | ||
|
|
86ef0109f4 | ||
|
|
b4142261c2 | ||
|
|
359624e8eb | ||
|
|
5bcc11d9e8 | ||
|
|
5aa414dd24 | ||
|
|
1e51b03971 | ||
|
|
fc484bd319 | ||
|
|
9c4783142e | ||
|
|
4dc03318e8 | ||
|
|
15ba54c4c5 | ||
|
|
342138aecb | ||
|
|
9f53c62e06 | ||
|
|
2d996033e3 | ||
|
|
9fa6dc4131 | ||
|
|
4930227743 |
@@ -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:
|
||||
|
||||
+12
-1
@@ -459,6 +459,13 @@ else()
|
||||
option(FILAMENT_ENABLE_MATDBG "Enable the material debugger" OFF)
|
||||
endif()
|
||||
|
||||
# By default, link in fgdbg for Desktop + Debug
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND IS_HOST_PLATFORM)
|
||||
option(FILAMENT_ENABLE_FGDBG "Enable the framegraph debugger" ON)
|
||||
else()
|
||||
option(FILAMENT_ENABLE_FGDBG "Enable the framegraph debugger" OFF)
|
||||
endif()
|
||||
|
||||
# Only optimize materials in Release mode (so error message lines match the source code)
|
||||
if (CMAKE_BUILD_TYPE MATCHES Release)
|
||||
option(FILAMENT_DISABLE_MATOPT "Disable material optimizations" OFF)
|
||||
@@ -666,7 +673,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)
|
||||
@@ -685,6 +692,10 @@ if (FILAMENT_BUILD_FILAMAT OR IS_HOST_PLATFORM)
|
||||
if (FILAMENT_ENABLE_MATDBG OR IS_HOST_PLATFORM)
|
||||
add_subdirectory(${LIBRARIES}/matdbg)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_ENABLE_FGDBG OR IS_HOST_PLATFORM)
|
||||
add_subdirectory(${LIBRARIES}/fgdbg)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
|
||||
@@ -179,6 +179,7 @@ steps:
|
||||
- [x] KHR_mesh_quantization
|
||||
- [x] KHR_texture_basisu
|
||||
- [x] KHR_texture_transform
|
||||
- [x] EXT_meshopt_compression
|
||||
|
||||
|
||||
## Rendering with Filament
|
||||
@@ -312,6 +313,7 @@ and tools.
|
||||
- `bluegl`: OpenGL bindings for macOS, Linux and Windows
|
||||
- `bluevk`: Vulkan bindings for macOS, Linux, Windows and Android
|
||||
- `camutils`: Camera manipulation utilities
|
||||
- `fgdbg`: Frame Graph inspector and debugger (debug builds only)
|
||||
- `filabridge`: Library shared by the Filament engine and host tools
|
||||
- `filaflat`: Serialization/deserialization library used for materials
|
||||
- `filagui`: Helper library for [Dear ImGui](https://github.com/ocornut/imgui)
|
||||
|
||||
@@ -5,6 +5,21 @@ A new header is inserted each time a *tag* is created.
|
||||
|
||||
## main branch
|
||||
|
||||
- gltfio: support EXT_meshopt_compression
|
||||
- release packaging: fixed location of default IBL file
|
||||
|
||||
## v1.28.1
|
||||
|
||||
- gltfio: fix reloading crash in ubershader mode
|
||||
- Vulkan: improve performance in the readPixels path
|
||||
- engine: raise the spot shadows limit to 64, from 14.
|
||||
- engine: add experimental support for point light shadows.
|
||||
- gltfio: fix ubershader issues with assignment of dummy textures
|
||||
- gltfio: material instances and variants are now accessed via `FilamentInstance` [⚠️ **API Change**]
|
||||
- gltfio: the animator can now only be accessed via `FilamentInstance` [⚠️ **API Change**]
|
||||
- engine: fix "stable" shadows and make the default cascade splits logarithmic.
|
||||
- engine: Add new quality options to EVSM shadows + rendering fixes
|
||||
|
||||
## v1.28.0
|
||||
|
||||
- engine: LiSPSM is now a user settable option
|
||||
@@ -23,6 +38,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
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ buildscript {
|
||||
}
|
||||
|
||||
ext.versions = [
|
||||
'minSdk': 21,
|
||||
'minSdk': 19,
|
||||
'targetSdk': 33,
|
||||
'compileSdk': 33,
|
||||
'kotlin': '1.7.10',
|
||||
|
||||
@@ -79,7 +79,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
|
||||
jfloat shadowFarHint, jboolean stable, jboolean lispsm,
|
||||
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
|
||||
jboolean screenSpaceContactShadows, jint stepCount,
|
||||
jfloat maxShadowDistance, jint vsmMsaaSamples, jfloat blurWidth, jfloat shadowBulbRadius) {
|
||||
jfloat maxShadowDistance, jint vsmMsaaSamples, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
|
||||
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
|
||||
LightManager::ShadowOptions shadowOptions {
|
||||
.mapSize = (uint32_t)mapSize,
|
||||
@@ -98,6 +98,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
|
||||
.maxShadowDistance = maxShadowDistance,
|
||||
.vsm = {
|
||||
.msaaSamples = (uint8_t) vsmMsaaSamples,
|
||||
.elvsm = (bool)elvsm,
|
||||
.blurWidth = blurWidth
|
||||
},
|
||||
.shadowBulbRadius = shadowBulbRadius
|
||||
|
||||
@@ -149,12 +149,13 @@ Java_com_google_android_filament_View_nSetShadowType(JNIEnv*, jclass, jlong nati
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetVsmShadowOptions(JNIEnv*, jclass, jlong nativeView,
|
||||
jint anisotropy, jboolean mipmapping, jfloat minVarianceScale,
|
||||
jint anisotropy, jboolean mipmapping, jboolean highPrecision, jfloat minVarianceScale,
|
||||
jfloat lightBleedReduction) {
|
||||
View* view = (View*) nativeView;
|
||||
View::VsmShadowOptions options;
|
||||
options.anisotropy = (uint8_t)anisotropy;
|
||||
options.mipmapping = (bool)mipmapping;
|
||||
options.highPrecision = (bool)highPrecision;
|
||||
options.minVarianceScale = minVarianceScale;
|
||||
options.lightBleedReduction = lightBleedReduction;
|
||||
view->setVsmShadowOptions(options);
|
||||
|
||||
+12
-8
@@ -240,7 +240,7 @@ public class LightManager {
|
||||
*/
|
||||
@NonNull
|
||||
@Size(min = 3)
|
||||
public float[] cascadeSplitPositions = { 0.25f, 0.50f, 0.75f };
|
||||
public float[] cascadeSplitPositions = { 0.125f, 0.25f, 0.50f };
|
||||
|
||||
/** Constant bias in world units (e.g. meters) by which shadows are moved away from the
|
||||
* light. 1mm by default.
|
||||
@@ -362,6 +362,15 @@ public class LightManager {
|
||||
@IntRange(from = 1)
|
||||
public int vsmMsaaSamples = 1;
|
||||
|
||||
|
||||
/**
|
||||
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
|
||||
* an improvement to the default EVSM which suffers important light leaks. Enabling
|
||||
* ELVSM for a single shadowmap doubles the memory usage of all shadow maps.
|
||||
* ELVSM is mostly useful when large blurs are used.
|
||||
*/
|
||||
public boolean elvsm = false;
|
||||
|
||||
/**
|
||||
* Blur width for the VSM blur. Zero do disable.
|
||||
* The maximum value is 125.
|
||||
@@ -486,11 +495,6 @@ public class LightManager {
|
||||
/**
|
||||
* Whether this Light casts shadows (disabled by default)
|
||||
*
|
||||
* <p>
|
||||
* <b>warning:</b>
|
||||
* {@link Type#POINT} lights cannot cast shadows.
|
||||
* </p>
|
||||
*
|
||||
* @param enable Enables or disables casting shadows from this Light.
|
||||
*
|
||||
* @return This Builder, for chaining calls.
|
||||
@@ -516,7 +520,7 @@ public class LightManager {
|
||||
options.polygonOffsetConstant, options.polygonOffsetSlope,
|
||||
options.screenSpaceContactShadows,
|
||||
options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples,
|
||||
options.blurWidth, options.shadowBulbRadius);
|
||||
options.elvsm, options.blurWidth, options.shadowBulbRadius);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1181,7 +1185,7 @@ public class LightManager {
|
||||
boolean stable, boolean lispsm,
|
||||
float polygonOffsetConstant, float polygonOffsetSlope,
|
||||
boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance,
|
||||
int vsmMsaaSamples, float blurWidth, float shadowBulbRadius);
|
||||
int vsmMsaaSamples, boolean elvsm, float blurWidth, float shadowBulbRadius);
|
||||
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
|
||||
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
|
||||
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);
|
||||
|
||||
@@ -793,7 +793,7 @@ public class View {
|
||||
public void setVsmShadowOptions(@NonNull VsmShadowOptions options) {
|
||||
mVsmShadowOptions = options;
|
||||
nSetVsmShadowOptions(getNativeObject(), options.anisotropy, options.mipmapping,
|
||||
options.minVarianceScale, options.lightBleedReduction);
|
||||
options.highPrecision, options.minVarianceScale, options.lightBleedReduction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1144,7 +1144,7 @@ public class View {
|
||||
private static native void nSetRenderQuality(long nativeView, int hdrColorBufferQuality);
|
||||
private static native void nSetDynamicLightingOptions(long nativeView, float zLightNear, float zLightFar);
|
||||
private static native void nSetShadowType(long nativeView, int type);
|
||||
private static native void nSetVsmShadowOptions(long nativeView, int anisotropy, boolean mipmapping, float minVarianceScale, float lightBleedReduction);
|
||||
private static native void nSetVsmShadowOptions(long nativeView, int anisotropy, boolean mipmapping, boolean highPrecision, float minVarianceScale, float lightBleedReduction);
|
||||
private static native void nSetSoftShadowOptions(long nativeView, float penumbraScale, float penumbraRatioScale);
|
||||
private static native void nSetColorGrading(long nativeView, long nativeColorGrading);
|
||||
private static native void nSetPostProcessingEnabled(long nativeView, boolean enabled);
|
||||
@@ -1817,6 +1817,13 @@ public class View {
|
||||
* Whether to generate mipmaps for all VSM shadow maps.
|
||||
*/
|
||||
public boolean mipmapping = false;
|
||||
/**
|
||||
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
|
||||
* precision is rarely needed, but it does reduces light leaks as well as "fading"
|
||||
* of the shadows in some situations. Setting highPrecision to true for a single
|
||||
* shadow map will double the memory usage of all shadow maps.
|
||||
*/
|
||||
public boolean highPrecision = false;
|
||||
/**
|
||||
* VSM minimum variance scale, must be positive.
|
||||
*/
|
||||
|
||||
+3
-3
@@ -179,7 +179,7 @@ class ModelViewer(
|
||||
asset = assetLoader.createAsset(buffer)
|
||||
asset?.let { asset ->
|
||||
resourceLoader.asyncBeginLoad(asset)
|
||||
animator = asset.animator
|
||||
animator = asset.getInstance().animator
|
||||
asset.releaseSourceData()
|
||||
}
|
||||
}
|
||||
@@ -202,7 +202,7 @@ class ModelViewer(
|
||||
resourceLoader.addResourceData(uri, resourceBuffer)
|
||||
}
|
||||
resourceLoader.asyncBeginLoad(asset)
|
||||
animator = asset.animator
|
||||
animator = asset.getInstance().animator
|
||||
asset.releaseSourceData()
|
||||
}
|
||||
}
|
||||
@@ -359,7 +359,7 @@ class ModelViewer(
|
||||
resourceLoader.addResourceData(uri, buffer)
|
||||
}
|
||||
resourceLoader.asyncBeginLoad(asset)
|
||||
animator = asset.animator
|
||||
animator = asset.getInstance().animator
|
||||
asset.releaseSourceData()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ add_library(dracodec STATIC IMPORTED)
|
||||
set_target_properties(dracodec PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libdracodec.a)
|
||||
|
||||
add_library(meshoptimizer STATIC IMPORTED)
|
||||
set_target_properties(meshoptimizer PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libmeshoptimizer.a)
|
||||
|
||||
add_library(ktxreader STATIC IMPORTED)
|
||||
set_target_properties(ktxreader PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libktxreader.a)
|
||||
@@ -76,7 +80,7 @@ set(GLTFIO_SRCS
|
||||
${GLTFIO_DIR}/src/UbershaderProvider.cpp
|
||||
${GLTFIO_DIR}/src/Wireframe.cpp
|
||||
${GLTFIO_DIR}/src/Wireframe.h
|
||||
${GLTFIO_DIR}/src/upcast.h
|
||||
${GLTFIO_DIR}/src/downcast.h
|
||||
|
||||
src/main/cpp/Animator.cpp
|
||||
src/main/cpp/AssetLoader.cpp
|
||||
@@ -99,6 +103,7 @@ set(GLTFIO_INCLUDE_DIRS
|
||||
../../libs/gltfio/include
|
||||
../../third_party/basisu/zstd
|
||||
../../third_party/cgltf
|
||||
../../third_party/meshoptimizer/src
|
||||
../../third_party/robin-map
|
||||
../../third_party/hat-trie
|
||||
../../third_party/stb
|
||||
@@ -111,7 +116,7 @@ target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
|
||||
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
|
||||
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
|
||||
target_link_libraries(gltfio-jni filament-jni utils uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
|
||||
target_link_libraries(gltfio-jni dracodec)
|
||||
target_link_libraries(gltfio-jni dracodec meshoptimizer)
|
||||
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
|
||||
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
|
||||
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/tnt)
|
||||
|
||||
@@ -172,27 +172,6 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetCameraEntityCount(JNIE
|
||||
return asset->getCameraEntityCount();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialInstanceCount(JNIEnv*, jclass,
|
||||
jlong nativeAsset) {
|
||||
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
|
||||
return asset->getMaterialInstanceCount();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialInstances(JNIEnv* env, jclass,
|
||||
jlong nativeAsset, jlongArray result) {
|
||||
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
|
||||
jsize available = env->GetArrayLength(result);
|
||||
jsize count = std::min(available, (jsize) asset->getMaterialInstanceCount());
|
||||
jlong* dst = env->GetLongArrayElements(result, nullptr);
|
||||
const MaterialInstance * const* src = asset->getMaterialInstances();
|
||||
for (jsize i = 0; i < count; i++) {
|
||||
dst[i] = (jlong) src[i];
|
||||
}
|
||||
env->ReleaseLongArrayElements(result, dst, 0);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentAsset_nGetBoundingBox(JNIEnv* env, jclass,
|
||||
jlong nativeAsset, jfloatArray result) {
|
||||
@@ -229,10 +208,10 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetExtras(JNIEnv* env, jc
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentAsset_nGetAnimator(JNIEnv* , jclass,
|
||||
Java_com_google_android_filament_gltfio_FilamentAsset_nGetInstance(JNIEnv* , jclass,
|
||||
jlong nativeAsset) {
|
||||
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
|
||||
return (jlong) asset->getAnimator();
|
||||
return (jlong) asset->getInstance();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
@@ -272,30 +251,6 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetMorphTargetNames(JNIEn
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialVariantCount(JNIEnv*, jclass,
|
||||
jlong nativeAsset) {
|
||||
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
|
||||
return (jint) asset->getMaterialVariantCount();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialVariantNames(JNIEnv* env, jclass,
|
||||
jlong nativeAsset, jobjectArray result) {
|
||||
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
|
||||
for (int i = 0; i < asset->getMaterialVariantCount(); ++i) {
|
||||
const char* name = asset->getMaterialVariantName(i);
|
||||
env->SetObjectArrayElement(result, (jsize) i, env->NewStringUTF(name));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentAsset_nApplyMaterialVariant(JNIEnv* env, jclass,
|
||||
jlong nativeAsset, jint variantIndex) {
|
||||
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
|
||||
asset->applyMaterialVariant(variantIndex);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentAsset_nReleaseSourceData(JNIEnv* env, jclass,
|
||||
jlong nativeAsset) {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::gltfio;
|
||||
using namespace utils;
|
||||
|
||||
@@ -62,6 +63,44 @@ Java_com_google_android_filament_gltfio_FilamentInstance_nApplyMaterialVariant(J
|
||||
instance->applyMaterialVariant(variantIndex);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialVariantCount(JNIEnv*, jclass,
|
||||
jlong nativeInstance) {
|
||||
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
|
||||
return (jint) instance->getMaterialVariantCount();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialVariantNames(JNIEnv* env, jclass,
|
||||
jlong nativeInstance, jobjectArray result) {
|
||||
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
|
||||
for (int i = 0; i < instance->getMaterialVariantCount(); ++i) {
|
||||
const char* name = instance->getMaterialVariantName(i);
|
||||
env->SetObjectArrayElement(result, (jsize) i, env->NewStringUTF(name));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialInstanceCount(JNIEnv*, jclass,
|
||||
jlong nativeInstance) {
|
||||
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
|
||||
return instance->getMaterialInstanceCount();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialInstances(JNIEnv* env, jclass,
|
||||
jlong nativeInstance, jlongArray result) {
|
||||
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
|
||||
jsize available = env->GetArrayLength(result);
|
||||
jsize count = std::min(available, (jsize) instance->getMaterialInstanceCount());
|
||||
jlong* dst = env->GetLongArrayElements(result, nullptr);
|
||||
const MaterialInstance * const* src = instance->getMaterialInstances();
|
||||
for (jsize i = 0; i < count; i++) {
|
||||
dst[i] = (jlong) src[i];
|
||||
}
|
||||
env->ReleaseLongArrayElements(result, dst, 0);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_FilamentInstance_nAttachSkin(JNIEnv* env, jclass,
|
||||
jlong nativeInstance, jint skinIndex, jint targetEntity) {
|
||||
|
||||
+19
-70
@@ -23,7 +23,6 @@ import androidx.annotation.Nullable;
|
||||
import com.google.android.filament.Box;
|
||||
import com.google.android.filament.Engine;
|
||||
import com.google.android.filament.Entity;
|
||||
import com.google.android.filament.MaterialInstance;
|
||||
|
||||
/**
|
||||
* Owns a bundle of Filament objects that have been created by <code>AssetLoader</code>.
|
||||
@@ -35,25 +34,32 @@ import com.google.android.filament.MaterialInstance;
|
||||
* <code>NameComponentManager</code>, <code>RenderableManager</code>, and others.</p>
|
||||
*
|
||||
* <p>In addition to the aforementioned entities, an asset has strong ownership over a list of
|
||||
* <code>VertexBuffer</code>, <code>IndexBuffer</code>, <code>MaterialInstance</code>, and
|
||||
* <code>Texture</code>.</p>
|
||||
* <code>VertexBuffer</code>, <code>IndexBuffer</code>, and <code>Texture</code>.</p>
|
||||
*
|
||||
* <p>Clients can use {@link ResourceLoader} to create textures, compute tangent quaternions, and
|
||||
* upload data into vertex buffers and index buffers.</p>
|
||||
*
|
||||
* @see ResourceLoader
|
||||
* @see Animator
|
||||
* @see FilamentInstance
|
||||
* @see AssetLoader
|
||||
*/
|
||||
public class FilamentAsset {
|
||||
private long mNativeObject;
|
||||
private Animator mAnimator;
|
||||
private FilamentInstance mPrimaryInstance;
|
||||
private Engine mEngine;
|
||||
|
||||
FilamentAsset(Engine engine, long nativeObject) {
|
||||
mEngine = engine;
|
||||
mNativeObject = nativeObject;
|
||||
mAnimator = null;
|
||||
}
|
||||
|
||||
public FilamentInstance getInstance() {
|
||||
if (mPrimaryInstance != null) {
|
||||
return mPrimaryInstance;
|
||||
}
|
||||
long nativeInstance = nGetInstance(getNativeObject());
|
||||
mPrimaryInstance = new FilamentInstance(this, nativeInstance);
|
||||
return mPrimaryInstance;
|
||||
}
|
||||
|
||||
long getNativeObject() {
|
||||
@@ -169,17 +175,6 @@ public class FilamentAsset {
|
||||
return result;
|
||||
}
|
||||
|
||||
public @NonNull MaterialInstance[] getMaterialInstances() {
|
||||
final int count = nGetMaterialInstanceCount(mNativeObject);
|
||||
MaterialInstance[] result = new MaterialInstance[count];
|
||||
long[] natives = new long[count];
|
||||
nGetMaterialInstances(mNativeObject, natives);
|
||||
for (int i = 0; i < count; i++) {
|
||||
result[i] = new MaterialInstance(mEngine, natives[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bounding box computed from the supplied min / max values in glTF accessors.
|
||||
*
|
||||
@@ -209,25 +204,6 @@ public class FilamentAsset {
|
||||
return nGetExtras(mNativeObject, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the <code>Animator</code> interface for this asset.
|
||||
*
|
||||
* <p>When calling this for the first time, this must be called after
|
||||
* {@link ResourceLoader#loadResources} or {@link ResourceLoader#asyncBeginLoad}. When the asset
|
||||
* is destroyed, its animator becomes invalid.</p>
|
||||
*/
|
||||
public @NonNull Animator getAnimator() {
|
||||
if (mAnimator != null) {
|
||||
return mAnimator;
|
||||
}
|
||||
long nativeAnimator = nGetAnimator(getNativeObject());
|
||||
if (nativeAnimator == 0) {
|
||||
throw new IllegalStateException("Unable to create animator");
|
||||
}
|
||||
mAnimator = new Animator(nativeAnimator);
|
||||
return mAnimator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of all morph targets in the given entity.
|
||||
*/
|
||||
@@ -246,31 +222,6 @@ public class FilamentAsset {
|
||||
return uris;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of all material variants.
|
||||
*/
|
||||
public @NonNull String[] getMaterialVariantNames() {
|
||||
String[] names = new String[nGetMaterialVariantCount(mNativeObject)];
|
||||
nGetMaterialVariantNames(mNativeObject, names);
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given material variant to all primitives that it affects.
|
||||
*
|
||||
* This is efficient because it merely swaps around persistent MaterialInstances. If you change
|
||||
* a material parameter while a certain variant is active, the updated value will be remembered
|
||||
* after you re-apply that variant.
|
||||
*
|
||||
* If the asset is instanced, this affects all instances in the same way.
|
||||
* To set the variant on an individual instance, use FilamentInstance#applyMaterialVariant.
|
||||
*
|
||||
* Ignored if variantIndex is out of bounds.
|
||||
*/
|
||||
public void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
|
||||
nApplyMaterialVariant(getNativeObject(), variantIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reclaims CPU-side memory for URI strings, binding lists, and raw animation data.
|
||||
*
|
||||
@@ -282,8 +233,10 @@ public class FilamentAsset {
|
||||
nReleaseSourceData(mNativeObject);
|
||||
}
|
||||
|
||||
public Engine getEngine() { return mEngine; }
|
||||
|
||||
void clearNativeObject() {
|
||||
if (mAnimator != null) mAnimator.clearNativeObject();
|
||||
mPrimaryInstance = null;
|
||||
mNativeObject = 0;
|
||||
}
|
||||
|
||||
@@ -307,21 +260,17 @@ public class FilamentAsset {
|
||||
private static native int nGetCameraEntityCount(long nativeAsset);
|
||||
private static native void nGetCameraEntities(long nativeAsset, int[] result);
|
||||
|
||||
private static native int nGetMaterialInstanceCount(long nativeAsset);
|
||||
private static native void nGetMaterialInstances(long nativeAsset, long[] nativeResults);
|
||||
|
||||
private static native int nGetMaterialVariantCount(long nativeAsset);
|
||||
private static native void nGetMaterialVariantNames(long nativeAsset, String[] result);
|
||||
|
||||
private static native int nGetMorphTargetCount(long nativeAsset, int entity);
|
||||
private static native void nGetMorphTargetNames(long nativeAsset, int entity, String[] result);
|
||||
|
||||
private static native void nGetBoundingBox(long nativeAsset, float[] box);
|
||||
private static native String nGetName(long nativeAsset, int entity);
|
||||
private static native String nGetExtras(long nativeAsset, int entity);
|
||||
private static native long nGetAnimator(long nativeAsset);
|
||||
private static native void nApplyMaterialVariant(long nativeAsset, int variantIndex);
|
||||
|
||||
private static native long nGetInstance(long nativeAsset);
|
||||
|
||||
private static native int nGetResourceUriCount(long nativeAsset);
|
||||
private static native void nGetResourceUris(long nativeAsset, String[] result);
|
||||
|
||||
private static native void nReleaseSourceData(long nativeAsset);
|
||||
}
|
||||
|
||||
+30
@@ -19,7 +19,9 @@ package com.google.android.filament.gltfio;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.android.filament.Engine;
|
||||
import com.google.android.filament.Entity;
|
||||
import com.google.android.filament.MaterialInstance;
|
||||
|
||||
/**
|
||||
* Provides access to a hierarchy of entities that have been instanced from a glTF asset.
|
||||
@@ -146,11 +148,39 @@ public class FilamentInstance {
|
||||
nApplyMaterialVariant(mNativeObject, variantIndex);
|
||||
}
|
||||
|
||||
public @NonNull MaterialInstance[] getMaterialInstances() {
|
||||
final int count = nGetMaterialInstanceCount(mNativeObject);
|
||||
MaterialInstance[] result = new MaterialInstance[count];
|
||||
long[] natives = new long[count];
|
||||
nGetMaterialInstances(mNativeObject, natives);
|
||||
Engine engine = mAsset.getEngine();
|
||||
for (int i = 0; i < count; i++) {
|
||||
result[i] = new MaterialInstance(engine, natives[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of all material variants.
|
||||
*/
|
||||
public @NonNull String[] getMaterialVariantNames() {
|
||||
String[] names = new String[nGetMaterialVariantCount(mNativeObject)];
|
||||
nGetMaterialVariantNames(mNativeObject, names);
|
||||
return names;
|
||||
}
|
||||
|
||||
private static native int nGetRoot(long nativeInstance);
|
||||
private static native int nGetEntityCount(long nativeInstance);
|
||||
private static native void nGetEntities(long nativeInstance, int[] result);
|
||||
private static native long nGetAnimator(long nativeInstance);
|
||||
|
||||
private static native int nGetMaterialInstanceCount(long nativeAsset);
|
||||
private static native void nGetMaterialInstances(long nativeAsset, long[] nativeResults);
|
||||
|
||||
private static native void nApplyMaterialVariant(long nativeInstance, int variantIndex);
|
||||
private static native int nGetMaterialVariantCount(long nativeAsset);
|
||||
private static native void nGetMaterialVariantNames(long nativeAsset, String[] result);
|
||||
|
||||
private static native void nGetJointsAt(long nativeInstance, int skinIndex, int[] result);
|
||||
private static native int nGetSkinCount(long nativeInstance);
|
||||
private static native void nGetSkinNames(long nativeInstance, String[] result);
|
||||
|
||||
@@ -112,9 +112,9 @@ the Android SDK.
|
||||
|
||||
## Android Studio
|
||||
|
||||
You must use Android Studio 3.6 RC 1 or higher. To open the project, point Studio to the `android`
|
||||
folder. After opening the project and syncing to gradle, select the sample of your choice using the
|
||||
drop-down widget in the toolbar.
|
||||
You must use the latest stable release of Android Studio. To open the project, point Studio to the
|
||||
`android` folder. After opening the project and syncing to gradle, select the sample of your choice
|
||||
using the drop-down widget in the toolbar.
|
||||
|
||||
## Compiling
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.filament.gltf"
|
||||
minSdkVersion 21
|
||||
minSdkVersion 19
|
||||
targetSdkVersion versions.targetSdk
|
||||
missingDimensionStrategy 'functionality', 'full'
|
||||
}
|
||||
|
||||
@@ -162,6 +162,8 @@ EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF"
|
||||
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF"
|
||||
MATDBG_GRADLE_OPTION=""
|
||||
|
||||
FGDBG_OPTION="-DFILAMENT_ENABLE_FGDBG=OFF"
|
||||
|
||||
IOS_BUILD_SIMULATOR=false
|
||||
BUILD_UNIVERSAL_LIBRARIES=false
|
||||
|
||||
@@ -221,6 +223,7 @@ function build_desktop_target {
|
||||
${SWIFTSHADER_OPTION} \
|
||||
${EGL_ON_LINUX_OPTION} \
|
||||
${MATDBG_OPTION} \
|
||||
${FGDBG_OPTION} \
|
||||
${deployment_target} \
|
||||
${architectures} \
|
||||
../..
|
||||
@@ -347,6 +350,7 @@ function build_android_target {
|
||||
-DCMAKE_INSTALL_PREFIX="../android-${lc_target}/filament" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="../../build/toolchain-${arch}-linux-android.cmake" \
|
||||
${MATDBG_OPTION} \
|
||||
${FGDBG_OPTION} \
|
||||
${VULKAN_ANDROID_OPTION} \
|
||||
../..
|
||||
fi
|
||||
@@ -576,6 +580,7 @@ function build_ios_target {
|
||||
-DIOS=1 \
|
||||
-DCMAKE_TOOLCHAIN_FILE=../../third_party/clang/iOS.cmake \
|
||||
${MATDBG_OPTION} \
|
||||
${FGDBG_OPTION} \
|
||||
../..
|
||||
fi
|
||||
|
||||
@@ -760,6 +765,7 @@ while getopts ":hacCfijmp:q:uvslwtedk:" opt; do
|
||||
PRINT_MATDBG_HELP=true
|
||||
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=ON, -DFILAMENT_DISABLE_MATOPT=ON, -DFILAMENT_BUILD_FILAMAT=ON"
|
||||
MATDBG_GRADLE_OPTION="-Pcom.google.android.filament.matdbg"
|
||||
FGDBG_OPTION="-DFILAMENT_ENABLE_FGDBG=ON"
|
||||
;;
|
||||
f)
|
||||
ISSUE_CMAKE_ALWAYS=true
|
||||
|
||||
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# android
|
||||
set(API_LEVEL 21)
|
||||
set(API_LEVEL 19)
|
||||
|
||||
# architecture
|
||||
set(ARCH armv7a-linux-androideabi)
|
||||
|
||||
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# android
|
||||
set(API_LEVEL 21)
|
||||
set(API_LEVEL 19)
|
||||
|
||||
# architecture
|
||||
set(ARCH i686-linux-android)
|
||||
|
||||
@@ -198,7 +198,7 @@ set(PRIVATE_HDRS
|
||||
src/materials/fsr/ffx_a.h
|
||||
src/materials/fsr/ffx_fsr1.h
|
||||
src/materials/fsr/ffx_fsr1_mobile.fs
|
||||
src/upcast.h
|
||||
src/downcast.h
|
||||
)
|
||||
|
||||
set(MATERIAL_SRCS
|
||||
@@ -537,6 +537,13 @@ target_link_libraries(${TARGET} PUBLIC filaflat)
|
||||
target_link_libraries(${TARGET} PUBLIC filabridge)
|
||||
target_link_libraries(${TARGET} PUBLIC ibl-lite)
|
||||
|
||||
if (FILAMENT_ENABLE_FGDBG)
|
||||
target_link_libraries(${TARGET} PUBLIC fgdbg)
|
||||
add_definitions(-DFILAMENT_ENABLE_FGDBG=1)
|
||||
else()
|
||||
add_definitions(-DFILAMENT_ENABLE_FGDBG=0)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_ENABLE_MATDBG)
|
||||
target_link_libraries(${TARGET} PUBLIC matdbg)
|
||||
add_definitions(-DFILAMENT_ENABLE_MATDBG=1)
|
||||
|
||||
@@ -49,6 +49,11 @@ void removeGoogleLineDirectives(char* shader, size_t length) noexcept;
|
||||
*/
|
||||
size_t getFormatSize(TextureFormat format) noexcept;
|
||||
|
||||
/**
|
||||
* Returns the number of component (1 to 4) for the given format.
|
||||
*/
|
||||
size_t getFormatComponentCount(TextureFormat format) noexcept;
|
||||
|
||||
/**
|
||||
* For compressed texture formats, returns the number of horizontal texels per block. Otherwise
|
||||
* returns 0.
|
||||
|
||||
@@ -158,6 +158,9 @@ DECL_DRIVER_API_0(flush)
|
||||
// flush and wait for the effects to be done
|
||||
DECL_DRIVER_API_0(finish)
|
||||
|
||||
// reset state tracking, if the driver does any state tracking (e.g. GL)
|
||||
DECL_DRIVER_API_0(resetState)
|
||||
|
||||
/*
|
||||
* Creating driver objects
|
||||
* -----------------------
|
||||
|
||||
@@ -209,6 +209,129 @@ size_t getFormatSize(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
size_t getFormatComponentCount(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::R8:
|
||||
case TextureFormat::R8_SNORM:
|
||||
case TextureFormat::R8UI:
|
||||
case TextureFormat::R8I:
|
||||
case TextureFormat::R16F:
|
||||
case TextureFormat::R16UI:
|
||||
case TextureFormat::R16I:
|
||||
case TextureFormat::R32F:
|
||||
case TextureFormat::R32I:
|
||||
case TextureFormat::R32UI:
|
||||
case TextureFormat::STENCIL8:
|
||||
case TextureFormat::DEPTH16:
|
||||
case TextureFormat::DEPTH24:
|
||||
case TextureFormat::DEPTH32F:
|
||||
return 1;
|
||||
|
||||
case TextureFormat::RG8:
|
||||
case TextureFormat::RG8_SNORM:
|
||||
case TextureFormat::RG8UI:
|
||||
case TextureFormat::RG8I:
|
||||
case TextureFormat::RG16F:
|
||||
case TextureFormat::RG16UI:
|
||||
case TextureFormat::RG16I:
|
||||
case TextureFormat::RG32F:
|
||||
case TextureFormat::RG32UI:
|
||||
case TextureFormat::RG32I:
|
||||
case TextureFormat::DEPTH24_STENCIL8:
|
||||
case TextureFormat::DEPTH32F_STENCIL8:
|
||||
return 2;
|
||||
|
||||
case TextureFormat::RGB565:
|
||||
case TextureFormat::RGB8:
|
||||
case TextureFormat::SRGB8:
|
||||
case TextureFormat::RGB8_SNORM:
|
||||
case TextureFormat::RGB8UI:
|
||||
case TextureFormat::RGB8I:
|
||||
case TextureFormat::R11F_G11F_B10F:
|
||||
case TextureFormat::RGB16F:
|
||||
case TextureFormat::RGB16UI:
|
||||
case TextureFormat::RGB16I:
|
||||
case TextureFormat::RGB32F:
|
||||
case TextureFormat::RGB32UI:
|
||||
case TextureFormat::RGB32I:
|
||||
return 3;
|
||||
|
||||
case TextureFormat::RGB5_A1:
|
||||
case TextureFormat::RGBA4:
|
||||
case TextureFormat::RGB9_E5:
|
||||
case TextureFormat::RGBA8:
|
||||
case TextureFormat::SRGB8_A8:
|
||||
case TextureFormat::RGBA8_SNORM:
|
||||
case TextureFormat::RGB10_A2:
|
||||
case TextureFormat::RGBA8UI:
|
||||
case TextureFormat::RGBA8I:
|
||||
case TextureFormat::RGBA16F:
|
||||
case TextureFormat::RGBA16UI:
|
||||
case TextureFormat::RGBA16I:
|
||||
case TextureFormat::RGBA32F:
|
||||
case TextureFormat::RGBA32UI:
|
||||
case TextureFormat::RGBA32I:
|
||||
return 4;
|
||||
|
||||
// Compressed formats ---------------------------------------------------------------------
|
||||
case TextureFormat::EAC_R11:
|
||||
case TextureFormat::EAC_R11_SIGNED:
|
||||
return 1;
|
||||
|
||||
case TextureFormat::EAC_RG11:
|
||||
case TextureFormat::EAC_RG11_SIGNED:
|
||||
return 2;
|
||||
|
||||
case TextureFormat::ETC2_RGB8:
|
||||
case TextureFormat::ETC2_SRGB8:
|
||||
case TextureFormat::DXT1_RGB:
|
||||
case TextureFormat::DXT1_SRGB:
|
||||
return 3;
|
||||
|
||||
case TextureFormat::ETC2_EAC_RGBA8:
|
||||
case TextureFormat::ETC2_EAC_SRGBA8:
|
||||
case TextureFormat::ETC2_RGB8_A1:
|
||||
case TextureFormat::ETC2_SRGB8_A1:
|
||||
case TextureFormat::DXT1_RGBA:
|
||||
case TextureFormat::DXT1_SRGBA:
|
||||
case TextureFormat::DXT3_RGBA:
|
||||
case TextureFormat::DXT3_SRGBA:
|
||||
case TextureFormat::DXT5_RGBA:
|
||||
case TextureFormat::DXT5_SRGBA:
|
||||
case TextureFormat::RGBA_ASTC_4x4:
|
||||
case TextureFormat::RGBA_ASTC_5x4:
|
||||
case TextureFormat::RGBA_ASTC_5x5:
|
||||
case TextureFormat::RGBA_ASTC_6x5:
|
||||
case TextureFormat::RGBA_ASTC_6x6:
|
||||
case TextureFormat::RGBA_ASTC_8x5:
|
||||
case TextureFormat::RGBA_ASTC_8x6:
|
||||
case TextureFormat::RGBA_ASTC_8x8:
|
||||
case TextureFormat::RGBA_ASTC_10x5:
|
||||
case TextureFormat::RGBA_ASTC_10x6:
|
||||
case TextureFormat::RGBA_ASTC_10x8:
|
||||
case TextureFormat::RGBA_ASTC_10x10:
|
||||
case TextureFormat::RGBA_ASTC_12x10:
|
||||
case TextureFormat::RGBA_ASTC_12x12:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
|
||||
return 4;
|
||||
case TextureFormat::UNUSED:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t getBlockWidth(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::EAC_RG11:
|
||||
@@ -384,53 +507,53 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
namespace utils {
|
||||
|
||||
template<>
|
||||
CString to_string<filament::backend::TextureUsage>(filament::backend::TextureUsage usage) noexcept {
|
||||
CString to_string<filament::backend::TextureUsage>(filament::backend::TextureUsage value) noexcept {
|
||||
using namespace filament::backend;
|
||||
char string[7] = {'-', '-', '-', '-', '-', '-', 0};
|
||||
if (any(usage & TextureUsage::UPLOADABLE)) {
|
||||
if (any(value & TextureUsage::UPLOADABLE)) {
|
||||
string[0]='U';
|
||||
}
|
||||
if (any(usage & TextureUsage::SAMPLEABLE)) {
|
||||
if (any(value & TextureUsage::SAMPLEABLE)) {
|
||||
string[1]='S';
|
||||
}
|
||||
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
|
||||
if (any(value & TextureUsage::COLOR_ATTACHMENT)) {
|
||||
string[2]='c';
|
||||
}
|
||||
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
|
||||
if (any(value & TextureUsage::DEPTH_ATTACHMENT)) {
|
||||
string[3]='d';
|
||||
}
|
||||
if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) {
|
||||
if (any(value & TextureUsage::STENCIL_ATTACHMENT)) {
|
||||
string[4] = 's';
|
||||
}
|
||||
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
|
||||
if (any(value & TextureUsage::SUBPASS_INPUT)) {
|
||||
string[5]='f';
|
||||
}
|
||||
return CString(string, 6);
|
||||
return { string, 6 };
|
||||
}
|
||||
|
||||
template<>
|
||||
CString to_string<filament::backend::TargetBufferFlags>(filament::backend::TargetBufferFlags flags) noexcept {
|
||||
CString to_string<filament::backend::TargetBufferFlags>(filament::backend::TargetBufferFlags value) noexcept {
|
||||
using namespace filament::backend;
|
||||
char string[7] = {'-', '-', '-', '-', '-', '-', 0};
|
||||
if (any(flags & TargetBufferFlags::COLOR0)) {
|
||||
if (any(value & TargetBufferFlags::COLOR0)) {
|
||||
string[0]='0';
|
||||
}
|
||||
if (any(flags & TargetBufferFlags::COLOR1)) {
|
||||
if (any(value & TargetBufferFlags::COLOR1)) {
|
||||
string[1]='1';
|
||||
}
|
||||
if (any(flags & TargetBufferFlags::COLOR2)) {
|
||||
if (any(value & TargetBufferFlags::COLOR2)) {
|
||||
string[2]='2';
|
||||
}
|
||||
if (any(flags & TargetBufferFlags::COLOR3)) {
|
||||
if (any(value & TargetBufferFlags::COLOR3)) {
|
||||
string[3]='3';
|
||||
}
|
||||
if (any(flags & TargetBufferFlags::DEPTH)) {
|
||||
if (any(value & TargetBufferFlags::DEPTH)) {
|
||||
string[4]='D';
|
||||
}
|
||||
if (any(flags & TargetBufferFlags::STENCIL)) {
|
||||
if (any(value & TargetBufferFlags::STENCIL)) {
|
||||
string[5]='S';
|
||||
}
|
||||
return CString(string, 6);
|
||||
return { string, 6 };
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
||||
@@ -59,6 +59,21 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static void copyImage(uint8_t* UTILS_RESTRICT dest,
|
||||
const uint8_t* UTILS_RESTRICT src,
|
||||
size_t srcBytesPerRow, size_t /*srcChannelCount*/,
|
||||
size_t dstBytesPerRow, size_t /*dstChannelCount*/,
|
||||
size_t /*width*/, size_t height, bool /*swizzle*/) {
|
||||
if (srcBytesPerRow == dstBytesPerRow) {
|
||||
std::memcpy(dest, src, height * srcBytesPerRow);
|
||||
return;
|
||||
}
|
||||
const size_t minBytesPerRow = std::min(srcBytesPerRow, dstBytesPerRow);
|
||||
for (size_t i = 0; i < height; ++i, src += srcBytesPerRow, dest += dstBytesPerRow) {
|
||||
std::memcpy(dest, src, minBytesPerRow);
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a n-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
|
||||
template<typename dstComponentType, typename srcComponentType>
|
||||
static void reshapeImage(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
|
||||
@@ -119,7 +134,13 @@ public:
|
||||
switch (dst->type) {
|
||||
case UBYTE:
|
||||
switch (srcType) {
|
||||
case UBYTE: reshaper = reshapeImage<uint8_t, uint8_t>; break;
|
||||
case UBYTE:
|
||||
reshaper = reshapeImage<uint8_t, uint8_t>;
|
||||
if (dst->format == PixelDataFormat::RGBA &&
|
||||
dstChannelCount == srcChannelCount && !swizzle) {
|
||||
reshaper = copyImage;
|
||||
}
|
||||
break;
|
||||
case FLOAT: reshaper = reshapeImage<uint8_t, float>; break;
|
||||
case INT: reshaper = reshapeImage<uint8_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<uint8_t, uint32_t>; break;
|
||||
|
||||
@@ -27,13 +27,15 @@
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class MetalBuffer {
|
||||
public:
|
||||
|
||||
MetalBuffer(MetalContext& context, BufferUsage usage, size_t size, bool forceGpuBuffer = false);
|
||||
MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
|
||||
size_t size, bool forceGpuBuffer = false);
|
||||
~MetalBuffer();
|
||||
|
||||
MetalBuffer(const MetalBuffer& rhs) = delete;
|
||||
@@ -60,18 +62,21 @@ public:
|
||||
|
||||
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
|
||||
|
||||
enum Stage {
|
||||
VERTEX = 1,
|
||||
FRAGMENT = 2
|
||||
enum Stage : uint8_t {
|
||||
VERTEX = 1u << 0u,
|
||||
FRAGMENT = 1u << 1u,
|
||||
COMPUTE = 1u << 2u
|
||||
};
|
||||
|
||||
/**
|
||||
* Bind multiple buffers to pipeline stages.
|
||||
*
|
||||
* bindBuffers binds an array of buffers to the given stage(s) of a MTLRenderCommandEncoder's
|
||||
* pipeline.
|
||||
* bindBuffers binds an array of buffers to the given stage(s) of a MTLCommandEncoders's
|
||||
* pipeline. The encoder must be either a MTLRenderCommandEncoder or a MTLComputeCommandEncoder.
|
||||
* For MTLRenderCommandEncoders, only the VERTEX and FRAGMENT stages may be specified.
|
||||
* For MTLComputeCommandEncoders, only the COMPUTE stage may be specified.
|
||||
*/
|
||||
static void bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderCommandEncoder> encoder,
|
||||
static void bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
|
||||
size_t bufferStart, uint8_t stages, MetalBuffer* const* buffers, size_t const* offsets,
|
||||
size_t count);
|
||||
|
||||
@@ -106,7 +111,12 @@ public:
|
||||
// address space. Constant buffers have specific alignment requirements when specifying an
|
||||
// offset.
|
||||
#if defined(IOS)
|
||||
#if TARGET_OS_SIMULATOR
|
||||
// The iOS simulator has differing alignment requirements.
|
||||
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 256;
|
||||
#else
|
||||
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 4;
|
||||
#endif // TARGET_OS_SIMULATOR
|
||||
#else
|
||||
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 32;
|
||||
#endif
|
||||
@@ -132,7 +142,7 @@ public:
|
||||
* @return the id<MTLBuffer> and offset for the new allocation
|
||||
*/
|
||||
std::pair<id<MTLBuffer>, NSUInteger> createNewAllocation(id<MTLCommandBuffer> cmdBuffer) {
|
||||
const auto occupiedSlots = mOccupiedSlots.load(std::memory_order_relaxed);
|
||||
const auto occupiedSlots = mOccupiedSlots->load(std::memory_order_relaxed);
|
||||
assert_invariant(occupiedSlots <= mSlotCount);
|
||||
if (UTILS_UNLIKELY(occupiedSlots == mSlotCount)) {
|
||||
// We don't have any room left, so we fall back to creating a one-off aux buffer.
|
||||
@@ -144,14 +154,19 @@ public:
|
||||
return {mAuxBuffer, 0};
|
||||
}
|
||||
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
|
||||
mOccupiedSlots.fetch_add(1, std::memory_order_relaxed);
|
||||
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// Release the previous allocation.
|
||||
if (UTILS_UNLIKELY(mAuxBuffer)) {
|
||||
mAuxBuffer = nil;
|
||||
} else {
|
||||
// Capture the mOccupiedSlots var via a weak_ptr because the MetalRingBuffer could be
|
||||
// destructed before the block executes.
|
||||
std::weak_ptr<AtomicCounterType> slots = mOccupiedSlots;
|
||||
[cmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
|
||||
mOccupiedSlots.fetch_sub(1, std::memory_order_relaxed);
|
||||
if (auto s = slots.lock()) {
|
||||
s->fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
}];
|
||||
}
|
||||
return getCurrentAllocation();
|
||||
@@ -185,7 +200,8 @@ private:
|
||||
NSUInteger mSlotCount;
|
||||
|
||||
NSUInteger mCurrentSlot = 0;
|
||||
std::atomic<NSUInteger> mOccupiedSlots = 1;
|
||||
using AtomicCounterType = std::atomic<NSUInteger>;
|
||||
std::shared_ptr<AtomicCounterType> mOccupiedSlots = std::make_shared<AtomicCounterType>(1);
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <stack>
|
||||
|
||||
#if defined(FILAMENT_METAL_PROFILING)
|
||||
@@ -43,6 +44,7 @@ class MetalBufferPool;
|
||||
class MetalRenderTarget;
|
||||
class MetalSamplerGroup;
|
||||
class MetalSwapChain;
|
||||
class MetalTexture;
|
||||
class MetalTimerQueryInterface;
|
||||
struct MetalUniformBuffer;
|
||||
struct MetalIndexBuffer;
|
||||
@@ -58,8 +60,11 @@ struct MetalContext {
|
||||
id<MTLCommandBuffer> pendingCommandBuffer = nullptr;
|
||||
id<MTLRenderCommandEncoder> currentRenderPassEncoder = nullptr;
|
||||
|
||||
std::atomic<bool> memorylessLimitsReached = false;
|
||||
|
||||
// Supported features.
|
||||
bool supportsTextureSwizzling = false;
|
||||
bool supportsMemorylessRenderTargets = false;
|
||||
uint8_t maxColorRenderTargets = 4;
|
||||
struct {
|
||||
uint8_t common;
|
||||
@@ -79,7 +84,8 @@ struct MetalContext {
|
||||
// State trackers.
|
||||
PipelineStateTracker pipelineState;
|
||||
DepthStencilStateTracker depthStencilState;
|
||||
UniformBufferState uniformState[Program::UNIFORM_BINDING_COUNT];
|
||||
std::array<BufferState, Program::UNIFORM_BINDING_COUNT> uniformState;
|
||||
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
|
||||
CullModeStateTracker cullModeState;
|
||||
WindingStateTracker windingState;
|
||||
|
||||
@@ -94,8 +100,9 @@ struct MetalContext {
|
||||
// Keeps track of sampler groups we've finalized for the current render pass.
|
||||
tsl::robin_set<MetalSamplerGroup*> finalizedSamplerGroups;
|
||||
|
||||
// Keeps track of all alive sampler groups.
|
||||
// Keeps track of all alive sampler groups, textures.
|
||||
tsl::robin_set<MetalSamplerGroup*> samplerGroups;
|
||||
tsl::robin_set<MetalTexture*> textures;
|
||||
|
||||
MetalBufferPool* bufferPool;
|
||||
|
||||
@@ -127,10 +134,6 @@ struct MetalContext {
|
||||
os_log_t log;
|
||||
os_signpost_id_t signpostId;
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
tsl::robin_set<HandleBase::HandleId> aliveTextures;
|
||||
#endif
|
||||
};
|
||||
|
||||
void initializeSupportedGpuFamilies(MetalContext* context);
|
||||
|
||||
@@ -103,6 +103,15 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
||||
// all frames and their completion handlers finish before context is deallocated.
|
||||
[context->pendingCommandBuffer addCompletedHandler:^(id <MTLCommandBuffer> buffer) {
|
||||
context->resourceTracker.clearResources((__bridge void*) buffer);
|
||||
|
||||
auto errorCode = (MTLCommandBufferError)buffer.error.code;
|
||||
if (@available(macOS 11.0, *)) {
|
||||
if (errorCode == MTLCommandBufferErrorMemoryless) {
|
||||
utils::slog.w << "Metal: memoryless geometry limit reached. "
|
||||
"Continuing with private storage mode." << utils::io::endl;
|
||||
context->memorylessLimitsReached = true;
|
||||
}
|
||||
}
|
||||
}];
|
||||
ASSERT_POSTCONDITION(context->pendingCommandBuffer, "Could not obtain command buffer.");
|
||||
return context->pendingCommandBuffer;
|
||||
|
||||
@@ -35,10 +35,11 @@ class MetalPlatform;
|
||||
|
||||
class MetalBuffer;
|
||||
class MetalSamplerGroup;
|
||||
class MetalTexture;
|
||||
struct MetalUniformBuffer;
|
||||
struct MetalContext;
|
||||
struct MetalProgram;
|
||||
struct UniformBufferState;
|
||||
struct BufferState;
|
||||
|
||||
#ifndef FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB
|
||||
#define FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB 8
|
||||
@@ -128,8 +129,8 @@ private:
|
||||
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
|
||||
|
||||
void finalizeSamplerGroup(MetalSamplerGroup* sg);
|
||||
void enumerateBoundUniformBuffers(const std::function<void(const UniformBufferState&,
|
||||
MetalBuffer*, uint32_t)>& f);
|
||||
void enumerateBoundBuffers(BufferObjectBinding bindingType,
|
||||
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -92,6 +92,11 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
mContext->highestSupportedGpuFamily.mac >= 2; // newer macOS GPUs
|
||||
}
|
||||
|
||||
// In order to support memoryless render targets, an Apple GPU is needed.
|
||||
// Available starting macOS 11.0, the first version to run on Apple GPUs.
|
||||
// On iOS, it's available on all OS versions.
|
||||
mContext->supportsMemorylessRenderTargets = mContext->highestSupportedGpuFamily.apple >= 1;
|
||||
|
||||
mContext->maxColorRenderTargets = 4;
|
||||
if (mContext->highestSupportedGpuFamily.apple >= 2 ||
|
||||
mContext->highestSupportedGpuFamily.mac >= 1) {
|
||||
@@ -191,6 +196,18 @@ void MetalDriver::endFrame(uint32_t frameId) {
|
||||
|
||||
assert_invariant(mContext->groupMarkers.empty());
|
||||
|
||||
// If we exceeded memoryless limits, turn it off for the rest of the lifetime of the driver.
|
||||
if (mContext->supportsMemorylessRenderTargets && mContext->memorylessLimitsReached) {
|
||||
for (MetalTexture* texture : mContext->textures) {
|
||||
// Release memoryless MTLTexture-s, which are currently only the MSAA sidecars.
|
||||
// Creation of new render targets is going to trigger the re-allocation of sidecars,
|
||||
// with private storage mode from now on. Here, at the end of the frame,
|
||||
// all render targets that have textures with sidecars are assumed to be destroyed.
|
||||
texture->msaaSidecar = nil;
|
||||
}
|
||||
mContext->supportsMemorylessRenderTargets = false;
|
||||
}
|
||||
|
||||
#if defined(FILAMENT_METAL_PROFILING)
|
||||
os_signpost_interval_end(mContext->log, mContext->signpostId, "Frame encoding");
|
||||
#endif
|
||||
@@ -226,7 +243,7 @@ void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elem
|
||||
|
||||
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage) {
|
||||
construct_handle<MetalBufferObject>(boh, *mContext, usage, byteCount);
|
||||
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
@@ -236,13 +253,10 @@ void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8
|
||||
auto& sc = mContext->sampleCountLookup;
|
||||
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
|
||||
|
||||
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
|
||||
width, height, depth, usage, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1,
|
||||
TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3);
|
||||
|
||||
#ifndef NDEBUG
|
||||
mContext->aliveTextures.insert(th.getId());
|
||||
#endif
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
|
||||
target, levels, format, samples, width, height, depth, usage,
|
||||
TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1,
|
||||
TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3));
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
@@ -253,12 +267,8 @@ void MetalDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType targe
|
||||
auto& sc = mContext->sampleCountLookup;
|
||||
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
|
||||
|
||||
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
|
||||
width, height, depth, usage, r, g, b, a);
|
||||
|
||||
#ifndef NDEBUG
|
||||
mContext->aliveTextures.insert(th.getId());
|
||||
#endif
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
|
||||
target, levels, format, samples, width, height, depth, usage, r, g, b, a));
|
||||
}
|
||||
|
||||
void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
|
||||
@@ -279,12 +289,8 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
|
||||
ASSERT_PRECONDITION(metalTexture.textureType == filamentMetalType,
|
||||
"Imported id<MTLTexture> type (%d) != Filament texture type (%d)",
|
||||
metalTexture.textureType, filamentMetalType);
|
||||
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
|
||||
width, height, depth, usage, metalTexture);
|
||||
|
||||
#ifndef NDEBUG
|
||||
mContext->aliveTextures.insert(th.getId());
|
||||
#endif
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
|
||||
target, levels, format, samples, width, height, depth, usage, metalTexture));
|
||||
}
|
||||
|
||||
void MetalDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t size) {
|
||||
@@ -475,10 +481,20 @@ void MetalDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
|
||||
return;
|
||||
}
|
||||
auto* bo = handle_cast<MetalBufferObject>(boh);
|
||||
// Unbind this buffer object from any uniform slots it's still bound to.
|
||||
// Unbind this buffer object from any uniform / SSBO slots it's still bound to.
|
||||
bo->boundUniformBuffers.forEachSetBit([this](size_t index) {
|
||||
mContext->uniformState[index].buffer = nullptr;
|
||||
mContext->uniformState[index].bound = false;
|
||||
mContext->uniformState[index] = BufferState {
|
||||
.buffer = nullptr,
|
||||
.offset = 0,
|
||||
.bound = false
|
||||
};
|
||||
});
|
||||
bo->boundSsbos.forEachSetBit([this](size_t index) {
|
||||
mContext->ssboState[index] = BufferState {
|
||||
.buffer = nullptr,
|
||||
.offset = 0,
|
||||
.bound = false
|
||||
};
|
||||
});
|
||||
destruct_handle<MetalBufferObject>(boh);
|
||||
}
|
||||
@@ -515,10 +531,7 @@ void MetalDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
mContext->aliveTextures.erase(th.getId());
|
||||
#endif
|
||||
|
||||
mContext->textures.erase(handle_cast<MetalTexture>(th));
|
||||
destruct_handle<MetalTexture>(th);
|
||||
}
|
||||
|
||||
@@ -847,13 +860,13 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
|
||||
if (!samplers[s].t) {
|
||||
continue;
|
||||
}
|
||||
auto iter = mContext->aliveTextures.find(samplers[s].t.getId());
|
||||
if (iter == mContext->aliveTextures.end()) {
|
||||
auto iter = mContext->textures.find(handle_cast<MetalTexture>(samplers[s].t));
|
||||
if (iter == mContext->textures.end()) {
|
||||
utils::slog.e << "updateSamplerGroup: texture #"
|
||||
<< (int) s << " is dead, texture handle = "
|
||||
<< samplers[s].t << utils::io::endl;
|
||||
}
|
||||
assert_invariant(iter != mContext->aliveTextures.end());
|
||||
assert_invariant(iter != mContext->textures.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1048,7 +1061,7 @@ void MetalDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh)
|
||||
currentBo->boundUniformBuffers.unset(index);
|
||||
}
|
||||
bo->boundUniformBuffers.set(index);
|
||||
mContext->uniformState[index] = UniformBufferState{
|
||||
mContext->uniformState[index] = BufferState{
|
||||
.buffer = bo,
|
||||
.offset = 0,
|
||||
.bound = true
|
||||
@@ -1061,24 +1074,81 @@ void MetalDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t inde
|
||||
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
|
||||
bindingType == BufferObjectBinding::UNIFORM);
|
||||
|
||||
// TODO: implement BufferObjectBinding::SHADER_STORAGE case
|
||||
|
||||
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
|
||||
auto* bo = handle_cast<MetalBufferObject>(boh);
|
||||
auto* currentBo = mContext->uniformState[index].buffer;
|
||||
if (currentBo) {
|
||||
currentBo->boundUniformBuffers.unset(index);
|
||||
|
||||
switch (bindingType) {
|
||||
default:
|
||||
case BufferObjectBinding::UNIFORM: {
|
||||
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
|
||||
auto* currentBo = mContext->uniformState[index].buffer;
|
||||
if (currentBo) {
|
||||
currentBo->boundUniformBuffers.unset(index);
|
||||
}
|
||||
bo->boundUniformBuffers.set(index);
|
||||
mContext->uniformState[index] = BufferState {
|
||||
.buffer = bo,
|
||||
.offset = offset,
|
||||
.bound = true
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BufferObjectBinding::SHADER_STORAGE: {
|
||||
assert_invariant(index < MAX_SSBO_COUNT);
|
||||
auto* currentBo = mContext->ssboState[index].buffer;
|
||||
if (currentBo) {
|
||||
currentBo->boundSsbos.unset(index);
|
||||
}
|
||||
bo->boundSsbos.set(index);
|
||||
mContext->ssboState[index] = BufferState {
|
||||
.buffer = bo,
|
||||
.offset = offset,
|
||||
.bound = true
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
bo->boundUniformBuffers.set(index);
|
||||
mContext->uniformState[index] = UniformBufferState{
|
||||
.buffer = bo,
|
||||
.offset = offset,
|
||||
.bound = true
|
||||
};
|
||||
}
|
||||
|
||||
void MetalDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
|
||||
// TODO: implement unbindBuffer()
|
||||
|
||||
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
|
||||
bindingType == BufferObjectBinding::UNIFORM);
|
||||
|
||||
switch (bindingType) {
|
||||
default:
|
||||
case BufferObjectBinding::UNIFORM: {
|
||||
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
|
||||
auto* currentBo = mContext->uniformState[index].buffer;
|
||||
if (currentBo) {
|
||||
currentBo->boundUniformBuffers.unset(index);
|
||||
}
|
||||
mContext->uniformState[index] = BufferState {
|
||||
.buffer = nullptr,
|
||||
.offset = 0,
|
||||
.bound = false
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BufferObjectBinding::SHADER_STORAGE: {
|
||||
assert_invariant(index < MAX_SSBO_COUNT);
|
||||
auto* currentBo = mContext->ssboState[index].buffer;
|
||||
if (currentBo) {
|
||||
currentBo->boundSsbos.unset(index);
|
||||
}
|
||||
mContext->ssboState[index] = BufferState {
|
||||
.buffer = nullptr,
|
||||
.offset = 0,
|
||||
.bound = false
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
@@ -1307,13 +1377,13 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
|
||||
if (!handles[s]) {
|
||||
continue;
|
||||
}
|
||||
auto iter = mContext->aliveTextures.find(handles[s].getId());
|
||||
if (iter == mContext->aliveTextures.end()) {
|
||||
auto iter = mContext->textures.find(handle_cast<MetalTexture>(handles[s]));
|
||||
if (iter == mContext->textures.end()) {
|
||||
utils::slog.e << "finalizeSamplerGroup: texture #"
|
||||
<< (int) s << " is dead, texture handle = "
|
||||
<< handles[s] << utils::io::endl;
|
||||
}
|
||||
assert_invariant(iter != mContext->aliveTextures.end());
|
||||
assert_invariant(iter != mContext->textures.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1549,8 +1619,9 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
|
||||
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
|
||||
|
||||
enumerateBoundUniformBuffers([&uniformsToBind, &offsets](const UniformBufferState& state,
|
||||
MetalBuffer* buffer, uint32_t index) {
|
||||
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
|
||||
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
|
||||
uint32_t index) {
|
||||
uniformsToBind[index] = buffer;
|
||||
offsets[index] = state.offset;
|
||||
});
|
||||
@@ -1632,7 +1703,67 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
}
|
||||
|
||||
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
// FIXME: implement me
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"dispatchCompute must be called outside of a render pass.");
|
||||
|
||||
auto mtlProgram = handle_cast<MetalProgram>(program);
|
||||
|
||||
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
|
||||
// during the draw call when the program is invalid. The shader compile error has already been
|
||||
// dumped to the console at this point, so it's fine to simply return early.
|
||||
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!mtlProgram->isValid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert_invariant(mtlProgram->isValid && mtlProgram->computeFunction);
|
||||
|
||||
id<MTLComputeCommandEncoder> computeEncoder =
|
||||
[getPendingCommandBuffer(mContext) computeCommandEncoder];
|
||||
|
||||
NSError* error = nil;
|
||||
id<MTLComputePipelineState> computePipelineState =
|
||||
[mContext->device newComputePipelineStateWithFunction:mtlProgram->computeFunction
|
||||
error:&error];
|
||||
if (error) {
|
||||
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.e << description << utils::io::endl;
|
||||
}
|
||||
assert_invariant(!error);
|
||||
|
||||
// Bind uniform buffers.
|
||||
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
|
||||
NSUInteger uniformOffsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
|
||||
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
|
||||
[&uniformsToBind, &uniformOffsets](const BufferState& state, MetalBuffer* buffer,
|
||||
uint32_t index) {
|
||||
uniformsToBind[index] = buffer;
|
||||
uniformOffsets[index] = state.offset;
|
||||
});
|
||||
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), computeEncoder,
|
||||
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::COMPUTE, uniformsToBind,
|
||||
uniformOffsets, Program::UNIFORM_BINDING_COUNT);
|
||||
|
||||
// Bind SSBOs.
|
||||
MetalBuffer* ssbosToBind[MAX_SSBO_COUNT] = { nil };
|
||||
NSUInteger ssboOffsets[MAX_SSBO_COUNT] = { 0 };
|
||||
enumerateBoundBuffers(BufferObjectBinding::SHADER_STORAGE,
|
||||
[&ssbosToBind, &ssboOffsets](const BufferState& state, MetalBuffer* buffer,
|
||||
uint32_t index) {
|
||||
ssbosToBind[index] = buffer;
|
||||
ssboOffsets[index] = state.offset;
|
||||
});
|
||||
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), computeEncoder, SSBO_BINDING_START,
|
||||
MetalBuffer::Stage::COMPUTE, ssbosToBind, ssboOffsets, MAX_SSBO_COUNT);
|
||||
|
||||
[computeEncoder setComputePipelineState:computePipelineState];
|
||||
|
||||
MTLSize threadgroupsPerGrid = MTLSizeMake(workGroupCount.x, workGroupCount.y, workGroupCount.z);
|
||||
// FIXME: the threadgroup size should be specified in the Program
|
||||
MTLSize threadsPerThreadgroup = MTLSizeMake(16u, 1u, 1u);
|
||||
[computeEncoder dispatchThreadgroups:threadgroupsPerGrid
|
||||
threadsPerThreadgroup:threadsPerThreadgroup];
|
||||
|
||||
[computeEncoder endEncoding];
|
||||
}
|
||||
|
||||
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
@@ -1649,17 +1780,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>;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -429,8 +433,7 @@ private:
|
||||
|
||||
static MTLLoadAction getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
static MTLStoreAction getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
static id<MTLTexture> createMultisampledTexture(id<MTLDevice> device, MTLPixelFormat format,
|
||||
uint32_t width, uint32_t height, uint8_t samples);
|
||||
id<MTLTexture> createMultisampledTexture(MTLPixelFormat format, uint32_t width, uint32_t height, uint8_t samples) const;
|
||||
|
||||
MetalContext* context;
|
||||
bool defaultRenderTarget = false;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -908,7 +916,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (samples > 1 && color[i].getSampleCount() == 1) {
|
||||
auto& sidecar = color[i].metalTexture->msaaSidecar;
|
||||
if (!sidecar) {
|
||||
sidecar = createMultisampledTexture(context->device, color[i].getPixelFormat(),
|
||||
sidecar = createMultisampledTexture(color[i].getPixelFormat(),
|
||||
color[i].metalTexture->width, color[i].metalTexture->height, samples);
|
||||
}
|
||||
}
|
||||
@@ -933,7 +941,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (samples > 1 && depth.getSampleCount() == 1) {
|
||||
auto& sidecar = depth.metalTexture->msaaSidecar;
|
||||
if (!sidecar) {
|
||||
sidecar = createMultisampledTexture(context->device, depth.getPixelFormat(),
|
||||
sidecar = createMultisampledTexture(depth.getPixelFormat(),
|
||||
depth.metalTexture->width, depth.metalTexture->height, samples);
|
||||
}
|
||||
}
|
||||
@@ -958,7 +966,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (samples > 1 && stencil.getSampleCount() == 1) {
|
||||
auto& sidecar = stencil.metalTexture->msaaSidecar;
|
||||
if (!sidecar) {
|
||||
sidecar = createMultisampledTexture(context->device, stencil.getPixelFormat(),
|
||||
sidecar = createMultisampledTexture(stencil.getPixelFormat(),
|
||||
stencil.metalTexture->width, stencil.metalTexture->height, samples);
|
||||
}
|
||||
}
|
||||
@@ -1142,8 +1150,8 @@ MTLStoreAction MetalRenderTarget::getStoreAction(const RenderPassParams& params,
|
||||
return MTLStoreActionStore;
|
||||
}
|
||||
|
||||
id<MTLTexture> MetalRenderTarget::createMultisampledTexture(id<MTLDevice> device,
|
||||
MTLPixelFormat format, uint32_t width, uint32_t height, uint8_t samples) {
|
||||
id<MTLTexture> MetalRenderTarget::createMultisampledTexture(MTLPixelFormat format,
|
||||
uint32_t width, uint32_t height, uint8_t samples) const {
|
||||
MTLTextureDescriptor* descriptor =
|
||||
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
|
||||
width:width
|
||||
@@ -1154,7 +1162,13 @@ id<MTLTexture> MetalRenderTarget::createMultisampledTexture(id<MTLDevice> device
|
||||
descriptor.usage = MTLTextureUsageRenderTarget;
|
||||
descriptor.resourceOptions = MTLResourceStorageModePrivate;
|
||||
|
||||
return [device newTextureWithDescriptor:descriptor];
|
||||
if (context->supportsMemorylessRenderTargets) {
|
||||
if (@available(macOS 11.0, *)) {
|
||||
descriptor.resourceOptions = MTLResourceStorageModeMemoryless;
|
||||
}
|
||||
}
|
||||
|
||||
return [context->device newTextureWithDescriptor:descriptor];
|
||||
}
|
||||
|
||||
math::uint2 MetalRenderTarget::getAttachmentSize() noexcept {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -328,4 +328,7 @@ void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
void NoopDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
}
|
||||
|
||||
void NoopDriver::resetState(int) {
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -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);
|
||||
@@ -3185,7 +3188,8 @@ void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
|
||||
useProgram(p);
|
||||
|
||||
#if defined(GL_ES_VERSION_3_1) || defined(GL_VERSION_4_3)
|
||||
glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z);
|
||||
// FIXME: this line is commented-out for now due to issues switching G3 clients to API 21.
|
||||
// glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z);
|
||||
#endif
|
||||
|
||||
#ifdef FILAMENT_ENABLE_MATDBG
|
||||
|
||||
@@ -35,9 +35,11 @@
|
||||
#include <jni.h>
|
||||
|
||||
|
||||
// We require filament to be built with a API 21 toolchain, before that, OpenGLES 3.1 didn't exist
|
||||
#if __ANDROID_API__ < 21
|
||||
# error "__ANDROID_API__ must be at least 21"
|
||||
// We require filament to be built with a API 19 toolchain, before that, OpenGLES 3.0 didn't exist
|
||||
// Actually, OpenGL ES 3.0 was added to API 18, but API 19 is the better target and
|
||||
// the minimum for Jetpack at the time of this comment.
|
||||
#if __ANDROID_API__ < 19
|
||||
# error "__ANDROID_API__ must be at least 19"
|
||||
#endif
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -1583,7 +1583,8 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = mContext.selectMemoryType(memReqs.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
|
||||
};
|
||||
|
||||
vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory);
|
||||
@@ -2011,6 +2012,9 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanDriver::resetState(int) {
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
template class ConcreteDispatcher<VulkanDriver>;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -281,7 +281,6 @@ public:
|
||||
* By default, this is an identity matrix.
|
||||
*
|
||||
* @param shift x and y translation added to the projection matrix, specified in NDC
|
||||
* @param shift x and y translation added to the projection matrix, specified in NDC
|
||||
* coordinates, that is, if the translation must be specified in pixels,
|
||||
* shift must be scaled by 1.0 / { viewport.width, viewport.height }.
|
||||
*
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -226,7 +226,7 @@ public:
|
||||
* @see ShadowCascades::computeLogSplits
|
||||
* @see ShadowCascades::computePracticalSplits
|
||||
*/
|
||||
float cascadeSplitPositions[3] = { 0.25f, 0.50f, 0.75f };
|
||||
float cascadeSplitPositions[3] = { 0.125f, 0.25f, 0.50f };
|
||||
|
||||
/** Constant bias in world units (e.g. meters) by which shadows are moved away from the
|
||||
* light. 1mm by default.
|
||||
@@ -348,6 +348,14 @@ public:
|
||||
*/
|
||||
uint8_t msaaSamples = 1;
|
||||
|
||||
/**
|
||||
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
|
||||
* an improvement to the default EVSM which suffers important light leaks. Enabling
|
||||
* ELVSM for a single shadowmap doubles the memory usage of all shadow maps.
|
||||
* ELVSM is mostly useful when large blurs are used.
|
||||
*/
|
||||
bool elvsm = false;
|
||||
|
||||
/**
|
||||
* Blur width for the VSM blur. Zero do disable.
|
||||
* The maximum value is 125.
|
||||
@@ -438,9 +446,6 @@ public:
|
||||
* @param enable Enables or disables casting shadows from this Light.
|
||||
*
|
||||
* @return This Builder, for chaining calls.
|
||||
*
|
||||
* @warning
|
||||
* - Only a Type.DIRECTIONAL, Type.SUN, Type.SPOT, or Type.FOCUSED_SPOT light can cast shadows
|
||||
*/
|
||||
Builder& castShadows(bool enable) noexcept;
|
||||
|
||||
|
||||
@@ -390,6 +390,14 @@ struct VsmShadowOptions {
|
||||
*/
|
||||
bool mipmapping = false;
|
||||
|
||||
/**
|
||||
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
|
||||
* precision is rarely needed, but it does reduces light leaks as well as "fading"
|
||||
* of the shadows in some situations. Setting highPrecision to true for a single
|
||||
* shadow map will double the memory usage of all shadow maps.
|
||||
*/
|
||||
bool highPrecision = false;
|
||||
|
||||
/**
|
||||
* VSM minimum variance scale, must be positive.
|
||||
*/
|
||||
|
||||
@@ -24,11 +24,11 @@ namespace filament {
|
||||
|
||||
void BufferObject::setBuffer(Engine& engine,
|
||||
BufferObject::BufferDescriptor&& buffer, uint32_t byteOffset) {
|
||||
upcast(this)->setBuffer(upcast(engine), std::move(buffer), byteOffset);
|
||||
downcast(this)->setBuffer(downcast(engine), std::move(buffer), byteOffset);
|
||||
}
|
||||
|
||||
size_t BufferObject::getByteCount() const noexcept {
|
||||
return upcast(this)->getByteCount();
|
||||
return downcast(this)->getByteCount();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+32
-32
@@ -69,134 +69,134 @@ mat4 Camera::inverseProjection(const mat4 & p) noexcept {
|
||||
|
||||
void Camera::setProjection(Camera::Projection projection, double left, double right, double bottom,
|
||||
double top, double near, double far) {
|
||||
upcast(this)->setProjection(projection, left, right, bottom, top, near, far);
|
||||
downcast(this)->setProjection(projection, left, right, bottom, top, near, far);
|
||||
}
|
||||
|
||||
void Camera::setProjection(double fovInDegrees, double aspect, double near, double far,
|
||||
Camera::Fov direction) {
|
||||
upcast(this)->setProjection(fovInDegrees, aspect, near, far, direction);
|
||||
downcast(this)->setProjection(fovInDegrees, aspect, near, far, direction);
|
||||
}
|
||||
|
||||
void Camera::setLensProjection(double focalLengthInMillimeters,
|
||||
double aspect, double near, double far) {
|
||||
upcast(this)->setLensProjection(focalLengthInMillimeters, aspect, near, far);
|
||||
downcast(this)->setLensProjection(focalLengthInMillimeters, aspect, near, far);
|
||||
}
|
||||
|
||||
void Camera::setCustomProjection(mat4 const& projection, double near, double far) noexcept {
|
||||
upcast(this)->setCustomProjection(projection, near, far);
|
||||
downcast(this)->setCustomProjection(projection, near, far);
|
||||
}
|
||||
|
||||
void Camera::setCustomProjection(mat4 const& projection, mat4 const& projectionForCulling,
|
||||
double near, double far) noexcept {
|
||||
upcast(this)->setCustomProjection(projection, projectionForCulling, near, far);
|
||||
downcast(this)->setCustomProjection(projection, projectionForCulling, near, far);
|
||||
}
|
||||
|
||||
void Camera::setScaling(double2 scaling) noexcept {
|
||||
upcast(this)->setScaling(scaling);
|
||||
downcast(this)->setScaling(scaling);
|
||||
}
|
||||
|
||||
void Camera::setShift(double2 shift) noexcept {
|
||||
upcast(this)->setShift(shift);
|
||||
downcast(this)->setShift(shift);
|
||||
}
|
||||
|
||||
mat4 Camera::getProjectionMatrix() const noexcept {
|
||||
return upcast(this)->getUserProjectionMatrix();
|
||||
return downcast(this)->getUserProjectionMatrix();
|
||||
}
|
||||
|
||||
mat4 Camera::getCullingProjectionMatrix() const noexcept {
|
||||
return upcast(this)->getUserCullingProjectionMatrix();
|
||||
return downcast(this)->getUserCullingProjectionMatrix();
|
||||
}
|
||||
|
||||
double4 Camera::getScaling() const noexcept {
|
||||
return upcast(this)->getScaling();
|
||||
return downcast(this)->getScaling();
|
||||
}
|
||||
|
||||
double2 Camera::getShift() const noexcept {
|
||||
return upcast(this)->getShift();
|
||||
return downcast(this)->getShift();
|
||||
}
|
||||
|
||||
double Camera::getNear() const noexcept {
|
||||
return upcast(this)->getNear();
|
||||
return downcast(this)->getNear();
|
||||
}
|
||||
|
||||
double Camera::getCullingFar() const noexcept {
|
||||
return upcast(this)->getCullingFar();
|
||||
return downcast(this)->getCullingFar();
|
||||
}
|
||||
|
||||
void Camera::setModelMatrix(const mat4& modelMatrix) noexcept {
|
||||
upcast(this)->setModelMatrix(modelMatrix);
|
||||
downcast(this)->setModelMatrix(modelMatrix);
|
||||
}
|
||||
|
||||
void Camera::setModelMatrix(const mat4f& modelMatrix) noexcept {
|
||||
upcast(this)->setModelMatrix(modelMatrix);
|
||||
downcast(this)->setModelMatrix(modelMatrix);
|
||||
}
|
||||
|
||||
void Camera::lookAt(double3 const& eye, double3 const& center, double3 const& up) noexcept {
|
||||
upcast(this)->lookAt(eye, center, up);
|
||||
downcast(this)->lookAt(eye, center, up);
|
||||
}
|
||||
|
||||
mat4 Camera::getModelMatrix() const noexcept {
|
||||
return upcast(this)->getModelMatrix();
|
||||
return downcast(this)->getModelMatrix();
|
||||
}
|
||||
|
||||
mat4 Camera::getViewMatrix() const noexcept {
|
||||
return upcast(this)->getViewMatrix();
|
||||
return downcast(this)->getViewMatrix();
|
||||
}
|
||||
|
||||
double3 Camera::getPosition() const noexcept {
|
||||
return upcast(this)->getPosition();
|
||||
return downcast(this)->getPosition();
|
||||
}
|
||||
|
||||
float3 Camera::getLeftVector() const noexcept {
|
||||
return upcast(this)->getLeftVector();
|
||||
return downcast(this)->getLeftVector();
|
||||
}
|
||||
|
||||
float3 Camera::getUpVector() const noexcept {
|
||||
return upcast(this)->getUpVector();
|
||||
return downcast(this)->getUpVector();
|
||||
}
|
||||
|
||||
float3 Camera::getForwardVector() const noexcept {
|
||||
return upcast(this)->getForwardVector();
|
||||
return downcast(this)->getForwardVector();
|
||||
}
|
||||
|
||||
float Camera::getFieldOfViewInDegrees(Camera::Fov direction) const noexcept {
|
||||
return upcast(this)->getFieldOfViewInDegrees(direction);
|
||||
return downcast(this)->getFieldOfViewInDegrees(direction);
|
||||
}
|
||||
|
||||
Frustum Camera::getFrustum() const noexcept {
|
||||
return upcast(this)->getCullingFrustum();
|
||||
return downcast(this)->getCullingFrustum();
|
||||
}
|
||||
|
||||
utils::Entity Camera::getEntity() const noexcept {
|
||||
return upcast(this)->getEntity();
|
||||
return downcast(this)->getEntity();
|
||||
}
|
||||
|
||||
void Camera::setExposure(float aperture, float shutterSpeed, float ISO) noexcept {
|
||||
upcast(this)->setExposure(aperture, shutterSpeed, ISO);
|
||||
downcast(this)->setExposure(aperture, shutterSpeed, ISO);
|
||||
}
|
||||
|
||||
float Camera::getAperture() const noexcept {
|
||||
return upcast(this)->getAperture();
|
||||
return downcast(this)->getAperture();
|
||||
}
|
||||
|
||||
float Camera::getShutterSpeed() const noexcept {
|
||||
return upcast(this)->getShutterSpeed();
|
||||
return downcast(this)->getShutterSpeed();
|
||||
}
|
||||
|
||||
float Camera::getSensitivity() const noexcept {
|
||||
return upcast(this)->getSensitivity();
|
||||
return downcast(this)->getSensitivity();
|
||||
}
|
||||
|
||||
void Camera::setFocusDistance(float distance) noexcept {
|
||||
upcast(this)->setFocusDistance(distance);
|
||||
downcast(this)->setFocusDistance(distance);
|
||||
}
|
||||
|
||||
float Camera::getFocusDistance() const noexcept {
|
||||
return upcast(this)->getFocusDistance();
|
||||
return downcast(this)->getFocusDistance();
|
||||
}
|
||||
|
||||
double Camera::getFocalLength() const noexcept {
|
||||
return upcast(this)->getFocalLength();
|
||||
return downcast(this)->getFocalLength();
|
||||
}
|
||||
|
||||
double Camera::computeEffectiveFocalLength(double focalLength, double focusDistance) noexcept {
|
||||
|
||||
@@ -86,7 +86,10 @@ void Culler::intersects(
|
||||
visible &= fast::signbit(dot) << bit;
|
||||
}
|
||||
|
||||
results[i] |= result_type(visible);
|
||||
auto r = results[i];
|
||||
r &= ~result_type(1u << bit);
|
||||
r |= result_type(visible);
|
||||
results[i] = r;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,22 +104,20 @@ bool Culler::intersects(Frustum const& frustum, Box const& box) noexcept {
|
||||
Culler::result_type results[MODULO];
|
||||
centers[0] = box.center;
|
||||
extents[0] = box.halfExtent;
|
||||
results[0] = 0;
|
||||
Culler::intersects(results, frustum, centers, extents, MODULO, 0);
|
||||
return bool(results[0]);
|
||||
return bool(results[0] & 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* returns whether an sphere intersects with the frustum
|
||||
* returns whether a sphere intersects with the frustum
|
||||
*/
|
||||
bool Culler::intersects(Frustum const& frustum, float4 const& sphere) noexcept {
|
||||
// The main intersection routine assumes multiples of 8 items
|
||||
float4 spheres[MODULO];
|
||||
Culler::result_type results[MODULO];
|
||||
spheres[0] = sphere;
|
||||
results[0] = 0;
|
||||
Culler::intersects(results, frustum, spheres, MODULO);
|
||||
return bool(results[0]);
|
||||
return bool(results[0] & 1);
|
||||
}
|
||||
|
||||
// For testing...
|
||||
|
||||
@@ -42,7 +42,7 @@ void DFG::init(FEngine& engine) noexcept {
|
||||
|
||||
lut->setImage(engine, 0, std::move(buffer));
|
||||
|
||||
mLUT = upcast(lut);
|
||||
mLUT = downcast(lut);
|
||||
}
|
||||
|
||||
void DFG::terminate(FEngine& engine) noexcept {
|
||||
|
||||
@@ -25,64 +25,64 @@ namespace filament {
|
||||
using namespace math;
|
||||
|
||||
bool DebugRegistry::hasProperty(const char* name) const noexcept {
|
||||
return upcast(this)->hasProperty(name);
|
||||
return downcast(this)->hasProperty(name);
|
||||
}
|
||||
|
||||
bool DebugRegistry::setProperty(const char* name, bool v) noexcept {
|
||||
return upcast(this)->setProperty(name, v);
|
||||
return downcast(this)->setProperty(name, v);
|
||||
}
|
||||
|
||||
bool DebugRegistry::setProperty(const char* name, int v) noexcept {
|
||||
return upcast(this)->setProperty(name, v);
|
||||
return downcast(this)->setProperty(name, v);
|
||||
}
|
||||
|
||||
bool DebugRegistry::setProperty(const char* name, float v) noexcept {
|
||||
return upcast(this)->setProperty(name, v);
|
||||
return downcast(this)->setProperty(name, v);
|
||||
}
|
||||
|
||||
bool DebugRegistry::setProperty(const char* name, float2 v) noexcept {
|
||||
return upcast(this)->setProperty(name, v);
|
||||
return downcast(this)->setProperty(name, v);
|
||||
}
|
||||
|
||||
bool DebugRegistry::setProperty(const char* name, float3 v) noexcept {
|
||||
return upcast(this)->setProperty(name, v);
|
||||
return downcast(this)->setProperty(name, v);
|
||||
}
|
||||
|
||||
bool DebugRegistry::setProperty(const char* name, float4 v) noexcept {
|
||||
return upcast(this)->setProperty(name, v);
|
||||
return downcast(this)->setProperty(name, v);
|
||||
}
|
||||
|
||||
|
||||
bool DebugRegistry::getProperty(const char* name, bool* v) const noexcept {
|
||||
return upcast(this)->getProperty(name, v);
|
||||
return downcast(this)->getProperty(name, v);
|
||||
}
|
||||
|
||||
bool DebugRegistry::getProperty(const char* name, int* v) const noexcept {
|
||||
return upcast(this)->getProperty(name, v);
|
||||
return downcast(this)->getProperty(name, v);
|
||||
}
|
||||
|
||||
bool DebugRegistry::getProperty(const char* name, float* v) const noexcept {
|
||||
return upcast(this)->getProperty(name, v);
|
||||
return downcast(this)->getProperty(name, v);
|
||||
}
|
||||
|
||||
bool DebugRegistry::getProperty(const char* name, float2* v) const noexcept {
|
||||
return upcast(this)->getProperty(name, v);
|
||||
return downcast(this)->getProperty(name, v);
|
||||
}
|
||||
|
||||
bool DebugRegistry::getProperty(const char* name, float3* v) const noexcept {
|
||||
return upcast(this)->getProperty(name, v);
|
||||
return downcast(this)->getProperty(name, v);
|
||||
}
|
||||
|
||||
bool DebugRegistry::getProperty(const char* name, float4* v) const noexcept {
|
||||
return upcast(this)->getProperty(name, v);
|
||||
return downcast(this)->getProperty(name, v);
|
||||
}
|
||||
|
||||
void *DebugRegistry::getPropertyAddress(const char *name) noexcept {
|
||||
return upcast(this)->getPropertyAddress(name);
|
||||
return downcast(this)->getPropertyAddress(name);
|
||||
}
|
||||
|
||||
DebugRegistry::DataSource DebugRegistry::getDataSource(const char* name) const noexcept {
|
||||
return upcast(this)->getDataSource(name);
|
||||
return downcast(this)->getDataSource(name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+56
-50
@@ -51,7 +51,7 @@ Engine* Engine::create(Backend backend, Platform* platform, void* sharedGLContex
|
||||
}
|
||||
|
||||
void Engine::destroy(Engine* engine) {
|
||||
FEngine::destroy(upcast(engine));
|
||||
FEngine::destroy(downcast(engine));
|
||||
}
|
||||
|
||||
#if UTILS_HAS_THREADING
|
||||
@@ -68,7 +68,7 @@ Engine* Engine::getEngine(void* token) {
|
||||
void Engine::destroy(Engine** pEngine) {
|
||||
if (pEngine) {
|
||||
Engine* engine = *pEngine;
|
||||
FEngine::destroy(upcast(engine));
|
||||
FEngine::destroy(downcast(engine));
|
||||
*pEngine = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -78,151 +78,151 @@ void Engine::destroy(Engine** pEngine) {
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
const Material* Engine::getDefaultMaterial() const noexcept {
|
||||
return upcast(this)->getDefaultMaterial();
|
||||
return downcast(this)->getDefaultMaterial();
|
||||
}
|
||||
|
||||
Backend Engine::getBackend() const noexcept {
|
||||
return upcast(this)->getBackend();
|
||||
return downcast(this)->getBackend();
|
||||
}
|
||||
|
||||
Platform* Engine::getPlatform() const noexcept {
|
||||
return upcast(this)->getPlatform();
|
||||
return downcast(this)->getPlatform();
|
||||
}
|
||||
|
||||
Renderer* Engine::createRenderer() noexcept {
|
||||
return upcast(this)->createRenderer();
|
||||
return downcast(this)->createRenderer();
|
||||
}
|
||||
|
||||
View* Engine::createView() noexcept {
|
||||
return upcast(this)->createView();
|
||||
return downcast(this)->createView();
|
||||
}
|
||||
|
||||
Scene* Engine::createScene() noexcept {
|
||||
return upcast(this)->createScene();
|
||||
return downcast(this)->createScene();
|
||||
}
|
||||
|
||||
Camera* Engine::createCamera(Entity entity) noexcept {
|
||||
return upcast(this)->createCamera(entity);
|
||||
return downcast(this)->createCamera(entity);
|
||||
}
|
||||
|
||||
Camera* Engine::getCameraComponent(utils::Entity entity) noexcept {
|
||||
return upcast(this)->getCameraComponent(entity);
|
||||
return downcast(this)->getCameraComponent(entity);
|
||||
}
|
||||
|
||||
void Engine::destroyCameraComponent(utils::Entity entity) noexcept {
|
||||
upcast(this)->destroyCameraComponent(entity);
|
||||
downcast(this)->destroyCameraComponent(entity);
|
||||
}
|
||||
|
||||
Fence* Engine::createFence() noexcept {
|
||||
return upcast(this)->createFence(FFence::Type::SOFT);
|
||||
return downcast(this)->createFence(FFence::Type::SOFT);
|
||||
}
|
||||
|
||||
SwapChain* Engine::createSwapChain(void* nativeWindow, uint64_t flags) noexcept {
|
||||
return upcast(this)->createSwapChain(nativeWindow, flags);
|
||||
return downcast(this)->createSwapChain(nativeWindow, flags);
|
||||
}
|
||||
|
||||
SwapChain* Engine::createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept {
|
||||
return upcast(this)->createSwapChain(width, height, flags);
|
||||
return downcast(this)->createSwapChain(width, height, flags);
|
||||
}
|
||||
|
||||
bool Engine::destroy(const BufferObject* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const VertexBuffer* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const IndexBuffer* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const SkinningBuffer* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const MorphTargetBuffer* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const IndirectLight* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const Material* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const MaterialInstance* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const Renderer* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const View* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const Scene* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const Skybox* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const ColorGrading* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const Stream* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const Texture* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const RenderTarget* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const Fence* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
bool Engine::destroy(const SwapChain* p) {
|
||||
return upcast(this)->destroy(upcast(p));
|
||||
return downcast(this)->destroy(downcast(p));
|
||||
}
|
||||
|
||||
void Engine::destroy(Entity e) {
|
||||
upcast(this)->destroy(e);
|
||||
downcast(this)->destroy(e);
|
||||
}
|
||||
|
||||
void Engine::flushAndWait() {
|
||||
upcast(this)->flushAndWait();
|
||||
downcast(this)->flushAndWait();
|
||||
}
|
||||
|
||||
void Engine::flush() {
|
||||
upcast(this)->flush();
|
||||
downcast(this)->flush();
|
||||
}
|
||||
|
||||
utils::EntityManager& Engine::getEntityManager() noexcept {
|
||||
return upcast(this)->getEntityManager();
|
||||
return downcast(this)->getEntityManager();
|
||||
}
|
||||
|
||||
RenderableManager& Engine::getRenderableManager() noexcept {
|
||||
return upcast(this)->getRenderableManager();
|
||||
return downcast(this)->getRenderableManager();
|
||||
}
|
||||
|
||||
LightManager& Engine::getLightManager() noexcept {
|
||||
return upcast(this)->getLightManager();
|
||||
return downcast(this)->getLightManager();
|
||||
}
|
||||
|
||||
TransformManager& Engine::getTransformManager() noexcept {
|
||||
return upcast(this)->getTransformManager();
|
||||
return downcast(this)->getTransformManager();
|
||||
}
|
||||
|
||||
void Engine::enableAccurateTranslations() noexcept {
|
||||
@@ -230,47 +230,53 @@ void Engine::enableAccurateTranslations() noexcept {
|
||||
}
|
||||
|
||||
void* Engine::streamAlloc(size_t size, size_t alignment) noexcept {
|
||||
return upcast(this)->streamAlloc(size, alignment);
|
||||
return downcast(this)->streamAlloc(size, alignment);
|
||||
}
|
||||
|
||||
// The external-facing execute does a flush, and is meant only for single-threaded environments.
|
||||
// It also discards the boolean return value, which would otherwise indicate a thread exit.
|
||||
void Engine::execute() {
|
||||
ASSERT_PRECONDITION(!UTILS_HAS_THREADING, "Execute is meant for single-threaded platforms.");
|
||||
upcast(this)->flush();
|
||||
upcast(this)->execute();
|
||||
downcast(this)->flush();
|
||||
downcast(this)->execute();
|
||||
}
|
||||
|
||||
utils::JobSystem& Engine::getJobSystem() noexcept {
|
||||
return upcast(this)->getJobSystem();
|
||||
return downcast(this)->getJobSystem();
|
||||
}
|
||||
|
||||
DebugRegistry& Engine::getDebugRegistry() noexcept {
|
||||
return upcast(this)->getDebugRegistry();
|
||||
return downcast(this)->getDebugRegistry();
|
||||
}
|
||||
|
||||
void Engine::pumpMessageQueues() {
|
||||
upcast(this)->pumpMessageQueues();
|
||||
downcast(this)->pumpMessageQueues();
|
||||
}
|
||||
|
||||
void Engine::setAutomaticInstancingEnabled(bool enable) noexcept {
|
||||
upcast(this)->setAutomaticInstancingEnabled(enable);
|
||||
downcast(this)->setAutomaticInstancingEnabled(enable);
|
||||
}
|
||||
|
||||
bool Engine::isAutomaticInstancingEnabled() const noexcept {
|
||||
return upcast(this)->isAutomaticInstancingEnabled();
|
||||
return downcast(this)->isAutomaticInstancingEnabled();
|
||||
}
|
||||
|
||||
FeatureLevel Engine::getSupportedFeatureLevel() const noexcept {
|
||||
return upcast(this)->getSupportedFeatureLevel();
|
||||
return downcast(this)->getSupportedFeatureLevel();
|
||||
}
|
||||
|
||||
FeatureLevel Engine::setActiveFeatureLevel(FeatureLevel featureLevel) {
|
||||
return upcast(this)->setActiveFeatureLevel(featureLevel);
|
||||
return downcast(this)->setActiveFeatureLevel(featureLevel);
|
||||
}
|
||||
|
||||
FeatureLevel Engine::getActiveFeatureLevel() const noexcept {
|
||||
return upcast(this)->getActiveFeatureLevel();
|
||||
return downcast(this)->getActiveFeatureLevel();
|
||||
}
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
void Engine::resetBackendState() noexcept {
|
||||
downcast(this)->resetBackendState();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace filament {
|
||||
namespace Exposure {
|
||||
|
||||
float ev100(const Camera& c) noexcept {
|
||||
const FCamera& camera = upcast(c);
|
||||
const FCamera& camera = downcast(c);
|
||||
return ev100(camera.getAperture(), camera.getShutterSpeed(), camera.getSensitivity());
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ float ev100FromIlluminance(float illuminance) noexcept {
|
||||
}
|
||||
|
||||
float exposure(const Camera& c) noexcept {
|
||||
const FCamera& camera = upcast(c);
|
||||
const FCamera& camera = downcast(c);
|
||||
return exposure(camera.getAperture(), camera.getShutterSpeed(), camera.getSensitivity());
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ float exposure(float ev100) noexcept {
|
||||
}
|
||||
|
||||
float luminance(const Camera& c) noexcept {
|
||||
const FCamera& camera = upcast(c);
|
||||
const FCamera& camera = downcast(c);
|
||||
return luminance(camera.getAperture(), camera.getShutterSpeed(), camera.getSensitivity());
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ float luminance(float ev100) noexcept {
|
||||
}
|
||||
|
||||
float illuminance(const Camera& c) noexcept {
|
||||
const FCamera& camera = upcast(c);
|
||||
const FCamera& camera = downcast(c);
|
||||
return illuminance(camera.getAperture(), camera.getShutterSpeed(), camera.getSensitivity());
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ namespace filament {
|
||||
using namespace backend;
|
||||
|
||||
FenceStatus Fence::waitAndDestroy(Fence* fence, Mode mode) {
|
||||
return FFence::waitAndDestroy(upcast(fence), mode);
|
||||
return FFence::waitAndDestroy(downcast(fence), mode);
|
||||
}
|
||||
|
||||
FenceStatus Fence::wait(Mode mode, uint64_t timeout) {
|
||||
return upcast(this)->wait(mode, timeout);
|
||||
return downcast(this)->wait(mode, timeout);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace filament {
|
||||
|
||||
void IndexBuffer::setBuffer(Engine& engine,
|
||||
IndexBuffer::BufferDescriptor&& buffer, uint32_t byteOffset) {
|
||||
upcast(this)->setBuffer(upcast(engine), std::move(buffer), byteOffset);
|
||||
downcast(this)->setBuffer(downcast(engine), std::move(buffer), byteOffset);
|
||||
}
|
||||
|
||||
size_t IndexBuffer::getIndexCount() const noexcept {
|
||||
return upcast(this)->getIndexCount();
|
||||
return downcast(this)->getIndexCount();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -23,35 +23,35 @@ namespace filament {
|
||||
using namespace math;
|
||||
|
||||
void IndirectLight::setIntensity(float intensity) noexcept {
|
||||
upcast(this)->setIntensity(intensity);
|
||||
downcast(this)->setIntensity(intensity);
|
||||
}
|
||||
|
||||
float IndirectLight::getIntensity() const noexcept {
|
||||
return upcast(this)->getIntensity();
|
||||
return downcast(this)->getIntensity();
|
||||
}
|
||||
|
||||
void IndirectLight::setRotation(mat3f const& rotation) noexcept {
|
||||
upcast(this)->setRotation(rotation);
|
||||
downcast(this)->setRotation(rotation);
|
||||
}
|
||||
|
||||
const mat3f& IndirectLight::getRotation() const noexcept {
|
||||
return upcast(this)->getRotation();
|
||||
return downcast(this)->getRotation();
|
||||
}
|
||||
|
||||
Texture const* IndirectLight::getReflectionsTexture() const noexcept {
|
||||
return upcast(this)->getReflectionsTexture();
|
||||
return downcast(this)->getReflectionsTexture();
|
||||
}
|
||||
|
||||
Texture const* IndirectLight::getIrradianceTexture() const noexcept {
|
||||
return upcast(this)->getIrradianceTexture();
|
||||
return downcast(this)->getIrradianceTexture();
|
||||
}
|
||||
|
||||
float3 IndirectLight::getDirectionEstimate() const noexcept {
|
||||
return upcast(this)->getDirectionEstimate();
|
||||
return downcast(this)->getDirectionEstimate();
|
||||
}
|
||||
|
||||
float4 IndirectLight::getColorEstimate(float3 direction) const noexcept {
|
||||
return upcast(this)->getColorEstimate(direction);
|
||||
return downcast(this)->getColorEstimate(direction);
|
||||
}
|
||||
|
||||
float3 IndirectLight::getDirectionEstimate(const float3* sh) noexcept {
|
||||
|
||||
@@ -23,132 +23,132 @@ namespace filament {
|
||||
using namespace math;
|
||||
|
||||
size_t LightManager::getComponentCount() const noexcept {
|
||||
return upcast(this)->getComponentCount();
|
||||
return downcast(this)->getComponentCount();
|
||||
}
|
||||
|
||||
utils::Entity const* LightManager::getEntities() const noexcept {
|
||||
return upcast(this)->getEntities();
|
||||
return downcast(this)->getEntities();
|
||||
}
|
||||
|
||||
bool LightManager::hasComponent(Entity e) const noexcept {
|
||||
return upcast(this)->hasComponent(e);
|
||||
return downcast(this)->hasComponent(e);
|
||||
}
|
||||
|
||||
LightManager::Instance LightManager::getInstance(Entity e) const noexcept {
|
||||
return upcast(this)->getInstance(e);
|
||||
return downcast(this)->getInstance(e);
|
||||
}
|
||||
|
||||
void LightManager::destroy(Entity e) noexcept {
|
||||
return upcast(this)->destroy(e);
|
||||
return downcast(this)->destroy(e);
|
||||
}
|
||||
|
||||
void LightManager::setLightChannel(Instance i, unsigned int channel, bool enable) noexcept {
|
||||
upcast(this)->setLightChannel(i, channel, enable);
|
||||
downcast(this)->setLightChannel(i, channel, enable);
|
||||
}
|
||||
|
||||
bool LightManager::getLightChannel(LightManager::Instance i, unsigned int channel) const noexcept {
|
||||
return upcast(this)->getLightChannel(i, channel);
|
||||
return downcast(this)->getLightChannel(i, channel);
|
||||
}
|
||||
|
||||
void LightManager::setPosition(Instance i, const float3& position) noexcept {
|
||||
upcast(this)->setLocalPosition(i, position);
|
||||
downcast(this)->setLocalPosition(i, position);
|
||||
}
|
||||
|
||||
const float3& LightManager::getPosition(Instance i) const noexcept {
|
||||
return upcast(this)->getLocalPosition(i);
|
||||
return downcast(this)->getLocalPosition(i);
|
||||
}
|
||||
|
||||
void LightManager::setDirection(Instance i, const float3& direction) noexcept {
|
||||
upcast(this)->setLocalDirection(i, direction);
|
||||
downcast(this)->setLocalDirection(i, direction);
|
||||
}
|
||||
|
||||
const float3& LightManager::getDirection(Instance i) const noexcept {
|
||||
return upcast(this)->getLocalDirection(i);
|
||||
return downcast(this)->getLocalDirection(i);
|
||||
}
|
||||
|
||||
void LightManager::setColor(Instance i, const LinearColor& color) noexcept {
|
||||
upcast(this)->setColor(i, color);
|
||||
downcast(this)->setColor(i, color);
|
||||
}
|
||||
|
||||
const float3& LightManager::getColor(Instance i) const noexcept {
|
||||
return upcast(this)->getColor(i);
|
||||
return downcast(this)->getColor(i);
|
||||
}
|
||||
|
||||
void LightManager::setIntensity(Instance i, float intensity) noexcept {
|
||||
upcast(this)->setIntensity(i, intensity, FLightManager::IntensityUnit::LUMEN_LUX);
|
||||
downcast(this)->setIntensity(i, intensity, FLightManager::IntensityUnit::LUMEN_LUX);
|
||||
}
|
||||
|
||||
void LightManager::setIntensityCandela(Instance i, float intensity) noexcept {
|
||||
upcast(this)->setIntensity(i, intensity, FLightManager::IntensityUnit::CANDELA);
|
||||
downcast(this)->setIntensity(i, intensity, FLightManager::IntensityUnit::CANDELA);
|
||||
}
|
||||
|
||||
float LightManager::getIntensity(Instance i) const noexcept {
|
||||
return upcast(this)->getIntensity(i);
|
||||
return downcast(this)->getIntensity(i);
|
||||
}
|
||||
|
||||
void LightManager::setFalloff(Instance i, float radius) noexcept {
|
||||
upcast(this)->setFalloff(i, radius);
|
||||
downcast(this)->setFalloff(i, radius);
|
||||
}
|
||||
|
||||
float LightManager::getFalloff(Instance i) const noexcept {
|
||||
return upcast(this)->getFalloff(i);
|
||||
return downcast(this)->getFalloff(i);
|
||||
}
|
||||
|
||||
void LightManager::setSpotLightCone(Instance i, float inner, float outer) noexcept {
|
||||
upcast(this)->setSpotLightCone(i, inner, outer);
|
||||
downcast(this)->setSpotLightCone(i, inner, outer);
|
||||
}
|
||||
|
||||
float LightManager::getSpotLightOuterCone(Instance i) const noexcept {
|
||||
return upcast(this)->getSpotParams(i).outerClamped;
|
||||
return downcast(this)->getSpotParams(i).outerClamped;
|
||||
}
|
||||
|
||||
float LightManager::getSpotLightInnerCone(Instance i) const noexcept {
|
||||
return upcast(this)->getSpotLightInnerCone(i);
|
||||
return downcast(this)->getSpotLightInnerCone(i);
|
||||
}
|
||||
|
||||
void LightManager::setSunAngularRadius(Instance i, float angularRadius) noexcept {
|
||||
upcast(this)->setSunAngularRadius(i, angularRadius);
|
||||
downcast(this)->setSunAngularRadius(i, angularRadius);
|
||||
}
|
||||
|
||||
float LightManager::getSunAngularRadius(Instance i) const noexcept {
|
||||
float radius = upcast(this)->getSunAngularRadius(i);
|
||||
float radius = downcast(this)->getSunAngularRadius(i);
|
||||
return radius * f::RAD_TO_DEG;
|
||||
}
|
||||
|
||||
void LightManager::setSunHaloSize(Instance i, float haloSize) noexcept {
|
||||
upcast(this)->setSunHaloSize(i, haloSize);
|
||||
downcast(this)->setSunHaloSize(i, haloSize);
|
||||
}
|
||||
|
||||
float LightManager::getSunHaloSize(Instance i) const noexcept {
|
||||
return upcast(this)->getSunHaloSize(i);
|
||||
return downcast(this)->getSunHaloSize(i);
|
||||
}
|
||||
|
||||
void LightManager::setSunHaloFalloff(Instance i, float haloFalloff) noexcept {
|
||||
upcast(this)->setSunHaloFalloff(i, haloFalloff);
|
||||
downcast(this)->setSunHaloFalloff(i, haloFalloff);
|
||||
}
|
||||
|
||||
float LightManager::getSunHaloFalloff(Instance i) const noexcept {
|
||||
return upcast(this)->getSunHaloFalloff(i);
|
||||
return downcast(this)->getSunHaloFalloff(i);
|
||||
}
|
||||
|
||||
LightManager::Type LightManager::getType(LightManager::Instance i) const noexcept {
|
||||
return upcast(this)->getType(i);
|
||||
return downcast(this)->getType(i);
|
||||
}
|
||||
|
||||
const LightManager::ShadowOptions& LightManager::getShadowOptions(Instance i) const noexcept {
|
||||
return upcast(this)->getShadowOptions(i);
|
||||
return downcast(this)->getShadowOptions(i);
|
||||
}
|
||||
|
||||
void LightManager::setShadowOptions(Instance i, ShadowOptions const& options) noexcept {
|
||||
upcast(this)->setShadowOptions(i, options);
|
||||
downcast(this)->setShadowOptions(i, options);
|
||||
}
|
||||
|
||||
bool LightManager::isShadowCaster(Instance i) const noexcept {
|
||||
return upcast(this)->isShadowCaster(i);
|
||||
return downcast(this)->isShadowCaster(i);
|
||||
}
|
||||
|
||||
void LightManager::setShadowCaster(Instance i, bool castShadows) noexcept {
|
||||
upcast(this)->setShadowCaster(i, castShadows);
|
||||
downcast(this)->setShadowCaster(i, castShadows);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+28
-28
@@ -21,115 +21,115 @@ namespace filament {
|
||||
using namespace backend;
|
||||
|
||||
MaterialInstance* Material::createInstance(const char* name) const noexcept {
|
||||
return upcast(this)->createInstance(name);
|
||||
return downcast(this)->createInstance(name);
|
||||
}
|
||||
|
||||
const char* Material::getName() const noexcept {
|
||||
return upcast(this)->getName().c_str();
|
||||
return downcast(this)->getName().c_str();
|
||||
}
|
||||
|
||||
Shading Material::getShading() const noexcept {
|
||||
return upcast(this)->getShading();
|
||||
return downcast(this)->getShading();
|
||||
}
|
||||
|
||||
Interpolation Material::getInterpolation() const noexcept {
|
||||
return upcast(this)->getInterpolation();
|
||||
return downcast(this)->getInterpolation();
|
||||
}
|
||||
|
||||
BlendingMode Material::getBlendingMode() const noexcept {
|
||||
return upcast(this)->getBlendingMode();
|
||||
return downcast(this)->getBlendingMode();
|
||||
}
|
||||
|
||||
VertexDomain Material::getVertexDomain() const noexcept {
|
||||
return upcast(this)->getVertexDomain();
|
||||
return downcast(this)->getVertexDomain();
|
||||
}
|
||||
|
||||
MaterialDomain Material::getMaterialDomain() const noexcept {
|
||||
return upcast(this)->getMaterialDomain();
|
||||
return downcast(this)->getMaterialDomain();
|
||||
}
|
||||
|
||||
CullingMode Material::getCullingMode() const noexcept {
|
||||
return upcast(this)->getCullingMode();
|
||||
return downcast(this)->getCullingMode();
|
||||
}
|
||||
|
||||
TransparencyMode Material::getTransparencyMode() const noexcept {
|
||||
return upcast(this)->getTransparencyMode();
|
||||
return downcast(this)->getTransparencyMode();
|
||||
}
|
||||
|
||||
bool Material::isColorWriteEnabled() const noexcept {
|
||||
return upcast(this)->isColorWriteEnabled();
|
||||
return downcast(this)->isColorWriteEnabled();
|
||||
}
|
||||
|
||||
bool Material::isDepthWriteEnabled() const noexcept {
|
||||
return upcast(this)->isDepthWriteEnabled();
|
||||
return downcast(this)->isDepthWriteEnabled();
|
||||
}
|
||||
|
||||
bool Material::isDepthCullingEnabled() const noexcept {
|
||||
return upcast(this)->isDepthCullingEnabled();
|
||||
return downcast(this)->isDepthCullingEnabled();
|
||||
}
|
||||
|
||||
bool Material::isDoubleSided() const noexcept {
|
||||
return upcast(this)->isDoubleSided();
|
||||
return downcast(this)->isDoubleSided();
|
||||
}
|
||||
|
||||
float Material::getMaskThreshold() const noexcept {
|
||||
return upcast(this)->getMaskThreshold();
|
||||
return downcast(this)->getMaskThreshold();
|
||||
}
|
||||
|
||||
bool Material::hasShadowMultiplier() const noexcept {
|
||||
return upcast(this)->hasShadowMultiplier();
|
||||
return downcast(this)->hasShadowMultiplier();
|
||||
}
|
||||
|
||||
bool Material::hasSpecularAntiAliasing() const noexcept {
|
||||
return upcast(this)->hasSpecularAntiAliasing();
|
||||
return downcast(this)->hasSpecularAntiAliasing();
|
||||
}
|
||||
|
||||
float Material::getSpecularAntiAliasingVariance() const noexcept {
|
||||
return upcast(this)->getSpecularAntiAliasingVariance();
|
||||
return downcast(this)->getSpecularAntiAliasingVariance();
|
||||
}
|
||||
|
||||
float Material::getSpecularAntiAliasingThreshold() const noexcept {
|
||||
return upcast(this)->getSpecularAntiAliasingThreshold();
|
||||
return downcast(this)->getSpecularAntiAliasingThreshold();
|
||||
}
|
||||
|
||||
size_t Material::getParameterCount() const noexcept {
|
||||
return upcast(this)->getParameterCount();
|
||||
return downcast(this)->getParameterCount();
|
||||
}
|
||||
|
||||
size_t Material::getParameters(ParameterInfo* parameters, size_t count) const noexcept {
|
||||
return upcast(this)->getParameters(parameters, count);
|
||||
return downcast(this)->getParameters(parameters, count);
|
||||
}
|
||||
|
||||
AttributeBitset Material::getRequiredAttributes() const noexcept {
|
||||
return upcast(this)->getRequiredAttributes();
|
||||
return downcast(this)->getRequiredAttributes();
|
||||
}
|
||||
|
||||
RefractionMode Material::getRefractionMode() const noexcept {
|
||||
return upcast(this)->getRefractionMode();
|
||||
return downcast(this)->getRefractionMode();
|
||||
}
|
||||
|
||||
RefractionType Material::getRefractionType() const noexcept {
|
||||
return upcast(this)->getRefractionType();
|
||||
return downcast(this)->getRefractionType();
|
||||
}
|
||||
|
||||
ReflectionMode Material::getReflectionMode() const noexcept {
|
||||
return upcast(this)->getReflectionMode();
|
||||
return downcast(this)->getReflectionMode();
|
||||
}
|
||||
|
||||
bool Material::hasParameter(const char* name) const noexcept {
|
||||
return upcast(this)->hasParameter(name);
|
||||
return downcast(this)->hasParameter(name);
|
||||
}
|
||||
|
||||
bool Material::isSampler(const char* name) const noexcept {
|
||||
return upcast(this)->isSampler(name);
|
||||
return downcast(this)->isSampler(name);
|
||||
}
|
||||
|
||||
MaterialInstance* Material::getDefaultInstance() noexcept {
|
||||
return upcast(this)->getDefaultInstance();
|
||||
return downcast(this)->getDefaultInstance();
|
||||
}
|
||||
|
||||
MaterialInstance const* Material::getDefaultInstance() const noexcept {
|
||||
return upcast(this)->getDefaultInstance();
|
||||
return downcast(this)->getDefaultInstance();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -79,7 +79,7 @@ inline void FMaterialInstance::setParameterImpl(std::string_view name,
|
||||
|
||||
template<typename T, typename>
|
||||
void MaterialInstance::setParameter(const char* name, size_t nameLength, T const& value) {
|
||||
upcast(this)->setParameterImpl({ name, nameLength }, value);
|
||||
downcast(this)->setParameterImpl({ name, nameLength }, value);
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -126,7 +126,7 @@ template UTILS_PUBLIC void MaterialInstance::setParameter<mat4f> (const char*
|
||||
|
||||
template <typename T, typename>
|
||||
void MaterialInstance::setParameter(const char* name, size_t nameLength, const T* value, size_t count) {
|
||||
upcast(this)->setParameterImpl({ name, nameLength }, value, count);
|
||||
downcast(this)->setParameterImpl({ name, nameLength }, value, count);
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -184,111 +184,111 @@ template UTILS_PUBLIC void MaterialInstance::setParameter<mat4f> (const char*
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
Material const* MaterialInstance::getMaterial() const noexcept {
|
||||
return upcast(this)->getMaterial();
|
||||
return downcast(this)->getMaterial();
|
||||
}
|
||||
|
||||
const char* MaterialInstance::getName() const noexcept {
|
||||
return upcast(this)->getName();
|
||||
return downcast(this)->getName();
|
||||
}
|
||||
|
||||
void MaterialInstance::setParameter(const char* name, size_t nameLength, Texture const* texture,
|
||||
TextureSampler const& sampler) {
|
||||
return upcast(this)->setParameterImpl({ name, nameLength }, upcast(texture), sampler);
|
||||
return downcast(this)->setParameterImpl({ name, nameLength }, downcast(texture), sampler);
|
||||
}
|
||||
|
||||
void MaterialInstance::setParameter(
|
||||
const char* name, size_t nameLength, RgbType type, float3 color) {
|
||||
upcast(this)->setParameterImpl<float3>({ name, nameLength }, Color::toLinear(type, color));
|
||||
downcast(this)->setParameterImpl<float3>({ name, nameLength }, Color::toLinear(type, color));
|
||||
}
|
||||
|
||||
void MaterialInstance::setParameter(
|
||||
const char* name, size_t nameLength, RgbaType type, float4 color) {
|
||||
upcast(this)->setParameterImpl<float4>({ name, nameLength }, Color::toLinear(type, color));
|
||||
downcast(this)->setParameterImpl<float4>({ name, nameLength }, Color::toLinear(type, color));
|
||||
}
|
||||
|
||||
void MaterialInstance::setScissor(
|
||||
uint32_t left, uint32_t bottom, uint32_t width, uint32_t height) noexcept {
|
||||
upcast(this)->setScissor(left, bottom, width, height);
|
||||
downcast(this)->setScissor(left, bottom, width, height);
|
||||
}
|
||||
|
||||
void MaterialInstance::unsetScissor() noexcept {
|
||||
upcast(this)->unsetScissor();
|
||||
downcast(this)->unsetScissor();
|
||||
}
|
||||
|
||||
void MaterialInstance::setPolygonOffset(float scale, float constant) noexcept {
|
||||
upcast(this)->setPolygonOffset(scale, constant);
|
||||
downcast(this)->setPolygonOffset(scale, constant);
|
||||
}
|
||||
|
||||
void MaterialInstance::setMaskThreshold(float threshold) noexcept {
|
||||
upcast(this)->setMaskThreshold(threshold);
|
||||
downcast(this)->setMaskThreshold(threshold);
|
||||
}
|
||||
|
||||
void MaterialInstance::setSpecularAntiAliasingVariance(float variance) noexcept {
|
||||
upcast(this)->setSpecularAntiAliasingVariance(variance);
|
||||
downcast(this)->setSpecularAntiAliasingVariance(variance);
|
||||
}
|
||||
|
||||
void MaterialInstance::setSpecularAntiAliasingThreshold(float threshold) noexcept {
|
||||
upcast(this)->setSpecularAntiAliasingThreshold(threshold);
|
||||
downcast(this)->setSpecularAntiAliasingThreshold(threshold);
|
||||
}
|
||||
|
||||
void MaterialInstance::setDoubleSided(bool doubleSided) noexcept {
|
||||
upcast(this)->setDoubleSided(doubleSided);
|
||||
downcast(this)->setDoubleSided(doubleSided);
|
||||
}
|
||||
|
||||
void MaterialInstance::setTransparencyMode(TransparencyMode mode) noexcept {
|
||||
upcast(this)->setTransparencyMode(mode);
|
||||
downcast(this)->setTransparencyMode(mode);
|
||||
}
|
||||
|
||||
void MaterialInstance::setCullingMode(CullingMode culling) noexcept {
|
||||
upcast(this)->setCullingMode(culling);
|
||||
downcast(this)->setCullingMode(culling);
|
||||
}
|
||||
|
||||
void MaterialInstance::setColorWrite(bool enable) noexcept {
|
||||
upcast(this)->setColorWrite(enable);
|
||||
downcast(this)->setColorWrite(enable);
|
||||
}
|
||||
|
||||
void MaterialInstance::setDepthWrite(bool enable) noexcept {
|
||||
upcast(this)->setDepthWrite(enable);
|
||||
downcast(this)->setDepthWrite(enable);
|
||||
}
|
||||
|
||||
void MaterialInstance::setDepthCulling(bool enable) noexcept {
|
||||
upcast(this)->setDepthCulling(enable);
|
||||
downcast(this)->setDepthCulling(enable);
|
||||
}
|
||||
|
||||
void MaterialInstance::setStencilWrite(bool enable) noexcept {
|
||||
upcast(this)->setStencilWrite(enable);
|
||||
downcast(this)->setStencilWrite(enable);
|
||||
}
|
||||
|
||||
void MaterialInstance::setStencilCompareFunction(StencilCompareFunc func, StencilFace face) noexcept {
|
||||
upcast(this)->setStencilCompareFunction(func, face);
|
||||
downcast(this)->setStencilCompareFunction(func, face);
|
||||
}
|
||||
|
||||
void MaterialInstance::setStencilOpStencilFail(StencilOperation op, StencilFace face) noexcept {
|
||||
upcast(this)->setStencilOpStencilFail(op, face);
|
||||
downcast(this)->setStencilOpStencilFail(op, face);
|
||||
}
|
||||
|
||||
void MaterialInstance::setStencilOpDepthFail(StencilOperation op, StencilFace face) noexcept {
|
||||
upcast(this)->setStencilOpDepthFail(op, face);
|
||||
downcast(this)->setStencilOpDepthFail(op, face);
|
||||
}
|
||||
|
||||
void MaterialInstance::setStencilOpDepthStencilPass(StencilOperation op, StencilFace face) noexcept {
|
||||
upcast(this)->setStencilOpDepthStencilPass(op, face);
|
||||
downcast(this)->setStencilOpDepthStencilPass(op, face);
|
||||
}
|
||||
|
||||
void MaterialInstance::setStencilReferenceValue(uint8_t value, StencilFace face) noexcept {
|
||||
upcast(this)->setStencilReferenceValue(value, face);
|
||||
downcast(this)->setStencilReferenceValue(value, face);
|
||||
}
|
||||
|
||||
void MaterialInstance::setStencilReadMask(uint8_t readMask, StencilFace face) noexcept {
|
||||
upcast(this)->setStencilReadMask(readMask, face);
|
||||
downcast(this)->setStencilReadMask(readMask, face);
|
||||
}
|
||||
|
||||
void MaterialInstance::setStencilWriteMask(uint8_t writeMask, StencilFace face) noexcept {
|
||||
upcast(this)->setStencilWriteMask(writeMask, face);
|
||||
downcast(this)->setStencilWriteMask(writeMask, face);
|
||||
}
|
||||
|
||||
MaterialInstance* MaterialInstance::duplicate(MaterialInstance const* other, const char* name) noexcept {
|
||||
return FMaterialInstance::duplicate(upcast(other), name);
|
||||
return FMaterialInstance::duplicate(downcast(other), name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,25 +22,25 @@ namespace filament {
|
||||
|
||||
void MorphTargetBuffer::setPositionsAt(Engine& engine, size_t targetIndex,
|
||||
math::float3 const* positions, size_t count, size_t offset) {
|
||||
upcast(this)->setPositionsAt(upcast(engine), targetIndex, positions, count, offset);
|
||||
downcast(this)->setPositionsAt(downcast(engine), targetIndex, positions, count, offset);
|
||||
}
|
||||
|
||||
void MorphTargetBuffer::setPositionsAt(Engine& engine, size_t targetIndex,
|
||||
math::float4 const* positions, size_t count, size_t offset) {
|
||||
upcast(this)->setPositionsAt(upcast(engine), targetIndex, positions, count, offset);
|
||||
downcast(this)->setPositionsAt(downcast(engine), targetIndex, positions, count, offset);
|
||||
}
|
||||
|
||||
void MorphTargetBuffer::setTangentsAt(Engine& engine, size_t targetIndex,
|
||||
math::short4 const* tangents, size_t count, size_t offset) {
|
||||
upcast(this)->setTangentsAt(upcast(engine), targetIndex, tangents, count, offset);
|
||||
downcast(this)->setTangentsAt(downcast(engine), targetIndex, tangents, count, offset);
|
||||
}
|
||||
|
||||
size_t MorphTargetBuffer::getVertexCount() const noexcept {
|
||||
return upcast(this)->getVertexCount();
|
||||
return downcast(this)->getVertexCount();
|
||||
}
|
||||
|
||||
size_t MorphTargetBuffer::getCount() const noexcept {
|
||||
return upcast(this)->getCount();
|
||||
return downcast(this)->getCount();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -288,9 +288,11 @@ void PerViewUniforms::prepareDynamicLights(Froxelizer& froxelizer) noexcept {
|
||||
s.lightFarAttenuationParams = 0.5f * float2{ 10.0f, 10.0f / (f * f) };
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareShadowMapping() noexcept {
|
||||
void PerViewUniforms::prepareShadowMapping(bool highPrecision) noexcept {
|
||||
auto& s = mUniforms.edit();
|
||||
s.vsmExponent = 5.54f; // fp16: max 5.54f, fp32: max 42.0
|
||||
constexpr float low = 5.54f; // ~ std::log(std::numeric_limits<math::half>::max()) * 0.5f;
|
||||
constexpr float high = 42.0f; // ~ std::log(std::numeric_limits<float>::max()) * 0.5f;
|
||||
s.vsmExponent = highPrecision ? high : low;
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms,
|
||||
@@ -302,11 +304,14 @@ void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms,
|
||||
uniforms.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance;
|
||||
uniforms.directionalShadows = shadowMappingUniforms.directionalShadows;
|
||||
uniforms.cascades = shadowMappingUniforms.cascades;
|
||||
uniforms.cascades |= uint32_t(shadowMappingUniforms.elvsm) << 31u;
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms,
|
||||
VsmShadowOptions const& options) noexcept {
|
||||
constexpr float low = 5.54f; // ~ std::log(std::numeric_limits<math::half>::max()) * 0.5f;
|
||||
constexpr float high = 42.0f; // ~ std::log(std::numeric_limits<float>::max()) * 0.5f;
|
||||
SamplerMinFilter filterMin = SamplerMinFilter::LINEAR;
|
||||
if (options.anisotropy > 0 || options.mipmapping) {
|
||||
filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR;
|
||||
@@ -318,8 +323,8 @@ void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
|
||||
.anisotropyLog2 = options.anisotropy,
|
||||
}});
|
||||
auto& s = mUniforms.edit();
|
||||
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_VSM;
|
||||
s.vsmExponent = 5.54f; // fp16: max 5.54f, fp32: max 42.0
|
||||
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_EVSM;
|
||||
s.vsmExponent = options.highPrecision ? high : low;
|
||||
s.vsmDepthScale = options.minVarianceScale * 0.01f * s.vsmExponent;
|
||||
s.vsmLightBleedReduction = options.lightBleedReduction;
|
||||
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
|
||||
|
||||
@@ -53,10 +53,10 @@ class PerViewUniforms {
|
||||
using LightManagerInstance = utils::EntityInstance<LightManager>;
|
||||
using TextureHandle = backend::Handle<backend::HwTexture>;
|
||||
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCF = 0u;
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_VSM = 1u;
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_DPCF = 2u;
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCSS = 3u;
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCF = 0u;
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_EVSM = 1u;
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_DPCF = 2u;
|
||||
static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCSS = 3u;
|
||||
|
||||
public:
|
||||
explicit PerViewUniforms(FEngine& engine) noexcept;
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
math::mat4f const& uvFromViewMatrix,
|
||||
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
|
||||
|
||||
void prepareShadowMapping() noexcept;
|
||||
void prepareShadowMapping(bool highPrecision) noexcept;
|
||||
|
||||
void prepareDirectionalLight(float exposure,
|
||||
math::float3 const& sceneSpaceDirection, LightManagerInstance instance) noexcept;
|
||||
|
||||
@@ -127,7 +127,7 @@ void PostProcessManager::PostProcessMaterial::loadMaterial(FEngine& engine) cons
|
||||
// TODO: After all materials using this class have been converted to the post-process material
|
||||
// domain, load both OPAQUE and TRANSPARENT variants here.
|
||||
mHasMaterial = true;
|
||||
mMaterial = upcast(Material::Builder().package(mData, mSize).build(engine));
|
||||
mMaterial = downcast(Material::Builder().package(mData, mSize).build(engine));
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
@@ -399,9 +399,9 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
auto out = resources.getRenderPassInfo();
|
||||
renderPass.setRenderFlags(structureRenderFlags);
|
||||
renderPass.setVariant(structureVariant);
|
||||
renderPass.appendCommands(RenderPass::CommandTypeFlags::SSAO);
|
||||
renderPass.sortCommands();
|
||||
renderPass.execute(resources.getPassName(), out.target, out.params);
|
||||
renderPass.appendCommands(mEngine, RenderPass::CommandTypeFlags::SSAO);
|
||||
renderPass.sortCommands(mEngine);
|
||||
renderPass.execute(mEngine, resources.getPassName(), out.target, out.params);
|
||||
});
|
||||
|
||||
auto depth = structurePass->depth;
|
||||
@@ -544,9 +544,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
|
||||
// the SCREEN_SPACE ReflectionMode.
|
||||
renderPass.setVariant(Variant{Variant::SPECIAL_SSR});
|
||||
// generate all our drawing commands, except blended objects.
|
||||
renderPass.appendCommands(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
|
||||
renderPass.sortCommands();
|
||||
renderPass.execute(resources.getPassName(), out.target, out.params);
|
||||
renderPass.appendCommands(mEngine, RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
|
||||
renderPass.sortCommands(mEngine);
|
||||
renderPass.execute(mEngine, resources.getPassName(), out.target, out.params);
|
||||
});
|
||||
|
||||
return ssrPass->reflections;
|
||||
@@ -990,8 +990,15 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
|
||||
float x1 = float(i * 2);
|
||||
float k0 = std::exp(-alpha * x0 * x0);
|
||||
float k1 = std::exp(-alpha * x1 * x1);
|
||||
|
||||
// k * textureLod(..., o) with bilinear sampling is equivalent to:
|
||||
// k * (s[0] * (1 - o) + s[1] * o)
|
||||
// solve:
|
||||
// k0 = k * (1 - o)
|
||||
// k1 = k * o
|
||||
|
||||
float k = k0 + k1;
|
||||
float o = k0 / k;
|
||||
float o = k1 / k;
|
||||
kernel[i].x = k;
|
||||
kernel[i].y = o;
|
||||
totalWeight += (k0 + k1) * 2.0f;
|
||||
@@ -1039,7 +1046,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
|
||||
[=](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
|
||||
// don't use auto for those, b/c the can't resolve them
|
||||
// don't use auto for those, b/c the ide can't resolve them
|
||||
using FGTD = FrameGraphTexture::Descriptor;
|
||||
using FGTSD = FrameGraphTexture::SubResourceDescriptor;
|
||||
|
||||
@@ -1055,7 +1062,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
|
||||
using namespace std::literals;
|
||||
std::string_view materialName;
|
||||
const bool is2dArray = inDesc.type == SamplerType::SAMPLER_2D_ARRAY;
|
||||
switch (backend::getFormatSize(outDesc.format)) {
|
||||
switch (backend::getFormatComponentCount(outDesc.format)) {
|
||||
case 1: materialName = is2dArray ?
|
||||
"separableGaussianBlur1L"sv : "separableGaussianBlur1"sv; break;
|
||||
case 2: materialName = is2dArray ?
|
||||
@@ -2258,7 +2265,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
|
||||
if (bloomOptions.enabled) {
|
||||
bloomStrength = clamp(bloomOptions.strength, 0.0f, 1.0f);
|
||||
if (bloomOptions.dirt) {
|
||||
FTexture* fdirt = upcast(bloomOptions.dirt);
|
||||
FTexture* fdirt = downcast(bloomOptions.dirt);
|
||||
FrameGraphTexture frameGraphTexture{ .handle = fdirt->getHwHandle() };
|
||||
bloomDirt = fg.import("dirt", {
|
||||
.width = (uint32_t)fdirt->getWidth(0u),
|
||||
|
||||
+32
-38
@@ -39,7 +39,7 @@ using namespace backend;
|
||||
|
||||
RenderPass::RenderPass(FEngine& engine,
|
||||
RenderPass::Arena& arena) noexcept
|
||||
: mEngine(engine), mCommandArena(arena),
|
||||
: mCommandArena(arena),
|
||||
mCustomCommands(engine.getPerRenderPassAllocator()) {
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ void RenderPass::overrideScissor(backend::Viewport const* scissor) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcept {
|
||||
void RenderPass::appendCommands(FEngine& engine, CommandTypeFlags const commandTypeFlags) noexcept {
|
||||
SYSTRACE_CONTEXT();
|
||||
|
||||
assert_invariant(mRenderableSoa);
|
||||
@@ -110,7 +110,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 +178,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 +191,12 @@ void RenderPass::sortCommands() noexcept {
|
||||
|
||||
resize(uint32_t(last - mCommandBegin));
|
||||
|
||||
if (mEngine.isAutomaticInstancingEnabled()) {
|
||||
instanceify();
|
||||
if (engine.isAutomaticInstancingEnabled()) {
|
||||
instanceify(engine);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::instanceify() noexcept {
|
||||
void RenderPass::instanceify(FEngine& engine) noexcept {
|
||||
SYSTRACE_NAME("instanceify");
|
||||
|
||||
// instanceify works by scanning the **sorted** command stream, looking for repeat draw
|
||||
@@ -284,7 +283,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
|
||||
@@ -378,12 +377,14 @@ void RenderPass::generateCommands(uint32_t commandTypeFlags, Command* const comm
|
||||
// the list twice)
|
||||
|
||||
// compute how much maximum storage we need
|
||||
uint32_t offset = FScene::getPrimitiveCount(soa, range.first);
|
||||
// double the color pass for transparent objects that need to render twice
|
||||
const bool colorPass = bool(commandTypeFlags & CommandTypeFlags::COLOR);
|
||||
const bool depthPass = bool(commandTypeFlags & CommandTypeFlags::DEPTH);
|
||||
offset *= uint32_t(colorPass * 2 + depthPass);
|
||||
Command* const curr = commands + offset;
|
||||
const size_t commandsPerPrimitive = uint32_t(colorPass * 2 + depthPass);
|
||||
const size_t offsetBegin = FScene::getPrimitiveCount(soa, range.first) * commandsPerPrimitive;
|
||||
const size_t offsetEnd = FScene::getPrimitiveCount(soa, range.last) * commandsPerPrimitive;
|
||||
Command* curr = commands + offsetBegin;
|
||||
Command* const last = commands + offsetEnd;
|
||||
|
||||
/*
|
||||
* The switch {} below is to coerce the compiler into generating different versions of
|
||||
@@ -396,23 +397,31 @@ void RenderPass::generateCommands(uint32_t commandTypeFlags, Command* const comm
|
||||
|
||||
switch (commandTypeFlags & (CommandTypeFlags::COLOR | CommandTypeFlags::DEPTH)) {
|
||||
case CommandTypeFlags::COLOR:
|
||||
generateCommandsImpl<CommandTypeFlags::COLOR>(commandTypeFlags, curr,
|
||||
curr = generateCommandsImpl<CommandTypeFlags::COLOR>(commandTypeFlags, curr,
|
||||
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward);
|
||||
break;
|
||||
case CommandTypeFlags::DEPTH:
|
||||
generateCommandsImpl<CommandTypeFlags::DEPTH>(commandTypeFlags, curr,
|
||||
curr = generateCommandsImpl<CommandTypeFlags::DEPTH>(commandTypeFlags, curr,
|
||||
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward);
|
||||
break;
|
||||
default:
|
||||
// we should never end-up here
|
||||
break;
|
||||
}
|
||||
|
||||
assert_invariant(curr <= last);
|
||||
|
||||
// commands may have been skipped, cancel all of them.
|
||||
while (curr != last) {
|
||||
curr->key = uint64_t(Pass::SENTINEL);
|
||||
++curr;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
template<uint32_t commandTypeFlags>
|
||||
UTILS_NOINLINE
|
||||
void RenderPass::generateCommandsImpl(uint32_t extraFlags,
|
||||
RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
|
||||
Command* UTILS_RESTRICT curr,
|
||||
FScene::RenderableSoa const& UTILS_RESTRICT soa, Range<uint32_t> range,
|
||||
Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
|
||||
@@ -458,17 +467,8 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
|
||||
const float cameraPositionDotCameraForward = dot(cameraPosition, cameraForward);
|
||||
|
||||
for (uint32_t i = range.first; i < range.last; ++i) {
|
||||
// Check if this renderable passes the visibilityMask. If it doesn't, encode SENTINEL
|
||||
// commands (no-op).
|
||||
// Check if this renderable passes the visibilityMask.
|
||||
if (UTILS_UNLIKELY(!(soaVisibilityMask[i] & visibilityMask))) {
|
||||
// We need to encode a SENTINEL for each command that would have been generated
|
||||
// otherwise. Color passes get 2 commands per primitive; depth passes get 1.
|
||||
const Slice<FRenderPrimitive>& primitives = soaPrimitives[i];
|
||||
const size_t commandsToEncode = (isColorPass * 2 + isDepthPass) * primitives.size();
|
||||
for (size_t j = 0; j < commandsToEncode; j++) {
|
||||
curr->key = uint64_t(Pass::SENTINEL);
|
||||
++curr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -541,6 +541,11 @@ void 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();
|
||||
@@ -597,9 +602,6 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
|
||||
// draw this command AFTER THE NEXT ONE
|
||||
key |= makeField(1, BLEND_TWO_PASS_MASK, BLEND_TWO_PASS_SHIFT);
|
||||
|
||||
// handle the case where this primitive is empty / no-op
|
||||
key |= select(primitive.getPrimitiveType() == PrimitiveType::NONE);
|
||||
|
||||
// correct for TransparencyMode::DEFAULT -- i.e. cancel the command
|
||||
key |= select(mode == TransparencyMode::DEFAULT);
|
||||
|
||||
@@ -629,14 +631,9 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
|
||||
// bucketizes the depth by its log2 and in 4 linear chunks in each bucket.
|
||||
cmdColor.key &= ~Z_BUCKET_MASK;
|
||||
cmdColor.key |= makeField(distanceBits >> 22u, Z_BUCKET_MASK, Z_BUCKET_SHIFT);
|
||||
|
||||
curr->key = uint64_t(Pass::SENTINEL);
|
||||
++curr;
|
||||
}
|
||||
|
||||
*curr = cmdColor;
|
||||
// handle the case where this primitive is empty / no-op
|
||||
curr->key |= select(primitive.getPrimitiveType() == PrimitiveType::NONE);
|
||||
++curr;
|
||||
}
|
||||
|
||||
@@ -666,13 +663,11 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
|
||||
& !(depthFilterAlphaMaskedObjects & rs.alphaToCoverage))
|
||||
| writeDepthForShadowCasters;
|
||||
*curr = cmdDepth;
|
||||
|
||||
// handle the case where this primitive is empty / no-op
|
||||
curr->key |= select(primitive.getPrimitiveType() == PrimitiveType::NONE);
|
||||
++curr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
void RenderPass::updateSummedPrimitiveCounts(
|
||||
@@ -690,10 +685,9 @@ void RenderPass::updateSummedPrimitiveCounts(
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void RenderPass::Executor::execute(const char* name,
|
||||
void RenderPass::Executor::execute(FEngine& engine, const char* name,
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams const& params) const noexcept {
|
||||
FEngine& engine = mEngine;
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
// this is a good time to flush the CommandStream, because we're about to potentially
|
||||
@@ -835,7 +829,7 @@ 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),
|
||||
: mBegin(b), mEnd(e),
|
||||
mCustomCommands(pass->mCustomCommands),
|
||||
mUboHandle(pass->mUboHandle),
|
||||
mInstancedUboHandle(pass->mInstancedUboHandle),
|
||||
|
||||
@@ -310,16 +310,16 @@ 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);
|
||||
getExecutor().execute(engine, name, renderTarget, params);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -331,7 +331,6 @@ public:
|
||||
utils::STLAllocator<CustomCommandFn, LinearAllocatorArena>>;
|
||||
|
||||
friend class RenderPass;
|
||||
FEngine& mEngine;
|
||||
Command const* mBegin;
|
||||
Command const* mEnd;
|
||||
const CustomCommandVector mCustomCommands;
|
||||
@@ -351,7 +350,7 @@ public:
|
||||
public:
|
||||
Executor(Executor const& rhs);
|
||||
~Executor() noexcept;
|
||||
void execute(const char* name,
|
||||
void execute(FEngine& engine, const char* name,
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams const& params) const noexcept;
|
||||
};
|
||||
@@ -376,7 +375,7 @@ private:
|
||||
|
||||
Command* append(size_t count) noexcept;
|
||||
void resize(size_t count) noexcept;
|
||||
void instanceify() noexcept;
|
||||
void instanceify(FEngine& engine) noexcept;
|
||||
|
||||
// on 64-bits systems, we process batches of 256 (64 bytes) cache-lines, or 512 (32 bytes) commands
|
||||
// on 32-bits systems, we process batches of 512 (32 bytes) cache-lines, or 512 (32 bytes) commands
|
||||
@@ -394,7 +393,7 @@ private:
|
||||
math::float3 cameraPosition, math::float3 cameraForward) noexcept;
|
||||
|
||||
template<uint32_t commandTypeFlags>
|
||||
static inline void generateCommandsImpl(uint32_t, Command* commands,
|
||||
static inline Command* generateCommandsImpl(uint32_t extraFlags, Command* curr,
|
||||
FScene::RenderableSoa const& soa, utils::Range<uint32_t> range,
|
||||
Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
|
||||
math::float3 cameraPosition, math::float3 cameraForward) noexcept;
|
||||
@@ -406,7 +405,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;
|
||||
|
||||
@@ -30,12 +30,12 @@ void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverAp
|
||||
|
||||
assert_invariant(entry.materialInstance);
|
||||
|
||||
mMaterialInstance = upcast(entry.materialInstance);
|
||||
mMaterialInstance = downcast(entry.materialInstance);
|
||||
mBlendOrder = entry.blendOrder;
|
||||
|
||||
if (entry.indices && entry.vertices) {
|
||||
FVertexBuffer* vertexBuffer = upcast(entry.vertices);
|
||||
FIndexBuffer* indexBuffer = upcast(entry.indices);
|
||||
FVertexBuffer* vertexBuffer = downcast(entry.vertices);
|
||||
FIndexBuffer* indexBuffer = downcast(entry.indices);
|
||||
|
||||
AttributeBitset enabledAttributes = vertexBuffer->getDeclaredAttributes();
|
||||
|
||||
|
||||
@@ -23,23 +23,23 @@ namespace filament {
|
||||
using namespace backend;
|
||||
|
||||
Texture* RenderTarget::getTexture(AttachmentPoint attachment) const noexcept {
|
||||
return upcast(this)->getAttachment(attachment).texture;
|
||||
return downcast(this)->getAttachment(attachment).texture;
|
||||
}
|
||||
|
||||
uint8_t RenderTarget::getMipLevel(AttachmentPoint attachment) const noexcept {
|
||||
return upcast(this)->getAttachment(attachment).mipLevel;
|
||||
return downcast(this)->getAttachment(attachment).mipLevel;
|
||||
}
|
||||
|
||||
RenderTarget::CubemapFace RenderTarget::getFace(AttachmentPoint attachment) const noexcept {
|
||||
return upcast(this)->getAttachment(attachment).face;
|
||||
return downcast(this)->getAttachment(attachment).face;
|
||||
}
|
||||
|
||||
uint32_t RenderTarget::getLayer(AttachmentPoint attachment) const noexcept {
|
||||
return upcast(this)->getAttachment(attachment).layer;
|
||||
return downcast(this)->getAttachment(attachment).layer;
|
||||
}
|
||||
|
||||
uint8_t RenderTarget::getSupportedColorAttachmentsCount() const noexcept {
|
||||
return upcast(this)->getSupportedColorAttachmentsCount();
|
||||
return downcast(this)->getSupportedColorAttachmentsCount();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -28,137 +28,137 @@ using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
bool RenderableManager::hasComponent(utils::Entity e) const noexcept {
|
||||
return upcast(this)->hasComponent(e);
|
||||
return downcast(this)->hasComponent(e);
|
||||
}
|
||||
|
||||
RenderableManager::Instance
|
||||
RenderableManager::getInstance(utils::Entity e) const noexcept {
|
||||
return upcast(this)->getInstance(e);
|
||||
return downcast(this)->getInstance(e);
|
||||
}
|
||||
|
||||
void RenderableManager::destroy(utils::Entity e) noexcept {
|
||||
return upcast(this)->destroy(e);
|
||||
return downcast(this)->destroy(e);
|
||||
}
|
||||
|
||||
void RenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept {
|
||||
upcast(this)->setAxisAlignedBoundingBox(instance, aabb);
|
||||
downcast(this)->setAxisAlignedBoundingBox(instance, aabb);
|
||||
}
|
||||
|
||||
void RenderableManager::setLayerMask(Instance instance, uint8_t select, uint8_t values) noexcept {
|
||||
upcast(this)->setLayerMask(instance, select, values);
|
||||
downcast(this)->setLayerMask(instance, select, values);
|
||||
}
|
||||
|
||||
void RenderableManager::setPriority(Instance instance, uint8_t priority) noexcept {
|
||||
upcast(this)->setPriority(instance, priority);
|
||||
downcast(this)->setPriority(instance, priority);
|
||||
}
|
||||
|
||||
void RenderableManager::setCulling(Instance instance, bool enable) noexcept {
|
||||
upcast(this)->setCulling(instance, enable);
|
||||
downcast(this)->setCulling(instance, enable);
|
||||
}
|
||||
|
||||
void RenderableManager::setCastShadows(Instance instance, bool enable) noexcept {
|
||||
upcast(this)->setCastShadows(instance, enable);
|
||||
downcast(this)->setCastShadows(instance, enable);
|
||||
}
|
||||
|
||||
void RenderableManager::setReceiveShadows(Instance instance, bool enable) noexcept {
|
||||
upcast(this)->setReceiveShadows(instance, enable);
|
||||
downcast(this)->setReceiveShadows(instance, enable);
|
||||
}
|
||||
|
||||
void RenderableManager::setScreenSpaceContactShadows(Instance instance, bool enable) noexcept {
|
||||
upcast(this)->setScreenSpaceContactShadows(instance, enable);
|
||||
downcast(this)->setScreenSpaceContactShadows(instance, enable);
|
||||
}
|
||||
|
||||
bool RenderableManager::isShadowCaster(Instance instance) const noexcept {
|
||||
return upcast(this)->isShadowCaster(instance);
|
||||
return downcast(this)->isShadowCaster(instance);
|
||||
}
|
||||
|
||||
bool RenderableManager::isShadowReceiver(Instance instance) const noexcept {
|
||||
return upcast(this)->isShadowReceiver(instance);
|
||||
return downcast(this)->isShadowReceiver(instance);
|
||||
}
|
||||
|
||||
const Box& RenderableManager::getAxisAlignedBoundingBox(Instance instance) const noexcept {
|
||||
return upcast(this)->getAxisAlignedBoundingBox(instance);
|
||||
return downcast(this)->getAxisAlignedBoundingBox(instance);
|
||||
}
|
||||
|
||||
uint8_t RenderableManager::getLayerMask(Instance instance) const noexcept {
|
||||
return upcast(this)->getLayerMask(instance);
|
||||
return downcast(this)->getLayerMask(instance);
|
||||
}
|
||||
|
||||
size_t RenderableManager::getPrimitiveCount(Instance instance) const noexcept {
|
||||
return upcast(this)->getPrimitiveCount(instance, 0);
|
||||
return downcast(this)->getPrimitiveCount(instance, 0);
|
||||
}
|
||||
|
||||
void RenderableManager::setMaterialInstanceAt(Instance instance,
|
||||
size_t primitiveIndex, MaterialInstance const* materialInstance) {
|
||||
upcast(this)->setMaterialInstanceAt(instance, 0, primitiveIndex, upcast(materialInstance));
|
||||
downcast(this)->setMaterialInstanceAt(instance, 0, primitiveIndex, downcast(materialInstance));
|
||||
}
|
||||
|
||||
MaterialInstance* RenderableManager::getMaterialInstanceAt(
|
||||
Instance instance, size_t primitiveIndex) const noexcept {
|
||||
return upcast(this)->getMaterialInstanceAt(instance, 0, primitiveIndex);
|
||||
return downcast(this)->getMaterialInstanceAt(instance, 0, primitiveIndex);
|
||||
}
|
||||
|
||||
void RenderableManager::setBlendOrderAt(Instance instance, size_t primitiveIndex, uint16_t order) noexcept {
|
||||
upcast(this)->setBlendOrderAt(instance, 0, primitiveIndex, order);
|
||||
downcast(this)->setBlendOrderAt(instance, 0, primitiveIndex, order);
|
||||
}
|
||||
|
||||
void RenderableManager::setGlobalBlendOrderEnabledAt(RenderableManager::Instance instance,
|
||||
size_t primitiveIndex, bool enabled) noexcept {
|
||||
upcast(this)->setGlobalBlendOrderEnabledAt(instance, 0, primitiveIndex, enabled);
|
||||
downcast(this)->setGlobalBlendOrderEnabledAt(instance, 0, primitiveIndex, enabled);
|
||||
}
|
||||
|
||||
AttributeBitset RenderableManager::getEnabledAttributesAt(Instance instance, size_t primitiveIndex) const noexcept {
|
||||
return upcast(this)->getEnabledAttributesAt(instance, 0, primitiveIndex);
|
||||
return downcast(this)->getEnabledAttributesAt(instance, 0, primitiveIndex);
|
||||
}
|
||||
|
||||
void RenderableManager::setGeometryAt(Instance instance, size_t primitiveIndex,
|
||||
PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices,
|
||||
size_t offset, size_t count) noexcept {
|
||||
upcast(this)->setGeometryAt(instance, 0, primitiveIndex,
|
||||
type, upcast(vertices), upcast(indices), offset, count);
|
||||
downcast(this)->setGeometryAt(instance, 0, primitiveIndex,
|
||||
type, downcast(vertices), downcast(indices), offset, count);
|
||||
}
|
||||
|
||||
void RenderableManager::setBones(Instance instance,
|
||||
RenderableManager::Bone const* transforms, size_t boneCount, size_t offset) {
|
||||
upcast(this)->setBones(instance, transforms, boneCount, offset);
|
||||
downcast(this)->setBones(instance, transforms, boneCount, offset);
|
||||
}
|
||||
|
||||
void RenderableManager::setBones(Instance instance,
|
||||
mat4f const* transforms, size_t boneCount, size_t offset) {
|
||||
upcast(this)->setBones(instance, transforms, boneCount, offset);
|
||||
downcast(this)->setBones(instance, transforms, boneCount, offset);
|
||||
}
|
||||
|
||||
void RenderableManager::setSkinningBuffer(Instance instance,
|
||||
SkinningBuffer* skinningBuffer, size_t count, size_t offset) {
|
||||
upcast(this)->setSkinningBuffer(instance, upcast(skinningBuffer), count, offset);
|
||||
downcast(this)->setSkinningBuffer(instance, downcast(skinningBuffer), count, offset);
|
||||
}
|
||||
|
||||
void RenderableManager::setMorphWeights(Instance instance, float const* weights,
|
||||
size_t count, size_t offset) {
|
||||
upcast(this)->setMorphWeights(instance, weights, count, offset);
|
||||
downcast(this)->setMorphWeights(instance, weights, count, offset);
|
||||
}
|
||||
|
||||
void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
|
||||
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) {
|
||||
upcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex,
|
||||
upcast(morphTargetBuffer), offset, count);
|
||||
downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex,
|
||||
downcast(morphTargetBuffer), offset, count);
|
||||
}
|
||||
|
||||
MorphTargetBuffer* RenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level,
|
||||
size_t primitiveIndex) const noexcept {
|
||||
return upcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex);
|
||||
return downcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex);
|
||||
}
|
||||
|
||||
size_t RenderableManager::getMorphTargetCount(Instance instance) const noexcept {
|
||||
return upcast(this)->getMorphTargetCount(instance);
|
||||
return downcast(this)->getMorphTargetCount(instance);
|
||||
}
|
||||
|
||||
void RenderableManager::setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept {
|
||||
upcast(this)->setLightChannel(instance, channel, enable);
|
||||
downcast(this)->setLightChannel(instance, channel, enable);
|
||||
}
|
||||
|
||||
bool RenderableManager::getLightChannel(Instance instance, unsigned int channel) const noexcept {
|
||||
return upcast(this)->getLightChannel(instance, channel);
|
||||
return downcast(this)->getLightChannel(instance, channel);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+14
-14
@@ -25,64 +25,64 @@ using namespace math;
|
||||
using namespace backend;
|
||||
|
||||
Engine* Renderer::getEngine() noexcept {
|
||||
return &upcast(this)->getEngine();
|
||||
return &downcast(this)->getEngine();
|
||||
}
|
||||
|
||||
void Renderer::render(View const* view) {
|
||||
upcast(this)->render(upcast(view));
|
||||
downcast(this)->render(downcast(view));
|
||||
}
|
||||
|
||||
void Renderer::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
upcast(this)->setPresentationTime(monotonic_clock_ns);
|
||||
downcast(this)->setPresentationTime(monotonic_clock_ns);
|
||||
}
|
||||
|
||||
bool Renderer::beginFrame(SwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) {
|
||||
return upcast(this)->beginFrame(upcast(swapChain), vsyncSteadyClockTimeNano);
|
||||
return downcast(this)->beginFrame(downcast(swapChain), vsyncSteadyClockTimeNano);
|
||||
}
|
||||
|
||||
void Renderer::copyFrame(SwapChain* dstSwapChain, filament::Viewport const& dstViewport,
|
||||
filament::Viewport const& srcViewport, CopyFrameFlag flags) {
|
||||
upcast(this)->copyFrame(upcast(dstSwapChain), dstViewport, srcViewport, flags);
|
||||
downcast(this)->copyFrame(downcast(dstSwapChain), dstViewport, srcViewport, flags);
|
||||
}
|
||||
|
||||
void Renderer::readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
PixelBufferDescriptor&& buffer) {
|
||||
upcast(this)->readPixels(xoffset, yoffset, width, height, std::move(buffer));
|
||||
downcast(this)->readPixels(xoffset, yoffset, width, height, std::move(buffer));
|
||||
}
|
||||
|
||||
void Renderer::readPixels(RenderTarget* renderTarget,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
PixelBufferDescriptor&& buffer) {
|
||||
upcast(this)->readPixels(upcast(renderTarget),
|
||||
downcast(this)->readPixels(downcast(renderTarget),
|
||||
xoffset, yoffset, width, height, std::move(buffer));
|
||||
}
|
||||
|
||||
void Renderer::endFrame() {
|
||||
upcast(this)->endFrame();
|
||||
downcast(this)->endFrame();
|
||||
}
|
||||
|
||||
double Renderer::getUserTime() const {
|
||||
return upcast(this)->getUserTime();
|
||||
return downcast(this)->getUserTime();
|
||||
}
|
||||
|
||||
void Renderer::resetUserTime() {
|
||||
upcast(this)->resetUserTime();
|
||||
downcast(this)->resetUserTime();
|
||||
}
|
||||
|
||||
void Renderer::setDisplayInfo(const DisplayInfo& info) noexcept {
|
||||
upcast(this)->setDisplayInfo(info);
|
||||
downcast(this)->setDisplayInfo(info);
|
||||
}
|
||||
|
||||
void Renderer::setFrameRateOptions(FrameRateOptions const& options) noexcept {
|
||||
upcast(this)->setFrameRateOptions(options);
|
||||
downcast(this)->setFrameRateOptions(options);
|
||||
}
|
||||
|
||||
void Renderer::setClearOptions(const ClearOptions& options) {
|
||||
upcast(this)->setClearOptions(options);
|
||||
downcast(this)->setClearOptions(options);
|
||||
}
|
||||
|
||||
void Renderer::renderStandaloneView(View const* view) {
|
||||
upcast(this)->renderStandaloneView(upcast(view));
|
||||
downcast(this)->renderStandaloneView(downcast(view));
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -173,7 +173,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
view.prepareSSAO(data.ssao ?
|
||||
resources.getTexture(data.ssao) : engine.getOneTextureArray());
|
||||
|
||||
view.prepareShadowMap();
|
||||
view.prepareShadowMap(view.getVsmShadowOptions().highPrecision);
|
||||
|
||||
// set shadow sampler
|
||||
view.prepareShadow(data.shadows ?
|
||||
@@ -217,7 +217,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
if (colorGradingConfig.asSubpass || colorGradingConfig.customResolve) {
|
||||
out.params.subpassMask = 1;
|
||||
}
|
||||
passExecutor.execute(resources.getPassName(), out.target, out.params);
|
||||
passExecutor.execute(engine, resources.getPassName(), out.target, out.params);
|
||||
|
||||
// color pass is typically heavy, and we don't have much CPU work left after
|
||||
// this point, so flushing now allows us to start the GPU earlier and reduce
|
||||
|
||||
+12
-12
@@ -24,51 +24,51 @@ using namespace utils;
|
||||
namespace filament {
|
||||
|
||||
void Scene::setSkybox(Skybox* skybox) noexcept {
|
||||
upcast(this)->setSkybox(upcast(skybox));
|
||||
downcast(this)->setSkybox(downcast(skybox));
|
||||
}
|
||||
|
||||
Skybox* Scene::getSkybox() noexcept {
|
||||
return upcast(this)->getSkybox();
|
||||
return downcast(this)->getSkybox();
|
||||
}
|
||||
|
||||
Skybox const* Scene::getSkybox() const noexcept {
|
||||
return upcast(this)->getSkybox();
|
||||
return downcast(this)->getSkybox();
|
||||
}
|
||||
|
||||
void Scene::setIndirectLight(IndirectLight const* ibl) noexcept {
|
||||
upcast(this)->setIndirectLight(upcast(ibl));
|
||||
downcast(this)->setIndirectLight(downcast(ibl));
|
||||
}
|
||||
|
||||
void Scene::addEntity(Entity entity) {
|
||||
upcast(this)->addEntity(entity);
|
||||
downcast(this)->addEntity(entity);
|
||||
}
|
||||
|
||||
void Scene::addEntities(const Entity* entities, size_t count) {
|
||||
upcast(this)->addEntities(entities, count);
|
||||
downcast(this)->addEntities(entities, count);
|
||||
}
|
||||
|
||||
void Scene::remove(Entity entity) {
|
||||
upcast(this)->remove(entity);
|
||||
downcast(this)->remove(entity);
|
||||
}
|
||||
|
||||
void Scene::removeEntities(const Entity* entities, size_t count) {
|
||||
upcast(this)->removeEntities(entities, count);
|
||||
downcast(this)->removeEntities(entities, count);
|
||||
}
|
||||
|
||||
size_t Scene::getRenderableCount() const noexcept {
|
||||
return upcast(this)->getRenderableCount();
|
||||
return downcast(this)->getRenderableCount();
|
||||
}
|
||||
|
||||
size_t Scene::getLightCount() const noexcept {
|
||||
return upcast(this)->getLightCount();
|
||||
return downcast(this)->getLightCount();
|
||||
}
|
||||
|
||||
bool Scene::hasEntity(Entity entity) const noexcept {
|
||||
return upcast(this)->hasEntity(entity);
|
||||
return downcast(this)->hasEntity(entity);
|
||||
}
|
||||
|
||||
void Scene::forEach(Invocable<void(utils::Entity)>&& functor) const noexcept {
|
||||
upcast(this)->forEach(std::move(functor));
|
||||
downcast(this)->forEach(std::move(functor));
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+334
-228
@@ -40,14 +40,13 @@ using namespace backend;
|
||||
// do this only if depth-clamp is available
|
||||
static constexpr bool USE_DEPTH_CLAMP = false;
|
||||
|
||||
ShadowMap::ShadowMap(FEngine& engine) noexcept :
|
||||
mEngine(engine),
|
||||
mClipSpaceFlipped(engine.getBackend() == Backend::VULKAN),
|
||||
mTextureSpaceFlipped(engine.getBackend() == Backend::METAL) {
|
||||
ShadowMap::ShadowMap(FEngine& engine) noexcept
|
||||
: mShadowType(ShadowType::DIRECTIONAL),
|
||||
mHasVisibleShadows(false) {
|
||||
Entity entities[2];
|
||||
engine.getEntityManager().create(2, entities);
|
||||
mCamera = mEngine.createCamera(entities[0]);
|
||||
mDebugCamera = mEngine.createCamera((entities[1]));
|
||||
mCamera = engine.createCamera(entities[0]);
|
||||
mDebugCamera = engine.createCamera((entities[1]));
|
||||
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
|
||||
debugRegistry.registerProperty("d.shadowmap.focus_shadowcasters", &engine.debug.shadowmap.focus_shadowcasters);
|
||||
debugRegistry.registerProperty("d.shadowmap.far_uses_shadowcasters", &engine.debug.shadowmap.far_uses_shadowcasters);
|
||||
@@ -56,10 +55,6 @@ ShadowMap::ShadowMap(FEngine& engine) noexcept :
|
||||
}
|
||||
|
||||
void ShadowMap::terminate(FEngine& engine) {
|
||||
}
|
||||
|
||||
ShadowMap::~ShadowMap() {
|
||||
FEngine& engine = mEngine;
|
||||
Entity entities[] = { mCamera->getEntity(), mDebugCamera->getEntity() };
|
||||
for (Entity e : entities) {
|
||||
engine.destroyCameraComponent(e);
|
||||
@@ -67,41 +62,52 @@ ShadowMap::~ShadowMap() {
|
||||
engine.getEntityManager().destroy(sizeof(entities) / sizeof(Entity), entities);
|
||||
}
|
||||
|
||||
void ShadowMap::render(FScene const& scene, utils::Range<uint32_t> range,
|
||||
FScene::VisibleMaskType visibilityMask, RenderPass* const pass) noexcept {
|
||||
const backend::Viewport disabledScissor{ 0, 0,
|
||||
(uint32_t)std::numeric_limits<int32_t>::max(),
|
||||
(uint32_t)std::numeric_limits<int32_t>::max() };
|
||||
pass->setVisibilityMask(visibilityMask);
|
||||
pass->setGeometry(scene.getRenderableData(), range, scene.getRenderableUBO());
|
||||
pass->overridePolygonOffset(&mShadowMapInfo.polygonOffset);
|
||||
pass->overrideScissor(&disabledScissor);
|
||||
pass->appendCommands(RenderPass::SHADOW);
|
||||
pass->sortCommands();
|
||||
ShadowMap::~ShadowMap() = default;
|
||||
|
||||
void ShadowMap::initialize(size_t lightIndex, ShadowType shadowType, uint16_t shadowIndex,
|
||||
LightManager::ShadowOptions const* options) {
|
||||
mLightIndex = lightIndex;
|
||||
mShadowIndex = shadowIndex;
|
||||
mOptions = options;
|
||||
mShadowType = shadowType;
|
||||
}
|
||||
|
||||
mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position) noexcept {
|
||||
const mat4f M = mat4f::lookAt(position, position + direction, float3{ 0, 1, 0 });
|
||||
return FCamera::rigidTransformInverse(M);
|
||||
const mat4f Mm = mat4f::lookAt(position, position + direction, float3{ 0, 1, 0 });
|
||||
return FCamera::rigidTransformInverse(Mm);
|
||||
}
|
||||
|
||||
void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t index,
|
||||
math::mat4f ShadowMap::getPointLightViewMatrix(backend::TextureCubemapFace face,
|
||||
math::float3 position) noexcept {
|
||||
float3 direction;
|
||||
switch (TextureCubemapFace(face)) {
|
||||
case TextureCubemapFace::POSITIVE_X: direction = { 1, 0, 0 }; break;
|
||||
case TextureCubemapFace::NEGATIVE_X: direction = { -1, 0, 0 }; break;
|
||||
case TextureCubemapFace::POSITIVE_Y: direction = { 0, 1, 0 }; break;
|
||||
case TextureCubemapFace::NEGATIVE_Y: direction = { 0, -1, 0 }; break;
|
||||
case TextureCubemapFace::POSITIVE_Z: direction = { 0, 0, 1 }; break;
|
||||
case TextureCubemapFace::NEGATIVE_Z: direction = { 0, 0, -1 }; break;
|
||||
}
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
|
||||
return Mv;
|
||||
}
|
||||
|
||||
ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
ShadowMapInfo const& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept {
|
||||
|
||||
mShadowMapInfo = shadowMapInfo;
|
||||
|
||||
// Note: we keep the polygon offset even with VSM as it seems to help.
|
||||
auto& lcm = mEngine.getLightManager();
|
||||
auto& lcm = engine.getLightManager();
|
||||
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
FLightManager::ShadowParams params = lcm.getShadowParams(li);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// LISPSM debugging for directional light (works because we only have one)
|
||||
const float dz = camera.zf - camera.zn;
|
||||
float& dzn = mEngine.debug.shadowmap.dzn;
|
||||
float& dzf = mEngine.debug.shadowmap.dzf;
|
||||
float& dzn = engine.debug.shadowmap.dzn;
|
||||
float& dzf = engine.debug.shadowmap.dzf;
|
||||
if (dzn < 0) dzn = std::max(0.0f, params.options.shadowNearHint - camera.zn) / dz;
|
||||
else params.options.shadowNearHint = dzn * dz - camera.zn;
|
||||
if (dzf > 0) dzf =-std::max(0.0f, camera.zf - params.options.shadowFarHint) / dz;
|
||||
@@ -109,17 +115,19 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
|
||||
#endif
|
||||
|
||||
// Adjust the camera's projection for the light's shadowFar
|
||||
mat4f cullingProjection(camera.cullingProjection);
|
||||
if (params.options.shadowFar > 0.0f) {
|
||||
float n = camera.zn;
|
||||
float f = params.options.shadowFar;
|
||||
// orthographic projection
|
||||
assert_invariant(std::abs(cullingProjection[2].w) <= std::numeric_limits<float>::epsilon());
|
||||
cullingProjection[2].z = 2.0f / (n - f);
|
||||
cullingProjection[3].z = (f + n) / (n - f);
|
||||
}
|
||||
const mat4f cullingProjection{ [&](auto p) {
|
||||
if (params.options.shadowFar > 0.0f) {
|
||||
float n = camera.zn;
|
||||
float f = params.options.shadowFar;
|
||||
// orthographic projection
|
||||
assert_invariant(std::abs(p[2].w) <= std::numeric_limits<float>::epsilon());
|
||||
p[2].z = 2.0f / (n - f);
|
||||
p[3].z = (f + n) / (n - f);
|
||||
}
|
||||
return p;
|
||||
}(camera.cullingProjection) };
|
||||
|
||||
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
|
||||
const auto direction = lightData.elementAt<FScene::DIRECTION>(index);
|
||||
|
||||
/*
|
||||
* Compute the light's model matrix
|
||||
@@ -131,13 +139,13 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
|
||||
const mat4f MvAtOrigin = getDirectionalLightViewMatrix(direction);
|
||||
|
||||
// Compute scene-dependent values shared across all cascades
|
||||
ShadowMap::updateSceneInfo(MvAtOrigin, scene, sceneInfo);
|
||||
ShadowMap::updateSceneInfoDirectional(MvAtOrigin, scene, sceneInfo);
|
||||
|
||||
const Aabb wsShadowCastersVolume = sceneInfo.wsShadowCastersVolume;
|
||||
const Aabb wsShadowReceiversVolume = sceneInfo.wsShadowReceiversVolume;
|
||||
if (wsShadowCastersVolume.isEmpty() || wsShadowReceiversVolume.isEmpty()) {
|
||||
mHasVisibleShadows = false;
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
// view frustum vertices in world-space
|
||||
@@ -155,6 +163,11 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
|
||||
size_t vertexCount = intersectFrustumWithBox(wsClippedShadowReceiverVolume,
|
||||
wsFrustum, wsViewFrustumVertices, wsShadowReceiversVolume);
|
||||
|
||||
if (UTILS_UNLIKELY(vertexCount < 4)) {
|
||||
mHasVisibleShadows = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
/*
|
||||
* compute scene zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
|
||||
* (near/far correspond to max/min because the light looks down the -z axis).
|
||||
@@ -183,7 +196,7 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
|
||||
lsLightFrustumBounds.max.z = std::max(lsLightFrustumBounds.max.z, v.z);
|
||||
}
|
||||
}
|
||||
if (mEngine.debug.shadowmap.far_uses_shadowcasters) {
|
||||
if (engine.debug.shadowmap.far_uses_shadowcasters) {
|
||||
// far: closest of the farthest shadow casters and receivers
|
||||
lsLightFrustumBounds.min.z = std::max(lsLightFrustumBounds.min.z, sceneInfo.lsNearFar[1]);
|
||||
}
|
||||
@@ -200,11 +213,11 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
|
||||
// if znear >= zfar, it means we don't have any shadow caster in front of a shadow receiver
|
||||
if (UTILS_UNLIKELY(znear >= zfar)) {
|
||||
mHasVisibleShadows = false;
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
float4 viewVolumeBoundingSphere = {};
|
||||
if (params.options.stable) {
|
||||
if (UTILS_UNLIKELY(params.options.stable)) {
|
||||
// In stable mode, the light frustum size must be fixed, so we can choose either the
|
||||
// whole view frustum, or the whole scene bounding volume. We simply pick whichever
|
||||
// is smaller.
|
||||
@@ -217,177 +230,193 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
|
||||
viewVolumeBoundingSphere = computeBoundingSphere(wsViewFrustumVertices, 8);
|
||||
|
||||
if (shadowReceiverVolumeBoundingSphere.w < viewVolumeBoundingSphere.w) {
|
||||
|
||||
// When using the shadowReceiver volume, we don't have to use its enclosing sphere
|
||||
// because (we assume) the scene volume doesn't change. Seen from the light it only
|
||||
// changes when the light moves or rotates, and it is acceptable in that case to have
|
||||
// non "stable" shadows (the shadow will never be stable when the light moves).
|
||||
//
|
||||
// On the other hand, when using the view volume, we must use a sphere because otherwise
|
||||
// its projection's bounds in light space change with the camera, leading to unstable
|
||||
// shadows with camera movement.
|
||||
|
||||
viewVolumeBoundingSphere.w = 0;
|
||||
std::copy_n(wsShadowReceiversVolume.getCorners().data(), 8,
|
||||
wsClippedShadowReceiverVolume.data());
|
||||
}
|
||||
}
|
||||
|
||||
mHasVisibleShadows = vertexCount >= 4;
|
||||
if (mHasVisibleShadows) {
|
||||
// We can't use LISPSM in stable mode
|
||||
const bool useLispsm = params.options.lispsm && !params.options.stable;
|
||||
mHasVisibleShadows = true;
|
||||
|
||||
/*
|
||||
* Compute the light's projection matrix
|
||||
* (directional/point lights, i.e. projection to use, including znear/zfar clip planes)
|
||||
*/
|
||||
// We can't use LISPSM in stable mode
|
||||
const bool useLispsm = params.options.lispsm && !params.options.stable;
|
||||
|
||||
// The light's projection, ortho for directional lights, perspective otherwise
|
||||
const mat4f Mp = directionalLightFrustum(znear, zfar);
|
||||
/*
|
||||
* Compute the light's projection matrix
|
||||
* (directional/point lights, i.e. projection to use, including znear/zfar clip planes)
|
||||
*/
|
||||
|
||||
const mat4f MpMv(Mp * Mv);
|
||||
// The light's projection, ortho for directional lights, perspective otherwise
|
||||
const mat4f Mp = directionalLightFrustum(znear, zfar);
|
||||
|
||||
/*
|
||||
* Compute warping (optional, improve quality)
|
||||
*/
|
||||
const mat4f MpMv(Mp * Mv);
|
||||
|
||||
mat4f LMpMv = MpMv;
|
||||
/*
|
||||
* Compute warping (optional, improve quality)
|
||||
*/
|
||||
|
||||
// Compute the LiSPSM warping
|
||||
mat4f W, Wp;
|
||||
mat4f L; // Rotation matrix in light space
|
||||
if (useLispsm) {
|
||||
// Orient the shadow map in the direction of the view vector by constructing a
|
||||
// rotation matrix in light space around the z-axis, that aligns the y-axis with the camera's
|
||||
// forward vector (V) -- this gives the wrap direction, vp, for LiSPSM.
|
||||
const float3 wsCameraFwd = camera.getForwardVector();
|
||||
const float3 lsCameraFwd = Mv.upperLeft() * wsCameraFwd;
|
||||
// If the light and view vector are parallel, this rotation becomes
|
||||
// meaningless. Just use identity.
|
||||
// (LdotV == (Mv*V).z, because L = {0,0,1} in light-space)
|
||||
if (UTILS_LIKELY(std::abs(lsCameraFwd.z) < 0.9997f)) { // this is |dot(L, V)|
|
||||
const float3 vp{ normalize(lsCameraFwd.xy), 0 }; // wrap direction in light-space
|
||||
L[0].xyz = cross(vp, float3{ 0, 0, 1 });
|
||||
L[1].xyz = vp;
|
||||
L[2].xyz = { 0, 0, 1 };
|
||||
L = transpose(L);
|
||||
}
|
||||
mat4f LMpMv = MpMv;
|
||||
|
||||
LMpMv = L * MpMv;
|
||||
|
||||
W = applyLISPSM(Wp, camera, params, LMpMv,
|
||||
wsClippedShadowReceiverVolume, vertexCount, direction);
|
||||
// Compute the LiSPSM warping
|
||||
mat4f W, Wp;
|
||||
mat4f L; // Rotation matrix in light space
|
||||
if (useLispsm) {
|
||||
// Orient the shadow map in the direction of the view vector by constructing a
|
||||
// rotation matrix in light space around the z-axis, that aligns the y-axis with the camera's
|
||||
// forward vector (V) -- this gives the wrap direction, vp, for LiSPSM.
|
||||
const float3 wsCameraFwd = camera.getForwardVector();
|
||||
const float3 lsCameraFwd = Mv.upperLeft() * wsCameraFwd;
|
||||
// If the light and view vector are parallel, this rotation becomes
|
||||
// meaningless. Just use identity.
|
||||
// (LdotV == (Mv*V).z, because L = {0,0,1} in light-space)
|
||||
if (UTILS_LIKELY(std::abs(lsCameraFwd.z) < 0.9997f)) { // this is |dot(L, V)|
|
||||
const float3 vp{ normalize(lsCameraFwd.xy), 0 }; // wrap direction in light-space
|
||||
L[0].xyz = cross(vp, float3{ 0, 0, 1 });
|
||||
L[1].xyz = vp;
|
||||
L[2].xyz = { 0, 0, 1 };
|
||||
L = transpose(L);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute focusing matrix (optional, greatly improves quality)
|
||||
*/
|
||||
LMpMv = L * MpMv;
|
||||
|
||||
// construct the warped light-space
|
||||
const mat4f WLMpMv = W * LMpMv;
|
||||
|
||||
// 2) Now we find the x-y bounds of our convex-hull (view volume & shadow receivers)
|
||||
// in light space, so we can "focus" the shadow map to the interesting area.
|
||||
// This is the most important step to increase the quality of the shadow map.
|
||||
//
|
||||
// In LiPSM mode, we're using the warped space here.
|
||||
|
||||
Aabb bounds;
|
||||
if (params.options.stable && viewVolumeBoundingSphere.w > 0) {
|
||||
bounds = compute2DBounds(Mv, viewVolumeBoundingSphere);
|
||||
} else {
|
||||
bounds = compute2DBounds(WLMpMv, wsClippedShadowReceiverVolume.data(), vertexCount);
|
||||
}
|
||||
lsLightFrustumBounds.min.xy = bounds.min.xy;
|
||||
lsLightFrustumBounds.max.xy = bounds.max.xy;
|
||||
|
||||
if (params.options.stable) {
|
||||
// in stable mode we can't do anything that can change the scaling of the texture
|
||||
} else {
|
||||
// For directional lights, we further constraint the light frustum to the
|
||||
// intersection of the shadow casters & shadow receivers in light-space.
|
||||
// ** This relies on the 1-texel shadow map border **
|
||||
if (mEngine.debug.shadowmap.focus_shadowcasters) {
|
||||
intersectWithShadowCasters(lsLightFrustumBounds, WLMpMv, wsShadowCastersVolume);
|
||||
}
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY((lsLightFrustumBounds.min.x >= lsLightFrustumBounds.max.x) ||
|
||||
(lsLightFrustumBounds.min.y >= lsLightFrustumBounds.max.y))) {
|
||||
// this could happen if the only thing visible is a perfectly horizontal or
|
||||
// vertical thin line
|
||||
mHasVisibleShadows = false;
|
||||
return;
|
||||
}
|
||||
|
||||
assert_invariant(lsLightFrustumBounds.min.x < lsLightFrustumBounds.max.x);
|
||||
assert_invariant(lsLightFrustumBounds.min.y < lsLightFrustumBounds.max.y);
|
||||
|
||||
// compute focus scale and offset
|
||||
float2 s = 2.0f / float2(lsLightFrustumBounds.max.xy - lsLightFrustumBounds.min.xy);
|
||||
float2 o = -s * float2(lsLightFrustumBounds.max.xy + lsLightFrustumBounds.min.xy) * 0.5f;
|
||||
|
||||
if (params.options.stable) {
|
||||
// Use the world origin as reference point, fixed w.r.t. the camera
|
||||
snapLightFrustum(s, o, Mv, -camera.getWorldOffset(),
|
||||
1.0f / mShadowMapInfo.shadowDimension);
|
||||
}
|
||||
|
||||
const mat4f F(mat4f::row_major_init {
|
||||
s.x, 0.0f, 0.0f, o.x,
|
||||
0.0f, s.y, 0.0f, o.y,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
});
|
||||
|
||||
/*
|
||||
* Final shadow map transform
|
||||
*/
|
||||
|
||||
// Final shadow transform
|
||||
const mat4f S = F * WLMpMv;
|
||||
|
||||
// Computes St the transform to use in the shader to access the shadow map texture
|
||||
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
|
||||
const mat4 MbMt = getTextureCoordsMapping();
|
||||
const mat4f St = mat4f(MbMt * S);
|
||||
|
||||
// note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because
|
||||
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
|
||||
// if Wp[3][1] is 0, then LISPSM was cancelled.
|
||||
if (useLispsm && Wp[3][1] != 0.0f) {
|
||||
mTexelSizeAtOneMeterWs = texelSizeWorldSpace(Wp, mat4f(MbMt * F));
|
||||
} else {
|
||||
// We know we're using an ortho projection
|
||||
mTexelSizeAtOneMeterWs = texelSizeWorldSpace(St.upperLeft());
|
||||
}
|
||||
if (!mShadowMapInfo.vsm) {
|
||||
mLightSpace = St;
|
||||
} else {
|
||||
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar);
|
||||
}
|
||||
|
||||
// We apply the constant bias in world space (as opposed to light-space) to account
|
||||
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
|
||||
// by baking it in the shadow-map itself.
|
||||
const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
const mat4f b = mat4f::translation(direction * constantBias);
|
||||
|
||||
// It's important to set the light camera's model matrix separately from its projection, so
|
||||
// that the cameraPosition uniform gets set correctly.
|
||||
// mLightSpace is used in the shader to access the shadow map texture, and has the model
|
||||
// matrix baked in.
|
||||
|
||||
// The model matrix below is in fact inverted to get the view matrix and passed to the
|
||||
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
|
||||
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
|
||||
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
|
||||
mCamera->setCustomProjection(mat4(F * W * L * Mp), znear, zfar);
|
||||
|
||||
// for the debug camera, we need to undo the world origin
|
||||
mDebugCamera->setCustomProjection(mat4(S * b * camera.worldOrigin), znear, zfar);
|
||||
W = applyLISPSM(Wp, camera, params, LMpMv,
|
||||
wsClippedShadowReceiverVolume, vertexCount, direction);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute focusing matrix (optional, greatly improves quality)
|
||||
*/
|
||||
|
||||
// construct the warped light-space
|
||||
const mat4f WLMpMv = W * LMpMv;
|
||||
|
||||
// 2) Now we find the x-y bounds of our convex-hull (view volume & shadow receivers)
|
||||
// in light space, so we can "focus" the shadow map to the interesting area.
|
||||
// This is the most important step to increase the quality of the shadow map.
|
||||
//
|
||||
// In LiPSM mode, we're using the warped space here.
|
||||
|
||||
Aabb bounds;
|
||||
if (params.options.stable && viewVolumeBoundingSphere.w > 0) {
|
||||
bounds = compute2DBounds(Mv, viewVolumeBoundingSphere);
|
||||
} else {
|
||||
bounds = compute2DBounds(WLMpMv, wsClippedShadowReceiverVolume.data(), vertexCount);
|
||||
}
|
||||
lsLightFrustumBounds.min.xy = bounds.min.xy;
|
||||
lsLightFrustumBounds.max.xy = bounds.max.xy;
|
||||
|
||||
if (params.options.stable) {
|
||||
// in stable mode we can't do anything that can change the scaling of the texture
|
||||
} else {
|
||||
// For directional lights, we further constraint the light frustum to the
|
||||
// intersection of the shadow casters & shadow receivers in light-space.
|
||||
// ** This relies on the 1-texel shadow map border **
|
||||
if (engine.debug.shadowmap.focus_shadowcasters) {
|
||||
intersectWithShadowCasters(lsLightFrustumBounds, WLMpMv, wsShadowCastersVolume);
|
||||
}
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY((lsLightFrustumBounds.min.x >= lsLightFrustumBounds.max.x) ||
|
||||
(lsLightFrustumBounds.min.y >= lsLightFrustumBounds.max.y))) {
|
||||
// this could happen if the only thing visible is a perfectly horizontal or
|
||||
// vertical thin line
|
||||
mHasVisibleShadows = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
assert_invariant(lsLightFrustumBounds.min.x < lsLightFrustumBounds.max.x);
|
||||
assert_invariant(lsLightFrustumBounds.min.y < lsLightFrustumBounds.max.y);
|
||||
|
||||
// compute focus scale and offset
|
||||
float2 s = 2.0f / float2(lsLightFrustumBounds.max.xy - lsLightFrustumBounds.min.xy);
|
||||
float2 o = -s * float2(lsLightFrustumBounds.max.xy + lsLightFrustumBounds.min.xy) * 0.5f;
|
||||
|
||||
if (params.options.stable) {
|
||||
// Use the world origin as reference point, fixed w.r.t. the camera
|
||||
snapLightFrustum(s, o, Mv, -camera.getWorldOffset(),
|
||||
1.0f / float(shadowMapInfo.shadowDimension));
|
||||
}
|
||||
|
||||
const mat4f F(mat4f::row_major_init {
|
||||
s.x, 0.0f, 0.0f, o.x,
|
||||
0.0f, s.y, 0.0f, o.y,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
});
|
||||
|
||||
/*
|
||||
* Final shadow map transform
|
||||
*/
|
||||
|
||||
// Final shadow transform
|
||||
const mat4f S = F * WLMpMv;
|
||||
|
||||
// Computes St the transform to use in the shader to access the shadow map texture
|
||||
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
|
||||
const mat4 MbMt = getTextureCoordsMapping(shadowMapInfo);
|
||||
const mat4f St = mat4f(MbMt * S);
|
||||
|
||||
ShadowMap::ShaderParameters shaderParameters;
|
||||
|
||||
// note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because
|
||||
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
|
||||
// if Wp[3][1] is 0, then LISPSM was cancelled.
|
||||
if (useLispsm && Wp[3][1] != 0.0f) {
|
||||
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(Wp, mat4f(MbMt * F),
|
||||
shadowMapInfo.shadowDimension);
|
||||
} else {
|
||||
// We know we're using an ortho projection
|
||||
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(St.upperLeft(),
|
||||
shadowMapInfo.shadowDimension);
|
||||
}
|
||||
if (!shadowMapInfo.vsm) {
|
||||
shaderParameters.lightSpace = St;
|
||||
} else {
|
||||
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar);
|
||||
}
|
||||
|
||||
// We apply the constant bias in world space (as opposed to light-space) to account
|
||||
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
|
||||
// by baking it in the shadow-map itself.
|
||||
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
const mat4f b = mat4f::translation(direction * constantBias);
|
||||
|
||||
// It's important to set the light camera's model matrix separately from its projection, so
|
||||
// that the cameraPosition uniform gets set correctly.
|
||||
// mLightSpace is used in the shader to access the shadow map texture, and has the model
|
||||
// matrix baked in.
|
||||
|
||||
// The model matrix below is in fact inverted to get the view matrix and passed to the
|
||||
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
|
||||
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
|
||||
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
|
||||
mCamera->setCustomProjection(mat4(F * W * L * Mp), znear, zfar);
|
||||
|
||||
// for the debug camera, we need to undo the world origin
|
||||
mDebugCamera->setCustomProjection(mat4(S * b * camera.worldOrigin), znear, zfar);
|
||||
|
||||
return shaderParameters;
|
||||
}
|
||||
|
||||
void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo,
|
||||
FScene const& scene, SceneInfo& sceneInfo) noexcept {
|
||||
FScene const& scene, SceneInfo sceneInfo) noexcept {
|
||||
|
||||
mShadowMapInfo = shadowMapInfo;
|
||||
ShaderParameters shaderParameters;
|
||||
|
||||
auto& lcm = mEngine.getLightManager();
|
||||
auto& lcm = engine.getLightManager();
|
||||
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
|
||||
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
|
||||
@@ -395,9 +424,6 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
|
||||
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
|
||||
|
||||
// TODO: correctly compute if this spot light has any visible shadows.
|
||||
mHasVisibleShadows = true;
|
||||
|
||||
/*
|
||||
* Compute the light model matrix.
|
||||
*/
|
||||
@@ -406,10 +432,14 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
|
||||
|
||||
// find decent near/far
|
||||
ShadowMap::updateSceneInfo(Mv, scene, sceneInfo, mShadowMapInfo.spotIndex);
|
||||
ShadowMap::updateSceneInfoSpot(Mv, scene, sceneInfo);
|
||||
|
||||
// if the scene was empty, near > far
|
||||
mHasVisibleShadows = -sceneInfo.lsNearFar[0] < -sceneInfo.lsNearFar[1];
|
||||
|
||||
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
|
||||
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar.x);
|
||||
float farPlane = std::min(radius, -sceneInfo.lsNearFar.y);
|
||||
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar[0]);
|
||||
float farPlane = std::min(radius, -sceneInfo.lsNearFar[1]);
|
||||
|
||||
float outerConeAngleDegrees = outerConeAngle * f::RAD_TO_DEG;
|
||||
const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2.0f, 1.0f, nearPlane, farPlane);
|
||||
@@ -418,7 +448,7 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
// Final shadow transform
|
||||
const mat4f S = MpMv;
|
||||
|
||||
const mat4 MbMt = getTextureCoordsMapping();
|
||||
const mat4 MbMt = getTextureCoordsMapping(shadowMapInfo);
|
||||
const mat4f St = mat4f(MbMt * S);
|
||||
|
||||
// TODO: focus projection
|
||||
@@ -434,16 +464,16 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
// = zInLightSpace * texelSizeAtOneMeter
|
||||
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
|
||||
// Note: this would not work with LISPSM, which warps the texture space.
|
||||
mTexelSizeAtOneMeterWs = (2.0f * std::tan(outerConeAngle) / float(mShadowMapInfo.shadowDimension));
|
||||
mLightFromWorldZ = -transpose(Mv)[2]; // negate because camera looks in -Z
|
||||
shaderParameters.texelSizeAtOneMeterWs = (2.0f * std::tan(outerConeAngle) / float(shadowMapInfo.shadowDimension));
|
||||
shaderParameters.lightFromWorldZ = -transpose(Mv)[2]; // negate because camera looks in -Z
|
||||
|
||||
if (!mShadowMapInfo.vsm) {
|
||||
mLightSpace = St;
|
||||
if (!shadowMapInfo.vsm) {
|
||||
shaderParameters.lightSpace = St;
|
||||
} else {
|
||||
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
|
||||
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
|
||||
}
|
||||
|
||||
const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
const mat4f b = mat4f::translation(direction * constantBias);
|
||||
const mat4f Sb = S * b;
|
||||
|
||||
@@ -460,6 +490,76 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
|
||||
// for the debug camera, we need to undo the world origin
|
||||
mDebugCamera->setCustomProjection(mat4(Sb * camera.worldOrigin), nearPlane, radius);
|
||||
|
||||
return shaderParameters;
|
||||
}
|
||||
|
||||
ShadowMap::ShaderParameters ShadowMap::updatePoint(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo, uint8_t face) noexcept {
|
||||
|
||||
ShaderParameters shaderParameters;
|
||||
|
||||
// check if this shadow map has anything to render
|
||||
mHasVisibleShadows = false;
|
||||
FScene::RenderableSoa const& UTILS_RESTRICT soa = scene.getRenderableData();
|
||||
auto const* const UTILS_RESTRICT visibleMasks = soa.data<FScene::VISIBLE_MASK>();
|
||||
size_t c = soa.size();
|
||||
for (size_t i = 0; i < c; i++) {
|
||||
if (visibleMasks[i] & VISIBLE_DYN_SHADOW_RENDERABLE) {
|
||||
mHasVisibleShadows = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mHasVisibleShadows) {
|
||||
return shaderParameters;
|
||||
}
|
||||
|
||||
auto& lcm = engine.getLightManager();
|
||||
|
||||
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
|
||||
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
|
||||
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
|
||||
|
||||
/*
|
||||
* Compute the light model matrix.
|
||||
*/
|
||||
|
||||
const mat4f Mv = getPointLightViewMatrix(TextureCubemapFace(face), position);
|
||||
const float3 direction = -transpose(Mv)[2].xyz;
|
||||
|
||||
// TODO: don't hardcode near plane
|
||||
// Choose a reasonable value for the near plane.
|
||||
float nearPlane = 0.01f;
|
||||
float farPlane = radius;
|
||||
const mat4f Mp = mat4f::perspective(90.0f, 1.0f, nearPlane, farPlane);
|
||||
|
||||
// For calculating the point light normal bias, we need the texel size in world space at the
|
||||
// sample location. Using Thales's theorem, we find:
|
||||
// texelSize(zInLightSpace) = zInLightSpace * texelSizeOnTheNearPlane / near
|
||||
// = zInLightSpace * texelSizeAtOneMeter
|
||||
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
|
||||
// Note: this would not work with LISPSM, which warps the texture space.
|
||||
shaderParameters.texelSizeAtOneMeterWs =
|
||||
(2.0f * std::tan(f::PI_4) / float(shadowMapInfo.shadowDimension));
|
||||
|
||||
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
const mat4f b = mat4f::translation(direction * constantBias);
|
||||
|
||||
// It's important to set the light camera's model matrix separately from its projection, so that
|
||||
// the cameraPosition uniform gets set correctly.
|
||||
// mLightSpace is used in the shader to access the shadow map texture, and has the model matrix
|
||||
// baked in.
|
||||
|
||||
// The model matrix below is in fact inverted to get the view matrix and passed to the
|
||||
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
|
||||
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
|
||||
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
|
||||
mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane);
|
||||
|
||||
return shaderParameters;
|
||||
}
|
||||
|
||||
mat4f ShadowMap::applyLISPSM(mat4f& Wp,
|
||||
@@ -499,7 +599,8 @@ mat4f ShadowMap::applyLISPSM(mat4f& Wp,
|
||||
mat4f W;
|
||||
// see nopt1 below for an explanation about this test
|
||||
// sinLV is positive since it comes from a square-root
|
||||
if (sinLV > 0 && 3.0f * (dzn / (zf - zn)) < 2.0f) {
|
||||
constexpr float epsilon = 0.02f; // very roughly 1 degree
|
||||
if (sinLV > epsilon && 3.0f * (dzn / (zf - zn)) < 2.0f) {
|
||||
// nopt is the optimal near plane distance of Wp (i.e. distance from P).
|
||||
|
||||
// virtual near and far planes
|
||||
@@ -540,10 +641,10 @@ mat4f ShadowMap::applyLISPSM(mat4f& Wp,
|
||||
|
||||
|
||||
// Apply these remapping in double to maintain a high precision for the depth axis
|
||||
mat4 ShadowMap::getTextureCoordsMapping() const noexcept {
|
||||
mat4 ShadowMap::getTextureCoordsMapping(ShadowMapInfo const& info) noexcept {
|
||||
// remapping from NDC to texture coordinates (i.e. [-1,1] -> [0, 1])
|
||||
// ([1, 0] for depth mapping)
|
||||
const mat4f Mt(mClipSpaceFlipped ? mat4f::row_major_init{
|
||||
const mat4f Mt(info.clipSpaceFlipped ? mat4f::row_major_init{
|
||||
0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, -0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, -0.5f, 0.5f,
|
||||
@@ -557,7 +658,7 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept {
|
||||
|
||||
// the shadow map texture might be larger than the shadow map dimension, so we add a scaling
|
||||
// factor
|
||||
const float v = (float) mShadowMapInfo.textureDimension / mShadowMapInfo.atlasDimension;
|
||||
const float v = float(info.textureDimension) / float(info.atlasDimension);
|
||||
const mat4f Mv(mat4f::row_major_init{
|
||||
v, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, v, 0.0f, 0.0f,
|
||||
@@ -566,8 +667,8 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept {
|
||||
});
|
||||
|
||||
// apply the 1-texel border viewport transform
|
||||
const float o = 1.0f / mShadowMapInfo.atlasDimension;
|
||||
const float s = 1.0f - 2.0f * (1.0f / mShadowMapInfo.textureDimension);
|
||||
const float o = 1.0f / float(info.atlasDimension);
|
||||
const float s = 1.0f - 2.0f * (1.0f / float(info.textureDimension));
|
||||
const mat4f Mb(mat4f::row_major_init{
|
||||
s, 0.0f, 0.0f, o,
|
||||
0.0f, s, 0.0f, o,
|
||||
@@ -575,7 +676,7 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept {
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
});
|
||||
|
||||
const mat4f Mf = mTextureSpaceFlipped ? mat4f(mat4f::row_major_init{
|
||||
const mat4f Mf = info.textureSpaceFlipped ? mat4f(mat4f::row_major_init{
|
||||
1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
@@ -883,9 +984,11 @@ size_t ShadowMap::intersectFrustumWithBox(
|
||||
wsFrustumCorners, wsSceneReceiversCorners.vertices);
|
||||
} else {
|
||||
// by construction vertexCount should be 8 here, but it can be more because
|
||||
// step (b) above can classify a point as inside the frustum that isn't quite.
|
||||
// step (b) above can classify a point as inside the frustum that isn't quite inside.
|
||||
// -> We can't blindly reset vertexCount to 8, we need to remove the "duplicate"
|
||||
// vertices.
|
||||
// It shouldn't cause any major issues later to have "nearly" duplicate vertices.
|
||||
assert_invariant(vertexCount >= 8);
|
||||
vertexCount = 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -966,13 +1069,14 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p,
|
||||
return hit;
|
||||
}
|
||||
|
||||
float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture) const noexcept {
|
||||
float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture,
|
||||
uint16_t shadowDimension) const noexcept {
|
||||
// The Jacobian of the transformation from texture-to-world is the matrix itself for
|
||||
// orthographic projections. We just need to inverse worldToShadowTexture,
|
||||
// which is guaranteed to be orthographic.
|
||||
// The two first columns give us how a texel maps in world-space.
|
||||
const float ures = 1.0f / float(mShadowMapInfo.shadowDimension);
|
||||
const float vres = 1.0f / float(mShadowMapInfo.shadowDimension);
|
||||
const float ures = 1.0f / float(shadowDimension);
|
||||
const float vres = 1.0f / float(shadowDimension);
|
||||
const mat3f shadowTextureToWorld(inverse(worldToShadowTexture));
|
||||
const float3 Jx = shadowTextureToWorld[0];
|
||||
const float3 Jy = shadowTextureToWorld[1];
|
||||
@@ -980,7 +1084,8 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture) const no
|
||||
return s;
|
||||
}
|
||||
|
||||
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const noexcept {
|
||||
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF,
|
||||
uint16_t shadowDimension) const noexcept {
|
||||
// Here we compute the Jacobian of inverse(MbMtF * Wp).
|
||||
// The expression below has been computed with Mathematica. However, it's not very hard,
|
||||
// albeit error-prone, to do it by hand because MbMtF is a linear transform.
|
||||
@@ -995,8 +1100,8 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const
|
||||
// It might be better to do this computation in the vertex shader.
|
||||
float3 p = {0.5, 0.5, 0.0};
|
||||
|
||||
const float ures = 1.0f / float(mShadowMapInfo.shadowDimension);
|
||||
const float vres = 1.0f / float(mShadowMapInfo.shadowDimension);
|
||||
const float ures = 1.0f / float(shadowDimension);
|
||||
const float vres = 1.0f / float(shadowDimension);
|
||||
const float dres = 1.0f / 65536.0f;
|
||||
|
||||
constexpr bool JACOBIAN_ESTIMATE = false;
|
||||
@@ -1072,8 +1177,9 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMap::initSceneInfo(FScene const& scene, mat4f const& viewMatrix,
|
||||
ShadowMap::SceneInfo& sceneInfo) {
|
||||
void ShadowMap::initSceneInfo(ShadowMap::SceneInfo& sceneInfo,
|
||||
uint8_t visibleLayers, FScene const& scene, mat4f const& viewMatrix) {
|
||||
sceneInfo.visibleLayers = visibleLayers;
|
||||
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
|
||||
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
|
||||
@@ -1101,8 +1207,8 @@ void ShadowMap::initSceneInfo(FScene const& scene, mat4f const& viewMatrix,
|
||||
);
|
||||
}
|
||||
|
||||
void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
|
||||
ShadowMap::SceneInfo& sceneInfo) {
|
||||
void ShadowMap::updateSceneInfoDirectional(mat4f const& Mv, FScene const& scene,
|
||||
SceneInfo& sceneInfo) {
|
||||
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
visitScene(scene, sceneInfo.visibleLayers,
|
||||
[&](Aabb caster, Culler::result_type) {
|
||||
@@ -1115,13 +1221,13 @@ void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
|
||||
);
|
||||
}
|
||||
|
||||
void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
|
||||
ShadowMap::SceneInfo& sceneInfo, uint16_t index) {
|
||||
void ShadowMap::updateSceneInfoSpot(mat4f const& Mv, FScene const& scene,
|
||||
SceneInfo& sceneInfo) {
|
||||
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
visitScene(scene, sceneInfo.visibleLayers,
|
||||
[&](Aabb caster, Culler::result_type mask) {
|
||||
if (mask & VISIBLE_SPOT_SHADOW_RENDERABLE_N(index)) {
|
||||
if (mask & VISIBLE_DYN_SHADOW_RENDERABLE) {
|
||||
float2 nf = ShadowMap::computeNearFar(Mv, caster);
|
||||
sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near
|
||||
sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far
|
||||
|
||||
+74
-65
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "backend/DriverApiForward.h"
|
||||
#include "private/backend/SamplerGroup.h"
|
||||
#include "math/mat4.h"
|
||||
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec4.h>
|
||||
@@ -40,9 +41,7 @@ class RenderPass;
|
||||
// +------------------------------------------------------+
|
||||
// VISIBLE_RENDERABLE X
|
||||
// VISIBLE_DIR_SHADOW_RENDERABLE X
|
||||
// VISIBLE_SPOT_SHADOW_RENDERABLE_0 X
|
||||
// VISIBLE_SPOT_SHADOW_RENDERABLE_1 X
|
||||
// ...
|
||||
// VISIBLE_DYN_SHADOW_RENDERABLE X
|
||||
|
||||
// A "shadow renderable" is a renderable rendered to the shadow map during a shadow pass:
|
||||
// PCF shadows: only shadow casters
|
||||
@@ -50,25 +49,20 @@ class RenderPass;
|
||||
|
||||
static constexpr size_t VISIBLE_RENDERABLE_BIT = 0u;
|
||||
static constexpr size_t VISIBLE_DIR_SHADOW_RENDERABLE_BIT = 1u;
|
||||
|
||||
static constexpr size_t VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(size_t n) { return n + 2; }
|
||||
static constexpr size_t VISIBLE_DYN_SHADOW_RENDERABLE_BIT = 2u;
|
||||
|
||||
static constexpr Culler::result_type VISIBLE_DIR_SHADOW_RENDERABLE = 1u << VISIBLE_DIR_SHADOW_RENDERABLE_BIT;
|
||||
static constexpr Culler::result_type VISIBLE_SPOT_SHADOW_RENDERABLE_N(size_t n) {
|
||||
return 1u << VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(n);
|
||||
}
|
||||
|
||||
// ORing of all the VISIBLE_SPOT_SHADOW_RENDERABLE bits
|
||||
static constexpr Culler::result_type VISIBLE_SPOT_SHADOW_RENDERABLE =
|
||||
(0xFFu >> (sizeof(Culler::result_type) * 8u - CONFIG_MAX_SHADOW_CASTING_SPOTS)) << 2u;
|
||||
|
||||
// Because we're using a uint16_t for the visibility mask, we're limited to 14 spot light shadows.
|
||||
// (2 of the bits are used for visible renderables + directional light shadow casters).
|
||||
static_assert(CONFIG_MAX_SHADOW_CASTING_SPOTS <= sizeof(Culler::result_type) * 8 - 2,
|
||||
"CONFIG_MAX_SHADOW_CASTING_SPOTS cannot be higher than 14.");
|
||||
static constexpr Culler::result_type VISIBLE_DYN_SHADOW_RENDERABLE = 1u << VISIBLE_DYN_SHADOW_RENDERABLE_BIT;
|
||||
|
||||
class ShadowMap {
|
||||
public:
|
||||
|
||||
enum class ShadowType : uint8_t {
|
||||
DIRECTIONAL,
|
||||
SPOT,
|
||||
POINT
|
||||
};
|
||||
|
||||
explicit ShadowMap(FEngine& engine) noexcept;
|
||||
|
||||
// ShadowMap is not copyable for now
|
||||
@@ -91,29 +85,26 @@ public:
|
||||
// e.g., for a texture dimension of 512, shadowDimension would be 510
|
||||
uint16_t shadowDimension = 0;
|
||||
|
||||
// This spot shadowmap index.
|
||||
uint16_t spotIndex = 0;
|
||||
// e.g. Vulkan clip-space
|
||||
bool clipSpaceFlipped = false;
|
||||
|
||||
// e.g. metal textures
|
||||
bool textureSpaceFlipped = false;
|
||||
|
||||
// whether we're using vsm
|
||||
bool vsm = false;
|
||||
|
||||
// polygon offset
|
||||
backend::PolygonOffset polygonOffset{};
|
||||
};
|
||||
|
||||
struct SceneInfo {
|
||||
explicit SceneInfo(uint8_t visibleLayers) noexcept : visibleLayers(visibleLayers) { }
|
||||
|
||||
// The near and far planes, in clip space, to use for this shadow map
|
||||
// scratch data: The near and far planes, in clip space, to use for this shadow map
|
||||
math::float2 csNearFar = { -1.0f, 1.0f };
|
||||
|
||||
// The following fields are set by computeSceneCascadeParams.
|
||||
|
||||
// light's near/far expressed in light-space, calculated from the scene's content
|
||||
// assuming the light is at the origin.
|
||||
// scratch data: light's near/far expressed in light-space, calculated from the scene's
|
||||
// content assuming the light is at the origin.
|
||||
math::float2 lsNearFar{};
|
||||
|
||||
// Viewing camera's near/far expressed in view-space, calculated from the scene's content
|
||||
// scratch data: Viewing camera's near/far expressed in view-space, calculated from the
|
||||
// scene's content.
|
||||
math::float2 vsNearFar{};
|
||||
|
||||
// World-space shadow-casters volume
|
||||
@@ -128,51 +119,68 @@ public:
|
||||
static math::mat4f getDirectionalLightViewMatrix(
|
||||
math::float3 direction, math::float3 position = {}) noexcept;
|
||||
|
||||
static math::mat4f getPointLightViewMatrix(backend::TextureCubemapFace face,
|
||||
math::float3 position) noexcept;
|
||||
|
||||
void initialize(size_t lightIndex, ShadowType shadowType, uint16_t shadowIndex,
|
||||
LightManager::ShadowOptions const* options);
|
||||
|
||||
struct ShaderParameters {
|
||||
math::mat4f lightSpace{};
|
||||
math::float4 lightFromWorldZ{};
|
||||
float texelSizeAtOneMeterWs{};
|
||||
};
|
||||
|
||||
// Call once per frame if the light, scene (or visible layers) or camera changes.
|
||||
// This computes the light's camera.
|
||||
void updateDirectional(const FScene::LightSoa& lightData, size_t index,
|
||||
ShaderParameters updateDirectional(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
ShadowMapInfo const& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
void updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
ShaderParameters updateSpot(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept;
|
||||
SceneInfo sceneInfo) noexcept;
|
||||
|
||||
void render(FScene const& scene, utils::Range<uint32_t> range,
|
||||
FScene::VisibleMaskType visibilityMask, RenderPass* pass) noexcept;
|
||||
ShaderParameters updatePoint(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo,
|
||||
FScene const& scene,
|
||||
SceneInfo sceneInfo, uint8_t face) noexcept;
|
||||
|
||||
// Do we have visible shadows. Valid after calling update().
|
||||
bool hasVisibleShadows() const noexcept { return mHasVisibleShadows; }
|
||||
|
||||
// Computes the transform to use in the shader to access the shadow map.
|
||||
// Valid after calling update().
|
||||
math::mat4f const& getLightSpaceMatrix() const noexcept { return mLightSpace; }
|
||||
|
||||
// return the size of a texel in world space (pre-warping)
|
||||
float getTexelSizAtOneMeterWs() const noexcept { return mTexelSizeAtOneMeterWs; }
|
||||
math::float4 getLightFromWorldZ() const noexcept { return mLightFromWorldZ; }
|
||||
|
||||
// Returns the light's projection. Valid after calling update().
|
||||
FCamera const& getCamera() const noexcept { return *mCamera; }
|
||||
|
||||
// use only for debugging
|
||||
FCamera const& getDebugCamera() const noexcept { return *mDebugCamera; }
|
||||
|
||||
backend::PolygonOffset getPolygonOffset() const noexcept { return mShadowMapInfo.polygonOffset; }
|
||||
|
||||
// Call once per frame to populate the SceneInfo struct, then pass to update().
|
||||
// This computes values constant across all shadow maps.
|
||||
static void initSceneInfo(FScene const& scene, math::mat4f const& viewMatrix,
|
||||
ShadowMap::SceneInfo& sceneInfo);
|
||||
static void initSceneInfo(ShadowMap::SceneInfo& sceneInfo, uint8_t visibleLayers,
|
||||
FScene const& scene, math::mat4f const& viewMatrix);
|
||||
|
||||
// Update SceneInfo struct for a given light
|
||||
static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene,
|
||||
ShadowMap::SceneInfo& sceneInfo);
|
||||
static void updateSceneInfoDirectional(const math::mat4f& Mv, FScene const& scene,
|
||||
SceneInfo& sceneInfo);
|
||||
|
||||
static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene,
|
||||
ShadowMap::SceneInfo& sceneInfo, uint16_t index);
|
||||
static void updateSceneInfoSpot(const math::mat4f& Mv, FScene const& scene,
|
||||
SceneInfo& sceneInfo);
|
||||
|
||||
LightManager::ShadowOptions const* getShadowOptions() const noexcept { return mOptions; }
|
||||
size_t getLightIndex() const { return mLightIndex; }
|
||||
uint16_t getShadowIndex() const { return mShadowIndex; }
|
||||
void setLayer(uint8_t layer) noexcept { mLayer = layer; }
|
||||
uint8_t getLayer() const noexcept { return mLayer; }
|
||||
bool isDirectionalShadow() const noexcept { return mShadowType == ShadowType::DIRECTIONAL; }
|
||||
bool isSpotShadow() const noexcept { return mShadowType == ShadowType::SPOT; }
|
||||
bool isPointShadow() const noexcept { return mShadowType == ShadowType::POINT; }
|
||||
ShadowType getShadowType() const noexcept { return mShadowType; }
|
||||
|
||||
private:
|
||||
struct Segment {
|
||||
@@ -245,13 +253,16 @@ private:
|
||||
|
||||
static math::mat4f directionalLightFrustum(float n, float f) noexcept;
|
||||
|
||||
math::mat4 getTextureCoordsMapping() const noexcept;
|
||||
static math::mat4 getTextureCoordsMapping(ShadowMapInfo const& info) noexcept;
|
||||
|
||||
static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf,
|
||||
const math::mat4f& Mv, float znear, float zfar) noexcept;
|
||||
|
||||
float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture) const noexcept;
|
||||
float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF) const noexcept;
|
||||
float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
|
||||
uint16_t shadowDimension) const noexcept;
|
||||
|
||||
float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
|
||||
uint16_t shadowDimension) const noexcept;
|
||||
|
||||
static constexpr const Segment sBoxSegments[12] = {
|
||||
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
|
||||
@@ -269,17 +280,15 @@ private:
|
||||
|
||||
FCamera* mCamera = nullptr; // 8
|
||||
FCamera* mDebugCamera = nullptr; // 8
|
||||
math::mat4f mLightSpace; // 64
|
||||
float mTexelSizeAtOneMeterWs = 0.0f; // 4
|
||||
math::float4 mLightFromWorldZ{};
|
||||
|
||||
// set-up in update()
|
||||
ShadowMapInfo mShadowMapInfo; // 20
|
||||
bool mHasVisibleShadows = false; // 1
|
||||
|
||||
FEngine& mEngine; // 8
|
||||
const bool mClipSpaceFlipped; // 1
|
||||
const bool mTextureSpaceFlipped; // 1
|
||||
// The data below technically belongs to ShadowMapManager, but it simplifies allocations
|
||||
// to store it here. This data is always associated with this shadow map anyway.
|
||||
LightManager::ShadowOptions const* mOptions = nullptr; // 8
|
||||
uint32_t mLightIndex = 0; // which light are we shadowing // 4
|
||||
uint16_t mShadowIndex = 0; // our index in the shadowMap vector // 2
|
||||
uint8_t mLayer = 0; // our layer in the shadowMap texture // 1
|
||||
ShadowType mShadowType : 2;
|
||||
bool mHasVisibleShadows : 2;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+513
-254
@@ -32,7 +32,8 @@ namespace filament {
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
ShadowMapManager::ShadowMapManager(FEngine& engine) { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
ShadowMapManager::ShadowMapManager(FEngine& engine)
|
||||
: mEngine(engine) {
|
||||
// initialize our ShadowMap array in-place
|
||||
UTILS_NOUNROLL
|
||||
for (auto& entry : mShadowMapCache) {
|
||||
@@ -49,6 +50,14 @@ ShadowMapManager::ShadowMapManager(FEngine& engine) { // NOLINT(cppcoreguideline
|
||||
&engine.debug.shadowmap.tightly_bound_scene);
|
||||
}
|
||||
|
||||
ShadowMapManager::~ShadowMapManager() {
|
||||
// destroy the ShadowMap array in-place
|
||||
UTILS_NOUNROLL
|
||||
for (auto& entry : mShadowMapCache) {
|
||||
std::destroy_at(std::launder(reinterpret_cast<ShadowMap*>(&entry)));
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMapManager::terminate(FEngine& engine) {
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
driver.destroyBufferObject(mShadowUbh);
|
||||
@@ -58,13 +67,6 @@ void ShadowMapManager::terminate(FEngine& engine) {
|
||||
}
|
||||
}
|
||||
|
||||
ShadowMapManager::~ShadowMapManager() {
|
||||
// destroy the ShadowMap array in-place
|
||||
UTILS_NOUNROLL
|
||||
for (auto& entry : mShadowMapCache) {
|
||||
std::destroy_at(std::launder(reinterpret_cast<ShadowMap*>(&entry)));
|
||||
}
|
||||
}
|
||||
|
||||
ShadowMapManager::ShadowTechnique ShadowMapManager::update(FEngine& engine, FView& view,
|
||||
CameraInfo const& cameraInfo,
|
||||
@@ -73,22 +75,15 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(FEngine& engine, FVie
|
||||
|
||||
calculateTextureRequirements(engine, view, lightData);
|
||||
|
||||
ShadowMap::SceneInfo sceneInfo(view.getVisibleLayers());
|
||||
|
||||
// Compute scene-dependent values shared across all shadow maps
|
||||
ShadowMap::initSceneInfo(*view.getScene(), cameraInfo.view, sceneInfo);
|
||||
ShadowMap::initSceneInfo(mSceneInfo,
|
||||
view.getVisibleLayers(), *view.getScene(), cameraInfo.view);
|
||||
|
||||
shadowTechnique |= updateCascadeShadowMaps(
|
||||
engine, view, cameraInfo, renderableData, lightData, sceneInfo);
|
||||
engine, view, cameraInfo, renderableData, lightData, mSceneInfo);
|
||||
|
||||
shadowTechnique |= updateSpotShadowMaps(
|
||||
engine, view, cameraInfo, renderableData, lightData, sceneInfo);
|
||||
|
||||
if (mShadowUb.isDirty()) {
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
driver.updateBufferObject(mShadowUbh,
|
||||
mShadowUb.toBufferDescriptor(driver), 0);
|
||||
}
|
||||
engine, lightData);
|
||||
|
||||
return shadowTechnique;
|
||||
}
|
||||
@@ -98,103 +93,196 @@ void ShadowMapManager::reset() noexcept {
|
||||
mSpotShadowMaps.clear();
|
||||
}
|
||||
|
||||
void ShadowMapManager::setShadowCascades(size_t lightIndex,
|
||||
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* shadowMap = getCascadeShadowMap(c);
|
||||
mCascadeShadowMaps.emplace_back(shadowMap, lightIndex, options);
|
||||
auto* pShadowMap = getCascadeShadowMap(c);
|
||||
pShadowMap->initialize(lightIndex, ShadowType::DIRECTIONAL, c, options);
|
||||
mCascadeShadowMaps.push_back(pShadowMap);
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMapManager::addSpotShadowMap(size_t lightIndex,
|
||||
void ShadowMapManager::addShadowMap(size_t lightIndex, bool spotlight,
|
||||
LightManager::ShadowOptions const* options) noexcept {
|
||||
const size_t c = mSpotShadowMaps.size();
|
||||
assert_invariant(c < CONFIG_MAX_SHADOW_CASTING_SPOTS);
|
||||
auto* shadowMap = getSpotShadowMap(c);
|
||||
mSpotShadowMaps.emplace_back(shadowMap, lightIndex, options);
|
||||
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);
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine& engine,
|
||||
RenderPass const& pass, FView& view) noexcept {
|
||||
RenderPass const& pass, FView& view, CameraInfo const& mainCameraInfo) noexcept {
|
||||
|
||||
constexpr size_t MAX_SHADOW_LAYERS =
|
||||
CONFIG_MAX_SHADOW_CASCADES + CONFIG_MAX_SHADOW_CASTING_SPOTS;
|
||||
|
||||
const TextureFormat vsmTextureFormat = TextureFormat::RG16F;
|
||||
|
||||
// make a copy here, because it's a very small structure
|
||||
const TextureAtlasRequirements textureRequirements = mTextureAtlasRequirements;
|
||||
assert_invariant(textureRequirements.layers <= MAX_SHADOW_LAYERS);
|
||||
|
||||
struct ShadowPass {
|
||||
ShadowMapEntry const* shadowMapEntry;
|
||||
utils::Range<uint32_t> range;
|
||||
FScene::VisibleMaskType visibilityMask;
|
||||
};
|
||||
|
||||
auto passList = utils::FixedCapacityVector<ShadowPass>::with_capacity(MAX_SHADOW_LAYERS);
|
||||
const float moment2 = std::numeric_limits<half>::max();
|
||||
const float moment1 = std::sqrt(moment2);
|
||||
const float4 vsmClearColor{ moment1, moment2, -moment1, moment2 };
|
||||
|
||||
FScene* scene = view.getScene();
|
||||
assert_invariant(scene);
|
||||
|
||||
// these loops create a list of the shadow maps that need to be rendered (i.e. that have
|
||||
// visible shadows).
|
||||
|
||||
// Directional, cascaded shadowmaps
|
||||
auto const directionalShadowCastersRange = view.getVisibleDirectionalShadowCasters();
|
||||
if (!directionalShadowCastersRange.empty()) {
|
||||
for (const auto& map : mCascadeShadowMaps) {
|
||||
if (map.hasVisibleShadows()) {
|
||||
passList.push_back({
|
||||
&map, directionalShadowCastersRange, VISIBLE_DIR_SHADOW_RENDERABLE });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Spotlight shadowmaps
|
||||
auto const spotShadowCastersRange = view.getVisibleSpotShadowCasters();
|
||||
if (!spotShadowCastersRange.empty()) {
|
||||
for (size_t i = 0, c = mSpotShadowMaps.size(); i < c; i++) {
|
||||
const auto& map = mSpotShadowMaps[i];
|
||||
if (map.hasVisibleShadows()) {
|
||||
passList.push_back({
|
||||
&map, spotShadowCastersRange, VISIBLE_SPOT_SHADOW_RENDERABLE_N(i) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_invariant(passList.size() <= textureRequirements.layers);
|
||||
// make a copy here, because it's a very small structure
|
||||
const TextureAtlasRequirements textureRequirements = mTextureAtlasRequirements;
|
||||
assert_invariant(textureRequirements.layers <= CONFIG_MAX_SHADOW_LAYERS);
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// Prepare Shadow Pass
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
struct PrepareShadowPassData {
|
||||
FrameGraphId<FrameGraphTexture> shadows; // the actual shadowmap
|
||||
struct ShadowPass {
|
||||
mutable RenderPass pass;
|
||||
mutable CameraInfo cameraInfo;
|
||||
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;
|
||||
// a RenderPass per shadow map
|
||||
utils::FixedCapacityVector<ShadowPass> passList;
|
||||
};
|
||||
|
||||
auto& prepareShadowPass = fg.addPass<PrepareShadowPassData>("Prepare Shadow Pass",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.passList.reserve(CONFIG_MAX_SHADOW_LAYERS);
|
||||
data.shadows = builder.createTexture("Shadowmap", {
|
||||
.width = textureRequirements.size, .height = textureRequirements.size,
|
||||
.depth = textureRequirements.layers,
|
||||
.levels = textureRequirements.levels,
|
||||
.type = SamplerType::SAMPLER_2D_ARRAY,
|
||||
.format = view.hasVSM() ? vsmTextureFormat : mTextureFormat
|
||||
.format = textureRequirements.format
|
||||
});
|
||||
|
||||
// these loops create a list of the shadow maps that might need to be rendered
|
||||
auto& passList = data.passList;
|
||||
|
||||
// Directional, cascaded shadow maps
|
||||
auto const directionalShadowCastersRange = view.getVisibleDirectionalShadowCasters();
|
||||
if (!directionalShadowCastersRange.empty()) {
|
||||
for (auto* pShadowMap : mCascadeShadowMaps) {
|
||||
// for the directional light, we already know if it has visible shadows.
|
||||
if (pShadowMap->hasVisibleShadows()) {
|
||||
passList.push_back({
|
||||
pass, {}, pShadowMap, directionalShadowCastersRange,
|
||||
VISIBLE_DIR_SHADOW_RENDERABLE });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Point lights and Spotlight shadow maps
|
||||
auto const spotShadowCastersRange = view.getVisibleSpotShadowCasters();
|
||||
if (!spotShadowCastersRange.empty()) {
|
||||
for (auto* pShadowMap : mSpotShadowMaps) {
|
||||
assert_invariant(!pShadowMap->isDirectionalShadow());
|
||||
passList.push_back({
|
||||
pass, {}, 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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_invariant(passList.size() <= textureRequirements.layers);
|
||||
|
||||
// This pass must be declared as having a side effect because it never gets a
|
||||
// "read" from one of its resource (only writes), so the FrameGraph culls it.
|
||||
builder.sideEffect();
|
||||
},
|
||||
[&view](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
[this, &engine, &view, scene, &mainCameraInfo](
|
||||
FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
// set uniforms needed to render this ShadowMap
|
||||
view.prepareShadowMap();
|
||||
view.prepareShadowMap(view.getVsmShadowOptions().highPrecision);
|
||||
|
||||
// Note: we could almost parallel_for the loop below, the problem currently is
|
||||
// that updatePrimitivesLod() updates temporary global state.
|
||||
// prepareSpotShadowMap() also update the visibility of renderable. These two
|
||||
// pieces of state are needed only until shadowMap.render() returns.
|
||||
// Conceptually, we could store this out-of-band.
|
||||
|
||||
// Generate a RenderPass for each shadow map
|
||||
for (auto const& entry : data.passList) {
|
||||
ShadowMap& shadowMap = *entry.shadowMap;
|
||||
|
||||
// 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,
|
||||
scene->getRenderableData(), entry.range,
|
||||
scene->getLightData(), mSceneInfo);
|
||||
break;
|
||||
case ShadowType::POINT:
|
||||
preparePointShadowMap(shadowMap, engine, view, mainCameraInfo,
|
||||
scene->getRenderableData(), entry.range,
|
||||
scene->getLightData(), entry.face, 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.
|
||||
|
||||
// generate and sort the commands for rendering the shadow map
|
||||
|
||||
// cameraInfo only valid after calling update
|
||||
const CameraInfo cameraInfo{ shadowMap.getCamera() };
|
||||
|
||||
// updatePrimitivesLod must be run before RenderPass::appendCommands.
|
||||
view.updatePrimitivesLod(engine,
|
||||
cameraInfo, scene->getRenderableData(), entry.range);
|
||||
|
||||
if (!view.hasVSM()) {
|
||||
auto li = scene->getLightData().elementAt<FScene::LIGHT_INSTANCE>(
|
||||
shadowMap.getLightIndex());
|
||||
auto const& params = engine.getLightManager().getShadowParams(li);
|
||||
const PolygonOffset polygonOffset = { // handle reversed Z
|
||||
.slope = -params.options.polygonOffsetSlope,
|
||||
.constant = -params.options.polygonOffsetConstant
|
||||
};
|
||||
entry.pass.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(engine, RenderPass::SHADOW);
|
||||
entry.pass.sortCommands(engine);
|
||||
entry.cameraInfo = cameraInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally update our UBO in one batch
|
||||
if (mShadowUb.isDirty()) {
|
||||
driver.updateBufferObject(mShadowUbh,
|
||||
mShadowUb.toBufferDescriptor(driver), 0);
|
||||
}
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
const float vsmMoment2 = std::numeric_limits<half>::max();
|
||||
const float vsmMoment1 = std::sqrt(vsmMoment2);
|
||||
const float4 vsmClearColor{ vsmMoment1, vsmMoment2, 0.0f, 0.0f };
|
||||
// Shadow Passes
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
struct ShadowPassData {
|
||||
FrameGraphId<FrameGraphTexture> tempBlurSrc{}; // temporary shadowmap when blurring
|
||||
@@ -203,11 +291,14 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
|
||||
uint32_t shadowRt{};
|
||||
};
|
||||
|
||||
auto& ppm = engine.getPostProcessManager();
|
||||
|
||||
for (auto const& entry : passList) {
|
||||
const auto layer = entry.shadowMapEntry->getLayer();
|
||||
const auto* options = entry.shadowMapEntry->getShadowOptions();
|
||||
auto const& passList = prepareShadowPass.getData().passList;
|
||||
for (auto const& entry: passList) {
|
||||
if (!entry.shadowMap->hasVisibleShadows()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint8_t layer = entry.shadowMap->getLayer() + entry.face;
|
||||
const auto* options = entry.shadowMap->getShadowOptions();
|
||||
|
||||
auto& shadowPass = fg.addPass<ShadowPassData>("Shadow Pass",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
@@ -227,14 +318,14 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
|
||||
auto depth = builder.createTexture("Temporary VSM Depth Texture", {
|
||||
.width = textureRequirements.size, .height = textureRequirements.size,
|
||||
.samples = options->vsm.msaaSamples,
|
||||
.format = mTextureFormat,
|
||||
.format = TextureFormat::DEPTH16,
|
||||
});
|
||||
|
||||
// Temporary (resolved) texture used to render the shadowmap when blurring
|
||||
// is needed -- it'll be used as the source of the blur.
|
||||
data.tempBlurSrc = builder.createTexture("Temporary Shadowmap", {
|
||||
.width = textureRequirements.size, .height = textureRequirements.size,
|
||||
.format = vsmTextureFormat
|
||||
.format = textureRequirements.format
|
||||
});
|
||||
|
||||
depth = builder.write(depth,
|
||||
@@ -262,7 +353,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
|
||||
.clearColor = vsmClearColor,
|
||||
.samples = options->vsm.msaaSamples,
|
||||
.clearFlags = TargetBufferFlags::COLOR
|
||||
| TargetBufferFlags::DEPTH
|
||||
| TargetBufferFlags::DEPTH
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -276,22 +367,22 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
|
||||
// finally, create the shadowmap render target -- one per layer.
|
||||
data.shadowRt = builder.declareRenderPass("Shadow RT", renderTargetDesc);
|
||||
},
|
||||
[=, &engine, &view](FrameGraphResources const& resources,
|
||||
[=, &engine, &entry, &view](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
|
||||
ShadowMap& shadowMap = entry.shadowMapEntry->getShadowMap();
|
||||
const CameraInfo cameraInfo(shadowMap.getCamera());
|
||||
// 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
|
||||
// initialized, as this happens in an `execute` block.
|
||||
|
||||
// updatePrimitivesLod must be run before RenderPass::appendCommands.
|
||||
view.updatePrimitivesLod(engine, cameraInfo, scene->getRenderableData(), entry.range);
|
||||
CameraInfo const& cameraInfo = entry.cameraInfo;
|
||||
|
||||
// generate and sort the commands for rendering the shadow map
|
||||
RenderPass entryPass(pass);
|
||||
shadowMap.render(*scene, entry.range, entry.visibilityMask, &entryPass);
|
||||
|
||||
const auto& executor = entryPass.getExecutor();
|
||||
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
|
||||
@@ -306,6 +397,11 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
|
||||
// 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);
|
||||
@@ -315,22 +411,23 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
|
||||
auto rt = resources.getRenderPassInfo(blur ? data.blurRt : data.shadowRt);
|
||||
rt.params.viewport = viewport;
|
||||
|
||||
executor.execute("Shadow Pass", rt.target, rt.params);
|
||||
executor.execute(engine, "Shadow Pass", rt.target, rt.params);
|
||||
});
|
||||
|
||||
|
||||
// now emit the blurring passes
|
||||
// now emit the blurring passes if needed
|
||||
if (view.hasVSM()) {
|
||||
auto& ppm = engine.getPostProcessManager();
|
||||
|
||||
const float blurWidth = options->vsm.blurWidth;
|
||||
if (blurWidth > 0.0f) {
|
||||
const float sigma = (blurWidth + 1.0f) / 6.0f;
|
||||
size_t kernelWidth = std::ceil((blurWidth - 5.0f) / 4.0f);
|
||||
kernelWidth = kernelWidth * 4 + 5;
|
||||
const float ratio = float(kernelWidth + 1) / sigma;
|
||||
ppm.gaussianBlurPass(fg,
|
||||
shadowPass->tempBlurSrc,
|
||||
shadowPass->output,
|
||||
false, kernelWidth, ratio);
|
||||
false, kernelWidth, sigma);
|
||||
}
|
||||
|
||||
// If the shadow texture has more than one level, mipmapping was requested, either directly
|
||||
@@ -360,104 +457,110 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
|
||||
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options.mapSize),
|
||||
.shadowDimension = uint16_t(options.mapSize - 2u),
|
||||
.vsm = view.hasVSM(),
|
||||
.polygonOffset = { // handle reversed Z
|
||||
.slope = view.hasVSM() ? 0.0f : -params.options.polygonOffsetSlope,
|
||||
.constant = view.hasVSM() ? 0.0f : -params.options.polygonOffsetConstant
|
||||
}
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options.mapSize),
|
||||
.shadowDimension = uint16_t(options.mapSize - 2u),
|
||||
.clipSpaceFlipped = engine.getBackend() == Backend::VULKAN,
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL,
|
||||
.vsm = view.hasVSM()
|
||||
};
|
||||
|
||||
bool hasVisibleShadows = false;
|
||||
if (!mCascadeShadowMaps.empty()) {
|
||||
// Even if we have more than one cascade, we cull directional shadow casters against the
|
||||
// entire camera frustum, as if we only had a single cascade.
|
||||
ShadowMapEntry& entry = mCascadeShadowMaps[0];
|
||||
ShadowMap& shadowMap = entry.getShadowMap();
|
||||
ShadowMap& shadowMap = *mCascadeShadowMaps[0];
|
||||
|
||||
shadowMap.updateDirectional(lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
|
||||
auto shaderParameters = shadowMap.updateDirectional(mEngine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
|
||||
|
||||
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
|
||||
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
|
||||
VISIBLE_DIR_SHADOW_RENDERABLE_BIT);
|
||||
hasVisibleShadows = shadowMap.hasVisibleShadows();
|
||||
|
||||
// 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 = shadowMap.getTexelSizAtOneMeterWs();
|
||||
mShadowMappingUniforms.shadowBias = normalBias * wsTexelSize;
|
||||
mShadowMappingUniforms.shadowBulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
|
||||
if (hasVisibleShadows) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust the near and far planes to tightly bound the scene.
|
||||
float vsNear = -cameraInfo.zn;
|
||||
float vsFar = -cameraInfo.zf;
|
||||
if (engine.debug.shadowmap.tightly_bound_scene) {
|
||||
vsNear = std::min(vsNear, sceneInfo.vsNearFar.x);
|
||||
vsFar = std::max(vsFar, sceneInfo.vsNearFar.y);
|
||||
}
|
||||
|
||||
const size_t cascadeCount = mCascadeShadowMaps.size();
|
||||
|
||||
// We divide the camera frustum into N cascades. This gives us N + 1 split positions.
|
||||
// The first split position is the near plane; the last split position is the far plane.
|
||||
std::array<float, CascadeSplits::SPLIT_COUNT> splitPercentages{};
|
||||
splitPercentages[cascadeCount] = 1.0f;
|
||||
for (size_t i = 1 ; i < cascadeCount; i++) {
|
||||
splitPercentages[i] = options.cascadeSplitPositions[i - 1];
|
||||
}
|
||||
|
||||
const CascadeSplits::Params p {
|
||||
.proj = cameraInfo.cullingProjection,
|
||||
.near = vsNear,
|
||||
.far = vsFar,
|
||||
.cascadeCount = cascadeCount,
|
||||
.splitPositions = splitPercentages
|
||||
};
|
||||
if (p != mCascadeSplitParams) {
|
||||
mCascadeSplits = CascadeSplits{ p };
|
||||
mCascadeSplitParams = p;
|
||||
}
|
||||
|
||||
const CascadeSplits& splits = mCascadeSplits;
|
||||
|
||||
// The split positions uniform is a float4. To save space, we chop off the first split position
|
||||
// (which is the near plane, and doesn't need to be communicated to the shaders).
|
||||
static_assert(CONFIG_MAX_SHADOW_CASCADES <= 5,
|
||||
"At most, a float4 can fit 4 split positions for 5 shadow cascades");
|
||||
float4 wsSplitPositionUniform{ -std::numeric_limits<float>::infinity() };
|
||||
std::copy(splits.beginWs() + 1, splits.endWs(), &wsSplitPositionUniform[0]);
|
||||
|
||||
float csSplitPosition[CONFIG_MAX_SHADOW_CASCADES + 1];
|
||||
std::copy(splits.beginCs(), splits.endCs(), csSplitPosition);
|
||||
|
||||
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
|
||||
|
||||
ShadowTechnique shadowTechnique{};
|
||||
uint32_t directionalShadowsMask = 0;
|
||||
uint32_t cascadeHasVisibleShadows = 0;
|
||||
for (size_t i = 0, c = mCascadeShadowMaps.size(); i < c; i++) {
|
||||
auto& entry = mCascadeShadowMaps[i];
|
||||
|
||||
// Compute the frustum for the directional light.
|
||||
ShadowMap& shadowMap = entry.getShadowMap();
|
||||
assert_invariant(entry.getLightIndex() == 0);
|
||||
if (hasVisibleShadows) {
|
||||
// Adjust the near and far planes to tightly bound the scene.
|
||||
float vsNear = -cameraInfo.zn;
|
||||
float vsFar = -cameraInfo.zf;
|
||||
if (engine.debug.shadowmap.tightly_bound_scene && !params.options.stable) {
|
||||
vsNear = std::min(vsNear, sceneInfo.vsNearFar.x);
|
||||
vsFar = std::max(vsFar, sceneInfo.vsNearFar.y);
|
||||
}
|
||||
|
||||
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
|
||||
const size_t cascadeCount = mCascadeShadowMaps.size();
|
||||
|
||||
shadowMap.updateDirectional(lightData, 0,
|
||||
cameraInfo, shadowMapInfo,
|
||||
*scene, sceneInfo);
|
||||
// We divide the camera frustum into N cascades. This gives us N + 1 split positions.
|
||||
// The first split position is the near plane; the last split position is the far plane.
|
||||
std::array<float, CascadeSplits::SPLIT_COUNT> splitPercentages{};
|
||||
splitPercentages[cascadeCount] = 1.0f;
|
||||
for (size_t i = 1; i < cascadeCount; i++) {
|
||||
splitPercentages[i] = options.cascadeSplitPositions[i - 1];
|
||||
}
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
mShadowMappingUniforms.lightFromWorldMatrix[i] = shadowMap.getLightSpaceMatrix();
|
||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||
cascadeHasVisibleShadows |= 0x1u << i;
|
||||
const CascadeSplits::Params p{
|
||||
.proj = cameraInfo.cullingProjection,
|
||||
.near = vsNear,
|
||||
.far = vsFar,
|
||||
.cascadeCount = cascadeCount,
|
||||
.splitPositions = splitPercentages
|
||||
};
|
||||
if (p != mCascadeSplitParams) {
|
||||
mCascadeSplits = CascadeSplits{ p };
|
||||
mCascadeSplitParams = p;
|
||||
}
|
||||
|
||||
const CascadeSplits& splits = mCascadeSplits;
|
||||
|
||||
// The split positions uniform is a float4. To save space, we chop off the first split position
|
||||
// (which is the near plane, and doesn't need to be communicated to the shaders).
|
||||
static_assert(CONFIG_MAX_SHADOW_CASCADES <= 5,
|
||||
"At most, a float4 can fit 4 split positions for 5 shadow cascades");
|
||||
float4 wsSplitPositionUniform{ -std::numeric_limits<float>::infinity() };
|
||||
std::copy(splits.beginWs() + 1, splits.endWs(), &wsSplitPositionUniform[0]);
|
||||
|
||||
float csSplitPosition[CONFIG_MAX_SHADOW_CASCADES + 1];
|
||||
std::copy(splits.beginCs(), splits.endCs(), csSplitPosition);
|
||||
|
||||
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
|
||||
|
||||
for (size_t i = 0, c = mCascadeShadowMaps.size(); i < c; i++) {
|
||||
assert_invariant(mCascadeShadowMaps[i]);
|
||||
|
||||
// Compute the frustum for the directional light.
|
||||
ShadowMap& shadowMap = *mCascadeShadowMaps[i];
|
||||
assert_invariant(shadowMap.getLightIndex() == 0);
|
||||
|
||||
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
|
||||
|
||||
auto shaderParameters = shadowMap.updateDirectional(mEngine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
mShadowMappingUniforms.lightFromWorldMatrix[i] = shaderParameters.lightSpace;
|
||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||
cascadeHasVisibleShadows |= 0x1u << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,85 +592,216 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
return shadowTechnique;
|
||||
}
|
||||
|
||||
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine,
|
||||
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept {
|
||||
void ShadowMapManager::updateSpotVisibilityMasks(
|
||||
uint8_t visibleLayers,
|
||||
uint8_t const* UTILS_RESTRICT layers,
|
||||
FRenderableManager::Visibility const* UTILS_RESTRICT visibility,
|
||||
Culler::result_type* UTILS_RESTRICT visibleMask, size_t count) {
|
||||
// __restrict__ seems to only be taken into account as function parameters. This is very
|
||||
// important here, otherwise, this loop doesn't get vectorized.
|
||||
// This is vectorized 16x.
|
||||
count = (count + 0xFu) & ~0xFu; // capacity guaranteed to be multiple of 16
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
const Culler::result_type mask = visibleMask[i];
|
||||
const FRenderableManager::Visibility v = visibility[i];
|
||||
const bool inVisibleLayer = layers[i] & visibleLayers;
|
||||
|
||||
const bool visSpotShadowRenderable = v.castShadows && inVisibleLayer &&
|
||||
(!v.culling || (mask & VISIBLE_DYN_SHADOW_RENDERABLE));
|
||||
|
||||
using Type = Culler::result_type;
|
||||
|
||||
visibleMask[i] &= ~Type(VISIBLE_DYN_SHADOW_RENDERABLE);
|
||||
visibleMask[i] |= Type(visSpotShadowRenderable << VISIBLE_DYN_SHADOW_RENDERABLE_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo const& sceneInfo) noexcept {
|
||||
auto& lcm = engine.getLightManager();
|
||||
|
||||
// shadow-map shadows for point/spotlights
|
||||
ShadowTechnique shadowTechnique{};
|
||||
FScene::ShadowInfo* const shadowInfo = lightData.data<FScene::SHADOW_INFO>();
|
||||
for (size_t i = 0, c = mSpotShadowMaps.size(); i < c; i++) {
|
||||
auto& entry = mSpotShadowMaps[i];
|
||||
const size_t lightIndex = shadowMap.getLightIndex();
|
||||
const FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(lightIndex);
|
||||
FLightManager::ShadowOptions const* const options = shadowMap.getShadowOptions();
|
||||
|
||||
// compute the frustum for this light
|
||||
ShadowMap& shadowMap = entry.getShadowMap();
|
||||
const size_t lightIndex = entry.getLightIndex();
|
||||
const FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(lightIndex);
|
||||
FLightManager::ShadowParams params = lcm.getShadowParams(li);
|
||||
// compute the frustum for this light
|
||||
// for spotlights, we cull shadow casters first because we already know the frustum,
|
||||
// this will help us find better near/far plane later
|
||||
const auto position = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).xyz;
|
||||
const auto direction = lightData.elementAt<FScene::DIRECTION>(lightIndex);
|
||||
const auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).w;
|
||||
const auto outerConeAngle = lcm.getSpotLightOuterCone(li);
|
||||
|
||||
FLightManager::ShadowOptions const* const options = entry.getShadowOptions();
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options->mapSize),
|
||||
.shadowDimension = uint16_t(options->mapSize - 2u),
|
||||
.spotIndex = uint16_t(i),
|
||||
.vsm = view.hasVSM(),
|
||||
.polygonOffset = { // handle reversed Z
|
||||
.slope = view.hasVSM() ? 0.0f : -params.options.polygonOffsetSlope,
|
||||
.constant = view.hasVSM() ? 0.0f : -params.options.polygonOffsetConstant
|
||||
}
|
||||
// compute shadow map frustum for culling
|
||||
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, position);
|
||||
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, 0.01f, radius);
|
||||
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
|
||||
const Frustum frustum(MpMv);
|
||||
|
||||
// Cull shadow casters
|
||||
float3 const* worldAABBCenter = renderableData.data<FScene::WORLD_AABB_CENTER>();
|
||||
float3 const* worldAABBExtent = renderableData.data<FScene::WORLD_AABB_EXTENT>();
|
||||
FScene::VisibleMaskType* visibleArray = renderableData.data<FScene::VISIBLE_MASK>();
|
||||
Culler::intersects(
|
||||
visibleArray + range.first,
|
||||
frustum,
|
||||
worldAABBCenter + range.first,
|
||||
worldAABBExtent + range.first,
|
||||
range.size(),
|
||||
VISIBLE_DYN_SHADOW_RENDERABLE_BIT);
|
||||
|
||||
// update their visibility mask
|
||||
uint8_t const* layers = renderableData.data<FScene::LAYERS>();
|
||||
auto const* visibility = renderableData.data<FScene::VISIBILITY_STATE>();
|
||||
updateSpotVisibilityMasks(
|
||||
view.getVisibleLayers(),
|
||||
layers + range.first,
|
||||
visibility + range.first,
|
||||
visibleArray + range.first,
|
||||
range.size());
|
||||
|
||||
// update the shadow map frustum/camera
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options->mapSize),
|
||||
.shadowDimension = uint16_t(options->mapSize - 2u),
|
||||
.clipSpaceFlipped = engine.getBackend() == Backend::VULKAN,
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL,
|
||||
.vsm = view.hasVSM()
|
||||
};
|
||||
|
||||
auto shaderParameters = shadowMap.updateSpot(mEngine,
|
||||
lightData, lightIndex, mainCameraInfo, shadowMapInfo, *view.getScene(), sceneInfo);
|
||||
|
||||
// and if we need to generate it, update all the UBO data
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||
|
||||
auto& s = mShadowUb.edit();
|
||||
const double n = shadowMap.getCamera().getNear();
|
||||
const double f = shadowMap.getCamera().getCullingFar();
|
||||
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
|
||||
s.shadows[shadowIndex].direction = direction;
|
||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||
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,
|
||||
ShadowMap::SceneInfo const& sceneInfo) noexcept {
|
||||
|
||||
const size_t lightIndex = shadowMap.getLightIndex();
|
||||
FLightManager::ShadowOptions const* const options = shadowMap.getShadowOptions();
|
||||
|
||||
// compute the frustum for this light
|
||||
// for spotlights, we cull shadow casters first because we already know the frustum,
|
||||
// this will help us find better near/far plane later
|
||||
const auto position = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).xyz;
|
||||
const auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).w;
|
||||
|
||||
// compute shadow map frustum for culling
|
||||
const mat4f Mv = ShadowMap::getPointLightViewMatrix(TextureCubemapFace(face), position);
|
||||
const mat4f Mp = mat4f::perspective(90.0f, 1.0f, 0.01f, radius);
|
||||
const Frustum frustum{ math::highPrecisionMultiply(Mp, Mv) };
|
||||
|
||||
// Cull shadow casters
|
||||
float3 const* worldAABBCenter = renderableData.data<FScene::WORLD_AABB_CENTER>();
|
||||
float3 const* worldAABBExtent = renderableData.data<FScene::WORLD_AABB_EXTENT>();
|
||||
FScene::VisibleMaskType* visibleArray = renderableData.data<FScene::VISIBLE_MASK>();
|
||||
Culler::intersects(
|
||||
visibleArray + range.first,
|
||||
frustum,
|
||||
worldAABBCenter + range.first,
|
||||
worldAABBExtent + range.first,
|
||||
range.size(),
|
||||
VISIBLE_DYN_SHADOW_RENDERABLE_BIT);
|
||||
|
||||
// update their visibility mask
|
||||
uint8_t const* layers = renderableData.data<FScene::LAYERS>();
|
||||
auto const* visibility = renderableData.data<FScene::VISIBILITY_STATE>();
|
||||
updateSpotVisibilityMasks(
|
||||
view.getVisibleLayers(),
|
||||
layers + range.first,
|
||||
visibility + range.first,
|
||||
visibleArray + range.first,
|
||||
range.size());
|
||||
|
||||
// update the shadow map frustum/camera
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options->mapSize),
|
||||
.shadowDimension = uint16_t(options->mapSize), // point-lights don't have a border
|
||||
.clipSpaceFlipped = engine.getBackend() == Backend::VULKAN,
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL,
|
||||
.vsm = view.hasVSM()
|
||||
};
|
||||
|
||||
auto shaderParameters = shadowMap.updatePoint(mEngine, lightData, lightIndex,
|
||||
mainCameraInfo, shadowMapInfo, *view.getScene(), sceneInfo, face);
|
||||
|
||||
|
||||
// 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;
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||
|
||||
auto& s = mShadowUb.edit();
|
||||
const double n = shadowMap.getCamera().getNear();
|
||||
const double f = shadowMap.getCamera().getCullingFar();
|
||||
|
||||
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;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = {
|
||||
-((n + f) / (f - n)) * 0.5f + 0.5f,
|
||||
(f * n) / (f - n),
|
||||
-n / (f - n),
|
||||
1.0f / (f - n),
|
||||
};
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||
/ wsTexelSizeAtOneMeter;
|
||||
}
|
||||
}
|
||||
|
||||
// for spotlights, we cull shadow casters first because we already know the frustum,
|
||||
// this will help us find better near/far plane later
|
||||
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine,
|
||||
FScene::LightSoa& lightData) noexcept {
|
||||
|
||||
const auto position = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).xyz;
|
||||
const auto direction = lightData.elementAt<FScene::DIRECTION>(lightIndex);
|
||||
const auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).w;
|
||||
const auto outerConeAngle = lcm.getSpotLightOuterCone(li);
|
||||
FScene::ShadowInfo* const shadowInfo = lightData.data<FScene::SHADOW_INFO>();
|
||||
|
||||
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, position);
|
||||
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f,
|
||||
1.0f, 0.01f, radius);
|
||||
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
|
||||
const Frustum frustum(MpMv);
|
||||
|
||||
// Cull shadow casters
|
||||
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
|
||||
VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(i));
|
||||
|
||||
shadowMap.updateSpot(lightData, lightIndex,
|
||||
cameraInfo, shadowMapInfo,
|
||||
*view.getScene(), sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
shadowInfo[lightIndex].castsShadows = true;
|
||||
shadowInfo[lightIndex].index = i;
|
||||
shadowInfo[lightIndex].layer = entry.getLayer();
|
||||
|
||||
const float wsTexelSizeAtOneMeter = shadowMap.getTexelSizAtOneMeterWs();
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||
|
||||
auto& s = mShadowUb.edit();
|
||||
const double n = shadowMap.getCamera().getNear();
|
||||
const double f = shadowMap.getCamera().getCullingFar();
|
||||
s.shadows[i].lightFromWorldMatrix = shadowMap.getLightSpaceMatrix();
|
||||
s.shadows[i].direction = direction;
|
||||
s.shadows[i].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[i].lightFromWorldZ = shadowMap.getLightFromWorldZ();
|
||||
s.shadows[i].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[i].nearOverFarMinusNear = float(n / (f - n));
|
||||
s.shadows[i].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius / wsTexelSizeAtOneMeter;
|
||||
|
||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||
ShadowTechnique shadowTechnique{};
|
||||
if (!mSpotShadowMaps.empty()) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// screen-space contact shadows for point/spotlights
|
||||
auto& lcm = engine.getLightManager();
|
||||
auto *pLightInstances = lightData.data<FScene::LIGHT_INSTANCE>();
|
||||
for (size_t i = 0, c = lightData.size(); i < c; i++) {
|
||||
// screen-space contact shadows
|
||||
@@ -590,16 +824,23 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
|
||||
// The directional shadow cascades start on layer 0, followed by spotlights.
|
||||
uint8_t layer = 0;
|
||||
uint32_t maxDimension = 0;
|
||||
for (auto& entry : mCascadeShadowMaps) {
|
||||
bool elvsm = false;
|
||||
for (auto* pShadowMap : mCascadeShadowMaps) {
|
||||
// Shadow map size should be the same for all cascades.
|
||||
auto const& options = entry.getShadowOptions();
|
||||
auto const& options = pShadowMap->getShadowOptions();
|
||||
maxDimension = std::max(maxDimension, options->mapSize);
|
||||
entry.setLayer(layer++);
|
||||
elvsm = elvsm || options->vsm.elvsm;
|
||||
pShadowMap->setLayer(layer++);
|
||||
}
|
||||
for (auto& entry : mSpotShadowMaps) {
|
||||
auto const& options = entry.getShadowOptions();
|
||||
for (auto& pShadowMap : mSpotShadowMaps) {
|
||||
auto const& options = pShadowMap->getShadowOptions();
|
||||
maxDimension = std::max(maxDimension, options->mapSize);
|
||||
entry.setLayer(layer++);
|
||||
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;
|
||||
@@ -609,6 +850,23 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
|
||||
const bool useMipmapping = view.hasVSM() &&
|
||||
((vsmShadowOptions.anisotropy > 0) || vsmShadowOptions.mipmapping);
|
||||
|
||||
TextureFormat format = TextureFormat::DEPTH16;
|
||||
if (view.hasVSM()) {
|
||||
if (vsmShadowOptions.highPrecision) {
|
||||
if (elvsm) {
|
||||
format = TextureFormat::RGBA32F;
|
||||
} else {
|
||||
format = TextureFormat::RG32F;
|
||||
}
|
||||
} else {
|
||||
if (elvsm) {
|
||||
format = TextureFormat::RGBA16F;
|
||||
} else {
|
||||
format = TextureFormat::RG16F;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mSoftShadowOptions = view.getSoftShadowOptions();
|
||||
|
||||
uint8_t mipLevels = 1u;
|
||||
@@ -622,7 +880,8 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
|
||||
mTextureAtlasRequirements = {
|
||||
(uint16_t)maxDimension,
|
||||
layersNeeded,
|
||||
mipLevels
|
||||
mipLevels,
|
||||
format
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,7 @@
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
|
||||
#include <private/backend/DriverApi.h>
|
||||
#include "backend/DriverApiForward.h"
|
||||
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
@@ -54,6 +52,7 @@ struct ShadowMappingUniforms {
|
||||
float ssContactShadowDistance;
|
||||
uint32_t directionalShadows;
|
||||
uint32_t cascades;
|
||||
bool elvsm;
|
||||
};
|
||||
|
||||
class ShadowMapManager {
|
||||
@@ -61,6 +60,8 @@ public:
|
||||
|
||||
using ShadowMappingUniforms = ShadowMappingUniforms;
|
||||
|
||||
using ShadowType = ShadowMap::ShadowType;
|
||||
|
||||
enum class ShadowTechnique : uint8_t {
|
||||
NONE = 0x0u,
|
||||
SHADOW_MAP = 0x1u,
|
||||
@@ -76,10 +77,13 @@ public:
|
||||
// Reset shadow map layout.
|
||||
void reset() noexcept;
|
||||
|
||||
void setShadowCascades(size_t lightIndex, LightManager::ShadowOptions const* options) noexcept;
|
||||
void addSpotShadowMap(size_t lightIndex, LightManager::ShadowOptions const* options) noexcept;
|
||||
void setDirectionalShadowMap(size_t lightIndex,
|
||||
LightManager::ShadowOptions const* options) noexcept;
|
||||
|
||||
// Updates all of the shadow maps and performs culling.
|
||||
void addShadowMap(size_t lightIndex, bool spotlight,
|
||||
LightManager::ShadowOptions const* options) noexcept;
|
||||
|
||||
// Updates all the shadow maps and performs culling.
|
||||
// Returns true if any of the shadow maps have visible shadows.
|
||||
ShadowMapManager::ShadowTechnique update(FEngine& engine, FView& view,
|
||||
CameraInfo const& cameraInfo,
|
||||
@@ -87,7 +91,7 @@ public:
|
||||
|
||||
// Renders all the shadow maps.
|
||||
FrameGraphId<FrameGraphTexture> render(FrameGraph& fg, FEngine& engine,
|
||||
RenderPass const& pass, FView& view) noexcept;
|
||||
RenderPass const& pass, FView& view, CameraInfo const& mainCameraInfo) noexcept;
|
||||
|
||||
ShadowMap* getCascadeShadowMap(size_t cascade) noexcept {
|
||||
assert_invariant(cascade < CONFIG_MAX_SHADOW_CASCADES);
|
||||
@@ -98,14 +102,14 @@ public:
|
||||
return const_cast<ShadowMapManager*>(this)->getCascadeShadowMap(cascade);
|
||||
}
|
||||
|
||||
ShadowMap* getSpotShadowMap(size_t spot) noexcept {
|
||||
assert_invariant(spot < CONFIG_MAX_SHADOW_CASTING_SPOTS);
|
||||
ShadowMap* getPointOrSpotShadowMap(size_t index) noexcept {
|
||||
assert_invariant(index < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
|
||||
return std::launder(reinterpret_cast<ShadowMap*>(
|
||||
&mShadowMapCache[CONFIG_MAX_SHADOW_CASCADES + spot]));
|
||||
&mShadowMapCache[CONFIG_MAX_SHADOW_CASCADES + index]));
|
||||
}
|
||||
|
||||
ShadowMap const* getSpotShadowMap(size_t spot) const noexcept {
|
||||
return const_cast<ShadowMapManager*>(this)->getSpotShadowMap(spot);
|
||||
ShadowMap const* getPointOrSpotShadowMap(size_t spot) const noexcept {
|
||||
return const_cast<ShadowMapManager*>(this)->getPointOrSpotShadowMap(spot);
|
||||
}
|
||||
|
||||
// valid after calling update() above
|
||||
@@ -113,46 +117,37 @@ public:
|
||||
return mShadowMappingUniforms;
|
||||
}
|
||||
|
||||
auto& getShadowUniforms() const { return mShadowUb; }
|
||||
|
||||
auto& getShadowUniformsHandle() const { return mShadowUbh; }
|
||||
|
||||
bool hasSpotShadows() const { return !mSpotShadowMaps.empty(); }
|
||||
|
||||
private:
|
||||
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
|
||||
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine,
|
||||
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept;
|
||||
FScene::LightSoa& lightData) noexcept;
|
||||
|
||||
void calculateTextureRequirements(FEngine& engine, FView& view, FScene::LightSoa& lightData) noexcept;
|
||||
void calculateTextureRequirements(FEngine& engine, FView& view,
|
||||
FScene::LightSoa& lightData) noexcept;
|
||||
|
||||
class ShadowMapEntry {
|
||||
public:
|
||||
ShadowMapEntry() = default;
|
||||
ShadowMapEntry(ShadowMap* shadowMap, size_t light,
|
||||
LightManager::ShadowOptions const* options) :
|
||||
mShadowMap(shadowMap), mOptions(options), mLightIndex(light) {
|
||||
}
|
||||
void prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo const& sceneInfo) noexcept;
|
||||
|
||||
explicit operator bool() const { return mShadowMap != nullptr; }
|
||||
void preparePointShadowMap(ShadowMap& map,
|
||||
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
|
||||
FScene::LightSoa& lightData, uint8_t face,
|
||||
ShadowMap::SceneInfo const& sceneInfo) noexcept;
|
||||
|
||||
void setLayer(uint8_t layer) noexcept { mLayer = layer; }
|
||||
uint8_t getLayer() const noexcept { return mLayer; }
|
||||
|
||||
LightManager::ShadowOptions const* getShadowOptions() const noexcept { return mOptions; }
|
||||
ShadowMap& getShadowMap() const { return *mShadowMap; }
|
||||
size_t getLightIndex() const { return mLightIndex; }
|
||||
|
||||
bool hasVisibleShadows() const { return mShadowMap->hasVisibleShadows(); }
|
||||
|
||||
private:
|
||||
ShadowMap* mShadowMap = nullptr;
|
||||
LightManager::ShadowOptions const* mOptions = nullptr;
|
||||
uint32_t mLightIndex = 0;
|
||||
uint8_t mLayer = 0;
|
||||
};
|
||||
static void updateSpotVisibilityMasks(
|
||||
uint8_t visibleLayers,
|
||||
uint8_t const* UTILS_RESTRICT layers,
|
||||
FRenderableManager::Visibility const* UTILS_RESTRICT visibility,
|
||||
Culler::result_type* UTILS_RESTRICT visibleMask, size_t count);
|
||||
|
||||
class CascadeSplits {
|
||||
public:
|
||||
@@ -191,12 +186,15 @@ private:
|
||||
size_t mSplitCount;
|
||||
};
|
||||
|
||||
FEngine& mEngine;
|
||||
|
||||
// Atlas requirements, updated in ShadowMapManager::update(),
|
||||
// consumed in ShadowMapManager::render()
|
||||
struct TextureAtlasRequirements {
|
||||
uint16_t size = 0;
|
||||
uint8_t layers = 0;
|
||||
uint8_t levels = 0;
|
||||
backend::TextureFormat format = backend::TextureFormat::DEPTH16;
|
||||
} mTextureAtlasRequirements;
|
||||
|
||||
SoftShadowOptions mSoftShadowOptions;
|
||||
@@ -204,30 +202,26 @@ private:
|
||||
CascadeSplits::Params mCascadeSplitParams;
|
||||
CascadeSplits mCascadeSplits;
|
||||
|
||||
// 16-bits seems enough.
|
||||
// TODO: make it an option.
|
||||
// TODO: iOS does not support the DEPTH16 texture format.
|
||||
backend::TextureFormat mTextureFormat = backend::TextureFormat::DEPTH16;
|
||||
|
||||
mutable TypedUniformBuffer<ShadowUib> mShadowUb;
|
||||
backend::Handle<backend::HwBufferObject> mShadowUbh;
|
||||
|
||||
ShadowMappingUniforms mShadowMappingUniforms;
|
||||
|
||||
utils::FixedCapacityVector<ShadowMapEntry> mCascadeShadowMaps{
|
||||
utils::FixedCapacityVector<ShadowMapEntry>::with_capacity(
|
||||
ShadowMap::SceneInfo mSceneInfo;
|
||||
|
||||
utils::FixedCapacityVector<ShadowMap*> mCascadeShadowMaps{
|
||||
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
|
||||
CONFIG_MAX_SHADOW_CASCADES) };
|
||||
|
||||
utils::FixedCapacityVector<ShadowMapEntry> mSpotShadowMaps{
|
||||
utils::FixedCapacityVector<ShadowMapEntry>::with_capacity(
|
||||
CONFIG_MAX_SHADOW_CASTING_SPOTS) };
|
||||
utils::FixedCapacityVector<ShadowMap*> mSpotShadowMaps{
|
||||
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
|
||||
CONFIG_MAX_SHADOWMAP_PUNCTUAL) };
|
||||
|
||||
// inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly.
|
||||
// because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations.
|
||||
// Each ShadowMap is currently 128 bytes.
|
||||
// Each ShadowMap is currently 32 bytes.
|
||||
using ShadowMapStorage = std::aligned_storage<sizeof(ShadowMap), alignof(ShadowMap)>::type;
|
||||
std::array<ShadowMapStorage,
|
||||
CONFIG_MAX_SHADOW_CASCADES + CONFIG_MAX_SHADOW_CASTING_SPOTS> mShadowMapCache;
|
||||
std::array<ShadowMapStorage, CONFIG_MAX_SHADOW_LAYERS> mShadowMapCache;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -25,16 +25,16 @@ using namespace math;
|
||||
|
||||
void SkinningBuffer::setBones(Engine& engine,
|
||||
RenderableManager::Bone const* transforms, size_t count, size_t offset) {
|
||||
upcast(this)->setBones(upcast(engine), transforms, count, offset);
|
||||
downcast(this)->setBones(downcast(engine), transforms, count, offset);
|
||||
}
|
||||
|
||||
void SkinningBuffer::setBones(Engine& engine,
|
||||
math::mat4f const* transforms, size_t count, size_t offset) {
|
||||
upcast(this)->setBones(upcast(engine), transforms, count, offset);
|
||||
downcast(this)->setBones(downcast(engine), transforms, count, offset);
|
||||
}
|
||||
|
||||
size_t SkinningBuffer::getBoneCount() const noexcept {
|
||||
return upcast(this)->getBoneCount();
|
||||
return downcast(this)->getBoneCount();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -21,23 +21,23 @@
|
||||
namespace filament {
|
||||
|
||||
void Skybox::setLayerMask(uint8_t select, uint8_t values) noexcept {
|
||||
upcast(this)->setLayerMask(select, values);
|
||||
downcast(this)->setLayerMask(select, values);
|
||||
}
|
||||
|
||||
uint8_t Skybox::getLayerMask() const noexcept {
|
||||
return upcast(this)->getLayerMask();
|
||||
return downcast(this)->getLayerMask();
|
||||
}
|
||||
|
||||
float Skybox::getIntensity() const noexcept {
|
||||
return upcast(this)->getIntensity();
|
||||
return downcast(this)->getIntensity();
|
||||
}
|
||||
|
||||
void Skybox::setColor(math::float4 color) noexcept {
|
||||
upcast(this)->setColor(color);
|
||||
downcast(this)->setColor(color);
|
||||
}
|
||||
|
||||
Texture const* Skybox::getTexture() const noexcept {
|
||||
return upcast(this)->getTexture();
|
||||
return downcast(this)->getTexture();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -21,23 +21,23 @@ namespace filament {
|
||||
using namespace backend;
|
||||
|
||||
StreamType Stream::getStreamType() const noexcept {
|
||||
return upcast(this)->getStreamType();
|
||||
return downcast(this)->getStreamType();
|
||||
}
|
||||
|
||||
void Stream::setAcquiredImage(void* image, Callback callback, void* userdata) noexcept {
|
||||
upcast(this)->setAcquiredImage(image, callback, userdata);
|
||||
downcast(this)->setAcquiredImage(image, callback, userdata);
|
||||
}
|
||||
|
||||
void Stream::setAcquiredImage(void* image, backend::CallbackHandler* handler, Callback callback, void* userdata) noexcept {
|
||||
upcast(this)->setAcquiredImage(image, handler, callback, userdata);
|
||||
downcast(this)->setAcquiredImage(image, handler, callback, userdata);
|
||||
}
|
||||
|
||||
void Stream::setDimensions(uint32_t width, uint32_t height) noexcept {
|
||||
upcast(this)->setDimensions(width, height);
|
||||
downcast(this)->setDimensions(width, height);
|
||||
}
|
||||
|
||||
int64_t Stream::getTimestamp() const noexcept {
|
||||
return upcast(this)->getTimestamp();
|
||||
return downcast(this)->getTimestamp();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -19,15 +19,15 @@
|
||||
namespace filament {
|
||||
|
||||
void* SwapChain::getNativeWindow() const noexcept {
|
||||
return upcast(this)->getNativeWindow();
|
||||
return downcast(this)->getNativeWindow();
|
||||
}
|
||||
|
||||
void SwapChain::setFrameScheduledCallback(FrameScheduledCallback callback, void* user) {
|
||||
return upcast(this)->setFrameScheduledCallback(callback, user);
|
||||
return downcast(this)->setFrameScheduledCallback(callback, user);
|
||||
}
|
||||
|
||||
void SwapChain::setFrameCompletedCallback(FrameCompletedCallback callback, void* user) {
|
||||
return upcast(this)->setFrameCompletedCallback(callback, user);
|
||||
return downcast(this)->setFrameCompletedCallback(callback, user);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+15
-15
@@ -22,64 +22,64 @@
|
||||
namespace filament {
|
||||
|
||||
size_t Texture::getWidth(size_t level) const noexcept {
|
||||
return upcast(this)->getWidth(level);
|
||||
return downcast(this)->getWidth(level);
|
||||
}
|
||||
|
||||
size_t Texture::getHeight(size_t level) const noexcept {
|
||||
return upcast(this)->getHeight(level);
|
||||
return downcast(this)->getHeight(level);
|
||||
}
|
||||
|
||||
size_t Texture::getDepth(size_t level) const noexcept {
|
||||
return upcast(this)->getDepth(level);
|
||||
return downcast(this)->getDepth(level);
|
||||
}
|
||||
|
||||
size_t Texture::getLevels() const noexcept {
|
||||
return upcast(this)->getLevelCount();
|
||||
return downcast(this)->getLevelCount();
|
||||
}
|
||||
|
||||
Texture::Sampler Texture::getTarget() const noexcept {
|
||||
return upcast(this)->getTarget();
|
||||
return downcast(this)->getTarget();
|
||||
}
|
||||
|
||||
Texture::InternalFormat Texture::getFormat() const noexcept {
|
||||
return upcast(this)->getFormat();
|
||||
return downcast(this)->getFormat();
|
||||
}
|
||||
|
||||
void Texture::setImage(Engine& engine, size_t level,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
PixelBufferDescriptor&& buffer) const {
|
||||
upcast(this)->setImage(upcast(engine),
|
||||
downcast(this)->setImage(downcast(engine),
|
||||
level, xoffset, yoffset, zoffset, width, height, depth, std::move(buffer));
|
||||
}
|
||||
|
||||
void Texture::setImage(Engine& engine, size_t level,
|
||||
Texture::PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const {
|
||||
upcast(this)->setImage(upcast(engine), level, std::move(buffer), faceOffsets);
|
||||
downcast(this)->setImage(downcast(engine), level, std::move(buffer), faceOffsets);
|
||||
}
|
||||
|
||||
void Texture::setExternalImage(Engine& engine, void* image) noexcept {
|
||||
upcast(this)->setExternalImage(upcast(engine), image);
|
||||
downcast(this)->setExternalImage(downcast(engine), image);
|
||||
}
|
||||
|
||||
void Texture::setExternalImage(Engine& engine, void* image, size_t plane) noexcept {
|
||||
upcast(this)->setExternalImage(upcast(engine), image, plane);
|
||||
downcast(this)->setExternalImage(downcast(engine), image, plane);
|
||||
}
|
||||
|
||||
void Texture::setExternalStream(Engine& engine, Stream* stream) noexcept {
|
||||
upcast(this)->setExternalStream(upcast(engine), upcast(stream));
|
||||
downcast(this)->setExternalStream(downcast(engine), downcast(stream));
|
||||
}
|
||||
|
||||
void Texture::generateMipmaps(Engine& engine) const noexcept {
|
||||
upcast(this)->generateMipmaps(upcast(engine));
|
||||
downcast(this)->generateMipmaps(downcast(engine));
|
||||
}
|
||||
|
||||
bool Texture::isTextureFormatSupported(Engine& engine, InternalFormat format) noexcept {
|
||||
return FTexture::isTextureFormatSupported(upcast(engine), format);
|
||||
return FTexture::isTextureFormatSupported(downcast(engine), format);
|
||||
}
|
||||
|
||||
bool Texture::isTextureSwizzleSupported(Engine& engine) noexcept {
|
||||
return FTexture::isTextureSwizzleSupported(upcast(engine));
|
||||
return FTexture::isTextureSwizzleSupported(downcast(engine));
|
||||
}
|
||||
|
||||
size_t Texture::computeTextureDataSize(Texture::Format format, Texture::Type type, size_t stride,
|
||||
@@ -89,7 +89,7 @@ size_t Texture::computeTextureDataSize(Texture::Format format, Texture::Type typ
|
||||
|
||||
void Texture::generatePrefilterMipmap(Engine& engine, Texture::PixelBufferDescriptor&& buffer,
|
||||
const Texture::FaceOffsets& faceOffsets, PrefilterOptions const* options) {
|
||||
upcast(this)->generatePrefilterMipmap(upcast(engine), std::move(buffer), faceOffsets, options);
|
||||
downcast(this)->generatePrefilterMipmap(downcast(engine), std::move(buffer), faceOffsets, options);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -23,94 +23,94 @@ namespace filament {
|
||||
using namespace math;
|
||||
|
||||
void TransformManager::create(Entity entity, Instance parent, const mat4f& worldTransform) {
|
||||
upcast(this)->create(entity, parent, worldTransform);
|
||||
downcast(this)->create(entity, parent, worldTransform);
|
||||
}
|
||||
|
||||
void TransformManager::create(Entity entity, Instance parent, const mat4& worldTransform) {
|
||||
upcast(this)->create(entity, parent, worldTransform);
|
||||
downcast(this)->create(entity, parent, worldTransform);
|
||||
}
|
||||
|
||||
void TransformManager::create(Entity entity, Instance parent) {
|
||||
upcast(this)->create(entity, parent, mat4f{});
|
||||
downcast(this)->create(entity, parent, mat4f{});
|
||||
}
|
||||
|
||||
void TransformManager::destroy(Entity e) noexcept {
|
||||
upcast(this)->destroy(e);
|
||||
downcast(this)->destroy(e);
|
||||
}
|
||||
|
||||
bool TransformManager::hasComponent(Entity e) const noexcept {
|
||||
return upcast(this)->hasComponent(e);
|
||||
return downcast(this)->hasComponent(e);
|
||||
}
|
||||
|
||||
TransformManager::Instance TransformManager::getInstance(Entity e) const noexcept {
|
||||
return upcast(this)->getInstance(e);
|
||||
return downcast(this)->getInstance(e);
|
||||
}
|
||||
|
||||
void TransformManager::setTransform(Instance ci, const mat4f& model) noexcept {
|
||||
upcast(this)->setTransform(ci, model);
|
||||
downcast(this)->setTransform(ci, model);
|
||||
}
|
||||
|
||||
void TransformManager::setTransform(Instance ci, const mat4& model) noexcept {
|
||||
upcast(this)->setTransform(ci, model);
|
||||
downcast(this)->setTransform(ci, model);
|
||||
}
|
||||
|
||||
const mat4f& TransformManager::getTransform(Instance ci) const noexcept {
|
||||
return upcast(this)->getTransform(ci);
|
||||
return downcast(this)->getTransform(ci);
|
||||
}
|
||||
|
||||
const mat4 TransformManager::getTransformAccurate(Instance ci) const noexcept {
|
||||
return upcast(this)->getTransformAccurate(ci);
|
||||
return downcast(this)->getTransformAccurate(ci);
|
||||
}
|
||||
|
||||
const mat4f& TransformManager::getWorldTransform(Instance ci) const noexcept {
|
||||
return upcast(this)->getWorldTransform(ci);
|
||||
return downcast(this)->getWorldTransform(ci);
|
||||
}
|
||||
|
||||
const mat4 TransformManager::getWorldTransformAccurate(Instance ci) const noexcept {
|
||||
return upcast(this)->getWorldTransformAccurate(ci);
|
||||
return downcast(this)->getWorldTransformAccurate(ci);
|
||||
}
|
||||
|
||||
void TransformManager::setParent(Instance i, Instance newParent) noexcept {
|
||||
upcast(this)->setParent(i, newParent);
|
||||
downcast(this)->setParent(i, newParent);
|
||||
}
|
||||
|
||||
utils::Entity TransformManager::getParent(Instance i) const noexcept {
|
||||
return upcast(this)->getParent(i);
|
||||
return downcast(this)->getParent(i);
|
||||
}
|
||||
|
||||
size_t TransformManager::getChildCount(Instance i) const noexcept {
|
||||
return upcast(this)->getChildCount(i);
|
||||
return downcast(this)->getChildCount(i);
|
||||
}
|
||||
|
||||
size_t TransformManager::getChildren(Instance i, utils::Entity* children,
|
||||
size_t count) const noexcept {
|
||||
return upcast(this)->getChildren(i, children, count);
|
||||
return downcast(this)->getChildren(i, children, count);
|
||||
}
|
||||
|
||||
void TransformManager::openLocalTransformTransaction() noexcept {
|
||||
upcast(this)->openLocalTransformTransaction();
|
||||
downcast(this)->openLocalTransformTransaction();
|
||||
}
|
||||
|
||||
void TransformManager::commitLocalTransformTransaction() noexcept {
|
||||
upcast(this)->commitLocalTransformTransaction();
|
||||
downcast(this)->commitLocalTransformTransaction();
|
||||
}
|
||||
|
||||
TransformManager::children_iterator TransformManager::getChildrenBegin(
|
||||
TransformManager::Instance parent) const noexcept {
|
||||
return upcast(this)->getChildrenBegin(parent);
|
||||
return downcast(this)->getChildrenBegin(parent);
|
||||
}
|
||||
|
||||
TransformManager::children_iterator TransformManager::getChildrenEnd(
|
||||
TransformManager::Instance parent) const noexcept {
|
||||
return upcast(this)->getChildrenEnd(parent);
|
||||
return downcast(this)->getChildrenEnd(parent);
|
||||
}
|
||||
|
||||
void TransformManager::setAccurateTranslationsEnabled(bool enable) noexcept {
|
||||
upcast(this)->setAccurateTranslationsEnabled(enable);
|
||||
downcast(this)->setAccurateTranslationsEnabled(enable);
|
||||
}
|
||||
|
||||
bool TransformManager::isAccurateTranslationsEnabled() const noexcept {
|
||||
return upcast(this)->isAccurateTranslationsEnabled();;
|
||||
return downcast(this)->isAccurateTranslationsEnabled();;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -21,17 +21,17 @@
|
||||
namespace filament {
|
||||
|
||||
size_t VertexBuffer::getVertexCount() const noexcept {
|
||||
return upcast(this)->getVertexCount();
|
||||
return downcast(this)->getVertexCount();
|
||||
}
|
||||
|
||||
void VertexBuffer::setBufferAt(Engine& engine, uint8_t bufferIndex,
|
||||
backend::BufferDescriptor&& buffer, uint32_t byteOffset) {
|
||||
upcast(this)->setBufferAt(upcast(engine), bufferIndex, std::move(buffer), byteOffset);
|
||||
downcast(this)->setBufferAt(downcast(engine), bufferIndex, std::move(buffer), byteOffset);
|
||||
}
|
||||
|
||||
void VertexBuffer::setBufferObjectAt(Engine& engine, uint8_t bufferIndex,
|
||||
BufferObject const* bufferObject) {
|
||||
upcast(this)->setBufferObjectAt(upcast(engine), bufferIndex, upcast(bufferObject));
|
||||
downcast(this)->setBufferObjectAt(downcast(engine), bufferIndex, downcast(bufferObject));
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+67
-67
@@ -19,273 +19,273 @@
|
||||
namespace filament {
|
||||
|
||||
void View::setScene(Scene* scene) {
|
||||
return upcast(this)->setScene(upcast(scene));
|
||||
return downcast(this)->setScene(downcast(scene));
|
||||
}
|
||||
|
||||
Scene* View::getScene() noexcept {
|
||||
return upcast(this)->getScene();
|
||||
return downcast(this)->getScene();
|
||||
}
|
||||
|
||||
|
||||
void View::setCamera(Camera* camera) noexcept {
|
||||
upcast(this)->setCameraUser(upcast(camera));
|
||||
downcast(this)->setCameraUser(downcast(camera));
|
||||
}
|
||||
|
||||
Camera& View::getCamera() noexcept {
|
||||
return upcast(this)->getCameraUser();
|
||||
return downcast(this)->getCameraUser();
|
||||
}
|
||||
|
||||
void View::setViewport(filament::Viewport const& viewport) noexcept {
|
||||
upcast(this)->setViewport(viewport);
|
||||
downcast(this)->setViewport(viewport);
|
||||
}
|
||||
|
||||
filament::Viewport const& View::getViewport() const noexcept {
|
||||
return upcast(this)->getViewport();
|
||||
return downcast(this)->getViewport();
|
||||
}
|
||||
|
||||
void View::setFrustumCullingEnabled(bool culling) noexcept {
|
||||
upcast(this)->setFrustumCullingEnabled(culling);
|
||||
downcast(this)->setFrustumCullingEnabled(culling);
|
||||
}
|
||||
|
||||
bool View::isFrustumCullingEnabled() const noexcept {
|
||||
return upcast(this)->isFrustumCullingEnabled();
|
||||
return downcast(this)->isFrustumCullingEnabled();
|
||||
}
|
||||
|
||||
void View::setDebugCamera(Camera* camera) noexcept {
|
||||
upcast(this)->setViewingCamera(upcast(camera));
|
||||
downcast(this)->setViewingCamera(downcast(camera));
|
||||
}
|
||||
|
||||
void View::setVisibleLayers(uint8_t select, uint8_t values) noexcept {
|
||||
upcast(this)->setVisibleLayers(select, values);
|
||||
downcast(this)->setVisibleLayers(select, values);
|
||||
}
|
||||
|
||||
void View::setName(const char* name) noexcept {
|
||||
upcast(this)->setName(name);
|
||||
downcast(this)->setName(name);
|
||||
}
|
||||
|
||||
const char* View::getName() const noexcept {
|
||||
return upcast(this)->getName();
|
||||
return downcast(this)->getName();
|
||||
}
|
||||
|
||||
Camera const* View::getDirectionalLightCamera() const noexcept {
|
||||
return upcast(this)->getDirectionalLightCamera();
|
||||
return downcast(this)->getDirectionalLightCamera();
|
||||
}
|
||||
|
||||
void View::setShadowingEnabled(bool enabled) noexcept {
|
||||
upcast(this)->setShadowingEnabled(enabled);
|
||||
downcast(this)->setShadowingEnabled(enabled);
|
||||
}
|
||||
|
||||
void View::setRenderTarget(RenderTarget* renderTarget) noexcept {
|
||||
upcast(this)->setRenderTarget(upcast(renderTarget));
|
||||
downcast(this)->setRenderTarget(downcast(renderTarget));
|
||||
}
|
||||
|
||||
RenderTarget* View::getRenderTarget() const noexcept {
|
||||
return upcast(this)->getRenderTarget();
|
||||
return downcast(this)->getRenderTarget();
|
||||
}
|
||||
|
||||
void View::setSampleCount(uint8_t count) noexcept {
|
||||
upcast(this)->setSampleCount(count);
|
||||
downcast(this)->setSampleCount(count);
|
||||
}
|
||||
|
||||
uint8_t View::getSampleCount() const noexcept {
|
||||
return upcast(this)->getSampleCount();
|
||||
return downcast(this)->getSampleCount();
|
||||
}
|
||||
|
||||
void View::setAntiAliasing(AntiAliasing type) noexcept {
|
||||
upcast(this)->setAntiAliasing(type);
|
||||
downcast(this)->setAntiAliasing(type);
|
||||
}
|
||||
|
||||
View::AntiAliasing View::getAntiAliasing() const noexcept {
|
||||
return upcast(this)->getAntiAliasing();
|
||||
return downcast(this)->getAntiAliasing();
|
||||
}
|
||||
|
||||
void View::setTemporalAntiAliasingOptions(TemporalAntiAliasingOptions options) noexcept {
|
||||
upcast(this)->setTemporalAntiAliasingOptions(options);
|
||||
downcast(this)->setTemporalAntiAliasingOptions(options);
|
||||
}
|
||||
|
||||
const View::TemporalAntiAliasingOptions& View::getTemporalAntiAliasingOptions() const noexcept {
|
||||
return upcast(this)->getTemporalAntiAliasingOptions();
|
||||
return downcast(this)->getTemporalAntiAliasingOptions();
|
||||
}
|
||||
|
||||
void View::setMultiSampleAntiAliasingOptions(MultiSampleAntiAliasingOptions options) noexcept {
|
||||
upcast(this)->setMultiSampleAntiAliasingOptions(options);
|
||||
downcast(this)->setMultiSampleAntiAliasingOptions(options);
|
||||
}
|
||||
|
||||
const View::MultiSampleAntiAliasingOptions& View::getMultiSampleAntiAliasingOptions() const noexcept {
|
||||
return upcast(this)->getMultiSampleAntiAliasingOptions();
|
||||
return downcast(this)->getMultiSampleAntiAliasingOptions();
|
||||
}
|
||||
|
||||
void View::setScreenSpaceReflectionsOptions(ScreenSpaceReflectionsOptions options) noexcept {
|
||||
upcast(this)->setScreenSpaceReflectionsOptions(options);
|
||||
downcast(this)->setScreenSpaceReflectionsOptions(options);
|
||||
}
|
||||
|
||||
const View::ScreenSpaceReflectionsOptions& View::getScreenSpaceReflectionsOptions() const noexcept {
|
||||
return upcast(this)->getScreenSpaceReflectionsOptions();
|
||||
return downcast(this)->getScreenSpaceReflectionsOptions();
|
||||
}
|
||||
|
||||
void View::setGuardBandOptions(GuardBandOptions options) noexcept {
|
||||
upcast(this)->setGuardBandOptions(options);
|
||||
downcast(this)->setGuardBandOptions(options);
|
||||
}
|
||||
|
||||
GuardBandOptions const& View::getGuardBandOptions() const noexcept {
|
||||
return upcast(this)->getGuardBandOptions();
|
||||
return downcast(this)->getGuardBandOptions();
|
||||
}
|
||||
|
||||
void View::setColorGrading(ColorGrading* colorGrading) noexcept {
|
||||
return upcast(this)->setColorGrading(upcast(colorGrading));
|
||||
return downcast(this)->setColorGrading(downcast(colorGrading));
|
||||
}
|
||||
|
||||
const ColorGrading* View::getColorGrading() const noexcept {
|
||||
return upcast(this)->getColorGrading();
|
||||
return downcast(this)->getColorGrading();
|
||||
}
|
||||
|
||||
void View::setDithering(Dithering dithering) noexcept {
|
||||
upcast(this)->setDithering(dithering);
|
||||
downcast(this)->setDithering(dithering);
|
||||
}
|
||||
|
||||
View::Dithering View::getDithering() const noexcept {
|
||||
return upcast(this)->getDithering();
|
||||
return downcast(this)->getDithering();
|
||||
}
|
||||
|
||||
void View::setDynamicResolutionOptions(const DynamicResolutionOptions& options) noexcept {
|
||||
upcast(this)->setDynamicResolutionOptions(options);
|
||||
downcast(this)->setDynamicResolutionOptions(options);
|
||||
}
|
||||
|
||||
View::DynamicResolutionOptions View::getDynamicResolutionOptions() const noexcept {
|
||||
return upcast(this)->getDynamicResolutionOptions();
|
||||
return downcast(this)->getDynamicResolutionOptions();
|
||||
}
|
||||
|
||||
void View::setRenderQuality(const RenderQuality& renderQuality) noexcept {
|
||||
upcast(this)->setRenderQuality(renderQuality);
|
||||
downcast(this)->setRenderQuality(renderQuality);
|
||||
}
|
||||
|
||||
View::RenderQuality View::getRenderQuality() const noexcept {
|
||||
return upcast(this)->getRenderQuality();
|
||||
return downcast(this)->getRenderQuality();
|
||||
}
|
||||
|
||||
void View::setPostProcessingEnabled(bool enabled) noexcept {
|
||||
upcast(this)->setPostProcessingEnabled(enabled);
|
||||
downcast(this)->setPostProcessingEnabled(enabled);
|
||||
}
|
||||
|
||||
bool View::isPostProcessingEnabled() const noexcept {
|
||||
return upcast(this)->hasPostProcessPass();
|
||||
return downcast(this)->hasPostProcessPass();
|
||||
}
|
||||
|
||||
void View::setFrontFaceWindingInverted(bool inverted) noexcept {
|
||||
upcast(this)->setFrontFaceWindingInverted(inverted);
|
||||
downcast(this)->setFrontFaceWindingInverted(inverted);
|
||||
}
|
||||
|
||||
bool View::isFrontFaceWindingInverted() const noexcept {
|
||||
return upcast(this)->isFrontFaceWindingInverted();
|
||||
return downcast(this)->isFrontFaceWindingInverted();
|
||||
}
|
||||
|
||||
void View::setDynamicLightingOptions(float zLightNear, float zLightFar) noexcept {
|
||||
upcast(this)->setDynamicLightingOptions(zLightNear, zLightFar);
|
||||
downcast(this)->setDynamicLightingOptions(zLightNear, zLightFar);
|
||||
}
|
||||
|
||||
void View::setShadowType(View::ShadowType shadow) noexcept {
|
||||
upcast(this)->setShadowType(shadow);
|
||||
downcast(this)->setShadowType(shadow);
|
||||
}
|
||||
|
||||
void View::setVsmShadowOptions(VsmShadowOptions const& options) noexcept {
|
||||
upcast(this)->setVsmShadowOptions(options);
|
||||
downcast(this)->setVsmShadowOptions(options);
|
||||
}
|
||||
|
||||
View::VsmShadowOptions View::getVsmShadowOptions() const noexcept {
|
||||
return upcast(this)->getVsmShadowOptions();
|
||||
return downcast(this)->getVsmShadowOptions();
|
||||
}
|
||||
|
||||
void View::setSoftShadowOptions(SoftShadowOptions const& options) noexcept {
|
||||
upcast(this)->setSoftShadowOptions(options);
|
||||
downcast(this)->setSoftShadowOptions(options);
|
||||
}
|
||||
|
||||
SoftShadowOptions View::getSoftShadowOptions() const noexcept {
|
||||
return upcast(this)->getSoftShadowOptions();
|
||||
return downcast(this)->getSoftShadowOptions();
|
||||
}
|
||||
|
||||
void View::setAmbientOcclusion(View::AmbientOcclusion ambientOcclusion) noexcept {
|
||||
upcast(this)->setAmbientOcclusion(ambientOcclusion);
|
||||
downcast(this)->setAmbientOcclusion(ambientOcclusion);
|
||||
}
|
||||
|
||||
View::AmbientOcclusion View::getAmbientOcclusion() const noexcept {
|
||||
return upcast(this)->getAmbientOcclusion();
|
||||
return downcast(this)->getAmbientOcclusion();
|
||||
}
|
||||
|
||||
void View::setAmbientOcclusionOptions(View::AmbientOcclusionOptions const& options) noexcept {
|
||||
upcast(this)->setAmbientOcclusionOptions(options);
|
||||
downcast(this)->setAmbientOcclusionOptions(options);
|
||||
}
|
||||
|
||||
View::AmbientOcclusionOptions const& View::getAmbientOcclusionOptions() const noexcept {
|
||||
return upcast(this)->getAmbientOcclusionOptions();
|
||||
return downcast(this)->getAmbientOcclusionOptions();
|
||||
}
|
||||
|
||||
void View::setBloomOptions(View::BloomOptions options) noexcept {
|
||||
upcast(this)->setBloomOptions(options);
|
||||
downcast(this)->setBloomOptions(options);
|
||||
}
|
||||
|
||||
View::BloomOptions View::getBloomOptions() const noexcept {
|
||||
return upcast(this)->getBloomOptions();
|
||||
return downcast(this)->getBloomOptions();
|
||||
}
|
||||
|
||||
void View::setFogOptions(View::FogOptions options) noexcept {
|
||||
upcast(this)->setFogOptions(options);
|
||||
downcast(this)->setFogOptions(options);
|
||||
}
|
||||
|
||||
View::FogOptions View::getFogOptions() const noexcept {
|
||||
return upcast(this)->getFogOptions();
|
||||
return downcast(this)->getFogOptions();
|
||||
}
|
||||
|
||||
void View::setDepthOfFieldOptions(DepthOfFieldOptions options) noexcept {
|
||||
upcast(this)->setDepthOfFieldOptions(options);
|
||||
downcast(this)->setDepthOfFieldOptions(options);
|
||||
}
|
||||
|
||||
View::DepthOfFieldOptions View::getDepthOfFieldOptions() const noexcept {
|
||||
return upcast(this)->getDepthOfFieldOptions();
|
||||
return downcast(this)->getDepthOfFieldOptions();
|
||||
}
|
||||
|
||||
void View::setVignetteOptions(View::VignetteOptions options) noexcept {
|
||||
upcast(this)->setVignetteOptions(options);
|
||||
downcast(this)->setVignetteOptions(options);
|
||||
}
|
||||
|
||||
View::VignetteOptions View::getVignetteOptions() const noexcept {
|
||||
return upcast(this)->getVignetteOptions();
|
||||
return downcast(this)->getVignetteOptions();
|
||||
}
|
||||
|
||||
void View::setBlendMode(BlendMode blendMode) noexcept {
|
||||
upcast(this)->setBlendMode(blendMode);
|
||||
downcast(this)->setBlendMode(blendMode);
|
||||
}
|
||||
|
||||
View::BlendMode View::getBlendMode() const noexcept {
|
||||
return upcast(this)->getBlendMode();
|
||||
return downcast(this)->getBlendMode();
|
||||
}
|
||||
|
||||
uint8_t View::getVisibleLayers() const noexcept {
|
||||
return upcast(this)->getVisibleLayers();
|
||||
return downcast(this)->getVisibleLayers();
|
||||
}
|
||||
|
||||
bool View::isShadowingEnabled() const noexcept {
|
||||
return upcast(this)->isShadowingEnabled();
|
||||
return downcast(this)->isShadowingEnabled();
|
||||
}
|
||||
|
||||
void View::setScreenSpaceRefractionEnabled(bool enabled) noexcept {
|
||||
upcast(this)->setScreenSpaceRefractionEnabled(enabled);
|
||||
downcast(this)->setScreenSpaceRefractionEnabled(enabled);
|
||||
}
|
||||
|
||||
bool View::isScreenSpaceRefractionEnabled() const noexcept {
|
||||
return upcast(this)->isScreenSpaceRefractionEnabled();
|
||||
return downcast(this)->isScreenSpaceRefractionEnabled();
|
||||
}
|
||||
|
||||
void View::setStencilBufferEnabled(bool enabled) noexcept {
|
||||
upcast(this)->setStencilBufferEnabled(enabled);
|
||||
downcast(this)->setStencilBufferEnabled(enabled);
|
||||
}
|
||||
|
||||
bool View::isStencilBufferEnabled() const noexcept {
|
||||
return upcast(this)->isStencilBufferEnabled();
|
||||
return downcast(this)->isStencilBufferEnabled();
|
||||
}
|
||||
|
||||
View::PickingQuery& View::pick(uint32_t x, uint32_t y, backend::CallbackHandler* handler,
|
||||
View::PickingQueryResultCallback callback) noexcept {
|
||||
return upcast(this)->pick(x, y, handler, callback);
|
||||
return downcast(this)->pick(x, y, handler, callback);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_COMPONENTS_CAMERAMANAGER_H
|
||||
#define TNT_FILAMENT_COMPONENTS_CAMERAMANAGER_H
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include <filament/FilamentAPI.h>
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ LightManager::Builder& LightManager::Builder::lightChannel(unsigned int channel,
|
||||
}
|
||||
|
||||
LightManager::Builder::Result LightManager::Builder::build(Engine& engine, Entity entity) {
|
||||
upcast(engine).createLight(*this, entity);
|
||||
downcast(engine).createLight(*this, entity);
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_COMPONENTS_LIGHTMANAGER_H
|
||||
#define TNT_FILAMENT_COMPONENTS_LIGHTMANAGER_H
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include "backend/DriverApiForward.h"
|
||||
|
||||
@@ -300,7 +300,7 @@ private:
|
||||
FEngine& mEngine;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(LightManager)
|
||||
FILAMENT_DOWNCAST(LightManager)
|
||||
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -185,7 +185,7 @@ RenderableManager::Builder& RenderableManager::Builder::skinning(
|
||||
|
||||
RenderableManager::Builder& RenderableManager::Builder::skinning(
|
||||
SkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept {
|
||||
mImpl->mSkinningBuffer = upcast(skinningBuffer);
|
||||
mImpl->mSkinningBuffer = downcast(skinningBuffer);
|
||||
mImpl->mSkinningBoneCount = count;
|
||||
mImpl->mSkinningBufferOffset = offset;
|
||||
return *this;
|
||||
@@ -241,10 +241,10 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
|
||||
// entry.materialInstance must be set to something even if indices/vertices are null
|
||||
FMaterial const* material = nullptr;
|
||||
if (!entry.materialInstance) {
|
||||
material = upcast(engine.getDefaultMaterial());
|
||||
material = downcast(engine.getDefaultMaterial());
|
||||
entry.materialInstance = material->getDefaultInstance();
|
||||
} else {
|
||||
material = upcast(entry.materialInstance->getMaterial());
|
||||
material = downcast(entry.materialInstance->getMaterial());
|
||||
}
|
||||
|
||||
// primitives without indices or vertices will be ignored
|
||||
@@ -253,7 +253,7 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
|
||||
}
|
||||
|
||||
// we want a feature level violation to be a hard error (exception if enabled, or crash)
|
||||
ASSERT_PRECONDITION(upcast(engine).hasFeatureLevel(material->getFeatureLevel()),
|
||||
ASSERT_PRECONDITION(downcast(engine).hasFeatureLevel(material->getFeatureLevel()),
|
||||
"Material \"%s\" has feature level %u which is not supported by this Engine",
|
||||
material->getName().c_str_safe(), (uint8_t)material->getFeatureLevel());
|
||||
|
||||
@@ -271,7 +271,7 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
|
||||
// this can't be an error because (1) those values are not immutable, so the caller
|
||||
// could fix later, and (2) the material's shader will work (i.e. compile), and
|
||||
// use the default values for this attribute, which maybe be acceptable.
|
||||
AttributeBitset declared = upcast(entry.vertices)->getDeclaredAttributes();
|
||||
AttributeBitset declared = downcast(entry.vertices)->getDeclaredAttributes();
|
||||
AttributeBitset required = material->getRequiredAttributes();
|
||||
if ((declared & required) != required) {
|
||||
slog.w << "[entity=" << entity.getId() << ", primitive @ " << i
|
||||
@@ -290,7 +290,7 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
|
||||
"[entity=%u] AABB can't be empty, unless culling is disabled and "
|
||||
"the object is not a shadow caster/receiver", entity.getId());
|
||||
|
||||
upcast(engine).createRenderable(*this, entity);
|
||||
downcast(engine).createRenderable(*this, entity);
|
||||
return Success;
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ void FRenderableManager::create(
|
||||
if (!morphing.buffer) {
|
||||
continue;
|
||||
}
|
||||
morphTargets[i] = { upcast(morphing.buffer), (uint32_t)morphing.offset,
|
||||
morphTargets[i] = { downcast(morphing.buffer), (uint32_t)morphing.offset,
|
||||
(uint32_t)morphing.count };
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_COMPONENTS_RENDERABLEMANAGER_H
|
||||
#define TNT_FILAMENT_COMPONENTS_RENDERABLEMANAGER_H
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include "HwRenderPrimitiveFactory.h"
|
||||
#include "UniformBuffer.h"
|
||||
@@ -258,7 +258,7 @@ private:
|
||||
HwRenderPrimitiveFactory mHwRenderPrimitiveFactory;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(RenderableManager)
|
||||
FILAMENT_DOWNCAST(RenderableManager)
|
||||
|
||||
void FRenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept {
|
||||
if (instance) {
|
||||
|
||||
@@ -473,7 +473,7 @@ void FTransformManager::gc(utils::EntityManager& em) noexcept {
|
||||
}
|
||||
|
||||
TransformManager::children_iterator& TransformManager::children_iterator::operator++() {
|
||||
FTransformManager const& that = upcast(mManager);
|
||||
FTransformManager const& that = downcast(mManager);
|
||||
mInstance = that.mManager[mInstance].next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_COMPONENTS_TRANSFORMMANAGER_H
|
||||
#define TNT_FILAMENT_COMPONENTS_TRANSFORMMANAGER_H
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include <filament/TransformManager.h>
|
||||
|
||||
@@ -197,7 +197,7 @@ private:
|
||||
bool mAccurateTranslations = false;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(TransformManager)
|
||||
FILAMENT_DOWNCAST(TransformManager)
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ BufferObject::Builder& BufferObject::Builder::bindingType(BindingType bindingTyp
|
||||
}
|
||||
|
||||
BufferObject* BufferObject::Builder::build(Engine& engine) {
|
||||
return upcast(engine).createBufferObject(*this);
|
||||
return downcast(engine).createBufferObject(*this);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_DETAILS_BUFFEROBJECT_H
|
||||
#define TNT_FILAMENT_DETAILS_BUFFEROBJECT_H
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include <backend/Handle.h>
|
||||
|
||||
@@ -50,7 +50,7 @@ private:
|
||||
BindingType mBindingType;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(BufferObject)
|
||||
FILAMENT_DOWNCAST(BufferObject)
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include <filament/Camera.h>
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include <filament/Frustum.h>
|
||||
|
||||
@@ -223,7 +223,7 @@ struct CameraInfo {
|
||||
math::mat4 getUserViewMatrix() const noexcept { return view * worldOrigin; }
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(Camera)
|
||||
FILAMENT_DOWNCAST(Camera)
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ ColorGrading* ColorGrading::Builder::build(Engine& engine) {
|
||||
}
|
||||
}
|
||||
|
||||
FColorGrading* colorGrading = upcast(engine).createColorGrading(*this);
|
||||
FColorGrading* colorGrading = downcast(engine).createColorGrading(*this);
|
||||
|
||||
if (needToneMapper) {
|
||||
delete mImpl->toneMapper;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_DETAILS_COLORGRADING_H
|
||||
#define TNT_FILAMENT_DETAILS_COLORGRADING_H
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
@@ -50,7 +50,7 @@ private:
|
||||
uint32_t mDimension;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(ColorGrading)
|
||||
FILAMENT_DOWNCAST(ColorGrading)
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_DETAILS_DEBUGREGISTRY_H
|
||||
#define TNT_FILAMENT_DETAILS_DEBUGREGISTRY_H
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include <filament/DebugRegistry.h>
|
||||
|
||||
@@ -76,7 +76,7 @@ private:
|
||||
std::unordered_map<std::string_view, DataSource> mDataSourceMap;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(DebugRegistry)
|
||||
FILAMENT_DOWNCAST(DebugRegistry)
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -258,6 +258,12 @@ uint32_t FEngine::getJobSystemThreadPoolSize() noexcept {
|
||||
void FEngine::init() {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
#if FILAMENT_ENABLE_FGDBG
|
||||
if (!debug.fgdbg) {
|
||||
debug.fgdbg = std::make_unique<fgdbg::DebugServer>();
|
||||
}
|
||||
#endif
|
||||
|
||||
// this must be first.
|
||||
assert_invariant( intptr_t(&mDriverApiStorage) % alignof(DriverApi) == 0 );
|
||||
::new(&mDriverApiStorage) DriverApi(*mDriver, mCommandBufferQueue.getCircularBuffer());
|
||||
@@ -268,7 +274,7 @@ void FEngine::init() {
|
||||
|
||||
mResourceAllocator = new ResourceAllocator(driverApi);
|
||||
|
||||
mFullScreenTriangleVb = upcast(VertexBuffer::Builder()
|
||||
mFullScreenTriangleVb = downcast(VertexBuffer::Builder()
|
||||
.vertexCount(3)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT4, 0)
|
||||
@@ -277,7 +283,7 @@ void FEngine::init() {
|
||||
mFullScreenTriangleVb->setBufferAt(*this, 0,
|
||||
{ sFullScreenTriangleVertices, sizeof(sFullScreenTriangleVertices) });
|
||||
|
||||
mFullScreenTriangleIb = upcast(IndexBuffer::Builder()
|
||||
mFullScreenTriangleIb = downcast(IndexBuffer::Builder()
|
||||
.indexCount(3)
|
||||
.bufferType(IndexBuffer::IndexType::USHORT)
|
||||
.build(*this));
|
||||
@@ -308,7 +314,7 @@ void FEngine::init() {
|
||||
});
|
||||
}
|
||||
|
||||
mDefaultIblTexture = upcast(Texture::Builder()
|
||||
mDefaultIblTexture = downcast(Texture::Builder()
|
||||
.width(1).height(1).levels(1)
|
||||
.format(Texture::InternalFormat::RGBA8)
|
||||
.sampler(Texture::Sampler::SAMPLER_CUBEMAP)
|
||||
@@ -316,14 +322,14 @@ void FEngine::init() {
|
||||
|
||||
// 3 bands = 9 float3
|
||||
const float sh[9 * 3] = { 0.0f };
|
||||
mDefaultIbl = upcast(IndirectLight::Builder()
|
||||
mDefaultIbl = downcast(IndirectLight::Builder()
|
||||
.irradiance(3, reinterpret_cast<const float3*>(sh))
|
||||
.build(*this));
|
||||
|
||||
mDefaultColorGrading = upcast(ColorGrading::Builder().build(*this));
|
||||
mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this));
|
||||
|
||||
// Always initialize the default material, most materials' depth shaders fallback on it.
|
||||
mDefaultMaterial = upcast(
|
||||
mDefaultMaterial = downcast(
|
||||
FMaterial::DefaultMaterialBuilder()
|
||||
.package(MATERIALS_DEFAULTMATERIAL_DATA, MATERIALS_DEFAULTMATERIAL_SIZE)
|
||||
.build(*const_cast<FEngine*>(this)));
|
||||
@@ -606,16 +612,16 @@ int FEngine::loop() {
|
||||
#endif
|
||||
if (portString != nullptr) {
|
||||
const int port = atoi(portString);
|
||||
debug.server = new matdbg::DebugServer(mBackend, port);
|
||||
debug.matdbg = std::make_unique<matdbg::DebugServer>(mBackend, port);
|
||||
|
||||
// Sometimes the server can fail to spin up (e.g. if the above port is already in use).
|
||||
// When this occurs, carry onward, developers can look at civetweb.txt for details.
|
||||
if (!debug.server->isReady()) {
|
||||
delete debug.server;
|
||||
debug.server = nullptr;
|
||||
if (!debug.matdbg->isReady()) {
|
||||
delete debug.matdbg.get();
|
||||
debug.matdbg = nullptr;
|
||||
} else {
|
||||
debug.server->setEditCallback(FMaterial::onEditCallback);
|
||||
debug.server->setQueryCallback(FMaterial::onQueryCallback);
|
||||
debug.matdbg->setEditCallback(FMaterial::onEditCallback);
|
||||
debug.matdbg->setQueryCallback(FMaterial::onQueryCallback);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -640,6 +646,16 @@ int FEngine::loop() {
|
||||
uint32_t id = std::thread::hardware_concurrency() - 1;
|
||||
|
||||
while (true) {
|
||||
#if FILAMENT_ENABLE_FGDBG
|
||||
// TODO (@feresr): Only for testing purposes, remove
|
||||
if (debug.fgdbg) {
|
||||
static int fgdbgMessage = 0;
|
||||
fgdbgMessage++;
|
||||
if (fgdbgMessage % 100 == 0) {
|
||||
debug.fgdbg->sendMessage(fgdbgMessage/100);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// looks like thread affinity needs to be reset regularly (on Android)
|
||||
JobSystem::setThreadAffinityById(id);
|
||||
if (!execute()) {
|
||||
@@ -1072,4 +1088,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
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_DETAILS_ENGINE_H
|
||||
#define TNT_FILAMENT_DETAILS_ENGINE_H
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include "Allocators.h"
|
||||
#include "DFG.h"
|
||||
@@ -57,6 +57,14 @@
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
|
||||
#if FILAMENT_ENABLE_FGDBG
|
||||
#include <fgdbg/DebugServer.h>
|
||||
#else
|
||||
namespace filament::fgdbg {
|
||||
class DebugServer;
|
||||
} // namespace filament::fgdbg
|
||||
#endif
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
#include <matdbg/DebugServer.h>
|
||||
#else
|
||||
@@ -374,6 +382,10 @@ public:
|
||||
return FEngine::getActiveFeatureLevel() >= neededFeatureLevel;
|
||||
}
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
void resetBackendState() noexcept;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static Config validateConfig(const Config* pConfig) noexcept;
|
||||
|
||||
@@ -515,11 +527,12 @@ public:
|
||||
// capture to file. At the moment, only supported by the Metal backend.
|
||||
bool doFrameCapture = false;
|
||||
} renderer;
|
||||
matdbg::DebugServer* server = nullptr;
|
||||
std::unique_ptr<matdbg::DebugServer> matdbg = nullptr;
|
||||
std::unique_ptr<fgdbg::DebugServer> fgdbg = nullptr;
|
||||
} debug;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(Engine)
|
||||
FILAMENT_DOWNCAST(Engine)
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_DETAILS_FENCE_H
|
||||
#define TNT_FILAMENT_DETAILS_FENCE_H
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include <filament/Fence.h>
|
||||
|
||||
@@ -70,7 +70,7 @@ private:
|
||||
std::shared_ptr<FenceSignal> mFenceSignal;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(Fence)
|
||||
FILAMENT_DOWNCAST(Fence)
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ IndexBuffer::Builder& IndexBuffer::Builder::bufferType(IndexType indexType) noex
|
||||
}
|
||||
|
||||
IndexBuffer* IndexBuffer::Builder::build(Engine& engine) {
|
||||
return upcast(engine).createIndexBuffer(*this);
|
||||
return downcast(engine).createIndexBuffer(*this);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_DETAILS_INDEXBUFFER_H
|
||||
#define TNT_FILAMENT_DETAILS_INDEXBUFFER_H
|
||||
|
||||
#include "upcast.h"
|
||||
#include "downcast.h"
|
||||
|
||||
#include <backend/Handle.h>
|
||||
|
||||
@@ -48,7 +48,7 @@ private:
|
||||
uint32_t mIndexCount;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(IndexBuffer)
|
||||
FILAMENT_DOWNCAST(IndexBuffer)
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user