diff --git a/README.md b/README.md
index 38a30f56a33..97e81b5e083 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
- implementation 'com.google.android.filament:filament-android:1.53.1'
+ implementation 'com.google.android.filament:filament-android:1.53.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:
```shell
-pod 'Filament', '~> 1.53.1'
+pod 'Filament', '~> 1.53.2'
```
### Snapshots
@@ -176,6 +176,7 @@ steps:
- [x] KHR_materials_unlit
- [x] KHR_materials_variants
- [x] KHR_materials_volume
+ - [x] KHR_materials_specular
- [x] KHR_mesh_quantization
- [x] KHR_texture_basisu
- [x] KHR_texture_transform
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 2a4ed29bcd6..3591acf61ec 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -7,6 +7,9 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
+## v1.53.2
+
+
## v1.53.1
diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp
index 4b3b33e530f..f5714222559 100644
--- a/android/filament-android/src/main/cpp/Engine.cpp
+++ b/android/filament-android/src/main/cpp/Engine.cpp
@@ -571,3 +571,9 @@ Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nat
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
return (jlong) builder->build();
}
+
+extern "C"
+JNIEXPORT jlong JNICALL
+Java_com_google_android_filament_Engine_getSteadyClockTimeNano(JNIEnv *env, jclass clazz) {
+ return (jlong)Engine::getSteadyClockTimeNano();
+}
diff --git a/android/filament-android/src/main/cpp/Material.cpp b/android/filament-android/src/main/cpp/Material.cpp
index 7ef3b0b5be2..e923d210b01 100644
--- a/android/filament-android/src/main/cpp/Material.cpp
+++ b/android/filament-android/src/main/cpp/Material.cpp
@@ -25,12 +25,17 @@ using namespace filament;
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
- jlong nativeEngine, jobject buffer_, jint size) {
+ jlong nativeEngine, jobject buffer_, jint size, jint shBandCount) {
Engine* engine = (Engine*) nativeEngine;
AutoBuffer buffer(env, buffer_, size);
- Material* material = Material::Builder()
+ auto builder = Material::Builder();
+ if (shBandCount) {
+ builder.sphericalHarmonicsBandCount(shBandCount);
+ }
+ Material* material = builder
.package(buffer.getData(), buffer.getSize())
.build(*engine);
+
return (jlong) material;
}
diff --git a/android/filament-android/src/main/cpp/RenderableManager.cpp b/android/filament-android/src/main/cpp/RenderableManager.cpp
index db0255d414e..4f38e46f82d 100644
--- a/android/filament-android/src/main/cpp/RenderableManager.cpp
+++ b/android/filament-android/src/main/cpp/RenderableManager.cpp
@@ -253,16 +253,10 @@ Java_com_google_android_filament_RenderableManager_nBuilderMorphingStandard(JNIE
}
extern "C" JNIEXPORT void JNICALL
-Java_com_google_android_filament_RenderableManager_nBuilderSetMorphTargetBufferAt(JNIEnv*, jclass,
- jlong nativeBuilder, int level, int primitiveIndex, jlong nativeMorphTargetBuffer,
- int offset, int count) {
+Java_com_google_android_filament_RenderableManager_nBuilderSetMorphTargetBufferOffsetAt(JNIEnv*, jclass,
+ jlong nativeBuilder, int level, int primitiveIndex, int offset) {
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
- if (nativeMorphTargetBuffer) {
- MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
- builder->morphing(level, primitiveIndex, morphTargetBuffer, offset, count);
- } else {
- builder->morphing(level, primitiveIndex, offset, count);
- }
+ builder->morphing(level, primitiveIndex, offset);
}
extern "C" JNIEXPORT void JNICALL
@@ -334,18 +328,12 @@ Java_com_google_android_filament_RenderableManager_nSetMorphWeights(JNIEnv* env,
}
extern "C" JNIEXPORT void JNICALL
-Java_com_google_android_filament_RenderableManager_nSetMorphTargetBufferAt(JNIEnv*,
+Java_com_google_android_filament_RenderableManager_nSetMorphTargetBufferOffsetAt(JNIEnv*,
jclass, jlong nativeRenderableManager, jint i, int level, jint primitiveIndex,
- jlong nativeMorphTargetBuffer, jint offset, jint count) {
+ jlong, jint offset) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
- if (nativeMorphTargetBuffer) {
- MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
- rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
- (size_t) primitiveIndex, morphTargetBuffer, (size_t) offset, (size_t) count);
- } else {
- rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
- (size_t) primitiveIndex, (size_t) offset, (size_t) count);
- }
+ rm->setMorphTargetBufferOffsetAt((RenderableManager::Instance) i, (uint8_t) level,
+ (size_t) primitiveIndex, (size_t) offset);
}
extern "C" JNIEXPORT jint JNICALL
diff --git a/android/filament-android/src/main/cpp/Renderer.cpp b/android/filament-android/src/main/cpp/Renderer.cpp
index c48b616f563..1dc252d3560 100644
--- a/android/filament-android/src/main/cpp/Renderer.cpp
+++ b/android/filament-android/src/main/cpp/Renderer.cpp
@@ -187,3 +187,10 @@ Java_com_google_android_filament_Renderer_nSetPresentationTime(JNIEnv *, jclass
Renderer *renderer = (Renderer *) nativeRenderer;
renderer->setPresentationTime(monotonicClockNanos);
}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_android_filament_Renderer_nSetVsyncTime(JNIEnv *, jclass,
+ jlong nativeRenderer, jlong steadyClockTimeNano) {
+ Renderer *renderer = (Renderer *) nativeRenderer;
+ renderer->setVsyncTime(steadyClockTimeNano);
+}
diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java
index 5a28d1eae87..6c4c5119e3d 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java
@@ -1292,7 +1292,6 @@ public void setPaused(boolean paused) {
/**
* Switch the command queue to unprotected mode. Protected mode can be activated via
* Renderer::beginFrame() using a protected SwapChain.
-
* @see Renderer
* @see SwapChain
*/
@@ -1300,6 +1299,13 @@ public void unprotected() {
nUnprotected(getNativeObject());
}
+ /**
+ * Get the current time. This is a convenience function that simply returns the
+ * time in nanosecond since epoch of std::chrono::steady_clock.
+ * @return current time in nanosecond since epoch of std::chrono::steady_clock.
+ * @see Renderer#beginFrame
+ */
+ public static native long getSteadyClockTimeNano();
@UsedByReflection("TextureHelper.java")
public long getNativeObject() {
diff --git a/android/filament-android/src/main/java/com/google/android/filament/Material.java b/android/filament-android/src/main/java/com/google/android/filament/Material.java
index 80b143ee80d..cd71bbae736 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/Material.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/Material.java
@@ -346,6 +346,7 @@ public Material(long nativeMaterial) {
public static class Builder {
private Buffer mBuffer;
private int mSize;
+ private int mShBandCount = 0;
/**
* Specifies the material data. The material data is a binary blob produced by
@@ -361,6 +362,22 @@ public Builder payload(@NonNull Buffer buffer, @IntRange(from = 0) int size) {
return this;
}
+ /**
+ * Sets the quality of the indirect lights computations. This is only taken into account
+ * if this material is lit and in the surface domain. This setting will affect the
+ * IndirectLight computation if one is specified on the Scene and Spherical Harmonics
+ * are used for the irradiance.
+ *
+ * @param shBandCount Number of spherical harmonic bands. Must be 1, 2 or 3 (default).
+ * @return Reference to this Builder for chaining calls.
+ * @see IndirectLight
+ */
+ @NonNull
+ public Builder sphericalHarmonicsBandCount(@IntRange(from = 0) int shBandCount) {
+ mShBandCount = shBandCount;
+ return this;
+ }
+
/**
* Creates and returns the Material object.
*
@@ -372,7 +389,8 @@ public Builder payload(@NonNull Buffer buffer, @IntRange(from = 0) int size) {
*/
@NonNull
public Material build(@NonNull Engine engine) {
- long nativeMaterial = nBuilderBuild(engine.getNativeObject(), mBuffer, mSize);
+ long nativeMaterial = nBuilderBuild(engine.getNativeObject(),
+ mBuffer, mSize, mShBandCount);
if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material");
return new Material(nativeMaterial);
}
@@ -1023,7 +1041,7 @@ void clearNativeObject() {
mNativeObject = 0;
}
- private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size);
+ private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount);
private static native long nCreateInstance(long nativeMaterial);
private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name);
private static native long nGetDefaultInstance(long nativeMaterial);
diff --git a/android/filament-android/src/main/java/com/google/android/filament/MorphTargetBuffer.java b/android/filament-android/src/main/java/com/google/android/filament/MorphTargetBuffer.java
index f82ca4780b0..619870d372d 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/MorphTargetBuffer.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/MorphTargetBuffer.java
@@ -74,7 +74,7 @@ public Builder count(@IntRange(from = 1) int count) {
*
* @exception IllegalStateException if the MorphTargetBuffer could not be created
*
- * @see #setMorphTargetBufferAt
+ * @see #setMorphTargetBufferOffsetAt
*/
@NonNull
public MorphTargetBuffer build(@NonNull Engine engine) {
diff --git a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java
index dfce1af4a16..0da9a0d6e79 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java
@@ -569,40 +569,13 @@ public Builder morphing(@NonNull MorphTargetBuffer morphTargetBuffer) {
*
* @param level the level of detail (lod), only 0 can be specified
* @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor
- * @param morphTargetBuffer specifies the morph target buffer
* @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices)
- * @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
@NonNull
public Builder morphing(@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
- @IntRange(from = 0) int offset,
- @IntRange(from = 0) int count) {
- nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex, 0, offset, count);
- return this;
- }
-
- /** @deprecated */
- @Deprecated
- @NonNull
- public Builder morphing(@IntRange(from = 0) int level,
- @IntRange(from = 0) int primitiveIndex,
- @NonNull MorphTargetBuffer morphTargetBuffer,
- @IntRange(from = 0) int offset,
- @IntRange(from = 0) int count) {
- nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex,
- morphTargetBuffer.getNativeObject(), offset, count);
- return this;
- }
-
- /** @deprecated */
- @Deprecated
- @NonNull
- public Builder morphing(@IntRange(from = 0) int level,
- @IntRange(from = 0) int primitiveIndex,
- @NonNull MorphTargetBuffer morphTargetBuffer) {
- nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex,
- morphTargetBuffer.getNativeObject(), 0, morphTargetBuffer.getVertexCount());
+ @IntRange(from = 0) int offset) {
+ nBuilderSetMorphTargetBufferOffsetAt(mNativeBuilder, level, primitiveIndex, offset);
return this;
}
@@ -705,34 +678,11 @@ public void setMorphWeights(@EntityInstance int i, @NonNull float[] weights, @In
*
* @see Builder#morphing
*/
- public void setMorphTargetBufferAt(@EntityInstance int i,
- @IntRange(from = 0) int level,
- @IntRange(from = 0) int primitiveIndex,
- @IntRange(from = 0) int offset,
- @IntRange(from = 0) int count) {
- nSetMorphTargetBufferAt(mNativeObject, i, level, primitiveIndex, 0, offset, count);
- }
-
- /** @deprecated */
- @Deprecated
- public void setMorphTargetBufferAt(@EntityInstance int i,
- @IntRange(from = 0) int level,
- @IntRange(from = 0) int primitiveIndex,
- @NonNull MorphTargetBuffer morphTargetBuffer,
- @IntRange(from = 0) int offset,
- @IntRange(from = 0) int count) {
- nSetMorphTargetBufferAt(mNativeObject, i, level, primitiveIndex,
- morphTargetBuffer.getNativeObject(), offset, count);
- }
-
- /** @deprecated */
- @Deprecated
- public void setMorphTargetBufferAt(@EntityInstance int i,
+ public void setMorphTargetBufferOffsetAt(@EntityInstance int i,
@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
- @NonNull MorphTargetBuffer morphTargetBuffer) {
- nSetMorphTargetBufferAt(mNativeObject, i, level, primitiveIndex,
- morphTargetBuffer.getNativeObject(), 0, morphTargetBuffer.getVertexCount());
+ @IntRange(from = 0) int offset) {
+ nSetMorphTargetBufferOffsetAt(mNativeObject, i, level, primitiveIndex, 0, offset);
}
/**
@@ -1033,7 +983,7 @@ public long getNativeObject() {
private static native void nBuilderSkinningBuffer(long nativeBuilder, long nativeSkinningBuffer, int boneCount, int offset);
private static native void nBuilderMorphing(long nativeBuilder, int targetCount);
private static native void nBuilderMorphingStandard(long nativeBuilder, long nativeMorphTargetBuffer);
- private static native void nBuilderSetMorphTargetBufferAt(long nativeBuilder, int level, int primitiveIndex, long nativeMorphTargetBuffer, int offset, int count);
+ private static native void nBuilderSetMorphTargetBufferOffsetAt(long nativeBuilder, int level, int primitiveIndex, int offset);
private static native void nBuilderEnableSkinningBuffers(long nativeBuilder, boolean enabled);
private static native void nBuilderFog(long nativeBuilder, boolean enabled);
private static native void nBuilderLightChannel(long nativeRenderableManager, int channel, boolean enable);
@@ -1043,7 +993,7 @@ public long getNativeObject() {
private static native int nSetBonesAsMatrices(long nativeObject, int i, Buffer matrices, int remaining, int boneCount, int offset);
private static native int nSetBonesAsQuaternions(long nativeObject, int i, Buffer quaternions, int remaining, int boneCount, int offset);
private static native void nSetMorphWeights(long nativeObject, int instance, float[] weights, int offset);
- private static native void nSetMorphTargetBufferAt(long nativeObject, int i, int level, int primitiveIndex, long nativeMorphTargetBuffer, int offset, int count);
+ private static native void nSetMorphTargetBufferOffsetAt(long nativeObject, int i, int level, int primitiveIndex, long nativeMorphTargetBuffer, int offset);
private static native int nGetMorphTargetCount(long nativeObject, int i);
private static native void nSetAxisAlignedBoundingBox(long nativeRenderableManager, int i, float cx, float cy, float cz, float ex, float ey, float ez);
private static native void nSetLayerMask(long nativeRenderableManager, int i, int select, int value);
diff --git a/android/filament-android/src/main/java/com/google/android/filament/Renderer.java b/android/filament-android/src/main/java/com/google/android/filament/Renderer.java
index ab70e9ed385..4fb447db2c7 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/Renderer.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/Renderer.java
@@ -284,6 +284,19 @@ public void setPresentationTime(long monotonicClockNanos) {
nSetPresentationTime(getNativeObject(), monotonicClockNanos);
}
+ /**
+ * The use of this method is optional. It sets the VSYNC time expressed as the duration in
+ * nanosecond since epoch of std::chrono::steady_clock.
+ * If called, passing 0 to frameTimeNanos in Renderer.BeginFrame will use this
+ * time instead.
+ * @param steadyClockTimeNano duration in nanosecond since epoch of std::chrono::steady_clock
+ * @see Engine#getSteadyClockTimeNano
+ * @see Renderer#beginFrame
+ */
+ public void setVsyncTime(long steadyClockTimeNano) {
+ nSetVsyncTime(getNativeObject(), steadyClockTimeNano);
+ }
+
/**
* Sets up a frame for this Renderer
.
*
beginFrame
manages frame pacing, and returns whether or not a frame should be
@@ -702,6 +715,7 @@ void clearNativeObject() {
}
private static native void nSetPresentationTime(long nativeObject, long monotonicClockNanos);
+ private static native void nSetVsyncTime(long nativeObject, long steadyClockTimeNano);
private static native boolean nBeginFrame(long nativeRenderer, long nativeSwapChain, long frameTimeNanos);
private static native void nEndFrame(long nativeRenderer);
private static native void nRender(long nativeRenderer, long nativeView);
diff --git a/android/gradle.properties b/android/gradle.properties
index cca037f8f71..7994c61d4a1 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
-VERSION_NAME=1.53.1
+VERSION_NAME=1.53.2
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h
index 4f73c4375cd..b8befb4cee6 100644
--- a/filament/backend/include/backend/Platform.h
+++ b/filament/backend/include/backend/Platform.h
@@ -75,6 +75,8 @@ class UTILS_PUBLIC Platform {
*/
size_t textureUseAfterFreePoolSize = 0;
+ size_t metalUploadBufferSizeBytes = 512 * 1024;
+
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL and Metal backends.
diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h
index afc6dc924e0..d083fe3da92 100644
--- a/filament/backend/include/private/backend/HandleAllocator.h
+++ b/filament/backend/include/private/backend/HandleAllocator.h
@@ -181,6 +181,18 @@ class HandleAllocator {
return static_cast(p);
}
+ template
+ bool is_valid(Handle& handle) {
+ if (handle && isPoolHandle(handle.getId())) {
+ auto [p, tag] = handleToPointer(handle.getId());
+ uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
+ auto const pNode = static_cast(p);
+ uint8_t const expectedAge = pNode[-1].age;
+ return expectedAge == age;
+ }
+ return true;
+ }
+
template
inline typename std::enable_if_t<
std::is_pointer_v &&
diff --git a/filament/backend/src/metal/MetalBuffer.h b/filament/backend/src/metal/MetalBuffer.h
index 081ca6c3fd0..bb3de6d27c9 100644
--- a/filament/backend/src/metal/MetalBuffer.h
+++ b/filament/backend/src/metal/MetalBuffer.h
@@ -204,6 +204,15 @@ class MetalBuffer {
private:
+ enum class UploadStrategy {
+ POOL,
+ BUMP_ALLOCATOR,
+ };
+
+ void uploadWithPoolBuffer(void* src, size_t size, size_t byteOffset) const;
+ void uploadWithBumpAllocator(void* src, size_t size, size_t byteOffset) const;
+
+ UploadStrategy mUploadStrategy;
TrackedMetalBuffer mBuffer;
size_t mBufferSize = 0;
void* mCpuBuffer = nullptr;
diff --git a/filament/backend/src/metal/MetalBuffer.mm b/filament/backend/src/metal/MetalBuffer.mm
index 83fe25d5983..6070cf47fb3 100644
--- a/filament/backend/src/metal/MetalBuffer.mm
+++ b/filament/backend/src/metal/MetalBuffer.mm
@@ -27,7 +27,16 @@
MetalPlatform* ScopedAllocationTimer::platform = nullptr;
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
- size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
+ size_t size, bool forceGpuBuffer)
+ : mBufferSize(size), mContext(context) {
+ const MetalBumpAllocator& allocator = *mContext.bumpAllocator;
+ // VERTEX is also used for index buffers
+ if (allocator.getCapacity() > 0 && bindingType == BufferObjectBinding::VERTEX) {
+ mUploadStrategy = UploadStrategy::BUMP_ALLOCATOR;
+ } else {
+ mUploadStrategy = UploadStrategy::POOL;
+ }
+
// 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:.
// This won't work for SSBOs, since they are read/write.
@@ -61,34 +70,23 @@
FILAMENT_CHECK_PRECONDITION(size + byteOffset <= mBufferSize)
<< "Attempting to copy " << size << " bytes into a buffer of size " << mBufferSize
<< " at offset " << byteOffset;
+ // The copy blit requires that byteOffset be a multiple of 4.
+ FILAMENT_CHECK_PRECONDITION(!(byteOffset & 0x3)) << "byteOffset must be a multiple of 4";
- // Either copy into the Metal buffer or into our cpu buffer.
+ // If we have a cpu buffer, we can directly copy into it.
if (mCpuBuffer) {
memcpy(static_cast(mCpuBuffer) + byteOffset, src, size);
return;
}
- // Acquire a staging buffer to hold the contents of this update.
- MetalBufferPool* bufferPool = mContext.bufferPool;
- const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
- memcpy(staging->buffer.get().contents, src, size);
-
- // The blit below requires that byteOffset be a multiple of 4.
- FILAMENT_CHECK_PRECONDITION(!(byteOffset & 0x3u)) << "byteOffset must be a multiple of 4";
-
- // Encode a blit from the staging buffer into the private GPU buffer.
- id cmdBuffer = getPendingCommandBuffer(&mContext);
- id blitEncoder = [cmdBuffer blitCommandEncoder];
- blitEncoder.label = @"Buffer upload blit";
- [blitEncoder copyFromBuffer:staging->buffer.get()
- sourceOffset:0
- toBuffer:mBuffer.get()
- destinationOffset:byteOffset
- size:size];
- [blitEncoder endEncoding];
- [cmdBuffer addCompletedHandler:^(id cb) {
- bufferPool->releaseBuffer(staging);
- }];
+ switch (mUploadStrategy) {
+ case UploadStrategy::BUMP_ALLOCATOR:
+ uploadWithBumpAllocator(src, size, byteOffset);
+ break;
+ case UploadStrategy::POOL:
+ uploadWithPoolBuffer(src, size, byteOffset);
+ break;
+ }
}
void MetalBuffer::copyIntoBufferUnsynchronized(void* src, size_t size, size_t byteOffset) {
@@ -199,5 +197,42 @@
}
}
+void MetalBuffer::uploadWithPoolBuffer(void* src, size_t size, size_t byteOffset) const {
+ MetalBufferPool* bufferPool = mContext.bufferPool;
+ const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
+ memcpy(staging->buffer.get().contents, src, size);
+
+ // Encode a blit from the staging buffer into the private GPU buffer.
+ id cmdBuffer = getPendingCommandBuffer(&mContext);
+ id blitEncoder = [cmdBuffer blitCommandEncoder];
+ blitEncoder.label = @"Buffer upload blit - pool buffer";
+ [blitEncoder copyFromBuffer:staging->buffer.get()
+ sourceOffset:0
+ toBuffer:mBuffer.get()
+ destinationOffset:byteOffset
+ size:size];
+ [blitEncoder endEncoding];
+ [cmdBuffer addCompletedHandler:^(id cb) {
+ bufferPool->releaseBuffer(staging);
+ }];
+}
+
+void MetalBuffer::uploadWithBumpAllocator(void* src, size_t size, size_t byteOffset) const {
+ MetalBumpAllocator& allocator = *mContext.bumpAllocator;
+ auto [buffer, offset] = allocator.allocateStagingArea(size);
+ memcpy(static_cast(buffer.contents) + offset, src, size);
+
+ // Encode a blit from the staging buffer into the private GPU buffer.
+ id cmdBuffer = getPendingCommandBuffer(&mContext);
+ id blitEncoder = [cmdBuffer blitCommandEncoder];
+ blitEncoder.label = @"Buffer upload blit - bump allocator";
+ [blitEncoder copyFromBuffer:buffer
+ sourceOffset:offset
+ toBuffer:mBuffer.get()
+ destinationOffset:byteOffset
+ size:size];
+ [blitEncoder endEncoding];
+}
+
} // namespace backend
} // namespace filament
diff --git a/filament/backend/src/metal/MetalBufferPool.h b/filament/backend/src/metal/MetalBufferPool.h
index 03688ab3c43..770527e092a 100644
--- a/filament/backend/src/metal/MetalBufferPool.h
+++ b/filament/backend/src/metal/MetalBufferPool.h
@@ -38,6 +38,28 @@ struct MetalBufferPoolEntry {
mutable uint32_t referenceCount;
};
+class MetalBumpAllocator {
+public:
+ MetalBumpAllocator(id device, size_t capacity);
+
+ /**
+ * Allocates a staging area of the given size. Returns a pair of the buffer and the offset
+ * within the buffer. The buffer is guaranteed to be at least the given size, but may be larger.
+ * Clients must not write to the buffer beyond the returned offset + size.
+ * Clients are responsible for holding a reference to the returned buffer.
+ * Allocations are guaranteed to be aligned to 4 bytes.
+ */
+ std::pair, size_t> allocateStagingArea(size_t size);
+
+ size_t getCapacity() const noexcept { return mCapacity; }
+
+private:
+ id mDevice;
+ TrackedMetalBuffer mCurrentUploadBuffer = nil;
+ size_t mHead = 0;
+ size_t mCapacity;
+};
+
// Manages a pool of Metal buffers, periodically releasing ones that have been unused for awhile.
class MetalBufferPool {
public:
diff --git a/filament/backend/src/metal/MetalBufferPool.mm b/filament/backend/src/metal/MetalBufferPool.mm
index c9a80e83520..837d4c4d130 100644
--- a/filament/backend/src/metal/MetalBufferPool.mm
+++ b/filament/backend/src/metal/MetalBufferPool.mm
@@ -116,5 +116,39 @@
mFreeStages.clear();
}
+MetalBumpAllocator::MetalBumpAllocator(id device, size_t capacity)
+ : mDevice(device), mCapacity(capacity) {
+ if (mCapacity > 0) {
+ mCurrentUploadBuffer = { [device newBufferWithLength:capacity options:MTLStorageModeShared],
+ TrackedMetalBuffer::Type::STAGING };
+ }
+}
+
+std::pair, size_t> MetalBumpAllocator::allocateStagingArea(size_t size) {
+ if (size == 0) {
+ return { nil, 0 };
+ }
+ if (size > mCapacity) {
+ return { [mDevice newBufferWithLength:size options:MTLStorageModeShared], 0 };
+ }
+ assert_invariant(mCurrentUploadBuffer);
+
+ // Align the head to a 4-byte boundary.
+ mHead = (mHead + 3) & ~3;
+
+ if (UTILS_LIKELY(mHead + size <= mCapacity)) {
+ const size_t oldHead = mHead;
+ mHead += size;
+ return { mCurrentUploadBuffer.get(), oldHead };
+ }
+
+ // We're finished with the current allocation.
+ mCurrentUploadBuffer = { [mDevice newBufferWithLength:mCapacity options:MTLStorageModeShared],
+ TrackedMetalBuffer::Type::STAGING };
+ mHead = size;
+
+ return { mCurrentUploadBuffer.get(), 0 };
+}
+
} // namespace backend
} // namespace filament
diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h
index 8a14d9a36c8..0f920466e98 100644
--- a/filament/backend/src/metal/MetalContext.h
+++ b/filament/backend/src/metal/MetalContext.h
@@ -44,6 +44,7 @@ namespace backend {
class MetalDriver;
class MetalBlitter;
class MetalBufferPool;
+class MetalBumpAllocator;
class MetalRenderTarget;
class MetalSamplerGroup;
class MetalSwapChain;
@@ -141,6 +142,7 @@ struct MetalContext {
utils::FixedCircularBuffer> texturesToDestroy;
MetalBufferPool* bufferPool;
+ MetalBumpAllocator* bumpAllocator;
MetalSwapChain* currentDrawSwapChain = nil;
MetalSwapChain* currentReadSwapChain = nil;
diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm
index 58852a67143..92b1b22f736 100644
--- a/filament/backend/src/metal/MetalDriver.mm
+++ b/filament/backend/src/metal/MetalDriver.mm
@@ -171,6 +171,8 @@
mContext->samplerStateCache.setDevice(mContext->device);
mContext->argumentEncoderCache.setDevice(mContext->device);
mContext->bufferPool = new MetalBufferPool(*mContext);
+ mContext->bumpAllocator =
+ new MetalBumpAllocator(mContext->device, driverConfig.metalUploadBufferSizeBytes);
mContext->blitter = new MetalBlitter(*mContext);
if (@available(iOS 12, *)) {
@@ -209,6 +211,7 @@
mContext->emptyTexture = nil;
CFRelease(mContext->textureCache);
delete mContext->bufferPool;
+ delete mContext->bumpAllocator;
delete mContext->blitter;
delete mContext->timerQueryImpl;
delete mContext->shaderCompiler;
diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp
index 414d0c8aa21..5b011ee6254 100644
--- a/filament/backend/src/opengl/OpenGLDriver.cpp
+++ b/filament/backend/src/opengl/OpenGLDriver.cpp
@@ -304,6 +304,13 @@ void OpenGLDriver::bindSampler(GLuint unit, GLuint sampler) noexcept {
void OpenGLDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
assert_invariant(stage == ShaderStage::VERTEX || stage == ShaderStage::FRAGMENT);
+
+#if FILAMENT_ENABLE_MATDBG
+ if (UTILS_UNLIKELY(!mValidProgram)) {
+ return;
+ }
+#endif
+
utils::Slice> constants;
if (stage == ShaderStage::VERTEX) {
constants = mCurrentPushConstants->vertexConstants;
@@ -340,15 +347,11 @@ void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
}
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
- if (UTILS_UNLIKELY(!p->isValid())) {
- // If the program is not valid, we can't call use().
- return false;
- }
-
// set-up textures and samplers in the proper TMUs (as specified in setSamplers)
- p->use(this, mContext);
+ bool const success = p->use(this, mContext);
+ assert_invariant(success == p->isValid());
- if (UTILS_UNLIKELY(mContext.isES2())) {
+ if (UTILS_UNLIKELY(mContext.isES2() && success)) {
for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) {
auto [id, buffer, age] = mContext.getEs2UniformBinding(i);
if (buffer) {
@@ -359,7 +362,8 @@ bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
// when mPlatform.isSRGBSwapChainSupported() is false (no need to check though).
p->setRec709ColorSpace(mRec709OutputColorspace);
}
- return true;
+
+ return success;
}
@@ -2332,9 +2336,9 @@ void OpenGLDriver::updateSamplerGroup(Handle sbh,
auto const* const pSamplers = (SamplerDescriptor const*)data.buffer;
for (size_t i = 0, c = sb->textureUnitEntries.size(); i < c; i++) {
GLuint samplerId = 0u;
- const GLTexture* t = nullptr;
- if (UTILS_LIKELY(pSamplers[i].t)) {
- t = handle_cast(pSamplers[i].t);
+ Handle th = pSamplers[i].t;
+ if (UTILS_LIKELY(th)) {
+ GLTexture const* const t = handle_cast(th);
assert_invariant(t);
SamplerParams params = pSamplers[i].s;
@@ -2390,7 +2394,7 @@ void OpenGLDriver::updateSamplerGroup(Handle sbh,
// which is not an error.
}
- sb->textureUnitEntries[i] = { t, samplerId };
+ sb->textureUnitEntries[i] = { th, samplerId };
}
scheduleDestroy(std::move(data));
}
diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h
index d5e1b797b68..5345d474bf6 100644
--- a/filament/backend/src/opengl/OpenGLDriver.h
+++ b/filament/backend/src/opengl/OpenGLDriver.h
@@ -126,7 +126,7 @@ class OpenGLDriver final : public DriverBase {
struct GLSamplerGroup : public HwSamplerGroup {
using HwSamplerGroup::HwSamplerGroup;
struct Entry {
- GLTexture const* texture = nullptr;
+ Handle th;
GLuint sampler = 0u;
};
utils::FixedCapacityVector textureUnitEntries;
@@ -256,6 +256,11 @@ class OpenGLDriver final : public DriverBase {
return mHandleAllocator.handle_cast(handle);
}
+ template
+ bool is_valid(Handle& handle) {
+ return mHandleAllocator.is_valid(handle);
+ }
+
template
inline typename std::enable_if_t<
std::is_pointer_v &&
diff --git a/filament/backend/src/opengl/OpenGLProgram.cpp b/filament/backend/src/opengl/OpenGLProgram.cpp
index 6c5eab9a442..7a85e3c63cd 100644
--- a/filament/backend/src/opengl/OpenGLProgram.cpp
+++ b/filament/backend/src/opengl/OpenGLProgram.cpp
@@ -20,21 +20,24 @@
#include "OpenGLDriver.h"
#include "ShaderCompilerService.h"
+#include
#include
+#include
-#include
-
-#include
#include
+#include
+#include
#include
#include
+#include
#include
#include
#include
#include
#include
+#include
namespace filament::backend {
@@ -241,8 +244,9 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
assert_invariant(sb);
if (!sb) continue; // should never happen, this would be a user error.
for (uint8_t j = 0, m = sb->textureUnitEntries.size(); j < m; ++j, ++tmu) { // "<=" on purpose here
- const GLTexture* const t = sb->textureUnitEntries[j].texture;
- if (t) { // program may not use all samplers of sampler group
+ Handle th = sb->textureUnitEntries[j].th;
+ if (th) { // program may not use all samplers of sampler group
+ GLTexture const* const t = gld->handle_cast(th);
gld->bindTexture(tmu, t);
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (UTILS_LIKELY(!es2)) {
diff --git a/filament/backend/src/opengl/OpenGLProgram.h b/filament/backend/src/opengl/OpenGLProgram.h
index 19be485ac6b..2cb43131ea1 100644
--- a/filament/backend/src/opengl/OpenGLProgram.h
+++ b/filament/backend/src/opengl/OpenGLProgram.h
@@ -53,11 +53,21 @@ class OpenGLProgram : public HwProgram {
bool isValid() const noexcept { return mToken || gl.program != 0; }
- void use(OpenGLDriver* const gld, OpenGLContext& context) noexcept {
- if (UTILS_UNLIKELY(!gl.program)) {
+ bool use(OpenGLDriver* const gld, OpenGLContext& context) noexcept {
+ // both non-null is impossible by construction
+ assert_invariant(!mToken || !gl.program);
+
+ if (UTILS_UNLIKELY(mToken && !gl.program)) {
+ // first time a program is used
initialize(*gld);
}
+ if (UTILS_UNLIKELY(!gl.program)) {
+ // compilation failed (token should be null)
+ assert_invariant(!mToken);
+ return false;
+ }
+
context.useProgram(gl.program);
if (UTILS_UNLIKELY(mUsedBindingsCount)) {
// We rely on GL state tracking to avoid unnecessary glBindTexture / glBindSampler
@@ -74,6 +84,7 @@ class OpenGLProgram : public HwProgram {
updateSamplers(gld);
}
+ return true;
}
// For ES2 only
diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp
index 5b9397ddd4b..b40bda0249f 100644
--- a/filament/backend/src/opengl/ShaderCompilerService.cpp
+++ b/filament/backend/src/opengl/ShaderCompilerService.cpp
@@ -359,7 +359,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t& token) {
GLuint const program = initialize(token);
assert_invariant(token == nullptr);
-#ifndef FILAMENT_ENABLE_MATDBG
+#if !FILAMENT_ENABLE_MATDBG
assert_invariant(program);
#endif
return program;
@@ -572,16 +572,23 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
// split shader source, so we can insert the specialization constants and the packing
// functions
- auto const [prolog, body] = splitShaderSource({ shader_src, shader_len });
+ auto [version, prolog, body] = splitShaderSource({ shader_src, shader_len });
- const std::array sources = {
+ // enable ESSL 3.10 if available
+ if (context.isAtLeastGLES<3, 1>()) {
+ version = "#version 310 es\n";
+ }
+
+ const std::array sources = {
+ version.data(),
prolog.data(),
specializationConstantString.c_str(),
packingFunctions.data(),
body.data()
};
- const std::array lengths = {
+ const std::array lengths = {
+ (GLint)version.length(),
(GLint)prolog.length(),
(GLint)specializationConstantString.length(),
(GLint)packingFunctions.length(),
@@ -661,6 +668,7 @@ void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context,
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
// macOS doesn't support GL_ARB_shading_language_packing
+// Also GLES3.0 didn't have the full set of packing/unpacking functions
std::string_view ShaderCompilerService::process_ARB_shading_language_packing(OpenGLContext& context) noexcept {
using namespace std::literals;
#ifdef BACKEND_OPENGL_VERSION_GL
@@ -700,31 +708,102 @@ highp uint packHalf2x16(vec2 v) {
highp uint y = fp32tou16(v.y);
return (y << 16u) | x;
}
+highp uint packUnorm4x8(mediump vec4 v) {
+ v = round(clamp(v, 0.0, 1.0) * 255.0);
+ highp uint a = uint(v.x);
+ highp uint b = uint(v.y) << 8;
+ highp uint c = uint(v.z) << 16;
+ highp uint d = uint(v.w) << 24;
+ return (a|b|c|d);
+}
+highp uint packSnorm4x8(mediump vec4 v) {
+ v = round(clamp(v, -1.0, 1.0) * 127.0);
+ highp uint a = uint((int(v.x) & 0xff));
+ highp uint b = uint((int(v.y) & 0xff)) << 8;
+ highp uint c = uint((int(v.z) & 0xff)) << 16;
+ highp uint d = uint((int(v.w) & 0xff)) << 24;
+ return (a|b|c|d);
+}
+mediump vec4 unpackUnorm4x8(highp uint v) {
+ return vec4(float((v & 0x000000ffu) ),
+ float((v & 0x0000ff00u) >> 8),
+ float((v & 0x00ff0000u) >> 16),
+ float((v & 0xff000000u) >> 24)) / 255.0;
+}
+mediump vec4 unpackSnorm4x8(highp uint v) {
+ int a = int(((v ) & 0xffu) << 24u) >> 24 ;
+ int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
+ int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
+ int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
+ return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
+}
)"sv;
}
#endif // BACKEND_OPENGL_VERSION_GL
+
+#ifdef BACKEND_OPENGL_VERSION_GLES
+ if (!context.isES2() && !context.isAtLeastGLES<3, 1>()) {
+ return R"(
+
+highp uint packUnorm4x8(mediump vec4 v) {
+ v = round(clamp(v, 0.0, 1.0) * 255.0);
+ highp uint a = uint(v.x);
+ highp uint b = uint(v.y) << 8;
+ highp uint c = uint(v.z) << 16;
+ highp uint d = uint(v.w) << 24;
+ return (a|b|c|d);
+}
+highp uint packSnorm4x8(mediump vec4 v) {
+ v = round(clamp(v, -1.0, 1.0) * 127.0);
+ highp uint a = uint((int(v.x) & 0xff));
+ highp uint b = uint((int(v.y) & 0xff)) << 8;
+ highp uint c = uint((int(v.z) & 0xff)) << 16;
+ highp uint d = uint((int(v.w) & 0xff)) << 24;
+ return (a|b|c|d);
+}
+mediump vec4 unpackUnorm4x8(highp uint v) {
+ return vec4(float((v & 0x000000ffu) ),
+ float((v & 0x0000ff00u) >> 8),
+ float((v & 0x00ff0000u) >> 16),
+ float((v & 0xff000000u) >> 24)) / 255.0;
+}
+mediump vec4 unpackSnorm4x8(highp uint v) {
+ int a = int(((v ) & 0xffu) << 24u) >> 24 ;
+ int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
+ int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
+ int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
+ return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
+}
+)"sv;
+ }
+#endif // BACKEND_OPENGL_VERSION_GLES
return ""sv;
}
-// split shader source code in two, the first section goes from the start to the line after the
-// last #extension, and the 2nd part goes from there to the end.
-std::array ShaderCompilerService::splitShaderSource(std::string_view source) noexcept {
- auto start = source.find("#version");
- assert_invariant(start != std::string_view::npos);
+// split shader source code in three:
+// - the version line
+// - extensions
+// - everything else
+std::array ShaderCompilerService::splitShaderSource(std::string_view source) noexcept {
+ auto version_start = source.find("#version");
+ assert_invariant(version_start != std::string_view::npos);
- auto pos = source.rfind("\n#extension");
- if (pos == std::string_view::npos) {
- pos = start;
+ auto version_eol = source.find('\n', version_start) + 1;
+ assert_invariant(version_eol != std::string_view::npos);
+
+ auto prolog_start = version_eol;
+ auto prolog_eol = source.rfind("\n#extension"); // last #extension line
+ if (prolog_eol == std::string_view::npos) {
+ prolog_eol = prolog_start;
} else {
- ++pos;
+ prolog_eol = source.find('\n', prolog_eol + 1) + 1;
}
+ auto body_start = prolog_eol;
- auto eol = source.find('\n', pos) + 1;
- assert_invariant(eol != std::string_view::npos);
-
- std::string_view const version = source.substr(start, eol - start);
- std::string_view const body = source.substr(version.length(), source.length() - version.length());
- return { version, body };
+ std::string_view const version = source.substr(version_start, version_eol - version_start);
+ std::string_view const prolog = source.substr(prolog_start, prolog_eol - prolog_start);
+ std::string_view const body = source.substr(body_start, source.length() - body_start);
+ return { version, prolog, body };
}
/*
diff --git a/filament/backend/src/opengl/ShaderCompilerService.h b/filament/backend/src/opengl/ShaderCompilerService.h
index 27255ea0002..ef4059fc0bc 100644
--- a/filament/backend/src/opengl/ShaderCompilerService.h
+++ b/filament/backend/src/opengl/ShaderCompilerService.h
@@ -146,7 +146,7 @@ class ShaderCompilerService {
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
- static std::array splitShaderSource(std::string_view source) noexcept;
+ static std::array splitShaderSource(std::string_view source) noexcept;
static GLuint linkProgram(OpenGLContext& context,
std::array shaders,
diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h
index 2aed82736f2..6c3d87c29c4 100644
--- a/filament/include/filament/Engine.h
+++ b/filament/include/filament/Engine.h
@@ -298,6 +298,24 @@ class UTILS_PUBLIC Engine {
*/
size_t textureUseAfterFreePoolSize = 0;
+ /**
+ * When uploading vertex or index data, the Filament Metal backend copies data
+ * into a shared staging area before transferring it to the GPU. This setting controls
+ * the total size of the buffer used to perform these allocations.
+ *
+ * Higher values can improve performance when performing many uploads across a small
+ * number of frames.
+ *
+ * This buffer remains alive throughout the lifetime of the Engine, so this size adds to the
+ * memory footprint of the app and should be set as conservative as possible.
+ *
+ * A value of 0 disables the shared staging buffer entirely; uploads will acquire an
+ * individual buffer from a pool of shared buffers.
+ *
+ * Only respected by the Metal backend.
+ */
+ size_t metalUploadBufferSizeBytes = 512 * 1024;
+
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL and Metal backends.
@@ -1021,6 +1039,21 @@ class UTILS_PUBLIC Engine {
void resetBackendState() noexcept;
#endif
+ /**
+ * Get the current time. This is a convenience function that simply returns the
+ * time in nanosecond since epoch of std::chrono::steady_clock.
+ * A possible implementation is:
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * return std::chrono::steady_clock::now().time_since_epoch().count();
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * @return current time in nanosecond since epoch of std::chrono::steady_clock.
+ * @see Renderer::beginFrame()
+ */
+ static uint64_t getSteadyClockTimeNano() noexcept;
+
+
DebugRegistry& getDebugRegistry() noexcept;
protected:
diff --git a/filament/include/filament/IndirectLight.h b/filament/include/filament/IndirectLight.h
index c230dac8fe6..c4cc62ecd79 100644
--- a/filament/include/filament/IndirectLight.h
+++ b/filament/include/filament/IndirectLight.h
@@ -158,6 +158,8 @@ class UTILS_PUBLIC IndirectLight : public FilamentAPI {
*
* @return This Builder, for chaining calls.
*
+ * @see Material::Builder::sphericalHarmonicsBandCount()
+ *
* @note
* Because the coefficients are pre-scaled, `sh[0]` is the environment's
* average irradiance.
diff --git a/filament/include/filament/Material.h b/filament/include/filament/Material.h
index b4b9bbed3e2..11cbaef30dd 100644
--- a/filament/include/filament/Material.h
+++ b/filament/include/filament/Material.h
@@ -140,6 +140,18 @@ class UTILS_PUBLIC Material : public FilamentAPI {
return constant(name, strlen(name), value);
}
+ /**
+ * Sets the quality of the indirect lights computations. This is only taken into account
+ * if this material is lit and in the surface domain. This setting will affect the
+ * IndirectLight computation if one is specified on the Scene and Spherical Harmonics
+ * are used for the irradiance.
+ *
+ * @param shBandCount Number of spherical harmonic bands. Must be 1, 2 or 3 (default).
+ * @return Reference to this Builder for chaining calls.
+ * @see IndirectLight
+ */
+ Builder& sphericalHarmonicsBandCount(size_t shBandCount) noexcept;
+
/**
* Creates the Material object and returns a pointer to it.
*
diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h
index 363ef2d7c1f..2c227e0c644 100644
--- a/filament/include/filament/RenderableManager.h
+++ b/filament/include/filament/RenderableManager.h
@@ -489,43 +489,20 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI {
* For standard morphing, A MorphTargetBuffer must be provided.
* Standard morphing supports up to \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets.
*
- * For legacy morphing, the attached VertexBuffer must provide data in the
- * appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only
- * supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must
- * be enabled on the material definition: either via the legacyMorphing material attribute
- * or by calling filamat::MaterialBuilder::useLegacyMorphing().
- *
* See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis
* to advance the animation.
*/
Builder& morphing(MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept;
- /**
- * @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead
- */
- Builder& morphing(uint8_t level, size_t primitiveIndex,
- MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer,
- size_t offset, size_t count) noexcept;
-
- /**
- * @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead
- */
- inline Builder& morphing(uint8_t level, size_t primitiveIndex,
- MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
- return morphing(level, primitiveIndex, morphTargetBuffer, 0,
- morphTargetBuffer->getVertexCount());
- }
-
/**
* Specifies the the range of the MorphTargetBuffer to use with this primitive.
*
* @param level the level of detail (lod), only 0 can be specified
* @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor
* @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices)
- * @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
- Builder& morphing(uint8_t level, size_t primitiveIndex,
- size_t offset, size_t count) noexcept;
+ RenderableManager::Builder& morphing(uint8_t level,
+ size_t primitiveIndex, size_t offset) noexcept;
/**
@@ -786,25 +763,13 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI {
/**
* Associates a MorphTargetBuffer to the given primitive.
*/
- void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
- size_t offset, size_t count);
-
- /** @deprecated */
- void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
- MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count);
-
- /** @deprecated */
- inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
- MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) {
- setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0,
- morphTargetBuffer->getVertexCount());
- }
+ void setMorphTargetBufferOffsetAt(Instance instance, uint8_t level, size_t primitiveIndex,
+ size_t offset);
/**
- * Get a MorphTargetBuffer to the given primitive or null if it doesn't exist.
+ * Get a MorphTargetBuffer to the given renderable or null if it doesn't exist.
*/
- MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBufferAt(Instance instance,
- uint8_t level, size_t primitiveIndex) const noexcept;
+ MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBuffer(Instance instance) const noexcept;
/**
* Gets the number of morphing in the given entity.
diff --git a/filament/include/filament/Renderer.h b/filament/include/filament/Renderer.h
index fdc291b1fd1..83b62816044 100644
--- a/filament/include/filament/Renderer.h
+++ b/filament/include/filament/Renderer.h
@@ -227,6 +227,17 @@ class UTILS_PUBLIC Renderer : public FilamentAPI {
static constexpr CopyFrameFlag CLEAR = 0x4;
+ /**
+ * The use of this method is optional. It sets the VSYNC time expressed as the duration in
+ * nanosecond since epoch of std::chrono::steady_clock.
+ * If called, passing 0 to vsyncSteadyClockTimeNano in Renderer::BeginFrame will use this
+ * time instead.
+ * @param steadyClockTimeNano duration in nanosecond since epoch of std::chrono::steady_clock
+ * @see Engine::getSteadyClockTimeNano()
+ * @see Renderer::BeginFrame()
+ */
+ void setVsyncTime(uint64_t steadyClockTimeNano) noexcept;
+
/**
* Set-up a frame for this Renderer.
*
diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp
index ad110dbc922..477a0871c84 100644
--- a/filament/src/Engine.cpp
+++ b/filament/src/Engine.cpp
@@ -39,6 +39,8 @@
#include
#include
+#include
+
#include
#include
@@ -361,7 +363,7 @@ const Engine::Config& Engine::getConfig() const noexcept {
return downcast(this)->getConfig();
}
-bool Engine::isStereoSupported(StereoscopicType stereoscopicType) const noexcept {
+bool Engine::isStereoSupported(StereoscopicType) const noexcept {
return downcast(this)->isStereoSupported();
}
@@ -369,6 +371,10 @@ size_t Engine::getMaxStereoscopicEyes() noexcept {
return FEngine::getMaxStereoscopicEyes();
}
+uint64_t Engine::getSteadyClockTimeNano() noexcept {
+ return std::chrono::steady_clock::now().time_since_epoch().count();
+}
+
#if defined(__EMSCRIPTEN__)
void Engine::resetBackendState() noexcept {
downcast(this)->resetBackendState();
diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp
index ee5092370d0..cc892b910d4 100644
--- a/filament/src/RenderPass.cpp
+++ b/filament/src/RenderPass.cpp
@@ -671,7 +671,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
*/
for (size_t pi = 0, c = primitives.size(); pi < c; ++pi) {
auto const& primitive = primitives[pi];
- auto const& morphTargets = morphing.targets[pi];
FMaterialInstance const* const mi = primitive.getMaterialInstance();
FMaterial const* const ma = mi->getMaterial();
@@ -684,8 +683,9 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.indexOffset = primitive.getIndexOffset();
cmd.info.indexCount = primitive.getIndexCount();
cmd.info.type = primitive.getPrimitiveType();
- cmd.info.morphTargetBuffer = morphTargets.buffer->getHwHandle();
- cmd.info.morphingOffset = morphTargets.offset;
+ cmd.info.morphTargetBuffer = morphing.morphTargetBuffer ?
+ morphing.morphTargetBuffer->getHwHandle() : SamplerGroupHandle{};
+ cmd.info.morphingOffset = primitive.getMorphingBufferOffset();
if constexpr (isColorPass) {
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
diff --git a/filament/src/RenderPrimitive.h b/filament/src/RenderPrimitive.h
index 07d7431f021..a8600dd3a6f 100644
--- a/filament/src/RenderPrimitive.h
+++ b/filament/src/RenderPrimitive.h
@@ -56,6 +56,7 @@ class FRenderPrimitive {
backend::VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; }
uint32_t getIndexOffset() const noexcept { return mIndexOffset; }
uint32_t getIndexCount() const noexcept { return mIndexCount; }
+ uint32_t getMorphingBufferOffset() const noexcept { return mMorphingBufferOffset; }
backend::PrimitiveType getPrimitiveType() const noexcept { return mPrimitiveType; }
AttributeBitset getEnabledAttributes() const noexcept { return mEnabledAttributes; }
@@ -72,6 +73,10 @@ class FRenderPrimitive {
mGlobalBlendOrderEnabled = enabled;
}
+ void setMorphingBufferOffset(uint32_t offset) noexcept {
+ mMorphingBufferOffset = offset;
+ }
+
private:
// These first fields are dereferences from PrimitiveInfo, keep them together
struct {
@@ -80,6 +85,7 @@ class FRenderPrimitive {
backend::Handle mVertexBufferInfoHandle = {};
uint32_t mIndexOffset = 0;
uint32_t mIndexCount = 0;
+ uint32_t mMorphingBufferOffset = 0;
};
AttributeBitset mEnabledAttributes = {};
diff --git a/filament/src/RenderableManager.cpp b/filament/src/RenderableManager.cpp
index 9550197acf0..87ea0d0e462 100644
--- a/filament/src/RenderableManager.cpp
+++ b/filament/src/RenderableManager.cpp
@@ -158,20 +158,14 @@ void RenderableManager::setMorphWeights(Instance instance, float const* weights,
downcast(this)->setMorphWeights(instance, weights, count, offset);
}
-void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
- MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) {
- downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex,
- downcast(morphTargetBuffer), offset, count);
+void RenderableManager::setMorphTargetBufferOffsetAt(Instance instance, uint8_t level,
+ size_t primitiveIndex,
+ size_t offset) {
+ downcast(this)->setMorphTargetBufferOffsetAt(instance, level, primitiveIndex, offset);
}
-void RenderableManager::setMorphTargetBufferAt(
- Instance instance, uint8_t level, size_t primitiveIndex, size_t offset, size_t count) {
- downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex, offset, count);
-}
-
-MorphTargetBuffer* RenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level,
- size_t primitiveIndex) const noexcept {
- return downcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex);
+MorphTargetBuffer* RenderableManager::getMorphTargetBuffer(Instance instance) const noexcept {
+ return downcast(this)->getMorphTargetBuffer(instance);
}
size_t RenderableManager::getMorphTargetCount(Instance instance) const noexcept {
diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp
index 85d58f9ec15..00837319b24 100644
--- a/filament/src/Renderer.cpp
+++ b/filament/src/Renderer.cpp
@@ -89,4 +89,8 @@ void Renderer::renderStandaloneView(View const* view) {
downcast(this)->renderStandaloneView(downcast(view));
}
+void Renderer::setVsyncTime(uint64_t steadyClockTimeNano) noexcept {
+ downcast(this)->setVsyncTime(steadyClockTimeNano);
+}
+
} // namespace filament
diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp
index 596cd8e7fe7..cdadc4e35e0 100644
--- a/filament/src/components/RenderableManager.cpp
+++ b/filament/src/components/RenderableManager.cpp
@@ -281,19 +281,13 @@ RenderableManager::Builder& RenderableManager::Builder::morphing(
return *this;
}
-RenderableManager::Builder& RenderableManager::Builder::morphing(
- uint8_t level, size_t primitiveIndex, size_t offset, size_t count) noexcept {
- return morphing(level, primitiveIndex, mImpl->mMorphTargetBuffer, offset, count);
-}
-
-RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t, size_t primitiveIndex,
- MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept {
- std::vector& entries = mImpl->mEntries;
+RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t level,
+ size_t primitiveIndex, size_t offset) noexcept {
+ // the last parameter "count" is unused, because it must be equal to the primitive's vertex count
+ std::vector& entries = mImpl->mEntries;
if (primitiveIndex < entries.size()) {
auto& morphing = entries[primitiveIndex].morphing;
- morphing.buffer = morphTargetBuffer;
- morphing.offset = offset;
- morphing.count = count;
+ morphing.offset = uint32_t(offset);
}
return *this;
}
@@ -668,13 +662,6 @@ void FRenderableManager::create(
if (morphTargetBuffer == nullptr) {
morphTargetBuffer = mEngine.getDummyMorphTargetBuffer();
}
- MorphTargets* const morphTargets = new MorphTargets[entryCount];
- std::generate_n(morphTargets, entryCount,
- [morphTargetBuffer]() -> MorphTargets {
- return { morphTargetBuffer, 0, 0 };
- });
-
- mManager[ci].morphTargets = { morphTargets, size_type(entryCount) };
// Always create skinning and morphing resources if one of them is enabled because
// the shader always handles both. See Variant::SKINNING_OR_MORPHING.
@@ -700,13 +687,13 @@ void FRenderableManager::create(
backend::BufferUsage::DYNAMIC),
.count = targetCount };
- for (size_t i = 0; i < entryCount; ++i) {
- const auto& morphing = builder->mEntries[i].morphing;
- if (!morphing.buffer) {
- continue;
+ Slice& primitives = mManager[ci].primitives;
+ mManager[ci].morphTargetBuffer = morphTargetBuffer;
+ if (builder->mMorphTargetBuffer) {
+ for (size_t i = 0; i < entryCount; ++i) {
+ const auto& morphing = builder->mEntries[i].morphing;
+ primitives[i].setMorphingBufferOffset(morphing.offset);
}
- morphTargets[i] = { downcast(morphing.buffer), (uint32_t)morphing.offset,
- (uint32_t)morphing.count };
}
// When targetCount equal 0, boneCount>0 in this case, do an initialization for the
@@ -762,7 +749,6 @@ void FRenderableManager::destroyComponent(Instance ci) noexcept {
// See create(RenderableManager::Builder&, Entity)
destroyComponentPrimitives(mHwRenderPrimitiveFactory, driver, manager[ci].primitives);
- destroyComponentMorphTargets(engine, manager[ci].morphTargets);
// destroy the bones structures if any
Bones const& bones = manager[ci].bones;
@@ -795,11 +781,6 @@ void FRenderableManager::destroyComponentPrimitives(
delete[] primitives.data();
}
-void FRenderableManager::destroyComponentMorphTargets(FEngine&,
- utils::Slice& morphTargets) noexcept {
- delete[] morphTargets.data();
-}
-
void FRenderableManager::setMaterialInstanceAt(Instance instance, uint8_t level,
size_t primitiveIndex, FMaterialInstance const* mi) {
if (instance) {
@@ -968,42 +949,21 @@ void FRenderableManager::setMorphWeights(Instance instance, float const* weights
}
}
-void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level,
- size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) {
- if (instance) {
- assert_invariant(morphTargetBuffer);
-
- MorphWeights const& morphWeights = mManager[instance].morphWeights;
- FILAMENT_CHECK_PRECONDITION(morphWeights.count == count)
- << "Only " << morphWeights.count << " morph targets can be set (count=" << count
- << ")";
-
- Slice& morphTargets = getMorphTargets(instance, level);
- if (primitiveIndex < morphTargets.size()) {
- morphTargets[primitiveIndex] = { morphTargetBuffer, (uint32_t)offset,
- (uint32_t)count };
- }
- }
-}
-
-void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level,
- size_t primitiveIndex, size_t offset, size_t count) {
+void FRenderableManager::setMorphTargetBufferOffsetAt(Instance instance, uint8_t level,
+ size_t primitiveIndex,
+ size_t offset) {
if (instance) {
- Slice& morphTargets = getMorphTargets(instance, level);
- if (primitiveIndex < morphTargets.size()) {
- setMorphTargetBufferAt(instance, level,
- primitiveIndex, morphTargets[primitiveIndex].buffer, offset, count);
+ assert_invariant(mManager[instance].morphTargetBuffer);
+ Slice& primitives = mManager[instance].primitives;
+ if (primitiveIndex < primitives.size()) {
+ primitives[primitiveIndex].setMorphingBufferOffset(offset);
}
}
}
-MorphTargetBuffer* FRenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level,
- size_t primitiveIndex) const noexcept {
+MorphTargetBuffer* FRenderableManager::getMorphTargetBuffer(Instance instance) const noexcept {
if (instance) {
- const Slice& morphTargets = getMorphTargets(instance, level);
- if (primitiveIndex < morphTargets.size()) {
- return morphTargets[primitiveIndex].buffer;
- }
+ return mManager[instance].morphTargetBuffer;
}
return nullptr;
}
diff --git a/filament/src/components/RenderableManager.h b/filament/src/components/RenderableManager.h
index 0fa44c83e77..649d38829d8 100644
--- a/filament/src/components/RenderableManager.h
+++ b/filament/src/components/RenderableManager.h
@@ -80,12 +80,6 @@ class FRenderableManager : public RenderableManager {
static_assert(sizeof(Visibility) == sizeof(uint16_t), "Visibility should be 16 bits");
- struct MorphTargets {
- FMorphTargetBuffer* buffer = nullptr;
- uint32_t offset = 0;
- uint32_t count = 0;
- };
-
explicit FRenderableManager(FEngine& engine) noexcept;
~FRenderableManager();
@@ -155,11 +149,9 @@ class FRenderableManager : public RenderableManager {
inline void setMorphing(Instance instance, bool enable);
void setMorphWeights(Instance instance, float const* weights, size_t count, size_t offset);
- void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
- FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count);
- void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
- size_t offset, size_t count);
- MorphTargetBuffer* getMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept;
+ void setMorphTargetBufferOffsetAt(Instance instance, uint8_t level, size_t primitiveIndex,
+ size_t offset);
+ MorphTargetBuffer* getMorphTargetBuffer(Instance instance) const noexcept;
size_t getMorphTargetCount(Instance instance) const noexcept;
void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept;
@@ -189,7 +181,7 @@ class FRenderableManager : public RenderableManager {
struct MorphingBindingInfo {
backend::Handle handle;
uint32_t count;
- MorphTargets const* targets; // Pointer to Slice at a renderable.
+ FMorphTargetBuffer const* morphTargetBuffer;
};
inline MorphingBindingInfo getMorphingBufferInfo(Instance instance) const noexcept;
@@ -218,16 +210,12 @@ class FRenderableManager : public RenderableManager {
AttributeBitset getEnabledAttributesAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept;
inline utils::Slice const& getRenderPrimitives(Instance instance, uint8_t level) const noexcept;
inline utils::Slice& getRenderPrimitives(Instance instance, uint8_t level) noexcept;
- inline utils::Slice const& getMorphTargets(Instance instance, uint8_t level) const noexcept;
- inline utils::Slice& getMorphTargets(Instance instance, uint8_t level) noexcept;
private:
void destroyComponent(Instance ci) noexcept;
static void destroyComponentPrimitives(
HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
utils::Slice& primitives) noexcept;
- static void destroyComponentMorphTargets(FEngine& engine,
- utils::Slice& morphTargets) noexcept;
struct Bones {
backend::Handle handle;
@@ -254,7 +242,7 @@ class FRenderableManager : public RenderableManager {
VISIBILITY, // user data
PRIMITIVES, // user data
BONES, // filament data, UBO storing a pointer to the bones information
- MORPH_TARGETS
+ MORPHTARGET_BUFFER // morphtarget buffer for the component
};
using Base = utils::SingleInstanceComponentManager<
@@ -266,7 +254,7 @@ class FRenderableManager : public RenderableManager {
Visibility, // VISIBILITY
utils::Slice, // PRIMITIVES
Bones, // BONES
- utils::Slice // MORPH_TARGETS
+ FMorphTargetBuffer* // MORPHTARGET_BUFFER
>;
struct Sim : public Base {
@@ -289,7 +277,7 @@ class FRenderableManager : public RenderableManager {
Field visibility;
Field primitives;
Field bones;
- Field morphTargets;
+ Field morphTargetBuffer;
};
};
@@ -461,8 +449,8 @@ inline uint32_t FRenderableManager::getBoneCount(Instance instance) const noexce
FRenderableManager::MorphingBindingInfo
FRenderableManager::getMorphingBufferInfo(Instance instance) const noexcept {
MorphWeights const& morphWeights = mManager[instance].morphWeights;
- utils::Slice const& morphTargets = getMorphTargets(instance, 0);
- return { morphWeights.handle, morphWeights.count, morphTargets.data() };
+ FMorphTargetBuffer const* const buffer = mManager[instance].morphTargetBuffer;
+ return { morphWeights.handle, morphWeights.count, buffer };
}
FRenderableManager::InstancesInfo
@@ -480,16 +468,6 @@ utils::Slice& FRenderableManager::getRenderPrimitives(
return mManager[instance].primitives;
}
-utils::Slice const& FRenderableManager::getMorphTargets(
- Instance instance, UTILS_UNUSED uint8_t level) const noexcept {
- return mManager[instance].morphTargets;
-}
-
-utils::Slice& FRenderableManager::getMorphTargets(
- Instance instance, UTILS_UNUSED uint8_t level) noexcept {
- return mManager[instance].morphTargets;
-}
-
} // namespace filament
#endif // TNT_FILAMENT_COMPONENTS_RENDERABLEMANAGER_H
diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp
index 2547b40103d..72b5b599c3f 100644
--- a/filament/src/details/Engine.cpp
+++ b/filament/src/details/Engine.cpp
@@ -102,6 +102,7 @@ Engine* FEngine::create(Engine::Builder const& builder) {
DriverConfig const driverConfig{
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
.textureUseAfterFreePoolSize = instance->getConfig().textureUseAfterFreePoolSize,
+ .metalUploadBufferSizeBytes = instance->getConfig().metalUploadBufferSizeBytes,
.disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile,
.disableHandleUseAfterFreeCheck = instance->getConfig().disableHandleUseAfterFreeCheck,
.forceGLES2Context = instance->getConfig().forceGLES2Context,
@@ -678,6 +679,7 @@ int FEngine::loop() {
DriverConfig const driverConfig {
.handleArenaSize = getRequestedDriverHandleArenaSize(),
.textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize,
+ .metalUploadBufferSizeBytes = mConfig.metalUploadBufferSizeBytes,
.disableParallelShaderCompile = mConfig.disableParallelShaderCompile,
.disableHandleUseAfterFreeCheck = mConfig.disableHandleUseAfterFreeCheck,
.forceGLES2Context = mConfig.forceGLES2Context,
diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp
index b8fa8d2999b..6d73f96027a 100644
--- a/filament/src/details/Material.cpp
+++ b/filament/src/details/Material.cpp
@@ -119,6 +119,7 @@ struct Material::BuilderDetails {
const void* mPayload = nullptr;
size_t mSize = 0;
bool mDefaultMaterial = false;
+ int32_t mShBandsCount = 3;
std::unordered_map<
utils::CString,
std::variant,
@@ -143,6 +144,11 @@ Material::Builder& Material::Builder::package(const void* payload, size_t size)
return *this;
}
+Material::Builder& Material::Builder::sphericalHarmonicsBandCount(size_t shBandCount) noexcept {
+ mImpl->mShBandsCount = math::clamp(shBandCount, size_t(1), size_t(3));
+ return *this;
+}
+
template
Material::Builder& Material::Builder::constant(const char* name, size_t nameLength, T value) {
FILAMENT_CHECK_PRECONDITION(name != nullptr) << "name cannot be null";
@@ -897,6 +903,8 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Material::Builde
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT,
(int)engine.getConfig().stereoscopicEyeCount });
+ mSpecializationConstants.push_back({
+ +ReservedSpecializationConstants::CONFIG_SH_BANDS_COUNT, builder->mShBandsCount });
if (UTILS_UNLIKELY(parser->getShaderLanguage() == ShaderLanguage::ESSL1)) {
// The actual value of this spec-constant is set in the OpenGLDriver backend.
mSpecializationConstants.push_back({
diff --git a/filament/src/details/MorphTargetBuffer.h b/filament/src/details/MorphTargetBuffer.h
index 50e8fc92b92..b1d45b2e2eb 100644
--- a/filament/src/details/MorphTargetBuffer.h
+++ b/filament/src/details/MorphTargetBuffer.h
@@ -29,7 +29,8 @@
#include
-#include
+#include
+#include
namespace filament {
@@ -59,16 +60,21 @@ class FMorphTargetBuffer : public MorphTargetBuffer {
inline size_t getVertexCount() const noexcept { return mVertexCount; }
inline size_t getCount() const noexcept { return mCount; }
-private:
- friend class FView;
- friend class RenderPass;
+ backend::TextureHandle getPositionsHandle() const noexcept {
+ return mPbHandle;
+ }
+
+ backend::TextureHandle getTangentsHandle() const noexcept {
+ return mTbHandle;
+ }
+
+ inline backend::Handle getHwHandle() const noexcept { return mSbHandle; }
+private:
void updateDataAt(backend::DriverApi& driver, backend::Handle handle,
backend::PixelDataFormat format, backend::PixelDataType type, const char* out,
size_t elementSize, size_t targetIndex, size_t count, size_t offset);
- inline backend::Handle getHwHandle() const noexcept { return mSbHandle; }
-
backend::Handle mSbHandle;
backend::Handle mPbHandle;
backend::Handle mTbHandle;
diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp
index 15094a07591..b27486542f6 100644
--- a/filament/src/details/Renderer.cpp
+++ b/filament/src/details/Renderer.cpp
@@ -232,6 +232,10 @@ void FRenderer::setPresentationTime(int64_t monotonic_clock_ns) {
driver.setPresentationTime(monotonic_clock_ns);
}
+void FRenderer::setVsyncTime(uint64_t steadyClockTimeNano) noexcept {
+ mVsyncSteadyClockTimeNano = steadyClockTimeNano;
+}
+
bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) {
assert_invariant(swapChain);
@@ -252,6 +256,11 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
}
#endif
+ if (!vsyncSteadyClockTimeNano) {
+ vsyncSteadyClockTimeNano = mVsyncSteadyClockTimeNano;
+ mVsyncSteadyClockTimeNano = 0;
+ }
+
// get the timestamp as soon as possible
using namespace std::chrono;
const steady_clock::time_point now{ steady_clock::now() };
diff --git a/filament/src/details/Renderer.h b/filament/src/details/Renderer.h
index 056d5599770..263f8998863 100644
--- a/filament/src/details/Renderer.h
+++ b/filament/src/details/Renderer.h
@@ -29,9 +29,6 @@
#include "backend/DriverApiForward.h"
-#include
-#include
-
#include
#include
@@ -41,8 +38,18 @@
#include
#include
+#include