diff --git a/README.md b/README.md
index b04cd6fa55..b368b37c95 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
- implementation 'com.google.android.filament:filament-android:1.53.0'
+ implementation 'com.google.android.filament:filament-android:1.53.1'
}
```
@@ -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.0'
+pod 'Filament', '~> 1.53.1'
```
### 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 2a4ed29bcd..3591acf61e 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 4b3b33e530..f571422255 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 7ef3b0b5be..e923d210b0 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/Renderer.cpp b/android/filament-android/src/main/cpp/Renderer.cpp
index c48b616f56..1dc252d356 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 5a28d1eae8..6c4c5119e3 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 class Engine {
/**
* 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 class Engine {
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 80b143ee80..cd71bbae73 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 class Material {
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 class Material {
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 class Material {
*/
@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 @@ public class Material {
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/Renderer.java b/android/filament-android/src/main/java/com/google/android/filament/Renderer.java
index ab70e9ed38..4fb447db2c 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 class Renderer {
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 @@ public class Renderer {
}
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/filament-utils-android/src/main/cpp/Manipulator.cpp b/android/filament-utils-android/src/main/cpp/Manipulator.cpp
index 22d471def2..84ac111e75 100644
--- a/android/filament-utils-android/src/main/cpp/Manipulator.cpp
+++ b/android/filament-utils-android/src/main/cpp/Manipulator.cpp
@@ -125,6 +125,11 @@ extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBu
builder->groundPlane(a, b, c, d);
}
+extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderPanning(JNIEnv*, jclass, jlong nativeBuilder, jboolean enabled) {
+ Builder* builder = (Builder*) nativeBuilder;
+ builder->panning(enabled);
+}
+
extern "C" JNIEXPORT long Java_com_google_android_filament_utils_Manipulator_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder, jint mode) {
Builder* builder = (Builder*) nativeBuilder;
return (jlong) builder->build((Mode) mode);
diff --git a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/Manipulator.java b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/Manipulator.java
index 2c0ab85a14..548a6e6f8c 100644
--- a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/Manipulator.java
+++ b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/Manipulator.java
@@ -274,6 +274,17 @@ public class Manipulator {
return this;
}
+ /**
+ * Sets whether panning is enabled in the manipulator.
+ *
+ * @return this Builder object for chaining calls
+ */
+ @NonNull
+ public Builder panning(Boolean enabled) {
+ nBuilderPanning(mNativeBuilder, enabled);
+ return this;
+ }
+
/**
* Creates and returns the Manipulator object.
*
@@ -483,6 +494,7 @@ public class Manipulator {
private static native void nBuilderFlightPanSpeed(long nativeBuilder, float x, float y);
private static native void nBuilderFlightMoveDamping(long nativeBuilder, float damping);
private static native void nBuilderGroundPlane(long nativeBuilder, float a, float b, float c, float d);
+ private static native void nBuilderPanning(long nativeBuilder, Boolean enabled);
private static native long nBuilderBuild(long nativeBuilder, int mode);
private static native void nDestroyManipulator(long nativeManip);
diff --git a/android/gradle.properties b/android/gradle.properties
index 3e27e61499..cca037f8f7 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
-VERSION_NAME=1.53.0
+VERSION_NAME=1.53.1
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 4f73c4375c..b8befb4cee 100644
--- a/filament/backend/include/backend/Platform.h
+++ b/filament/backend/include/backend/Platform.h
@@ -75,6 +75,8 @@ public:
*/
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 afc6dc924e..d083fe3da9 100644
--- a/filament/backend/include/private/backend/HandleAllocator.h
+++ b/filament/backend/include/private/backend/HandleAllocator.h
@@ -181,6 +181,18 @@ public:
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 081ca6c3fd..bb3de6d27c 100644
--- a/filament/backend/src/metal/MetalBuffer.h
+++ b/filament/backend/src/metal/MetalBuffer.h
@@ -204,6 +204,15 @@ public:
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 83fe25d598..6070cf47fb 100644
--- a/filament/backend/src/metal/MetalBuffer.mm
+++ b/filament/backend/src/metal/MetalBuffer.mm
@@ -27,7 +27,16 @@ MetalPlatform* TrackedMetalBuffer::platform = nullptr;
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 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
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::bindBuffers(id cmdBuffer, idacquireBuffer(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 03688ab3c4..770527e092 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 c9a80e8352..a601a85052 100644
--- a/filament/backend/src/metal/MetalBufferPool.mm
+++ b/filament/backend/src/metal/MetalBufferPool.mm
@@ -116,5 +116,38 @@ void MetalBufferPool::reset() noexcept {
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 };
+
+ 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 8a14d9a36c..0f920466e9 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 58852a6714..92b1b22f73 100644
--- a/filament/backend/src/metal/MetalDriver.mm
+++ b/filament/backend/src/metal/MetalDriver.mm
@@ -171,6 +171,8 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
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 @@ MetalDriver::~MetalDriver() noexcept {
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/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h
index 4d6f864639..4fba4724ea 100644
--- a/filament/backend/src/metal/MetalHandles.h
+++ b/filament/backend/src/metal/MetalHandles.h
@@ -109,7 +109,7 @@ private:
NSUInteger headlessWidth = 0;
NSUInteger headlessHeight = 0;
CAMetalLayer* layer = nullptr;
- std::mutex layerDrawableMutex;
+ std::shared_ptr layerDrawableMutex;
MetalExternalImage externalImage;
SwapChainType type;
diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm
index 3d56956387..c54b978217 100644
--- a/filament/backend/src/metal/MetalHandles.mm
+++ b/filament/backend/src/metal/MetalHandles.mm
@@ -73,6 +73,7 @@ MetalSwapChain::MetalSwapChain(MetalContext& context, CAMetalLayer* nativeWindow
: context(context),
depthStencilFormat(decideDepthStencilFormat(flags)),
layer(nativeWindow),
+ layerDrawableMutex(std::make_shared()),
externalImage(context),
type(SwapChainType::CAMETALLAYER) {
@@ -179,7 +180,7 @@ id MetalSwapChain::acquireDrawable() {
// calling -nextDrawable, or when releasing the last known reference
// to any CAMetalDrawable returned from a previous -nextDrawable.
{
- std::lock_guard lock(layerDrawableMutex);
+ std::lock_guard lock(*layerDrawableMutex);
drawable = [layer nextDrawable];
}
@@ -188,8 +189,10 @@ id MetalSwapChain::acquireDrawable() {
}
void MetalSwapChain::releaseDrawable() {
- std::lock_guard lock(layerDrawableMutex);
- drawable = nil;
+ if (drawable) {
+ std::lock_guard lock(*layerDrawableMutex);
+ drawable = nil;
+ }
}
id MetalSwapChain::acquireDepthTexture() {
@@ -265,7 +268,7 @@ public:
PresentDrawableData& operator=(const PresentDrawableData&) = delete;
static PresentDrawableData* create(id drawable,
- std::mutex* drawableMutex, MetalDriver* driver) {
+ std::shared_ptr drawableMutex, MetalDriver* driver) {
assert_invariant(drawableMutex);
assert_invariant(driver);
return new PresentDrawableData(drawable, drawableMutex, driver);
@@ -287,22 +290,22 @@ public:
}
private:
- PresentDrawableData(id drawable, std::mutex* drawableMutex,
+ PresentDrawableData(id drawable, std::shared_ptr drawableMutex,
MetalDriver* driver)
: mDrawable(drawable), mDrawableMutex(drawableMutex), mDriver(driver) {}
static void cleanupAndDestroy(PresentDrawableData *that) {
- {
+ if (that->mDrawable) {
std::lock_guard lock(*(that->mDrawableMutex));
that->mDrawable = nil;
}
- that->mDrawableMutex = nullptr;
+ that->mDrawableMutex.reset();
that->mDriver = nullptr;
delete that;
}
id mDrawable;
- std::mutex* mDrawableMutex = nullptr;
+ std::shared_ptr mDrawableMutex;
MetalDriver* mDriver = nullptr;
};
@@ -320,7 +323,7 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
struct Callback {
Callback(std::shared_ptr callback, id drawable,
- std::mutex* drawableMutex, MetalDriver* driver)
+ std::shared_ptr drawableMutex, MetalDriver* driver)
: f(callback), data(PresentDrawableData::create(drawable, drawableMutex, driver)) {}
std::shared_ptr f;
// PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later.
@@ -337,7 +340,7 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
// This callback pointer will be captured by the block. Even if the scheduled handler is never
// called, the unique_ptr will still ensure we don't leak memory.
__block auto callback = std::make_unique(
- frameScheduled.callback, drawable, &layerDrawableMutex, context.driver);
+ frameScheduled.callback, drawable, layerDrawableMutex, context.driver);
backend::CallbackHandler* handler = frameScheduled.handler;
MetalDriver* driver = context.driver;
diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp
index 4f88c9717f..5b011ee625 100644
--- a/filament/backend/src/opengl/OpenGLDriver.cpp
+++ b/filament/backend/src/opengl/OpenGLDriver.cpp
@@ -2336,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;
@@ -2394,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 d5e1b797b6..5345d474bf 100644
--- a/filament/backend/src/opengl/OpenGLDriver.h
+++ b/filament/backend/src/opengl/OpenGLDriver.h
@@ -126,7 +126,7 @@ public:
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 @@ private:
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 6c5eab9a44..7a85e3c63c 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/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp
index dd64a1b794..6935868b14 100644
--- a/filament/backend/src/opengl/ShaderCompilerService.cpp
+++ b/filament/backend/src/opengl/ShaderCompilerService.cpp
@@ -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,21 +708,92 @@ 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 {
+// split shader source code in three:
+// - the version line
+// - extensions
+// - everything else
+std::array ShaderCompilerService::splitShaderSource(std::string_view source) noexcept {
auto start = source.find("#version");
assert_invariant(start != std::string_view::npos);
+ auto version_eol = source.find('\n', start) + 1;
+ assert_invariant(version_eol != std::string_view::npos);
+
auto pos = source.rfind("\n#extension");
if (pos == std::string_view::npos) {
- pos = start;
+ pos = version_eol;
} else {
++pos;
}
@@ -722,9 +801,10 @@ std::array ShaderCompilerService::splitShaderSource(std::st
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(start, version_eol - start);
+ std::string_view const prolog = source.substr(version_eol, eol - version_eol);
+ std::string_view const body = source.substr(eol, source.length() - eol);
+ return { version, prolog, body };
}
/*
diff --git a/filament/backend/src/opengl/ShaderCompilerService.h b/filament/backend/src/opengl/ShaderCompilerService.h
index 27255ea000..ef4059fc0b 100644
--- a/filament/backend/src/opengl/ShaderCompilerService.h
+++ b/filament/backend/src/opengl/ShaderCompilerService.h
@@ -146,7 +146,7 @@ private:
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 2aed82736f..6c3d87c29c 100644
--- a/filament/include/filament/Engine.h
+++ b/filament/include/filament/Engine.h
@@ -298,6 +298,24 @@ public:
*/
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 @@ public:
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 c230dac8fe..c4cc62ecd7 100644
--- a/filament/include/filament/IndirectLight.h
+++ b/filament/include/filament/IndirectLight.h
@@ -158,6 +158,8 @@ public:
*
* @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 b4b9bbed3e..11cbaef30d 100644
--- a/filament/include/filament/Material.h
+++ b/filament/include/filament/Material.h
@@ -140,6 +140,18 @@ public:
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/Renderer.h b/filament/include/filament/Renderer.h
index fdc291b1fd..83b6281604 100644
--- a/filament/include/filament/Renderer.h
+++ b/filament/include/filament/Renderer.h
@@ -227,6 +227,17 @@ public:
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 ad110dbc92..477a0871c8 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/Renderer.cpp b/filament/src/Renderer.cpp
index 85d58f9ec1..00837319b2 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/details/Engine.cpp b/filament/src/details/Engine.cpp
index 2547b40103..72b5b599c3 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 b8fa8d2999..6d73f96027 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/Renderer.cpp b/filament/src/details/Renderer.cpp
index 15094a0759..b27486542f 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 056d559977..263f899886 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