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 + #include +#include +#include +#include +#include + +#include +#include + namespace filament { namespace backend { @@ -78,6 +85,8 @@ public: void setPresentationTime(int64_t monotonic_clock_ns); + void setVsyncTime(uint64_t steadyClockTimeNano) noexcept; + // start a frame bool beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano); @@ -187,6 +196,7 @@ private: backend::TargetBufferFlags mClearFlags{}; tsl::robin_set mPreviousRenderTargets; std::function mBeginFrameInternal; + uint64_t mVsyncSteadyClockTimeNano = 0; }; FILAMENT_DOWNCAST(Renderer) diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 45fce39806..fc42e6ee98 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.53.0" + spec.version = "1.53.1" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.53.0/filament-v1.53.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.53.1/filament-v1.53.1-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/libs/camutils/include/camutils/Manipulator.h b/libs/camutils/include/camutils/Manipulator.h index 2cc8a4da03..5ea49eaf5a 100644 --- a/libs/camutils/include/camutils/Manipulator.h +++ b/libs/camutils/include/camutils/Manipulator.h @@ -109,6 +109,7 @@ public: vec4 groundPlane; RayCallback raycastCallback; void* raycastUserdata; + bool panning = true; }; struct Builder { @@ -143,6 +144,8 @@ public: Builder& groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d); //! Plane equation used as a raycast fallback Builder& raycastCallback(RayCallback cb, void* userdata); //! Raycast function for accurate grab-and-pan + Builder& panning(bool enabled); //! Sets whether panning is enabled + /** * Creates a new camera manipulator, either ORBIT, MAP, or FREE_FLIGHT. * diff --git a/libs/camutils/src/Manipulator.cpp b/libs/camutils/src/Manipulator.cpp index d44528cd50..897d75c6d1 100644 --- a/libs/camutils/src/Manipulator.cpp +++ b/libs/camutils/src/Manipulator.cpp @@ -144,6 +144,14 @@ Manipulator::Builder& Manipulator::Builder::raycastCallback(RayCal return *this; } + + +template typename +Manipulator::Builder& Manipulator::Builder::panning(bool enabled) { + details.panning = enabled; + return *this; +} + template Manipulator* Manipulator::Builder::build(Mode mode) { switch (mode) { diff --git a/libs/camutils/src/OrbitManipulator.h b/libs/camutils/src/OrbitManipulator.h index 54e325cc3b..f89b7ada03 100644 --- a/libs/camutils/src/OrbitManipulator.h +++ b/libs/camutils/src/OrbitManipulator.h @@ -69,7 +69,7 @@ public: } void grabBegin(int x, int y, bool strafe) override { - mGrabState = strafe ? PANNING : ORBITING; + mGrabState = strafe && Base::mProps.panning ? PANNING : ORBITING; mGrabPivot = mPivot; mGrabEye = Base::mEye; mGrabTarget = Base::mTarget; diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index c15c6c457c..cad7057fb1 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -203,7 +203,7 @@ enum class ReflectionMode : uint8_t { // can't really use std::underlying_type::type because the driver takes a uint32_t using AttributeBitset = utils::bitset32; -static constexpr size_t MATERIAL_PROPERTIES_COUNT = 27; +static constexpr size_t MATERIAL_PROPERTIES_COUNT = 29; enum class Property : uint8_t { BASE_COLOR, //!< float4, all shading models ROUGHNESS, //!< float, lit shading models only @@ -232,6 +232,8 @@ enum class Property : uint8_t { IOR, //!< float, material's index of refraction MICRO_THICKNESS, //!< float, thickness of the thin layer BENT_NORMAL, //!< float3, all shading models only, except unlit + SPECULAR_FACTOR, //!< float, lit shading models only, except subsurface and cloth + SPECULAR_COLOR_FACTOR, //!< float3, lit shading models only, except subsurface and cloth // when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT }; diff --git a/libs/filabridge/include/private/filament/EngineEnums.h b/libs/filabridge/include/private/filament/EngineEnums.h index 4a8c042e75..9491f2e118 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -70,6 +70,7 @@ enum class ReservedSpecializationConstants : uint8_t { CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP = 6, CONFIG_DEBUG_FROXEL_VISUALIZATION = 7, CONFIG_STEREO_EYE_COUNT = 8, // don't change (hardcoded in ShaderCompilerService.cpp) + CONFIG_SH_BANDS_COUNT = 9 }; enum class PushConstantIds : uint8_t { diff --git a/libs/filamat/src/Enums.cpp b/libs/filamat/src/Enums.cpp index 583d376f95..bb163425da 100644 --- a/libs/filamat/src/Enums.cpp +++ b/libs/filamat/src/Enums.cpp @@ -21,33 +21,35 @@ namespace filamat { std::unordered_map Enums::mStringToProperty = { - { "baseColor", Property::BASE_COLOR }, - { "roughness", Property::ROUGHNESS }, - { "metallic", Property::METALLIC }, - { "reflectance", Property::REFLECTANCE }, - { "ambientOcclusion", Property::AMBIENT_OCCLUSION }, - { "clearCoat", Property::CLEAR_COAT }, - { "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS }, - { "clearCoatNormal", Property::CLEAR_COAT_NORMAL }, - { "anisotropy", Property::ANISOTROPY }, - { "anisotropyDirection", Property::ANISOTROPY_DIRECTION }, - { "thickness", Property::THICKNESS }, - { "subsurfacePower", Property::SUBSURFACE_POWER }, - { "subsurfaceColor", Property::SUBSURFACE_COLOR }, - { "sheenColor", Property::SHEEN_COLOR }, - { "sheenRoughness", Property::SHEEN_ROUGHNESS }, - { "glossiness", Property::GLOSSINESS }, - { "specularColor", Property::SPECULAR_COLOR }, - { "emissive", Property::EMISSIVE }, - { "normal", Property::NORMAL }, - { "postLightingColor", Property::POST_LIGHTING_COLOR }, + { "baseColor", Property::BASE_COLOR }, + { "roughness", Property::ROUGHNESS }, + { "metallic", Property::METALLIC }, + { "reflectance", Property::REFLECTANCE }, + { "ambientOcclusion", Property::AMBIENT_OCCLUSION }, + { "clearCoat", Property::CLEAR_COAT }, + { "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS }, + { "clearCoatNormal", Property::CLEAR_COAT_NORMAL }, + { "anisotropy", Property::ANISOTROPY }, + { "anisotropyDirection", Property::ANISOTROPY_DIRECTION }, + { "thickness", Property::THICKNESS }, + { "subsurfacePower", Property::SUBSURFACE_POWER }, + { "subsurfaceColor", Property::SUBSURFACE_COLOR }, + { "sheenColor", Property::SHEEN_COLOR }, + { "sheenRoughness", Property::SHEEN_ROUGHNESS }, + { "glossiness", Property::GLOSSINESS }, + { "specularColor", Property::SPECULAR_COLOR }, + { "emissive", Property::EMISSIVE }, + { "normal", Property::NORMAL }, + { "postLightingColor", Property::POST_LIGHTING_COLOR }, { "postLightingMixFactor", Property::POST_LIGHTING_MIX_FACTOR }, - { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM }, - { "absorption", Property::ABSORPTION }, - { "transmission", Property::TRANSMISSION }, - { "ior", Property::IOR }, - { "microThickness", Property::MICRO_THICKNESS }, - { "bentNormal", Property::BENT_NORMAL }, + { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM }, + { "absorption", Property::ABSORPTION }, + { "transmission", Property::TRANSMISSION }, + { "ior", Property::IOR }, + { "microThickness", Property::MICRO_THICKNESS }, + { "bentNormal", Property::BENT_NORMAL }, + { "specularFactor", Property::SPECULAR_FACTOR }, + { "specularColorFactor", Property::SPECULAR_COLOR_FACTOR } }; template <> diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index 75d2b940d8..1c74cbd779 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -332,6 +332,9 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade generateSpecializationConstant(out, "CONFIG_STEREO_EYE_COUNT", +ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT, material.stereoscopicEyeCount); + generateSpecializationConstant(out, "CONFIG_SH_BANDS_COUNT", + +ReservedSpecializationConstants::CONFIG_SH_BANDS_COUNT, 3); + // CONFIG_MAX_STEREOSCOPIC_EYES is used to size arrays and on Adreno GPUs + vulkan, this has to // be explicitly, statically defined (as in #define). Otherwise (using const int for // example), we'd run into a GPU crash. @@ -1189,6 +1192,8 @@ char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) n case Property::IOR: return "IOR"; case Property::MICRO_THICKNESS: return "MICRO_THICKNESS"; case Property::BENT_NORMAL: return "BENT_NORMAL"; + case Property::SPECULAR_FACTOR: return "SPECULAR_FACTOR"; + case Property::SPECULAR_COLOR_FACTOR: return "SPECULAR_COLOR_FACTOR"; } } diff --git a/libs/gltfio/CMakeLists.txt b/libs/gltfio/CMakeLists.txt index c066cc2297..c7c7d6a63c 100644 --- a/libs/gltfio/CMakeLists.txt +++ b/libs/gltfio/CMakeLists.txt @@ -147,6 +147,9 @@ build_ubershader(volume_opaque volume lit opaque) build_ubershader(volume_fade volume lit fade) build_ubershader(volume_masked volume lit masked) build_ubershader(sheen sheen _ _) +build_ubershader(specular_opaque specular lit opaque) +build_ubershader(specular_fade specular lit fade) +build_ubershader(specular_masked specular lit masked) add_custom_target(uberz_file DEPENDS ${UBERZ_OUTPUT_PATH}) set_target_properties(uberz_file PROPERTIES FOLDER Libs) diff --git a/libs/gltfio/include/gltfio/MaterialProvider.h b/libs/gltfio/include/gltfio/MaterialProvider.h index f62d667b35..042f303bb6 100644 --- a/libs/gltfio/include/gltfio/MaterialProvider.h +++ b/libs/gltfio/include/gltfio/MaterialProvider.h @@ -93,10 +93,17 @@ struct alignas(4) MaterialKey { bool hasSheen : 1; bool hasIOR : 1; bool hasVolume : 1; - uint8_t padding : 5; + bool hasSpecular : 1; + bool hasSpecularTexture : 1; + bool hasSpecularColorTexture : 1; + bool padding : 2; + // -- 32 bit boundary -- + uint8_t specularTextureUV; + uint8_t specularColorTextureUV; + uint16_t padding2; }; -static_assert(sizeof(MaterialKey) == 16, "MaterialKey has unexpected size."); +static_assert(sizeof(MaterialKey) == 20, "MaterialKey has unexpected size."); UTILS_WARNING_POP diff --git a/libs/gltfio/materials/base.spec.in b/libs/gltfio/materials/base.spec.in index 6e6e9375f5..f80236f5a1 100644 --- a/libs/gltfio/materials/base.spec.in +++ b/libs/gltfio/materials/base.spec.in @@ -30,3 +30,8 @@ TransmissionTexture = unsupported # Volume Features Volume = unsupported VolumeThicknessTexture = unsupported + +# Specular Features +Specular = unsupported +SpecularTexture = unsupported +SpecularColorTexture = unsupported diff --git a/libs/gltfio/materials/sheen.mat.in b/libs/gltfio/materials/sheen.mat.in index b8682ca34f..6ac496c96d 100644 --- a/libs/gltfio/materials/sheen.mat.in +++ b/libs/gltfio/materials/sheen.mat.in @@ -55,7 +55,16 @@ material { { type : mat3, name : sheenRoughnessUvMatrix, precision: high }, // Reflectance - { type : float, name : reflectance } + { type : float, name : reflectance }, + + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : mat3, name : specularColorUvMatrix, precision: high } ${CUSTOM_PARAMS} ], @@ -136,6 +145,15 @@ fragment { material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; } + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + ${CUSTOM_FRAGMENT} } } diff --git a/libs/gltfio/materials/sheen.spec.in b/libs/gltfio/materials/sheen.spec.in index dcd41bf25c..6c72f0b3f9 100644 --- a/libs/gltfio/materials/sheen.spec.in +++ b/libs/gltfio/materials/sheen.spec.in @@ -17,3 +17,8 @@ TextureTransforms=optional Sheen=optional SheenColorTexture=optional SheenRoughnessTexture=optional + +# KHR_material_specular Features +Specular=optional +SpecularTexture=optional +SpecularColorTexture=unsupported diff --git a/libs/gltfio/materials/specular.mat.in b/libs/gltfio/materials/specular.mat.in new file mode 100644 index 0000000000..acb4f2dbd7 --- /dev/null +++ b/libs/gltfio/materials/specular.mat.in @@ -0,0 +1,168 @@ +material { + name : specular_ubershader, + requires : [ uv0, uv1, color ], + shadingModel : lit, + blending : ${BLENDING}, + doubleSided : ${DOUBLESIDED}, + transparency : ${TRANSPARENCY}, + flipUV : false, + specularAmbientOcclusion : simple, + specularAntiAliasing : true, + clearCoatIorChange : false, + reflections: screenspace, + parameters : [ + + // Base Color + { type : int, name : baseColorIndex }, + { type : float4, name : baseColorFactor }, + { type : sampler2d, name : baseColorMap }, + { type : mat3, name : baseColorUvMatrix, precision: high }, + + // Metallic-Roughness Map + { type : int, name : metallicRoughnessIndex }, + { type : float, name : metallicFactor }, + { type : float, name : roughnessFactor }, + { type : sampler2d, name : metallicRoughnessMap }, + { type : mat3, name : metallicRoughnessUvMatrix, precision: high }, + + // Normal Map + { type : int, name : normalIndex }, + { type : float, name : normalScale }, + { type : sampler2d, name : normalMap }, + { type : mat3, name : normalUvMatrix, precision: high }, + + // Ambient Occlusion + { type : int, name : aoIndex }, + { type : float, name : aoStrength }, + { type : sampler2d, name : occlusionMap }, + { type : mat3, name : occlusionUvMatrix, precision: high }, + + // Emissive Map + { type : int, name : emissiveIndex }, + { type : float3, name : emissiveFactor }, + { type : float, name : emissiveStrength }, + { type : sampler2d, name : emissiveMap }, + { type : mat3, name : emissiveUvMatrix, precision: high }, + + // Clear coat, we drop texture support for clear coat due to 8 texture limits. + { type : float, name : clearCoatFactor }, + { type : float, name : clearCoatRoughnessFactor }, + { type : int, name : clearCoatIndex }, + { type : sampler2d, name : clearCoatMap }, + { type : mat3, name : clearCoatUvMatrix, precision: high }, + { type : int, name : clearCoatRoughnessIndex }, + { type : mat3, name : clearCoatRoughnessUvMatrix, precision: high }, + { type : int, name : clearCoatNormalIndex }, + { type : mat3, name : clearCoatNormalUvMatrix, precision: high }, + { type : float, name : clearCoatNormalScale }, + + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : sampler2d, name : specularColorMap }, + { type : mat3, name : specularColorUvMatrix, precision: high }, + + // IOR + { type : float, name : ior } + + ${CUSTOM_PARAMS} + ], +} + +vertex { +void materialVertex(inout MaterialVertexInputs material) { + ${CUSTOM_VERTEX} +} +} + +fragment { +void material(inout MaterialInputs material) { + highp float2 uvs[2]; + uvs[0] = getUV0(); + uvs[1] = getUV1(); + + if (materialParams.normalIndex > -1) { + highp float2 uv = uvs[materialParams.normalIndex]; + uv = (vec3(uv, 1.0) * materialParams.normalUvMatrix).xy; + material.normal = texture(materialParams_normalMap, uv).xyz * 2.0 - 1.0; + material.normal.xy *= materialParams.normalScale; + } + + prepareMaterial(material); + material.baseColor = materialParams.baseColorFactor; + + if (materialParams.baseColorIndex > -1) { + highp float2 uv = uvs[materialParams.baseColorIndex]; + uv = (vec3(uv, 1.0) * materialParams.baseColorUvMatrix).xy; + material.baseColor *= texture(materialParams_baseColorMap, uv); + } + +#if defined(BLEND_MODE_TRANSPARENT) + material.baseColor.rgb *= material.baseColor.a; +#endif + + material.baseColor *= getColor(); + + material.roughness = materialParams.roughnessFactor; + material.metallic = materialParams.metallicFactor; + material.ior = materialParams.ior; + + material.emissive = vec4(materialParams.emissiveStrength * + materialParams.emissiveFactor.rgb, 0.0); + + // KHR_materials_clearcoat forbids clear coat from + // being applied in the specular/glossiness model + material.clearCoat = materialParams.clearCoatFactor; + material.clearCoatRoughness = materialParams.clearCoatRoughnessFactor; + + if (materialParams.clearCoatIndex > -1) { + highp float2 uv = uvs[materialParams.clearCoatIndex]; + uv = (vec3(uv, 1.0) * materialParams.clearCoatUvMatrix).xy; + material.clearCoat *= texture(materialParams_clearCoatMap, uv).r; + } + + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + + if (materialParams.specularColorIndex > -1) { + highp float2 uv = uvs[materialParams.specularColorIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularColorUvMatrix).xy; + vec4 sc = texture(materialParams_specularColorMap, uv); + material.specularColorFactor.r *= sc.r; + material.specularColorFactor.g *= sc.g; + material.specularColorFactor.b *= sc.b; + } + + if (materialParams.metallicRoughnessIndex > -1) { + highp float2 uv = uvs[materialParams.metallicRoughnessIndex]; + uv = (vec3(uv, 1.0) * materialParams.metallicRoughnessUvMatrix).xy; + vec4 mr = texture(materialParams_metallicRoughnessMap, uv); + material.roughness *= mr.g; + material.metallic *= mr.b; + } + + if (materialParams.aoIndex > -1) { + highp float2 uv = uvs[materialParams.aoIndex]; + uv = (vec3(uv, 1.0) * materialParams.occlusionUvMatrix).xy; + float occlusion = texture(materialParams_occlusionMap, uv).r; + material.ambientOcclusion = 1.0 + materialParams.aoStrength * (occlusion - 1.0); + } + if (materialParams.emissiveIndex > -1) { + highp float2 uv = uvs[materialParams.emissiveIndex]; + uv = (vec3(uv, 1.0) * materialParams.emissiveUvMatrix).xy; + material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; + } + + ${CUSTOM_FRAGMENT} +} +} diff --git a/libs/gltfio/materials/specular.spec.in b/libs/gltfio/materials/specular.spec.in new file mode 100644 index 0000000000..1b381ee8b5 --- /dev/null +++ b/libs/gltfio/materials/specular.spec.in @@ -0,0 +1,35 @@ +ShadingModel=lit + +# Required Aspects +BlendingMode = ${BLENDING} + +VertexColors = optional +BaseColorTexture = optional +NormalTexture = optional +OcclusionTexture = optional +EmissiveTexture = optional +MetallicRoughnessTexture = optional +TextureTransforms = optional + +# KHR_materials_specular Features +Specular = optional +SpecularTexture = optional +SpecularColorTexture = optional + +# ClearCoat Features +ClearCoat = optional +ClearCoatTexture = optional + +# Sheen Features +Sheen = unsupported +SheenColorTexture = unsupported +SheenRoughnessTexture = unsupported + +# Transmission and IOR Features +Transmission = unsupported +Ior = optional +TransmissionTexture = unsupported + +# Volume Features +Volume = unsupported +VolumeThicknessTexture = unsupported diff --git a/libs/gltfio/materials/transmission.mat.in b/libs/gltfio/materials/transmission.mat.in index 631ec89763..608b8576f2 100644 --- a/libs/gltfio/materials/transmission.mat.in +++ b/libs/gltfio/materials/transmission.mat.in @@ -52,7 +52,18 @@ material { { type : mat3, name : transmissionUvMatrix, precision: high }, // IOR - { type : float, name : ior } + { type : float, name : ior }, + + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : sampler2d, name : specularColorMap }, + { type : mat3, name : specularColorUvMatrix, precision: high } + ${CUSTOM_PARAMS} ], @@ -126,6 +137,24 @@ fragment { material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; } + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + + if (materialParams.specularColorIndex > -1) { + highp float2 uv = uvs[materialParams.specularColorIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularColorUvMatrix).xy; + vec4 sc = texture(materialParams_specularColorMap, uv); + material.specularColorFactor.r *= sc.r; + material.specularColorFactor.g *= sc.g; + material.specularColorFactor.b *= sc.b; + } + ${CUSTOM_FRAGMENT} } } diff --git a/libs/gltfio/materials/transmission.spec.in b/libs/gltfio/materials/transmission.spec.in index ac76ae5072..ff0c364813 100644 --- a/libs/gltfio/materials/transmission.spec.in +++ b/libs/gltfio/materials/transmission.spec.in @@ -15,6 +15,11 @@ EmissiveTexture=optional MetallicRoughnessTexture=optional TextureTransforms=optional +# KHR_materials_specular Features +Specular=optional +SpecularTexture=optional +SpecularColorTexture=optional + Transmission=optional Ior=optional TransmissionTexture=optional diff --git a/libs/gltfio/materials/volume.mat.in b/libs/gltfio/materials/volume.mat.in index 18998f5877..d7470bb257 100644 --- a/libs/gltfio/materials/volume.mat.in +++ b/libs/gltfio/materials/volume.mat.in @@ -58,6 +58,15 @@ material { { type : sampler2d, name : volumeThicknessMap }, { type : mat3, name : volumeThicknessUvMatrix, precision: high }, + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : mat3, name : specularColorUvMatrix, precision: high }, + // IOR { type : float, name : ior } @@ -142,6 +151,15 @@ fragment { material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; } + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + ${CUSTOM_FRAGMENT} } } diff --git a/libs/gltfio/materials/volume.spec.in b/libs/gltfio/materials/volume.spec.in index fe290ab7f1..5f65676765 100644 --- a/libs/gltfio/materials/volume.spec.in +++ b/libs/gltfio/materials/volume.spec.in @@ -26,3 +26,8 @@ TransmissionTexture=optional # Volume Features Volume=optional VolumeThicknessTexture=optional + +# KHR_materials_specular Features +Specular=optional +SpecularTexture=optional +SpecularColorTexture=unsupported diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 31bbc169f7..19aa85e3a6 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -1222,6 +1222,7 @@ MaterialKey FAssetLoader::getMaterialKey(const cgltf_data* srcAsset, auto trConfig = inputMat->transmission; auto shConfig = inputMat->sheen; auto vlConfig = inputMat->volume; + auto spConfig = inputMat->specular; *baseColorTexture = mrConfig.base_color_texture; *metallicRoughnessTexture = mrConfig.metallic_roughness_texture; @@ -1238,7 +1239,9 @@ MaterialKey FAssetLoader::getMaterialKey(const cgltf_data* srcAsset, ccConfig.clearcoat_normal_texture.has_transform || shConfig.sheen_color_texture.has_transform || shConfig.sheen_roughness_texture.has_transform || - trConfig.transmission_texture.has_transform; + trConfig.transmission_texture.has_transform || + spConfig.specular_color_texture.has_transform || + spConfig.specular_texture.has_transform; MaterialKey matkey { .doubleSided = !!inputMat->double_sided, @@ -1273,6 +1276,11 @@ MaterialKey FAssetLoader::getMaterialKey(const cgltf_data* srcAsset, .hasSheen = !!inputMat->has_sheen, .hasIOR = !!inputMat->has_ior, .hasVolume = !!inputMat->has_volume, + .hasSpecular = !!inputMat->has_specular, + .hasSpecularTexture = spConfig.specular_texture.texture != nullptr, + .hasSpecularColorTexture = spConfig.specular_color_texture.texture != nullptr, + .specularTextureUV = (uint8_t) spConfig.specular_texture.texcoord, + .specularColorTextureUV = (uint8_t) spConfig.specular_color_texture.texcoord, }; if (inputMat->has_pbr_specular_glossiness) { @@ -1361,6 +1369,7 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp auto trConfig = inputMat->transmission; auto shConfig = inputMat->sheen; auto vlConfig = inputMat->volume; + auto spConfig = inputMat->specular; // Check the material blending mode, not the cgltf blending mode, because the provider // might have selected an alternative blend mode (e.g. to support transmission). @@ -1560,6 +1569,30 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp inputMat->emissive_strength.emissive_strength : 1.0f); } + if (matkey.hasSpecular) { + const float* s = spConfig.specular_color_factor; + mi->setParameter("specularColorFactor", float3{s[0], s[1], s[2]}); + mi->setParameter("specularStrength", spConfig.specular_factor); + + if (matkey.hasSpecularColorTexture) { + fAsset->addTextureBinding(mi, "specularColorMap", spConfig.specular_color_texture.texture, sRGB); + if (matkey.hasTextureTransforms) { + const cgltf_texture_transform uvt = spConfig.specular_color_texture.transform; + auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale); + mi->setParameter("specularColorUvMatrix", uvmat); + } + } + if (matkey.hasSpecularTexture) { + bool sameTexture = spConfig.specular_color_texture.texture == spConfig.specular_texture.texture; + fAsset->addTextureBinding(mi, "specularMap", spConfig.specular_texture.texture, sameTexture ? sRGB : LINEAR); + if (matkey.hasTextureTransforms) { + const cgltf_texture_transform uvt = spConfig.specular_texture.transform; + auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale); + mi->setParameter("specularUvMatrix", uvmat); + } + } + } + *cacheEntry = { mi, *uvmap }; return mi; } diff --git a/libs/gltfio/src/JitShaderProvider.cpp b/libs/gltfio/src/JitShaderProvider.cpp index b42acda520..44651176a5 100644 --- a/libs/gltfio/src/JitShaderProvider.cpp +++ b/libs/gltfio/src/JitShaderProvider.cpp @@ -308,6 +308,35 @@ std::string shaderFromKey(const MaterialKey& config) { material.ior = materialParams.ior; )SHADER"; } + + if (config.hasSpecular) { + shader += R"SHADER( + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + )SHADER"; + + if (config.hasSpecularTexture) { + shader += "highp float2 specularUV = ${specular};\n"; + if (config.hasTextureTransforms) { + shader += "specularUV = (vec3(specularUV, 1.0) * " + "materialParams.specularUvMatrix).xy;\n"; + } + shader += R"SHADER( + material.specularFactor *= texture(materialParams_specularMap, specularUV).a; + )SHADER"; + } + + if (config.hasSpecularColorTexture) { + shader += "highp float2 specularColorUV = ${specularColor};\n"; + if (config.hasTextureTransforms) { + shader += "specularColorUV = (vec3(specularColorUV, 1.0) * " + "materialParams.specularColorUvMatrix).xy;\n"; + } + shader += R"SHADER( + material.specularColorFactor *= texture(materialParams_specularColorMap, specularColorUV).rgb; + )SHADER"; + } + } } shader += "}\n"; @@ -463,6 +492,26 @@ Material* createMaterial(Engine* engine, const MaterialKey& config, const UvMap& } } + // SPECULAR + if (config.hasSpecular) { + builder.parameter("specularStrength", MaterialBuilder::UniformType::FLOAT); + builder.parameter("specularColorFactor", MaterialBuilder::UniformType::FLOAT3); + if (config.hasSpecularTexture) { + builder.parameter("specularMap", MaterialBuilder::SamplerType::SAMPLER_2D); + if (config.hasTextureTransforms) { + builder.parameter("specularUvMatrix", MaterialBuilder::UniformType::MAT3, + MaterialBuilder::ParameterPrecision::HIGH); + } + } + if (config.hasSpecularColorTexture) { + builder.parameter("specularColorMap", MaterialBuilder::SamplerType::SAMPLER_2D); + if (config.hasTextureTransforms) { + builder.parameter("specularColorUvMatrix", MaterialBuilder::UniformType::MAT3, + MaterialBuilder::ParameterPrecision::HIGH); + } + } + } + // BLENDING switch (config.alphaMode) { case AlphaMode::OPAQUE: diff --git a/libs/gltfio/src/MaterialProvider.cpp b/libs/gltfio/src/MaterialProvider.cpp index b42bd9a930..7e7fa6d947 100644 --- a/libs/gltfio/src/MaterialProvider.cpp +++ b/libs/gltfio/src/MaterialProvider.cpp @@ -143,6 +143,21 @@ void constrainMaterial(MaterialKey* key, UvMap* uvmap) { retval[key->volumeThicknessUV] = (UvSet) index++; } } + if (key->hasSpecularTexture && retval[key->specularTextureUV] == UNUSED) { + if (index > MAX_INDEX) { + key->hasSpecularTexture = false; + } else { + retval[key->specularTextureUV] = (UvSet) index++; + } + } + if (key->hasSpecularColorTexture && retval[key->specularColorTextureUV] == UNUSED) { + if (index > MAX_INDEX) { + key->hasSpecularColorTexture = false; + } else { + retval[key->specularColorTextureUV] = (UvSet) index++; + } + } + // NOTE: KHR_materials_clearcoat does not provide separate UVs, we'll assume UV0 *uvmap = retval; } @@ -167,6 +182,8 @@ void processShaderString(std::string* shader, const UvMap& uvmap, const Material const auto& sheenColorUV = uvstrings[uvmap[config.sheenColorUV]]; const auto& sheenRoughnessUV = uvstrings[uvmap[config.sheenRoughnessUV]]; const auto& volumeThicknessUV = uvstrings[uvmap[config.volumeThicknessUV]]; + const auto& specularUV = uvstrings[uvmap[config.specularTextureUV]]; + const auto& specularColorUV = uvstrings[uvmap[config.specularColorTextureUV]]; replaceAll("${normal}", normalUV); replaceAll("${color}", baseColorUV); @@ -180,6 +197,8 @@ void processShaderString(std::string* shader, const UvMap& uvmap, const Material replaceAll("${sheenColor}", sheenColorUV); replaceAll("${sheenRoughness}", sheenRoughnessUV); replaceAll("${volumeThickness}", volumeThicknessUV); + replaceAll("${specular}", specularUV); + replaceAll("${specularColor}", specularColorUV); } } // namespace filament::gltfio diff --git a/libs/gltfio/src/UbershaderProvider.cpp b/libs/gltfio/src/UbershaderProvider.cpp index 7e184f648d..64451aa363 100644 --- a/libs/gltfio/src/UbershaderProvider.cpp +++ b/libs/gltfio/src/UbershaderProvider.cpp @@ -63,6 +63,42 @@ static void prepareConfig(MaterialKey* config, const char* label) { config->hasSheen = false; config->hasIOR = false; } + + if (config->useSpecularGlossiness && config->hasSpecular) { + slog.w << "SpecularGlossiness and specular are not supported together in ubershader mode," + " removing specularGlossiness (" << label << ")." << io::endl; + + config->useSpecularGlossiness = false; + } + + if (config->unlit && config->hasSpecular) { + slog.w << "Unlit and specular are not supported together in ubershader mode," + " removing unlit (" << label << ")." << io::endl; + + config->unlit = false; + } + + // Also due to sampler overload, clearcoat with specular, we can only support with specular map. + if ((config->hasClearCoatNormalTexture || config->hasClearCoatRoughnessTexture) && config->hasSpecular) { + slog.w << "Specular can only work with clearcoat that does not use clear coat normal or clear coat roughness map," + " removing clear coat normal and roughness (" << label << ")." << io::endl; + config->hasClearCoatNormalTexture = false; + config->hasClearCoatRoughnessTexture = false; + } + + // Also due to sampler overload, sheen with specular, we can only support without specular color texture. + if (config->hasSpecularColorTexture && config->hasSheen) { + slog.w << "Sheen can only work with specular that does not use specular color map," + " removing specular color (" << label << ")." << io::endl; + config->hasSpecularColorTexture = false; + } + + // Also due to sampler overload, volume with specular, we can only support without specular color texture. + if (config->hasSpecularColorTexture && config->hasVolume) { + slog.w << "Volume can only work with specular that does not use specular color map," + "removing specular color (" << label << ")." << io::endl; + config->hasSpecularColorTexture = false; + } } class UbershaderProvider : public MaterialProvider { @@ -155,6 +191,9 @@ Material* UbershaderProvider::getMaterial(const MaterialKey& config) const { requirements.features["SheenColorTexture"] = config.hasSheenColorTexture; requirements.features["SheenRoughnessTexture"] = config.hasSheenRoughnessTexture; requirements.features["VolumeThicknessTexture"] = config.hasVolumeThicknessTexture; + requirements.features["Specular"] = config.hasSpecular; + requirements.features["SpecularTexture"] = config.hasSpecularTexture; + requirements.features["SpecularColorTexture"] = config.hasSpecularColorTexture; if (Material* mat = mMaterials.getMaterial(requirements); mat != nullptr) { return mat; @@ -250,6 +289,14 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config mi->setParameter("transmissionIndex", getUvIndex(config->transmissionUV, config->hasTransmissionTexture)); } + if (config->hasSpecular) { + mi->setParameter("specularUvMatrix", identity); + mi->setParameter("specularIndex", + getUvIndex(config->specularTextureUV, config->hasSpecularTexture)); + mi->setParameter("specularColorUvMatrix", identity); + mi->setParameter("specularColorIndex", + getUvIndex(config->specularColorTextureUV, config->hasSpecularColorTexture)); + } } TextureSampler sampler; @@ -300,6 +347,14 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config mi->setParameter("reflectance", 0.5f); } + if (needsTexture("SpecularTexture")) { + mi->setParameter("specularMap", mDummyTexture, sampler); + } + + if (needsTexture("SpecularColorTexture")) { + mi->setParameter("specularColorMap", mDummyTexture, sampler); + } + return mi; } diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 6b83316f1b..340391ec26 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -547,10 +547,11 @@ static bool checkAsset(const utils::Path& filename) { return false; } - // Parse the glTF file and create Filament entities. + // Try parsing the glTF file to check the validity of the file format. cgltf_options options{}; - cgltf_data* sourceAsset; + cgltf_data* sourceAsset = nullptr; cgltf_result result = cgltf_parse(&options, buffer.data(), contentSize, &sourceAsset); + cgltf_free(sourceAsset); if (result != cgltf_result_success) { slog.e << "Unable to parse glTF file." << io::endl; return false; diff --git a/shaders/src/brdf.fs b/shaders/src/brdf.fs index f4204cf44d..2f4d152255 100644 --- a/shaders/src/brdf.fs +++ b/shaders/src/brdf.fs @@ -181,6 +181,16 @@ vec3 fresnel(const vec3 f0, float LoH) { #endif } +vec3 fresnel(const vec3 f0, const float f90, float LoH) { +#if BRDF_SPECULAR_F == SPECULAR_F_SCHLICK +#if FILAMENT_QUALITY == FILAMENT_QUALITY_LOW + return F_Schlick(f0, LoH); // f90 = 1.0 +#else + return F_Schlick(f0, f90, LoH); +#endif +#endif +} + float distributionAnisotropic(float at, float ab, float ToH, float BoH, float NoH) { #if BRDF_ANISOTROPIC_D == SPECULAR_D_GGX_ANISOTROPIC return D_GGX_Anisotropic(at, ab, ToH, BoH, NoH); diff --git a/shaders/src/common_lighting.fs b/shaders/src/common_lighting.fs index 2f822460de..43df890838 100644 --- a/shaders/src/common_lighting.fs +++ b/shaders/src/common_lighting.fs @@ -18,6 +18,11 @@ struct PixelParams { float perceptualRoughness; float perceptualRoughnessUnclamped; vec3 f0; +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR) + float f90; + float specular; + vec3 specularColor; +#endif float roughness; vec3 dfg; vec3 energyCompensation; diff --git a/shaders/src/light_indirect.fs b/shaders/src/light_indirect.fs index 5084b10b64..5dc3cad35d 100644 --- a/shaders/src/light_indirect.fs +++ b/shaders/src/light_indirect.fs @@ -2,9 +2,6 @@ // Image based lighting configuration //------------------------------------------------------------------------------ -// Number of spherical harmonics bands (1, 2 or 3) -#define SPHERICAL_HARMONICS_BANDS 3 - // IBL integration algorithm #define IBL_INTEGRATION_PREFILTERED_CUBEMAP 0 #define IBL_INTEGRATION_IMPORTANCE_SAMPLING 1 @@ -44,21 +41,25 @@ vec3 prefilteredDFG(float perceptualRoughness, float NoV) { //------------------------------------------------------------------------------ vec3 Irradiance_SphericalHarmonics(const vec3 n) { - return max( - frameUniforms.iblSH[0] -#if SPHERICAL_HARMONICS_BANDS >= 2 - + frameUniforms.iblSH[1] * (n.y) - + frameUniforms.iblSH[2] * (n.z) - + frameUniforms.iblSH[3] * (n.x) -#endif -#if SPHERICAL_HARMONICS_BANDS >= 3 - + frameUniforms.iblSH[4] * (n.y * n.x) - + frameUniforms.iblSH[5] * (n.y * n.z) - + frameUniforms.iblSH[6] * (3.0 * n.z * n.z - 1.0) - + frameUniforms.iblSH[7] * (n.z * n.x) - + frameUniforms.iblSH[8] * (n.x * n.x - n.y * n.y) -#endif - , 0.0); + vec3 sphericalHarmonics = frameUniforms.iblSH[0]; + + if (CONFIG_SH_BANDS_COUNT >= 2) { + sphericalHarmonics += + frameUniforms.iblSH[1] * (n.y) + + frameUniforms.iblSH[2] * (n.z) + + frameUniforms.iblSH[3] * (n.x); + } + + if (CONFIG_SH_BANDS_COUNT >= 3) { + sphericalHarmonics += + frameUniforms.iblSH[4] * (n.y * n.x) + + frameUniforms.iblSH[5] * (n.y * n.z) + + frameUniforms.iblSH[6] * (3.0 * n.z * n.z - 1.0) + + frameUniforms.iblSH[7] * (n.z * n.x) + + frameUniforms.iblSH[8] * (n.x * n.x - n.y * n.y); + } + + return max(sphericalHarmonics, 0.0); } vec3 Irradiance_RoughnessOne(const vec3 n) { @@ -135,9 +136,13 @@ vec3 getSpecularDominantDirection(const vec3 n, const vec3 r, float roughness) { vec3 specularDFG(const PixelParams pixel) { #if defined(SHADING_MODEL_CLOTH) return pixel.f0 * pixel.dfg.z; +#else +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR) + return mix(pixel.dfg.xxx, pixel.dfg.yyy, pixel.f0) * pixel.specular; #else return mix(pixel.dfg.xxx, pixel.dfg.yyy, pixel.f0); #endif +#endif } vec3 getReflectedVector(const PixelParams pixel, const vec3 n) { @@ -662,7 +667,6 @@ void evaluateIBL(const MaterialInputs material, const PixelParams pixel, inout v // Combine all terms // Note: iblLuminance is already premultiplied by the exposure - color.rgb += Fr + Fd; #if defined(MATERIAL_HAS_REFRACTION) color.rgb += Ft; diff --git a/shaders/src/material_inputs.fs b/shaders/src/material_inputs.fs index 53d4ade1ec..32923dc90a 100644 --- a/shaders/src/material_inputs.fs +++ b/shaders/src/material_inputs.fs @@ -89,6 +89,15 @@ struct MaterialInputs { #endif #endif #endif + +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + float specularFactor; +#endif + +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + vec3 specularColorFactor; +#endif + }; void initMaterial(out MaterialInputs material) { @@ -177,6 +186,15 @@ void initMaterial(out MaterialInputs material) { #endif #endif #endif + +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + material.specularFactor = 1.0; +#endif + +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + material.specularColorFactor = vec3(1.0); +#endif + } #if defined(MATERIAL_HAS_CUSTOM_SURFACE_SHADING) diff --git a/shaders/src/shading_lit.fs b/shaders/src/shading_lit.fs index bf80ea57cd..103005845c 100644 --- a/shaders/src/shading_lit.fs +++ b/shaders/src/shading_lit.fs @@ -75,7 +75,21 @@ void getCommonPixelParams(const MaterialInputs material, inout PixelParams pixel // Assumes an interface from air to an IOR of 1.5 for dielectrics float reflectance = computeDielectricF0(material.reflectance); #endif +#if !defined(MATERIAL_HAS_SPECULAR_FACTOR) && !defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) pixel.f0 = computeF0(baseColor, material.metallic, reflectance); +#else + vec3 dielectricSpecularF0 = vec3(0.0); + float dielectricSpecularF90 = 0.0; +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + dielectricSpecularF0 = min(reflectance * material.specularColorFactor, vec3(1.0)); +#endif +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + dielectricSpecularF0 *= material.specularFactor; + dielectricSpecularF90 = material.specularFactor; +#endif + pixel.f0 = baseColor.rgb * material.metallic + dielectricSpecularF0 * (1.0 - material.metallic); + pixel.f90 = material.metallic + dielectricSpecularF90 * (1.0 - material.metallic); +#endif #else pixel.diffuseColor = baseColor.rgb; pixel.f0 = material.sheenColor; @@ -123,6 +137,16 @@ void getCommonPixelParams(const MaterialInputs material, inout PixelParams pixel #endif } +void getSpecularPixelParams(const MaterialInputs material, inout PixelParams pixel) { +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + pixel.specular = material.specularFactor; +#endif + +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + pixel.specularColor = material.specularColorFactor; +#endif +} + void getSheenPixelParams(const MaterialInputs material, inout PixelParams pixel) { #if defined(MATERIAL_HAS_SHEEN_COLOR) && !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) pixel.sheenColor = material.sheenColor; @@ -232,6 +256,7 @@ void getEnergyCompensationPixelParams(inout PixelParams pixel) { * testing fails. */ void getPixelParams(const MaterialInputs material, out PixelParams pixel) { + getSpecularPixelParams(material, pixel); getCommonPixelParams(material, pixel); getSheenPixelParams(material, pixel); getClearCoatPixelParams(material, pixel); diff --git a/shaders/src/shading_model_standard.fs b/shaders/src/shading_model_standard.fs index 025295ba27..ca71d1de0f 100644 --- a/shaders/src/shading_model_standard.fs +++ b/shaders/src/shading_model_standard.fs @@ -63,8 +63,11 @@ vec3 isotropicLobe(const PixelParams pixel, const Light light, const vec3 h, float D = distribution(pixel.roughness, NoH, h); float V = visibility(pixel.roughness, NoV, NoL); +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR) + vec3 F = fresnel(pixel.f0, pixel.f90, LoH); +#else vec3 F = fresnel(pixel.f0, LoH); - +#endif return (D * V) * F; } @@ -144,7 +147,6 @@ vec3 surfaceShading(const PixelParams pixel, const Light light, float occlusion) color += clearCoat; #endif #endif - return (color * light.colorIntensity.rgb) * (light.colorIntensity.w * light.attenuation * NoL * occlusion); } diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 735305a563..e1bc5c3755 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -388,6 +388,8 @@ class_("Engine") return Engine::create(); }, allow_raw_pointers()) + .class_function("getSteadyClockTimeNano", &Engine::getSteadyClockTimeNano) + .function("unprotected", &Engine::unprotected) .function("enableAccurateTranslations", &Engine::enableAccurateTranslations) @@ -623,6 +625,8 @@ class_("Renderer") }), allow_raw_pointers()) .function("_setClearOptions", &Renderer::setClearOptions, allow_raw_pointers()) .function("getClearOptions", &Renderer::getClearOptions) + .function("setPresentationTime", &Renderer::setPresentationTime) + .function("setVsyncTime", &Renderer::setVsyncTime) .function("beginFrame", EMBIND_LAMBDA(bool, (Renderer* self, SwapChain* swapChain), { return self->beginFrame(swapChain); }), allow_raw_pointers()) diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 56b18b1cf4..7cea353927 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.53.0", + "version": "1.53.1", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",