diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 77a7acc5f46..4a1a9c7fa7e 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,5 +7,3 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - -- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()` diff --git a/README.md b/README.md index a1dc91cdc6b..f2e6856085d 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.51.1' + implementation 'com.google.android.filament:filament-android:1.51.2' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.51.1' +pod 'Filament', '~> 1.51.2' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ba94019fceb..d60e6d45843 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,10 @@ 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.51.2 + +- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()` + ## v1.51.1 diff --git a/android/filament-android/src/main/cpp/RenderableManager.cpp b/android/filament-android/src/main/cpp/RenderableManager.cpp index 2713dffa0b4..4e586179baa 100644 --- a/android/filament-android/src/main/cpp/RenderableManager.cpp +++ b/android/filament-android/src/main/cpp/RenderableManager.cpp @@ -104,6 +104,14 @@ Java_com_google_android_filament_RenderableManager_nBuilderGeometry__JIIJJIIII(J (size_t) count); } +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_RenderableManager_nBuilderGeometryType(JNIEnv*, jclass, + jlong nativeBuilder, int type) { + RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder; + builder->geometryType((RenderableManager::Builder::GeometryType)type); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_RenderableManager_nBuilderMaterial(JNIEnv*, jclass, 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 68f10986548..97301df9806 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 @@ -243,9 +243,11 @@ public Builder featureLevel(FeatureLevel featureLevel) { /** * Sets the initial paused state of the rendering thread. * + *

