Compare commits
129
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dbee125b3 | ||
|
|
e64b498d6e | ||
|
|
31ab4a13c9 | ||
|
|
e9728dcc8d | ||
|
|
40a3644086 | ||
|
|
41ef71abdf | ||
|
|
3b40e52c98 | ||
|
|
de8c7969f3 | ||
|
|
f2c049222b | ||
|
|
be30ac1fcb | ||
|
|
c5cc7fe26a | ||
|
|
d04b0a5c73 | ||
|
|
72ab2aef0a | ||
|
|
1527bb7ad3 | ||
|
|
8cb26fc9ac | ||
|
|
c21794d0a0 | ||
|
|
1a6338569a | ||
|
|
c5ebe1a88a | ||
|
|
a3f2028ae4 | ||
|
|
c393ee4591 | ||
|
|
83a82ae10e | ||
|
|
676694e458 | ||
|
|
2ca218928f | ||
|
|
776ce223fc | ||
|
|
0d4d1f55c9 | ||
|
|
d6dba71ace | ||
|
|
61ee11ae6c | ||
|
|
90f32e7277 | ||
|
|
b85d9afdb3 | ||
|
|
4297d43d9a | ||
|
|
cfcf714c21 | ||
|
|
f7cfc6a65e | ||
|
|
4ead3bcebf | ||
|
|
21e0677039 | ||
|
|
107b5a4854 | ||
|
|
9761b65137 | ||
|
|
b1831d2786 | ||
|
|
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 | ||
|
|
fbfcce563d | ||
|
|
7f5ad3743b | ||
|
|
981020e72b | ||
|
|
7c1a705a07 | ||
|
|
3d53ae583f | ||
|
|
dc9f03cbd0 | ||
|
|
de7bca7ef8 | ||
|
|
f151f56a2e | ||
|
|
fe3d1713a4 | ||
|
|
2cad3be7a3 | ||
|
|
5262512eea | ||
|
|
fabba73b1e | ||
|
|
d291ec739b | ||
|
|
0dc0bbd4f3 | ||
|
|
984a029ce4 | ||
|
|
1cbc1f30a6 | ||
|
|
08fb39a1e4 | ||
|
|
d8a71ce195 | ||
|
|
13986c12bc | ||
|
|
cc64704a82 | ||
|
|
df2ad99260 | ||
|
|
fb1f277b9a | ||
|
|
0388161464 | ||
|
|
e989d5603f | ||
|
|
defaa0d008 | ||
|
|
e161ba80f2 | ||
|
|
034aaff56c | ||
|
|
b0fb52c5f4 | ||
|
|
a05f9c172f | ||
|
|
adde936d18 | ||
|
|
feda999eb3 | ||
|
|
8dae181c2f | ||
|
|
8cdac431a6 | ||
|
|
0ecf5118ba | ||
|
|
f21638a1e2 | ||
|
|
8dc16494a7 | ||
|
|
a8765b1dc0 | ||
|
|
d346b94b8a | ||
|
|
c4abe4f46a | ||
|
|
ad6a5eb4e1 | ||
|
|
c84834d666 |
@@ -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:
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ To build Filament for Android you must also install the following:
|
||||
|
||||
- Android Studio Arctic Fox or more recent
|
||||
- Android SDK
|
||||
- Android NDK "side-by-side" 23.1 or higher
|
||||
- Android NDK 25.1 or higher
|
||||
|
||||
### Environment variables
|
||||
|
||||
|
||||
+10
-6
@@ -337,19 +337,23 @@ endif()
|
||||
# ==================================================================================================
|
||||
# Release compiler flags
|
||||
# ==================================================================================================
|
||||
if (NOT MSVC)
|
||||
if (NOT MSVC AND NOT IOS)
|
||||
# Omitting stack frame pointers prevents the generation of readable stack traces in crash reports on iOS
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer")
|
||||
|
||||
# These aren't compatible with -fembed-bitcode (and seem to have no effect on Apple platforms anyway)
|
||||
if (NOT IOS)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffunction-sections -fdata-sections")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffunction-sections -fdata-sections")
|
||||
endif()
|
||||
|
||||
# On Android RELEASE builds, we disable exceptions and RTTI to save some space (about 75 KiB
|
||||
# saved by -fno-exception and 10 KiB saved by -fno-rtti).
|
||||
if (ANDROID OR IOS OR WEBGL)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-rtti")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions -fno-rtti")
|
||||
|
||||
if (ANDROID OR WEBGL)
|
||||
# Omitting unwind info prevents the generation of readable stack traces in crash reports on iOS
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-unwind-tables -fno-asynchronous-unwind-tables")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# With WebGL, we disable RTTI even for debug builds because we pass emscripten::val back and forth
|
||||
@@ -666,7 +670,7 @@ add_subdirectory(${EXTERNAL}/imgui/tnt)
|
||||
add_subdirectory(${EXTERNAL}/robin-map/tnt)
|
||||
add_subdirectory(${EXTERNAL}/smol-v/tnt)
|
||||
add_subdirectory(${EXTERNAL}/benchmark/tnt)
|
||||
add_subdirectory(${EXTERNAL}/meshoptimizer)
|
||||
add_subdirectory(${EXTERNAL}/meshoptimizer/tnt)
|
||||
add_subdirectory(${EXTERNAL}/cgltf/tnt)
|
||||
add_subdirectory(${EXTERNAL}/draco/tnt)
|
||||
add_subdirectory(${EXTERNAL}/jsmn/tnt)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.27.1'
|
||||
implementation 'com.google.android.filament:filament-android:1.28.2'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```
|
||||
pod 'Filament', '~> 1.27.1'
|
||||
pod 'Filament', '~> 1.28.2'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
@@ -179,6 +179,7 @@ steps:
|
||||
- [x] KHR_mesh_quantization
|
||||
- [x] KHR_texture_basisu
|
||||
- [x] KHR_texture_transform
|
||||
- [x] EXT_meshopt_compression
|
||||
|
||||
|
||||
## Rendering with Filament
|
||||
|
||||
@@ -5,6 +5,50 @@ A new header is inserted each time a *tag* is created.
|
||||
|
||||
## main branch
|
||||
|
||||
- backend: add support for GGP platform
|
||||
- gltfio: calculate primitive's AABB correctly.
|
||||
- gltfio: fix glTF breaking issue
|
||||
- gltfio: recompute bounding boxes with morph targets
|
||||
- engine: primitives with `CullingMode::FRONT_AND_BACK` are now skipped.
|
||||
- engine: add missing getters on `MaterialInstance`
|
||||
|
||||
## v1.28.2
|
||||
|
||||
- gltfio: support EXT_meshopt_compression
|
||||
- release packaging: fixed location of default IBL file
|
||||
|
||||
## v1.28.1
|
||||
|
||||
- gltfio: fix reloading crash in ubershader mode
|
||||
- Vulkan: improve performance in the readPixels path
|
||||
- engine: raise the spot shadows limit to 64, from 14.
|
||||
- engine: add experimental support for point light shadows.
|
||||
- gltfio: fix ubershader issues with assignment of dummy textures
|
||||
- gltfio: material instances and variants are now accessed via `FilamentInstance` [⚠️ **API Change**]
|
||||
- gltfio: the animator can now only be accessed via `FilamentInstance` [⚠️ **API Change**]
|
||||
- engine: fix "stable" shadows and make the default cascade splits logarithmic.
|
||||
- engine: Add new quality options to EVSM shadows + rendering fixes
|
||||
|
||||
## v1.28.0
|
||||
|
||||
- engine: LiSPSM is now a user settable option
|
||||
- engine: get the morph target buffer to the given primitive
|
||||
- Java: Fix TransformManager.getChildren()
|
||||
- Metal: newer devices are no longer limited to 16 samplers per Material.
|
||||
- gltfio: fix interpretation of occlusion strength
|
||||
- engine: minsdk is now 21 instead of 19. This allows the use of OpenGL ES 3.1
|
||||
- Vulkan: fix black screen regression
|
||||
|
||||
## v1.27.2
|
||||
|
||||
- gltfio: punctual lights are now duplicated when adding new asset instances
|
||||
- gltfio: FilamentInstance getAsset method now returns an immutable asset
|
||||
- gltfio: allow zero-instance assets
|
||||
- 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
|
||||
|
||||
- Java: add methods for TransformManager.getChildCount(), TransformManager.getChildren() and Scene.hasEntity()
|
||||
|
||||
+21
-21
@@ -29,15 +29,12 @@
|
||||
|
||||
// Publishing to Maven Central:
|
||||
// - Build and upload artifacts with ./gradlew publish
|
||||
// - Close and release staging repo on Nexus with ./gradlew closeAndReleaseRepository
|
||||
// - Close and release staging repo on Nexus with ./gradlew closeAndReleaseStagingRepository
|
||||
//
|
||||
// The following is needed in ~/gradle/gradle.properties:
|
||||
//
|
||||
// SONATYPE_NEXUS_USERNAME=nexus_user
|
||||
// SONATYPE_NEXUS_PASSWORD=nexus_password
|
||||
//
|
||||
// nexusUsername=nexus_user
|
||||
// nexusPassword=nexus_password
|
||||
// sonatypeUsername=nexus_user
|
||||
// sonatypePassword=nexus_password
|
||||
//
|
||||
// signing.keyId=pgp_key_id
|
||||
// signing.password=pgp_key_password
|
||||
@@ -47,7 +44,7 @@
|
||||
buildscript {
|
||||
def path = providers
|
||||
.gradleProperty("com.google.android.filament.dist-dir")
|
||||
.forUseAtConfigurationTime().get()
|
||||
.get()
|
||||
|
||||
def directory = objects.fileProperty().fileValue(new File(path)).getAsFile().get()
|
||||
def filamentPath = directory.absolutePath
|
||||
@@ -59,18 +56,15 @@ buildscript {
|
||||
// Warning: changing this property does not work well with incremental builds.
|
||||
def excludeVulkan = providers
|
||||
.gradleProperty("com.google.android.filament.exclude-vulkan")
|
||||
.forUseAtConfigurationTime()
|
||||
.isPresent()
|
||||
|
||||
def matdbg = providers
|
||||
.gradleProperty("com.google.android.filament.matdbg")
|
||||
.forUseAtConfigurationTime()
|
||||
.isPresent()
|
||||
|
||||
def abis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
|
||||
def newAbis = providers
|
||||
.gradleProperty("com.google.android.filament.abis")
|
||||
.forUseAtConfigurationTime()
|
||||
.get()
|
||||
.split(',')
|
||||
if (!newAbis.contains("all")) {
|
||||
@@ -79,18 +73,20 @@ buildscript {
|
||||
|
||||
ext.versions = [
|
||||
'minSdk': 19,
|
||||
'targetSdk': 31,
|
||||
'compileSdk': 31,
|
||||
'targetSdk': 33,
|
||||
'compileSdk': 33,
|
||||
'kotlin': '1.7.10',
|
||||
'kotlin_coroutines': '1.6.1',
|
||||
'buildTools': '33.0.0',
|
||||
'ndk': '25.0.8775105'
|
||||
'ndk': '25.1.8937393',
|
||||
'androidx_core': '1.9.0',
|
||||
'androidx_annotations': '1.3.0'
|
||||
]
|
||||
|
||||
ext.deps = [
|
||||
'androidx': [
|
||||
'annotations': "androidx.annotation:annotation:1.3.0",
|
||||
'core': "androidx.core:core:1.7.0",
|
||||
'annotations': "androidx.annotation:annotation:${versions.androidx_annotations}",
|
||||
'core': "androidx.core:core:${versions.androidx_core}",
|
||||
],
|
||||
'kotlin': "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}",
|
||||
'coroutines': [
|
||||
@@ -101,7 +97,7 @@ buildscript {
|
||||
|
||||
dependencies {
|
||||
// NOTE: See TODO in gradle.properties once we move to Gradle 7.4
|
||||
classpath 'com.android.tools.build:gradle:7.2.1'
|
||||
classpath 'com.android.tools.build:gradle:7.3.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
|
||||
}
|
||||
|
||||
@@ -148,14 +144,18 @@ buildscript {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'io.codearte.nexus-staging' version '0.30.0'
|
||||
id "io.github.gradle-nexus.publish-plugin" version "1.1.0"
|
||||
}
|
||||
|
||||
// Nexus Staging configuration
|
||||
// See https://github.com/Codearte/gradle-nexus-staging-plugin/
|
||||
nexusStaging {
|
||||
// See https://github.com/gradle-nexus/publish-plugin
|
||||
// Publish to https://oss.sonatype.org/ (not s01)
|
||||
nexusPublishing {
|
||||
packageGroup = 'com.google.android'
|
||||
stagingProfileId = '9a75a224a4f17b'
|
||||
repositories {
|
||||
sonatype {
|
||||
stagingProfileId = '9a75a224a4f17b'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
||||
@@ -126,8 +126,8 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderUniform
|
||||
JNIEnv* env, jclass, jlong nativeBuilder, jint uniformType, jint precision, jstring name_) {
|
||||
auto builder = (MaterialBuilder*) nativeBuilder;
|
||||
const char* name = env->GetStringUTFChars(name_, nullptr);
|
||||
builder->parameter((MaterialBuilder::UniformType) uniformType,
|
||||
(MaterialBuilder::ParameterPrecision) precision, name);
|
||||
builder->parameter(name, (MaterialBuilder::UniformType) uniformType,
|
||||
(MaterialBuilder::ParameterPrecision) precision);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
@@ -137,8 +137,8 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderUniform
|
||||
jstring name_) {
|
||||
auto builder = (MaterialBuilder*) nativeBuilder;
|
||||
const char* name = env->GetStringUTFChars(name_, nullptr);
|
||||
builder->parameter((MaterialBuilder::UniformType) uniformType, (size_t) size,
|
||||
(MaterialBuilder::ParameterPrecision) precision, name);
|
||||
builder->parameter(name, (size_t) size, (MaterialBuilder::UniformType) uniformType,
|
||||
(MaterialBuilder::ParameterPrecision) precision);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
@@ -148,9 +148,8 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderSampler
|
||||
jint precision, jstring name_) {
|
||||
auto builder = (MaterialBuilder*) nativeBuilder;
|
||||
const char* name = env->GetStringUTFChars(name_, nullptr);
|
||||
builder->parameter((MaterialBuilder::SamplerType) samplerType,
|
||||
(MaterialBuilder::SamplerFormat) format, (MaterialBuilder::ParameterPrecision) precision,
|
||||
name);
|
||||
builder->parameter(name, (MaterialBuilder::SamplerType) samplerType,
|
||||
(MaterialBuilder::SamplerFormat) format, (MaterialBuilder::ParameterPrecision) precision);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,10 +76,10 @@ extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env, jclass,
|
||||
jlong nativeBuilder, jint mapSize, jint cascades, jfloatArray splitPositions,
|
||||
jfloat constantBias, jfloat normalBias, jfloat shadowFar, jfloat shadowNearHint,
|
||||
jfloat shadowFarHint, jboolean stable,
|
||||
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,
|
||||
@@ -90,6 +90,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
|
||||
.shadowNearHint = shadowNearHint,
|
||||
.shadowFarHint = shadowFarHint,
|
||||
.stable = (bool)stable,
|
||||
.lispsm = (bool)lispsm,
|
||||
.polygonOffsetConstant = polygonOffsetConstant,
|
||||
.polygonOffsetSlope = polygonOffsetConstant,
|
||||
.screenSpaceContactShadows = (bool)screenSpaceContactShadows,
|
||||
@@ -97,6 +98,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
|
||||
.maxShadowDistance = maxShadowDistance,
|
||||
.vsm = {
|
||||
.msaaSamples = (uint8_t) vsmMsaaSamples,
|
||||
.elvsm = (bool)elvsm,
|
||||
.blurWidth = blurWidth
|
||||
},
|
||||
.shadowBulbRadius = shadowBulbRadius
|
||||
|
||||
@@ -452,3 +452,84 @@ Java_com_google_android_filament_MaterialInstance_nDuplicate(JNIEnv* env, jclass
|
||||
}
|
||||
return (jlong)mi;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jfloat JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nGetMaskThreshold(JNIEnv* env, jclass clazz,
|
||||
jlong nativeMaterialInstance) {
|
||||
// TODO: implement nGetMaskThreshold()
|
||||
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
|
||||
return instance->getMaskThreshold();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jfloat JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingVariance(JNIEnv* env,
|
||||
jclass clazz, jlong nativeMaterialInstance) {
|
||||
// TODO: implement nGetSpecularAntiAliasingVariance()
|
||||
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
|
||||
return instance->getSpecularAntiAliasingVariance();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jfloat JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingThreshold(JNIEnv* env,
|
||||
jclass clazz, jlong nativeMaterialInstance) {
|
||||
// TODO: implement nGetSpecularAntiAliasingThreshold()
|
||||
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
|
||||
return instance->getSpecularAntiAliasingThreshold();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nIsDoubleSided(JNIEnv* env, jclass clazz,
|
||||
jlong nativeMaterialInstance) {
|
||||
// TODO: implement nIsDoubleSided()
|
||||
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
|
||||
return instance->isDoubleSided();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nGetCullingMode(JNIEnv* env, jclass clazz,
|
||||
jlong nativeMaterialInstance) {
|
||||
// TODO: implement nGetCullingMode()
|
||||
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
|
||||
return (jint)instance->getCullingMode();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nIsColorWriteEnabled(JNIEnv* env, jclass clazz,
|
||||
jlong nativeMaterialInstance) {
|
||||
// TODO: implement nIsColorWriteEnabled()
|
||||
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
|
||||
return instance->isColorWriteEnabled();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nIsDepthWriteEnabled(JNIEnv* env, jclass clazz,
|
||||
jlong nativeMaterialInstance) {
|
||||
// TODO: implement nIsDepthWriteEnabled()
|
||||
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
|
||||
return instance->isDepthWriteEnabled();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nIsStencilWriteEnabled(JNIEnv* env, jclass clazz,
|
||||
jlong nativeMaterialInstance) {
|
||||
// TODO: implement nIsStencilWriteEnabled()
|
||||
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
|
||||
return instance->isStencilWriteEnabled();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nIsDepthCullingEnabled(JNIEnv* env, jclass clazz,
|
||||
jlong nativeMaterialInstance) {
|
||||
// TODO: implement nIsDepthCullingEnabled()
|
||||
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
|
||||
return instance->isDepthCullingEnabled();
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ Java_com_google_android_filament_TransformManager_nGetChildren(JNIEnv* env,
|
||||
// (which it is), but still.
|
||||
tm->getChildren((TransformManager::Instance) i,
|
||||
reinterpret_cast<Entity *>(entities), (size_t) count);
|
||||
env->ReleaseIntArrayElements(outEntities_, entities, JNI_ABORT);
|
||||
env->ReleaseIntArrayElements(outEntities_, entities, 0);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
|
||||
@@ -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);
|
||||
|
||||
+36
-9
@@ -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.
|
||||
@@ -280,9 +280,25 @@ public class LightManager {
|
||||
* Controls whether the shadow map should be optimized for resolution or stability.
|
||||
* When set to true, all resolution enhancing features that can affect stability are
|
||||
* disabling, resulting in significantly lower resolution shadows, albeit stable ones.
|
||||
*
|
||||
* Setting this flag to true always disables LiSPSM (see below).
|
||||
*/
|
||||
public boolean stable = false;
|
||||
|
||||
/**
|
||||
* LiSPSM, or light-space perspective shadow-mapping is a technique allowing to better
|
||||
* optimize the use of the shadow-map texture. When enabled the effective resolution of
|
||||
* shadows is greatly improved and yields result similar to using cascades without the
|
||||
* extra cost. LiSPSM comes with some drawbacks however, in particular it is incompatible
|
||||
* with blurring because it effectively affects the blur kernel size.
|
||||
*
|
||||
* Blurring is only an issue when using ShadowType.VSM with a large blur or with
|
||||
* ShadowType.PCSS however.
|
||||
*
|
||||
* If these blurring artifacts become problematic, this flag can be used to disable LiSPSM.
|
||||
*/
|
||||
public boolean lispsm = false;
|
||||
|
||||
/**
|
||||
* Constant bias in depth-resolution units by which shadows are moved away from the
|
||||
* light. The default value of 0.5 is used to round depth values up.
|
||||
@@ -346,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.
|
||||
@@ -470,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.
|
||||
@@ -496,11 +516,11 @@ public class LightManager {
|
||||
nBuilderShadowOptions(mNativeBuilder,
|
||||
options.mapSize, options.shadowCascades, options.cascadeSplitPositions,
|
||||
options.constantBias, options.normalBias, options.shadowFar, options.shadowNearHint,
|
||||
options.shadowFarHint, options.stable,
|
||||
options.shadowFarHint, options.stable, options.lispsm,
|
||||
options.polygonOffsetConstant, options.polygonOffsetSlope,
|
||||
options.screenSpaceContactShadows,
|
||||
options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples,
|
||||
options.blurWidth, options.shadowBulbRadius);
|
||||
options.elvsm, options.blurWidth, options.shadowBulbRadius);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1158,7 +1178,14 @@ public class LightManager {
|
||||
private static native void nDestroyBuilder(long nativeBuilder);
|
||||
private static native boolean nBuilderBuild(long nativeBuilder, long nativeEngine, int entity);
|
||||
private static native void nBuilderCastShadows(long nativeBuilder, boolean enable);
|
||||
private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize, int cascades, float[] splitPositions, float constantBias, float normalBias, float shadowFar, float shadowNearHint, float shadowFarhint, boolean stable, float polygonOffsetConstant, float polygonOffsetSlope, boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, int vsmMsaaSamples, float blurWidth, float shadowBulbRadius);
|
||||
private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize,
|
||||
int cascades, float[] splitPositions,
|
||||
float constantBias, float normalBias,
|
||||
float shadowFar, float shadowNearHint, float shadowFarhint,
|
||||
boolean stable, boolean lispsm,
|
||||
float polygonOffsetConstant, float polygonOffsetSlope,
|
||||
boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance,
|
||||
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);
|
||||
|
||||
+84
-1
@@ -24,6 +24,7 @@ import com.google.android.filament.proguard.UsedByNative;
|
||||
|
||||
@UsedByNative("AssetLoader.cpp")
|
||||
public class MaterialInstance {
|
||||
private static final Material.CullingMode[] sCullingModeValues = Material.CullingMode.values();
|
||||
private Material mMaterial;
|
||||
private String mName;
|
||||
private long mNativeObject;
|
||||
@@ -473,6 +474,14 @@ public class MaterialInstance {
|
||||
nSetMaskThreshold(getNativeObject(), threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum alpha value a fragment must have to not be discarded when the blend
|
||||
* mode is MASKED
|
||||
*/
|
||||
public float getMaskThreshold() {
|
||||
return nGetMaskThreshold(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the screen space variance of the filter kernel used when applying specular
|
||||
* anti-aliasing. The default value is set to 0.15. The specified value should be between
|
||||
@@ -486,6 +495,14 @@ public class MaterialInstance {
|
||||
nSetSpecularAntiAliasingVariance(getNativeObject(), variance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the screen space variance of the filter kernel used when applying specular
|
||||
* anti-aliasing.
|
||||
*/
|
||||
public float getSpecularAntiAliasingVariance() {
|
||||
return nGetSpecularAntiAliasingVariance(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the clamping threshold used to suppress estimation errors when applying specular
|
||||
* anti-aliasing. The default value is set to 0.2. The specified value should be between 0
|
||||
@@ -499,6 +516,14 @@ public class MaterialInstance {
|
||||
nSetSpecularAntiAliasingThreshold(getNativeObject(), threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the clamping threshold used to suppress estimation errors when applying specular
|
||||
* anti-aliasing.
|
||||
*/
|
||||
public float getSpecularAntiAliasingThreshold() {
|
||||
return nGetSpecularAntiAliasingThreshold(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables double-sided lighting if the parent Material has double-sided capability,
|
||||
* otherwise prints a warning. If double-sided lighting is enabled, backface culling is
|
||||
@@ -512,6 +537,14 @@ public class MaterialInstance {
|
||||
nSetDoubleSided(getNativeObject(), doubleSided);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether double-sided lighting is enabled when the parent Material has double-sided
|
||||
* capability.
|
||||
*/
|
||||
public boolean isDoubleSided() {
|
||||
return nIsDoubleSided(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default triangle culling state that was set on the material.
|
||||
*
|
||||
@@ -519,10 +552,18 @@ public class MaterialInstance {
|
||||
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:culling">
|
||||
* Rasterization: culling</a>
|
||||
*/
|
||||
public void setCullingMode(Material.CullingMode mode) {
|
||||
public void setCullingMode(@NonNull Material.CullingMode mode) {
|
||||
nSetCullingMode(getNativeObject(), mode.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the face culling mode.
|
||||
*/
|
||||
@NonNull
|
||||
public Material.CullingMode getCullingMode() {
|
||||
return sCullingModeValues[nGetCullingMode(getNativeObject())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default color-buffer write state that was set on the material.
|
||||
*
|
||||
@@ -534,6 +575,13 @@ public class MaterialInstance {
|
||||
nSetColorWrite(getNativeObject(), enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether color write is enabled.
|
||||
*/
|
||||
public boolean isColorWriteEnabled() {
|
||||
return nIsColorWriteEnabled(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default depth-buffer write state that was set on the material.
|
||||
*
|
||||
@@ -545,10 +593,27 @@ public class MaterialInstance {
|
||||
nSetDepthWrite(getNativeObject(), enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether depth write is enabled.
|
||||
*/
|
||||
public boolean isDepthWriteEnabled() {
|
||||
return nIsDepthWriteEnabled(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or Disable stencil writes
|
||||
*/
|
||||
public void setStencilWrite(boolean enable) {
|
||||
nSetStencilWrite(getNativeObject(), enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether stencil write is enabled.
|
||||
*/
|
||||
public boolean isStencilWriteEnabled() {
|
||||
return nIsStencilWriteEnabled(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default depth testing state that was set on the material.
|
||||
*
|
||||
@@ -560,6 +625,13 @@ public class MaterialInstance {
|
||||
nSetDepthCulling(getNativeObject(), enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether depth culling is enabled.
|
||||
*/
|
||||
public boolean isDepthCullingEnabled() {
|
||||
return nIsDepthCullingEnabled(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stencil comparison function (default is {@link TextureSampler.CompareFunction#ALWAYS}).
|
||||
*
|
||||
@@ -856,4 +928,15 @@ public class MaterialInstance {
|
||||
private static native long nGetMaterial(long nativeMaterialInstance);
|
||||
|
||||
private static native long nDuplicate(long otherNativeMaterialInstance, String name);
|
||||
|
||||
|
||||
private static native float nGetMaskThreshold(long nativeMaterialInstance);
|
||||
private static native float nGetSpecularAntiAliasingVariance(long nativeMaterialInstance);
|
||||
private static native float nGetSpecularAntiAliasingThreshold(long nativeMaterialInstance);
|
||||
private static native boolean nIsDoubleSided(long nativeMaterialInstance);
|
||||
private static native int nGetCullingMode(long nativeMaterialInstance);
|
||||
private static native boolean nIsColorWriteEnabled(long nativeMaterialInstance);
|
||||
private static native boolean nIsDepthWriteEnabled(long nativeMaterialInstance);
|
||||
private static native boolean nIsStencilWriteEnabled(long nativeMaterialInstance);
|
||||
private static native boolean nIsDepthCullingEnabled(long nativeMaterialInstance);
|
||||
}
|
||||
|
||||
@@ -793,7 +793,7 @@ public class View {
|
||||
public void setVsmShadowOptions(@NonNull VsmShadowOptions options) {
|
||||
mVsmShadowOptions = options;
|
||||
nSetVsmShadowOptions(getNativeObject(), options.anisotropy, options.mipmapping,
|
||||
options.minVarianceScale, options.lightBleedReduction);
|
||||
options.highPrecision, options.minVarianceScale, options.lightBleedReduction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1144,7 +1144,7 @@ public class View {
|
||||
private static native void nSetRenderQuality(long nativeView, int hdrColorBufferQuality);
|
||||
private static native void nSetDynamicLightingOptions(long nativeView, float zLightNear, float zLightFar);
|
||||
private static native void nSetShadowType(long nativeView, int type);
|
||||
private static native void nSetVsmShadowOptions(long nativeView, int anisotropy, boolean mipmapping, float minVarianceScale, float lightBleedReduction);
|
||||
private static native void nSetVsmShadowOptions(long nativeView, int anisotropy, boolean mipmapping, boolean highPrecision, float minVarianceScale, float lightBleedReduction);
|
||||
private static native void nSetSoftShadowOptions(long nativeView, float penumbraScale, float penumbraRatioScale);
|
||||
private static native void nSetColorGrading(long nativeView, long nativeColorGrading);
|
||||
private static native void nSetPostProcessingEnabled(long nativeView, boolean enabled);
|
||||
@@ -1817,6 +1817,13 @@ public class View {
|
||||
* Whether to generate mipmaps for all VSM shadow maps.
|
||||
*/
|
||||
public boolean mipmapping = false;
|
||||
/**
|
||||
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
|
||||
* precision is rarely needed, but it does reduces light leaks as well as "fading"
|
||||
* of the shadows in some situations. Setting highPrecision to true for a single
|
||||
* shadow map will double the memory usage of all shadow maps.
|
||||
*/
|
||||
public boolean highPrecision = false;
|
||||
/**
|
||||
* VSM minimum variance scale, must be positive.
|
||||
*/
|
||||
|
||||
+5
-5
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -310,8 +310,8 @@ class ModelViewer(
|
||||
|
||||
private fun addDetachListener(view: android.view.View) {
|
||||
view.addOnAttachStateChangeListener(object : android.view.View.OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(v: android.view.View?) {}
|
||||
override fun onViewDetachedFromWindow(v: android.view.View?) {
|
||||
override fun onViewAttachedToWindow(v: android.view.View) {}
|
||||
override fun onViewDetachedFromWindow(v: android.view.View) {
|
||||
uiHelper.detach()
|
||||
|
||||
destroyModel()
|
||||
@@ -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)
|
||||
|
||||
@@ -14,5 +14,4 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.gltfio" />
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+22
-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,19 +175,11 @@ 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.
|
||||
*
|
||||
* This does not return a bounding box over all FilamentInstance, it's just a straightforward
|
||||
* AAAB that can be determined at load time from the asset data.
|
||||
*/
|
||||
public @NonNull Box getBoundingBox() {
|
||||
float[] box = new float[6];
|
||||
@@ -206,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.
|
||||
*/
|
||||
@@ -243,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.
|
||||
*
|
||||
@@ -279,8 +233,10 @@ public class FilamentAsset {
|
||||
nReleaseSourceData(mNativeObject);
|
||||
}
|
||||
|
||||
public Engine getEngine() { return mEngine; }
|
||||
|
||||
void clearNativeObject() {
|
||||
if (mAnimator != null) mAnimator.clearNativeObject();
|
||||
mPrimaryInstance = null;
|
||||
mNativeObject = 0;
|
||||
}
|
||||
|
||||
@@ -304,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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.27.1
|
||||
VERSION_NAME=1.28.2
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
@@ -19,10 +19,6 @@ org.gradle.jvmargs=-Xmx1536m
|
||||
|
||||
android.useAndroidX=true
|
||||
|
||||
org.gradle.unsafe.configuration-cache=true
|
||||
# TODO: Remove this when we switch to Gradle 7.4
|
||||
org.gradle.unsafe.configuration-cache.max-problems=3
|
||||
|
||||
com.google.android.filament.tools-dir=../../../out/release/filament
|
||||
com.google.android.filament.dist-dir=../out/android-release/filament
|
||||
com.google.android.filament.abis=all
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#Wed Nov 17 10:40:18 PST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.gltf">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
|
||||
+1
-1
@@ -417,7 +417,7 @@ class MainActivity : Activity() {
|
||||
|
||||
// Just for testing purposes, this releases the current model and reloads the default model.
|
||||
inner class DoubleTapListener : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onDoubleTap(e: MotionEvent?): Boolean {
|
||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||
modelViewer.destroyModel()
|
||||
createDefaultRenderables()
|
||||
return super.onDoubleTap(e)
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.hellocam">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.hellotriangle">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.ibl">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.litcube">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.livewallpaper">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-feature android:name="android.software.live_wallpaper" />
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.material_builder">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.multiview">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.pagecurl">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.streamtest">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.textureview">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.textured">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.filament.transparentrendering">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -1 +1 @@
|
||||
25.0.8775105
|
||||
25.1.8937393
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
function print_help {
|
||||
local self_name=$(basename "$0")
|
||||
echo "This tool verifies that every Metal shader in the given Filament material file(s) successfully compiles."
|
||||
echo "Requires the matinfo tool and xcrun to be on the system PATH."
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " $self_name [options] [<material> ...]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -h"
|
||||
echo " Print this help message."
|
||||
echo " -w"
|
||||
echo " Pass the -w flag to the Metal compiler, to disable warnings."
|
||||
echo ""
|
||||
}
|
||||
|
||||
COMP_FLAGS=""
|
||||
while getopts ":hw" opt; do
|
||||
case ${opt} in
|
||||
h)
|
||||
print_help
|
||||
exit 1
|
||||
;;
|
||||
w)
|
||||
COMP_FLAGS="${COMP_FLAGS} -w"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
if [[ "$#" == "0" ]]; then
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Make sure matinfo and xcrun are available.
|
||||
if [[ ! $(command -v matinfo) ]]; then
|
||||
echo "Error: matinfo not on PATH."
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! $(command -v xcrun) ]]; then
|
||||
echo "Error: xcrun not on PATH."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function check_material {
|
||||
local material="$1"
|
||||
|
||||
tmpdir="$(mktemp -d /tmp/check_metal_shaders.XXXXX)"
|
||||
|
||||
# First check that the material is valid.
|
||||
if [[ ! $(matinfo "${material}") ]]; then
|
||||
echo "Invalid material file: ${material}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Checking that Metal shaders compile: ${material}"
|
||||
|
||||
set +e
|
||||
local i=0
|
||||
while true; do
|
||||
# The file must have a .metal extension.
|
||||
metalFile="${tmpdir}/${i}.metal"
|
||||
|
||||
# Extract shader i. matinfo will return a non-zero exit code if the shader doesn't exist,
|
||||
# which means we're finished with this material.
|
||||
matinfo --print-metal=${i} "${material}" > "${metalFile}" 2> /dev/null
|
||||
|
||||
if [[ "$?" -ne 0 ]]; then
|
||||
break
|
||||
fi
|
||||
|
||||
echo "Testing Metal shader ${i}"
|
||||
|
||||
# Attempt to compile the Metal shader.
|
||||
xcrun -sdk macosx metal ${COMP_FLAGS} -c "${metalFile}" -o /dev/null
|
||||
|
||||
if [[ "$?" -ne 0 ]]; then
|
||||
echo "Error compiling Metal shader: ${metalFile}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
((i++))
|
||||
done
|
||||
set -e
|
||||
|
||||
rm -r "${tmpdir}"
|
||||
}
|
||||
|
||||
for material in "$@"
|
||||
do
|
||||
check_material "${material}"
|
||||
done
|
||||
|
||||
+8
-12
File diff suppressed because one or more lines are too long
@@ -1143,7 +1143,7 @@ Where $F(v,h)$ is the Fresnel term of the cloth specular BRDF in equation $\ref{
|
||||
Subsurface scattering is implemented using the wrapped diffuse lighting technique, in its energy conservative form:
|
||||
|
||||
$$\begin{equation}
|
||||
f_{d}(v,h) = \frac{c_{diff}}{\pi}(1 - F(v,h)) \left< \NoL + \frac{w}{(1 + w)^2} \right> \left< c_{subsurface} + \NoL \right>
|
||||
f_{d}(v,h) = \frac{c_{diff}}{\pi}(1 - F(v,h)) \left< \frac{\NoL + w}{(1 + w)^2} \right> \left< c_{subsurface} + \NoL \right>
|
||||
\end{equation}$$
|
||||
|
||||
Where $w$ is a value between 0 and 1 defining by how much the diffuse light should wrap around the terminator. To avoid introducing another parameter, we fix $w = 0.5$. Note that with wrap diffuse lighting, the diffuse term must not be multiplied by $\NoL$. The effect of this cheap
|
||||
|
||||
+127
-79
@@ -86,37 +86,39 @@ counter-increment: h6;margin-right:10px}
|
||||
<a href="#materialdefinitions/format/example" class="level3"><span class="tocNumber">4.1.2 </span>Example</a><br>
|
||||
<a href="#materialdefinitions/materialblock" class="level2"><span class="tocNumber">4.2 </span>Material block</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:name" class="level3"><span class="tocNumber">4.2.1 </span>General: name</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:shadingmodel" class="level3"><span class="tocNumber">4.2.2 </span>General: shadingModel</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:parameters" class="level3"><span class="tocNumber">4.2.3 </span>General: parameters</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:variantfilter" class="level3"><span class="tocNumber">4.2.4 </span>General: variantFilter</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:flipuv" class="level3"><span class="tocNumber">4.2.5 </span>General: flipUV</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:quality" class="level3"><span class="tocNumber">4.2.6 </span>General: quality</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:instanced" class="level3"><span class="tocNumber">4.2.7 </span>General: instanced</a><br>
|
||||
<a href="#materialdefinitions/materialblock/vertexandattributes:requires" class="level3"><span class="tocNumber">4.2.8 </span>Vertex and attributes: requires</a><br>
|
||||
<a href="#materialdefinitions/materialblock/vertexandattributes:variables" class="level3"><span class="tocNumber">4.2.9 </span>Vertex and attributes: variables</a><br>
|
||||
<a href="#materialdefinitions/materialblock/vertexandattributes:vertexdomain" class="level3"><span class="tocNumber">4.2.10 </span>Vertex and attributes: vertexDomain</a><br>
|
||||
<a href="#materialdefinitions/materialblock/vertexandattributes:interpolation" class="level3"><span class="tocNumber">4.2.11 </span>Vertex and attributes: interpolation</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:blending" class="level3"><span class="tocNumber">4.2.12 </span>Blending and transparency: blending</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:postlightingblending" class="level3"><span class="tocNumber">4.2.13 </span>Blending and transparency: postLightingBlending</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:transparency" class="level3"><span class="tocNumber">4.2.14 </span>Blending and transparency: transparency</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:maskthreshold" class="level3"><span class="tocNumber">4.2.15 </span>Blending and transparency: maskThreshold</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:refractionmode" class="level3"><span class="tocNumber">4.2.16 </span>Blending and transparency: refractionMode</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:refractiontype" class="level3"><span class="tocNumber">4.2.17 </span>Blending and transparency: refractionType</a><br>
|
||||
<a href="#materialdefinitions/materialblock/rasterization:culling" class="level3"><span class="tocNumber">4.2.18 </span>Rasterization: culling</a><br>
|
||||
<a href="#materialdefinitions/materialblock/rasterization:colorwrite" class="level3"><span class="tocNumber">4.2.19 </span>Rasterization: colorWrite</a><br>
|
||||
<a href="#materialdefinitions/materialblock/rasterization:depthwrite" class="level3"><span class="tocNumber">4.2.20 </span>Rasterization: depthWrite</a><br>
|
||||
<a href="#materialdefinitions/materialblock/rasterization:depthculling" class="level3"><span class="tocNumber">4.2.21 </span>Rasterization: depthCulling</a><br>
|
||||
<a href="#materialdefinitions/materialblock/rasterization:doublesided" class="level3"><span class="tocNumber">4.2.22 </span>Rasterization: doubleSided</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:reflections" class="level3"><span class="tocNumber">4.2.23 </span>Lighting: reflections</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:shadowmultiplier" class="level3"><span class="tocNumber">4.2.24 </span>Lighting: shadowMultiplier</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:transparentshadow" class="level3"><span class="tocNumber">4.2.25 </span>Lighting: transparentShadow</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:clearcoatiorchange" class="level3"><span class="tocNumber">4.2.26 </span>Lighting: clearCoatIorChange</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:multibounceambientocclusion" class="level3"><span class="tocNumber">4.2.27 </span>Lighting: multiBounceAmbientOcclusion</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:specularambientocclusion" class="level3"><span class="tocNumber">4.2.28 </span>Lighting: specularAmbientOcclusion</a><br>
|
||||
<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasing" class="level3"><span class="tocNumber">4.2.29 </span>Anti-aliasing: specularAntiAliasing</a><br>
|
||||
<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasingvariance" class="level3"><span class="tocNumber">4.2.30 </span>Anti-aliasing: specularAntiAliasingVariance</a><br>
|
||||
<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasingthreshold" class="level3"><span class="tocNumber">4.2.31 </span>Anti-aliasing: specularAntiAliasingThreshold</a><br>
|
||||
<a href="#materialdefinitions/materialblock/shading:customsurfaceshading" class="level3"><span class="tocNumber">4.2.32 </span>Shading: customSurfaceShading</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:featurelevel" class="level3"><span class="tocNumber">4.2.2 </span>General: featureLevel</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:shadingmodel" class="level3"><span class="tocNumber">4.2.3 </span>General: shadingModel</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:parameters" class="level3"><span class="tocNumber">4.2.4 </span>General: parameters</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:variantfilter" class="level3"><span class="tocNumber">4.2.5 </span>General: variantFilter</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:flipuv" class="level3"><span class="tocNumber">4.2.6 </span>General: flipUV</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:quality" class="level3"><span class="tocNumber">4.2.7 </span>General: quality</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:instanced" class="level3"><span class="tocNumber">4.2.8 </span>General: instanced</a><br>
|
||||
<a href="#materialdefinitions/materialblock/general:vertexdomaindevicejittered" class="level3"><span class="tocNumber">4.2.9 </span>General: vertexDomainDeviceJittered</a><br>
|
||||
<a href="#materialdefinitions/materialblock/vertexandattributes:requires" class="level3"><span class="tocNumber">4.2.10 </span>Vertex and attributes: requires</a><br>
|
||||
<a href="#materialdefinitions/materialblock/vertexandattributes:variables" class="level3"><span class="tocNumber">4.2.11 </span>Vertex and attributes: variables</a><br>
|
||||
<a href="#materialdefinitions/materialblock/vertexandattributes:vertexdomain" class="level3"><span class="tocNumber">4.2.12 </span>Vertex and attributes: vertexDomain</a><br>
|
||||
<a href="#materialdefinitions/materialblock/vertexandattributes:interpolation" class="level3"><span class="tocNumber">4.2.13 </span>Vertex and attributes: interpolation</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:blending" class="level3"><span class="tocNumber">4.2.14 </span>Blending and transparency: blending</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:postlightingblending" class="level3"><span class="tocNumber">4.2.15 </span>Blending and transparency: postLightingBlending</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:transparency" class="level3"><span class="tocNumber">4.2.16 </span>Blending and transparency: transparency</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:maskthreshold" class="level3"><span class="tocNumber">4.2.17 </span>Blending and transparency: maskThreshold</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:refractionmode" class="level3"><span class="tocNumber">4.2.18 </span>Blending and transparency: refractionMode</a><br>
|
||||
<a href="#materialdefinitions/materialblock/blendingandtransparency:refractiontype" class="level3"><span class="tocNumber">4.2.19 </span>Blending and transparency: refractionType</a><br>
|
||||
<a href="#materialdefinitions/materialblock/rasterization:culling" class="level3"><span class="tocNumber">4.2.20 </span>Rasterization: culling</a><br>
|
||||
<a href="#materialdefinitions/materialblock/rasterization:colorwrite" class="level3"><span class="tocNumber">4.2.21 </span>Rasterization: colorWrite</a><br>
|
||||
<a href="#materialdefinitions/materialblock/rasterization:depthwrite" class="level3"><span class="tocNumber">4.2.22 </span>Rasterization: depthWrite</a><br>
|
||||
<a href="#materialdefinitions/materialblock/rasterization:depthculling" class="level3"><span class="tocNumber">4.2.23 </span>Rasterization: depthCulling</a><br>
|
||||
<a href="#materialdefinitions/materialblock/rasterization:doublesided" class="level3"><span class="tocNumber">4.2.24 </span>Rasterization: doubleSided</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:reflections" class="level3"><span class="tocNumber">4.2.25 </span>Lighting: reflections</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:shadowmultiplier" class="level3"><span class="tocNumber">4.2.26 </span>Lighting: shadowMultiplier</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:transparentshadow" class="level3"><span class="tocNumber">4.2.27 </span>Lighting: transparentShadow</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:clearcoatiorchange" class="level3"><span class="tocNumber">4.2.28 </span>Lighting: clearCoatIorChange</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:multibounceambientocclusion" class="level3"><span class="tocNumber">4.2.29 </span>Lighting: multiBounceAmbientOcclusion</a><br>
|
||||
<a href="#materialdefinitions/materialblock/lighting:specularambientocclusion" class="level3"><span class="tocNumber">4.2.30 </span>Lighting: specularAmbientOcclusion</a><br>
|
||||
<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasing" class="level3"><span class="tocNumber">4.2.31 </span>Anti-aliasing: specularAntiAliasing</a><br>
|
||||
<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasingvariance" class="level3"><span class="tocNumber">4.2.32 </span>Anti-aliasing: specularAntiAliasingVariance</a><br>
|
||||
<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasingthreshold" class="level3"><span class="tocNumber">4.2.33 </span>Anti-aliasing: specularAntiAliasingThreshold</a><br>
|
||||
<a href="#materialdefinitions/materialblock/shading:customsurfaceshading" class="level3"><span class="tocNumber">4.2.34 </span>Shading: customSurfaceShading</a><br>
|
||||
<a href="#materialdefinitions/vertexblock" class="level2"><span class="tocNumber">4.3 </span>Vertex block</a><br>
|
||||
<a href="#materialdefinitions/vertexblock/materialvertexinputs" class="level3"><span class="tocNumber">4.3.1 </span>Material vertex inputs</a><br>
|
||||
<a href="#materialdefinitions/vertexblock/customvertexattributes" class="level3"><span class="tocNumber">4.3.2 </span>Custom vertex attributes</a><br>
|
||||
@@ -776,7 +778,7 @@ and with (right)</span></center></div></center>
|
||||
|
||||
The <code>bentNormal</code> property defines the average unoccluded direction at a point on the surface. It is
|
||||
used to improve the accuracy of indirect lighting. Bent normals can also improve the quality of
|
||||
specular ambient occlusion (see section <a href="#toc4.2.28">4.2.28</a> about
|
||||
specular ambient occlusion (see section <a href="#toc4.2.30">4.2.30</a> about
|
||||
<code>specularAmbientOcclusion</code>).
|
||||
|
||||
</p><p>
|
||||
@@ -891,7 +893,7 @@ light to bend further away from the initial path.
|
||||
<p></p><p>
|
||||
|
||||
The appearance of a refractive material will greatly depend on the <code>refractionType</code> and
|
||||
<code>refractionMode</code> settings of the material. Refer to section <a href="#toc4.2.17">4.2.17</a> and section <a href="#toc4.2.16">4.2.16</a>
|
||||
<code>refractionMode</code> settings of the material. Refer to section <a href="#toc4.2.19">4.2.19</a> and section <a href="#toc4.2.18">4.2.18</a>
|
||||
for more information.
|
||||
|
||||
</p><p>
|
||||
@@ -1424,7 +1426,36 @@ non-shader data.
|
||||
<span class="line">material {</span>
|
||||
<span class="line"> name : <span class="hljs-string">"Wet pavement"</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="general:shadingmodel"> </a><a class="target" name="materialdefinitions/materialblock/general:shadingmodel"> </a><a class="target" name="toc4.2.2"> </a><h3>General: shadingModel</h3>
|
||||
<a class="target" name="general:featurelevel"> </a><a class="target" name="materialdefinitions/materialblock/general:featurelevel"> </a><a class="target" name="toc4.2.2"> </a><h3>General: featureLevel</h3>
|
||||
<p>
|
||||
|
||||
|
||||
</p><dl><table><tbody><tr valign="top"><td><dt>Type</dt></td><td><dd><p> <code>number</code>
|
||||
|
||||
</p></dd></td></tr><tr valign="top"><td><dt>Value</dt></td><td><dd><p> An integer value, either 1, 2 or 3. Defaults to 1.
|
||||
|
||||
</p></dd></td></tr></tbody></table></dl><div class="table">
|
||||
<table class="table"><tbody><tr><th style="text-align:left"> Feature Level </th><th style="text-align:left"> Guaranteed features </th></tr>
|
||||
<tr><td style="text-align:left"> 1 </td><td style="text-align:left"> 9 textures per material </td></tr>
|
||||
<tr><td style="text-align:left"> 2 </td><td style="text-align:left"> 9 textures per material, cubemap arrays, ESSL 3.10 </td></tr>
|
||||
<tr><td style="text-align:left"> 3 </td><td style="text-align:left"> 12 textures per material, cubemap arrays, ESSL 3.10 </td></tr>
|
||||
</tbody></table><center><div class="tablecaption"><a class="target" name="table_featurelevels"> </a><b style="font-style:normal;">Table 13:</b> Feature levels</div></center></div>
|
||||
|
||||
<p></p><p>
|
||||
|
||||
</p><dl><dt>Description</dt><dd><p> Sets the feature level of the material. Each feature level defines a set of features the
|
||||
material can use. If the material uses a feature not supported by the selected level, <code>matc</code>
|
||||
will generate an error during compilation. A given feature level is guaranteed to support
|
||||
all features of lower feature levels.
|
||||
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> featureLevel : <span class="hljs-number">2</span></span>
|
||||
<span class="line">}</span></code></pre><p>
|
||||
|
||||
</p><dl><table><tbody><tr valign="top"><td><dt>Bugs</dt></td><td><dd><p> <code>matc</code> doesn't verify that a material is not using features above its selected feature level.
|
||||
|
||||
</p></dd></td></tr></tbody></table></dl><p></p>
|
||||
<a class="target" name="general:shadingmodel"> </a><a class="target" name="materialdefinitions/materialblock/general:shadingmodel"> </a><a class="target" name="toc4.2.3"> </a><h3>General: shadingModel</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1441,7 +1472,7 @@ non-shader data.
|
||||
<span class="line">material {</span>
|
||||
<span class="line"> shadingModel : <span class="hljs-string">"subsurface"</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="general:parameters"> </a><a class="target" name="materialdefinitions/materialblock/general:parameters"> </a><a class="target" name="toc4.2.3"> </a><h3>General: parameters</h3>
|
||||
<a class="target" name="general:parameters"> </a><a class="target" name="materialdefinitions/materialblock/general:parameters"> </a><a class="target" name="toc4.2.4"> </a><h3>General: parameters</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1451,7 +1482,7 @@ non-shader data.
|
||||
name must be a valid GLSL identifier. Entries also have an optional <code>precision</code>, which can be
|
||||
one of <code>default</code> (best precision for the platform, typically <code>high</code> on desktop, <code>medium</code> on
|
||||
mobile), <code>low</code>, <code>medium</code>, <code>high</code>. The type must be one of the types described in
|
||||
<a href="#table_materialparamstypes">table 13</a>.
|
||||
<a href="#table_materialparamstypes">table 14</a>.
|
||||
|
||||
</p></dd></dl><div class="table">
|
||||
<table class="table"><tbody><tr><th style="text-align:left"> Type </th><th style="text-align:left"> Description </th></tr>
|
||||
@@ -1477,7 +1508,7 @@ non-shader data.
|
||||
<tr><td style="text-align:left"> sampler2dArray </td><td style="text-align:left"> Array of 2D textures </td></tr>
|
||||
<tr><td style="text-align:left"> samplerExternal </td><td style="text-align:left"> External texture (platform-specific) </td></tr>
|
||||
<tr><td style="text-align:left"> samplerCubemap </td><td style="text-align:left"> Cubemap texture </td></tr>
|
||||
</tbody></table><center><div class="tablecaption"><a class="target" name="table_materialparamstypes"> </a><b style="font-style:normal;">Table 13:</b> Material parameter types</div></center></div>
|
||||
</tbody></table><center><div class="tablecaption"><a class="target" name="table_materialparamstypes"> </a><b style="font-style:normal;">Table 14:</b> Material parameter types</div></center></div>
|
||||
|
||||
<p></p><p>
|
||||
|
||||
@@ -1533,7 +1564,7 @@ non-shader data.
|
||||
<span class="line"> material.reflectance = materialParams.metallicReflectance.y;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="general:variantfilter"> </a><a class="target" name="materialdefinitions/materialblock/general:variantfilter"> </a><a class="target" name="toc4.2.4"> </a><h3>General: variantFilter</h3>
|
||||
<a class="target" name="general:variantfilter"> </a><a class="target" name="materialdefinitions/materialblock/general:variantfilter"> </a><a class="target" name="toc4.2.5"> </a><h3>General: variantFilter</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1575,7 +1606,7 @@ non-shader data.
|
||||
<span class="line"> blending : transparent,</span>
|
||||
<span class="line"> variantFilter : [ skinning ]</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="general:flipuv"> </a><a class="target" name="materialdefinitions/materialblock/general:flipuv"> </a><a class="target" name="toc4.2.5"> </a><h3>General: flipUV</h3>
|
||||
<a class="target" name="general:flipuv"> </a><a class="target" name="materialdefinitions/materialblock/general:flipuv"> </a><a class="target" name="toc4.2.6"> </a><h3>General: flipUV</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1590,7 +1621,7 @@ non-shader data.
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> flipUV : <span class="hljs-literal">false</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="general:quality"> </a><a class="target" name="materialdefinitions/materialblock/general:quality"> </a><a class="target" name="toc4.2.6"> </a><h3>General: quality</h3>
|
||||
<a class="target" name="general:quality"> </a><a class="target" name="materialdefinitions/materialblock/general:quality"> </a><a class="target" name="toc4.2.7"> </a><h3>General: quality</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1606,7 +1637,7 @@ non-shader data.
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> quality : default</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="general:instanced"> </a><a class="target" name="materialdefinitions/materialblock/general:instanced"> </a><a class="target" name="toc4.2.7"> </a><h3>General: instanced</h3>
|
||||
<a class="target" name="general:instanced"> </a><a class="target" name="materialdefinitions/materialblock/general:instanced"> </a><a class="target" name="toc4.2.8"> </a><h3>General: instanced</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1623,7 +1654,26 @@ non-shader data.
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> instanced : <span class="hljs-literal">true</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="vertexandattributes:requires"> </a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:requires"> </a><a class="target" name="toc4.2.8"> </a><h3>Vertex and attributes: requires</h3>
|
||||
<a class="target" name="general:vertexdomaindevicejittered"> </a><a class="target" name="materialdefinitions/materialblock/general:vertexdomaindevicejittered"> </a><a class="target" name="toc4.2.9"> </a><h3>General: vertexDomainDeviceJittered</h3>
|
||||
<p>
|
||||
|
||||
|
||||
</p><dl><dt>Type</dt><dd><p> <code>boolean</code>
|
||||
|
||||
</p></dd><dt>Value</dt><dd><p> <code>true</code> or <code>false</code>. Defaults to <code>false</code>.
|
||||
|
||||
</p></dd><dt>Description</dt><dd><p> Only meaningful for <code>vertexDomain:Device</code> materials, this parameter specifies whether the
|
||||
filament clip-space transforms need to be applied or not, which affects TAA and guard bands.
|
||||
Generally it needs to be applied because by definition <code>vertexDomain:Device</code> materials
|
||||
vertices are not transformed and used <em class="asterisk">as is</em>.
|
||||
However, if the vertex shader uses for instance <code>getViewFromClipMatrix()</code> (or other
|
||||
matrices based on the projection), the clip-space transform is already applied.
|
||||
Setting this parameter incorrectly can prevent TAA or the guard bands to work correctly.
|
||||
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> vertexDomainDeviceJittered : <span class="hljs-literal">true</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="vertexandattributes:requires"> </a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:requires"> </a><a class="target" name="toc4.2.10"> </a><h3>Vertex and attributes: requires</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1658,7 +1708,7 @@ non-shader data.
|
||||
<span class="line"> material.baseColor.rgb *= getCustom0().rgb;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="vertexandattributes:variables"> </a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:variables"> </a><a class="target" name="toc4.2.9"> </a><h3>Vertex and attributes: variables</h3>
|
||||
<a class="target" name="vertexandattributes:variables"> </a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:variables"> </a><a class="target" name="toc4.2.11"> </a><h3>Vertex and attributes: variables</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1705,7 +1755,7 @@ non-shader data.
|
||||
<span class="line"> material.eyeDirection.xyz = mulMat3x3Float3(getWorldFromViewMatrix(), u);</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="vertexandattributes:vertexdomain"> </a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:vertexdomain"> </a><a class="target" name="toc4.2.10"> </a><h3>Vertex and attributes: vertexDomain</h3>
|
||||
<a class="target" name="vertexandattributes:vertexdomain"> </a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:vertexdomain"> </a><a class="target" name="toc4.2.12"> </a><h3>Vertex and attributes: vertexDomain</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1734,7 +1784,7 @@ non-shader data.
|
||||
<p></p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> vertexDomain : device</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="vertexandattributes:interpolation"> </a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:interpolation"> </a><a class="target" name="toc4.2.11"> </a><h3>Vertex and attributes: interpolation</h3>
|
||||
<a class="target" name="vertexandattributes:interpolation"> </a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:interpolation"> </a><a class="target" name="toc4.2.13"> </a><h3>Vertex and attributes: interpolation</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1750,7 +1800,7 @@ non-shader data.
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> interpolation : flat</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="blendingandtransparency:blending"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:blending"> </a><a class="target" name="toc4.2.12"> </a><h3>Blending and transparency: blending</h3>
|
||||
<a class="target" name="blendingandtransparency:blending"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:blending"> </a><a class="target" name="toc4.2.14"> </a><h3>Blending and transparency: blending</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1791,7 +1841,7 @@ non-shader data.
|
||||
<p></p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> blending : transparent</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="blendingandtransparency:postlightingblending"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:postlightingblending"> </a><a class="target" name="toc4.2.13"> </a><h3>Blending and transparency: postLightingBlending</h3>
|
||||
<a class="target" name="blendingandtransparency:postlightingblending"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:postlightingblending"> </a><a class="target" name="toc4.2.15"> </a><h3>Blending and transparency: postLightingBlending</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1821,7 +1871,7 @@ non-shader data.
|
||||
<p></p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> postLightingBlending : add</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="blendingandtransparency:transparency"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:transparency"> </a><a class="target" name="toc4.2.14"> </a><h3>Blending and transparency: transparency</h3>
|
||||
<a class="target" name="blendingandtransparency:transparency"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:transparency"> </a><a class="target" name="toc4.2.16"> </a><h3>Blending and transparency: transparency</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1868,7 +1918,7 @@ and correctly sorted</span></center></div></center>
|
||||
and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
|
||||
<p></p>
|
||||
<a class="target" name="blendingandtransparency:maskthreshold"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:maskthreshold"> </a><a class="target" name="toc4.2.15"> </a><h3>Blending and transparency: maskThreshold</h3>
|
||||
<a class="target" name="blendingandtransparency:maskthreshold"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:maskthreshold"> </a><a class="target" name="toc4.2.17"> </a><h3>Blending and transparency: maskThreshold</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1885,7 +1935,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
<span class="line"> blending : masked,</span>
|
||||
<span class="line"> maskThreshold : <span class="hljs-number">0.5</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="blendingandtransparency:refractionmode"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:refractionmode"> </a><a class="target" name="toc4.2.16"> </a><h3>Blending and transparency: refractionMode</h3>
|
||||
<a class="target" name="blendingandtransparency:refractionmode"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:refractionmode"> </a><a class="target" name="toc4.2.18"> </a><h3>Blending and transparency: refractionMode</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1906,7 +1956,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> refractionMode : cubemap,</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="blendingandtransparency:refractiontype"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:refractiontype"> </a><a class="target" name="toc4.2.17"> </a><h3>Blending and transparency: refractionType</h3>
|
||||
<a class="target" name="blendingandtransparency:refractiontype"> </a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:refractiontype"> </a><a class="target" name="toc4.2.19"> </a><h3>Blending and transparency: refractionType</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1925,7 +1975,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
<span class="line"> refractionMode : cubemap,</span>
|
||||
<span class="line"> refractionType : thin,</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="rasterization:culling"> </a><a class="target" name="materialdefinitions/materialblock/rasterization:culling"> </a><a class="target" name="toc4.2.18"> </a><h3>Rasterization: culling</h3>
|
||||
<a class="target" name="rasterization:culling"> </a><a class="target" name="materialdefinitions/materialblock/rasterization:culling"> </a><a class="target" name="toc4.2.20"> </a><h3>Rasterization: culling</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1939,7 +1989,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
</p></dd></td></tr></tbody></table></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> culling : none</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="rasterization:colorwrite"> </a><a class="target" name="materialdefinitions/materialblock/rasterization:colorwrite"> </a><a class="target" name="toc4.2.19"> </a><h3>Rasterization: colorWrite</h3>
|
||||
<a class="target" name="rasterization:colorwrite"> </a><a class="target" name="materialdefinitions/materialblock/rasterization:colorwrite"> </a><a class="target" name="toc4.2.21"> </a><h3>Rasterization: colorWrite</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1952,7 +2002,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
</p></dd></td></tr></tbody></table></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> colorWrite : <span class="hljs-literal">false</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="rasterization:depthwrite"> </a><a class="target" name="materialdefinitions/materialblock/rasterization:depthwrite"> </a><a class="target" name="toc4.2.20"> </a><h3>Rasterization: depthWrite</h3>
|
||||
<a class="target" name="rasterization:depthwrite"> </a><a class="target" name="materialdefinitions/materialblock/rasterization:depthwrite"> </a><a class="target" name="toc4.2.22"> </a><h3>Rasterization: depthWrite</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1965,7 +2015,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
</p></dd></td></tr></tbody></table></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> depthWrite : <span class="hljs-literal">false</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="rasterization:depthculling"> </a><a class="target" name="materialdefinitions/materialblock/rasterization:depthculling"> </a><a class="target" name="toc4.2.21"> </a><h3>Rasterization: depthCulling</h3>
|
||||
<a class="target" name="rasterization:depthculling"> </a><a class="target" name="materialdefinitions/materialblock/rasterization:depthculling"> </a><a class="target" name="toc4.2.23"> </a><h3>Rasterization: depthCulling</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1979,7 +2029,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> depthCulling : <span class="hljs-literal">false</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="rasterization:doublesided"> </a><a class="target" name="materialdefinitions/materialblock/rasterization:doublesided"> </a><a class="target" name="toc4.2.22"> </a><h3>Rasterization: doubleSided</h3>
|
||||
<a class="target" name="rasterization:doublesided"> </a><a class="target" name="materialdefinitions/materialblock/rasterization:doublesided"> </a><a class="target" name="toc4.2.24"> </a><h3>Rasterization: doubleSided</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -1987,26 +2037,24 @@ and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
|
||||
</p></dd><dt>Value</dt><dd><p> <code>true</code> or <code>false</code>. Defaults to <code>false</code>.
|
||||
|
||||
</p></dd><dt>Description</dt><dd><p> Only available in the <code>unlit</code> shading model. If this property is enabled, the final color
|
||||
computed by the material is multiplied by the shadowing factor (or visibility). This allows to
|
||||
create transparent shadow-receiving objects (for instance an invisible ground plane in AR).
|
||||
This is only supported with shadows from directional lights.
|
||||
</p></dd><dt>Description</dt><dd><p> Enables two-sided rendering and its capability to be toggled at run time. When set to <code>true</code>,
|
||||
<code>culling</code> is automatically set to <code>none</code>; if the triangle is back-facing, the triangle's
|
||||
normal is flipped to become front-facing. When explicitly set to <code>false</code>, this allows the
|
||||
double-sidedness to be toggled at run time.
|
||||
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> name : <span class="hljs-string">"Invisible shadow plane"</span>,</span>
|
||||
<span class="line"> shadingModel : unlit,</span>
|
||||
<span class="line"> shadowMultiplier : <span class="hljs-literal">true</span>,</span>
|
||||
<span class="line"> blending : transparent</span>
|
||||
<span class="line"> name : <span class="hljs-string">"Double sided material"</span>,</span>
|
||||
<span class="line"> shadingModel : lit,</span>
|
||||
<span class="line"> doubleSided : <span class="hljs-literal">true</span></span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line">fragment {</span>
|
||||
<span class="line"> void material(inout MaterialInputs material) {</span>
|
||||
<span class="line"> prepareMaterial(material);</span>
|
||||
<span class="line"> <span class="hljs-comment">// baseColor defines the color and opacity of the final shadow</span></span>
|
||||
<span class="line"> material.baseColor = vec4(0.0, 0.0, 0.0, 0.7);</span>
|
||||
<span class="line"> material.baseColor = materialParams.albedo;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="lighting:reflections"> </a><a class="target" name="materialdefinitions/materialblock/lighting:reflections"> </a><a class="target" name="toc4.2.23"> </a><h3>Lighting: reflections</h3>
|
||||
<a class="target" name="lighting:reflections"> </a><a class="target" name="materialdefinitions/materialblock/lighting:reflections"> </a><a class="target" name="toc4.2.25"> </a><h3>Lighting: reflections</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -2023,7 +2071,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
<span class="line"> name : <span class="hljs-string">"Glossy metal"</span>,</span>
|
||||
<span class="line"> reflections : screenspace</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="lighting:shadowmultiplier"> </a><a class="target" name="materialdefinitions/materialblock/lighting:shadowmultiplier"> </a><a class="target" name="toc4.2.24"> </a><h3>Lighting: shadowMultiplier</h3>
|
||||
<a class="target" name="lighting:shadowmultiplier"> </a><a class="target" name="materialdefinitions/materialblock/lighting:shadowmultiplier"> </a><a class="target" name="toc4.2.26"> </a><h3>Lighting: shadowMultiplier</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -2050,7 +2098,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
|
||||
<span class="line"> material.baseColor = vec4(0.0, 0.0, 0.0, 0.7);</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="lighting:transparentshadow"> </a><a class="target" name="materialdefinitions/materialblock/lighting:transparentshadow"> </a><a class="target" name="toc4.2.25"> </a><h3>Lighting: transparentShadow</h3>
|
||||
<a class="target" name="lighting:transparentshadow"> </a><a class="target" name="materialdefinitions/materialblock/lighting:transparentshadow"> </a><a class="target" name="toc4.2.27"> </a><h3>Lighting: transparentShadow</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -2084,7 +2132,7 @@ radius of 4. Model <a href="https://sketchfab.com/3d-models/bottle-of-water-48fd
|
||||
by <a href="https://sketchfab.com/person-x">T-Art</a>.</span></center></div></center>
|
||||
|
||||
<p></p>
|
||||
<a class="target" name="lighting:clearcoatiorchange"> </a><a class="target" name="materialdefinitions/materialblock/lighting:clearcoatiorchange"> </a><a class="target" name="toc4.2.26"> </a><h3>Lighting: clearCoatIorChange</h3>
|
||||
<a class="target" name="lighting:clearcoatiorchange"> </a><a class="target" name="materialdefinitions/materialblock/lighting:clearcoatiorchange"> </a><a class="target" name="toc4.2.28"> </a><h3>Lighting: clearCoatIorChange</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -2106,7 +2154,7 @@ with <code>clearCoatIorChange</code> enabled (left) and disabled
|
||||
(right).</span></center></div></center>
|
||||
|
||||
<p></p>
|
||||
<a class="target" name="lighting:multibounceambientocclusion"> </a><a class="target" name="materialdefinitions/materialblock/lighting:multibounceambientocclusion"> </a><a class="target" name="toc4.2.27"> </a><h3>Lighting: multiBounceAmbientOcclusion</h3>
|
||||
<a class="target" name="lighting:multibounceambientocclusion"> </a><a class="target" name="materialdefinitions/materialblock/lighting:multibounceambientocclusion"> </a><a class="target" name="toc4.2.29"> </a><h3>Lighting: multiBounceAmbientOcclusion</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -2135,7 +2183,7 @@ occclusion enabled (left) and disabled (right).</span></center></div></center>
|
||||
occclusion enabled and disabled.</span></center></div></center>
|
||||
|
||||
<p></p>
|
||||
<a class="target" name="lighting:specularambientocclusion"> </a><a class="target" name="materialdefinitions/materialblock/lighting:specularambientocclusion"> </a><a class="target" name="toc4.2.28"> </a><h3>Lighting: specularAmbientOcclusion</h3>
|
||||
<a class="target" name="lighting:specularambientocclusion"> </a><a class="target" name="materialdefinitions/materialblock/lighting:specularambientocclusion"> </a><a class="target" name="toc4.2.30"> </a><h3>Lighting: specularAmbientOcclusion</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -2161,7 +2209,7 @@ occclusion enabled and disabled.</span></center></div></center>
|
||||
particularly visible under the hose.</span></center></div></center>
|
||||
|
||||
<p></p>
|
||||
<a class="target" name="anti-aliasing:specularantialiasing"> </a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasing"> </a><a class="target" name="toc4.2.29"> </a><h3>Anti-aliasing: specularAntiAliasing</h3>
|
||||
<a class="target" name="anti-aliasing:specularantialiasing"> </a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasing"> </a><a class="target" name="toc4.2.31"> </a><h3>Anti-aliasing: specularAntiAliasing</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -2178,7 +2226,7 @@ particularly visible under the hose.</span></center></div></center>
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> specularAntiAliasing : <span class="hljs-literal">true</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="anti-aliasing:specularantialiasingvariance"> </a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasingvariance"> </a><a class="target" name="toc4.2.30"> </a><h3>Anti-aliasing: specularAntiAliasingVariance</h3>
|
||||
<a class="target" name="anti-aliasing:specularantialiasingvariance"> </a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasingvariance"> </a><a class="target" name="toc4.2.32"> </a><h3>Anti-aliasing: specularAntiAliasingVariance</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -2193,7 +2241,7 @@ particularly visible under the hose.</span></center></div></center>
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> specularAntiAliasingVariance : <span class="hljs-number">0.2</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="anti-aliasing:specularantialiasingthreshold"> </a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasingthreshold"> </a><a class="target" name="toc4.2.31"> </a><h3>Anti-aliasing: specularAntiAliasingThreshold</h3>
|
||||
<a class="target" name="anti-aliasing:specularantialiasingthreshold"> </a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasingthreshold"> </a><a class="target" name="toc4.2.33"> </a><h3>Anti-aliasing: specularAntiAliasingThreshold</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -2207,7 +2255,7 @@ particularly visible under the hose.</span></center></div></center>
|
||||
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
|
||||
<span class="line"> specularAntiAliasingThreshold : <span class="hljs-number">0.1</span></span>
|
||||
<span class="line">}</span></code></pre>
|
||||
<a class="target" name="shading:customsurfaceshading"> </a><a class="target" name="materialdefinitions/materialblock/shading:customsurfaceshading"> </a><a class="target" name="toc4.2.32"> </a><h3>Shading: customSurfaceShading</h3>
|
||||
<a class="target" name="shading:customsurfaceshading"> </a><a class="target" name="materialdefinitions/materialblock/shading:customsurfaceshading"> </a><a class="target" name="toc4.2.34"> </a><h3>Shading: customSurfaceShading</h3>
|
||||
<p>
|
||||
|
||||
|
||||
@@ -2271,7 +2319,7 @@ APIs listed in the <a href="#shaderpublicapis">Shader public APIs</a> section.
|
||||
<span class="line"> float3 worldNormal; <span class="hljs-comment">// only if the shading model is not unlit</span></span>
|
||||
<span class="line"> float4 worldPosition; <span class="hljs-comment">// always available (see note below about world-space)</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-type">mat4</span> clipSpaceTransform; <span class="hljs-comment">// default: identity, transforms the clip-space position</span></span>
|
||||
<span class="line"> <span class="hljs-type">mat4</span> clipSpaceTransform; <span class="hljs-comment">// default: identity, transforms the clip-space position, only available for `vertexDomain:device`</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// variable* names are replaced with actual names</span></span>
|
||||
<span class="line"> float4 variable0; <span class="hljs-comment">// if 1 or more variables is defined</span></span>
|
||||
@@ -2766,7 +2814,7 @@ source material definition file.
|
||||
<p>
|
||||
|
||||
|
||||
The command line flags relevant to application development are described in <a href="#table_matcflags">table 14</a>.
|
||||
The command line flags relevant to application development are described in <a href="#table_matcflags">table 15</a>.
|
||||
</p><div class="table">
|
||||
<table class="table"><tbody><tr><th style="text-align:right"> Flag </th><th style="text-align:center"> Value </th><th style="text-align:left"> Usage </th></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">-o</strong>, <strong class="asterisk">—output</strong> </td><td style="text-align:center"> [path] </td><td style="text-align:left"> Specify the output file path </td></tr>
|
||||
@@ -2775,7 +2823,7 @@ The command line flags relevant to application development are described in <a h
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">-S</strong>, <strong class="asterisk">—optimize-size</strong> </td><td style="text-align:center"> N/A </td><td style="text-align:left"> Optimize compiled material for size instead of just performance </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">-r</strong>, <strong class="asterisk">—reflect</strong> </td><td style="text-align:center"> parameters </td><td style="text-align:left"> Outputs the specified metadata as JSON </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">-v</strong>, <strong class="asterisk">—variant-filter</strong> </td><td style="text-align:center"> [variant] </td><td style="text-align:left"> Filters out the specified, comma-separated variants </td></tr>
|
||||
</tbody></table><center><div class="tablecaption"><a class="target" name="table_matcflags"> </a><b style="font-style:normal;">Table 14:</b> List of <code>matc</code> flags</div></center></div>
|
||||
</tbody></table><center><div class="tablecaption"><a class="target" name="table_matcflags"> </a><b style="font-style:normal;">Table 15:</b> List of <code>matc</code> flags</div></center></div>
|
||||
|
||||
<p></p><p>
|
||||
|
||||
|
||||
+11
-12
@@ -938,12 +938,13 @@ Type
|
||||
: `number`
|
||||
|
||||
Value
|
||||
: An integer value, either 1 or 2. Defaults to 1.
|
||||
: An integer value, either 1, 2 or 3. Defaults to 1.
|
||||
|
||||
Feature Level | Guaranteed features
|
||||
:----------------------|:---------------------------------
|
||||
1 | 9 textures per material
|
||||
2 | 12 textures per material, cubemap arrays, ESSL 3.10
|
||||
2 | 9 textures per material, cubemap arrays, ESSL 3.10
|
||||
3 | 12 textures per material, cubemap arrays, ESSL 3.10
|
||||
[Table [featureLevels]: Feature levels]
|
||||
|
||||
Description
|
||||
@@ -1581,24 +1582,22 @@ Value
|
||||
: `true` or `false`. Defaults to `false`.
|
||||
|
||||
Description
|
||||
: Only available in the `unlit` shading model. If this property is enabled, the final color
|
||||
computed by the material is multiplied by the shadowing factor (or visibility). This allows to
|
||||
create transparent shadow-receiving objects (for instance an invisible ground plane in AR).
|
||||
This is only supported with shadows from directional lights.
|
||||
: Enables two-sided rendering and its capability to be toggled at run time. When set to `true`,
|
||||
`culling` is automatically set to `none`; if the triangle is back-facing, the triangle's
|
||||
normal is flipped to become front-facing. When explicitly set to `false`, this allows the
|
||||
double-sidedness to be toggled at run time.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
name : "Invisible shadow plane",
|
||||
shadingModel : unlit,
|
||||
shadowMultiplier : true,
|
||||
blending : transparent
|
||||
name : "Double sided material",
|
||||
shadingModel : lit,
|
||||
doubleSided : true
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
// baseColor defines the color and opacity of the final shadow
|
||||
material.baseColor = vec4(0.0, 0.0, 0.0, 0.7);
|
||||
material.baseColor = materialParams.albedo;
|
||||
}
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -48,6 +48,7 @@ set(PUBLIC_HDRS
|
||||
)
|
||||
|
||||
set(SRCS
|
||||
src/AtlasAllocator.cpp
|
||||
src/Box.cpp
|
||||
src/BufferObject.cpp
|
||||
src/Camera.cpp
|
||||
@@ -72,6 +73,7 @@ set(SRCS
|
||||
src/MaterialParser.cpp
|
||||
src/MorphTargetBuffer.cpp
|
||||
src/PerViewUniforms.cpp
|
||||
src/PerShadowMapUniforms.cpp
|
||||
src/PostProcessManager.cpp
|
||||
src/RenderPass.cpp
|
||||
src/RenderPrimitive.cpp
|
||||
@@ -145,6 +147,7 @@ set(PRIVATE_HDRS
|
||||
src/Intersections.h
|
||||
src/MaterialParser.h
|
||||
src/PerViewUniforms.h
|
||||
src/PerShadowMapUniforms.h
|
||||
src/PIDController.h
|
||||
src/PostProcessManager.h
|
||||
src/RendererUtils.h
|
||||
@@ -198,7 +201,7 @@ set(PRIVATE_HDRS
|
||||
src/materials/fsr/ffx_a.h
|
||||
src/materials/fsr/ffx_fsr1.h
|
||||
src/materials/fsr/ffx_fsr1_mobile.fs
|
||||
src/upcast.h
|
||||
src/downcast.h
|
||||
)
|
||||
|
||||
set(MATERIAL_SRCS
|
||||
|
||||
@@ -450,3 +450,23 @@ if (APPLE AND NOT IOS)
|
||||
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Compute tests
|
||||
|
||||
if (NOT IOS AND NOT WEBGL)
|
||||
|
||||
add_executable(compute_test
|
||||
test/ComputeTest.cpp
|
||||
test/Arguments.cpp
|
||||
test/test_ComputeBasic.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(compute_test PRIVATE
|
||||
backend
|
||||
getopt
|
||||
gtest
|
||||
)
|
||||
|
||||
set_target_properties(compute_test PROPERTIES FOLDER Tests)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -48,17 +48,19 @@ static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8;
|
||||
|
||||
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_SAMPLER_COUNT = 32; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3.
|
||||
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
|
||||
static constexpr size_t MAX_SSBO_COUNT = 4; // This is guaranteed by OpenGL ES.
|
||||
|
||||
// Per feature level caps
|
||||
// Use (int)FeatureLevel to index this array
|
||||
static constexpr struct {
|
||||
const size_t MAX_VERTEX_SAMPLER_COUNT;
|
||||
const size_t MAX_FRAGMENT_SAMPLER_COUNT;
|
||||
} FEATURE_LEVEL_CAPS[3] = {
|
||||
} FEATURE_LEVEL_CAPS[4] = {
|
||||
{ 0, 0 }, // do not use
|
||||
{ 16, 16 }, // guaranteed by OpenGL ES, Vulkan and Metal
|
||||
{ 16, 16 }, // guaranteed by OpenGL ES, Vulkan and Metal
|
||||
{ 31, 31 }, // guaranteed by Metal
|
||||
};
|
||||
|
||||
@@ -74,7 +76,8 @@ static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guarantee
|
||||
*/
|
||||
enum class FeatureLevel : uint8_t {
|
||||
FEATURE_LEVEL_1 = 1, //!< OpenGL ES 3.0 features (default)
|
||||
FEATURE_LEVEL_2 //!< OpenGL ES 3.1 features + 31 textures units + cubemap arrays
|
||||
FEATURE_LEVEL_2, //!< OpenGL ES 3.1 features + 16 textures units + cubemap arrays
|
||||
FEATURE_LEVEL_3 //!< OpenGL ES 3.1 features + 31 textures units + cubemap arrays
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -217,8 +220,7 @@ enum class PrimitiveType : uint8_t {
|
||||
LINES = 1, //!< lines
|
||||
LINE_STRIP = 3, //!< line strip
|
||||
TRIANGLES = 4, //!< triangles
|
||||
TRIANGLE_STRIP = 5, //!< triangle strip
|
||||
NONE = 0xFF
|
||||
TRIANGLE_STRIP = 5 //!< triangle strip
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -311,7 +313,8 @@ enum class ElementType : uint8_t {
|
||||
//! Buffer object binding type
|
||||
enum class BufferObjectBinding : uint8_t {
|
||||
VERTEX,
|
||||
UNIFORM
|
||||
UNIFORM,
|
||||
SHADER_STORAGE
|
||||
};
|
||||
|
||||
//! Face culling Mode
|
||||
@@ -895,7 +898,8 @@ struct RasterState {
|
||||
|
||||
enum class ShaderStage : uint8_t {
|
||||
VERTEX = 0,
|
||||
FRAGMENT = 1
|
||||
FRAGMENT = 1,
|
||||
COMPUTE = 2
|
||||
};
|
||||
|
||||
static constexpr size_t PIPELINE_STAGE_COUNT = 2;
|
||||
@@ -903,7 +907,8 @@ enum class ShaderStageFlags : uint8_t {
|
||||
NONE = 0,
|
||||
VERTEX = 0x1,
|
||||
FRAGMENT = 0x2,
|
||||
ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT
|
||||
COMPUTE = 0x4,
|
||||
ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE
|
||||
};
|
||||
|
||||
static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept {
|
||||
@@ -912,6 +917,8 @@ static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage t
|
||||
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX));
|
||||
case ShaderStage::FRAGMENT:
|
||||
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::FRAGMENT));
|
||||
case ShaderStage::COMPUTE:
|
||||
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::COMPUTE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace filament::backend {
|
||||
class Program {
|
||||
public:
|
||||
|
||||
static constexpr size_t SHADER_TYPE_COUNT = 2;
|
||||
static constexpr size_t SHADER_TYPE_COUNT = 3;
|
||||
static constexpr size_t UNIFORM_BINDING_COUNT = CONFIG_UNIFORM_BINDING_COUNT;
|
||||
static constexpr size_t SAMPLER_BINDING_COUNT = CONFIG_SAMPLER_BINDING_COUNT;
|
||||
|
||||
|
||||
@@ -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
|
||||
* -----------------------
|
||||
@@ -406,12 +409,17 @@ DECL_DRIVER_API_N(bindUniformBuffer,
|
||||
uint32_t, index,
|
||||
backend::BufferObjectHandle, ubh)
|
||||
|
||||
DECL_DRIVER_API_N(bindUniformBufferRange,
|
||||
DECL_DRIVER_API_N(bindBufferRange,
|
||||
BufferObjectBinding, bindingType,
|
||||
uint32_t, index,
|
||||
backend::BufferObjectHandle, ubh,
|
||||
uint32_t, offset,
|
||||
uint32_t, size)
|
||||
|
||||
DECL_DRIVER_API_N(unbindBuffer,
|
||||
BufferObjectBinding, bindingType,
|
||||
uint32_t, index)
|
||||
|
||||
DECL_DRIVER_API_N(bindSamplers,
|
||||
uint32_t, index,
|
||||
backend::SamplerGroupHandle, sbh)
|
||||
@@ -444,6 +452,12 @@ DECL_DRIVER_API_N(readPixels,
|
||||
uint32_t, height,
|
||||
backend::PixelBufferDescriptor&&, data)
|
||||
|
||||
DECL_DRIVER_API_N(readBufferSubData,
|
||||
backend::BufferObjectHandle, src,
|
||||
uint32_t, offset,
|
||||
uint32_t, size,
|
||||
backend::BufferDescriptor&&, data)
|
||||
|
||||
/*
|
||||
* Rendering operations
|
||||
* --------------------
|
||||
@@ -462,6 +476,11 @@ DECL_DRIVER_API_N(draw,
|
||||
backend::RenderPrimitiveHandle, rph,
|
||||
uint32_t, instanceCount)
|
||||
|
||||
DECL_DRIVER_API_N(dispatchCompute,
|
||||
backend::ProgramHandle, program,
|
||||
math::uint3, workGroupCount)
|
||||
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#undef EXPAND
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -42,7 +42,9 @@
|
||||
#include "vulkan/PlatformVkCocoa.h"
|
||||
#endif
|
||||
#elif defined(__linux__)
|
||||
#if defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
#if defined(FILAMENT_SUPPORTS_GGP)
|
||||
#include "vulkan/PlatformVkLinuxGGP.h"
|
||||
#elif defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
#if defined (FILAMENT_DRIVER_SUPPORTS_VULKAN)
|
||||
#include "vulkan/PlatformVkLinuxWayland.h"
|
||||
#endif
|
||||
@@ -120,7 +122,9 @@ DefaultPlatform* DefaultPlatform::create(Backend* backend) noexcept {
|
||||
#elif defined(IOS)
|
||||
return new PlatformVkCocoaTouch();
|
||||
#elif defined(__linux__)
|
||||
#if defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
#if defined(FILAMENT_SUPPORTS_GGP)
|
||||
return new PlatformVkLinuxGGP();
|
||||
#elif defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
return new PlatformVkLinuxWayland();
|
||||
#elif defined(FILAMENT_SUPPORTS_X11)
|
||||
return new PlatformVkLinuxX11();
|
||||
|
||||
@@ -91,12 +91,14 @@ blitterFrag(VertexOut in [[stage_in]],
|
||||
{
|
||||
FragmentOut out = {};
|
||||
|
||||
#if defined(BLIT_COLOR) || defined(BLIT_DEPTH)
|
||||
// These coordinates match the Vulkan vkCmdBlitImage spec:
|
||||
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdBlitImage.html
|
||||
float2 uvbase = in.position.xy; // unnormalized coordinates at center of texel: (1.5, 2.5, etc)
|
||||
float2 uvoffset = uvbase - args->dstOffset;
|
||||
float2 uvscaled = uvoffset * args->scale;
|
||||
float2 uv = uvscaled + args->srcOffset;
|
||||
#endif
|
||||
|
||||
#ifdef BLIT_COLOR
|
||||
#ifdef MSAA_COLOR_SOURCE
|
||||
|
||||
@@ -24,13 +24,18 @@
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
#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;
|
||||
@@ -57,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);
|
||||
|
||||
@@ -80,7 +88,122 @@ private:
|
||||
MetalContext& mContext;
|
||||
};
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
template <typename TYPE>
|
||||
static inline TYPE align(TYPE p, size_t alignment) noexcept {
|
||||
// alignment must be a power-of-two
|
||||
assert(alignment && !(alignment & alignment-1));
|
||||
return (TYPE)((p + alignment - 1) & ~(alignment - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages a single id<MTLBuffer>, allowing sub-allocations in a "ring" fashion. Each slot in the
|
||||
* buffer has a fixed size. When a new allocation is made, previous allocations become available
|
||||
* when the current id<MTLCommandBuffer> has finished executing on the GPU.
|
||||
*
|
||||
* If there are no slots available when a new allocation is requested, MetalRingBuffer falls back to
|
||||
* allocating a new id<MTLBuffer> per allocation until a slot is freed.
|
||||
*
|
||||
* All methods must be called from the Metal backend thread.
|
||||
*/
|
||||
class MetalRingBuffer {
|
||||
public:
|
||||
// In practice, MetalRingBuffer is used for argument buffers, which are kept in the constant
|
||||
// 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
|
||||
static inline auto computeSlotSize(MTLSizeAndAlign layout) {
|
||||
return align(align(layout.size, layout.align), METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT);
|
||||
}
|
||||
|
||||
MetalRingBuffer(id<MTLDevice> device, MTLResourceOptions options, MTLSizeAndAlign layout,
|
||||
NSUInteger slotCount)
|
||||
: mDevice(device),
|
||||
mAuxBuffer(nil),
|
||||
mBufferOptions(options),
|
||||
mSlotSizeBytes(computeSlotSize(layout)),
|
||||
mSlotCount(slotCount) {
|
||||
mBuffer = [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions];
|
||||
assert_invariant(mBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new allocation in the buffer.
|
||||
* @param cmdBuffer When this command buffer has finished executing on the GPU, the previous
|
||||
* ring buffer allocation will be freed.
|
||||
* @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);
|
||||
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.
|
||||
// If we already have an aux buffer, it will get freed here, unless it has been retained
|
||||
// by a MTLCommandBuffer. In that case, it will be freed when the command buffer
|
||||
// finishes executing.
|
||||
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
|
||||
assert_invariant(mAuxBuffer);
|
||||
return {mAuxBuffer, 0};
|
||||
}
|
||||
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
|
||||
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) {
|
||||
if (auto s = slots.lock()) {
|
||||
s->fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
}];
|
||||
}
|
||||
return getCurrentAllocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an allocation (buffer and offset) that is guaranteed not to be in use by the GPU.
|
||||
* @param cmdBuffer When this command buffer has finished executing on the GPU, the previous
|
||||
* ring buffer allocation will be freed.
|
||||
* @return the id<MTLBuffer> and offset for the current allocation
|
||||
*/
|
||||
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
|
||||
if (UTILS_UNLIKELY(mAuxBuffer)) {
|
||||
return { mAuxBuffer, 0 };
|
||||
}
|
||||
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
|
||||
}
|
||||
|
||||
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
|
||||
return mSlotSizeBytes >= computeSlotSize(layout);
|
||||
}
|
||||
|
||||
private:
|
||||
id<MTLDevice> mDevice;
|
||||
id<MTLBuffer> mBuffer;
|
||||
id<MTLBuffer> mAuxBuffer;
|
||||
|
||||
MTLResourceOptions mBufferOptions;
|
||||
|
||||
NSUInteger mSlotSizeBytes;
|
||||
NSUInteger mSlotCount;
|
||||
|
||||
NSUInteger mCurrentSlot = 0;
|
||||
using AtomicCounterType = std::atomic<NSUInteger>;
|
||||
std::shared_ptr<AtomicCounterType> mOccupiedSlots = std::make_shared<AtomicCounterType>(1);
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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)
|
||||
@@ -41,11 +42,12 @@ class MetalDriver;
|
||||
class MetalBlitter;
|
||||
class MetalBufferPool;
|
||||
class MetalRenderTarget;
|
||||
class MetalSamplerGroup;
|
||||
class MetalSwapChain;
|
||||
class MetalTexture;
|
||||
class MetalTimerQueryInterface;
|
||||
struct MetalUniformBuffer;
|
||||
struct MetalIndexBuffer;
|
||||
struct MetalSamplerGroup;
|
||||
struct MetalVertexBuffer;
|
||||
|
||||
constexpr static uint8_t MAX_SAMPLE_COUNT = 8; // Metal devices support at most 8 MSAA samples
|
||||
@@ -58,8 +60,12 @@ struct MetalContext {
|
||||
id<MTLCommandBuffer> pendingCommandBuffer = nullptr;
|
||||
id<MTLRenderCommandEncoder> currentRenderPassEncoder = nullptr;
|
||||
|
||||
std::atomic<bool> memorylessLimitsReached = false;
|
||||
|
||||
// Supported features.
|
||||
bool supportsTextureSwizzling = false;
|
||||
bool supportsAutoDepthResolve = false;
|
||||
bool supportsMemorylessRenderTargets = false;
|
||||
uint8_t maxColorRenderTargets = 4;
|
||||
struct {
|
||||
uint8_t common;
|
||||
@@ -79,7 +85,8 @@ struct MetalContext {
|
||||
// State trackers.
|
||||
PipelineStateTracker pipelineState;
|
||||
DepthStencilStateTracker depthStencilState;
|
||||
UniformBufferState uniformState[UNIFORM_BUFFER_COUNT];
|
||||
std::array<BufferState, Program::UNIFORM_BINDING_COUNT> uniformState;
|
||||
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
|
||||
CullModeStateTracker cullModeState;
|
||||
WindingStateTracker windingState;
|
||||
|
||||
@@ -87,11 +94,16 @@ struct MetalContext {
|
||||
DepthStencilStateCache depthStencilStateCache;
|
||||
PipelineStateCache pipelineStateCache;
|
||||
SamplerStateCache samplerStateCache;
|
||||
ArgumentEncoderCache argumentEncoderCache;
|
||||
|
||||
MetalSamplerGroup* samplerBindings[SAMPLER_BINDING_COUNT] = {};
|
||||
MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {};
|
||||
|
||||
// Keeps track of all alive sampler groups.
|
||||
// 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, textures.
|
||||
tsl::robin_set<MetalSamplerGroup*> samplerGroups;
|
||||
tsl::robin_set<MetalTexture*> textures;
|
||||
|
||||
MetalBufferPool* bufferPool;
|
||||
|
||||
@@ -123,10 +135,6 @@ struct MetalContext {
|
||||
os_log_t log;
|
||||
os_signpost_id_t signpostId;
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
tsl::robin_set<HandleBase::HandleId> aliveTextures;
|
||||
#endif
|
||||
};
|
||||
|
||||
void initializeSupportedGpuFamilies(MetalContext* context);
|
||||
|
||||
@@ -103,6 +103,15 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
||||
// all frames and their completion handlers finish before context is deallocated.
|
||||
[context->pendingCommandBuffer addCompletedHandler:^(id <MTLCommandBuffer> buffer) {
|
||||
context->resourceTracker.clearResources((__bridge void*) buffer);
|
||||
|
||||
auto errorCode = (MTLCommandBufferError)buffer.error.code;
|
||||
if (@available(macOS 11.0, *)) {
|
||||
if (errorCode == MTLCommandBufferErrorMemoryless) {
|
||||
utils::slog.w << "Metal: memoryless geometry limit reached. "
|
||||
"Continuing with private storage mode." << utils::io::endl;
|
||||
context->memorylessLimitsReached = true;
|
||||
}
|
||||
}
|
||||
}];
|
||||
ASSERT_POSTCONDITION(context->pendingCommandBuffer, "Could not obtain command buffer.");
|
||||
return context->pendingCommandBuffer;
|
||||
|
||||
@@ -34,10 +34,12 @@ namespace backend {
|
||||
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
|
||||
@@ -126,10 +128,9 @@ private:
|
||||
inline void setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
|
||||
|
||||
void enumerateSamplerGroups(const MetalProgram* program, ShaderStage shaderType,
|
||||
const std::function<void(const SamplerDescriptor*, size_t)>& f);
|
||||
void enumerateBoundUniformBuffers(const std::function<void(const UniformBufferState&,
|
||||
MetalBuffer*, uint32_t)>& f);
|
||||
void finalizeSamplerGroup(MetalSamplerGroup* sg);
|
||||
void enumerateBoundBuffers(BufferObjectBinding bindingType,
|
||||
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
@@ -90,6 +92,18 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
mContext->highestSupportedGpuFamily.mac >= 2; // newer macOS GPUs
|
||||
}
|
||||
|
||||
// In order to support resolve store action on depth attachment, the GPU needs to support it.
|
||||
// Note that support for depth resolve implies support for stencil resolve using .sample0 resolve filter.
|
||||
// (Other resolve filters are supported starting .apple5 and .mac2 families).
|
||||
mContext->supportsAutoDepthResolve =
|
||||
mContext->highestSupportedGpuFamily.apple >= 3 ||
|
||||
mContext->highestSupportedGpuFamily.mac >= 2;
|
||||
|
||||
// In order to support memoryless render targets, an Apple GPU is needed.
|
||||
// Available starting macOS 11.0, the first version to run on Apple GPUs.
|
||||
// On iOS, it's available on all OS versions.
|
||||
mContext->supportsMemorylessRenderTargets = mContext->highestSupportedGpuFamily.apple >= 1;
|
||||
|
||||
mContext->maxColorRenderTargets = 4;
|
||||
if (mContext->highestSupportedGpuFamily.apple >= 2 ||
|
||||
mContext->highestSupportedGpuFamily.mac >= 1) {
|
||||
@@ -107,6 +121,7 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
mContext->pipelineStateCache.setDevice(mContext->device);
|
||||
mContext->depthStencilStateCache.setDevice(mContext->device);
|
||||
mContext->samplerStateCache.setDevice(mContext->device);
|
||||
mContext->argumentEncoderCache.setDevice(mContext->device);
|
||||
mContext->bufferPool = new MetalBufferPool(*mContext);
|
||||
mContext->blitter = new MetalBlitter(*mContext);
|
||||
|
||||
@@ -188,6 +203,18 @@ void MetalDriver::endFrame(uint32_t frameId) {
|
||||
|
||||
assert_invariant(mContext->groupMarkers.empty());
|
||||
|
||||
// If we exceeded memoryless limits, turn it off for the rest of the lifetime of the driver.
|
||||
if (mContext->supportsMemorylessRenderTargets && mContext->memorylessLimitsReached) {
|
||||
for (MetalTexture* texture : mContext->textures) {
|
||||
// Release memoryless MTLTexture-s, which are currently only the MSAA sidecars.
|
||||
// Creation of new render targets is going to trigger the re-allocation of sidecars,
|
||||
// with private storage mode from now on. Here, at the end of the frame,
|
||||
// all render targets that have textures with sidecars are assumed to be destroyed.
|
||||
texture->msaaSidecar = nil;
|
||||
}
|
||||
mContext->supportsMemorylessRenderTargets = false;
|
||||
}
|
||||
|
||||
#if defined(FILAMENT_METAL_PROFILING)
|
||||
os_signpost_interval_end(mContext->log, mContext->signpostId, "Frame encoding");
|
||||
#endif
|
||||
@@ -223,7 +250,7 @@ void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elem
|
||||
|
||||
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage) {
|
||||
construct_handle<MetalBufferObject>(boh, *mContext, usage, byteCount);
|
||||
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
@@ -233,13 +260,10 @@ void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8
|
||||
auto& sc = mContext->sampleCountLookup;
|
||||
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
|
||||
|
||||
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
|
||||
width, height, depth, usage, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1,
|
||||
TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3);
|
||||
|
||||
#ifndef NDEBUG
|
||||
mContext->aliveTextures.insert(th.getId());
|
||||
#endif
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
|
||||
target, levels, format, samples, width, height, depth, usage,
|
||||
TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1,
|
||||
TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3));
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
@@ -250,12 +274,8 @@ void MetalDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType targe
|
||||
auto& sc = mContext->sampleCountLookup;
|
||||
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
|
||||
|
||||
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
|
||||
width, height, depth, usage, r, g, b, a);
|
||||
|
||||
#ifndef NDEBUG
|
||||
mContext->aliveTextures.insert(th.getId());
|
||||
#endif
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
|
||||
target, levels, format, samples, width, height, depth, usage, r, g, b, a));
|
||||
}
|
||||
|
||||
void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
|
||||
@@ -276,12 +296,8 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
|
||||
ASSERT_PRECONDITION(metalTexture.textureType == filamentMetalType,
|
||||
"Imported id<MTLTexture> type (%d) != Filament texture type (%d)",
|
||||
metalTexture.textureType, filamentMetalType);
|
||||
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
|
||||
width, height, depth, usage, metalTexture);
|
||||
|
||||
#ifndef NDEBUG
|
||||
mContext->aliveTextures.insert(th.getId());
|
||||
#endif
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
|
||||
target, levels, format, samples, width, height, depth, usage, metalTexture));
|
||||
}
|
||||
|
||||
void MetalDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t size) {
|
||||
@@ -309,6 +325,8 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
|
||||
uint8_t samples, MRT color,
|
||||
TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"createRenderTarget must be called outside of a render pass.");
|
||||
// Clamp sample count to what the device supports.
|
||||
auto& sc = mContext->sampleCountLookup;
|
||||
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
|
||||
@@ -322,7 +340,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
ASSERT_PRECONDITION(buffer.handle,
|
||||
"The COLOR%u flag was specified, but invalid color handle provided.", i);
|
||||
auto colorTexture = handle_cast<MetalTexture>(buffer.handle);
|
||||
ASSERT_PRECONDITION(colorTexture->texture,
|
||||
ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(),
|
||||
"Color texture passed to render target has no texture allocation");
|
||||
colorTexture->updateLodRange(buffer.level);
|
||||
colorAttachments[i] = { colorTexture, color[i].level, color[i].layer };
|
||||
@@ -333,7 +351,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
ASSERT_PRECONDITION(depth.handle,
|
||||
"The DEPTH flag was specified, but invalid depth handle provided.");
|
||||
auto depthTexture = handle_cast<MetalTexture>(depth.handle);
|
||||
ASSERT_PRECONDITION(depthTexture->texture,
|
||||
ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(),
|
||||
"Depth texture passed to render target has no texture allocation.");
|
||||
depthTexture->updateLodRange(depth.level);
|
||||
depthAttachment = { depthTexture, depth.level, depth.layer };
|
||||
@@ -344,7 +362,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
ASSERT_PRECONDITION(stencil.handle,
|
||||
"The STENCIL flag was specified, but invalid stencil handle provided.");
|
||||
auto stencilTexture = handle_cast<MetalTexture>(stencil.handle);
|
||||
ASSERT_PRECONDITION(stencilTexture->texture,
|
||||
ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(),
|
||||
"Stencil texture passed to render target has no texture allocation.");
|
||||
stencilTexture->updateLodRange(stencil.level);
|
||||
stencilAttachment = { stencilTexture, stencil.level, stencil.layer };
|
||||
@@ -470,10 +488,20 @@ void MetalDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
|
||||
return;
|
||||
}
|
||||
auto* bo = handle_cast<MetalBufferObject>(boh);
|
||||
// Unbind this buffer object from any uniform slots it's still bound to.
|
||||
// Unbind this buffer object from any uniform / SSBO slots it's still bound to.
|
||||
bo->boundUniformBuffers.forEachSetBit([this](size_t index) {
|
||||
mContext->uniformState[index].buffer = nullptr;
|
||||
mContext->uniformState[index].bound = false;
|
||||
mContext->uniformState[index] = BufferState {
|
||||
.buffer = nullptr,
|
||||
.offset = 0,
|
||||
.bound = false
|
||||
};
|
||||
});
|
||||
bo->boundSsbos.forEachSetBit([this](size_t index) {
|
||||
mContext->ssboState[index] = BufferState {
|
||||
.buffer = nullptr,
|
||||
.offset = 0,
|
||||
.bound = false
|
||||
};
|
||||
});
|
||||
destruct_handle<MetalBufferObject>(boh);
|
||||
}
|
||||
@@ -510,21 +538,7 @@ void MetalDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
mContext->aliveTextures.erase(th.getId());
|
||||
#endif
|
||||
|
||||
// Unbind this texture from any sampler groups that currently reference it.
|
||||
for (auto* metalSamplerGroup : mContext->samplerGroups) {
|
||||
const SamplerDescriptor* samplers = metalSamplerGroup->sb->data();
|
||||
for (size_t i = 0; i < metalSamplerGroup->sb->getSize(); i++) {
|
||||
const SamplerDescriptor* sampler = samplers + i;
|
||||
if (sampler->t == th) {
|
||||
metalSamplerGroup->sb->setSampler(i, {{}, {}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mContext->textures.erase(handle_cast<MetalTexture>(th));
|
||||
destruct_handle<MetalTexture>(th);
|
||||
}
|
||||
|
||||
@@ -689,7 +703,7 @@ bool MetalDriver::isFrameTimeSupported() {
|
||||
}
|
||||
|
||||
bool MetalDriver::isAutoDepthResolveSupported() {
|
||||
return true;
|
||||
return mContext->supportsAutoDepthResolve;
|
||||
}
|
||||
|
||||
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
@@ -705,10 +719,14 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
}
|
||||
|
||||
FeatureLevel MetalDriver::getFeatureLevel() {
|
||||
// TODO: before we can return FEATURE_LEVEL_2 we need to decouple the samplers from
|
||||
// the textures. Metal only supports 16 samplers, but at least 31 textures on all
|
||||
// hardware.
|
||||
return FeatureLevel::FEATURE_LEVEL_1;
|
||||
// FEATURE_LEVEL_3 requires >= 31 textures, which all Metal devices support. However, older
|
||||
// Metal devices only support 16 unique samplers. We could get around this in the future by
|
||||
// virtualizing samplers.
|
||||
if (mContext->highestSupportedGpuFamily.apple >= 6 ||
|
||||
mContext->highestSupportedGpuFamily.mac >= 2) {
|
||||
return FeatureLevel::FEATURE_LEVEL_3;
|
||||
}
|
||||
return FeatureLevel::FEATURE_LEVEL_2;
|
||||
}
|
||||
|
||||
math::float2 MetalDriver::getClipSpaceParams() {
|
||||
@@ -791,11 +809,15 @@ void MetalDriver::cancelExternalImage(void* image) {
|
||||
}
|
||||
|
||||
void MetalDriver::setExternalImage(Handle<HwTexture> th, void* image) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"setExternalImage must be called outside of a render pass.");
|
||||
auto texture = handle_cast<MetalTexture>(th);
|
||||
texture->externalImage.set((CVPixelBufferRef) image);
|
||||
}
|
||||
|
||||
void MetalDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint32_t plane) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"setExternalImagePlane must be called outside of a render pass.");
|
||||
auto texture = handle_cast<MetalTexture>(th);
|
||||
texture->externalImage.set((CVPixelBufferRef) image, plane);
|
||||
}
|
||||
@@ -823,11 +845,7 @@ void MetalDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"generateMipmaps must be called outside of a render pass.");
|
||||
auto tex = handle_cast<MetalTexture>(th);
|
||||
id <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(mContext) blitCommandEncoder];
|
||||
[blitEncoder generateMipmapsForTexture:tex->texture];
|
||||
[blitEncoder endEncoding];
|
||||
tex->minLod = 0;
|
||||
tex->maxLod = tex->texture.mipmapLevelCount - 1;
|
||||
tex->generateMipmaps();
|
||||
}
|
||||
|
||||
bool MetalDriver::canGenerateMipmaps() {
|
||||
@@ -839,39 +857,125 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
|
||||
"updateSamplerGroup must be called outside of a render pass.");
|
||||
|
||||
auto sb = handle_cast<MetalSamplerGroup>(sbh);
|
||||
assert_invariant(sb->size == data.size / sizeof(SamplerDescriptor));
|
||||
auto const* const samplers = (SamplerDescriptor const*) data.buffer;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// In debug builds, verify that all the textures in the sampler group are still alive.
|
||||
// These bugs lead to memory corruption and can be difficult to track down.
|
||||
auto const* const samplers = (SamplerDescriptor const*) data.buffer;
|
||||
for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) {
|
||||
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
|
||||
|
||||
// FIXME: we shouldn't be using SamplerGroup here, instead the backend should create
|
||||
// a descriptor or any internal data-structure that represents the textures/samplers.
|
||||
// It's preferable to do as much work as possible here.
|
||||
// Here, we emulate the older backend API by re-creating a SamplerGroup from the
|
||||
// passed data.
|
||||
SamplerGroup samplerGroup(data.size / sizeof(SamplerDescriptor));
|
||||
memcpy(samplerGroup.data(), data.buffer, data.size);
|
||||
*sb->sb = std::move(samplerGroup);
|
||||
// Create a MTLArgumentEncoder for these textures.
|
||||
// Ideally, we would create this encoder at createSamplerGroup time, but we need to know the
|
||||
// texture type of each texture. It's also not guaranteed that the types won't change between
|
||||
// calls to updateSamplerGroup.
|
||||
utils::FixedCapacityVector<MTLTextureType> textureTypes(sb->size);
|
||||
std::transform(samplers, samplers + data.size / sizeof(SamplerDescriptor), textureTypes.begin(),
|
||||
[this](const SamplerDescriptor& sampler) {
|
||||
if (!sampler.t) {
|
||||
// Use Type2D for non-bound textures.
|
||||
return MTLTextureType2D;
|
||||
}
|
||||
auto* t = handle_cast<MetalTexture>(sampler.t);
|
||||
if (t->target == SamplerType::SAMPLER_EXTERNAL) {
|
||||
// Use Type2D for external image textures.
|
||||
return MTLTextureType2D;
|
||||
}
|
||||
id<MTLTexture> mtlTexture = t->getMtlTextureForRead();
|
||||
assert_invariant(mtlTexture);
|
||||
return mtlTexture.textureType;
|
||||
});
|
||||
auto& encoderCache = mContext->argumentEncoderCache;
|
||||
id<MTLArgumentEncoder> encoder =
|
||||
encoderCache.getOrCreateState(ArgumentEncoderState(std::move(textureTypes)));
|
||||
sb->reset(getPendingCommandBuffer(mContext), encoder, mContext->device);
|
||||
|
||||
// In a perfect world, all the MTLTexture bindings would be known at updateSamplerGroup time.
|
||||
// However, there are two special cases preventing this:
|
||||
// 1. External images
|
||||
// 2. LOD-clamped textures
|
||||
//
|
||||
// Both of these cases prevent us from knowing the final id<MTLTexture> that will be bound into
|
||||
// the argument buffer representing the sampler group. So, we bind what we can now and wait
|
||||
// until draw call time to bind any special cases (done in finalizeSamplerGroup).
|
||||
// The good news is that once a render pass has started, the texture bindings won't change.
|
||||
// A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in
|
||||
// a draw call.
|
||||
// Even if we do know all the final textures at this point, we still wait until draw call time
|
||||
// to call finalizeSamplerGroup, which has one additional responsibility: to call useResources
|
||||
// for all the textures, which is required by Metal.
|
||||
for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) {
|
||||
if (!samplers[s].t) {
|
||||
// Assign a default texture / sampler to empty slots.
|
||||
// Metal requires all samplers referenced in shaders to be bound.
|
||||
id<MTLTexture> empty = getOrCreateEmptyTexture(mContext);
|
||||
sb->setFinalizedTexture(s, empty);
|
||||
|
||||
id<MTLSamplerState> sampler = mContext->samplerStateCache.getOrCreateState({});
|
||||
sb->setFinalizedSampler(s, sampler);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// First, bind the sampler state. We always know the full sampler state at
|
||||
// updateSamplerGroup time.
|
||||
SamplerState samplerState {
|
||||
.samplerParams = samplers[s].s,
|
||||
};
|
||||
id<MTLSamplerState> sampler = mContext->samplerStateCache.getOrCreateState(samplerState);
|
||||
sb->setFinalizedSampler(s, sampler);
|
||||
|
||||
sb->setTextureHandle(s, samplers[s].t);
|
||||
|
||||
auto* t = handle_cast<MetalTexture>(samplers[s].t);
|
||||
assert_invariant(t);
|
||||
|
||||
// If this texture is an external texture, we defer binding the texture until draw call time
|
||||
// (in finalizeSamplerGroup).
|
||||
if (t->target == SamplerType::SAMPLER_EXTERNAL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!t->allLodsValid()) {
|
||||
// The texture doesn't have all of its LODs loaded, and this could change by the time we
|
||||
// issue a draw call with this sampler group. So, we defer binding the texture until
|
||||
// draw call time (in finalizeSamplerGroup).
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we get here, we know we have a valid MTLTexture that's guaranteed not to change.
|
||||
id<MTLTexture> mtlTexture = t->getMtlTextureForRead();
|
||||
assert_invariant(mtlTexture);
|
||||
sb->setFinalizedTexture(s, mtlTexture);
|
||||
}
|
||||
|
||||
scheduleDestroy(std::move(data));
|
||||
}
|
||||
|
||||
void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
|
||||
const RenderPassParams& params) {
|
||||
|
||||
#if defined(FILAMENT_METAL_PROFILING)
|
||||
const char* renderPassName = "Unknown";
|
||||
if (!mContext->groupMarkers.empty()) {
|
||||
renderPassName = mContext->groupMarkers.top();
|
||||
}
|
||||
os_signpost_interval_begin(mContext->log, OS_SIGNPOST_ID_EXCLUSIVE, "Render pass", "%s",
|
||||
renderPassName);
|
||||
#endif
|
||||
|
||||
auto renderTarget = handle_cast<MetalRenderTarget>(rth);
|
||||
mContext->currentRenderTarget = renderTarget;
|
||||
mContext->currentRenderPassFlags = params.flags;
|
||||
@@ -901,11 +1005,17 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
|
||||
mContext->depthStencilState.invalidate();
|
||||
mContext->cullModeState.invalidate();
|
||||
mContext->windingState.invalidate();
|
||||
|
||||
mContext->finalizedSamplerGroups.clear();
|
||||
}
|
||||
|
||||
void MetalDriver::nextSubpass(int dummy) {}
|
||||
|
||||
void MetalDriver::endRenderPass(int dummy) {
|
||||
#if defined(FILAMENT_METAL_PROFILING)
|
||||
os_signpost_interval_end(mContext->log, OS_SIGNPOST_ID_EXCLUSIVE, "Render pass");
|
||||
#endif
|
||||
|
||||
[mContext->currentRenderPassEncoder endEncoding];
|
||||
|
||||
// Command encoders are one time use. Set it to nil to release the encoder and ensure we don't
|
||||
@@ -951,34 +1061,101 @@ void MetalDriver::commit(Handle<HwSwapChain> sch) {
|
||||
}
|
||||
|
||||
void MetalDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh) {
|
||||
assert_invariant(index < UNIFORM_BUFFER_COUNT);
|
||||
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);
|
||||
}
|
||||
bo->boundUniformBuffers.set(index);
|
||||
mContext->uniformState[index] = UniformBufferState{
|
||||
mContext->uniformState[index] = BufferState{
|
||||
.buffer = bo,
|
||||
.offset = 0,
|
||||
.bound = true
|
||||
};
|
||||
}
|
||||
|
||||
void MetalDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> boh,
|
||||
uint32_t offset, uint32_t size) {
|
||||
assert_invariant(index < UNIFORM_BUFFER_COUNT);
|
||||
void MetalDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
|
||||
Handle<HwBufferObject> boh, uint32_t offset, uint32_t size) {
|
||||
|
||||
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
|
||||
bindingType == BufferObjectBinding::UNIFORM);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
bo->boundUniformBuffers.set(index);
|
||||
mContext->uniformState[index] = UniformBufferState{
|
||||
.buffer = bo,
|
||||
.offset = offset,
|
||||
.bound = true
|
||||
};
|
||||
}
|
||||
|
||||
void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
@@ -1108,6 +1285,12 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
|
||||
}];
|
||||
}
|
||||
|
||||
void MetalDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
|
||||
// TODO: implement readBufferSubData
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void MetalDriver::blit(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
@@ -1187,6 +1370,97 @@ void MetalDriver::blit(TargetBufferFlags buffers,
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
|
||||
// All of the id<MTLSamplerState> objects have already been bound to the argument buffer.
|
||||
// Here we bind any textures that were unable to be bound in updateSamplerGroup.
|
||||
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// In debug builds, verify that all the textures in the sampler group are still alive.
|
||||
// These bugs lead to memory corruption and can be difficult to track down.
|
||||
const auto& handles = samplerGroup->getTextureHandles();
|
||||
for (size_t s = 0; s < handles.size(); s++) {
|
||||
if (!handles[s]) {
|
||||
continue;
|
||||
}
|
||||
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->textures.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
for (size_t binding = 0; binding < samplerGroup->size; binding++) {
|
||||
auto [th, t] = samplerGroup->getFinalizedTexture(binding);
|
||||
|
||||
// This may be an external texture, in which case we can't cache the id<MTLTexture>, we
|
||||
// need to refetch it in case the external image has changed.
|
||||
bool isExternalImage = false;
|
||||
if (th) {
|
||||
auto* texture = handle_cast<MetalTexture>(th);
|
||||
isExternalImage = texture->target == SamplerType::SAMPLER_EXTERNAL;
|
||||
}
|
||||
|
||||
// If t is non-nil, then we've already finalized this texture.
|
||||
if (t && !isExternalImage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// It's possible that some texture handles are null, but we should have already handled
|
||||
// these inside updateSamplerGroup by binding an "empty" texture.
|
||||
assert_invariant(th);
|
||||
auto* texture = handle_cast<MetalTexture>(th);
|
||||
|
||||
// Determine if this SamplerGroup needs mutation.
|
||||
// We can't just simply mutate the SamplerGroup, since it could currently be in use by the
|
||||
// GPU from a prior render pass.
|
||||
// If the SamplerGroup does need mutation, then there's two cases:
|
||||
// 1. The SamplerGroup has not been finalized yet (which means it has not yet been used in a
|
||||
// draw call). We're free to mutate it.
|
||||
// 2. The SamplerGroup is finalized. We must call mutate(), which will create a new argument
|
||||
// buffer that we can then mutate freely.
|
||||
// TODO: don't just always call mutate, check to see if the texture is actually different.
|
||||
|
||||
if (samplerGroup->isFinalized()) {
|
||||
samplerGroup->mutate(cmdBuffer);
|
||||
}
|
||||
|
||||
// External images
|
||||
if (texture->target == SamplerType::SAMPLER_EXTERNAL) {
|
||||
if (texture->externalImage.isValid()) {
|
||||
id<MTLTexture> mtlTexture = texture->externalImage.getMetalTextureForDraw();
|
||||
assert_invariant(mtlTexture);
|
||||
samplerGroup->setFinalizedTexture(binding, mtlTexture);
|
||||
} else {
|
||||
// Bind an empty texture.
|
||||
samplerGroup->setFinalizedTexture(binding, getOrCreateEmptyTexture(mContext));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
samplerGroup->setFinalizedTexture(binding, texture->getMtlTextureForRead());
|
||||
}
|
||||
|
||||
samplerGroup->finalize();
|
||||
|
||||
// At this point, all the id<MTLTextures> should be set to valid textures. Some of them will be
|
||||
// the "empty" texture. Per Apple documentation, the useResource method must be called once per
|
||||
// render pass.
|
||||
samplerGroup->useResources(mContext->currentRenderPassEncoder);
|
||||
|
||||
// useResources won't retain references to the textures, so we need to do so manually.
|
||||
for (id<MTLTexture> texture : samplerGroup->textures) {
|
||||
const void* retainedTexture = CFBridgingRetain(texture);
|
||||
[cmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
|
||||
CFBridgingRelease(retainedTexture);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"Attempted to draw without a valid command encoder.");
|
||||
@@ -1352,8 +1626,9 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
|
||||
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
|
||||
|
||||
enumerateBoundUniformBuffers([&uniformsToBind, &offsets](const UniformBufferState& state,
|
||||
MetalBuffer* buffer, uint32_t index) {
|
||||
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
|
||||
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
|
||||
uint32_t index) {
|
||||
uniformsToBind[index] = buffer;
|
||||
offsets[index] = state.offset;
|
||||
});
|
||||
@@ -1361,105 +1636,37 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
|
||||
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
|
||||
|
||||
// Enumerate all the sampler buffers for the program and check which textures and samplers need
|
||||
// to be bound.
|
||||
|
||||
auto getTextureToBind = [this](const SamplerDescriptor* sampler) {
|
||||
const auto metalTexture = handle_const_cast<MetalTexture>(sampler->t);
|
||||
id<MTLTexture> textureToBind = metalTexture->swizzledTextureView ? metalTexture->swizzledTextureView
|
||||
: metalTexture->texture;
|
||||
if (metalTexture->externalImage.isValid()) {
|
||||
textureToBind = metalTexture->externalImage.getMetalTextureForDraw();
|
||||
// Bind sampler groups (argument buffers).
|
||||
for (size_t s = 0; s < Program::SAMPLER_BINDING_COUNT; s++) {
|
||||
MetalSamplerGroup* const samplerGroup = mContext->samplerBindings[s];
|
||||
if (!samplerGroup) {
|
||||
continue;
|
||||
}
|
||||
return textureToBind;
|
||||
};
|
||||
|
||||
auto getSamplerToBind = [this](const SamplerDescriptor* sampler) {
|
||||
const auto metalTexture = handle_const_cast<MetalTexture>(sampler->t);
|
||||
SamplerState s {
|
||||
.samplerParams = sampler->s,
|
||||
.minLod = metalTexture->minLod,
|
||||
.maxLod = metalTexture->maxLod
|
||||
};
|
||||
return mContext->samplerStateCache.getOrCreateState(s);
|
||||
};
|
||||
|
||||
id<MTLTexture> texturesToBindVertex[FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_1].MAX_VERTEX_SAMPLER_COUNT] = {};
|
||||
id<MTLSamplerState> samplersToBindVertex[FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_1].MAX_VERTEX_SAMPLER_COUNT] = {};
|
||||
|
||||
enumerateSamplerGroups(program, ShaderStage::VERTEX,
|
||||
[this, &getTextureToBind, &getSamplerToBind, &texturesToBindVertex, &samplersToBindVertex](
|
||||
const SamplerDescriptor* sampler, uint8_t binding) {
|
||||
// We currently only support a max of MAX_VERTEX_SAMPLER_COUNT samplers. Ignore any additional
|
||||
// samplers that may be bound.
|
||||
if (binding >= FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_1].MAX_VERTEX_SAMPLER_COUNT) {
|
||||
return;
|
||||
const auto& stageFlags = program->samplerGroupInfo[s].stageFlags;
|
||||
if (stageFlags == ShaderStageFlags::NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& textureToBind = texturesToBindVertex[binding];
|
||||
textureToBind = getTextureToBind(sampler);
|
||||
if (!textureToBind) {
|
||||
utils::slog.w << "Warning: no texture bound at binding point " << (size_t) binding
|
||||
<< " at the vertex shader." << utils::io::endl;
|
||||
textureToBind = getOrCreateEmptyTexture(mContext);
|
||||
auto iter = mContext->finalizedSamplerGroups.find(samplerGroup);
|
||||
if (iter == mContext->finalizedSamplerGroups.end()) {
|
||||
finalizeSamplerGroup(samplerGroup);
|
||||
mContext->finalizedSamplerGroups.insert(samplerGroup);
|
||||
}
|
||||
|
||||
auto& samplerToBind = samplersToBindVertex[binding];
|
||||
samplerToBind = getSamplerToBind(sampler);
|
||||
});
|
||||
assert_invariant(samplerGroup->getArgumentBuffer());
|
||||
|
||||
// Assign a default sampler to empty slots, in case Filament hasn't bound all samplers.
|
||||
// Metal requires all samplers referenced in shaders to be bound.
|
||||
for (auto& sampler : samplersToBindVertex) {
|
||||
if (!sampler) {
|
||||
sampler = mContext->samplerStateCache.getOrCreateState({});
|
||||
if (uint8_t(stageFlags) & uint8_t(ShaderStageFlags::VERTEX)) {
|
||||
[mContext->currentRenderPassEncoder setVertexBuffer:samplerGroup->getArgumentBuffer()
|
||||
offset:samplerGroup->getArgumentBufferOffset()
|
||||
atIndex:(SAMPLER_GROUP_BINDING_START + s)];
|
||||
}
|
||||
if (uint8_t(stageFlags) & uint8_t(ShaderStageFlags::FRAGMENT)) {
|
||||
[mContext->currentRenderPassEncoder setFragmentBuffer:samplerGroup->getArgumentBuffer()
|
||||
offset:samplerGroup->getArgumentBufferOffset()
|
||||
atIndex:(SAMPLER_GROUP_BINDING_START + s)];
|
||||
}
|
||||
}
|
||||
|
||||
NSRange vertexSamplerRange = NSMakeRange(0, FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_1].MAX_VERTEX_SAMPLER_COUNT);
|
||||
[mContext->currentRenderPassEncoder setVertexTextures:texturesToBindVertex
|
||||
withRange:vertexSamplerRange];
|
||||
[mContext->currentRenderPassEncoder setVertexSamplerStates:samplersToBindVertex
|
||||
withRange:vertexSamplerRange];
|
||||
|
||||
id<MTLTexture> texturesToBindFragment[FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_1].MAX_FRAGMENT_SAMPLER_COUNT] = {};
|
||||
id<MTLSamplerState> samplersToBindFragment[FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_1].MAX_FRAGMENT_SAMPLER_COUNT] = {};
|
||||
|
||||
enumerateSamplerGroups(program, ShaderStage::FRAGMENT,
|
||||
[this, &getTextureToBind, &getSamplerToBind, &texturesToBindFragment, &samplersToBindFragment](
|
||||
const SamplerDescriptor* sampler, uint8_t binding) {
|
||||
// We currently only support a max of MAX_FRAGMENT_SAMPLER_COUNT samplers. Ignore any additional
|
||||
// samplers that may be bound.
|
||||
if (binding >= FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_1].MAX_FRAGMENT_SAMPLER_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& textureToBind = texturesToBindFragment[binding];
|
||||
textureToBind = getTextureToBind(sampler);
|
||||
if (!textureToBind) {
|
||||
utils::slog.w << "Warning: no texture bound at binding point " << (size_t) binding
|
||||
<< " at the fragment shader." << utils::io::endl;
|
||||
textureToBind = getOrCreateEmptyTexture(mContext);
|
||||
}
|
||||
|
||||
auto& samplerToBind = samplersToBindFragment[binding];
|
||||
samplerToBind = getSamplerToBind(sampler);
|
||||
});
|
||||
|
||||
// Assign a default sampler to empty slots, in case Filament hasn't bound all samplers.
|
||||
// Metal requires all samplers referenced in shaders to be bound.
|
||||
for (auto& sampler : samplersToBindFragment) {
|
||||
if (!sampler) {
|
||||
sampler = mContext->samplerStateCache.getOrCreateState({});
|
||||
}
|
||||
}
|
||||
|
||||
NSRange fragmentSamplerRange = NSMakeRange(0, FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_1].MAX_FRAGMENT_SAMPLER_COUNT);
|
||||
[mContext->currentRenderPassEncoder setFragmentTextures:texturesToBindFragment
|
||||
withRange:fragmentSamplerRange];
|
||||
[mContext->currentRenderPassEncoder setFragmentSamplerStates:samplersToBindFragment
|
||||
withRange:fragmentSamplerRange];
|
||||
|
||||
// Bind the user vertex buffers.
|
||||
|
||||
MetalBuffer* buffers[MAX_VERTEX_BUFFER_COUNT] = {};
|
||||
@@ -1502,6 +1709,70 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
instanceCount:instanceCount];
|
||||
}
|
||||
|
||||
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
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) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"beginTimerQuery must be called outside of a render pass.");
|
||||
@@ -1516,47 +1787,33 @@ void MetalDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
mContext->timerQueryImpl->endTimeElapsedQuery(tq);
|
||||
}
|
||||
|
||||
void MetalDriver::enumerateSamplerGroups(
|
||||
const MetalProgram* program, ShaderStage shaderType,
|
||||
const std::function<void(const SamplerDescriptor*, size_t)>& f) {
|
||||
auto& samplerBlockInfo = (shaderType == ShaderStage::VERTEX) ?
|
||||
program->vertexSamplerBlockInfo : program->fragmentSamplerBlockInfo;
|
||||
auto maxSamplerCount = (shaderType == ShaderStage::VERTEX) ?
|
||||
FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_1].MAX_VERTEX_SAMPLER_COUNT :
|
||||
FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_1].MAX_FRAGMENT_SAMPLER_COUNT;
|
||||
for (size_t bindingIdx = 0; bindingIdx != maxSamplerCount; ++bindingIdx) {
|
||||
auto& blockInfo = samplerBlockInfo[bindingIdx];
|
||||
if (blockInfo.samplerGroup == UINT8_MAX) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
const auto* metalSamplerGroup = mContext->samplerBindings[blockInfo.samplerGroup];
|
||||
if (!metalSamplerGroup) {
|
||||
// Do not emit warning here. For example this can arise when skinning is enabled
|
||||
// and the morphing texture is unused.
|
||||
continue;
|
||||
}
|
||||
|
||||
SamplerGroup* sb = metalSamplerGroup->sb.get();
|
||||
const SamplerDescriptor* boundSampler = sb->data() + blockInfo.sampler;
|
||||
|
||||
if (!boundSampler->t) {
|
||||
continue;
|
||||
}
|
||||
|
||||
f(boundSampler, bindingIdx);
|
||||
switch (bindingType) {
|
||||
default:
|
||||
case (BufferObjectBinding::UNIFORM):
|
||||
enumerate(mContext->uniformState);
|
||||
break;
|
||||
case (BufferObjectBinding::SHADER_STORAGE):
|
||||
enumerate(mContext->ssboState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
f(thisUniform, thisUniform.buffer->getBuffer(), i);
|
||||
}
|
||||
void MetalDriver::resetState(int) {
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
|
||||
@@ -337,8 +337,6 @@ constexpr inline MTLPrimitiveType getMetalPrimitiveType(PrimitiveType type) noex
|
||||
case PrimitiveType::LINE_STRIP: return MTLPrimitiveTypeLineStrip;
|
||||
case PrimitiveType::TRIANGLES: return MTLPrimitiveTypeTriangle;
|
||||
case PrimitiveType::TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip;
|
||||
case PrimitiveType::NONE:
|
||||
ASSERT_POSTCONDITION(false, "NONE is not a valid primitive type.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,14 +166,9 @@ struct MetalProgram : public HwProgram {
|
||||
|
||||
id<MTLFunction> vertexFunction;
|
||||
id<MTLFunction> fragmentFunction;
|
||||
id<MTLFunction> computeFunction;
|
||||
|
||||
struct SamplerBlockInfo {
|
||||
uint8_t samplerGroup = UINT8_MAX;
|
||||
uint8_t sampler = UINT8_MAX;
|
||||
};
|
||||
|
||||
std::array<SamplerBlockInfo, FEATURE_LEVEL_CAPS[1].MAX_VERTEX_SAMPLER_COUNT> vertexSamplerBlockInfo;
|
||||
std::array<SamplerBlockInfo, FEATURE_LEVEL_CAPS[1].MAX_FRAGMENT_SAMPLER_COUNT> fragmentSamplerBlockInfo;
|
||||
Program::SamplerGroupInfo samplerGroupInfo;
|
||||
|
||||
bool isValid = false;
|
||||
};
|
||||
@@ -188,7 +186,8 @@ struct PixelBufferShape {
|
||||
MTLSize size, uint32_t byteOffset);
|
||||
};
|
||||
|
||||
struct MetalTexture : public HwTexture {
|
||||
class MetalTexture : public HwTexture {
|
||||
public:
|
||||
MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format,
|
||||
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
|
||||
TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a)
|
||||
@@ -201,40 +200,144 @@ struct MetalTexture : public HwTexture {
|
||||
|
||||
~MetalTexture();
|
||||
|
||||
// Returns an id<MTLTexture> suitable for reading in a shader, taking into account swizzle and
|
||||
// LOD clamping.
|
||||
id<MTLTexture> getMtlTextureForRead() noexcept;
|
||||
|
||||
// Returns the id<MTLTexture> for attaching to a render pass.
|
||||
id<MTLTexture> getMtlTextureForWrite() noexcept {
|
||||
return texture;
|
||||
}
|
||||
|
||||
void loadImage(uint32_t level, MTLRegion region, PixelBufferDescriptor& p) noexcept;
|
||||
void loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
|
||||
PixelBufferDescriptor const& data) noexcept;
|
||||
void loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
|
||||
const PixelBufferShape& shape);
|
||||
void loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
|
||||
const PixelBufferShape& shape);
|
||||
void generateMipmaps() noexcept;
|
||||
|
||||
// A texture starts out with none of its mip levels (also referred to as LODs) available for
|
||||
// reading. 3 actions update the range of LODs available:
|
||||
// - calling loadImage
|
||||
// - calling generateMipmaps
|
||||
// - using the texture as a render target attachment
|
||||
// The range of available mips can only increase, never decrease.
|
||||
// A texture's available mips are consistent throughout a render pass.
|
||||
void updateLodRange(uint32_t level);
|
||||
void updateLodRange(uint32_t minLevel, uint32_t maxLevel);
|
||||
|
||||
// Returns true if the texture has all of its mip levels accessible for reading.
|
||||
// For any MetalTexture, once this is true, will always return true.
|
||||
// The value returned will remain consistent for an entire render pass.
|
||||
bool allLodsValid() const {
|
||||
return minLod == 0 && maxLod == levels - 1;
|
||||
}
|
||||
|
||||
static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);
|
||||
|
||||
MetalContext& context;
|
||||
MetalExternalImage externalImage;
|
||||
id<MTLTexture> texture = nil;
|
||||
|
||||
// A "sidecar" texture used to implement automatic MSAA resolve.
|
||||
// This is created by MetalRenderTarget and stored here so it can be used with multiple
|
||||
// render targets.
|
||||
id<MTLTexture> msaaSidecar = nil;
|
||||
|
||||
MTLPixelFormat devicePixelFormat;
|
||||
|
||||
private:
|
||||
void loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
|
||||
PixelBufferDescriptor const& data) noexcept;
|
||||
void loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
|
||||
const PixelBufferShape& shape);
|
||||
void loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
|
||||
const PixelBufferShape& shape);
|
||||
|
||||
id<MTLTexture> texture = nil;
|
||||
|
||||
// If non-nil, a swizzled texture view to use instead of "texture".
|
||||
// Filament swizzling only affects texture reads, so this should not be used when the texture is
|
||||
// bound as a render target attachment.
|
||||
id<MTLTexture> swizzledTextureView = nil;
|
||||
id<MTLTexture> lodTextureView = nil;
|
||||
|
||||
MTLPixelFormat devicePixelFormat;
|
||||
uint32_t minLod = UINT_MAX;
|
||||
uint32_t maxLod = 0;
|
||||
};
|
||||
|
||||
struct MetalSamplerGroup : public HwSamplerGroup {
|
||||
// NOTE: we have to use out-of-line allocation here because the size of a Handle<> is limited
|
||||
std::unique_ptr<SamplerGroup> sb; // FIXME: this shouldn't depend on filament::SamplerGroup
|
||||
explicit MetalSamplerGroup(size_t size) noexcept : sb(new SamplerGroup(size)) { }
|
||||
class MetalSamplerGroup : public HwSamplerGroup {
|
||||
public:
|
||||
explicit MetalSamplerGroup(size_t size) noexcept
|
||||
: size(size),
|
||||
textureHandles(size, Handle<HwTexture>()),
|
||||
textures(size, nil),
|
||||
samplers(size, nil) {}
|
||||
|
||||
inline void setTextureHandle(size_t index, Handle<HwTexture> th) {
|
||||
assert_invariant(!finalized);
|
||||
textureHandles[index] = th;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// This method is only used for debugging, to ensure all texture handles are alive.
|
||||
const auto& getTextureHandles() const {
|
||||
return textureHandles;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Encode a MTLTexture into this SamplerGroup at the given index.
|
||||
inline void setFinalizedTexture(size_t index, id<MTLTexture> t) {
|
||||
assert_invariant(!finalized);
|
||||
textures[index] = t;
|
||||
}
|
||||
|
||||
// Encode a MTLSamplerState into this SamplerGroup at the given index.
|
||||
inline void setFinalizedSampler(size_t index, id<MTLSamplerState> s) {
|
||||
assert_invariant(!finalized);
|
||||
samplers[index] = s;
|
||||
}
|
||||
|
||||
// A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in
|
||||
// a draw call.
|
||||
// Once a SamplerGroup is finalized, it must be reset or mutated to be written into again.
|
||||
void finalize();
|
||||
bool isFinalized() const noexcept { return finalized; }
|
||||
|
||||
// Both of these methods "unfinalize" a SamplerGroup, allowing it to be updated via calls to
|
||||
// setFinalizedTexture or setFinalizedSampler. The difference is that when reset is called, all
|
||||
// the samplers/textures must be rebound. The MTLArgumentEncoder must be specified, in case
|
||||
// the texture types have changed.
|
||||
// Mutate re-encodes the current set of samplers/textures into the new argument
|
||||
// buffer.
|
||||
void reset(id<MTLCommandBuffer> cmdBuffer, id<MTLArgumentEncoder> e, id<MTLDevice> device);
|
||||
void mutate(id<MTLCommandBuffer> cmdBuffer);
|
||||
|
||||
id<MTLBuffer> getArgumentBuffer() const {
|
||||
assert_invariant(finalized);
|
||||
return argBuffer->getCurrentAllocation().first;
|
||||
}
|
||||
|
||||
NSUInteger getArgumentBufferOffset() const {
|
||||
return argBuffer->getCurrentAllocation().second;
|
||||
}
|
||||
|
||||
inline std::pair<Handle<HwTexture>, id<MTLTexture>> getFinalizedTexture(size_t index) {
|
||||
return {textureHandles[index], textures[index]};
|
||||
}
|
||||
|
||||
// Calls the Metal useResource:usage:stages: method for all the textures in this SamplerGroup.
|
||||
void useResources(id<MTLRenderCommandEncoder> renderPassEncoder);
|
||||
|
||||
size_t size;
|
||||
|
||||
public:
|
||||
|
||||
// These vectors are kept in sync with one another.
|
||||
utils::FixedCapacityVector<Handle<HwTexture>> textureHandles;
|
||||
utils::FixedCapacityVector<id<MTLTexture>> textures;
|
||||
utils::FixedCapacityVector<id<MTLSamplerState>> samplers;
|
||||
|
||||
id<MTLArgumentEncoder> encoder;
|
||||
|
||||
std::unique_ptr<MetalRingBuffer> argBuffer = nullptr;
|
||||
|
||||
bool finalized = false;
|
||||
};
|
||||
|
||||
class MetalRenderTarget : public HwRenderTarget {
|
||||
@@ -248,7 +351,7 @@ public:
|
||||
Attachment() = default;
|
||||
Attachment(MetalTexture* metalTexture, uint8_t level = 0, uint16_t layer = 0) :
|
||||
level(level), layer(layer),
|
||||
texture(metalTexture->texture),
|
||||
texture(metalTexture->getMtlTextureForWrite()),
|
||||
metalTexture(metalTexture) { }
|
||||
|
||||
id<MTLTexture> getTexture() const {
|
||||
@@ -330,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;
|
||||
|
||||
@@ -22,8 +22,11 @@
|
||||
|
||||
#include <filament/SwapChain.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include "private/backend/BackendUtils.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/trap.h>
|
||||
#include <utils/debug.h>
|
||||
@@ -268,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);
|
||||
@@ -285,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) {
|
||||
@@ -336,12 +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>*;
|
||||
|
||||
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++) {
|
||||
@@ -384,63 +388,25 @@ 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;
|
||||
|
||||
// This calculates Metal resource binding indices. Filament's sampler bindings range from 0-31
|
||||
// across both vertex and fragment stages. However, Metal binding indices must be < 16, with a
|
||||
// separate binding "namespace" for each stage. So, we recompute binding indices for each shader
|
||||
// stage. This logic should match updateResourceBinding in GLSLPostProcessor.cpp
|
||||
// This is an example how binding indices each of shader stages is generated from sampler's
|
||||
// bindings. Below is sampler's bindings.
|
||||
// 0 shadowMap { fragment }
|
||||
// 1 structure { fragment }
|
||||
// 2 targets { vertex }
|
||||
// 3 color { vertex | fragment }
|
||||
// 4 depth { vertex | fragment }
|
||||
// Below is generated vertex sampler binding indices.
|
||||
// 0 targets
|
||||
// 1 color
|
||||
// 2 depth
|
||||
// Below is generated fragment sampler binding indices.
|
||||
// 0 shadowMap
|
||||
// 1 structure
|
||||
// 2 color
|
||||
// 3 depth
|
||||
auto& samplerGroupInfo = program.getSamplerGroupInfo();
|
||||
for (size_t shaderType = 0; shaderType != PIPELINE_STAGE_COUNT; ++shaderType) {
|
||||
ShaderStage stage = (ShaderStage)shaderType;
|
||||
size_t bindingIdx = 0;
|
||||
|
||||
auto getShaderStage = [this](ShaderStage type) -> auto& {
|
||||
switch (type) {
|
||||
case ShaderStage::VERTEX: return vertexSamplerBlockInfo;
|
||||
case ShaderStage::FRAGMENT: return fragmentSamplerBlockInfo;
|
||||
}
|
||||
};
|
||||
|
||||
auto& samplerBlockInfo = getShaderStage(stage);
|
||||
|
||||
for (size_t samplerGroupIdx = 0; samplerGroupIdx != SAMPLER_GROUP_COUNT; ++samplerGroupIdx) {
|
||||
auto& groupData = samplerGroupInfo[samplerGroupIdx];
|
||||
auto stageFlags = groupData.stageFlags;
|
||||
if (!hasShaderType(stageFlags, static_cast<ShaderStage>(shaderType))) {
|
||||
continue;
|
||||
}
|
||||
auto& samplers = groupData.samplers;
|
||||
for (size_t samplerIdx = 0, c = samplers.size(); samplerIdx != c; ++samplerIdx) {
|
||||
samplerBlockInfo[bindingIdx].samplerGroup = samplerGroupIdx;
|
||||
samplerBlockInfo[bindingIdx].sampler = samplerIdx;
|
||||
++bindingIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Save this program's SamplerGroupInfo, it's used during draw calls to bind sampler groups to
|
||||
// the appropriate stage(s).
|
||||
samplerGroupInfo = program.getSamplerGroupInfo();
|
||||
}
|
||||
|
||||
MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels,
|
||||
@@ -555,14 +521,33 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
: HwTexture(target, levels, samples, width, height, depth, format, usage), context(context),
|
||||
externalImage(context) {
|
||||
texture = metalTexture;
|
||||
minLod = 0;
|
||||
maxLod = levels - 1;
|
||||
updateLodRange(0, levels - 1);
|
||||
}
|
||||
|
||||
MetalTexture::~MetalTexture() {
|
||||
externalImage.set(nullptr);
|
||||
}
|
||||
|
||||
id<MTLTexture> MetalTexture::getMtlTextureForRead() noexcept {
|
||||
if (lodTextureView) {
|
||||
return lodTextureView;
|
||||
}
|
||||
// The texture's swizzle remains constant throughout its lifetime, however its LOD range can
|
||||
// change. We'll cache the LOD view, and set lodTextureView to nil if minLod or maxLod is
|
||||
// updated.
|
||||
id<MTLTexture> t = swizzledTextureView ? swizzledTextureView : texture;
|
||||
if (!t) {
|
||||
return nil;
|
||||
}
|
||||
if (UTILS_UNLIKELY(minLod > maxLod)) {
|
||||
// If the texture does not have any available LODs, provide a view of only level 0.
|
||||
// Filament should prevent this from ever occurring.
|
||||
lodTextureView = createTextureViewWithLodRange(t, 0, 0);
|
||||
return lodTextureView;
|
||||
}
|
||||
lodTextureView = createTextureViewWithLodRange(t, minLod, maxLod);
|
||||
return lodTextureView;
|
||||
}
|
||||
|
||||
MTLPixelFormat MetalTexture::decidePixelFormat(MetalContext* context, TextureFormat format) {
|
||||
const MTLPixelFormat metalFormat = getMetalFormat(context, format);
|
||||
@@ -684,6 +669,13 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
|
||||
updateLodRange(level);
|
||||
}
|
||||
|
||||
void MetalTexture::generateMipmaps() noexcept {
|
||||
id <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(&context) blitCommandEncoder];
|
||||
[blitEncoder generateMipmapsForTexture:texture];
|
||||
[blitEncoder endEncoding];
|
||||
updateLodRange(0, texture.mipmapLevelCount - 1);
|
||||
}
|
||||
|
||||
void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
|
||||
PixelBufferDescriptor const& data) noexcept {
|
||||
const PixelBufferShape shape = PixelBufferShape::compute(data, format, region.size, byteOffset);
|
||||
@@ -804,8 +796,95 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
|
||||
}
|
||||
|
||||
void MetalTexture::updateLodRange(uint32_t level) {
|
||||
assert_invariant(!isInRenderPass(&context));
|
||||
minLod = std::min(minLod, level);
|
||||
maxLod = std::max(maxLod, level);
|
||||
lodTextureView = nil;
|
||||
}
|
||||
|
||||
void MetalTexture::updateLodRange(uint32_t min, uint32_t max) {
|
||||
assert_invariant(!isInRenderPass(&context));
|
||||
assert_invariant(min <= max);
|
||||
minLod = std::min(minLod, min);
|
||||
maxLod = std::max(maxLod, max);
|
||||
lodTextureView = nil;
|
||||
}
|
||||
|
||||
void MetalSamplerGroup::finalize() {
|
||||
assert_invariant(encoder);
|
||||
// TODO: we should be able to encode textures and samplers inside setFinalizedTexture and
|
||||
// setFinalizedSampler as they become available, but Metal doesn't seem to like this; the arg
|
||||
// buffer gets encoded incorrectly. This warrants more investigation.
|
||||
|
||||
auto [buffer, offset] = argBuffer->getCurrentAllocation();
|
||||
[encoder setArgumentBuffer:buffer offset:offset];
|
||||
|
||||
// Encode all textures and samplers.
|
||||
for (size_t s = 0; s < size; s++) {
|
||||
[encoder setTexture:textures[s] atIndex:(s * 2 + 0)];
|
||||
[encoder setSamplerState:samplers[s] atIndex:(s * 2 + 1)];
|
||||
}
|
||||
|
||||
finalized = true;
|
||||
}
|
||||
|
||||
void MetalSamplerGroup::reset(id<MTLCommandBuffer> cmdBuffer, id<MTLArgumentEncoder> e,
|
||||
id<MTLDevice> device) {
|
||||
encoder = e;
|
||||
|
||||
// The number of slots in the ring buffer we use to manage argument buffer allocations.
|
||||
// This number was chosen to avoid running out of slots and having to allocate a "fallback"
|
||||
// buffer when SamplerGroups are updated multiple times a frame. This value can reduced after
|
||||
// auditing Filament's calls to updateSamplerGroup, which should be as few times as possible.
|
||||
// For example, the bloom downsample pass should be refactored to maintain two separate
|
||||
// MaterialInstances instead of "ping ponging" between two texture bindings, which causes a
|
||||
// single SamplerGroup to be updated many times a frame.
|
||||
static constexpr auto METAL_ARGUMENT_BUFFER_SLOTS = 32;
|
||||
|
||||
MTLSizeAndAlign argBufferLayout;
|
||||
argBufferLayout.size = encoder.encodedLength;
|
||||
argBufferLayout.align = encoder.alignment;
|
||||
// Chances are, even though the MTLArgumentEncoder might change, the required size and alignment
|
||||
// probably won't. So we can re-use the previous ring buffer.
|
||||
if (UTILS_UNLIKELY(!argBuffer || !argBuffer->canAccomodateLayout(argBufferLayout))) {
|
||||
argBuffer = std::make_unique<MetalRingBuffer>(device, MTLResourceStorageModeShared,
|
||||
argBufferLayout, METAL_ARGUMENT_BUFFER_SLOTS);
|
||||
} else {
|
||||
argBuffer->createNewAllocation(cmdBuffer);
|
||||
}
|
||||
|
||||
// Clear all textures and samplers.
|
||||
assert_invariant(textureHandles.size() == textures.size());
|
||||
assert_invariant(textures.size() == samplers.size());
|
||||
for (size_t s = 0; s < textureHandles.size(); s++) {
|
||||
textureHandles[s] = {};
|
||||
textures[s] = nil;
|
||||
samplers[s] = nil;
|
||||
}
|
||||
|
||||
finalized = false;
|
||||
}
|
||||
|
||||
void MetalSamplerGroup::mutate(id<MTLCommandBuffer> cmdBuffer) {
|
||||
assert_invariant(finalized); // only makes sense to mutate if this sampler group is finalized
|
||||
assert_invariant(argBuffer);
|
||||
argBuffer->createNewAllocation(cmdBuffer);
|
||||
finalized = false;
|
||||
}
|
||||
|
||||
void MetalSamplerGroup::useResources(id<MTLRenderCommandEncoder> renderPassEncoder) {
|
||||
assert_invariant(finalized);
|
||||
if (@available(iOS 13, *)) {
|
||||
// TODO: pass only the appropriate stages to useResources.
|
||||
[renderPassEncoder useResources:textures.data()
|
||||
count:textures.size()
|
||||
usage:MTLResourceUsageRead | MTLResourceUsageSample
|
||||
stages:MTLRenderStageFragment | MTLRenderStageVertex];
|
||||
} else {
|
||||
[renderPassEncoder useResources:textures.data()
|
||||
count:textures.size()
|
||||
usage:MTLResourceUsageRead | MTLResourceUsageSample];
|
||||
}
|
||||
}
|
||||
|
||||
MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height,
|
||||
@@ -838,7 +917,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (samples > 1 && color[i].getSampleCount() == 1) {
|
||||
auto& sidecar = color[i].metalTexture->msaaSidecar;
|
||||
if (!sidecar) {
|
||||
sidecar = createMultisampledTexture(context->device, color[i].getPixelFormat(),
|
||||
sidecar = createMultisampledTexture(color[i].getPixelFormat(),
|
||||
color[i].metalTexture->width, color[i].metalTexture->height, samples);
|
||||
}
|
||||
}
|
||||
@@ -863,7 +942,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (samples > 1 && depth.getSampleCount() == 1) {
|
||||
auto& sidecar = depth.metalTexture->msaaSidecar;
|
||||
if (!sidecar) {
|
||||
sidecar = createMultisampledTexture(context->device, depth.getPixelFormat(),
|
||||
sidecar = createMultisampledTexture(depth.getPixelFormat(),
|
||||
depth.metalTexture->width, depth.metalTexture->height, samples);
|
||||
}
|
||||
}
|
||||
@@ -888,7 +967,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (samples > 1 && stencil.getSampleCount() == 1) {
|
||||
auto& sidecar = stencil.metalTexture->msaaSidecar;
|
||||
if (!sidecar) {
|
||||
sidecar = createMultisampledTexture(context->device, stencil.getPixelFormat(),
|
||||
sidecar = createMultisampledTexture(stencil.getPixelFormat(),
|
||||
stencil.metalTexture->width, stencil.metalTexture->height, samples);
|
||||
}
|
||||
}
|
||||
@@ -975,6 +1054,7 @@ void MetalRenderTarget::setUpRenderPassAttachments(MTLRenderPassDescriptor* desc
|
||||
descriptor.depthAttachment.slice = 0;
|
||||
const bool discard = any(discardFlags & TargetBufferFlags::DEPTH);
|
||||
if (!discard) {
|
||||
assert_invariant(context->supportsAutoDepthResolve);
|
||||
descriptor.depthAttachment.resolveTexture = depthAttachment.getTexture();
|
||||
descriptor.depthAttachment.resolveLevel = depthAttachment.level;
|
||||
descriptor.depthAttachment.resolveSlice = depthAttachment.layer;
|
||||
@@ -1007,10 +1087,14 @@ void MetalRenderTarget::setUpRenderPassAttachments(MTLRenderPassDescriptor* desc
|
||||
descriptor.stencilAttachment.slice = 0;
|
||||
const bool discard = any(discardFlags & TargetBufferFlags::STENCIL);
|
||||
if (!discard) {
|
||||
assert_invariant(context->supportsAutoDepthResolve);
|
||||
descriptor.stencilAttachment.resolveTexture = stencilAttachment.getTexture();
|
||||
descriptor.stencilAttachment.resolveLevel = stencilAttachment.level;
|
||||
descriptor.stencilAttachment.resolveSlice = stencilAttachment.layer;
|
||||
descriptor.stencilAttachment.storeAction = MTLStoreActionMultisampleResolve;
|
||||
if (@available(iOS 12.0, *)) {
|
||||
descriptor.stencilAttachment.stencilResolveFilter = MTLMultisampleStencilResolveFilterSample0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1072,8 +1156,8 @@ MTLStoreAction MetalRenderTarget::getStoreAction(const RenderPassParams& params,
|
||||
return MTLStoreActionStore;
|
||||
}
|
||||
|
||||
id<MTLTexture> MetalRenderTarget::createMultisampledTexture(id<MTLDevice> device,
|
||||
MTLPixelFormat format, uint32_t width, uint32_t height, uint8_t samples) {
|
||||
id<MTLTexture> MetalRenderTarget::createMultisampledTexture(MTLPixelFormat format,
|
||||
uint32_t width, uint32_t height, uint8_t samples) const {
|
||||
MTLTextureDescriptor* descriptor =
|
||||
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
|
||||
width:width
|
||||
@@ -1084,7 +1168,13 @@ id<MTLTexture> MetalRenderTarget::createMultisampledTexture(id<MTLDevice> device
|
||||
descriptor.usage = MTLTextureUsageRenderTarget;
|
||||
descriptor.resourceOptions = MTLResourceStorageModePrivate;
|
||||
|
||||
return [device newTextureWithDescriptor:descriptor];
|
||||
if (context->supportsMemorylessRenderTargets) {
|
||||
if (@available(macOS 11.0, *)) {
|
||||
descriptor.resourceOptions = MTLResourceStorageModeMemoryless;
|
||||
}
|
||||
}
|
||||
|
||||
return [context->device newTextureWithDescriptor:descriptor];
|
||||
}
|
||||
|
||||
math::uint2 MetalRenderTarget::getAttachmentSize() noexcept {
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <memory>
|
||||
#include <tsl/robin_map.h>
|
||||
#include <utils/Hash.h>
|
||||
@@ -35,17 +37,26 @@ 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
|
||||
// 1-16 Filament vertex buffers 16 limited by MAX_VERTEX_BUFFER_COUNT
|
||||
// 17-26 Uniform buffers 10
|
||||
// 27-30 Sampler Uniforms (argument buffers) 4 TODO: use arg buffers for samplers
|
||||
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
|
||||
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
|
||||
//
|
||||
// Total 31
|
||||
|
||||
static constexpr uint32_t SAMPLER_GROUP_COUNT = Program::SAMPLER_BINDING_COUNT;
|
||||
static constexpr uint32_t SAMPLER_BINDING_COUNT = MAX_SAMPLER_COUNT;
|
||||
// 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.
|
||||
@@ -58,10 +69,10 @@ static constexpr uint32_t ZERO_VERTEX_BUFFER_BINDING = 0u;
|
||||
|
||||
static constexpr uint32_t USER_VERTEX_BUFFER_BINDING_START = 1u;
|
||||
|
||||
// The max number of uniform buffers that the Metal backend can simultaneously bind.
|
||||
static constexpr uint32_t UNIFORM_BUFFER_COUNT = 10u;
|
||||
// This constant must match the equivalent in CodeGenerator.cpp.
|
||||
// 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.
|
||||
inline bool operator==(const MTLViewport& lhs, const MTLViewport& rhs);
|
||||
@@ -143,9 +154,11 @@ static_assert(sizeof(BlendState) == 56, "BlendState is unexpected size.");
|
||||
// StateCache caches Metal state objects using StateType as a key.
|
||||
// MetalType is the corresponding Metal API type.
|
||||
// StateCreator is a functor that creates a new state of type MetalType.
|
||||
// HashFn is a functor that hashes StateType.
|
||||
template<typename StateType,
|
||||
typename MetalType,
|
||||
typename StateCreator>
|
||||
typename StateCreator,
|
||||
typename HashFn = utils::hash::MurmurHashFn<StateType>>
|
||||
class StateCache {
|
||||
|
||||
public:
|
||||
@@ -158,6 +171,8 @@ public:
|
||||
void setDevice(id<MTLDevice> device) noexcept { mDevice = device; }
|
||||
|
||||
MetalType getOrCreateState(const StateType& state) noexcept {
|
||||
assert_invariant(mDevice);
|
||||
|
||||
// Check if a valid state already exists in the cache.
|
||||
auto iter = mStateCache.find(state);
|
||||
if (UTILS_LIKELY(iter != mStateCache.end())) {
|
||||
@@ -167,6 +182,7 @@ public:
|
||||
|
||||
// If we reach this point, we couldn't find one in the cache; create a new one.
|
||||
const auto& metalObject = creator(mDevice, state);
|
||||
assert_invariant(metalObject);
|
||||
|
||||
mStateCache.emplace(std::make_pair(
|
||||
state,
|
||||
@@ -181,7 +197,6 @@ private:
|
||||
StateCreator creator;
|
||||
id<MTLDevice> mDevice = nil;
|
||||
|
||||
using HashFn = utils::hash::MurmurHashFn<StateType>;
|
||||
tsl::robin_map<StateType, MetalType, HashFn> mStateCache;
|
||||
|
||||
};
|
||||
@@ -333,38 +348,19 @@ 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 {
|
||||
SamplerParams samplerParams;
|
||||
uint32_t minLod = 0;
|
||||
uint32_t maxLod = UINT_MAX;
|
||||
|
||||
bool operator==(const SamplerState& rhs) const noexcept {
|
||||
return this->samplerParams == rhs.samplerParams &&
|
||||
this->minLod == rhs.minLod &&
|
||||
this->maxLod == rhs.maxLod;
|
||||
return this->samplerParams == rhs.samplerParams;
|
||||
}
|
||||
|
||||
bool operator!=(const SamplerState& rhs) const noexcept {
|
||||
@@ -372,7 +368,7 @@ struct SamplerState {
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(SamplerState) == 12, "SamplerState unexpected size.");
|
||||
static_assert(sizeof(SamplerState) == 4, "SamplerState unexpected size.");
|
||||
|
||||
struct SamplerStateCreator {
|
||||
id<MTLSamplerState> operator()(id<MTLDevice> device, const SamplerState& state) noexcept;
|
||||
@@ -385,6 +381,38 @@ using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerS
|
||||
using CullModeStateTracker = StateTracker<MTLCullMode>;
|
||||
using WindingStateTracker = StateTracker<MTLWinding>;
|
||||
|
||||
// Argument encoder
|
||||
|
||||
struct ArgumentEncoderState {
|
||||
utils::FixedCapacityVector<MTLTextureType> textureTypes;
|
||||
|
||||
explicit ArgumentEncoderState(utils::FixedCapacityVector<MTLTextureType>&& types)
|
||||
: textureTypes(std::move(types)) {}
|
||||
|
||||
bool operator==(const ArgumentEncoderState& rhs) const noexcept {
|
||||
return std::equal(textureTypes.begin(), textureTypes.end(), rhs.textureTypes.begin(),
|
||||
rhs.textureTypes.end());
|
||||
}
|
||||
|
||||
bool operator!=(const ArgumentEncoderState& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct ArgumentEncoderHasher {
|
||||
uint32_t operator()(const ArgumentEncoderState& key) const noexcept {
|
||||
return utils::hash::murmur3((const uint32_t*)key.textureTypes.data(),
|
||||
sizeof(MTLTextureType) * key.textureTypes.size() / 4, 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct ArgumentEncoderCreator {
|
||||
id<MTLArgumentEncoder> operator()(id<MTLDevice> device, const ArgumentEncoderState& state) noexcept;
|
||||
};
|
||||
|
||||
using ArgumentEncoderCache = StateCache<ArgumentEncoderState, id<MTLArgumentEncoder>,
|
||||
ArgumentEncoderCreator, ArgumentEncoderHasher>;
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -137,11 +137,10 @@ id<MTLSamplerState> SamplerStateCreator::operator()(id<MTLDevice> device,
|
||||
samplerDescriptor.tAddressMode = getAddressMode(params.wrapT);
|
||||
samplerDescriptor.rAddressMode = getAddressMode(params.wrapR);
|
||||
samplerDescriptor.maxAnisotropy = 1u << params.anisotropyLog2;
|
||||
samplerDescriptor.lodMaxClamp = (float) state.maxLod;
|
||||
samplerDescriptor.lodMinClamp = (float) state.minLod;
|
||||
samplerDescriptor.compareFunction =
|
||||
params.compareMode == SamplerCompareMode::NONE ?
|
||||
MTLCompareFunctionNever : getCompareFunction(params.compareFunc);
|
||||
samplerDescriptor.supportArgumentBuffers = YES;
|
||||
|
||||
#if defined(IOS)
|
||||
// Older Apple devices (and the simulator) don't support setting a comparison function in
|
||||
@@ -156,5 +155,38 @@ id<MTLSamplerState> SamplerStateCreator::operator()(id<MTLDevice> device,
|
||||
return [device newSamplerStateWithDescriptor:samplerDescriptor];
|
||||
}
|
||||
|
||||
id<MTLArgumentEncoder> ArgumentEncoderCreator::operator()(id<MTLDevice> device,
|
||||
const ArgumentEncoderState &state) noexcept {
|
||||
const auto& textureTypes = state.textureTypes;
|
||||
const auto& count = textureTypes.size();
|
||||
assert_invariant(count > 0);
|
||||
|
||||
// Metal has separate data types for textures versus samplers, so the argument buffer layout
|
||||
// alternates between texture and sampler, i.e.:
|
||||
// textureA
|
||||
// samplerA
|
||||
// textureB
|
||||
// samplerB
|
||||
// etc
|
||||
NSMutableArray<MTLArgumentDescriptor*>* arguments =
|
||||
[NSMutableArray arrayWithCapacity:(count * 2)];
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor];
|
||||
textureArgument.index = i * 2 + 0;
|
||||
textureArgument.dataType = MTLDataTypeTexture;
|
||||
textureArgument.textureType = textureTypes[i];
|
||||
textureArgument.access = MTLArgumentAccessReadOnly;
|
||||
[arguments addObject:textureArgument];
|
||||
|
||||
MTLArgumentDescriptor* samplerArgument = [MTLArgumentDescriptor argumentDescriptor];
|
||||
samplerArgument.index = i * 2 + 1;
|
||||
samplerArgument.dataType = MTLDataTypeSampler;
|
||||
textureArgument.access = MTLArgumentAccessReadOnly;
|
||||
[arguments addObject:samplerArgument];
|
||||
}
|
||||
|
||||
return [device newArgumentEncoderWithArguments:arguments];
|
||||
}
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
@@ -29,6 +29,14 @@ API_AVAILABLE(ios(13.0))
|
||||
id<MTLTexture> createTextureViewWithSwizzle(id<MTLTexture> texture,
|
||||
MTLTextureSwizzleChannels swizzle);
|
||||
|
||||
/**
|
||||
* Creates a texture view of the passed-in texture with the given lod range (inclusive). If the
|
||||
* range represents all mip levels, simply returns texture.
|
||||
* lodMax must be greater than or equal to lodMin.
|
||||
*/
|
||||
id<MTLTexture> createTextureViewWithLodRange(id<MTLTexture> texture, NSUInteger lodMin,
|
||||
NSUInteger lodMax);
|
||||
|
||||
/**
|
||||
* Creates a texture view of the passed-in texture with the given layer.
|
||||
*
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "MetalUtils.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
id<MTLTexture> createTextureViewWithSwizzle(id<MTLTexture> texture,
|
||||
@@ -37,6 +39,24 @@ id<MTLTexture> createTextureViewWithSwizzle(id<MTLTexture> texture,
|
||||
swizzle:swizzle];
|
||||
}
|
||||
|
||||
id<MTLTexture> createTextureViewWithLodRange(id<MTLTexture> texture, NSUInteger lodMin,
|
||||
NSUInteger lodMax) {
|
||||
assert_invariant(lodMax >= lodMin);
|
||||
if (lodMin == 0 && lodMax >= texture.mipmapLevelCount - 1) {
|
||||
return texture;
|
||||
}
|
||||
NSUInteger slices = texture.arrayLength;
|
||||
if (texture.textureType == MTLTextureTypeCube ||
|
||||
texture.textureType == MTLTextureTypeCubeArray) {
|
||||
slices *= 6;
|
||||
}
|
||||
NSRange levelRange = NSMakeRange(lodMin, lodMax - lodMin + 1);
|
||||
return [texture newTextureViewWithPixelFormat:texture.pixelFormat
|
||||
textureType:texture.textureType
|
||||
levels:levelRange
|
||||
slices:NSMakeRange(0, slices)];
|
||||
}
|
||||
|
||||
id<MTLTexture> createTextureViewWithSingleSlice(id<MTLTexture> texture, NSUInteger slice) {
|
||||
if (texture.textureType == MTLTextureType2D) {
|
||||
return texture;
|
||||
|
||||
@@ -273,8 +273,11 @@ void NoopDriver::commit(Handle<HwSwapChain> sch) {
|
||||
void NoopDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> ubh) {
|
||||
}
|
||||
|
||||
void NoopDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> ubh,
|
||||
uint32_t offset, uint32_t size) {
|
||||
void NoopDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
|
||||
Handle<HwBufferObject> ubh, uint32_t offset, uint32_t size) {
|
||||
}
|
||||
|
||||
void NoopDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
|
||||
}
|
||||
|
||||
void NoopDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
@@ -301,6 +304,11 @@ void NoopDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void NoopDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void NoopDriver::blit(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
@@ -311,10 +319,16 @@ void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph
|
||||
uint32_t instanceCount) {
|
||||
}
|
||||
|
||||
void NoopDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
}
|
||||
|
||||
void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
}
|
||||
|
||||
void NoopDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
}
|
||||
|
||||
void NoopDriver::resetState(int) {
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -119,6 +119,8 @@ constexpr inline GLenum getBufferBindingType(BufferObjectBinding bindingType) no
|
||||
return GL_ARRAY_BUFFER;
|
||||
case BufferObjectBinding::UNIFORM:
|
||||
return GL_UNIFORM_BUFFER;
|
||||
case BufferObjectBinding::SHADER_STORAGE:
|
||||
return GL_SHADER_STORAGE_BUFFER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,21 +60,30 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &gets.max_samples);
|
||||
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];
|
||||
constexpr GLint MAX_VERTEX_SAMPLER_COUNT = caps3.MAX_VERTEX_SAMPLER_COUNT;
|
||||
constexpr GLint MAX_FRAGMENT_SAMPLER_COUNT = caps3.MAX_FRAGMENT_SAMPLER_COUNT;
|
||||
|
||||
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) {
|
||||
initExtensionsGLES();
|
||||
if (state.major == 3) {
|
||||
assert_invariant(gets.max_texture_image_units >= 16);
|
||||
if (state.minor >= 0) {
|
||||
mShaderModel = ShaderModel::MOBILE;
|
||||
}
|
||||
assert_invariant(gets.max_combined_texture_image_units >= 32);
|
||||
if (state.minor >= 1) {
|
||||
features.multisample_texture = true;
|
||||
// figure out our feature level
|
||||
if (gets.max_texture_image_units >= 31 &&
|
||||
ext.EXT_texture_cube_map_array) {
|
||||
if (ext.EXT_texture_cube_map_array) {
|
||||
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
|
||||
if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT &&
|
||||
gets.max_combined_texture_image_units >=
|
||||
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
|
||||
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,14 +91,16 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
// OpenGL version
|
||||
initExtensionsGL();
|
||||
if (state.major == 4) {
|
||||
if (state.minor >= 1) {
|
||||
mShaderModel = ShaderModel::DESKTOP;
|
||||
}
|
||||
assert_invariant(state.minor >= 1);
|
||||
mShaderModel = ShaderModel::DESKTOP;
|
||||
if (state.minor >= 3) {
|
||||
// cubemap arrays are available as of OpenGL 4.0
|
||||
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
|
||||
// figure out our feature level
|
||||
if (gets.max_texture_image_units >= 31) {
|
||||
// cubemap arrays are available as of OpenGL 4.0
|
||||
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
|
||||
if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT &&
|
||||
gets.max_combined_texture_image_units >=
|
||||
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
|
||||
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_3;
|
||||
}
|
||||
}
|
||||
features.multisample_texture = true;
|
||||
@@ -97,8 +108,8 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
// feedback loops are allowed on GL desktop as long as writes are disabled
|
||||
bugs.allow_read_only_ancillary_feedback_loop = true;
|
||||
assert_invariant(gets.max_texture_image_units >= 16);
|
||||
assert_invariant(gets.max_combined_texture_image_units >= 32);
|
||||
}
|
||||
|
||||
#ifdef GL_EXT_texture_filter_anisotropic
|
||||
if (ext.EXT_texture_filter_anisotropic) {
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gets.max_anisotropy);
|
||||
@@ -213,6 +224,7 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
// TODO: see if we could use `bugs.allow_read_only_ancillary_feedback_loop = true`
|
||||
}
|
||||
|
||||
slog.v << "Feature level: " << +mFeatureLevel << '\n';
|
||||
slog.v << "Active workarounds: " << '\n';
|
||||
UTILS_NOUNROLL
|
||||
for (auto [enabled, name, _] : mBugDatabase) {
|
||||
@@ -237,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) {
|
||||
@@ -280,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,
|
||||
@@ -315,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) {
|
||||
@@ -511,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
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -35,6 +36,7 @@ namespace filament::backend {
|
||||
class OpenGLContext {
|
||||
public:
|
||||
static constexpr const size_t MAX_TEXTURE_UNIT_COUNT = MAX_SAMPLER_COUNT;
|
||||
static constexpr const size_t DUMMY_TEXTURE_BINDING = 31; // highest binding less than 32
|
||||
static constexpr const size_t MAX_BUFFER_BINDINGS = 32;
|
||||
typedef math::details::TVec4<GLint> vec4gli;
|
||||
typedef math::details::TVec2<GLclampf> vec2glf;
|
||||
@@ -71,6 +73,7 @@ public:
|
||||
|
||||
ShaderModel getShaderModel() const noexcept { return mShaderModel; }
|
||||
|
||||
void resetState() noexcept;
|
||||
|
||||
inline void useProgram(GLuint program) noexcept;
|
||||
|
||||
@@ -126,6 +129,9 @@ public:
|
||||
GLint max_samples;
|
||||
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 = {};
|
||||
|
||||
@@ -308,7 +314,7 @@ public:
|
||||
GLsizeiptr size = 0;
|
||||
} buffers[MAX_BUFFER_BINDINGS];
|
||||
} targets[2]; // there are only 2 indexed buffer target (uniform and transform feedback)
|
||||
GLuint genericBinding[8] = { 0 };
|
||||
GLuint genericBinding[9] = { 0 };
|
||||
} buffers;
|
||||
|
||||
struct {
|
||||
@@ -406,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]);
|
||||
|
||||
@@ -457,17 +465,18 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
|
||||
constexpr size_t OpenGLContext::getIndexForBufferTarget(GLenum target) noexcept {
|
||||
size_t index = 0;
|
||||
switch (target) {
|
||||
// The indexed buffers MUST be first in this list
|
||||
// The indexed buffers MUST be first in this list (those usable with bindBufferRange)
|
||||
case GL_UNIFORM_BUFFER: index = 0; break;
|
||||
case GL_TRANSFORM_FEEDBACK_BUFFER: index = 1; break;
|
||||
case GL_SHADER_STORAGE_BUFFER: index = 2; break;
|
||||
|
||||
case GL_ARRAY_BUFFER: index = 2; break;
|
||||
case GL_COPY_READ_BUFFER: index = 3; break;
|
||||
case GL_COPY_WRITE_BUFFER: index = 4; break;
|
||||
case GL_ELEMENT_ARRAY_BUFFER: index = 5; break;
|
||||
case GL_PIXEL_PACK_BUFFER: index = 6; break;
|
||||
case GL_PIXEL_UNPACK_BUFFER: index = 7; break;
|
||||
default: index = 8; break; // should never happen
|
||||
case GL_ARRAY_BUFFER: index = 3; break;
|
||||
case GL_COPY_READ_BUFFER: index = 4; break;
|
||||
case GL_COPY_WRITE_BUFFER: index = 5; break;
|
||||
case GL_ELEMENT_ARRAY_BUFFER: index = 6; break;
|
||||
case GL_PIXEL_PACK_BUFFER: index = 7; break;
|
||||
case GL_PIXEL_UNPACK_BUFFER: index = 8; break;
|
||||
default: index = 9; break; // should never happen
|
||||
}
|
||||
assert_invariant(index < sizeof(state.buffers.genericBinding)/sizeof(state.buffers.genericBinding[0])); // NOLINT(misc-redundant-expression)
|
||||
return index;
|
||||
@@ -528,7 +537,7 @@ void OpenGLContext::bindVertexArray(RenderPrimitive const* p) noexcept {
|
||||
void OpenGLContext::bindBufferRange(GLenum target, GLuint index, GLuint buffer,
|
||||
GLintptr offset, GLsizeiptr size) noexcept {
|
||||
size_t targetIndex = getIndexForBufferTarget(target);
|
||||
assert_invariant(targetIndex <= 1); // validity check
|
||||
assert_invariant(targetIndex <= 2); // validity check
|
||||
|
||||
// this ALSO sets the generic binding
|
||||
if ( state.buffers.targets[targetIndex].buffers[index].name != buffer
|
||||
|
||||
@@ -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);
|
||||
@@ -512,8 +515,8 @@ void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t,
|
||||
|
||||
auto& gl = mContext;
|
||||
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
|
||||
switch (t->gl.target) {
|
||||
case GL_TEXTURE_2D:
|
||||
@@ -1871,8 +1874,8 @@ void OpenGLDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint
|
||||
auto& gl = mContext;
|
||||
|
||||
GLTexture* t = handle_cast<GLTexture *>(th);
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
|
||||
// Must fit within int8_t.
|
||||
assert_invariant(minLevel <= 0x7f && maxLevel <= 0x7f);
|
||||
@@ -1908,8 +1911,8 @@ void OpenGLDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
assert_invariant(t->gl.target != GL_TEXTURE_2D_MULTISAMPLE);
|
||||
// Note: glGenerateMimap can also fail if the internal format is not both
|
||||
// color-renderable and filterable (i.e.: doesn't work for depth)
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
|
||||
t->gl.baseLevel = 0;
|
||||
t->gl.maxLevel = static_cast<int8_t>(t->levels - 1);
|
||||
@@ -1956,8 +1959,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
// fallthrough...
|
||||
case SamplerType::SAMPLER_2D:
|
||||
// NOTE: GL_TEXTURE_2D_MULTISAMPLE is not allowed
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
assert_invariant(t->gl.target == GL_TEXTURE_2D);
|
||||
glTexSubImage2D(t->gl.target, GLint(level),
|
||||
GLint(xoffset), GLint(yoffset),
|
||||
@@ -1965,8 +1968,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
assert_invariant(zoffset + depth <= std::max(1u, t->depth >> level));
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
assert_invariant(t->gl.target == GL_TEXTURE_3D);
|
||||
glTexSubImage3D(t->gl.target, GLint(level),
|
||||
GLint(xoffset), GLint(yoffset), GLint(zoffset),
|
||||
@@ -1976,8 +1979,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
assert_invariant(zoffset + depth <= t->depth);
|
||||
// NOTE: GL_TEXTURE_2D_MULTISAMPLE is not allowed
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
assert_invariant(t->gl.target == GL_TEXTURE_2D_ARRAY ||
|
||||
t->gl.target == GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
glTexSubImage3D(t->gl.target, GLint(level),
|
||||
@@ -1986,8 +1989,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP: {
|
||||
assert_invariant(t->gl.target == GL_TEXTURE_CUBE_MAP);
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
|
||||
assert_invariant(width == height);
|
||||
const size_t faceSize = PixelBufferDescriptor::computeDataSize(
|
||||
@@ -2050,8 +2053,8 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
|
||||
// fallthrough...
|
||||
case SamplerType::SAMPLER_2D:
|
||||
// NOTE: GL_TEXTURE_2D_MULTISAMPLE is not allowed
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
assert_invariant(t->gl.target == GL_TEXTURE_2D);
|
||||
glCompressedTexSubImage2D(t->gl.target, GLint(level),
|
||||
GLint(xoffset), GLint(yoffset),
|
||||
@@ -2059,8 +2062,8 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
|
||||
t->gl.internalFormat, imageSize, p.buffer);
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
assert_invariant(t->gl.target == GL_TEXTURE_3D);
|
||||
glCompressedTexSubImage3D(t->gl.target, GLint(level),
|
||||
GLint(xoffset), GLint(yoffset), GLint(zoffset),
|
||||
@@ -2078,8 +2081,8 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP: {
|
||||
assert_invariant(t->gl.target == GL_TEXTURE_CUBE_MAP);
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
|
||||
assert_invariant(width == height);
|
||||
const size_t faceSize = PixelBufferDescriptor::computeDataSize(
|
||||
@@ -2139,8 +2142,8 @@ void OpenGLDriver::setExternalTexture(GLTexture* t, void* image) {
|
||||
assert_invariant(t->target == SamplerType::SAMPLER_EXTERNAL);
|
||||
assert_invariant(t->gl.target == GL_TEXTURE_EXTERNAL_OES);
|
||||
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
|
||||
#ifdef GL_OES_EGL_image
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, static_cast<GLeglImageOES>(image));
|
||||
@@ -2574,15 +2577,31 @@ void OpenGLDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> ubh)
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
void OpenGLDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> ubh,
|
||||
uint32_t offset, uint32_t size) {
|
||||
void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
|
||||
Handle<HwBufferObject> ubh, uint32_t offset, uint32_t size) {
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
|
||||
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
|
||||
bindingType == BufferObjectBinding::UNIFORM);
|
||||
|
||||
GLBufferObject* ub = handle_cast<GLBufferObject *>(ubh);
|
||||
assert_invariant(ub->gl.binding == GL_UNIFORM_BUFFER);
|
||||
|
||||
GLenum target = GLUtils::getBufferBindingType(bindingType);
|
||||
|
||||
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
|
||||
ub->gl.binding == target);
|
||||
|
||||
assert_invariant(offset + size <= ub->byteCount);
|
||||
gl.bindBufferRange(ub->gl.binding, GLuint(index), ub->gl.id, offset, size);
|
||||
gl.bindBufferRange(target, GLuint(index), ub->gl.id, offset, size);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
void OpenGLDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
GLenum target = GLUtils::getBufferBindingType(bindingType);
|
||||
gl.bindBufferRange(target, GLuint(index), 0, 0, 0);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
@@ -2759,6 +2778,58 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
|
||||
auto& gl = mContext;
|
||||
|
||||
GLBufferObject const* bo = handle_cast<GLBufferObject const*>(boh);
|
||||
|
||||
// TODO: measure the two solutions
|
||||
if constexpr (true) {
|
||||
// schedule a copy of the buffer we're reading into a PBO, this *should* happen
|
||||
// asynchronously without stalling the CPU.
|
||||
GLuint pbo;
|
||||
glGenBuffers(1, &pbo);
|
||||
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, (GLsizeiptr)size, nullptr, GL_STATIC_DRAW);
|
||||
gl.bindBuffer(bo->gl.binding, bo->gl.id);
|
||||
glCopyBufferSubData(bo->gl.binding, GL_PIXEL_PACK_BUFFER, offset, 0, size);
|
||||
gl.bindBuffer(bo->gl.binding, 0);
|
||||
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
// then, we schedule a mapBuffer of the PBO later, once the fence has signaled
|
||||
auto* pUserBuffer = new BufferDescriptor(std::move(p));
|
||||
whenGpuCommandsComplete([this, size, pbo, pUserBuffer]() mutable {
|
||||
BufferDescriptor& p = *pUserBuffer;
|
||||
auto& gl = mContext;
|
||||
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
|
||||
void* vaddr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, size, GL_MAP_READ_BIT);
|
||||
if (vaddr) {
|
||||
memcpy(p.buffer, vaddr, size);
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
}
|
||||
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
glDeleteBuffers(1, &pbo);
|
||||
scheduleDestroy(std::move(p));
|
||||
delete pUserBuffer;
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
});
|
||||
} else {
|
||||
gl.bindBuffer(bo->gl.binding, bo->gl.id);
|
||||
// TODO: this glMapBufferRange may stall. Ideally we want to use whenGpuCommandsComplete
|
||||
// but that's tricky because boh could be destroyed right after this call.
|
||||
void* vaddr = glMapBufferRange(bo->gl.binding, offset, size, GL_MAP_READ_BIT);
|
||||
if (vaddr) {
|
||||
memcpy(p.buffer, vaddr, size);
|
||||
glUnmapBuffer(bo->gl.binding);
|
||||
}
|
||||
gl.bindBuffer(bo->gl.binding, 0);
|
||||
scheduleDestroy(std::move(p));
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::whenGpuCommandsComplete(std::function<void()> fn) noexcept {
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
mGpuCommandCompleteOps.emplace_back(sync, std::move(fn));
|
||||
@@ -2872,7 +2943,7 @@ void OpenGLDriver::endFrame(uint32_t frameId) {
|
||||
// at least do some minimal safety things here, such as resetting the VAO to 0.
|
||||
auto& gl = mContext;
|
||||
gl.bindVertexArray(nullptr);
|
||||
for (int unit = OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1; unit >= 0; unit--) {
|
||||
for (int unit = OpenGLContext::DUMMY_TEXTURE_BINDING; unit >= 0; unit--) {
|
||||
gl.bindTexture(unit, GL_TEXTURE_2D, 0);
|
||||
}
|
||||
gl.disable(GL_CULL_FACE);
|
||||
@@ -3033,8 +3104,8 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
|
||||
auto& gl = mContext;
|
||||
if (texture && any(texture->usage & TextureUsage::SAMPLEABLE)) {
|
||||
if (targetLevel < texture->gl.baseLevel || targetLevel > texture->gl.maxLevel) {
|
||||
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, texture);
|
||||
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, texture);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
if (targetLevel < texture->gl.baseLevel) {
|
||||
texture->gl.baseLevel = targetLevel;
|
||||
glTexParameteri(texture->gl.target, GL_TEXTURE_BASE_LEVEL, targetLevel);
|
||||
@@ -3102,6 +3173,32 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
executeRenderPassOps();
|
||||
|
||||
OpenGLProgram* p = handle_cast<OpenGLProgram*>(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(!p->isValid())) {
|
||||
return;
|
||||
}
|
||||
|
||||
useProgram(p);
|
||||
|
||||
#if defined(GL_ES_VERSION_3_1) || defined(GL_VERSION_4_3)
|
||||
// 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
|
||||
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
|
||||
#else
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
#endif
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
template class ConcreteDispatcher<OpenGLDriver>;
|
||||
|
||||
|
||||
@@ -120,15 +120,12 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
|
||||
// build all shaders
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
|
||||
ShaderStage type = static_cast<ShaderStage>(i);
|
||||
const ShaderStage stage = static_cast<ShaderStage>(i);
|
||||
GLenum glShaderType;
|
||||
switch (type) {
|
||||
case ShaderStage::VERTEX:
|
||||
glShaderType = GL_VERTEX_SHADER;
|
||||
break;
|
||||
case ShaderStage::FRAGMENT:
|
||||
glShaderType = GL_FRAGMENT_SHADER;
|
||||
break;
|
||||
switch (stage) {
|
||||
case ShaderStage::VERTEX: glShaderType = GL_VERTEX_SHADER; break;
|
||||
case ShaderStage::FRAGMENT: glShaderType = GL_FRAGMENT_SHADER; break;
|
||||
case ShaderStage::COMPUTE: glShaderType = GL_COMPUTE_SHADER; break;
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(!shadersSource[i].empty())) {
|
||||
@@ -428,6 +425,7 @@ void logCompilationError(io::ostream& out, ShaderStage shaderType,
|
||||
switch (type) {
|
||||
case ShaderStage::VERTEX: return "vertex";
|
||||
case ShaderStage::FRAGMENT: return "fragment";
|
||||
case ShaderStage::COMPUTE: return "compute";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -19,10 +19,14 @@
|
||||
|
||||
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__)
|
||||
|
||||
#include <GLES3/gl3.h>
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
# include <GLES3/gl3.h>
|
||||
#else
|
||||
# include <GLES3/gl31.h>
|
||||
#endif
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
/* The Android NDK doesn't exposes extensions, fake it with eglGetProcAddress */
|
||||
/* The Android NDK doesn't expose extensions, fake it with eglGetProcAddress */
|
||||
namespace glext {
|
||||
// importGLESExtensionsEntryPoints is thread-safe and can be called multiple times.
|
||||
// it is currently called from PlatformEGL.
|
||||
@@ -50,49 +54,13 @@
|
||||
#endif
|
||||
#ifdef GL_EXT_disjoint_timer_query
|
||||
extern PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v;
|
||||
#define GL_TIME_ELAPSED 0x88BF
|
||||
#endif
|
||||
#ifdef GL_EXT_clip_control
|
||||
extern PFNGLCLIPCONTROLEXTPROC glClipControl;
|
||||
#ifndef GL_LOWER_LEFT
|
||||
# define GL_LOWER_LEFT GL_LOWER_LEFT_EXT
|
||||
#endif
|
||||
#ifndef GL_ZERO_TO_ONE
|
||||
# define GL_ZERO_TO_ONE GL_ZERO_TO_ONE_EXT
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Prevent lots of #ifdef's between desktop and mobile by providing some suffix-free constants:
|
||||
#define GL_DEBUG_OUTPUT 0x92E0
|
||||
#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
|
||||
|
||||
#define GL_DEBUG_SEVERITY_HIGH 0x9146
|
||||
#define GL_DEBUG_SEVERITY_MEDIUM 0x9147
|
||||
#define GL_DEBUG_SEVERITY_LOW 0x9148
|
||||
#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
|
||||
|
||||
#define GL_DEBUG_TYPE_MARKER 0x8268
|
||||
#define GL_DEBUG_TYPE_ERROR 0x824C
|
||||
#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D
|
||||
#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E
|
||||
#define GL_DEBUG_TYPE_PORTABILITY 0x824F
|
||||
#define GL_DEBUG_TYPE_PERFORMANCE 0x8250
|
||||
#define GL_DEBUG_TYPE_OTHER 0x8251
|
||||
|
||||
#define glDebugMessageCallback glext::glDebugMessageCallbackKHR
|
||||
|
||||
#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
|
||||
#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
|
||||
#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
|
||||
#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
|
||||
#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
|
||||
#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
|
||||
#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
|
||||
#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
|
||||
#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
|
||||
|
||||
using namespace glext;
|
||||
using namespace glext;
|
||||
|
||||
#elif defined(IOS)
|
||||
|
||||
@@ -101,27 +69,6 @@ using namespace glext;
|
||||
#include <OpenGLES/ES3/gl.h>
|
||||
#include <OpenGLES/ES3/glext.h>
|
||||
|
||||
/* The iOS SDK only provides OpenGL ES headers up to 3.0. Filament works with OpenGL 3.0, but
|
||||
* requires the following 3.1 declarations in order to compile. */
|
||||
|
||||
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
|
||||
#define GL_TIME_ELAPSED 0x88BF
|
||||
|
||||
#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
|
||||
#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
|
||||
#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
|
||||
#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
|
||||
#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
|
||||
#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
|
||||
#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
|
||||
#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
|
||||
#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
|
||||
|
||||
#ifdef GL_EXT_multisampled_render_to_texture
|
||||
extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT;
|
||||
extern PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT;
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
// bluegl exposes symbols prefixed with bluegl_ to avoid clashing with clients that also link
|
||||
@@ -135,6 +82,111 @@ using namespace glext;
|
||||
|
||||
#endif
|
||||
|
||||
#if (!defined(GL_ES_VERSION_2_0) && !defined(GL_VERSION_4_1))
|
||||
#error "Minimum header version must be OpenGL ES 2.0 or OpenGL 4.1"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Since we need ES3.1 headers and iOS only has ES3.0, we also define the constants we
|
||||
* need to avoid many #ifdef in the actual code.
|
||||
*/
|
||||
|
||||
#if defined(GL_ES_VERSION_2_0)
|
||||
|
||||
#ifdef GL_EXT_disjoint_timer_query
|
||||
# ifndef GL_TIME_ELAPSED
|
||||
# define GL_TIME_ELAPSED GL_TIME_ELAPSED_EXT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef GL_EXT_clip_control
|
||||
# ifndef GL_LOWER_LEFT
|
||||
# define GL_LOWER_LEFT GL_LOWER_LEFT_EXT
|
||||
# endif
|
||||
# ifndef GL_ZERO_TO_ONE
|
||||
# define GL_ZERO_TO_ONE GL_ZERO_TO_ONE_EXT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef GL_TEXTURE_CUBE_MAP_ARRAY
|
||||
# define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
|
||||
#endif
|
||||
|
||||
// Prevent lots of #ifdef's between desktop and mobile
|
||||
|
||||
#if defined(GL_KHR_debug)
|
||||
# ifndef GL_DEBUG_OUTPUT
|
||||
# define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
|
||||
# endif
|
||||
# ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS
|
||||
# define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
|
||||
# endif
|
||||
|
||||
# ifndef GL_DEBUG_SEVERITY_HIGH
|
||||
# define GL_DEBUG_SEVERITY_HIGH GL_DEBUG_SEVERITY_HIGH_KHR
|
||||
# endif
|
||||
# ifndef GL_DEBUG_SEVERITY_MEDIUM
|
||||
# define GL_DEBUG_SEVERITY_MEDIUM GL_DEBUG_SEVERITY_MEDIUM_KHR
|
||||
# endif
|
||||
# ifndef GL_DEBUG_SEVERITY_LOW
|
||||
# define GL_DEBUG_SEVERITY_LOW GL_DEBUG_SEVERITY_LOW_KHR
|
||||
# endif
|
||||
# ifndef GL_DEBUG_SEVERITY_NOTIFICATION
|
||||
# define GL_DEBUG_SEVERITY_NOTIFICATION GL_DEBUG_SEVERITY_NOTIFICATION_KHR
|
||||
# endif
|
||||
|
||||
# ifndef GL_DEBUG_TYPE_MARKER
|
||||
# define GL_DEBUG_TYPE_MARKER GL_DEBUG_TYPE_MARKER_KHR
|
||||
# endif
|
||||
# ifndef GL_DEBUG_TYPE_ERROR
|
||||
# define GL_DEBUG_TYPE_ERROR GL_DEBUG_TYPE_ERROR_KHR
|
||||
# endif
|
||||
# ifndef GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR
|
||||
# define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR
|
||||
# endif
|
||||
# ifndef GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR
|
||||
# define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR
|
||||
# endif
|
||||
# ifndef GL_DEBUG_TYPE_PORTABILITY
|
||||
# define GL_DEBUG_TYPE_PORTABILITY GL_DEBUG_TYPE_PORTABILITY_KHR
|
||||
# endif
|
||||
# ifndef GL_DEBUG_TYPE_PERFORMANCE
|
||||
# define GL_DEBUG_TYPE_PERFORMANCE GL_DEBUG_TYPE_PERFORMANCE_KHR
|
||||
# endif
|
||||
# ifndef GL_DEBUG_TYPE_OTHER
|
||||
# define GL_DEBUG_TYPE_OTHER GL_DEBUG_TYPE_OTHER_KHR
|
||||
# endif
|
||||
|
||||
# define glDebugMessageCallback glDebugMessageCallbackKHR
|
||||
#endif
|
||||
|
||||
/* The iOS SDK only provides OpenGL ES headers up to 3.0. Filament works with OpenGL 3.0, but
|
||||
* requires ES3.1 headers */
|
||||
#if !defined(GL_ES_VERSION_3_1)
|
||||
#define GL_SHADER_STORAGE_BUFFER 0x90D2
|
||||
#define GL_COMPUTE_SHADER 0x91B9
|
||||
|
||||
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
|
||||
|
||||
// FIXME: The GL_TIME_ELAPSED define is used unconditionally in Filament, but
|
||||
// requires extension support.
|
||||
#ifndef GL_TIME_ELAPSED
|
||||
#define GL_TIME_ELAPSED 0x88BF
|
||||
#endif
|
||||
|
||||
#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
|
||||
#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
|
||||
#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
|
||||
#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
|
||||
#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
|
||||
#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
|
||||
#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
|
||||
#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
|
||||
|
||||
#endif
|
||||
#endif // GL_ES_VERSION_2_0
|
||||
|
||||
// This is an odd duck function that exists in WebGL 2.0 but not in OpenGL ES.
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
extern "C" {
|
||||
@@ -142,10 +194,6 @@ void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void *d
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (!defined(GL_ES_VERSION_2_0) && !defined(GL_VERSION_4_1))
|
||||
#error "Minimum header version must be OpenGL ES 2.0 or OpenGL 4.1"
|
||||
#endif
|
||||
|
||||
|
||||
#define BACKEND_OPENGL_VERSION_GLES 0
|
||||
#define BACKEND_OPENGL_VERSION_GL 1
|
||||
|
||||
@@ -54,7 +54,6 @@ io::ostream& operator<<(io::ostream& out, PrimitiveType type) {
|
||||
CASE(PrimitiveType, LINES)
|
||||
CASE(PrimitiveType, LINE_STRIP)
|
||||
CASE(PrimitiveType, POINTS)
|
||||
CASE(PrimitiveType, NONE)
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -367,6 +366,7 @@ io::ostream& operator<<(io::ostream& out, BufferObjectBinding binding) {
|
||||
switch (binding) {
|
||||
CASE(BufferObjectBinding, VERTEX)
|
||||
CASE(BufferObjectBinding, UNIFORM)
|
||||
CASE(BufferObjectBinding, SHADER_STORAGE)
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -498,8 +498,11 @@ io::ostream& operator<<(io::ostream& stream, ShaderStageFlags stageFlags) {
|
||||
case ShaderStageFlags::FRAGMENT:
|
||||
str = "{ fragment }";
|
||||
break;
|
||||
case ShaderStageFlags::COMPUTE:
|
||||
str = "{ compute }";
|
||||
break;
|
||||
case ShaderStageFlags::ALL_SHADER_STAGE_FLAGS:
|
||||
str = "{ vertex | fragment }";
|
||||
str = "{ vertex | fragment | compute }";
|
||||
break;
|
||||
}
|
||||
return stream << str;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "vulkan/PlatformVkLinuxGGP.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#if defined(FILAMENT_SUPPORTS_GGP)
|
||||
#include <ggp_c/ggp.h>
|
||||
#endif
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include "VulkanDriverFactory.h"
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* PlatformVkLinuxGGP::createDriver(
|
||||
void* const sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept {
|
||||
#if defined(FILAMENT_SUPPORTS_GGP)
|
||||
ASSERT_PRECONDITION(sharedContext == nullptr,
|
||||
"Vulkan does not support shared contexts.");
|
||||
const char* requiredInstanceExtensions[] = {
|
||||
VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME};
|
||||
return VulkanDriverFactory::create(this, requiredInstanceExtensions,
|
||||
sizeof(requiredInstanceExtensions) /
|
||||
sizeof(requiredInstanceExtensions[0]),
|
||||
driverConfig);
|
||||
#else
|
||||
PANIC_PRECONDITION("Filament does not support GGP.");
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void* PlatformVkLinuxGGP::createVkSurfaceKHR(void* nativeWindow, void* instance,
|
||||
uint64_t flags) noexcept {
|
||||
#if defined(FILAMENT_SUPPORTS_GGP)
|
||||
VkStreamDescriptorSurfaceCreateInfoGGP surface_create_info = {
|
||||
VK_STRUCTURE_TYPE_STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP};
|
||||
surface_create_info.streamDescriptor = kGgpPrimaryStreamDescriptor;
|
||||
PFN_vkCreateStreamDescriptorSurfaceGGP fpCreateStreamDescriptorSurfaceGGP =
|
||||
reinterpret_cast<PFN_vkCreateStreamDescriptorSurfaceGGP>(
|
||||
vkGetInstanceProcAddr(static_cast<VkInstance>(instance),
|
||||
"vkCreateStreamDescriptorSurfaceGGP"));
|
||||
ASSERT_PRECONDITION(fpCreateStreamDescriptorSurfaceGGP != nullptr,
|
||||
"Error getting VkInstance "
|
||||
"function vkCreateStreamDescriptorSurfaceGGP");
|
||||
VkSurfaceKHR surface = nullptr;
|
||||
VkResult res = fpCreateStreamDescriptorSurfaceGGP(
|
||||
static_cast<VkInstance>(instance), &surface_create_info, nullptr,
|
||||
&surface);
|
||||
ASSERT_PRECONDITION(res == VK_SUCCESS, "Error in vulkan: %d", res);
|
||||
return surface;
|
||||
#else
|
||||
PANIC_PRECONDITION("Filament does not support GGP.");
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKAN_PLATFORM_VK_LINUX_GGP_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKAN_PLATFORM_VK_LINUX_GGP_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "VulkanPlatform.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class PlatformVkLinuxGGP final : public VulkanPlatform {
|
||||
public:
|
||||
Driver* createDriver(
|
||||
void* const sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept override;
|
||||
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance,
|
||||
uint64_t flags) noexcept override;
|
||||
|
||||
int getOSVersion() const noexcept override { return 0; }
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKAN_PLATFORM_VK_LINUX_GGP_H
|
||||
@@ -111,6 +111,10 @@ void VulkanBuffer::loadFromCpu(VulkanContext& context, VulkanStagePool& stagePoo
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
0, 0, nullptr, 1, &barrier, 0, nullptr);
|
||||
}
|
||||
|
||||
if (mUsage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
|
||||
// TODO: implement me
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -885,15 +885,28 @@ bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
}
|
||||
|
||||
FeatureLevel VulkanDriver::getFeatureLevel() {
|
||||
const auto supportedSamplerCount = std::min(
|
||||
mContext.physicalDeviceProperties.limits.maxDescriptorSetSamplers,
|
||||
mContext.physicalDeviceProperties.limits.maxDescriptorSetSampledImages);
|
||||
const VkPhysicalDeviceLimits& limits = mContext.physicalDeviceProperties.limits;
|
||||
|
||||
const bool imageCubeArray = (bool)mContext.physicalDeviceFeatures.imageCubeArray;
|
||||
// If cubemap arrays are not supported, then this is an FL1 device.
|
||||
if (!mContext.physicalDeviceFeatures.imageCubeArray) {
|
||||
return FeatureLevel::FEATURE_LEVEL_1;
|
||||
}
|
||||
|
||||
return (supportedSamplerCount >= 31 && imageCubeArray) ?
|
||||
FeatureLevel::FEATURE_LEVEL_2 :
|
||||
FeatureLevel::FEATURE_LEVEL_1;
|
||||
// If the max sampler counts do not meet FL2 standards, then this is an FL1 device.
|
||||
const auto& fl2 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_2];
|
||||
if (fl2.MAX_VERTEX_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers ||
|
||||
fl2.MAX_FRAGMENT_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers) {
|
||||
return FeatureLevel::FEATURE_LEVEL_1;
|
||||
}
|
||||
|
||||
// If the max sampler counts do not meet FL3 standards, then this is an FL2 device.
|
||||
const auto& fl3 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_3];
|
||||
if (fl3.MAX_VERTEX_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers ||
|
||||
fl3.MAX_FRAGMENT_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers) {
|
||||
return FeatureLevel::FEATURE_LEVEL_2;
|
||||
}
|
||||
|
||||
return FeatureLevel::FEATURE_LEVEL_3;
|
||||
}
|
||||
|
||||
math::float2 VulkanDriver::getClipSpaceParams() {
|
||||
@@ -1457,12 +1470,22 @@ void VulkanDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh)
|
||||
mPipelineCache.bindUniformBuffer((uint32_t) index, bo->buffer.getGpuBuffer(), offset, size);
|
||||
}
|
||||
|
||||
void VulkanDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> boh,
|
||||
uint32_t offset, uint32_t size) {
|
||||
void VulkanDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
|
||||
Handle<HwBufferObject> boh, uint32_t offset, uint32_t size) {
|
||||
|
||||
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
|
||||
bindingType == BufferObjectBinding::UNIFORM);
|
||||
|
||||
// TODO: implement BufferObjectBinding::SHADER_STORAGE case
|
||||
|
||||
auto* bo = handle_cast<VulkanBufferObject*>(boh);
|
||||
mPipelineCache.bindUniformBuffer((uint32_t)index, bo->buffer.getGpuBuffer(), offset, size);
|
||||
}
|
||||
|
||||
void VulkanDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
|
||||
// TODO: implement unbindBuffer()
|
||||
}
|
||||
|
||||
void VulkanDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
auto* hwsb = handle_cast<VulkanSamplerGroup*>(sbh);
|
||||
mSamplerBindings[index] = hwsb;
|
||||
@@ -1560,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);
|
||||
@@ -1676,6 +1700,12 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
|
||||
scheduleDestroy(std::move(pbd));
|
||||
}
|
||||
|
||||
void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
|
||||
// TODO: implement readBufferSubData
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect, SamplerMagFilter filter) {
|
||||
assert_invariant(mContext.currentRenderPass.renderPass == VK_NULL_HANDLE);
|
||||
@@ -1839,7 +1869,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
|
||||
assert_invariant(sb->getSize() == samplers.size());
|
||||
size_t samplerIdx = 0;
|
||||
for (auto& sampler : samplers) {
|
||||
size_t bindingPoint = sampler.binding;
|
||||
const SamplerDescriptor* boundSampler = sb->data() + samplerIdx;
|
||||
samplerIdx++;
|
||||
|
||||
@@ -1854,7 +1883,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
|
||||
#ifndef NDEBUG
|
||||
utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
|
||||
utils::slog.w << " in material '" << program->name.c_str() << "'";
|
||||
utils::slog.w << " at binding point " << +bindingPoint << utils::io::endl;
|
||||
utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl;
|
||||
#endif
|
||||
texture = mContext.emptyTexture;
|
||||
}
|
||||
@@ -1864,7 +1893,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
|
||||
|
||||
usage = VulkanPipelineCache::getUsageFlags(sampler.binding, samplerGroup.stageFlags, usage);
|
||||
|
||||
samplerInfo[bindingPoint] = {
|
||||
samplerInfo[sampler.binding] = {
|
||||
.sampler = vksampler,
|
||||
.imageView = texture->getPrimaryImageView(),
|
||||
.imageLayout = texture->getPrimaryImageLayout()
|
||||
@@ -1925,6 +1954,10 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
|
||||
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
|
||||
}
|
||||
|
||||
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
// FIXME: implement me
|
||||
}
|
||||
|
||||
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
VulkanCommandBuffer const* commands = &mContext.commands->get();
|
||||
VulkanTimerQuery* vtq = handle_cast<VulkanTimerQuery*>(tqh);
|
||||
@@ -1979,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>;
|
||||
|
||||
|
||||
@@ -49,14 +49,10 @@ VulkanProgram::VulkanProgram(VulkanContext& context, const Program& builder) noe
|
||||
HwProgram(builder.getName()), context(context) {
|
||||
auto const& blobs = builder.getShadersSource();
|
||||
VkShaderModule* modules[2] = { &bundle.vertex, &bundle.fragment };
|
||||
bool missing = false;
|
||||
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
|
||||
// TODO: handle compute shaders.
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
const auto& blob = blobs[i];
|
||||
VkShaderModule* module = modules[i];
|
||||
if (blob.empty()) {
|
||||
missing = true;
|
||||
continue;
|
||||
}
|
||||
VkShaderModuleCreateInfo moduleInfo = {};
|
||||
moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
moduleInfo.codeSize = blob.size();
|
||||
@@ -106,13 +102,6 @@ VulkanProgram::VulkanProgram(VulkanContext& context, const Program& builder) noe
|
||||
bundle.specializationInfos = pInfo;
|
||||
}
|
||||
|
||||
// Output a warning because it's okay to encounter empty blobs, but it's not okay to use
|
||||
// this program handle in a draw call.
|
||||
if (missing) {
|
||||
utils::slog.w << "Missing SPIR-V shader: " << this->name.c_str() << utils::io::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a copy of the binding map
|
||||
samplerGroupInfo = builder.getSamplerGroupInfo();
|
||||
if constexpr (FILAMENT_VULKAN_VERBOSE) {
|
||||
@@ -274,7 +263,6 @@ VulkanBufferObject::VulkanBufferObject(VulkanContext& context, VulkanStagePool&
|
||||
void VulkanRenderPrimitive::setPrimitiveType(PrimitiveType pt) {
|
||||
this->type = pt;
|
||||
switch (pt) {
|
||||
case PrimitiveType::NONE:
|
||||
case PrimitiveType::POINTS:
|
||||
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
break;
|
||||
|
||||
@@ -117,18 +117,18 @@ struct VulkanRenderPrimitive : public HwRenderPrimitive {
|
||||
};
|
||||
|
||||
struct VulkanFence : public HwFence {
|
||||
VulkanFence(const VulkanCommandBuffer& commands) : fence(commands.fence) {}
|
||||
explicit VulkanFence(const VulkanCommandBuffer& commands) : fence(commands.fence) {}
|
||||
std::shared_ptr<VulkanCmdFence> fence;
|
||||
};
|
||||
|
||||
struct VulkanSync : public HwSync {
|
||||
VulkanSync() {}
|
||||
VulkanSync(const VulkanCommandBuffer& commands) : fence(commands.fence) {}
|
||||
VulkanSync() = default;
|
||||
explicit VulkanSync(const VulkanCommandBuffer& commands) : fence(commands.fence) {}
|
||||
std::shared_ptr<VulkanCmdFence> fence;
|
||||
};
|
||||
|
||||
struct VulkanTimerQuery : public HwTimerQuery {
|
||||
VulkanTimerQuery(VulkanContext& context);
|
||||
explicit VulkanTimerQuery(VulkanContext& context);
|
||||
~VulkanTimerQuery();
|
||||
uint32_t startingQueryIndex;
|
||||
uint32_t stoppingQueryIndex;
|
||||
@@ -143,7 +143,9 @@ inline constexpr VkBufferUsageFlagBits getBufferObjectUsage(
|
||||
return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
||||
case BufferObjectBinding::UNIFORM:
|
||||
return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
||||
// when adding more buffer types here, make sure to update VulkanBuffer::loadFromCpu()
|
||||
case BufferObjectBinding::SHADER_STORAGE:
|
||||
return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
// when adding more buffer-types here, make sure to update VulkanBuffer::loadFromCpu()
|
||||
// if necessary.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,15 +35,14 @@ namespace filament::backend {
|
||||
|
||||
static VulkanPipelineCache::RasterState createDefaultRasterState();
|
||||
|
||||
static constexpr size_t MAX_VERTEX_SAMPLER_COUNT = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_2].MAX_VERTEX_SAMPLER_COUNT;
|
||||
|
||||
static VkShaderStageFlags getShaderStageFlags(VulkanPipelineCache::UsageFlags key, uint16_t binding) {
|
||||
// NOTE: if you modify this function, you also need to modify getUsageFlags.
|
||||
assert_invariant(binding < MAX_SAMPLER_COUNT);
|
||||
VkShaderStageFlags flags = 0;
|
||||
if (key.test(binding)) {
|
||||
flags |= VK_SHADER_STAGE_VERTEX_BIT;
|
||||
}
|
||||
if (key.test(MAX_VERTEX_SAMPLER_COUNT + binding)) {
|
||||
if (key.test(MAX_SAMPLER_COUNT + binding)) {
|
||||
flags |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
}
|
||||
return flags;
|
||||
@@ -52,13 +51,16 @@ static VkShaderStageFlags getShaderStageFlags(VulkanPipelineCache::UsageFlags ke
|
||||
VulkanPipelineCache::UsageFlags
|
||||
VulkanPipelineCache::getUsageFlags(uint16_t binding, ShaderStageFlags flags, UsageFlags src) {
|
||||
// NOTE: if you modify this function, you also need to modify getShaderStageFlags.
|
||||
assert_invariant(binding < MAX_SAMPLER_COUNT);
|
||||
if (any(flags & ShaderStageFlags::VERTEX)) {
|
||||
src.set(binding);
|
||||
}
|
||||
if (any(flags & ShaderStageFlags::FRAGMENT)) {
|
||||
src.set(MAX_VERTEX_SAMPLER_COUNT + binding);
|
||||
src.set(MAX_SAMPLER_COUNT + binding);
|
||||
}
|
||||
assert_invariant(!any(flags & ~(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)));
|
||||
// TODO: add support for compute by extending SHADER_MODULE_COUNT and ensuring UsageFlags
|
||||
// has 186 bits (MAX_SAMPLER_COUNT * 3)
|
||||
// assert_invariant(!any(flags & ~(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)));
|
||||
return src;
|
||||
}
|
||||
|
||||
@@ -828,8 +830,12 @@ void VulkanPipelineCache::growDescriptorPool() noexcept {
|
||||
mDescriptorSets.clear();
|
||||
}
|
||||
|
||||
size_t VulkanPipelineCache::PipelineLayoutKeyHashFn::operator()(const PipelineLayoutKey& key) const {
|
||||
return key.getValue();
|
||||
size_t VulkanPipelineCache::PipelineLayoutKeyHashFn::operator()(
|
||||
const PipelineLayoutKey& key) const {
|
||||
std::hash<uint64_t> hasher;
|
||||
auto h0 = hasher(key.getBitsAt(0));
|
||||
auto h1 = hasher(key.getBitsAt(1));
|
||||
return h0 ^ (h1 << 1);
|
||||
}
|
||||
|
||||
bool VulkanPipelineCache::PipelineLayoutKeyEqual::operator()(const PipelineLayoutKey& k1,
|
||||
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
VkSpecializationInfo* specializationInfos = nullptr;
|
||||
};
|
||||
|
||||
using UsageFlags = utils::bitset64;
|
||||
using UsageFlags = utils::bitset128;
|
||||
static UsageFlags getUsageFlags(uint16_t binding, ShaderStageFlags stages, UsageFlags src = {});
|
||||
|
||||
#pragma clang diagnostic push
|
||||
@@ -190,11 +190,9 @@ private:
|
||||
// PIPELINE LAYOUT CACHE KEY
|
||||
// -------------------------
|
||||
|
||||
using PipelineLayoutKey = utils::bitset64;
|
||||
using PipelineLayoutKey = utils::bitset128;
|
||||
|
||||
static_assert(PipelineLayoutKey::BIT_COUNT >=
|
||||
FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_2].MAX_VERTEX_SAMPLER_COUNT +
|
||||
FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_2].MAX_FRAGMENT_SAMPLER_COUNT);
|
||||
static_assert(PipelineLayoutKey::BIT_COUNT >= 2 * MAX_SAMPLER_COUNT);
|
||||
|
||||
struct PipelineLayoutKeyHashFn {
|
||||
size_t operator()(const PipelineLayoutKey& key) const;
|
||||
@@ -256,10 +254,10 @@ private:
|
||||
VertexInputBindingDescription vertexBuffers[VERTEX_ATTRIBUTE_COUNT]; // 128 : 156
|
||||
RasterState rasterState; // 16 : 284
|
||||
uint32_t padding; // 4 : 300
|
||||
PipelineLayoutKey layout; // 8 : 304
|
||||
PipelineLayoutKey layout; // 16 : 304
|
||||
};
|
||||
|
||||
static_assert(sizeof(PipelineKey) == 312, "PipelineKey must not have implicit padding.");
|
||||
static_assert(sizeof(PipelineKey) == 320, "PipelineKey must not have implicit padding.");
|
||||
|
||||
using PipelineHashFn = utils::hash::MurmurHashFn<PipelineKey>;
|
||||
|
||||
@@ -296,16 +294,16 @@ private:
|
||||
// Represents all the Vulkan state that comprises a bound descriptor set.
|
||||
struct DescriptorKey {
|
||||
VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 80 0
|
||||
DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 768 80
|
||||
DescriptorImageInfo inputAttachments[TARGET_BINDING_COUNT]; // 192 848
|
||||
uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 40 1040
|
||||
DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 1488 80
|
||||
DescriptorImageInfo inputAttachments[TARGET_BINDING_COUNT]; // 192 1568
|
||||
uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 40 1760
|
||||
uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 40 1080
|
||||
};
|
||||
static_assert(offsetof(DescriptorKey, samplers) == 80);
|
||||
static_assert(offsetof(DescriptorKey, inputAttachments) == 848);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1040);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1080);
|
||||
static_assert(sizeof(DescriptorKey) == 1120, "DescriptorKey must not have implicit padding.");
|
||||
static_assert(offsetof(DescriptorKey, inputAttachments) == 1568);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1760);
|
||||
static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1800);
|
||||
static_assert(sizeof(DescriptorKey) == 1840, "DescriptorKey must not have implicit padding.");
|
||||
|
||||
using DescHashFn = utils::hash::MurmurHashFn<DescriptorKey>;
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ComputeTest.h"
|
||||
#include "PlatformRunner.h"
|
||||
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include "private/backend/CommandBufferQueue.h"
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
auto backend = (Backend)test::parseArgumentsForBackend(argc, argv);
|
||||
ComputeTest::init(backend);
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
static constexpr size_t CONFIG_MIN_COMMAND_BUFFERS_SIZE = 1 * 1024 * 1024;
|
||||
static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE = 3 * CONFIG_MIN_COMMAND_BUFFERS_SIZE;
|
||||
|
||||
Backend ComputeTest::sBackend = Backend::NOOP;
|
||||
|
||||
void ComputeTest::init(Backend backend) {
|
||||
sBackend = backend;
|
||||
}
|
||||
|
||||
ComputeTest::ComputeTest()
|
||||
: commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE) {
|
||||
}
|
||||
|
||||
ComputeTest::~ComputeTest() = default;
|
||||
|
||||
void ComputeTest::SetUp() {
|
||||
Test::SetUp();
|
||||
|
||||
auto backend = sBackend;
|
||||
DefaultPlatform* platform = DefaultPlatform::create(&backend);
|
||||
|
||||
GTEST_ASSERT_EQ(uint8_t(backend), uint8_t(sBackend));
|
||||
|
||||
driver = platform->createDriver(nullptr, {});
|
||||
commandStream = std::make_unique<CommandStream>(*driver, commandBufferQueue.getCircularBuffer());
|
||||
|
||||
// We need at least feature level 2 to run the Compute tests
|
||||
if (driver->getFeatureLevel() < FeatureLevel::FEATURE_LEVEL_2) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeTest::TearDown() {
|
||||
Test::TearDown();
|
||||
flushAndWait();
|
||||
driver->terminate();
|
||||
delete driver;
|
||||
}
|
||||
|
||||
|
||||
void ComputeTest::executeCommands() {
|
||||
commandBufferQueue.flush();
|
||||
auto buffers = commandBufferQueue.waitForCommands();
|
||||
for (auto& item : buffers) {
|
||||
if (UTILS_LIKELY(item.begin)) {
|
||||
getDriverApi().execute(item.begin);
|
||||
commandBufferQueue.releaseBuffer(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeTest::flushAndWait(uint64_t timeout) {
|
||||
auto& api = getDriverApi();
|
||||
auto fence = api.createFence();
|
||||
api.finish();
|
||||
executeCommands();
|
||||
api.wait(fence, timeout);
|
||||
api.destroyFence(fence);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_COMPUTE_TEST_H
|
||||
#define TNT_COMPUTE_TEST_H
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "private/backend/CommandBufferQueue.h"
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
class ComputeTest : public ::testing::Test {
|
||||
static filament::backend::Backend sBackend;
|
||||
|
||||
filament::backend::Driver* driver = nullptr;
|
||||
filament::backend::CommandBufferQueue commandBufferQueue;
|
||||
std::unique_ptr<filament::backend::DriverApi> commandStream;
|
||||
|
||||
protected:
|
||||
void SetUp() override;
|
||||
void TearDown() override;
|
||||
|
||||
void executeCommands();
|
||||
void flushAndWait(uint64_t timeout = 1000);
|
||||
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
|
||||
filament::backend::Driver& getDriver() { return *driver; }
|
||||
|
||||
static filament::backend::Backend getBackend() noexcept { return sBackend; };
|
||||
bool isMobile() noexcept { return driver->getShaderModel() == filament::backend::ShaderModel::MOBILE; };
|
||||
|
||||
public:
|
||||
static void init(filament::backend::Backend backend);
|
||||
|
||||
ComputeTest();
|
||||
~ComputeTest() override;
|
||||
};
|
||||
|
||||
#endif // TNT_COMPUTE_TEST_H
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ComputeTest.h"
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
TEST_F(ComputeTest, basic) {
|
||||
auto& driver = getDriverApi();
|
||||
|
||||
std::string shader_gles310 = {R"(
|
||||
#version 310 es
|
||||
layout(local_size_x = 16) in;
|
||||
void main() {
|
||||
}
|
||||
)"};
|
||||
|
||||
std::string shader_gl450 = {R"(
|
||||
#version 450 core
|
||||
layout(local_size_x = 16) in;
|
||||
void main() {
|
||||
}
|
||||
)"};
|
||||
|
||||
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 shader_spirv = {R"(
|
||||
// TODO: spirv test
|
||||
)"};
|
||||
|
||||
std::string_view shader;
|
||||
switch (getBackend()) {
|
||||
case Backend::OPENGL: shader = isMobile() ? shader_gles310 : shader_gl450; break;
|
||||
case Backend::VULKAN: shader = shader_spirv; break;
|
||||
case Backend::METAL: shader = shader_msl; break;
|
||||
default:
|
||||
GTEST_FATAL_FAILURE_("unexpected backend");
|
||||
}
|
||||
|
||||
Program program;
|
||||
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size() + 1);
|
||||
|
||||
Handle<HwProgram> ph = driver.createProgram(std::move(program));
|
||||
driver.dispatchCompute(ph, { 1, 1, 1 });
|
||||
driver.destroyProgram(ph);
|
||||
driver.finish();
|
||||
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
}
|
||||
|
||||
TEST_F(ComputeTest, copy) {
|
||||
auto& driver = getDriverApi();
|
||||
|
||||
std::string shader_gles310 = {R"(
|
||||
#version 310 es
|
||||
layout(local_size_x = 16) in;
|
||||
layout(std430) buffer;
|
||||
layout(binding = 0) writeonly buffer Output { float elements[]; } output_data;
|
||||
layout(binding = 1) readonly buffer Input0 { float elements[]; } input_data;
|
||||
void main() {
|
||||
uint ident = gl_GlobalInvocationID.x;
|
||||
output_data.elements[ident] = input_data.elements[ident];
|
||||
}
|
||||
)"};
|
||||
|
||||
std::string shader_gl450 = {R"(
|
||||
#version 450 core
|
||||
layout(local_size_x = 16) in;
|
||||
layout(std430) buffer;
|
||||
layout(binding = 0) writeonly buffer Output { float elements[]; } output_data;
|
||||
layout(binding = 1) readonly buffer Input0 { float elements[]; } input_data;
|
||||
void main() {
|
||||
uint ident = gl_GlobalInvocationID.x;
|
||||
output_data.elements[ident] = input_data.elements[ident];
|
||||
}
|
||||
)"};
|
||||
|
||||
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 shader_spirv = {R"(
|
||||
// TODO: spirv test
|
||||
)"};
|
||||
|
||||
std::string_view shader;
|
||||
switch (getBackend()) {
|
||||
case Backend::OPENGL: shader = isMobile() ? shader_gles310 : shader_gl450; break;
|
||||
case Backend::VULKAN: shader = shader_spirv; break;
|
||||
case Backend::METAL: shader = shader_msl; break;
|
||||
default:
|
||||
GTEST_FATAL_FAILURE_("unexpected backend");
|
||||
}
|
||||
|
||||
size_t groupSize = 16;
|
||||
size_t groupCount = 1024;
|
||||
size_t size = groupSize * groupCount * sizeof(float);
|
||||
|
||||
std::vector<float> data(groupSize * groupCount);
|
||||
std::generate(data.begin(), data.end(), [v = 0.0f]() mutable {
|
||||
v = v + 1.0f;
|
||||
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() + 1);
|
||||
Handle<HwProgram> ph = driver.createProgram(std::move(program));
|
||||
|
||||
|
||||
driver.bindBufferRange(BufferObjectBinding::SHADER_STORAGE, 0, output_data, 0, size);
|
||||
driver.bindBufferRange(BufferObjectBinding::SHADER_STORAGE, 1, input_data, 0, size);
|
||||
|
||||
driver.dispatchCompute(ph, { groupCount, 1, 1 });
|
||||
|
||||
driver.unbindBuffer(BufferObjectBinding::SHADER_STORAGE, 0);
|
||||
driver.unbindBuffer(BufferObjectBinding::SHADER_STORAGE, 1);
|
||||
|
||||
float* const user = (float*)malloc(size);
|
||||
driver.readBufferSubData(output_data, 0, size, { user, size });
|
||||
|
||||
driver.destroyProgram(ph);
|
||||
driver.destroyBufferObject(input_data);
|
||||
driver.destroyBufferObject(output_data);
|
||||
driver.finish();
|
||||
|
||||
driver.stopCapture(0);
|
||||
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// TODO: check buffer content
|
||||
EXPECT_EQ(memcmp(user, data.data(), size), 0);
|
||||
|
||||
free(user);
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
</style>
|
||||
|
||||
**Vulkan Backend**
|
||||
*Last updated: 11 June 2021*
|
||||
*Last updated: 26 October 2022*
|
||||
|
||||

|
||||
|
||||
@@ -47,7 +47,7 @@ single `VkDevice` and `VkInstance`.
|
||||
* | | | | *
|
||||
* |------------------------------------------| |-----------------------------------------| *
|
||||
* | | | | *
|
||||
* | Helpers VulkanContext | | SwapContext SwapContext | *
|
||||
* | Helpers VulkanContext | | VulkanTexture VulkanTexture | *
|
||||
* | .--------------. .---------------. | | .----------------+----------------. | *
|
||||
* | | PipelineCache| | VkDevice | | | | VkImage | VkImage | | *
|
||||
* | +--------------+ | VkInstance | | | | VkImageView | VkImageView | | *
|
||||
@@ -91,13 +91,14 @@ that are defined in `DriverBase.h`.
|
||||
|
||||
Most of the handle types do not contain much logic, but they often do more than mere wrapping of
|
||||
Vk objects. Notably, `VulkanRenderTarget` does not have a straightforward mapping to any one Vk
|
||||
object. See its class comment for details.
|
||||
object. See its class comment for details. Also, `VulkanTexture` and `VulkanSwapChain` have their
|
||||
own cpps file because of complexity around layouts and subresources.
|
||||
|
||||
VulkanProgram
|
||||
: Descends from `HwProgram`, owns a pair of `VkShaderModule`.
|
||||
|
||||
VulkanRenderTarget
|
||||
: Descends from `HwRenderTarget`, contains references to `VkImage` objects.
|
||||
: Descends from `HwRenderTarget`, contains references to `VulkanTexture` objects.
|
||||
|
||||
VulkanSwapChain
|
||||
: Descends from `HwSwapChain`, has a reference to `VkSurfaceKHR` and `VkSwapchainKHR`.
|
||||
@@ -127,14 +128,16 @@ they do follow a common set of conventions. For example, most of them implement
|
||||
that is called once per frame to free unused objects.
|
||||
|
||||
VulkanPipelineCache
|
||||
: Cache of VkDescriptorSet and VkPipeline.
|
||||
: Cache of VkDescriptorSet, VkPipeline, and VkPipelineLayout.
|
||||
|
||||
VulkanDisposer
|
||||
: Holds reference counts to Vulkan objects in order to defer their destruction.
|
||||
|
||||
VulkanFboCache
|
||||
: Cache of VkRenderPass and VkFramebuffer. Contains weak references to VkImage handles, but does not
|
||||
own any pixel data.
|
||||
own any pixel data. The cache key for render passes is quite complex because it needs to include
|
||||
image layout information in a compact way. For details see the comments in the `RenderPassKey`
|
||||
declaration.
|
||||
|
||||
VulkanStagePool
|
||||
: Pool of VkBuffer objects that can be memory mapped (shared between host and device).
|
||||
@@ -149,6 +152,71 @@ no subfolders. All backend-specific types and functions live directly in the `fi
|
||||
namespace, and we simply prefix our names with "Vulkan" when necessary to avoid ambiguation with
|
||||
OpenGL.
|
||||
|
||||
# Image Layout Strategy
|
||||
|
||||
Adhering to a coherent strategy for image layout transitions is crucial. Without this, validation
|
||||
errors (and actual black screens with some GPU vendors) occur far too easily and can feel like
|
||||
whack-a-mole.
|
||||
|
||||
Vulkan provides several mechanisms for transitioning layout (render passes, barriers) so the
|
||||
strategy outlined below specifies which mechanism should be used in various scenarios.
|
||||
|
||||
The current image layouts are tracked at the subresource level in `VulkanTexture` using a
|
||||
specialized STL-ish container that we designed for this purpose:
|
||||
[RangeMap](https://github.com/google/filament/blob/main/libs/utils/include/utils/RangeMap.h).
|
||||
|
||||
In the past we tried to avoid tracking since it theoritically duplicates state that exists in the
|
||||
GPU vendor's driver. However in practice this was impossible to manage, because complex logic was
|
||||
required to deduce the current layout. Also, tracking lets us use `assert_invariant` in many places
|
||||
to ensure that the behavior described in this document is implemented properly. It is much easier
|
||||
to catch errors with asserts than with validation.
|
||||
|
||||
As of this writing, the current rules are:
|
||||
|
||||
(1) **Read-Only Color Textures**
|
||||
|
||||
These should be created in an UNDEFINED state and stay that way until data gets uploaded into them,
|
||||
at which point the appropriate subresources are transitioned to TRANSFER_DST_OPTIMAL for the upload,
|
||||
then to SHADER_READ_ONLY_OPTIMAL, both times using a barrier. Note that a subresource might be
|
||||
updated several times; each time it undergoes these two transitions.
|
||||
|
||||
If a shader samples from a texture whose mipmaps are only partially loaded, we might see validation
|
||||
warnings about how some subresources are in an UNDEFINED layout. However if we are properly using
|
||||
the `setMinMaxLevels` driver API, then the Vulkan backend will not bind those particular
|
||||
subresources, so validation should not complain.
|
||||
|
||||
(2) **Writeable Color Textures**
|
||||
|
||||
During construction of the HwTexture, these are transitioned to GENERAL using a barrier.
|
||||
|
||||
GENERAL layout is not recommended by AMD best practices but in our case we wish to avoid thrashing
|
||||
the layout between COLOR_ATTACHMENT_OPTIMAL and SHADER_READ_ONLY_OPTIMAL for post-processing
|
||||
effects. Moreover we do not have a priori knowledge about blitting and read pixels usage.
|
||||
|
||||
(3) **Depth Textures**
|
||||
|
||||
During construction of the HwTexture, these are transitioned to GENERAL using a barrier.
|
||||
|
||||
Note that some post-processing passes (e.g. SSAO) sample from the depth buffer while also enabling
|
||||
depth testing. Using GENERAL makes this legal.
|
||||
|
||||
(4) **Swap Chain Textures**
|
||||
|
||||
During construction, these are transitioned to GENERAL using a barrier. They are then transitioned
|
||||
to PRESENT at the end of the frame using a barrier, and transitioned back to GENERAL using the first
|
||||
render pass of the frame.
|
||||
|
||||
Note that we transition to PRESENT using a barrier, but with some GPU vendors it might be preferable
|
||||
to transition them using the last render pass. However we have no way of knowing ahead of time which
|
||||
pass is last.
|
||||
|
||||
Note that Filament allows swap chains to be used as blit targets and blit sources (e.g. for
|
||||
ReadPixels). Using GENERAL makes this easier to manage.
|
||||
|
||||
(5) **Headless Swap Chain Textures**
|
||||
|
||||
These follow the same rules as writeable color texture and depth textures.
|
||||
|
||||
# VulkanPipelineCache
|
||||
|
||||
Uniforms, samplers, and shaders are not directly bindable in Vulkan; instead they are bound through
|
||||
@@ -161,8 +229,6 @@ public methods. See its header file for details.
|
||||
|
||||
The pipeline cache also manages the *layout objects*, which constrain the number of uniform buffers
|
||||
and samplers that can be bound simultaneously (see `VkDescriptorSetLayout` and `VkPipelineLayout`).
|
||||
Here we take advantage of Filament's opinionated shading model in order to keep things simple, which
|
||||
allows us to create only one layout for the entire lifetime of the app.
|
||||
|
||||
# Memory Allocation
|
||||
|
||||
@@ -196,7 +262,8 @@ Here we list some of the most significant issues and to-be-done items for the Vu
|
||||
|
||||
- Improve the UBO strategy
|
||||
- Currently we use staging buffers and perform lots of copies (host-to-host, then host-to-device)
|
||||
- Consider an approach similar to MetalDriver, which effectively implements double-buffering using a pool of shared mem buffers
|
||||
- Consider an approach similar to MetalDriver, which effectively implements double-buffering
|
||||
using a pool of shared mem buffers
|
||||
- Consider using subpasses for the depth prepass
|
||||
- We lazily create pipeline objects
|
||||
1. This is notoriously slow in Vulkan
|
||||
|
||||
@@ -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.
|
||||
@@ -266,9 +266,29 @@ public:
|
||||
* Controls whether the shadow map should be optimized for resolution or stability.
|
||||
* When set to true, all resolution enhancing features that can affect stability are
|
||||
* disabling, resulting in significantly lower resolution shadows, albeit stable ones.
|
||||
*
|
||||
* Setting this flag to true always disables LiSPSM (see below).
|
||||
*
|
||||
* @see lispsm
|
||||
*/
|
||||
bool stable = false;
|
||||
|
||||
/**
|
||||
* LiSPSM, or light-space perspective shadow-mapping is a technique allowing to better
|
||||
* optimize the use of the shadow-map texture. When enabled the effective resolution of
|
||||
* shadows is greatly improved and yields result similar to using cascades without the
|
||||
* extra cost. LiSPSM comes with some drawbacks however, in particular it is incompatible
|
||||
* with blurring because it effectively affects the blur kernel size.
|
||||
*
|
||||
* Blurring is only an issue when using ShadowType::VSM with a large blur or with
|
||||
* ShadowType::PCSS however.
|
||||
*
|
||||
* If these blurring artifacts become problematic, this flag can be used to disable LiSPSM.
|
||||
*
|
||||
* @see stable
|
||||
*/
|
||||
bool lispsm = true;
|
||||
|
||||
/**
|
||||
* Constant bias in depth-resolution units by which shadows are moved away from the
|
||||
* light. The default value of 0.5 is used to round depth values up.
|
||||
@@ -328,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.
|
||||
@@ -418,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;
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class Material;
|
||||
class Texture;
|
||||
class TextureSampler;
|
||||
class UniformBuffer;
|
||||
class UniformInterfaceBlock;
|
||||
class BufferInterfaceBlock;
|
||||
|
||||
class UTILS_PUBLIC MaterialInstance : public FilamentAPI {
|
||||
template<size_t N>
|
||||
@@ -277,6 +277,12 @@ public:
|
||||
*/
|
||||
void setMaskThreshold(float threshold) noexcept;
|
||||
|
||||
/**
|
||||
* Gets the minimum alpha value a fragment must have to not be discarded when the blend
|
||||
* mode is MASKED
|
||||
*/
|
||||
float getMaskThreshold() const noexcept;
|
||||
|
||||
/**
|
||||
* Sets the screen space variance of the filter kernel used when applying specular
|
||||
* anti-aliasing. The default value is set to 0.15. The specified value should be between
|
||||
@@ -284,6 +290,12 @@ public:
|
||||
*/
|
||||
void setSpecularAntiAliasingVariance(float variance) noexcept;
|
||||
|
||||
/**
|
||||
* Gets the screen space variance of the filter kernel used when applying specular
|
||||
* anti-aliasing.
|
||||
*/
|
||||
float getSpecularAntiAliasingVariance() const noexcept;
|
||||
|
||||
/**
|
||||
* Sets the clamping threshold used to suppress estimation errors when applying specular
|
||||
* anti-aliasing. The default value is set to 0.2. The specified value should be between 0
|
||||
@@ -291,6 +303,12 @@ public:
|
||||
*/
|
||||
void setSpecularAntiAliasingThreshold(float threshold) noexcept;
|
||||
|
||||
/**
|
||||
* Gets the clamping threshold used to suppress estimation errors when applying specular
|
||||
* anti-aliasing.
|
||||
*/
|
||||
float getSpecularAntiAliasingThreshold() const noexcept;
|
||||
|
||||
/**
|
||||
* Enables or disables double-sided lighting if the parent Material has double-sided capability,
|
||||
* otherwise prints a warning. If double-sided lighting is enabled, backface culling is
|
||||
@@ -298,36 +316,72 @@ public:
|
||||
*/
|
||||
void setDoubleSided(bool doubleSided) noexcept;
|
||||
|
||||
/**
|
||||
* Returns whether double-sided lighting is enabled when the parent Material has double-sided
|
||||
* capability.
|
||||
*/
|
||||
bool isDoubleSided() const noexcept;
|
||||
|
||||
/**
|
||||
* Specifies how transparent objects should be rendered (default is DEFAULT).
|
||||
*/
|
||||
void setTransparencyMode(TransparencyMode mode) noexcept;
|
||||
|
||||
/**
|
||||
* Returns the transparency mode.
|
||||
*/
|
||||
TransparencyMode getTransparencyMode() const noexcept;
|
||||
|
||||
/**
|
||||
* Overrides the default triangle culling state that was set on the material.
|
||||
*/
|
||||
void setCullingMode(CullingMode culling) noexcept;
|
||||
|
||||
/**
|
||||
* Returns the face culling mode.
|
||||
*/
|
||||
CullingMode getCullingMode() const noexcept;
|
||||
|
||||
/**
|
||||
* Overrides the default color-buffer write state that was set on the material.
|
||||
*/
|
||||
void setColorWrite(bool enable) noexcept;
|
||||
|
||||
/**
|
||||
* Returns whether color write is enabled.
|
||||
*/
|
||||
bool isColorWriteEnabled() const noexcept;
|
||||
|
||||
/**
|
||||
* Overrides the default depth-buffer write state that was set on the material.
|
||||
*/
|
||||
void setDepthWrite(bool enable) noexcept;
|
||||
|
||||
/**
|
||||
* Returns whether depth write is enabled.
|
||||
*/
|
||||
bool isDepthWriteEnabled() const noexcept;
|
||||
|
||||
/**
|
||||
* Overrides the default depth testing state that was set on the material.
|
||||
*/
|
||||
void setDepthCulling(bool enable) noexcept;
|
||||
|
||||
/**
|
||||
* Returns whether depth culling is enabled.
|
||||
*/
|
||||
bool isDepthCullingEnabled() const noexcept;
|
||||
|
||||
/**
|
||||
* Overrides the default stencil-buffer write state that was set on the material.
|
||||
*/
|
||||
void setStencilWrite(bool enable) noexcept;
|
||||
|
||||
/**
|
||||
* Returns whether stencil write is enabled.
|
||||
*/
|
||||
bool isStencilWriteEnabled() const noexcept;
|
||||
|
||||
/**
|
||||
* Sets the stencil comparison function (default is StencilCompareFunc::A).
|
||||
*
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -566,6 +566,11 @@ public:
|
||||
inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
|
||||
MorphTargetBuffer* morphTargetBuffer);
|
||||
|
||||
/**
|
||||
* Get a MorphTargetBuffer to the given primitive or null if it doesn't exist.
|
||||
*/
|
||||
MorphTargetBuffer* getMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept;
|
||||
|
||||
/**
|
||||
* Gets the number of morphing in the given entity.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "AtlasAllocator.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace utils;
|
||||
|
||||
static inline constexpr std::pair<uint8_t, uint8_t> unmorton(uint16_t m) noexcept {
|
||||
uint32_t r = (m | (uint32_t(m) << 15u)) & 0x55555555u;
|
||||
r = (r | (r >> 1u)) & 0x33333333u;
|
||||
r = (r | (r >> 2u)) & 0x0f0f0f0fu;
|
||||
r = r | (r >> 4u);
|
||||
return { uint8_t(r), uint8_t(r >> 16u) };
|
||||
}
|
||||
|
||||
AtlasAllocator::AtlasAllocator(size_t maxTextureSize) noexcept {
|
||||
// round to power-of-two immediately inferior or equal to the size specified.
|
||||
mMaxTextureSizePot = (sizeof(maxTextureSize) * 8 - 1u) - utils::clz(maxTextureSize);
|
||||
}
|
||||
|
||||
Viewport AtlasAllocator::allocate(size_t textureSize) noexcept {
|
||||
Viewport result{};
|
||||
const size_t powerOfTwo = (sizeof(textureSize) * 8 - 1u) - utils::clz(textureSize);
|
||||
|
||||
// asked for a texture size too large
|
||||
if (UTILS_UNLIKELY(powerOfTwo > mMaxTextureSizePot)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// asked for a texture size too small
|
||||
if (UTILS_UNLIKELY(mMaxTextureSizePot - powerOfTwo >= QuadTree::height())) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const size_t layer = mMaxTextureSizePot - powerOfTwo;
|
||||
const NodeId loc = allocateInLayer(layer);
|
||||
if (loc.l >= 0) {
|
||||
assert_invariant(loc.l == int8_t(layer));
|
||||
size_t dimension = 1u << powerOfTwo;
|
||||
// find the location of in the texture from the morton code (quadtree position)
|
||||
auto [x, y] = unmorton(loc.code);
|
||||
// scale to our maximum allocation size
|
||||
result.left = int32_t(x) << powerOfTwo;
|
||||
result.bottom = int32_t(y) << powerOfTwo;
|
||||
result.width = dimension;
|
||||
result.height = dimension;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void AtlasAllocator::clear(size_t maxTextureSize) noexcept {
|
||||
std::fill(mQuadTree.begin(), mQuadTree.end(), Node{});
|
||||
mMaxTextureSizePot = (sizeof(maxTextureSize) * 8 - 1u) - utils::clz(maxTextureSize);
|
||||
}
|
||||
|
||||
AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t maxHeight) noexcept {
|
||||
using namespace QuadTreeUtils;
|
||||
|
||||
NodeId candidate{ -1, 0 };
|
||||
if (UTILS_UNLIKELY(maxHeight > QuadTree::height())) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
const int8_t n = int8_t(maxHeight);
|
||||
|
||||
QuadTree::traverse(0, 0, n,
|
||||
[this, n, &candidate](NodeId const& curr) -> QuadTree::TraversalResult {
|
||||
|
||||
// we should never reach a level higher than n
|
||||
assert_invariant(curr.l <= n);
|
||||
|
||||
// get the node being processed
|
||||
const size_t i = index(curr.l, curr.code);
|
||||
Node const& node = mQuadTree[i];
|
||||
|
||||
// if the node is allocated, the whole tree below it is unavailable
|
||||
if (node.isAllocated()) {
|
||||
// if a node is allocated it can't have children
|
||||
assert_invariant(!node.hasChildren());
|
||||
return QuadTree::TraversalResult::SKIP_CHILDREN;
|
||||
}
|
||||
|
||||
// we're looking for the `deepest` unallocated node that has no children,
|
||||
// up to the depth we're trying to allocate
|
||||
if (curr.l > candidate.l && !node.hasChildren()) {
|
||||
candidate = curr;
|
||||
// Special case where we found a fitting node, just exit.
|
||||
if (curr.l == n) {
|
||||
return QuadTree::TraversalResult::EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
// We only want to find a fitting node that already has siblings, in order
|
||||
// to accomplish a "best fit" allocation. So if we're a parent of a 'fitting'
|
||||
// node and don't have children, we skip the children recursion.
|
||||
if (curr.l == n - 1 && !node.hasChildren()) {
|
||||
return QuadTree::TraversalResult::SKIP_CHILDREN;
|
||||
}
|
||||
|
||||
// continue going down the tree
|
||||
return QuadTree::TraversalResult::RECURSE;
|
||||
});
|
||||
|
||||
if (candidate.l >= 0) {
|
||||
const size_t i = index(candidate.l, candidate.code);
|
||||
Node& allocation = mQuadTree[i];
|
||||
|
||||
if (candidate.l == n) {
|
||||
allocation.allocated = true;
|
||||
if (UTILS_LIKELY(n > 0)) {
|
||||
// the root doesn't have a parent
|
||||
const size_t p = parent(candidate.l, candidate.code);
|
||||
Node& parent = mQuadTree[p];
|
||||
assert_invariant(!parent.isAllocated());
|
||||
assert_invariant(parent.hasChildren());
|
||||
assert_invariant(!parent.hasAllChildren());
|
||||
parent.children++;
|
||||
}
|
||||
} else if (candidate.l < int8_t(QuadTree::height())) {
|
||||
// we need to create the hierarchy down to the level we need
|
||||
assert_invariant(!allocation.isAllocated());
|
||||
assert_invariant(!allocation.hasChildren());
|
||||
|
||||
NodeId found{};
|
||||
QuadTree::traverse(candidate.l, candidate.code,
|
||||
[this, n, &found](NodeId const& curr) -> QuadTree::TraversalResult {
|
||||
size_t i = index(curr.l, curr.code);
|
||||
Node& node = mQuadTree[i];
|
||||
if (curr.l == n) {
|
||||
found = curr;
|
||||
node.allocated = true;
|
||||
return QuadTree::TraversalResult::EXIT;
|
||||
}
|
||||
node.children++;
|
||||
return QuadTree::TraversalResult::RECURSE;
|
||||
});
|
||||
|
||||
candidate = found;
|
||||
}
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_ATLASALLOCATOR_H
|
||||
#define TNT_FILAMENT_ATLASALLOCATOR_H
|
||||
|
||||
#include <utils/QuadTree.h>
|
||||
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
class AtlasAllocator_AllocateFirstLevel_Test;
|
||||
class AtlasAllocator_AllocateSecondLevel_Test;
|
||||
class AtlasAllocator_AllocateMixed0_Test;
|
||||
class AtlasAllocator_AllocateMixed1_Test;
|
||||
class AtlasAllocator_AllocateMixed2_Test;
|
||||
|
||||
namespace filament {
|
||||
|
||||
/*
|
||||
* A 2D allocator. Allocations must be square and have a power-of-two size.
|
||||
* AtlasAllocator hard codes a depth of 4, that is only 4 allocation sizes are permitted.
|
||||
* This doesn't actually allocate memory, just manages space within an abstract 2D image.
|
||||
*/
|
||||
class AtlasAllocator {
|
||||
|
||||
/*
|
||||
* A quadtree is used to track the allocated regions. Each node of the quadtree stores a
|
||||
* `Node` data structure below.
|
||||
* The `Node` tracks if it is allocated as well as the number of children it has. It doesn't
|
||||
* track which children though.
|
||||
*/
|
||||
struct Node {
|
||||
// whether this node is allocated. Implies no children.
|
||||
constexpr bool isAllocated() const noexcept { return allocated; }
|
||||
// whether this node has children. Implies it's not allocated.
|
||||
constexpr bool hasChildren() const noexcept { return children != 0; }
|
||||
// whether this node has all four children. Implies hasChildren().
|
||||
constexpr bool hasAllChildren() const noexcept { return children == 4; }
|
||||
bool allocated : 1; // true / false
|
||||
uint8_t children : 3; // 0, 1, 2, 3, 4
|
||||
};
|
||||
|
||||
static constexpr size_t QUAD_TREE_DEPTH = 4u;
|
||||
using QuadTree = utils::QuadTreeArray<Node, QUAD_TREE_DEPTH>;
|
||||
using NodeId = QuadTree::NodeId;
|
||||
|
||||
public:
|
||||
/*
|
||||
* Create allocator and specify the maximum texture size. Must be a power of two.
|
||||
* Allocations size allowed are the four power-of-two smaller or equal to this size.
|
||||
*/
|
||||
explicit AtlasAllocator(size_t maxTextureSize = 1024) noexcept;
|
||||
|
||||
/*
|
||||
* Allocates a square of size `textureSize`. Must be one of the power-of-two allowed
|
||||
* (see above).
|
||||
* Returns the location of the allocation within the maxTextureSize^2 square.
|
||||
*/
|
||||
Viewport allocate(size_t textureSize) noexcept;
|
||||
|
||||
/*
|
||||
* Frees all allocations and reset the maximum texture size.
|
||||
*/
|
||||
void clear(size_t maxTextureSize = 1024) noexcept;
|
||||
|
||||
private:
|
||||
friend AtlasAllocator_AllocateFirstLevel_Test;
|
||||
friend AtlasAllocator_AllocateSecondLevel_Test;
|
||||
friend AtlasAllocator_AllocateMixed0_Test;
|
||||
friend AtlasAllocator_AllocateMixed1_Test;
|
||||
friend AtlasAllocator_AllocateMixed2_Test;
|
||||
|
||||
QuadTree::NodeId allocateInLayer(size_t n) noexcept;
|
||||
|
||||
// quad-tree array to store the allocated list
|
||||
QuadTree mQuadTree{};
|
||||
uint8_t mMaxTextureSizePot = 0;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_ATLASALLOCATOR_H
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user