diff --git a/README.md b/README.md index 3bff570f9c..8a4adea8f2 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.50.6' + implementation 'com.google.android.filament:filament-android:1.51.0' } ``` @@ -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.50.6' +pod 'Filament', '~> 1.51.0' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 64ab3f4769..e3b32ecc38 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.0 + +- materials: add support for post-lighting mix factor (b/328498606) [⚠️ **New Material Version**] + ## v1.50.6 - Add new API `SwapChain::getFrameScheduledCallback` 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 a858330eb4..aee4c2b34a 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 @@ -654,7 +654,7 @@ public class Engine { */ @NonNull public SwapChain createSwapChain(@NonNull Object surface) { - return createSwapChain(surface, SwapChain.CONFIG_DEFAULT); + return createSwapChain(surface, SwapChainFlags.CONFIG_DEFAULT); } /** @@ -662,15 +662,15 @@ public class Engine { * * @param surface on Android, must be an instance of {@link android.view.Surface} * - * @param flags configuration flags, see {@link SwapChain} + * @param flags configuration flags, see {@link SwapChainFlags} * * @return a newly created {@link SwapChain} object * * @exception IllegalStateException can be thrown if the SwapChain couldn't be created * - * @see SwapChain#CONFIG_DEFAULT - * @see SwapChain#CONFIG_TRANSPARENT - * @see SwapChain#CONFIG_READABLE + * @see SwapChainFlags#CONFIG_DEFAULT + * @see SwapChainFlags#CONFIG_TRANSPARENT + * @see SwapChainFlags#CONFIG_READABLE * */ @NonNull @@ -688,21 +688,22 @@ public class Engine { * * @param width width of the rendering buffer * @param height height of the rendering buffer - * @param flags configuration flags, see {@link SwapChain} + * @param flags configuration flags, see {@link SwapChainFlags} * * @return a newly created {@link SwapChain} object * * @exception IllegalStateException can be thrown if the SwapChain couldn't be created * - * @see SwapChain#CONFIG_DEFAULT - * @see SwapChain#CONFIG_TRANSPARENT - * @see SwapChain#CONFIG_READABLE + * @see SwapChainFlags#CONFIG_DEFAULT + * @see SwapChainFlags#CONFIG_TRANSPARENT + * @see SwapChainFlags#CONFIG_READABLE * */ @NonNull public SwapChain createSwapChain(int width, int height, long flags) { if (width >= 0 && height >= 0) { - long nativeSwapChain = nCreateSwapChainHeadless(getNativeObject(), width, height, flags); + long nativeSwapChain = + nCreateSwapChainHeadless(getNativeObject(), width, height, flags); if (nativeSwapChain == 0) throw new IllegalStateException("Couldn't create SwapChain"); return new SwapChain(nativeSwapChain, null); } @@ -714,11 +715,12 @@ public class Engine { * * @param surface a properly initialized {@link NativeSurface} * - * @param flags configuration flags, see {@link SwapChain} + * @param flags configuration flags, see {@link SwapChainFlags} * * @return a newly created {@link SwapChain} object * - * @exception IllegalStateException can be thrown if the {@link SwapChain} couldn't be created + * @exception IllegalStateException can be thrown if the {@link SwapChainFlags} couldn't be + * created */ @NonNull public SwapChain createSwapChainFromNativeSurface(@NonNull NativeSurface surface, long flags) { diff --git a/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java b/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java index e3d1cf43aa..db47d21563 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java +++ b/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java @@ -68,72 +68,6 @@ public class SwapChain { private final Object mSurface; private long mNativeObject; - public static final long CONFIG_DEFAULT = 0x0; - - /** - * This flag indicates that the SwapChain must be allocated with an - * alpha-channel. - */ - public static final long CONFIG_TRANSPARENT = 0x1; - - /** - * This flag indicates that the SwapChain may be used as a source surface - * for reading back render results. This config must be set when creating - * any SwapChain that will be used as the source for a blit operation. - * - * @see Renderer#copyFrame - */ - public static final long CONFIG_READABLE = 0x2; - - /** - * Indicates that the native X11 window is an XCB window rather than an XLIB window. - * This is ignored on non-Linux platforms and in builds that support only one X11 API. - */ - public static final long CONFIG_ENABLE_XCB = 0x4; - - /** - * Indicates that the SwapChain must automatically perform linear to sRGB encoding. - * - * This flag is ignored if isSRGBSwapChainSupported() is false. - * - * When using this flag, post-processing should be disabled. - * - * @see SwapChain#isSRGBSwapChainSupported - * @see View#setPostProcessingEnabled - */ - public static final long CONFIG_SRGB_COLORSPACE = 0x10; - - /** - * Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer. - * - * This flag is necessary when using View::setStencilBufferEnabled and rendering directly into - * the SwapChain (when post-processing is disabled). - * - * The specific format of the stencil buffer depends on platform support. The following pixel - * formats are tried, in order of preference: - * - * Depth only (without CONFIG_HAS_STENCIL_BUFFER): - * - DEPTH32F - * - DEPTH24 - * - * Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER): - * - DEPTH32F_STENCIL8 - * - DEPTH24F_STENCIL8 - * - * Note that enabling the stencil buffer may hinder depth precision and should only be used if - * necessary. - * - * @see View#setStencilBufferEnabled - * @see View#setPostProcessingEnabled - */ - public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20; - - /** - * The SwapChain contains protected content. Only supported when isProtectedContentSupported() - * is true. - */ - public static final long CONFIG_PROTECTED_CONTENT = 0x40; - SwapChain(long nativeSwapChain, Object surface) { mNativeObject = nativeSwapChain; mSurface = surface; @@ -145,17 +79,19 @@ public class SwapChain { * * @param engine A reference to the filament Engine * @return true if CONFIG_PROTECTED_CONTENT is supported, false otherwise. + * @see SwapChainFlags#CONFIG_PROTECTED_CONTENT */ public static boolean isProtectedContentSupported(@NonNull Engine engine) { return nIsProtectedContentSupported(engine.getNativeObject()); } /** - * Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag. + * Return whether createSwapChain supports the CONFIG_SRGB_COLORSPACE flag. * The default implementation returns false. * * @param engine A reference to the filament Engine - * @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise. + * @return true if CONFIG_SRGB_COLORSPACE is supported, false otherwise. + * @see SwapChainFlags#CONFIG_SRGB_COLORSPACE */ public static boolean isSRGBSwapChainSupported(@NonNull Engine engine) { return nIsSRGBSwapChainSupported(engine.getNativeObject()); diff --git a/android/filament-android/src/main/java/com/google/android/filament/SwapChainFlags.java b/android/filament-android/src/main/java/com/google/android/filament/SwapChainFlags.java new file mode 100644 index 0000000000..daef2dd15f --- /dev/null +++ b/android/filament-android/src/main/java/com/google/android/filament/SwapChainFlags.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.filament; + +// Note: SwapChainFlags is kept separate from SwapChain so that UiHelper does not need to depend +// on SwapChain. This allows clients to use UiHelper without requiring all of Filament's Java +// classes. + +/** + * Flags that a SwapChain can be created with to control behavior. + * + * @see Engine#createSwapChain + * @see Engine#createSwapChainFromNativeSurface + */ +public final class SwapChainFlags { + + public static final long CONFIG_DEFAULT = 0x0; + + /** + * This flag indicates that the SwapChain must be allocated with an + * alpha-channel. + */ + public static final long CONFIG_TRANSPARENT = 0x1; + + /** + * This flag indicates that the SwapChain may be used as a source surface + * for reading back render results. This config must be set when creating + * any SwapChain that will be used as the source for a blit operation. + * + * @see Renderer#copyFrame + */ + public static final long CONFIG_READABLE = 0x2; + + /** + * Indicates that the native X11 window is an XCB window rather than an XLIB window. + * This is ignored on non-Linux platforms and in builds that support only one X11 API. + */ + public static final long CONFIG_ENABLE_XCB = 0x4; + + /** + * Indicates that the SwapChain must automatically perform linear to sRGB encoding. + * + * This flag is ignored if isSRGBSwapChainSupported() is false. + * + * When using this flag, post-processing should be disabled. + * + * @see SwapChain#isSRGBSwapChainSupported + * @see View#setPostProcessingEnabled + */ + public static final long CONFIG_SRGB_COLORSPACE = 0x10; + + /** + * Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer. + * + * This flag is necessary when using View::setStencilBufferEnabled and rendering directly into + * the SwapChain (when post-processing is disabled). + * + * The specific format of the stencil buffer depends on platform support. The following pixel + * formats are tried, in order of preference: + * + * Depth only (without CONFIG_HAS_STENCIL_BUFFER): + * - DEPTH32F + * - DEPTH24 + * + * Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER): + * - DEPTH32F_STENCIL8 + * - DEPTH24F_STENCIL8 + * + * Note that enabling the stencil buffer may hinder depth precision and should only be used if + * necessary. + * + * @see View#setStencilBufferEnabled + * @see View#setPostProcessingEnabled + */ + public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20; + + /** + * The SwapChain contains protected content. Only supported when isProtectedContentSupported() + * is true. + */ + public static final long CONFIG_PROTECTED_CONTENT = 0x40; +} + diff --git a/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java b/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java index 451c09a33b..4d6f3a140f 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java +++ b/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java @@ -27,7 +27,7 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.TextureView; -import com.google.android.filament.SwapChain; +import com.google.android.filament.SwapChainFlags; /** * UiHelper is a simple class that can manage either a SurfaceView, TextureView, or a SurfaceHolder @@ -538,7 +538,7 @@ public class UiHelper { * the options set on this UiHelper. */ public long getSwapChainFlags() { - return isOpaque() ? SwapChain.CONFIG_DEFAULT : SwapChain.CONFIG_TRANSPARENT; + return isOpaque() ? SwapChainFlags.CONFIG_DEFAULT : SwapChainFlags.CONFIG_TRANSPARENT; } /** diff --git a/android/gradle.properties b/android/gradle.properties index 5dd1904abe..535a300264 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.50.6 +VERSION_NAME=1.51.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 698c139332..25318ec0fa 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -252,8 +252,8 @@ set(MATERIAL_SRCS ) set(MATERIAL_FL0_SRCS - src/materials/defaultMaterial0.mat - src/materials/skybox0.mat + src/materials/defaultMaterial.mat + src/materials/skybox.mat ) # Embed the binary resource blob for materials. @@ -316,32 +316,30 @@ foreach (mat_src ${MATERIAL_SRCS}) get_filename_component(fullname "${mat_src}" ABSOLUTE) set(output_path "${MATERIAL_DIR}/${localname}.filamat") - add_custom_command( - OUTPUT ${output_path} - COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} - MAIN_DEPENDENCY ${fullname} - DEPENDS matc - COMMENT "Compiling material ${mat_src} to ${output_path}" - ) - list(APPEND MATERIAL_BINS ${output_path}) -endforeach() - -if (FILAMENT_ENABLE_FEATURE_LEVEL_0) - foreach (mat_src ${MATERIAL_FL0_SRCS}) - get_filename_component(localname "${mat_src}" NAME_WE) - get_filename_component(fullname "${mat_src}" ABSOLUTE) - set(output_path "${MATERIAL_DIR}/${localname}.filamat") - + list(FIND MATERIAL_FL0_SRCS ${mat_src} index) + if (${index} GREATER -1 AND FILAMENT_ENABLE_FEATURE_LEVEL_0) + string(REGEX REPLACE "[.]filamat$" "0.filamat" output_path0 ${output_path}) + add_custom_command( + OUTPUT ${output_path} ${output_path0} + COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} + COMMAND matc ${MATC_BASE_FLAGS} -PfeatureLevel=0 -o ${output_path0} ${fullname} + MAIN_DEPENDENCY ${fullname} + DEPENDS matc + COMMENT "Compiling material ${fullname} to ${output_path} and ${output_path0}" + ) + list(APPEND MATERIAL_BINS ${output_path0}) + else () add_custom_command( OUTPUT ${output_path} COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} MAIN_DEPENDENCY ${fullname} DEPENDS matc - COMMENT "Compiling material ${mat_src} to ${output_path}" + COMMENT "Compiling material ${fullname} to ${output_path}" ) - list(APPEND MATERIAL_BINS ${output_path}) - endforeach () -endif () + endif () + + list(APPEND MATERIAL_BINS ${output_path}) +endforeach() # Additional dependencies on included files for materials diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index c2deaf547c..6b0424ca06 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -1186,11 +1186,27 @@ struct StencilState { //! Stencil operations for front-facing polygons StencilOperations front = { - .stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff }; + .stencilFunc = StencilFunction::A, + .stencilOpStencilFail = StencilOperation::KEEP, + .padding0 = 0, + .stencilOpDepthFail = StencilOperation::KEEP, + .stencilOpDepthStencilPass = StencilOperation::KEEP, + .padding1 = 0, + .ref = 0, + .readMask = 0xff, + .writeMask = 0xff }; //! Stencil operations for back-facing polygons StencilOperations back = { - .stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff }; + .stencilFunc = StencilFunction::A, + .stencilOpStencilFail = StencilOperation::KEEP, + .padding0 = 0, + .stencilOpDepthFail = StencilOperation::KEEP, + .stencilOpDepthStencilPass = StencilOperation::KEEP, + .padding1 = 0, + .ref = 0, + .readMask = 0xff, + .writeMask = 0xff }; //! Whether stencil-buffer writes are enabled bool stencilWrite = false; diff --git a/filament/backend/include/backend/PipelineState.h b/filament/backend/include/backend/PipelineState.h index 04001345d8..220d04bbf2 100644 --- a/filament/backend/include/backend/PipelineState.h +++ b/filament/backend/include/backend/PipelineState.h @@ -29,10 +29,13 @@ namespace filament::backend { //! \privatesection struct PipelineState { - Handle program; - RasterState rasterState; - StencilState stencilState; - PolygonOffset polygonOffset; + Handle program; // 4 + Handle vertexBufferInfo; // 4 + RasterState rasterState; // 4 + StencilState stencilState; // 12 + PolygonOffset polygonOffset; // 8 + PrimitiveType primitiveType = PrimitiveType::TRIANGLES; // 1 + uint8_t padding[3] = {}; // 3 }; } // namespace filament::backend diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h index b89fb98f7f..1d85568a88 100644 --- a/filament/backend/include/backend/Platform.h +++ b/filament/backend/include/backend/Platform.h @@ -56,7 +56,7 @@ public: /** * Set to `true` to forcibly disable parallel shader compilation in the backend. - * Currently only honored by the GL backend. + * Currently only honored by the GL and Metal backends. */ bool disableParallelShaderCompile = false; }; diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 05170ac8d4..ac1dc256c8 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -493,6 +493,17 @@ DECL_DRIVER_API_N(blit, math::uint2, srcOrigin, math::uint2, size) +DECL_DRIVER_API_N(bindPipeline, + backend::PipelineState, state) + +DECL_DRIVER_API_N(bindRenderPrimitive, + backend::RenderPrimitiveHandle, rph) + +DECL_DRIVER_API_N(draw2, + uint32_t, indexOffset, + uint32_t, indexCount, + uint32_t, instanceCount) + DECL_DRIVER_API_N(draw, backend::PipelineState, state, backend::RenderPrimitiveHandle, rph, @@ -500,6 +511,7 @@ DECL_DRIVER_API_N(draw, uint32_t, indexCount, uint32_t, instanceCount) + DECL_DRIVER_API_N(dispatchCompute, backend::ProgramHandle, program, math::uint3, workGroupCount) diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index 14e70fd06e..53bab21077 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -37,9 +37,9 @@ #include #include -#define HandleAllocatorGL HandleAllocator<24, 64, 136> // ~3640 / pool / MiB -#define HandleAllocatorVK HandleAllocator<80, 176, 320> // ~1820 / pool / MiB -#define HandleAllocatorMTL HandleAllocator<48, 160, 592> // ~1310 / pool / MiB +#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB +#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB +#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB namespace filament::backend { diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h index c98419b167..33b8f92c82 100644 --- a/filament/backend/src/metal/MetalContext.h +++ b/filament/backend/src/metal/MetalContext.h @@ -99,6 +99,7 @@ struct MetalContext { std::array ssboState; CullModeStateTracker cullModeState; WindingStateTracker windingState; + Handle currentRenderPrimitive; // State caches. DepthStencilStateCache depthStencilStateCache; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index e291ab60fd..8dfbb27c51 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -48,24 +48,26 @@ Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform // For reference on a 64-bits machine in Release mode: // MetalTimerQuery : 16 few // HwStream : 24 few + // MetalRenderPrimitive : 24 many + // MetalVertexBuffer : 32 moderate + // -- less than or equal 32 bytes // MetalIndexBuffer : 40 moderate // MetalFence : 48 few // MetalBufferObject : 48 many // -- less than or equal 48 bytes // MetalSamplerGroup : 112 few - // MetalProgram : 144 moderate + // MetalProgram : 152 moderate // MetalTexture : 152 moderate - // MetalVertexBuffer : 152 moderate - // -- less than or equal 160 bytes // MetalSwapChain : 184 few // MetalRenderTarget : 272 few - // MetalRenderPrimitive : 584 many - // -- less than or equal to 592 bytes + // MetalVertexBufferInfo : 552 moderate + // -- less than or equal to 552 bytes utils::slog.d << "\nMetalSwapChain: " << sizeof(MetalSwapChain) << "\nMetalBufferObject: " << sizeof(MetalBufferObject) << "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer) + << "\nMetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo) << "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer) << "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup) << "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive) @@ -178,7 +180,10 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig& mContext->eventListener = [[MTLSharedEventListener alloc] initWithDispatchQueue:queue]; } - mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this); + const MetalShaderCompiler::Mode compilerMode = driverConfig.disableParallelShaderCompile + ? MetalShaderCompiler::Mode::SYNCHRONOUS + : MetalShaderCompiler::Mode::ASYNCHRONOUS; + mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this, compilerMode); mContext->shaderCompiler->init(); #if defined(FILAMENT_METAL_PROFILING) @@ -786,7 +791,7 @@ bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) } bool MetalDriver::isParallelShaderCompileSupported() { - return true; + return mContext->shaderCompiler->isParallelShaderCompileSupported(); } bool MetalDriver::isDepthStencilResolveSupported() { @@ -1602,11 +1607,13 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) { } } -void MetalDriver::draw(PipelineState ps, Handle rph, - uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { +void MetalDriver::bindPipeline(PipelineState ps) { ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr, - "Attempted to draw without a valid command encoder."); - auto primitive = handle_cast(rph); + "bindPipeline() without a valid command encoder."); + + MetalVertexBufferInfo const* const vbi = + handle_cast(ps.vertexBufferInfo); + auto program = handle_cast(ps.program); const auto& rs = ps.rasterState; @@ -1620,7 +1627,7 @@ void MetalDriver::draw(PipelineState ps, Handle rph, return; } - functions.validate(); + ASSERT_PRECONDITION(bool(functions), "Attempting to bind an invalid Metal program."); auto [fragment, vertex] = functions.getRasterFunctions(); @@ -1647,7 +1654,7 @@ void MetalDriver::draw(PipelineState ps, Handle rph, MetalPipelineState const pipelineState { .vertexFunction = vertex, .fragmentFunction = fragment, - .vertexDescription = primitive->vertexDescription, + .vertexDescription = vbi->vertexDescription, .colorAttachmentPixelFormat = { colorPixelFormat[0], colorPixelFormat[1], @@ -1741,20 +1748,6 @@ void MetalDriver::draw(PipelineState ps, Handle rph, mContext->currentPolygonOffset = ps.polygonOffset; } - // Bind uniform buffers. - MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil }; - NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 }; - - enumerateBoundBuffers(BufferObjectBinding::UNIFORM, - [&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer, - uint32_t index) { - uniformsToBind[index] = buffer; - offsets[index] = state.offset; - }); - MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder, - UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT, - uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT); - // Bind sampler groups (argument buffers). for (size_t s = 0; s < Program::SAMPLER_BINDING_COUNT; s++) { MetalSamplerGroup* const samplerGroup = mContext->samplerBindings[s]; @@ -1785,19 +1778,29 @@ void MetalDriver::draw(PipelineState ps, Handle rph, atIndex:(SAMPLER_GROUP_BINDING_START + s)]; } } +} + +void MetalDriver::bindRenderPrimitive(Handle rph) { + ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr, + "bindRenderPrimitive() without a valid command encoder."); // Bind the user vertex buffers. - MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {}; size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {}; size_t maxBufferIndex = 0; + MetalRenderPrimitive const* const primitive = handle_cast(rph); + MetalVertexBufferInfo const* const vbi = + handle_cast(primitive->vertexBuffer->vbih); + + mContext->currentRenderPrimitive = rph; + auto vb = primitive->vertexBuffer; - for (auto m : primitive->bufferMapping) { + for (auto m : vbi->bufferMapping) { assert_invariant( m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START && m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT); - size_t vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START; + size_t const vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START; vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex]; maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex); } @@ -1812,6 +1815,27 @@ void MetalDriver::draw(PipelineState ps, Handle rph, [mContext->currentRenderPassEncoder setVertexBytes:bytes length:16 atIndex:ZERO_VERTEX_BUFFER_BINDING]; +} + +void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { + ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr, + "draw() without a valid command encoder."); + + // Bind uniform buffers. + MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil }; + NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 }; + + enumerateBoundBuffers(BufferObjectBinding::UNIFORM, + [&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer, + uint32_t index) { + uniformsToBind[index] = buffer; + offsets[index] = state.offset; + }); + MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder, + UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT, + uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT); + + auto primitive = handle_cast(mContext->currentRenderPrimitive); MetalIndexBuffer* indexBuffer = primitive->indexBuffer; @@ -1825,6 +1849,16 @@ void MetalDriver::draw(PipelineState ps, Handle rph, instanceCount:instanceCount]; } +void MetalDriver::draw(PipelineState ps, Handle rph, + uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { + MetalRenderPrimitive const* const rp = handle_cast(rph); + ps.primitiveType = rp->type; + ps.vertexBufferInfo = rp->vertexBuffer->vbih; + bindPipeline(ps); + bindRenderPrimitive(rph); + draw2(indexOffset, indexCount, instanceCount); +} + void MetalDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) { ASSERT_PRECONDITION(!isInRenderPass(mContext), "dispatchCompute must be called outside of a render pass."); diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 534d75044a..fea6f0947f 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -149,7 +149,21 @@ struct MetalVertexBufferInfo : public HwVertexBufferInfo { MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes); - AttributeArray attributes; + // This struct is used to create the pipeline description to describe vertex assembly. + VertexDescription vertexDescription = {}; + + struct Entry { + uint8_t sourceBufferIndex = 0; + uint8_t stride = 0; + // maps to -> + uint8_t bufferArgumentIndex = 0; + + Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex) + : sourceBufferIndex(sourceBufferIndex), + stride(stride), + bufferArgumentIndex(bufferArgumentIndex) {} + }; + utils::FixedCapacityVector bufferMapping; }; struct MetalVertexBuffer : public HwVertexBuffer { @@ -176,22 +190,6 @@ struct MetalRenderPrimitive : public HwRenderPrimitive { MetalVertexBuffer* vertexBuffer = nullptr; MetalIndexBuffer* indexBuffer = nullptr; - - // This struct is used to create the pipeline description to describe vertex assembly. - VertexDescription vertexDescription = {}; - - struct Entry { - uint8_t sourceBufferIndex = 0; - uint8_t stride = 0; - // maps to -> - uint8_t bufferArgumentIndex = 0; - - Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex) - : sourceBufferIndex(sourceBufferIndex), - stride(stride), - bufferArgumentIndex(bufferArgumentIndex) {} - }; - utils::FixedCapacityVector bufferMapping; }; class MetalProgram : public HwProgram { diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index eef4087cc5..18d8311825 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -343,27 +343,9 @@ void MetalBufferObject::updateBufferUnsynchronized(void* data, size_t size, uint MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes) : HwVertexBufferInfo(bufferCount, attributeCount), - attributes(attributes) { -} + bufferMapping(utils::FixedCapacityVector::with_capacity(MAX_VERTEX_BUFFER_COUNT)) { -MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, - uint32_t vertexCount, uint32_t bufferCount, Handle vbih) - : HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) { -} - -MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize, - uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount), - buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { } - -MetalRenderPrimitive::MetalRenderPrimitive() - : bufferMapping(utils::FixedCapacityVector::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {} - -void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi, - MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) { - this->vertexBuffer = vertexBuffer; - this->indexBuffer = indexBuffer; - - const size_t attributeCount = vbi->attributes.size(); + const size_t maxAttributeCount = attributes.size(); auto& mapping = bufferMapping; mapping.clear(); @@ -403,8 +385,8 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi, } }; - for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) { - const auto& attribute = vbi->attributes[attributeIndex]; + for (uint32_t attributeIndex = 0; attributeIndex < maxAttributeCount; attributeIndex++) { + const auto& attribute = attributes[attributeIndex]; // If the attribute is unused, bind it to the zero buffer. It's a Metal error for a shader // to read from missing vertex attributes. @@ -431,6 +413,24 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi, } } +MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, + uint32_t vertexCount, uint32_t bufferCount, Handle vbih) + : HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) { +} + +MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize, + uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount), + buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { } + +MetalRenderPrimitive::MetalRenderPrimitive() { +} + +void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi, + MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) { + this->vertexBuffer = vertexBuffer; + this->indexBuffer = indexBuffer; +} + MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept : HwProgram(program.getName()), mContext(context) { diff --git a/filament/backend/src/metal/MetalShaderCompiler.h b/filament/backend/src/metal/MetalShaderCompiler.h index 5d967081e4..f0b827ad6a 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.h +++ b/filament/backend/src/metal/MetalShaderCompiler.h @@ -41,6 +41,11 @@ class MetalShaderCompiler { struct MetalProgramToken; public: + enum class Mode { + SYNCHRONOUS, // synchronous shader compilation + ASYNCHRONOUS // asynchronous shader compilation + }; + class MetalFunctionBundle { public: using Raster = std::tuple, id>; @@ -110,7 +115,7 @@ public: using program_token_t = std::shared_ptr; - explicit MetalShaderCompiler(id device, MetalDriver& driver); + explicit MetalShaderCompiler(id device, MetalDriver& driver, Mode mode); MetalShaderCompiler(MetalShaderCompiler const& rhs) = delete; MetalShaderCompiler(MetalShaderCompiler&& rhs) = delete; @@ -120,15 +125,15 @@ public: void init() noexcept; void terminate() noexcept; - // Creates a program asynchronously + bool isParallelShaderCompileSupported() const noexcept; + + // Creates a program, either synchronously or asynchronously, depending on the Mode + // MetalShaderCompiler was constructed with. program_token_t createProgram(utils::CString const& name, Program&& program); // Returns the functions, blocking if necessary. The Token is destroyed and becomes invalid. MetalFunctionBundle getProgram(program_token_t& token); - // Destroys a valid token and all associated resources. Used to "cancel" a program compilation. - static void terminate(program_token_t& token); - void notifyWhenAllProgramsAreReady( CallbackHandler* handler, CallbackHandler::Callback callback, void* user); @@ -138,6 +143,7 @@ private: CompilerThreadPool mCompilerThreadPool; id mDevice; CallbackManager mCallbackManager; + Mode mMode; }; } // namespace filament::backend diff --git a/filament/backend/src/metal/MetalShaderCompiler.mm b/filament/backend/src/metal/MetalShaderCompiler.mm index 713133b1db..7741ae5448 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.mm +++ b/filament/backend/src/metal/MetalShaderCompiler.mm @@ -70,22 +70,31 @@ struct MetalShaderCompiler::MetalProgramToken : ProgramToken { MetalShaderCompiler::MetalProgramToken::~MetalProgramToken() = default; -MetalShaderCompiler::MetalShaderCompiler(id device, MetalDriver& driver) +MetalShaderCompiler::MetalShaderCompiler(id device, MetalDriver& driver, Mode mode) : mDevice(device), - mCallbackManager(driver) { + mCallbackManager(driver), + mMode(mode) { } void MetalShaderCompiler::init() noexcept { const uint32_t poolSize = 1; - mCompilerThreadPool.init(poolSize, []() {}, []() {}); + if (mMode == Mode::ASYNCHRONOUS) { + mCompilerThreadPool.init(poolSize, []() {}, []() {}); + } } void MetalShaderCompiler::terminate() noexcept { - mCompilerThreadPool.terminate(); + if (mMode == Mode::ASYNCHRONOUS) { + mCompilerThreadPool.terminate(); + } mCallbackManager.terminate(); } +bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept { + return mMode == Mode::ASYNCHRONOUS; +} + /* static */ MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::compileProgram( const Program& program, id device) { std::array, Program::SHADER_TYPE_COUNT> functions = { nil }; @@ -180,14 +189,26 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram( token->handle = mCallbackManager.get(); - CompilerPriorityQueue const priorityQueue = program.getPriorityQueue(); - mCompilerThreadPool.queue(priorityQueue, token, - [this, name, device = mDevice, program = std::move(program), token]() { - MetalFunctionBundle compiledProgram = compileProgram(program, device); + switch (mMode) { + case Mode::ASYNCHRONOUS: { + CompilerPriorityQueue const priorityQueue = program.getPriorityQueue(); + mCompilerThreadPool.queue(priorityQueue, token, + [this, name, device = mDevice, program = std::move(program), token]() { + MetalFunctionBundle compiledProgram = compileProgram(program, device); + token->set(compiledProgram); + mCallbackManager.put(token->handle); + }); - token->set(compiledProgram); - mCallbackManager.put(token->handle); - }); + break; + } + + case Mode::SYNCHRONOUS: { + MetalFunctionBundle compiledProgram = compileProgram(program, mDevice); + token->set(compiledProgram); + mCallbackManager.put(token->handle); + break; + } + } return token; } @@ -195,35 +216,23 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram( MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::getProgram(program_token_t& token) { assert_invariant(token); - if (!token->isReady()) { - auto job = mCompilerThreadPool.dequeue(token); - if (job) { - job(); + if (mMode == Mode::ASYNCHRONOUS) { + if (!token->isReady()) { + auto job = mCompilerThreadPool.dequeue(token); + if (job) { + job(); + } } } + // The job isn't guaranteed to have finished yet. We may have failed to dequeue it above, + // which means it's currently running. In that case get() will block until it finishes. + MetalShaderCompiler::MetalFunctionBundle program = token->get(); - token = nullptr; - return program; } -/* static */ void MetalShaderCompiler::terminate(program_token_t& token) { - assert_invariant(token); - - auto job = token->compiler.mCompilerThreadPool.dequeue(token); - if (!job) { - // The job is being executed right now (or has already executed). - token->wait(); - } else { - // The job has not executed yet. - token->compiler.mCallbackManager.put(token->handle); - } - - token.reset(); -} - void MetalShaderCompiler::notifyWhenAllProgramsAreReady( CallbackHandler* handler, CallbackHandler::Callback callback, void* user) { mCallbackManager.setCallback(handler, callback, user); diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 9f5a7b644d..7a150a3e74 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -350,6 +350,15 @@ void NoopDriver::blit( math::uint2 size) { } +void NoopDriver::bindPipeline(PipelineState pipelineState) { +} + +void NoopDriver::bindRenderPrimitive(Handle rph) { +} + +void NoopDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { +} + void NoopDriver::draw(PipelineState pipelineState, Handle rph, uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index a4a0ea2b3f..cb6958a253 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -94,10 +94,10 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform, // GLSamplerGroup : 16 few // GLSwapChain : 16 few // GLTimerQuery : 16 few - // GLRenderPrimitive : 20 many // GLFence : 24 few - // -- less than or equal 24 bytes + // GLRenderPrimitive : 32 many // GLBufferObject : 32 many + // -- less than or equal 32 bytes // OpenGLProgram : 56 moderate // GLTexture : 64 moderate // -- less than or equal 64 bytes @@ -518,10 +518,12 @@ void OpenGLDriver::createRenderPrimitiveR(Handle rph, GLIndexBuffer const* const ib = handle_cast(ibh); assert_invariant(ib->elementSize == 2 || ib->elementSize == 4); - GLRenderPrimitive* rp = handle_cast(rph); + GLVertexBuffer* const vb = handle_cast(vbh); + GLRenderPrimitive* const rp = handle_cast(rph); rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u; rp->gl.vertexBufferWithObjects = vbh; rp->type = pt; + rp->vbih = vb->vbih; // create a name for this VAO in the current context gl.procs.genVertexArrays(1, &rp->gl.vao[gl.contextIndex]); @@ -3784,39 +3786,42 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel) #endif } -void OpenGLDriver::draw(PipelineState state, Handle rph, - uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { +void OpenGLDriver::bindPipeline(PipelineState state) { DEBUG_MARKER() auto& gl = mContext; - + setRasterState(state.rasterState); + setStencilState(state.stencilState); + gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant); OpenGLProgram* const p = handle_cast(state.program); + mValidProgram = useProgram(p); +} - bool const success = useProgram(p); - if (UTILS_UNLIKELY(!success)) { - // Avoid fatal (or cascading) errors that can occur during the draw call when the program - // is invalid. The shader compile error has already been dumped to the console at this - // point, so it's fine to simply return early. - return; - } +void OpenGLDriver::bindRenderPrimitive(Handle rph) { + DEBUG_MARKER() + auto& gl = mContext; GLRenderPrimitive* const rp = handle_cast(rph); // Gracefully do nothing if the render primitive has not been set up. VertexBufferHandle vb = rp->gl.vertexBufferWithObjects; if (UTILS_UNLIKELY(!vb)) { + mBoundRenderPrimitive = nullptr; return; } - gl.bindVertexArray(&rp->gl); - // If necessary, mutate the bindings in the VAO. + gl.bindVertexArray(&rp->gl); GLVertexBuffer const* const glvb = handle_cast(vb); updateVertexArrayObject(rp, glvb); - setRasterState(state.rasterState); - setStencilState(state.stencilState); + mBoundRenderPrimitive = rp; +} - gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant); +void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { + GLRenderPrimitive const* const rp = mBoundRenderPrimitive; + if (UTILS_UNLIKELY(!rp || !mValidProgram)) { + return; + } if (UTILS_LIKELY(instanceCount <= 1)) { glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(), @@ -3842,6 +3847,17 @@ void OpenGLDriver::scissor(Viewport scissor) { setScissor(scissor); } +void OpenGLDriver::draw(PipelineState state, Handle rph, + uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { + DEBUG_MARKER() + GLRenderPrimitive* const rp = handle_cast(rph); + state.primitiveType = rp->type; + state.vertexBufferInfo = rp->vbih; + bindPipeline(state); + bindRenderPrimitive(rph); + draw2(indexOffset, indexCount, instanceCount); +} + void OpenGLDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) { getShaderCompilerService().tick(); diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index 7ef5bc4f20..ccaddeefbf 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -142,6 +142,7 @@ public: struct GLRenderPrimitive : public HwRenderPrimitive { using HwRenderPrimitive::HwRenderPrimitive; OpenGLContext::RenderPrimitive gl; + Handle vbih; }; struct GLTexture : public HwTexture { @@ -366,6 +367,10 @@ private: GLboolean mRenderPassDepthWrite{}; GLboolean mRenderPassStencilWrite{}; + GLRenderPrimitive const* mBoundRenderPrimitive = nullptr; + bool mValidProgram = false; + + void clearWithRasterPipe(TargetBufferFlags clearFlags, math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept; diff --git a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp index b61bbeab9a..81d390397f 100644 --- a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp +++ b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp @@ -14,21 +14,34 @@ * limitations under the License. */ +#include +#include +#include #include #include "opengl/GLUtils.h" #include "ExternalStreamManagerAndroid.h" #include +#include + +#include +#include +#include #include #include -#include -#include - #include +#include + +#include + +#include +#include +#include + // We require filament to be built with an API 19 toolchain, before that, OpenGLES 3.0 didn't exist // Actually, OpenGL ES 3.0 was added to API 18, but API 19 is the better target and // the minimum for Jetpack at the time of this comment. @@ -165,14 +178,28 @@ int PlatformEGLAndroid::getOSVersion() const noexcept { AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) noexcept { // Convert the AHardwareBuffer to EGLImage. - EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID((const AHardwareBuffer*)source.image); + AHardwareBuffer const* const pHardwareBuffer = (const AHardwareBuffer*)source.image; + + EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(pHardwareBuffer); if (!clientBuffer) { slog.e << "Unable to get EGLClientBuffer from AHardwareBuffer." << io::endl; return {}; } - // Note that this cannot be used to stream protected video (for now) because we do not set EGL_PROTECTED_CONTENT_EXT. - EGLint attrs[] = { EGL_NONE, EGL_NONE }; - EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); + + PlatformEGL::Config attributes; + + if (__builtin_available(android 26, *)) { + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(pHardwareBuffer, &desc); + bool const isProtectedContent = + desc.usage & AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT; + if (isProtectedContent) { + attributes[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE; + } + } + + EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay, + EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attributes.data()); if (eglImage == EGL_NO_IMAGE_KHR) { slog.e << "eglCreateImageKHR returned no image." << io::endl; return {}; diff --git a/filament/backend/src/vulkan/VulkanCommands.cpp b/filament/backend/src/vulkan/VulkanCommands.cpp index f5b8e9029a..21d5944cbd 100644 --- a/filament/backend/src/vulkan/VulkanCommands.cpp +++ b/filament/backend/src/vulkan/VulkanCommands.cpp @@ -435,8 +435,8 @@ void VulkanCommands::popGroupMarker() { auto const [marker, startTime] = mGroupMarkers->pop(); auto const endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration diff = endTime - startTime; - utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n" - << utils::io::flush; + utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms" + << utils::io::endl; #else mGroupMarkers->pop(); #endif diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index a2b62bd34c..5856e39164 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -63,11 +63,12 @@ // Enable the debug utils extension if it is available. #define FVK_DEBUG_DEBUG_UTILS 0x00008000 +#define FVK_DEBUG_RESOURCE_LEAK 0x00010000 + // Usefaul default combinations #define FVK_DEBUG_EVERYTHING 0xFFFFFFFF #define FVK_DEBUG_PERFORMANCE \ - FVK_DEBUG_SYSTRACE | \ - FVK_DEBUG_GROUP_MARKERS + FVK_DEBUG_SYSTRACE #define FVK_DEBUG_CORRECTNESS \ FVK_DEBUG_VALIDATION | \ @@ -80,14 +81,12 @@ FVK_DEBUG_PRINT_GROUP_MARKERS #ifndef NDEBUG -#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_DEBUG_UTILS | FVK_DEBUG_VALIDATION) +#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE) #else #define FVK_DEBUG_FLAGS 0 #endif -#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags)) -#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(flags)) - +#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags)) // Group marker only works only if validation or debug utils is enabled since it uses // vkCmd(Begin/End)DebugUtilsLabelEXT or vkCmdDebugMarker(Begin/End)EXT @@ -107,6 +106,15 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION)); // end dependcy checks +// Shorthand for combination of enabled debug flags +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_TEXTURE) +#define FVK_ENABLED_DEBUG_SAMPLER_NAME 1 +#else +#define FVK_ENABLED_DEBUG_SAMPLER_NAME 0 +#endif + +// end shorthands + #if FVK_ENABLED(FVK_DEBUG_SYSTRACE) #include diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index c1e8f4c0de..05e85a6d48 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -264,26 +264,28 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont #if 0 // this is useful for development, but too verbose even for debug builds // For reference on a 64-bits machine in Release mode: - // VulkanSamplerGroup : 24 few + // VulkanSamplerGroup : 16 few // HwStream : 24 few - // VulkanFence : 40 few - // VulkanProgram : 40 moderate - // VulkanIndexBuffer : 72 moderate - // VulkanBufferObject : 72 many - // -- less than or equal 80 bytes - // VulkanRenderPrimitive : 104 many - // VulkanSwapChain : 112 few - // VulkanTimerQuery : 168 few - // -- less than or equal 176 bytes - // VulkanTexture : 232 moderate - // VulkanVertexBuffer : 312 moderate - // VulkanRenderTarget : 320 few - // -- less than or equal to 320 bytes + // VulkanFence : 32 few + // VulkanProgram : 32 moderate + // VulkanIndexBuffer : 64 moderate + // VulkanBufferObject : 64 many + // -- less than or equal 64 bytes + // VulkanRenderPrimitive : 72 many + // VulkanVertexBufferInfo : 96 moderate + // VulkanSwapChain : 104 few + // VulkanTimerQuery : 160 few + // -- less than or equal 160 bytes + // VulkanVertexBuffer : 192 moderate + // VulkanTexture : 224 moderate + // VulkanRenderTarget : 312 few + // -- less than or equal to 312 bytes utils::slog.d << "\nVulkanSwapChain: " << sizeof(VulkanSwapChain) << "\nVulkanBufferObject: " << sizeof(VulkanBufferObject) << "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer) + << "\nVulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo) << "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer) << "\nVulkanSamplerGroup: " << sizeof(VulkanSamplerGroup) << "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive) @@ -359,6 +361,11 @@ void VulkanDriver::collectGarbage() { mCommands->gc(); mStagePool.gc(); mFramebufferCache.gc(); + +#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) + mResourceAllocator.print(); +#endif + FVK_SYSTRACE_END(); } void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) { @@ -411,9 +418,9 @@ void VulkanDriver::createSamplerGroupR(Handle sbh, uint32_t coun void VulkanDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, PrimitiveType pt) { - auto rp = mResourceAllocator.construct(rph, &mResourceAllocator); + auto rp = mResourceAllocator.construct(rph, + &mResourceAllocator, pt, vbh, ibh); mResourceManager.acquire(rp); - VulkanDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh); } void VulkanDriver::destroyRenderPrimitive(Handle rph) { @@ -982,7 +989,6 @@ void VulkanDriver::setVertexBufferObject(Handle vbh, uint32_t in auto vb = mResourceAllocator.handle_cast(vbh); auto bo = mResourceAllocator.handle_cast(boh); assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX); - vb->setBuffer(mResourceAllocator, bo, index); } @@ -1460,14 +1466,6 @@ void VulkanDriver::nextSubpass(int) { } } -void VulkanDriver::setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt, - Handle vbh, Handle ibh) { - auto primitive = mResourceAllocator.handle_cast(rph); - primitive->setBuffers(mResourceAllocator.handle_cast(vbh), - mResourceAllocator.handle_cast(ibh)); - primitive->setPrimitiveType(pt); -} - void VulkanDriver::makeCurrent(Handle drawSch, Handle readSch) { FVK_SYSTRACE_CONTEXT(); FVK_SYSTRACE_START("makeCurrent"); @@ -1713,14 +1711,13 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers, FVK_SYSTRACE_END(); } -void VulkanDriver::draw(PipelineState pipelineState, Handle rph, - uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { +void VulkanDriver::bindPipeline(PipelineState pipelineState) { FVK_SYSTRACE_CONTEXT(); FVK_SYSTRACE_START("draw"); VulkanCommandBuffer* commands = &mCommands->get(); - VkCommandBuffer cmdbuffer = commands->buffer(); - const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast(rph); + const VulkanVertexBufferInfo& vbi = + *mResourceAllocator.handle_cast(pipelineState.vertexBufferInfo); Handle programHandle = pipelineState.program; RasterState rasterState = pipelineState.rasterState; @@ -1728,15 +1725,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r auto* program = mResourceAllocator.handle_cast(programHandle); commands->acquire(program); - commands->acquire(prim.indexBuffer); - commands->acquire(prim.vertexBuffer); - - // If this is a debug build, validate the current shader. -#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) - if (program->bundle.vertex == VK_NULL_HANDLE || program->bundle.fragment == VK_NULL_HANDLE) { - utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl; - } -#endif // Update the VK raster state. const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; @@ -1762,20 +1750,19 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r .depthBiasSlopeFactor = depthOffset.slope }; + // unfortunately in Vulkan the topology is per pipeline + VkPrimitiveTopology const topology = + VulkanPipelineCache::getPrimitiveTopology(pipelineState.primitiveType); + // Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers. - VulkanVertexBufferInfo const* const vbi = - mResourceAllocator.handle_cast(prim.vertexBuffer->vbih); - uint32_t const bufferCount = vbi->attributes.size(); - VkVertexInputAttributeDescription const* attribDesc = prim.vertexBuffer->getAttribDescriptions(); - VkVertexInputBindingDescription const* bufferDesc = prim.vertexBuffer->getBufferDescriptions(); - VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers(); - VkDeviceSize const* offsets = prim.vertexBuffer->getOffsets(); + VkVertexInputAttributeDescription const* attribDesc = vbi.getAttribDescriptions(); + VkVertexInputBindingDescription const* bufferDesc = vbi.getBufferDescriptions(); // Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls. mPipelineCache.bindProgram(program); mPipelineCache.bindRasterState(vulkanRasterState); - mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology); - mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount); + mPipelineCache.bindPrimitiveTopology(topology); + mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi.getAttributeCount()); // Query the program for the mapping from (SamplerGroupBinding,Offset) to (SamplerBinding), // where "SamplerBinding" is the integer in the GLSL, and SamplerGroupBinding is the abstract @@ -1787,6 +1774,10 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex(); VulkanPipelineCache::UsageFlags usage = program->getUsage(); +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + auto const& bindingToName = program->getBindingToName(); +#endif + UTILS_NOUNROLL for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) { uint16_t const indexPair = bindingToSamplerIndex[binding]; @@ -1819,15 +1810,21 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r // matching characteristics. (e.g. if the missing texture is a 3D texture) if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) { #if FVK_ENABLED(FVK_DEBUG_TEXTURE) - utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'"; + utils::slog.w << "Uninitialized texture bound to '" << bindingToName[binding] << "'"; utils::slog.w << " in material '" << program->name.c_str() << "'"; - utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl; + utils::slog.w << " at binding point " << +binding << utils::io::endl; #endif texture = mEmptyTexture.get(); } SamplerParams const& samplerParams = boundSampler->s; VkSampler const vksampler = mSamplerCache.getSampler(samplerParams); + +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SAMPLER, + reinterpret_cast(vksampler), bindingToName[binding].c_str()); +#endif + VkImageView imageView = VK_NULL_HANDLE; VkImageSubresourceRange const range = texture->getPrimaryViewRange(); if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) && @@ -1851,19 +1848,33 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r mPipelineCache.bindSamplers(samplerInfo, samplerTextures, usage); - // Bind new descriptor sets if they need to change. - // If descriptor set allocation failed, skip the draw call and bail. No need to emit an error - // message since the validation layers already do so. - if (!mPipelineCache.bindDescriptors(cmdbuffer)) { - return; - } - // Bind a new pipeline if the pipeline state changed. // If allocation failed, skip the draw call and bail. We do not emit an error since the // validation layer will already do so. if (!mPipelineCache.bindPipeline(commands)) { return; } + FVK_SYSTRACE_END(); +} + +void VulkanDriver::bindRenderPrimitive(Handle rph) { + FVK_SYSTRACE_CONTEXT(); + FVK_SYSTRACE_START("bindRenderPrimitive"); + + VulkanCommandBuffer* commands = &mCommands->get(); + VkCommandBuffer cmdbuffer = commands->buffer(); + const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast(rph); + commands->acquire(prim.indexBuffer); + commands->acquire(prim.vertexBuffer); + + // This *must* match the VulkanVertexBufferInfo that was bound in bindPipeline(). But we want + // to allow to call this before bindPipeline(), so the validation can only happen in draw() + VulkanVertexBufferInfo const* const vbi = + mResourceAllocator.handle_cast(prim.vertexBuffer->vbih); + + uint32_t const bufferCount = vbi->getAttributeCount(); + VkDeviceSize const* offsets = vbi->getOffsets(); + VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers(); // Next bind the vertex buffers and index buffer. One potential performance improvement is to // avoid rebinding these if they are already bound, but since we do not (yet) support subranges @@ -1872,15 +1883,43 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0, prim.indexBuffer->indexType); + FVK_SYSTRACE_END(); +} + +void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { + FVK_SYSTRACE_CONTEXT(); + FVK_SYSTRACE_START("draw2"); + + VulkanCommandBuffer* commands = &mCommands->get(); + VkCommandBuffer cmdbuffer = commands->buffer(); + + // Bind new descriptor sets if they need to change. + // If descriptor set allocation failed, skip the draw call and bail. No need to emit an error + // message since the validation layers already do so. + if (!mPipelineCache.bindDescriptors(cmdbuffer)) { + return; + } + // Finally, make the actual draw call. TODO: support subranges const uint32_t firstIndex = indexOffset; const int32_t vertexOffset = 0; const uint32_t firstInstId = 0; vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId); + FVK_SYSTRACE_END(); } +void VulkanDriver::draw(PipelineState state, Handle rph, + uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { + VulkanRenderPrimitive* const rp = mResourceAllocator.handle_cast(rph); + state.primitiveType = rp->type; + state.vertexBufferInfo = rp->vertexBuffer->vbih; + bindPipeline(state); + bindRenderPrimitive(rph); + draw2(indexOffset, indexCount, instanceCount); +} + void VulkanDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) { // FIXME: implement me } diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h index 8e311b9bcc..d6398f8cd3 100644 --- a/filament/backend/src/vulkan/VulkanDriver.h +++ b/filament/backend/src/vulkan/VulkanDriver.h @@ -101,10 +101,6 @@ private: VulkanDriver& operator=(VulkanDriver const&) = delete; private: - inline void setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt, - Handle vbh, - Handle ibh); - void collectGarbage(); VulkanPlatform* mPlatform = nullptr; diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 7712115082..48dc56a2b1 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -99,6 +99,10 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept #endif } +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + auto& bindingToName = mInfo->bindingToName; +#endif + auto& groupInfo = builder.getSamplerGroupInfo(); auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex; auto& usage = mInfo->usage; @@ -109,12 +113,16 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept uint32_t const binding = samplers[i].binding; bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i); usage = VulkanPipelineCache::getUsageFlags(binding, group.stageFlags, usage); + +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + bindingToName[binding] = samplers[i].name.c_str(); +#endif } } #if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) - utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex - << ", " << bundle.fragment << ")" << utils::io::endl; + utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << modules[0] + << ", " << modules[1] << ")" << utils::io::endl; #endif } @@ -281,32 +289,21 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) co return count; } -VulkanVertexBufferInfo::VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, - AttributeArray const& attributes) - : HwVertexBufferInfo(bufferCount, attributeCount), - VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO), - attributes(attributes) { -} +VulkanVertexBufferInfo::VulkanVertexBufferInfo( + uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes) + : HwVertexBufferInfo(bufferCount, attributeCount), + VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO), + mInfo(attributes.size()) { -VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, - VulkanResourceAllocator* allocator, uint32_t vertexCount, Handle vbih) - : HwVertexBuffer(vertexCount), - VulkanResource(VulkanResourceType::VERTEX_BUFFER), - vbih(vbih), - mResources(allocator) { + auto attribDesc = mInfo.mSoa.data(); + auto bufferDesc = mInfo.mSoa.data(); + auto offsets = mInfo.mSoa.data(); + auto attribToBufferIndex = mInfo.mSoa.data(); + std::fill(mInfo.mSoa.begin(), + mInfo.mSoa.end(), -1); - VulkanVertexBufferInfo const* const vbi = allocator->handle_cast(vbih); - mInfo = new PipelineInfo(vbi->attributes.size()); - - auto attribDesc = mInfo->mSoa.data(); - auto bufferDesc = mInfo->mSoa.data(); - auto offsets = mInfo->mSoa.data(); - auto attribToBufferIndex = mInfo->mSoa.data(); - std::fill(mInfo->mSoa.begin(), - mInfo->mSoa.end(), -1); - - for (uint32_t attribIndex = 0; attribIndex < vbi->attributes.size(); attribIndex++) { - Attribute attrib = vbi->attributes[attribIndex]; + for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) { + Attribute attrib = attributes[attribIndex]; bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET; bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED; VkFormat vkformat = getVkFormat(attrib.type, isNormalized, isInteger); @@ -318,7 +315,7 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& // expects to receive floats or ints. if (attrib.buffer == Attribute::BUFFER_UNUSED) { vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM; - attrib = vbi->attributes[0]; + attrib = attributes[0]; } offsets[attribIndex] = attrib.offset; attribDesc[attribIndex] = { @@ -334,18 +331,23 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& } } -VulkanVertexBuffer::~VulkanVertexBuffer() { - delete mInfo; +VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, + VulkanResourceAllocator* allocator, + uint32_t vertexCount, Handle vbih) + : HwVertexBuffer(vertexCount), + VulkanResource(VulkanResourceType::VERTEX_BUFFER), + vbih(vbih), + mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here? + mResources(allocator) { } void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator, VulkanBufferObject* bufferObject, uint32_t index) { - VulkanVertexBufferInfo const* const vbi = const_cast(allocator).handle_cast(vbih); - size_t count = vbi->attributes.size(); - auto vkbuffers = mInfo->mSoa.data(); - auto attribToBuffer = mInfo->mSoa.data(); + size_t const count = vbi->getAttributeCount(); + VkBuffer* const vkbuffers = getVkBuffers(); + int8_t const* const attribToBuffer = vbi->getAttributeToBuffer(); for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) { if (attribToBuffer[attribIndex] == static_cast(index)) { vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer(); @@ -361,35 +363,6 @@ VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount), bindingType(bindingType) {} -void VulkanRenderPrimitive::setPrimitiveType(PrimitiveType pt) { - this->type = pt; - switch (pt) { - case PrimitiveType::POINTS: - primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; - break; - case PrimitiveType::LINES: - primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; - break; - case PrimitiveType::LINE_STRIP: - primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; - break; - case PrimitiveType::TRIANGLES: - primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - break; - case PrimitiveType::TRIANGLE_STRIP: - primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; - break; - } -} - -void VulkanRenderPrimitive::setBuffers(VulkanVertexBuffer* vertexBuffer, - VulkanIndexBuffer* indexBuffer) { - this->vertexBuffer = vertexBuffer; - this->indexBuffer = indexBuffer; - mResources.acquire(vertexBuffer); - mResources.acquire(indexBuffer); -} - VulkanTimerQuery::VulkanTimerQuery(std::tuple indices) : VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY), mStartingQueryIndex(std::get<0>(indices)), @@ -421,4 +394,15 @@ bool VulkanTimerQuery::isCompleted() noexcept { VulkanTimerQuery::~VulkanTimerQuery() = default; +VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator, + PrimitiveType pt, Handle vbh, Handle ibh) + : VulkanResource(VulkanResourceType::RENDER_PRIMITIVE), + mResources(resourceAllocator) { + type = pt; + vertexBuffer = resourceAllocator->handle_cast(vbh); + indexBuffer = resourceAllocator->handle_cast(ibh); + mResources.acquire(vertexBuffer); + mResources.acquire(indexBuffer); +} + } // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 9e1d51b537..7c65915d51 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -64,6 +64,12 @@ struct VulkanProgram : public HwProgram, VulkanResource { return mInfo->bindingToSamplerIndex; } +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + inline utils::FixedCapacityVector const& getBindingToName() const { + return mInfo->bindingToName; + } +#endif + private: // TODO: handle compute shaders. // The expected order of shaders - from frontend to backend - is vertex, fragment, compute. @@ -80,6 +86,12 @@ private: // We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit. utils::FixedCapacityVector bindingToSamplerIndex; VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE }; + +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + // We store the sampler name mapped from binding index (only for debug purposes). + utils::FixedCapacityVector bindingToName; +#endif + }; PipelineInfo* mInfo; @@ -132,7 +144,48 @@ struct VulkanBufferObject; struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource { VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes); - AttributeArray attributes; + + inline VkVertexInputAttributeDescription const* getAttribDescriptions() const { + return mInfo.mSoa.data(); + } + + inline VkVertexInputBindingDescription const* getBufferDescriptions() const { + return mInfo.mSoa.data(); + } + + inline int8_t const* getAttributeToBuffer() const { + return mInfo.mSoa.data(); + } + + inline VkDeviceSize const* getOffsets() const { + return mInfo.mSoa.data(); + } + + size_t getAttributeCount() const noexcept { + return mInfo.mSoa.size(); + } + +private: + struct PipelineInfo { + PipelineInfo(size_t size) : mSoa(size /* capacity */) { + mSoa.resize(size); + } + + // These correspond to the index of the element in the SoA + static constexpr uint8_t ATTRIBUTE_DESCRIPTION = 0; + static constexpr uint8_t BUFFER_DESCRIPTION = 1; + static constexpr uint8_t OFFSETS = 2; + static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 3; + + utils::StructureOfArrays< + VkVertexInputAttributeDescription, + VkVertexInputBindingDescription, + VkDeviceSize, + int8_t + > mSoa; + }; + + PipelineInfo mInfo; }; struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource { @@ -140,53 +193,20 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource { VulkanResourceAllocator* allocator, uint32_t vertexCount, Handle vbih); - ~VulkanVertexBuffer(); - void setBuffer(VulkanResourceAllocator const& allocator, VulkanBufferObject* bufferObject, uint32_t index); - inline VkVertexInputAttributeDescription const* getAttribDescriptions() { - return mInfo->mSoa.data(); - } - - inline VkVertexInputBindingDescription const* getBufferDescriptions() { - return mInfo->mSoa.data(); - } - inline VkBuffer const* getVkBuffers() const { - return mInfo->mSoa.data(); + return mBuffers.data(); } - inline VkDeviceSize const* getOffsets() const { - return mInfo->mSoa.data(); + inline VkBuffer* getVkBuffers() { + return mBuffers.data(); } Handle vbih; - private: - struct PipelineInfo { - PipelineInfo(size_t size) - : mSoa(size /* capacity */) { - mSoa.resize(size); - } - - // These correspond to the index of the element in the SoA - static constexpr uint8_t ATTRIBUTE_DESCRIPTION = 0; - static constexpr uint8_t BUFFER_DESCRIPTION = 1; - static constexpr uint8_t VK_BUFFER = 2; - static constexpr uint8_t OFFSETS = 3; - static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 4; - - utils::StructureOfArrays< - VkVertexInputAttributeDescription, - VkVertexInputBindingDescription, - VkBuffer, - VkDeviceSize, - int8_t - > mSoa; - }; - - PipelineInfo* mInfo; + utils::FixedCapacityVector mBuffers; FixedSizeVulkanResourceManager mResources; }; @@ -219,19 +239,15 @@ struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource { }; struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource { - VulkanRenderPrimitive(VulkanResourceAllocator* allocator) - : VulkanResource(VulkanResourceType::RENDER_PRIMITIVE), - mResources(allocator) {} + VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator, + PrimitiveType pt, Handle vbh, Handle ibh); ~VulkanRenderPrimitive() { mResources.clear(); } - void setPrimitiveType(PrimitiveType pt); - void setBuffers(VulkanVertexBuffer* vertexBuffer, VulkanIndexBuffer* indexBuffer); VulkanVertexBuffer* vertexBuffer = nullptr; VulkanIndexBuffer* indexBuffer = nullptr; - VkPrimitiveTopology primitiveTopology; private: // Keep references to the vertex buffer and the index buffer. diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp index 5b96344c0c..2cb474d7bb 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp +++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp @@ -563,6 +563,14 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept { mPipelineRequirements.shaders[0] = program->getVertexShader(); mPipelineRequirements.shaders[1] = program->getFragmentShader(); + + // If this is a debug build, validate the current shader. +#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) + if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE || + mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) { + utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl; + } +#endif } void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept { diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.h b/filament/backend/src/vulkan/VulkanPipelineCache.h index b2acb37ec2..9888c59fb8 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.h +++ b/filament/backend/src/vulkan/VulkanPipelineCache.h @@ -191,10 +191,19 @@ public: mDummyTargetInfo.imageView = imageView; } - // Acquires a resource to be bound to the current pipeline. The ownership of the resource - // will be transferred to the corresponding pipeline when pipeline is bound. - void acquireResource(VulkanResource* resource) { - mPipelineBoundResources.acquire(resource); + static VkPrimitiveTopology getPrimitiveTopology(PrimitiveType pt) noexcept { + switch (pt) { + case PrimitiveType::POINTS: + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + case PrimitiveType::LINES: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case PrimitiveType::LINE_STRIP: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; + case PrimitiveType::TRIANGLES: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + case PrimitiveType::TRIANGLE_STRIP: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + } } private: diff --git a/filament/backend/src/vulkan/VulkanResourceAllocator.h b/filament/backend/src/vulkan/VulkanResourceAllocator.h index 222c7be936..ac71f0ece0 100644 --- a/filament/backend/src/vulkan/VulkanResourceAllocator.h +++ b/filament/backend/src/vulkan/VulkanResourceAllocator.h @@ -17,6 +17,7 @@ #ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H #define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H +#include "VulkanConstants.h" #include "VulkanHandles.h" #include @@ -29,18 +30,17 @@ namespace filament::backend { -// RESOURCE_TYPE_COUNT matches the count of enum VulkanResourceType. -#define RESOURCE_TYPE_COUNT 12 -#define DEBUG_RESOURCE_LEAKS 0 +#define RESOURCE_TYPE_COUNT (static_cast(VulkanResourceType::END_TYPE)) +#define DEBUG_RESOURCE_LEAKS FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) #if DEBUG_RESOURCE_LEAKS - #define TRACK_INCREMENT() \ - if (!IS_MANAGED_TYPE(obj->mType)) { \ - mDebugOnlyResourceCount[static_cast(obj->mType)]++; \ + #define TRACK_INCREMENT() \ + if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \ + mDebugOnlyResourceCount[static_cast(obj->getType())]++; \ } - #define TRACK_DECREMENT() \ - if (!IS_MANAGED_TYPE(obj->mType)) { \ - mDebugOnlyResourceCount[static_cast(obj->mType)]--; \ + #define TRACK_DECREMENT() \ + if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \ + mDebugOnlyResourceCount[static_cast(obj->getType())]--; \ } #else // No-op diff --git a/filament/backend/src/vulkan/VulkanResources.cpp b/filament/backend/src/vulkan/VulkanResources.cpp index bbc8472239..a0bfd79435 100644 --- a/filament/backend/src/vulkan/VulkanResources.cpp +++ b/filament/backend/src/vulkan/VulkanResources.cpp @@ -70,6 +70,7 @@ void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType t // destruction. case VulkanResourceType::FENCE: case VulkanResourceType::HEAP_ALLOCATED: + case VulkanResourceType::END_TYPE: break; } } diff --git a/filament/backend/src/vulkan/VulkanResources.h b/filament/backend/src/vulkan/VulkanResources.h index 8364c232b0..dd40cbb3ab 100644 --- a/filament/backend/src/vulkan/VulkanResources.h +++ b/filament/backend/src/vulkan/VulkanResources.h @@ -51,6 +51,8 @@ enum class VulkanResourceType : uint8_t { // Below are resources that are managed manually (i.e. not ref counted). FENCE, HEAP_ALLOCATED, + + END_TYPE, // A placeholder }; #define IS_HEAP_ALLOC_TYPE(f) \ diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index 16ab0a2ecc..e595f3bec9 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -159,7 +159,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, imageInfo.samples = (VkSampleCountFlagBits) samples; VkResult error = vkCreateImage(mDevice, &imageInfo, VKALLOC, &mTextureImage); - if (error || FVK_ENABLED_BOOL(FVK_DEBUG_TEXTURE)) { + if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) { utils::slog.d << "vkCreateImage: " << "image = " << mTextureImage << ", " << "result = " << error << ", " diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 6eb1916f7a..04d71259b7 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -300,7 +300,7 @@ public: /** * Set to `true` to forcibly disable parallel shader compilation in the backend. - * Currently only honored by the GL backend. + * Currently only honored by the GL and Metal backends. */ bool disableParallelShaderCompile = false; diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 3a1b2b18a3..461acba3a2 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -41,6 +41,7 @@ #include "details/Material.h" #include "details/MaterialInstance.h" #include "details/Texture.h" +#include "details/VertexBuffer.h" #include "generated/resources/materials.h" @@ -190,8 +191,9 @@ std::pair FMaterial* const material = getMaterial(engine); material->prepareProgram(Variant{ variantKey }); return {{ - .program = material->getProgram(Variant{ variantKey }), - .rasterState = material->getRasterState() }, + .program = material->getProgram(Variant{ variantKey }), + .vertexBufferInfo = engine.getFullScreenVertexBuffer()->getVertexBufferInfoHandle(), + .rasterState = material->getRasterState() }, material->getDefaultInstance()->getScissor() }; } diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 016c9976a4..de60680970 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -55,6 +55,7 @@ #include #include #include +#include using namespace utils; using namespace filament::math; @@ -866,6 +867,10 @@ void RenderPass::Executor::execute(FEngine& engine, .polygonOffset = mPolygonOffset, }; + PipelineState currentPipeline{}; + Handle currentPrimitiveHandle{}; + bool rebindPipeline = true; + FMaterialInstance const* UTILS_RESTRICT mi = nullptr; FMaterial const* UTILS_RESTRICT ma = nullptr; auto const* UTILS_RESTRICT pCustomCommands = mCustomCommands.data(); @@ -918,7 +923,9 @@ void RenderPass::Executor::execute(FEngine& engine, // per-renderable uniform PrimitiveInfo const info = first->primitive; pipeline.rasterState = info.rasterState; - //pipeline.vertexBufferInfo = info.vertexBufferInfo; + pipeline.vertexBufferInfo = info.primitive->getVertexBufferInfoHandle(); + pipeline.primitiveType = info.primitive->getPrimitiveType(); + assert_invariant(pipeline.vertexBufferInfo); if (UTILS_UNLIKELY(mi != info.primitive->getMaterialInstance())) { // this is always taken the first time @@ -940,6 +947,11 @@ void RenderPass::Executor::execute(FEngine& engine, } pipeline.stencilState = mi->getStencilState(); mi->use(driver); + + // FIXME: MaterialInstance changed (not necessarily the program though), + // however, texture bindings may have changed and currently we need to + // rebind the pipeline when that happens. + rebindPipeline = true; } assert_invariant(ma); @@ -989,6 +1001,10 @@ void RenderPass::Executor::execute(FEngine& engine, // note: even if only skinning is enabled, binding morphTargetBuffer is needed. driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING, info.morphTargetBuffer); + + // FIXME: Currently we need to rebind the PipelineState when texture or + // UBO binding change. + rebindPipeline = true; } if (UTILS_UNLIKELY(info.morphWeightBuffer)) { @@ -1001,10 +1017,27 @@ void RenderPass::Executor::execute(FEngine& engine, // note: even if only morphing is enabled, binding skinningTexture is needed. driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING, info.skinningTexture); + + // FIXME: Currently we need to rebind the PipelineState when texture or + // UBO binding change. + rebindPipeline = true; } - driver.draw(pipeline, info.primitive->getHwHandle(), - info.primitive->getIndexOffset(), info.primitive->getIndexCount(), + if (rebindPipeline || + (memcmp(&pipeline, ¤tPipeline, sizeof(PipelineState)) != 0)) { + rebindPipeline = false; + currentPipeline = pipeline; + driver.bindPipeline(pipeline); + } + + if (info.primitive->getHwHandle() != currentPrimitiveHandle) { + currentPrimitiveHandle = info.primitive->getHwHandle(); + driver.bindRenderPrimitive(info.primitive->getHwHandle()); + } + + driver.draw2( + info.primitive->getIndexOffset(), + info.primitive->getIndexCount(), instanceCount); } } diff --git a/filament/src/RenderPrimitive.cpp b/filament/src/RenderPrimitive.cpp index 6a523b8b6e..8bd5e5ad57 100644 --- a/filament/src/RenderPrimitive.cpp +++ b/filament/src/RenderPrimitive.cpp @@ -16,14 +16,18 @@ #include "RenderPrimitive.h" -#include "details/Engine.h" +#include +#include + #include "details/IndexBuffer.h" -#include "details/Material.h" +#include "details/MaterialInstance.h" #include "details/VertexBuffer.h" +#include +#include + #include -#include #include namespace filament { @@ -39,18 +43,7 @@ void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverAp if (entry.indices && entry.vertices) { FVertexBuffer* vertexBuffer = downcast(entry.vertices); FIndexBuffer* indexBuffer = downcast(entry.indices); - - AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes(); - - auto const& ebh = vertexBuffer->getHwHandle(); - auto const& ibh = indexBuffer->getHwHandle(); - - mHandle = factory.create(driver, ebh, ibh, entry.type); - - mPrimitiveType = entry.type; - mIndexOffset = entry.offset; - mIndexCount = entry.count; - mEnabledAttributes = enabledAttributes; + set(factory, driver, entry.type, vertexBuffer, indexBuffer, entry.offset, entry.count); } } @@ -62,17 +55,19 @@ void FRenderPrimitive::terminate(HwRenderPrimitiveFactory& factory, backend::Dri void FRenderPrimitive::set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver, RenderableManager::PrimitiveType type, - FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset, size_t count) noexcept { + FVertexBuffer* vertexBuffer, FIndexBuffer* indexBuffer, + size_t offset, size_t count) noexcept { if (mHandle) { factory.destroy(driver, mHandle); } - AttributeBitset const enabledAttributes = vertices->getDeclaredAttributes(); + AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes(); - auto const& ebh = vertices->getHwHandle(); - auto const& ibh = indices->getHwHandle(); + auto const& ebh = vertexBuffer->getHwHandle(); + auto const& ibh = indexBuffer->getHwHandle(); mHandle = factory.create(driver, ebh, ibh, type); + mVertexBufferInfoHandle = vertexBuffer->getVertexBufferInfoHandle(); mPrimitiveType = type; mIndexOffset = offset; diff --git a/filament/src/RenderPrimitive.h b/filament/src/RenderPrimitive.h index bc1c897484..07d7431f02 100644 --- a/filament/src/RenderPrimitive.h +++ b/filament/src/RenderPrimitive.h @@ -45,14 +45,15 @@ public: void set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver, RenderableManager::PrimitiveType type, - FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset, + FVertexBuffer* vertexBuffer, FIndexBuffer* indexBuffer, size_t offset, size_t count) noexcept; // frees driver resources, object becomes invalid void terminate(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver); const FMaterialInstance* getMaterialInstance() const noexcept { return mMaterialInstance; } - backend::Handle getHwHandle() const noexcept { return mHandle; } + backend::RenderPrimitiveHandle getHwHandle() const noexcept { return mHandle; } + backend::VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; } uint32_t getIndexOffset() const noexcept { return mIndexOffset; } uint32_t getIndexCount() const noexcept { return mIndexCount; } @@ -76,7 +77,7 @@ private: struct { FMaterialInstance const* mMaterialInstance = nullptr; backend::Handle mHandle = {}; - UTILS_UNUSED uint8_t padding[4]= {}; + backend::Handle mVertexBufferInfoHandle = {}; uint32_t mIndexOffset = 0; uint32_t mIndexCount = 0; }; diff --git a/filament/src/details/VertexBuffer.h b/filament/src/details/VertexBuffer.h index 56c5f5bb61..759c7fe019 100644 --- a/filament/src/details/VertexBuffer.h +++ b/filament/src/details/VertexBuffer.h @@ -52,6 +52,8 @@ public: VertexBufferHandle getHwHandle() const noexcept { return mHandle; } + VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; } + size_t getVertexCount() const noexcept; AttributeBitset getDeclaredAttributes() const noexcept { diff --git a/filament/src/materials/defaultMaterial0.mat b/filament/src/materials/defaultMaterial0.mat deleted file mode 100644 index 914d29c3d1..0000000000 --- a/filament/src/materials/defaultMaterial0.mat +++ /dev/null @@ -1,12 +0,0 @@ -material { - name : "Filament Default Material", - shadingModel : unlit, - featureLevel: 0 -} - -fragment { - void material(inout MaterialInputs material) { - prepareMaterial(material); - material.baseColor.rgb = vec3(0.8); - } -} diff --git a/filament/src/materials/skybox.mat b/filament/src/materials/skybox.mat index 1d36108a27..d692fc238c 100644 --- a/filament/src/materials/skybox.mat +++ b/filament/src/materials/skybox.mat @@ -35,10 +35,14 @@ fragment { if (materialParams.constantColor != 0) { sky = materialParams.color; } else { +#if MATERIAL_FEATURE_LEVEL == 0 + sky = vec4(textureCube(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0); +#else sky = vec4(textureLod(materialParams_skybox, variable_eyeDirection.xyz, 0.0).rgb, 1.0); +#endif sky.rgb *= frameUniforms.iblLuminance; } - if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0f) { + if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0) { vec3 direction = normalize(variable_eyeDirection.xyz); // Assume the sun is a sphere vec3 sun = frameUniforms.lightColorIntensity.rgb * diff --git a/filament/src/materials/skybox0.mat b/filament/src/materials/skybox0.mat deleted file mode 100644 index 2891e32f4b..0000000000 --- a/filament/src/materials/skybox0.mat +++ /dev/null @@ -1,60 +0,0 @@ -material { - name : Skybox, - parameters : [ - { - type : int, - name : showSun - }, - { - type : int, - name : constantColor - }, - { - type : samplerCubemap, - name : skybox - }, - { - type : float4, - name : color - } - ], - variables : [ - eyeDirection - ], - vertexDomain : device, - depthWrite : false, - shadingModel : unlit, - variantFilter : [ skinning, shadowReceiver, vsm ], - culling: none, - featureLevel: 0 -} - -fragment { - void material(inout MaterialInputs material) { - prepareMaterial(material); - vec4 sky; - if (materialParams.constantColor != 0) { - sky = materialParams.color; - } else { - sky = vec4(textureCube(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0); - sky.rgb *= frameUniforms.iblLuminance; - } - if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0) { - vec3 direction = normalize(variable_eyeDirection.xyz); - // Assume the sun is a sphere - vec3 sun = frameUniforms.lightColorIntensity.rgb * - (frameUniforms.lightColorIntensity.a * (4.0 * PI)); - float cosAngle = dot(direction, frameUniforms.lightDirection); - float x = (cosAngle - frameUniforms.sun.x) * frameUniforms.sun.z; - float gradient = pow(1.0 - saturate(x), frameUniforms.sun.w); - sky.rgb = sky.rgb + gradient * sun; - } - material.baseColor = sky; - } -} - -vertex { - void materialVertex(inout MaterialVertexInputs material) { - material.eyeDirection.xyz = material.worldPosition.xyz; - } -} diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index e92cedb1b4..513d6c1975 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.50.6" + spec.version = "1.51.0" 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.50.6/filament-v1.50.6-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.0/filament-v1.51.0-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/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index f98c707e1c..ebe2ccce20 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -28,7 +28,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 50; +static constexpr size_t MATERIAL_VERSION = 51; /** * Supported shading models @@ -201,7 +201,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 = 26; +static constexpr size_t MATERIAL_PROPERTIES_COUNT = 27; enum class Property : uint8_t { BASE_COLOR, //!< float4, all shading models ROUGHNESS, //!< float, lit shading models only @@ -223,6 +223,7 @@ enum class Property : uint8_t { EMISSIVE, //!< float4, all shading models NORMAL, //!< float3, all shading models only, except unlit POST_LIGHTING_COLOR, //!< float4, all shading models + POST_LIGHTING_MIX_FACTOR,//!< float, all shading models CLIP_SPACE_TRANSFORM, //!< mat4, vertex shader only ABSORPTION, //!< float3, how much light is absorbed by the material TRANSMISSION, //!< float, how much light is refracted through the material diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 541153112c..2e2d096309 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -410,7 +410,8 @@ public: /** * Set the blending mode of the post-lighting color for this material. * Only OPAQUE, TRANSPARENT and ADD are supported, the default is TRANSPARENT. - * This setting requires the material property "postLightingColor" to be set. + * This setting requires the material properties "postLightingColor" and + * "postLightingMixFactor" to be set. */ MaterialBuilder& postLightingBlending(BlendingMode blending) noexcept; diff --git a/libs/filamat/src/Enums.cpp b/libs/filamat/src/Enums.cpp index 01c4011c7e..583d376f95 100644 --- a/libs/filamat/src/Enums.cpp +++ b/libs/filamat/src/Enums.cpp @@ -21,32 +21,33 @@ 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 }, - { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM }, - { "absorption", Property::ABSORPTION }, - { "transmission", Property::TRANSMISSION }, - { "ior", Property::IOR }, - { "microThickness", Property::MICRO_THICKNESS }, - { "bentNormal", Property::BENT_NORMAL }, + { "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 }, }; template <> diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index c242eee6d5..13c6bd0768 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -1115,32 +1115,33 @@ io::sstream& CodeGenerator::generateShaderReflections(utils::io::sstream& out, S char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) noexcept { using Property = MaterialBuilder::Property; switch (property) { - case Property::BASE_COLOR: return "BASE_COLOR"; - case Property::ROUGHNESS: return "ROUGHNESS"; - case Property::METALLIC: return "METALLIC"; - case Property::REFLECTANCE: return "REFLECTANCE"; - case Property::AMBIENT_OCCLUSION: return "AMBIENT_OCCLUSION"; - case Property::CLEAR_COAT: return "CLEAR_COAT"; - case Property::CLEAR_COAT_ROUGHNESS: return "CLEAR_COAT_ROUGHNESS"; - case Property::CLEAR_COAT_NORMAL: return "CLEAR_COAT_NORMAL"; - case Property::ANISOTROPY: return "ANISOTROPY"; - case Property::ANISOTROPY_DIRECTION: return "ANISOTROPY_DIRECTION"; - case Property::THICKNESS: return "THICKNESS"; - case Property::SUBSURFACE_POWER: return "SUBSURFACE_POWER"; - case Property::SUBSURFACE_COLOR: return "SUBSURFACE_COLOR"; - case Property::SHEEN_COLOR: return "SHEEN_COLOR"; - case Property::SHEEN_ROUGHNESS: return "SHEEN_ROUGHNESS"; - case Property::GLOSSINESS: return "GLOSSINESS"; - case Property::SPECULAR_COLOR: return "SPECULAR_COLOR"; - case Property::EMISSIVE: return "EMISSIVE"; - case Property::NORMAL: return "NORMAL"; - case Property::POST_LIGHTING_COLOR: return "POST_LIGHTING_COLOR"; - case Property::CLIP_SPACE_TRANSFORM: return "CLIP_SPACE_TRANSFORM"; - case Property::ABSORPTION: return "ABSORPTION"; - case Property::TRANSMISSION: return "TRANSMISSION"; - case Property::IOR: return "IOR"; - case Property::MICRO_THICKNESS: return "MICRO_THICKNESS"; - case Property::BENT_NORMAL: return "BENT_NORMAL"; + case Property::BASE_COLOR: return "BASE_COLOR"; + case Property::ROUGHNESS: return "ROUGHNESS"; + case Property::METALLIC: return "METALLIC"; + case Property::REFLECTANCE: return "REFLECTANCE"; + case Property::AMBIENT_OCCLUSION: return "AMBIENT_OCCLUSION"; + case Property::CLEAR_COAT: return "CLEAR_COAT"; + case Property::CLEAR_COAT_ROUGHNESS: return "CLEAR_COAT_ROUGHNESS"; + case Property::CLEAR_COAT_NORMAL: return "CLEAR_COAT_NORMAL"; + case Property::ANISOTROPY: return "ANISOTROPY"; + case Property::ANISOTROPY_DIRECTION: return "ANISOTROPY_DIRECTION"; + case Property::THICKNESS: return "THICKNESS"; + case Property::SUBSURFACE_POWER: return "SUBSURFACE_POWER"; + case Property::SUBSURFACE_COLOR: return "SUBSURFACE_COLOR"; + case Property::SHEEN_COLOR: return "SHEEN_COLOR"; + case Property::SHEEN_ROUGHNESS: return "SHEEN_ROUGHNESS"; + case Property::GLOSSINESS: return "GLOSSINESS"; + case Property::SPECULAR_COLOR: return "SPECULAR_COLOR"; + case Property::EMISSIVE: return "EMISSIVE"; + case Property::NORMAL: return "NORMAL"; + case Property::POST_LIGHTING_COLOR: return "POST_LIGHTING_COLOR"; + case Property::POST_LIGHTING_MIX_FACTOR: return "POST_LIGHTING_MIX_FACTOR"; + case Property::CLIP_SPACE_TRANSFORM: return "CLIP_SPACE_TRANSFORM"; + case Property::ABSORPTION: return "ABSORPTION"; + case Property::TRANSMISSION: return "TRANSMISSION"; + case Property::IOR: return "IOR"; + case Property::MICRO_THICKNESS: return "MICRO_THICKNESS"; + case Property::BENT_NORMAL: return "BENT_NORMAL"; } } diff --git a/libs/filamat/tests/test_filamat.cpp b/libs/filamat/tests/test_filamat.cpp index 91e4a9adeb..1d63fc15dc 100644 --- a/libs/filamat/tests/test_filamat.cpp +++ b/libs/filamat/tests/test_filamat.cpp @@ -701,6 +701,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) { void material(inout MaterialInputs material) { prepareMaterial(material); material.postLightingColor = vec4(1.0); + material.postLightingMixFactor = 0.5; } )"); @@ -712,6 +713,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) { glslTools.findProperties(ShaderStage::FRAGMENT, shaderCode, properties); MaterialBuilder::PropertyList expected{ false }; expected[size_t(filamat::MaterialBuilder::Property::POST_LIGHTING_COLOR)] = true; + expected[size_t(filamat::MaterialBuilder::Property::POST_LIGHTING_MIX_FACTOR)] = true; EXPECT_TRUE(PropertyListsMatch(expected, properties)); } diff --git a/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h b/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h index 815ff613ff..903c42589f 100644 --- a/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h +++ b/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h @@ -88,12 +88,23 @@ public: */ class EquirectangularToCubemap { public: + + struct Config { + bool mirror = true; //!< mirror the source horizontally + }; + /** - * Creates a EquirectangularToCubemap processor. + * Creates a EquirectangularToCubemap processor using the default Config * @param context IBLPrefilterContext to use */ explicit EquirectangularToCubemap(IBLPrefilterContext& context); + /** + * Creates a EquirectangularToCubemap processor using the provided Config + * @param context IBLPrefilterContext to use + */ + EquirectangularToCubemap(IBLPrefilterContext& context, Config const& config); + /** * Destroys all GPU resources created during initialization. */ @@ -125,6 +136,7 @@ public: private: IBLPrefilterContext& mContext; filament::Material* mEquirectMaterial = nullptr; + Config mConfig{}; }; /** diff --git a/libs/iblprefilter/src/IBLPrefilterContext.cpp b/libs/iblprefilter/src/IBLPrefilterContext.cpp index fc66e250eb..33705b140d 100644 --- a/libs/iblprefilter/src/IBLPrefilterContext.cpp +++ b/libs/iblprefilter/src/IBLPrefilterContext.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -29,12 +30,22 @@ #include #include -#include +#include + +#include #include +#include #include -#include -#include +#include +#include + +#include +#include +#include + +#include +#include #include "generated/resources/iblprefilter_materials.h" @@ -167,13 +178,19 @@ IBLPrefilterContext& IBLPrefilterContext::operator=(IBLPrefilterContext&& rhs) n // ------------------------------------------------------------------------------------------------ IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap( - IBLPrefilterContext& context) : mContext(context) { + IBLPrefilterContext& context, + IBLPrefilterContext::EquirectangularToCubemap::Config const& config) + : mContext(context), mConfig(config) { Engine& engine = mContext.mEngine; mEquirectMaterial = Material::Builder().package( IBLPREFILTER_MATERIALS_EQUIRECTTOCUBE_DATA, IBLPREFILTER_MATERIALS_EQUIRECTTOCUBE_SIZE).build(engine); } +IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap( + IBLPrefilterContext& context) : EquirectangularToCubemap(context, {}) { +} + IBLPrefilterContext::EquirectangularToCubemap::~EquirectangularToCubemap() noexcept { Engine& engine = mContext.mEngine; engine.destroy(mEquirectMaterial); @@ -257,6 +274,8 @@ Texture* IBLPrefilterContext::EquirectangularToCubemap::operator()( .texture(RenderTarget::AttachmentPoint::COLOR1, outCube) .texture(RenderTarget::AttachmentPoint::COLOR2, outCube); + mi->setParameter("mirror", mConfig.mirror ? -1.0f : 1.0f); + for (size_t i = 0; i < 2; i++) { mi->setParameter("side", i == 0 ? 1.0f : -1.0f); diff --git a/libs/iblprefilter/src/materials/equirectToCube.mat b/libs/iblprefilter/src/materials/equirectToCube.mat index b3b9bee9fb..eb147998ad 100644 --- a/libs/iblprefilter/src/materials/equirectToCube.mat +++ b/libs/iblprefilter/src/materials/equirectToCube.mat @@ -10,6 +10,11 @@ material { type : float, name : side, precision: medium + }, + { + type : float, + name : mirror, + precision: medium } ], outputs : [ @@ -51,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 = (1.0 - yf) * 0.5; // range [0, 1.0] + yf = (yf + 1.0) * 0.5; // range [0, 1.0] return vec2(xf, yf); } @@ -62,18 +67,16 @@ 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 = vec2( - uv.x * 2.0 - 1.0, - 1.0 - uv.y * 2.0); + highp vec2 p = uv * 2.0 - 1.0; float side = materialParams.side; + float mirror = materialParams.mirror; highp float l = inversesqrt(p.x * p.x + p.y * p.y + 1.0); // compute the view (and normal, since v = n) direction for each face - highp vec3 rx = vec3( side, p.y, side * -p.x); - highp vec3 ry = vec3( p.x, side, side * -p.y); - highp vec3 rz = vec3(side * p.x, p.y, side); - + highp vec3 rx = vec3( side * mirror, p.y, side * -p.x); + highp vec3 ry = vec3( p.x * mirror, side, side * -p.y); + highp vec3 rz = vec3(side * p.x * mirror, p.y, side); postProcess.outx = sampleEquirect(materialParams_equirect, rx * l); postProcess.outy = sampleEquirect(materialParams_equirect, ry * l); diff --git a/libs/iblprefilter/src/materials/iblprefilter.mat b/libs/iblprefilter/src/materials/iblprefilter.mat index d9fa379a8e..aa0c25c1d6 100644 --- a/libs/iblprefilter/src/materials/iblprefilter.mat +++ b/libs/iblprefilter/src/materials/iblprefilter.mat @@ -122,9 +122,10 @@ void postProcess(inout PostProcessInputs postProcess) { float side = materialParams.side; // compute the view (and normal, since v = n) direction for each face - vec3 rx = normalize(vec3( side, -p.y, side * -p.x)); - vec3 ry = normalize(vec3( p.x, side, side * p.y)); - vec3 rz = normalize(vec3(side * p.x, -p.y, side)); + float l = inversesqrt(p.x * p.x + p.y * p.y + 1.0); + vec3 rx = vec3( side, p.y, side * -p.x) * l; + vec3 ry = vec3( p.x, side, side * -p.y) * l; + vec3 rz = vec3(side * p.x, p.y, side) * l; // random rotation around r mediump float a = 2.0 * PI * random(gl_FragCoord.xy); diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 00a17efee6..9b7804e94f 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -222,6 +222,12 @@ function(add_demo NAME) target_link_libraries(${NAME} PRIVATE sample-resources filamentapp) target_compile_options(${NAME} PRIVATE ${COMPILER_FLAGS}) set_target_properties(${NAME} PROPERTIES FOLDER Samples) + + # This is needed after XCode 15.3 + if (APPLE) + set_target_properties(${NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) + set_target_properties(${NAME} PROPERTIES INSTALL_RPATH /usr/local/lib) + endif() endfunction() if (NOT ANDROID) diff --git a/shaders/src/main.fs b/shaders/src/main.fs index 375960515e..1bc74372c1 100644 --- a/shaders/src/main.fs +++ b/shaders/src/main.fs @@ -6,17 +6,19 @@ layout(location = 0) out vec4 fragColor; #if defined(MATERIAL_HAS_POST_LIGHTING_COLOR) void blendPostLightingColor(const MaterialInputs material, inout vec4 color) { + vec4 blend = color; #if defined(POST_LIGHTING_BLEND_MODE_OPAQUE) - color = material.postLightingColor; + blend = material.postLightingColor; #elif defined(POST_LIGHTING_BLEND_MODE_TRANSPARENT) - color = material.postLightingColor + color * (1.0 - material.postLightingColor.a); + blend = material.postLightingColor + color * (1.0 - material.postLightingColor.a); #elif defined(POST_LIGHTING_BLEND_MODE_ADD) - color += material.postLightingColor; + blend += material.postLightingColor; #elif defined(POST_LIGHTING_BLEND_MODE_MULTIPLY) - color *= material.postLightingColor; + blend *= material.postLightingColor; #elif defined(POST_LIGHTING_BLEND_MODE_SCREEN) - color += material.postLightingColor * (1.0 - color); + blend += material.postLightingColor * (1.0 - color); #endif + color = mix(color, blend, material.postLightingMixFactor); } #endif diff --git a/shaders/src/material_inputs.fs b/shaders/src/material_inputs.fs index 3844df9f2b..53d4ade1ec 100644 --- a/shaders/src/material_inputs.fs +++ b/shaders/src/material_inputs.fs @@ -66,6 +66,7 @@ struct MaterialInputs { #if defined(MATERIAL_HAS_POST_LIGHTING_COLOR) vec4 postLightingColor; + float postLightingMixFactor; #endif #if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) && !defined(SHADING_MODEL_UNLIT) @@ -153,6 +154,7 @@ void initMaterial(out MaterialInputs material) { #if defined(MATERIAL_HAS_POST_LIGHTING_COLOR) material.postLightingColor = vec4(0.0); + material.postLightingMixFactor = 1.0; #endif #if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) && !defined(SHADING_MODEL_UNLIT) diff --git a/tools/matc/src/matc/ParametersProcessor.cpp b/tools/matc/src/matc/ParametersProcessor.cpp index 498ea75a35..77a74fa9f9 100644 --- a/tools/matc/src/matc/ParametersProcessor.cpp +++ b/tools/matc/src/matc/ParametersProcessor.cpp @@ -1283,6 +1283,11 @@ bool ParametersProcessor::process(filamat::MaterialBuilder& builder, const std:: case JsonishValue::Type::STRING: var = std::make_unique(value); break; + default: + std::cerr << "Unsupported type: \"" + << JsonishValue::typeToString(mParameters.at(key).rootAssert) + << "\"" << std::endl; + return false; } auto fPointer = mParameters[key].callback; diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 582f75eec4..2b2c4d8af5 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.50.6", + "version": "1.51.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",