Warning: This is an experimental API. See {@link Engine#setPaused(boolean)} for + * caveats. + * * @param paused Whether to start the rendering thread paused. * @return A reference to this Builder for chaining calls. - * @warning Experimental. */ public Builder paused(boolean paused) { nSetBuilderPaused(mNativeBuilder, paused); @@ -1209,7 +1211,16 @@ public void flush() { /** * Pause or resume the rendering thread. - * @warning Experimental. + * + *

Warning: This is an experimental API. In particular, note the following caveats. + * + *

*/ public void setPaused(boolean paused) { nSetPaused(getNativeObject(), paused); diff --git a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java index a3c8f1ff1c2..f7a6319d7a8 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java @@ -175,6 +175,32 @@ public Builder geometry(@IntRange(from = 0) int index, @NonNull PrimitiveType ty return this; } + /** + * Type of geometry for a Renderable + */ + public enum GeometryType { + /** dynamic gemoetry has no restriction */ + DYNAMIC, + /** bounds and world space transform are immutable */ + STATIC_BOUNDS, + /** skinning/morphing not allowed and Vertex/IndexBuffer immutables */ + STATIC + } + + /** + * Specify whether this renderable has static bounds. In this context his means that + * the renderable's bounding box cannot change and that the renderable's transform is + * assumed immutable. Changing the renderable's transform via the TransformManager + * can lead to corrupted graphics. Note that skinning and morphing are not forbidden. + * Disabled by default. + * @param enable whether this renderable has static bounds. false by default. + */ + @NonNull + public Builder geometryType(GeometryType type) { + nBuilderGeometryType(mNativeBuilder, type.ordinal()); + return this; + } + /** * Binds a material instance to the specified primitive. * @@ -964,6 +990,7 @@ public long getNativeObject() { private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer); private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int count); private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int minIndex, int maxIndex, int count); + private static native void nBuilderGeometryType(long nativeBuilder, int type); private static native void nBuilderMaterial(long nativeBuilder, int index, long nativeMaterialInstance); private static native void nBuilderBlendOrder(long nativeBuilder, int index, int blendOrder); private static native void nBuilderGlobalBlendOrderEnabled(long nativeBuilder, int index, boolean enabled); diff --git a/android/gradle.properties b/android/gradle.properties index 685dd260796..c677c8262ab 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.51.1 +VERSION_NAME=1.51.2 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/build.sh b/build.sh index d9a778c7ba2..56562cc1c6c 100755 --- a/build.sh +++ b/build.sh @@ -516,7 +516,7 @@ function build_android { if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then for sample in ${ANDROID_SAMPLES}; do echo "Installing out/${sample}-debug.apk" - cp samples/${sample}/build/outputs/apk/debug/${sample}-debug-unsigned.apk \ + cp samples/${sample}/build/outputs/apk/debug/${sample}-debug.apk \ ../out/${sample}-debug.apk done fi diff --git a/filament/backend/include/backend/Program.h b/filament/backend/include/backend/Program.h index 97deb6c586b..fe1c4a9b6e8 100644 --- a/filament/backend/include/backend/Program.h +++ b/filament/backend/include/backend/Program.h @@ -116,6 +116,8 @@ class Program { Program& cacheId(uint64_t cacheId) noexcept; + Program& multiview(bool multiview) noexcept; + ShaderSource const& getShadersSource() const noexcept { return mShadersSource; } ShaderSource& getShadersSource() noexcept { return mShadersSource; } @@ -143,6 +145,8 @@ class Program { uint64_t getCacheId() const noexcept { return mCacheId; } + bool isMultiview() const noexcept { return mMultiview; } + CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; } private: @@ -158,6 +162,11 @@ class Program { utils::FixedCapacityVector> mAttributes; std::array mBindingUniformInfo; CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH; + // Indicates the current engine was initialized with multiview stereo, and the variant for this + // program contains STE flag. This will be referred later for the OpenGL shader compiler to + // determine whether shader code replacement for the num_views should be performed. + // This variable could be promoted as a more generic variable later if other similar needs occur. + bool mMultiview = false; }; } // namespace filament::backend diff --git a/filament/backend/src/CommandBufferQueue.cpp b/filament/backend/src/CommandBufferQueue.cpp index 9de07ed0e27..b721ce0c50f 100644 --- a/filament/backend/src/CommandBufferQueue.cpp +++ b/filament/backend/src/CommandBufferQueue.cpp @@ -58,6 +58,7 @@ void CommandBufferQueue::requestExit() { } void CommandBufferQueue::setPaused(bool paused) { + std::lock_guard const lock(mLock); if (paused) { mPaused = true; } else { @@ -125,6 +126,9 @@ void CommandBufferQueue::flush() noexcept { #endif SYSTRACE_NAME("waiting: CircularBuffer::flush()"); + ASSERT_POSTCONDITION(!mPaused, + "CommandStream is full, but since the rendering thread is paused, " + "the buffer cannot flush and we will deadlock. Instead, abort."); mCondition.wait(lock, [this, requiredSize]() -> bool { // TODO: on macOS, we need to call pumpEvents from time to time return mFreeSpace >= requiredSize; diff --git a/filament/backend/src/Program.cpp b/filament/backend/src/Program.cpp index 1bae22d97d0..dc92e8c2a26 100644 --- a/filament/backend/src/Program.cpp +++ b/filament/backend/src/Program.cpp @@ -91,6 +91,11 @@ Program& Program::cacheId(uint64_t cacheId) noexcept { return *this; } +Program& Program::multiview(bool multiview) noexcept { + mMultiview = multiview; + return *this; +} + io::ostream& operator<<(io::ostream& out, const Program& builder) { out << "Program{"; builder.mLogger(out); diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index e4668e33306..196d1bd2e66 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -2472,7 +2472,9 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level, size_t const stride = p.stride ? p.stride : width; size_t const bpp = PBD::computeDataSize(p.format, p.type, 1, 1, 1); size_t const bpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment); - void const* const buffer = static_cast(p.buffer) + p.left * bpp + bpr * p.top; + size_t const bpl = bpr * height; // TODO: PBD should have a "layer stride" + void const* const buffer = static_cast(p.buffer) + + bpp* p.left + bpr * p.top + bpl * 0; // TODO: PBD should have a p.depth switch (t->target) { case SamplerType::SAMPLER_EXTERNAL: diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp index 3c1f57f8329..5b9397ddd4b 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.cpp +++ b/filament/backend/src/opengl/ShaderCompilerService.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -264,6 +265,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( compileShaders(gl, std::move(program.getShadersSource()), program.getSpecializationConstants(), + program.isMultiview(), shaders, token->shaderSourceCode); @@ -299,6 +301,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( compileShaders(gl, std::move(program.getShadersSource()), program.getSpecializationConstants(), + program.isMultiview(), token->gl.shaders, token->shaderSourceCode); @@ -501,6 +504,7 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept { void ShaderCompilerService::compileShaders(OpenGLContext& context, Program::ShaderSource shadersSource, utils::FixedCapacityVector const& specializationConstants, + bool multiview, std::array& outShaders, UTILS_UNUSED_IN_RELEASE std::array& outShaderSourceCode) noexcept { @@ -514,8 +518,16 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context, }; std::string specializationConstantString; + int32_t numViews = 2; for (auto const& sc : specializationConstants) { appendSpecConstantString(specializationConstantString, sc); + if (sc.id == 8) { + // This constant must match + // ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT + // which we can't use here because it's defined in EngineEnums.h. + // (we're breaking layering here, but it's for the good cause). + numViews = std::get(sc.value); + } } if (!specializationConstantString.empty()) { specializationConstantString += '\n'; @@ -544,17 +556,23 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context, if (UTILS_LIKELY(!shadersSource[i].empty())) { Program::ShaderBlob& shader = shadersSource[i]; + char* shader_src = reinterpret_cast(shader.data()); + size_t shader_len = shader.size(); // remove GOOGLE_cpp_style_line_directive - std::string_view const source = process_GOOGLE_cpp_style_line_directive(context, - reinterpret_cast(shader.data()), shader.size()); + process_GOOGLE_cpp_style_line_directive(context, shader_src, shader_len); + + // replace the value of layout(num_views = X) for multiview extension + if (multiview && stage == ShaderStage::VERTEX) { + process_OVR_multiview2(context, numViews, shader_src, shader_len); + } // add support for ARB_shading_language_packing if needed auto const packingFunctions = process_ARB_shading_language_packing(context); // split shader source, so we can insert the specialization constants and the packing // functions - auto const [prolog, body] = splitShaderSource(source); + auto const [prolog, body] = splitShaderSource({ shader_src, shader_len }); const std::array sources = { prolog.data(), @@ -577,7 +595,7 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context, #ifndef NDEBUG // for debugging we return the original shader source (without the modifications we // made here), otherwise the line numbers wouldn't match. - outShaderSourceCode[i] = { source.data(), source.length() }; + outShaderSourceCode[i] = { shader_src, shader_len }; #endif outShaders[i] = shaderId; @@ -586,15 +604,59 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context, } // If usages of the Google-style line directive are present, remove them, as some -// drivers don't allow the quotation marks. This happens in-place. -std::string_view ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, +// drivers don't allow the quotation marks. This source modification happens in-place. +void ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, char* source, size_t len) noexcept { if (!context.ext.GOOGLE_cpp_style_line_directive) { if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) { removeGoogleLineDirectives(source, len); // length is unaffected } } - return { source, len }; +} + +// Look up the `source` to replace the number of eyes for multiview with the given number. This is +// necessary for OpenGL because OpenGL relies on the number specified in shader files to determine +// the number of views, which is assumed as a single digit, for multiview. +// This source modification happens in-place. +void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context, + int32_t eyeCount, char* source, size_t len) noexcept { + // We don't use regular expression in favor of performance. + if (context.ext.OVR_multiview2) { + const std::string_view shader{ source, len }; + const std::string_view layout = "layout"; + const std::string_view num_views = "num_views"; + size_t found = 0; + while (true) { + found = shader.find(layout, found); + if (found == std::string_view::npos) { + break; + } + found = shader.find_first_not_of(' ', found + layout.size()); + if (found == std::string_view::npos || shader[found] != '(') { + continue; + } + found = shader.find_first_not_of(' ', found + 1); + if (found == std::string_view::npos) { + continue; + } + if (shader.compare(found, num_views.size(), num_views) != 0) { + continue; + } + found = shader.find_first_not_of(' ', found + num_views.size()); + if (found == std::string_view::npos || shader[found] != '=') { + continue; + } + found = shader.find_first_not_of(' ', found + 1); + if (found == std::string_view::npos) { + continue; + } + // We assume the value should be one-digit number. + assert_invariant(eyeCount < 10); + assert_invariant(!::isdigit(source[found + 1])); + source[found] = '0' + eyeCount; + break; + } + } } // Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and diff --git a/filament/backend/src/opengl/ShaderCompilerService.h b/filament/backend/src/opengl/ShaderCompilerService.h index edd89aafdbb..27255ea0002 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.h +++ b/filament/backend/src/opengl/ShaderCompilerService.h @@ -134,10 +134,14 @@ class ShaderCompilerService { OpenGLContext& context, Program::ShaderSource shadersSource, utils::FixedCapacityVector const& specializationConstants, + bool multiview, std::array& outShaders, std::array& outShaderSourceCode) noexcept; - static std::string_view process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, + static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, + char* source, size_t len) noexcept; + + static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount, char* source, size_t len) noexcept; static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept; diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index 5856e391641..b4974950ef5 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -43,9 +43,12 @@ // Enables Android systrace #define FVK_DEBUG_SYSTRACE 0x00000001 -// Group markers are used to denote collection of GPU commands. It is typically at the granualarity -// of a renderpass. -#define FVK_DEBUG_GROUP_MARKERS 0x00000002 +// Group markers are used to denote collections of GPU commands. It is typically at the +// granualarity of a renderpass. You can enable this along with FVK_DEBUG_DEBUG_UTILS to take +// advantage of vkCmdBegin/EndDebugUtilsLabelEXT. You can also just enable this with +// FVK_DEBUG_PRINT_GROUP_MARKERS to print the current marker to stdout. +#define FVK_DEBUG_GROUP_MARKERS 0x00000002 + #define FVK_DEBUG_TEXTURE 0x00000004 #define FVK_DEBUG_LAYOUT_TRANSITION 0x00000008 #define FVK_DEBUG_COMMAND_BUFFER 0x00000010 @@ -63,9 +66,11 @@ // Enable the debug utils extension if it is available. #define FVK_DEBUG_DEBUG_UTILS 0x00008000 +// Use this to debug potential Handle/Resource leakage. It will print out reference counts for all +// the currently active resources. #define FVK_DEBUG_RESOURCE_LEAK 0x00010000 -// Usefaul default combinations +// Useful default combinations #define FVK_DEBUG_EVERYTHING 0xFFFFFFFF #define FVK_DEBUG_PERFORMANCE \ FVK_DEBUG_SYSTRACE diff --git a/filament/backend/src/vulkan/VulkanFboCache.cpp b/filament/backend/src/vulkan/VulkanFboCache.cpp index 009ca0273ca..f4b222b1545 100644 --- a/filament/backend/src/vulkan/VulkanFboCache.cpp +++ b/filament/backend/src/vulkan/VulkanFboCache.cpp @@ -239,7 +239,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { .format = config.colorFormat[i], .samples = (VkSampleCountFlagBits) config.samples, .loadOp = clear ? kClear : (discard ? kDontCare : kKeep), - .storeOp = config.samples == 1 ? kEnableStore : kDisableStore, + .storeOp = kEnableStore, .stencilLoadOp = kDontCare, .stencilStoreOp = kDisableStore, .initialLayout = ((!discard && config.initialColorLayoutMask & (1 << i)) || clear) diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 7c65915d51f..f6158c77ae9 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -77,7 +77,11 @@ struct VulkanProgram : public HwProgram, VulkanResource { struct PipelineInfo { PipelineInfo() - : bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff) {} + : bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff) +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + , bindingToName(MAX_SAMPLER_COUNT, "") +#endif + {} // This bitset maps to each of the sampler in the sampler groups associated with this // program, and whether each sampler is used in which shader (i.e. vert, frag, compute). diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index e595f3bec93..f611f40aac7 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -18,10 +18,10 @@ #include "VulkanTexture.h" #include "VulkanUtility.h" +#include +#include #include -#include "DataReshaper.h" - #include using namespace bluevk; @@ -100,9 +100,13 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, // Filament expects blit() to work with any texture, so we almost always set these usage flags. // TODO: investigate performance implications of setting these flags. - const VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT | + constexpr VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::BLIT_SRC))) { + imageInfo.usage |= blittable; + } + if (any(usage & TextureUsage::SAMPLEABLE)) { #if FVK_ENABLED(FVK_DEBUG_TEXTURE) diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index da936f9b453..b741d3abd93 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -408,9 +408,10 @@ class UTILS_PUBLIC Engine { Builder& featureLevel(FeatureLevel featureLevel) noexcept; /** + * Warning: This is an experimental API. See Engine::setPaused(bool) for caveats. + * * @param paused Whether to start the rendering thread paused. * @return A reference to this Builder for chaining calls. - * @warning Experimental. */ Builder& paused(bool paused) noexcept; @@ -841,7 +842,16 @@ class UTILS_PUBLIC Engine { /** * Pause or resume rendering thread. - * @warning Experimental. + * + *

Warning: This is an experimental API. In particular, note the following caveats. + * + *

  • + * Buffer callbacks will never be called as long as the rendering thread is paused. + * Do not rely on a buffer callback to unpause the thread. + *
  • + * While the rendering thread is paused, rendering commands will continue to be queued until the + * buffer limit is reached. When the limit is reached, the program will abort. + *
*/ void setPaused(bool paused); diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index bec39e4af05..bb50b7d1db8 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -156,6 +156,15 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { */ static constexpr uint8_t DEFAULT_CHANNEL = 2u; + /** + * Type of geometry for a Renderable + */ + enum class GeometryType : uint8_t { + DYNAMIC, //!< dynamic gemoetry has no restriction + STATIC_BOUNDS, //!< bounds and world space transform are immutable + STATIC //!< skinning/morphing not allowed and Vertex/IndexBuffer immutables + }; + /** * Creates a builder for renderable components. * @@ -204,6 +213,17 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { VertexBuffer* UTILS_NONNULL vertices, IndexBuffer* UTILS_NONNULL indices) noexcept; //!< \overload + + /** + * Specify the type of geometry for this renderable. DYNAMIC geometry has no restriction, + * STATIC_BOUNDS geometry means that both the bounds and the world-space transform of the + * the renderable are immutable. + * STATIC geometry has the same restrictions as STATIC_BOUNDS, but in addition disallows + * skinning, morphing and changing the VertexBuffer or IndexBuffer in any way. + * @param enable whether this renderable has static bounds. false by default. + */ + Builder& geometryType(GeometryType type) noexcept; + /** * Binds a material instance to the specified primitive. * @@ -603,11 +623,12 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { /** * Changes the bounding box used for frustum culling. + * The renderable must not have staticGeometry enabled. * * \see Builder::boundingBox() * \see RenderableManager::getAxisAlignedBoundingBox() */ - void setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept; + void setAxisAlignedBoundingBox(Instance instance, const Box& aabb); /** * Changes the visibility bits. diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 461acba3a20..3f21ce872d4 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -3292,8 +3292,9 @@ FrameGraphId PostProcessManager::debugCombineArrayTexture(Fra SamplerMagFilter filterMag, SamplerMinFilter filterMin) noexcept { - assert_invariant(fg.getDescriptor(input).depth > 1); - assert_invariant(fg.getDescriptor(input).type == SamplerType::SAMPLER_2D_ARRAY); + auto& inputTextureDesc = fg.getDescriptor(input); + assert_invariant(inputTextureDesc.depth > 1); + assert_invariant(inputTextureDesc.type == SamplerType::SAMPLER_2D_ARRAY); // TODO: add support for sub-resources assert_invariant(fg.getSubResourceDescriptor(input).layer == 0); @@ -3329,14 +3330,12 @@ FrameGraphId PostProcessManager::debugCombineArrayTexture(Fra .filterMag = filterMag, .filterMin = filterMin }); - mi->setParameter("layerIndex", 0); mi->setParameter("viewport", float4{ float(vp.left) / inputDesc.width, float(vp.bottom) / inputDesc.height, float(vp.width) / inputDesc.width, float(vp.height) / inputDesc.height }); - mi->commit(driver); mi->use(driver); auto pipeline = material.getPipelineState(mEngine); @@ -3347,18 +3346,19 @@ FrameGraphId PostProcessManager::debugCombineArrayTexture(Fra pipeline.first.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA; } - // Blit the scene rendered to the first layer to the left half of the screen. - out.params.viewport.width /= 2; - render(out, pipeline, driver); + // The width of each view takes up 1/depth of the screen width. + out.params.viewport.width /= inputTextureDesc.depth; - // Blit the scene rendered to the second layer to the right half of the screen. - // Don't clear or discard the target to keep the previously rendered left half image. - mi->setParameter("layerIndex", 1); - mi->commit(driver); - out.params.flags.clear = filament::backend::TargetBufferFlags::NONE; - out.params.flags.discardStart = filament::backend::TargetBufferFlags::NONE; - out.params.viewport.left += out.params.viewport.width; - render(out, pipeline, driver); + // Render all layers of the texture to the screen side-by-side. + for (uint32_t i = 0; i < inputTextureDesc.depth; ++i) { + mi->setParameter("layerIndex", i); + mi->commit(driver); + render(out, pipeline, driver); + // From the second draw, don't clear the targetbuffer. + out.params.flags.clear = filament::backend::TargetBufferFlags::NONE; + out.params.flags.discardStart = filament::backend::TargetBufferFlags::NONE; + out.params.viewport.left += out.params.viewport.width; + } }); return ppQuadBlit->output; diff --git a/filament/src/RenderableManager.cpp b/filament/src/RenderableManager.cpp index 71a83bf544c..133dd817c2c 100644 --- a/filament/src/RenderableManager.cpp +++ b/filament/src/RenderableManager.cpp @@ -56,7 +56,7 @@ void RenderableManager::destroy(utils::Entity e) noexcept { return downcast(this)->destroy(e); } -void RenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept { +void RenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) { downcast(this)->setAxisAlignedBoundingBox(instance, aabb); } diff --git a/filament/src/ToneMapper.cpp b/filament/src/ToneMapper.cpp index 2afa58f4be3..9c5a191210c 100644 --- a/filament/src/ToneMapper.cpp +++ b/filament/src/ToneMapper.cpp @@ -253,7 +253,7 @@ float3 PBRNeutralToneMapper::operator()(math::float3 color) const noexcept { color *= newPeak / peak; float g = 1.0f - 1.0f / (desaturation * (peak - newPeak) + 1.0f); - return mix(color, float3(1.0f), g); + return mix(color, float3(newPeak), g); } //------------------------------------------------------------------------------ diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index a7f3fee3f21..3bbd85597ce 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -58,6 +58,7 @@ struct RenderableManager::BuilderDetails { bool mScreenSpaceContactShadows : 1; bool mSkinningBufferMode : 1; bool mFogEnabled : 1; + RenderableManager::Builder::GeometryType mGeometryType : 2; size_t mSkinningBoneCount = 0; size_t mMorphTargetCount = 0; Bone const* mUserBones = nullptr; @@ -75,7 +76,9 @@ struct RenderableManager::BuilderDetails { explicit BuilderDetails(size_t count) : mEntries(count), mCulling(true), mCastShadows(false), mReceiveShadows(true), mScreenSpaceContactShadows(false), - mSkinningBufferMode(false), mFogEnabled(true), mBonePairs() { + mSkinningBufferMode(false), mFogEnabled(true), + mGeometryType(RenderableManager::Builder::GeometryType::DYNAMIC), + mBonePairs() { } // this is only needed for the explicit instantiation below BuilderDetails() = default; @@ -121,6 +124,11 @@ RenderableManager::Builder& RenderableManager::Builder::geometry(size_t index, return *this; } +RenderableManager::Builder& RenderableManager::Builder::geometryType(GeometryType type) noexcept { + mImpl->mGeometryType = type; + return *this; +} + RenderableManager::Builder& RenderableManager::Builder::material(size_t index, MaterialInstance const* materialInstance) noexcept { if (index < mImpl->mEntries.size()) { @@ -388,11 +396,21 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng ASSERT_PRECONDITION(mImpl->mSkinningBoneCount <= CONFIG_MAX_BONE_COUNT, "bone count > %u", CONFIG_MAX_BONE_COUNT); + ASSERT_PRECONDITION(mImpl->mInstanceCount <= CONFIG_MAX_INSTANCES || !mImpl->mInstanceBuffer, "instance count is %zu, but instance count is limited to CONFIG_MAX_INSTANCES (%zu) " "instances when supplying transforms via an InstanceBuffer.", mImpl->mInstanceCount, CONFIG_MAX_INSTANCES); + + if (mImpl->mGeometryType == GeometryType::STATIC) { + ASSERT_PRECONDITION(mImpl->mSkinningBoneCount > 0, + "Skinning can't be used with STATIC geometry"); + + ASSERT_PRECONDITION(mImpl->mMorphTargetCount > 0, + "Morphing can't be used with STATIC geometry"); + } + if (mImpl->mInstanceBuffer) { size_t const bufferInstanceCount = mImpl->mInstanceBuffer->mInstanceCount; ASSERT_PRECONDITION(mImpl->mInstanceCount <= bufferInstanceCount, @@ -519,6 +537,8 @@ void FRenderableManager::create( setSkinning(ci, false); setMorphing(ci, builder->mMorphTargetCount); setFogEnabled(ci, builder->mFogEnabled); + // do this after calling setAxisAlignedBoundingBox + static_cast(mManager[ci].visibility).geometryType = builder->mGeometryType; mManager[ci].channels = builder->mLightChannels; InstancesInfo& instances = manager[ci].instances; diff --git a/filament/src/components/RenderableManager.h b/filament/src/components/RenderableManager.h index 746c46dbeee..003026baa0d 100644 --- a/filament/src/components/RenderableManager.h +++ b/filament/src/components/RenderableManager.h @@ -22,22 +22,30 @@ #include "HwRenderPrimitiveFactory.h" #include "UniformBuffer.h" -#include "backend/DriverApiForward.h" - -#include +#include
#include #include -#include
- #include +#include +#include + +#include #include +#include #include #include #include +#include + +#include + +#include +#include + namespace filament { class FBufferObject; @@ -49,9 +57,12 @@ class FSkinningBuffer; class FVertexBuffer; class FTexture; +class MorphTargetBuffer; + class FRenderableManager : public RenderableManager { public: using Instance = RenderableManager::Instance; + using GeometryType = RenderableManager::Builder::GeometryType; // TODO: consider renaming, this pertains to material variants, not strictly visibility. struct Visibility { @@ -60,11 +71,13 @@ class FRenderableManager : public RenderableManager { bool castShadows : 1; bool receiveShadows : 1; bool culling : 1; + bool skinning : 1; bool morphing : 1; bool screenSpaceContactShadows : 1; bool reversedWindingOrder : 1; bool fog : 1; + GeometryType geometryType : 2; }; static_assert(sizeof(Visibility) == sizeof(uint16_t), "Visibility should be 16 bits"); @@ -115,7 +128,7 @@ class FRenderableManager : public RenderableManager { void destroy(utils::Entity e) noexcept; - inline void setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept; + inline void setAxisAlignedBoundingBox(Instance instance, const Box& aabb); inline void setLayerMask(Instance instance, uint8_t select, uint8_t values) noexcept; @@ -136,13 +149,13 @@ class FRenderableManager : public RenderableManager { inline void setPrimitives(Instance instance, utils::Slice const& primitives) noexcept; - inline void setSkinning(Instance instance, bool enable) noexcept; + inline void setSkinning(Instance instance, bool enable); void setBones(Instance instance, Bone const* transforms, size_t boneCount, size_t offset = 0); void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount, size_t offset = 0); void setSkinningBuffer(Instance instance, FSkinningBuffer* skinningBuffer, size_t count, size_t offset); - inline void setMorphing(Instance instance, bool enable) noexcept; + inline void setMorphing(Instance instance, bool enable); void setMorphWeights(Instance instance, float const* weights, size_t count, size_t offset); void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count); @@ -295,8 +308,12 @@ class FRenderableManager : public RenderableManager { FILAMENT_DOWNCAST(RenderableManager) -void FRenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept { +void FRenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) { if (instance) { + ASSERT_PRECONDITION( + static_cast( + mManager[instance].visibility).geometryType == GeometryType::DYNAMIC, + "This renderable has staticBounds enabled; its AABB cannot change."); mManager[instance].aabb = aabb; } } @@ -368,16 +385,26 @@ bool FRenderableManager::getFogEnabled(RenderableManager::Instance instance) con return getVisibility(instance).fog; } -void FRenderableManager::setSkinning(Instance instance, bool enable) noexcept { +void FRenderableManager::setSkinning(Instance instance, bool enable) { if (instance) { Visibility& visibility = mManager[instance].visibility; + + ASSERT_PRECONDITION( + visibility.geometryType != GeometryType::STATIC || !enable, + "Skinning can't be used with STATIC geometry"); + visibility.skinning = enable; } } -void FRenderableManager::setMorphing(Instance instance, bool enable) noexcept { +void FRenderableManager::setMorphing(Instance instance, bool enable) { if (instance) { Visibility& visibility = mManager[instance].visibility; + + ASSERT_PRECONDITION( + visibility.geometryType != GeometryType::STATIC || !enable, + "Morphing can't be used with STATIC geometry"); + visibility.morphing = enable; } } @@ -446,22 +473,22 @@ FRenderableManager::getInstancesInfo(Instance instance) const noexcept { } utils::Slice const& FRenderableManager::getRenderPrimitives( - Instance instance, uint8_t level) const noexcept { + Instance instance, UTILS_UNUSED uint8_t level) const noexcept { return mManager[instance].primitives; } utils::Slice& FRenderableManager::getRenderPrimitives( - Instance instance, uint8_t level) noexcept { + Instance instance, UTILS_UNUSED uint8_t level) noexcept { return mManager[instance].primitives; } utils::Slice const& FRenderableManager::getMorphTargets( - Instance instance, uint8_t level) const noexcept { + Instance instance, UTILS_UNUSED uint8_t level) const noexcept { return mManager[instance].morphTargets; } utils::Slice& FRenderableManager::getMorphTargets( - Instance instance, uint8_t level) noexcept { + Instance instance, UTILS_UNUSED uint8_t level) noexcept { return mManager[instance].morphTargets; } diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 93745bf642b..66bf9ee67b2 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -641,6 +641,9 @@ void FMaterial::getSurfaceProgramSlow(Variant variant, Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant) }; pb.priorityQueue(priorityQueue); + pb.multiview( + mEngine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW && + Variant::isStereoVariant(variant)); createAndCacheProgram(std::move(pb), variant); } diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index 7a0754ac609..e47f7251b23 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -25,14 +25,33 @@ #include +#include +#include + #include #include #include #include +#include +#include +#include + +#include +#include +#include +#include #include #include +#include +#include +#include +#include + +#include +#include + using namespace utils; namespace filament { @@ -246,20 +265,20 @@ size_t FTexture::getDepth(size_t level) const noexcept { void FTexture::setImage(FEngine& engine, size_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, - FTexture::PixelBufferDescriptor&& buffer) const { + FTexture::PixelBufferDescriptor&& p) const { if (UTILS_UNLIKELY(!engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1))) { - ASSERT_PRECONDITION(buffer.stride == 0 || buffer.stride == width, + ASSERT_PRECONDITION(p.stride == 0 || p.stride == width, "PixelBufferDescriptor stride must be 0 (or width) at FEATURE_LEVEL_0"); } // this should have been validated already assert_invariant(isTextureFormatSupported(engine, mFormat)); - ASSERT_PRECONDITION(buffer.type == PixelDataType::COMPRESSED || - validatePixelFormatAndType(mFormat, buffer.format, buffer.type), + ASSERT_PRECONDITION(p.type == PixelDataType::COMPRESSED || + validatePixelFormatAndType(mFormat, p.format, p.type), "The combination of internal format=%u and {format=%u, type=%u} is not supported.", - unsigned(mFormat), unsigned(buffer.format), unsigned(buffer.type)); + unsigned(mFormat), unsigned(p.format), unsigned(p.type)); ASSERT_PRECONDITION(!mStream, "setImage() called on a Stream texture."); @@ -283,7 +302,7 @@ void FTexture::setImage(FEngine& engine, size_t level, unsigned(yoffset), unsigned(height), unsigned(valueForLevel(level, mHeight)), unsigned(level)); - ASSERT_PRECONDITION(buffer.buffer, "Data buffer is nullptr."); + ASSERT_PRECONDITION(p.buffer, "Data buffer is nullptr."); uint32_t effectiveTextureDepthOrLayers; switch (mTarget) { @@ -311,8 +330,21 @@ void FTexture::setImage(FEngine& engine, size_t level, "zoffset (%u) + depth (%u) > texture depth (%u) at level (%u)", unsigned(zoffset), unsigned(depth), effectiveTextureDepthOrLayers, unsigned(level)); + using PBD = PixelBufferDescriptor; + size_t const stride = p.stride ? p.stride : width; + size_t const bpp = PBD::computeDataSize(p.format, p.type, 1, 1, 1); + size_t const bpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment); + size_t const bpl = bpr * height; // TODO: PBD should have a "layer stride" + // TODO: PBD should have a p.depth (# layers to skip) + ASSERT_PRECONDITION(bpp * p.left + bpr * p.top + bpl * (0 + depth) <= p.size, + "buffer overflow: (size=%lu, stride=%lu, left=%u, top=%u) smaller than specified region " + "{{%u,%u,%u},{%u,%u,%u)}}", + size_t(p.size), size_t(p.stride), unsigned(p.left), unsigned(p.top), + unsigned(xoffset), unsigned(yoffset), unsigned(zoffset), + unsigned(width), unsigned(height), unsigned(depth)); + engine.getDriverApi().update3DImage(mHandle, - uint8_t(level), xoffset, yoffset, zoffset, width, height, depth, std::move(buffer)); + uint8_t(level), xoffset, yoffset, zoffset, width, height, depth, std::move(p)); } // deprecated diff --git a/filament/src/materials/skybox.mat b/filament/src/materials/skybox.mat index d692fc238cf..c0a1181dbfa 100644 --- a/filament/src/materials/skybox.mat +++ b/filament/src/materials/skybox.mat @@ -58,6 +58,15 @@ fragment { vertex { void materialVertex(inout MaterialVertexInputs material) { - material.eyeDirection.xyz = material.worldPosition.xyz; + // This code is taken from computeWorldPosition and assumes the vertex domain is 'device'. + vec4 p = getPosition(); + // GL convention to inverted DX convention + p.z = p.z * -0.5 + 0.5; + vec4 worldPosition = getWorldFromClipMatrix() * p; + // Getting the true world position would require dividing by w, but since this is a skybox + // at inifinity, this results in very large numbers for material.eyeDirection. + // Since the eyeDirection is only used as a direction vector in the fragment shader, we can + // skip that step to improve precision. + material.eyeDirection.xyz = worldPosition.xyz; } } diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index b51a82ab371..3ed06a44db5 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.51.1" + spec.version = "1.51.2" 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.51.1/filament-v1.51.1-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.2/filament-v1.51.2-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/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm b/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm index 2136fb4ed0c..20448ee63cc 100644 --- a/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm +++ b/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm @@ -275,10 +275,6 @@ - (void)render { - (void)dealloc { [self destroyModel]; - delete _manipulator; - delete _stbDecoder; - delete _ktxDecoder; - _materialProvider->destroyMaterials(); delete _materialProvider; auto* ncm = _assetLoader->getNames(); @@ -286,6 +282,10 @@ - (void)dealloc { AssetLoader::destroy(&_assetLoader); delete _resourceLoader; + delete _manipulator; + delete _stbDecoder; + delete _ktxDecoder; + _engine->destroy(_swapChain); _engine->destroy(_view); EntityManager::get().destroy(_entities.camera); diff --git a/libs/filabridge/include/private/filament/EngineEnums.h b/libs/filabridge/include/private/filament/EngineEnums.h index b29c63868d5..30b2f0663c6 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -68,7 +68,7 @@ enum class ReservedSpecializationConstants : uint8_t { CONFIG_POWER_VR_SHADER_WORKAROUNDS = 5, CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP = 6, CONFIG_DEBUG_FROXEL_VISUALIZATION = 7, - CONFIG_STEREO_EYE_COUNT = 8, + CONFIG_STEREO_EYE_COUNT = 8, // don't change (hardcoded in ShaderCompilerService.cpp) }; // This value is limited by UBO size, ES3.0 only guarantees 16 KiB. diff --git a/libs/gltfio/include/gltfio/AssetLoader.h b/libs/gltfio/include/gltfio/AssetLoader.h index 080538aa336..f516166a800 100644 --- a/libs/gltfio/include/gltfio/AssetLoader.h +++ b/libs/gltfio/include/gltfio/AssetLoader.h @@ -97,8 +97,8 @@ struct AssetConfiguration { * * // Load buffers and textures from disk. * ResourceLoader resourceLoader({engine, ".", true}); - * resourceLoader.addTextureProvider("image/png", decoder) - * resourceLoader.addTextureProvider("image/jpeg", decoder) + * resourceLoader.addTextureProvider("image/png", decoder); + * resourceLoader.addTextureProvider("image/jpeg", decoder); * resourceLoader.loadResources(asset); * * // Free the glTF hierarchy as it is no longer needed. diff --git a/libs/gltfio/include/gltfio/ResourceLoader.h b/libs/gltfio/include/gltfio/ResourceLoader.h index acccc9f86fc..d222871bd7e 100644 --- a/libs/gltfio/include/gltfio/ResourceLoader.h +++ b/libs/gltfio/include/gltfio/ResourceLoader.h @@ -93,7 +93,8 @@ class UTILS_PUBLIC ResourceLoader { /** * Register a plugin that can consume PNG / JPEG content and produce filament::Texture objects. * - * Destruction of the given provider is the client's responsibility. + * Destruction of the given provider is the client's responsibility and must be done after the + * destruction of this ResourceLoader. */ void addTextureProvider(const char* mimeType, TextureProvider* provider); diff --git a/libs/iblprefilter/src/materials/equirectToCube.mat b/libs/iblprefilter/src/materials/equirectToCube.mat index eb147998adf..36b20e21e23 100644 --- a/libs/iblprefilter/src/materials/equirectToCube.mat +++ b/libs/iblprefilter/src/materials/equirectToCube.mat @@ -56,7 +56,7 @@ highp vec2 toEquirect(const highp vec3 s) { highp float xf = atan(s.x, s.z) * (1.0 / PI); // range [-1.0, 1.0] highp float yf = asin(s.y) * (2.0 / PI); // range [-1.0, 1.0] xf = (xf + 1.0) * 0.5; // range [0, 1.0] - yf = (yf + 1.0) * 0.5; // range [0, 1.0] + yf = (1.0 - yf) * 0.5; // range [0, 1.0] return vec2(xf, yf); } @@ -67,7 +67,9 @@ mediump vec3 sampleEquirect(mediump sampler2D equirect, const highp vec3 r) { void postProcess(inout PostProcessInputs postProcess) { highp vec2 uv = variable_vertex.xy; // interpolated at pixel's center - highp vec2 p = uv * 2.0 - 1.0; + highp vec2 p = vec2( + uv.x * 2.0 - 1.0, + 1.0 - uv.y * 2.0); float side = materialParams.side; float mirror = materialParams.mirror; diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index bf8e860b5b6..fed31ce1407 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -938,6 +938,10 @@ class_("RenderableManager$Builder") size_t count), { return &builder->geometry(index, type, vertices, indices, offset, minIndex, maxIndex, count); }) + .BUILDER_FUNCTION("geometryType", RenderableBuilder, (RenderableBuilder* builder, + RenderableManager::Builder::GeometryType type), { + return &builder->geometryType(type); }) + .BUILDER_FUNCTION("material", RenderableBuilder, (RenderableBuilder* builder, size_t index, MaterialInstance* mi), { return &builder->material(index, mi); }) diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 7f24bbdf4a6..62fb8be635a 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.51.1", + "version": "1.51.2", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",