From 4e6ae2b71412e4412470631ef96f7e70cc0778f5 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Wed, 14 Feb 2024 04:40:30 +0000 Subject: [PATCH 01/33] Add stereoscopic type to Engine::Config (#7574) * Add stereoscopic type to Engine::Config This new type value will determine the algorithm used when stereoscopic rendering is enabled. --- .../filament-android/src/main/cpp/Engine.cpp | 3 +- .../com/google/android/filament/Engine.java | 28 +++++++++++++++++-- .../backend/include/backend/DriverEnums.h | 8 ++++++ filament/include/filament/Engine.h | 14 ++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index 05893cbd5b..9a2180e022 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -484,7 +484,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderConfig(JNIEnv*, jclass, jlong nativeBuilder, jlong commandBufferSizeMB, jlong perRenderPassArenaSizeMB, jlong driverHandleArenaSizeMB, jlong minCommandBufferSizeMB, jlong perFrameCommandsSizeMB, - jlong jobSystemThreadCount, jlong stereoscopicEyeCount, + jlong jobSystemThreadCount, jint stereoscopicType, jlong stereoscopicEyeCount, jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge) { Engine::Builder* builder = (Engine::Builder*) nativeBuilder; Engine::Config config = { @@ -494,6 +494,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu .minCommandBufferSizeMB = (uint32_t) minCommandBufferSizeMB, .perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB, .jobSystemThreadCount = (uint32_t) jobSystemThreadCount, + .stereoscopicType = (Engine::StereoscopicType) stereoscopicType, .stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount, .resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB, .resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge, 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 6b4647e2ac..393b711ac9 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 @@ -158,6 +158,16 @@ public class Engine { FEATURE_LEVEL_3, }; + /** + * The type of technique for stereoscopic rendering + */ + public enum StereoscopicType { + /** Stereoscopic rendering is performed using instanced rendering technique. */ + INSTANCED, + /** Stereoscopic rendering is performed using the multiview feature from the graphics backend. */ + MULTIVIEW, + }; + /** * Constructs Engine objects using a builder pattern. */ @@ -211,7 +221,8 @@ public class Engine { nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB, config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB, config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB, - config.jobSystemThreadCount, config.stereoscopicEyeCount, + config.jobSystemThreadCount, + config.stereoscopicType.ordinal(), config.stereoscopicEyeCount, config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge); return this; } @@ -349,6 +360,19 @@ public class Engine { */ public long jobSystemThreadCount = 0; + /** + * The type of technique for stereoscopic rendering. + * + * This setting determines the algorithm used when stereoscopic rendering is enabled. This + * decision applies to the entire Engine for the lifetime of the Engine. E.g., multiple + * Views created from the Engine must use the same stereoscopic type. + * + * Each view can enable stereoscopic rendering via the StereoscopicOptions::enable flag. + * + * @see View#setStereoscopicOptions + */ + public StereoscopicType stereoscopicType = StereoscopicType.INSTANCED; + /** * The number of eyes to render when stereoscopic rendering is enabled. Supported values are * between 1 and Engine#getMaxStereoscopicEyes() (inclusive). @@ -1240,7 +1264,7 @@ public class Engine { private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB, long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB, long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount, - long stereoscopicEyeCount, + int stereoscopicType, long stereoscopicEyeCount, long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge); private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal); private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext); diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index aba2b40414..c41d1b8304 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -1212,6 +1212,14 @@ enum class Workaround : uint16_t { DISABLE_THREAD_AFFINITY }; +//! The type of technique for stereoscopic rendering +enum class StereoscopicType : uint8_t { + // Stereoscopic rendering is performed using instanced rendering technique. + INSTANCED, + // Stereoscopic rendering is performed using the multiview feature from the graphics backend. + MULTIVIEW, +}; + } // namespace filament::backend template<> struct utils::EnableBitMaskOperators diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 904cbda4a3..90cff6283d 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -178,6 +178,7 @@ public: using Backend = backend::Backend; using DriverConfig = backend::Platform::DriverConfig; using FeatureLevel = backend::FeatureLevel; + using StereoscopicType = backend::StereoscopicType; /** * Config is used to define the memory footprint used by the engine, such as the @@ -297,6 +298,19 @@ public: */ size_t textureUseAfterFreePoolSize = 0; + /* + * The type of technique for stereoscopic rendering. + * + * This setting determines the algorithm used when stereoscopic rendering is enabled. This + * decision applies to the entire Engine for the lifetime of the Engine. E.g., multiple + * Views created from the Engine must use the same stereoscopic type. + * + * Each view can enable stereoscopic rendering via the StereoscopicOptions::enable flag. + * + * @see View::setStereoscopicOptions + */ + StereoscopicType stereoscopicType = StereoscopicType::INSTANCED; + /* * The number of eyes to render when stereoscopic rendering is enabled. Supported values are * between 1 and Engine::getMaxStereoscopicEyes() (inclusive). From 5707043d962bfc1d03e96794005572c901a7643e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 12 Feb 2024 17:31:17 -0800 Subject: [PATCH 02/33] Modernize HwRenderPrimitive and draw() APIs HwRenderPrimitive doesn't need to know about the index offset and index count, these parameters are only needed when drawing. draw() is updated consequently. This is a first step towards being able to lower the overhead of draw similar draw calls. A side effect of this is that the HwRenderPrimitiveFactory now will cache buffers regardless of their index count & offset. --- .../include/private/backend/DriverAPI.inc | 8 ++--- filament/backend/src/DriverBase.h | 5 ---- filament/backend/src/metal/MetalDriver.h | 5 +--- filament/backend/src/metal/MetalDriver.mm | 25 +++++----------- filament/backend/src/noop/NoopDriver.cpp | 2 +- filament/backend/src/opengl/OpenGLDriver.cpp | 20 +++++-------- filament/backend/src/vulkan/VulkanDriver.cpp | 25 ++++------------ filament/backend/src/vulkan/VulkanDriver.h | 6 ++-- filament/backend/test/BackendTest.cpp | 2 +- filament/backend/test/TrianglePrimitive.cpp | 2 +- filament/backend/test/test_Blit.cpp | 2 +- filament/backend/test/test_BufferUpdates.cpp | 2 +- filament/backend/test/test_FeedbackLoops.cpp | 4 +-- filament/backend/test/test_MRT.cpp | 2 +- filament/backend/test/test_MipLevels.cpp | 6 ++-- .../test/test_MissingRequiredAttributes.cpp | 2 +- filament/backend/test/test_ReadPixels.cpp | 4 +-- .../backend/test/test_RenderExternalImage.cpp | 4 +-- filament/backend/test/test_Scissor.cpp | 6 ++-- filament/backend/test/test_StencilBuffer.cpp | 8 ++--- filament/include/filament/RenderableManager.h | 2 -- filament/src/HwRenderPrimitiveFactory.cpp | 20 ++++--------- filament/src/HwRenderPrimitiveFactory.h | 10 ++----- filament/src/PostProcessManager.cpp | 14 ++++----- filament/src/RenderPass.cpp | 9 +++++- filament/src/RenderPass.h | 5 ++-- filament/src/RenderPrimitive.cpp | 29 ++++++++++--------- filament/src/RenderPrimitive.h | 12 ++++++-- filament/src/components/RenderableManager.cpp | 11 ++----- filament/src/details/Engine.cpp | 2 +- 30 files changed, 104 insertions(+), 150 deletions(-) diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 37fcb6e2ea..47e7320b29 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -224,11 +224,7 @@ DECL_DRIVER_API_R_N(backend::SamplerGroupHandle, createSamplerGroup, DECL_DRIVER_API_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive, backend::VertexBufferHandle, vbh, backend::IndexBufferHandle, ibh, - backend::PrimitiveType, pt, - uint32_t, offset, - uint32_t, minIndex, - uint32_t, maxIndex, - uint32_t, count) + backend::PrimitiveType, pt) DECL_DRIVER_API_R_N(backend::ProgramHandle, createProgram, backend::Program&&, program) @@ -494,6 +490,8 @@ DECL_DRIVER_API_N(blit, DECL_DRIVER_API_N(draw, backend::PipelineState, state, backend::RenderPrimitiveHandle, rph, + uint32_t, indexOffset, + uint32_t, indexCount, uint32_t, instanceCount) DECL_DRIVER_API_N(dispatchCompute, diff --git a/filament/backend/src/DriverBase.h b/filament/backend/src/DriverBase.h index abf6890116..3887ceb9b6 100644 --- a/filament/backend/src/DriverBase.h +++ b/filament/backend/src/DriverBase.h @@ -88,11 +88,6 @@ struct HwIndexBuffer : public HwBase { }; struct HwRenderPrimitive : public HwBase { - uint32_t offset{}; - uint32_t minIndex{}; - uint32_t maxIndex{}; - uint32_t count{}; - uint32_t maxVertexCount{}; PrimitiveType type = PrimitiveType::TRIANGLES; }; diff --git a/filament/backend/src/metal/MetalDriver.h b/filament/backend/src/metal/MetalDriver.h index 036c89db12..51cd7902dd 100644 --- a/filament/backend/src/metal/MetalDriver.h +++ b/filament/backend/src/metal/MetalDriver.h @@ -133,12 +133,9 @@ private: mHandleAllocator.deallocate(handle, p); } - inline void setRenderPrimitiveBuffer(Handle rph, + inline void setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt, Handle vbh, Handle ibh); - inline void setRenderPrimitiveRange(Handle rph, PrimitiveType pt, - uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count); - void finalizeSamplerGroup(MetalSamplerGroup* sg); void enumerateBoundBuffers(BufferObjectBinding bindingType, const std::function& f); diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index b1e3d7574f..e11307503a 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -350,11 +350,9 @@ void MetalDriver::createSamplerGroupR( void MetalDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, - PrimitiveType pt, uint32_t offset, - uint32_t minIndex, uint32_t maxIndex, uint32_t count) { + PrimitiveType pt) { construct_handle(rph); - MetalDriver::setRenderPrimitiveBuffer(rph, vbh, ibh); - MetalDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count); + MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh); } void MetalDriver::createProgramR(Handle rph, Program&& program) { @@ -1063,23 +1061,13 @@ void MetalDriver::endRenderPass(int dummy) { mContext->currentRenderPassEncoder = nil; } -void MetalDriver::setRenderPrimitiveBuffer(Handle rph, +void MetalDriver::setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt, Handle vbh, Handle ibh) { auto primitive = handle_cast(rph); auto vertexBuffer = handle_cast(vbh); auto indexBuffer = handle_cast(ibh); primitive->setBuffers(vertexBuffer, indexBuffer); -} - -void MetalDriver::setRenderPrimitiveRange(Handle rph, - PrimitiveType pt, uint32_t offset, uint32_t minIndex, uint32_t maxIndex, - uint32_t count) { - auto primitive = handle_cast(rph); primitive->type = pt; - primitive->offset = offset * primitive->indexBuffer->elementSize; - primitive->count = count; - primitive->minIndex = minIndex; - primitive->maxIndex = maxIndex > minIndex ? maxIndex : primitive->maxVertexCount - 1; } void MetalDriver::makeCurrent(Handle schDraw, Handle schRead) { @@ -1581,7 +1569,8 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) { } } -void MetalDriver::draw(PipelineState ps, Handle rph, uint32_t instanceCount) { +void MetalDriver::draw(PipelineState ps, Handle rph, + uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr, "Attempted to draw without a valid command encoder."); auto primitive = handle_cast(rph); @@ -1826,10 +1815,10 @@ void MetalDriver::draw(PipelineState ps, Handle rph, uint32_t id cmdBuffer = getPendingCommandBuffer(mContext); id metalIndexBuffer = indexBuffer->buffer.getGpuBufferForDraw(cmdBuffer); [mContext->currentRenderPassEncoder drawIndexedPrimitives:getMetalPrimitiveType(primitive->type) - indexCount:primitive->count + indexCount:indexCount indexType:getIndexType(indexBuffer->elementSize) indexBuffer:metalIndexBuffer - indexBufferOffset:primitive->offset + indexBufferOffset:indexOffset * primitive->indexBuffer->elementSize instanceCount:instanceCount]; } diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 30e44d1447..5c5f0e7395 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -340,7 +340,7 @@ void NoopDriver::blit( } void NoopDriver::draw(PipelineState pipelineState, Handle rph, - uint32_t instanceCount) { + uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { } void NoopDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) { diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 047f28383d..6bfeb66b8b 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -501,8 +501,7 @@ void OpenGLDriver::createBufferObjectR(Handle boh, void OpenGLDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, - PrimitiveType pt, uint32_t offset, - uint32_t minIndex, uint32_t maxIndex, uint32_t count) { + PrimitiveType pt) { DEBUG_MARKER() auto& gl = mContext; @@ -515,10 +514,6 @@ void OpenGLDriver::createRenderPrimitiveR(Handle rph, rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u; rp->gl.vertexBufferWithObjects = vbh; rp->type = pt; - rp->offset = offset * rp->gl.indicesSize; - rp->count = count; - rp->minIndex = minIndex; - rp->maxIndex = maxIndex > minIndex ? maxIndex : rp->maxVertexCount - 1; // sanitize max index gl.procs.genVertexArrays(1, &rp->gl.vao); @@ -862,7 +857,6 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer // that it's always reset in draw } - rp->maxVertexCount = vb->vertexCount; for (size_t i = 0, n = vb->attributes.size(); i < n; i++) { const auto& attribute = vb->attributes[i]; const uint8_t bi = attribute.buffer; @@ -3698,7 +3692,8 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel) #endif } -void OpenGLDriver::draw(PipelineState state, Handle rph, uint32_t instanceCount) { +void OpenGLDriver::draw(PipelineState state, Handle rph, + uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { DEBUG_MARKER() auto& gl = mContext; @@ -3736,13 +3731,14 @@ void OpenGLDriver::draw(PipelineState state, Handle rph, uint setScissor(state.scissor); if (UTILS_LIKELY(instanceCount <= 1)) { - glDrawElements(GLenum(rp->type), (GLsizei)rp->count, rp->gl.getIndicesType(), - reinterpret_cast(rp->offset)); + glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(), + reinterpret_cast(indexOffset * rp->gl.indicesSize)); } else { assert_invariant(!mContext.isES2()); #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - glDrawElementsInstanced(GLenum(rp->type), (GLsizei)rp->count, - rp->gl.getIndicesType(), reinterpret_cast(rp->offset), + glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount, + rp->gl.getIndicesType(), + reinterpret_cast(indexOffset * rp->gl.indicesSize), (GLsizei)instanceCount); #endif } diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index c700ce9c6a..28764a4e12 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -359,12 +359,10 @@ void VulkanDriver::createSamplerGroupR(Handle sbh, uint32_t coun void VulkanDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, - PrimitiveType pt, uint32_t offset, - uint32_t minIndex, uint32_t maxIndex, uint32_t count) { + PrimitiveType pt) { auto rp = mResourceAllocator.construct(rph, &mResourceAllocator); mResourceManager.acquire(rp); - VulkanDriver::setRenderPrimitiveBuffer(rph, vbh, ibh); - VulkanDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count); + VulkanDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh); } void VulkanDriver::destroyRenderPrimitive(Handle rph) { @@ -1384,22 +1382,12 @@ void VulkanDriver::nextSubpass(int) { } } -void VulkanDriver::setRenderPrimitiveBuffer(Handle rph, +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)); -} - -void VulkanDriver::setRenderPrimitiveRange(Handle rph, - PrimitiveType pt, uint32_t offset, - uint32_t minIndex, uint32_t maxIndex, uint32_t count) { - auto& primitive = *mResourceAllocator.handle_cast(rph); - primitive.setPrimitiveType(pt); - primitive.offset = offset * primitive.indexBuffer->elementSize; - primitive.count = count; - primitive.minIndex = minIndex; - primitive.maxIndex = maxIndex > minIndex ? maxIndex : primitive.maxVertexCount - 1; + primitive->setPrimitiveType(pt); } void VulkanDriver::makeCurrent(Handle drawSch, Handle readSch) { @@ -1648,7 +1636,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers, } void VulkanDriver::draw(PipelineState pipelineState, Handle rph, - const uint32_t instanceCount) { + uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { FVK_SYSTRACE_CONTEXT(); FVK_SYSTRACE_START("draw"); @@ -1827,8 +1815,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r prim.indexBuffer->indexType); // Finally, make the actual draw call. TODO: support subranges - const uint32_t indexCount = prim.count; - const uint32_t firstIndex = prim.offset / prim.indexBuffer->elementSize; + const uint32_t firstIndex = indexOffset; const int32_t vertexOffset = 0; const uint32_t firstInstId = 0; diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h index 16fa3a1b01..d76b474dcf 100644 --- a/filament/backend/src/vulkan/VulkanDriver.h +++ b/filament/backend/src/vulkan/VulkanDriver.h @@ -77,12 +77,10 @@ private: VulkanDriver& operator=(VulkanDriver const&) = delete; private: - inline void setRenderPrimitiveBuffer(Handle rph, Handle vbh, + inline void setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt, + Handle vbh, Handle ibh); - inline void setRenderPrimitiveRange(Handle rph, PrimitiveType pt, - uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count); - void collectGarbage(); VulkanPlatform* mPlatform = nullptr; diff --git a/filament/backend/test/BackendTest.cpp b/filament/backend/test/BackendTest.cpp index 4a120b20ea..0730236344 100644 --- a/filament/backend/test/BackendTest.cpp +++ b/filament/backend/test/BackendTest.cpp @@ -143,7 +143,7 @@ void BackendTest::renderTriangle(Handle renderTarget, state.rasterState.depthFunc = RasterState::DepthFunc::A; state.rasterState.culling = CullingMode::NONE; - api.draw(state, triangle.getRenderPrimitive(), 1); + api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); } diff --git a/filament/backend/test/TrianglePrimitive.cpp b/filament/backend/test/TrianglePrimitive.cpp index a65ec208ee..f6c6337996 100644 --- a/filament/backend/test/TrianglePrimitive.cpp +++ b/filament/backend/test/TrianglePrimitive.cpp @@ -60,7 +60,7 @@ TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi, mDriverApi.updateIndexBuffer(mIndexBuffer, std::move(indexBufferDesc), 0); mRenderPrimitive = mDriverApi.createRenderPrimitive( - mVertexBuffer, mIndexBuffer, PrimitiveType::TRIANGLES, 0, 0, 2, 3); + mVertexBuffer, mIndexBuffer, PrimitiveType::TRIANGLES); } void TrianglePrimitive::updateVertices(const filament::math::float2 vertices[3]) noexcept { diff --git a/filament/backend/test/test_Blit.cpp b/filament/backend/test/test_Blit.cpp index c3901beb2e..e1043e0a7c 100644 --- a/filament/backend/test/test_Blit.cpp +++ b/filament/backend/test/test_Blit.cpp @@ -405,7 +405,7 @@ TEST_F(BackendTest, ColorResolve) { api.beginFrame(0, 0); api.beginRenderPass(srcRenderTarget, params); api.bindUniformBuffer(0, ubuffer); - api.draw(state, triangle.getRenderPrimitive(), 1); + api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); api.endFrame(0); diff --git a/filament/backend/test/test_BufferUpdates.cpp b/filament/backend/test/test_BufferUpdates.cpp index 27287612bf..632acd5d5a 100644 --- a/filament/backend/test/test_BufferUpdates.cpp +++ b/filament/backend/test/test_BufferUpdates.cpp @@ -172,7 +172,7 @@ TEST_F(BackendTest, VertexBufferUpdate) { } getDriverApi().beginRenderPass(defaultRenderTarget, params); - getDriverApi().draw(state, triangle.getRenderPrimitive(), 1); + getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1); getDriverApi().endRenderPass(); triangleIndex++; diff --git a/filament/backend/test/test_FeedbackLoops.cpp b/filament/backend/test/test_FeedbackLoops.cpp index eba8b2be2a..e726be44b1 100644 --- a/filament/backend/test/test_FeedbackLoops.cpp +++ b/filament/backend/test/test_FeedbackLoops.cpp @@ -218,7 +218,7 @@ TEST_F(BackendTest, FeedbackLoops) { .sourceLevel = float(sourceLevel), }); api.beginRenderPass(renderTargets[targetLevel], params); - api.draw(state, triangle.getRenderPrimitive(), 1); + api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); } @@ -237,7 +237,7 @@ TEST_F(BackendTest, FeedbackLoops) { .sourceLevel = float(sourceLevel), }); api.beginRenderPass(renderTargets[targetLevel], params); - api.draw(state, triangle.getRenderPrimitive(), 1); + api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); } diff --git a/filament/backend/test/test_MRT.cpp b/filament/backend/test/test_MRT.cpp index 3e6e08f361..52f27f742e 100644 --- a/filament/backend/test/test_MRT.cpp +++ b/filament/backend/test/test_MRT.cpp @@ -130,7 +130,7 @@ TEST_F(BackendTest, MRT) { // Draw a triangle. getDriverApi().beginRenderPass(renderTarget, params); - getDriverApi().draw(state, triangle.getRenderPrimitive(), 1); + getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1); getDriverApi().endRenderPass(); getDriverApi().flush(); diff --git a/filament/backend/test/test_MipLevels.cpp b/filament/backend/test/test_MipLevels.cpp index 738e21fc81..32b88f0a6b 100644 --- a/filament/backend/test/test_MipLevels.cpp +++ b/filament/backend/test/test_MipLevels.cpp @@ -168,7 +168,7 @@ TEST_F(BackendTest, SetMinMaxLevel) { ps.rasterState.colorWrite = true; ps.rasterState.depthWrite = false; api.beginRenderPass(renderTarget, params); - api.draw(ps, triangle.getRenderPrimitive(), 1); + api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); } @@ -203,7 +203,7 @@ TEST_F(BackendTest, SetMinMaxLevel) { // Because the min level is 1, the result color should be the white triangle drawn in the // previous pass. api.beginRenderPass(defaultRenderTarget, params); - api.draw(state, triangle.getRenderPrimitive(), 1); + api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); // Adjust the base mip to 2. @@ -221,7 +221,7 @@ TEST_F(BackendTest, SetMinMaxLevel) { params.flags.clear = TargetBufferFlags::NONE; params.flags.discardStart = TargetBufferFlags::NONE; api.beginRenderPass(defaultRenderTarget, params); - api.draw(state, triangle.getRenderPrimitive(), 1); + api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); api.commit(swapChain); diff --git a/filament/backend/test/test_MissingRequiredAttributes.cpp b/filament/backend/test/test_MissingRequiredAttributes.cpp index ed1399f2a4..c56cece599 100644 --- a/filament/backend/test/test_MissingRequiredAttributes.cpp +++ b/filament/backend/test/test_MissingRequiredAttributes.cpp @@ -104,7 +104,7 @@ TEST_F(BackendTest, MissingRequiredAttributes) { // Render a triangle. getDriverApi().beginRenderPass(defaultRenderTarget, params); - getDriverApi().draw(state, triangle.getRenderPrimitive(), 1); + getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1); getDriverApi().endRenderPass(); getDriverApi().flush(); diff --git a/filament/backend/test/test_ReadPixels.cpp b/filament/backend/test/test_ReadPixels.cpp index 1f000c6e6a..6c8bd337e3 100644 --- a/filament/backend/test/test_ReadPixels.cpp +++ b/filament/backend/test/test_ReadPixels.cpp @@ -309,7 +309,7 @@ TEST_F(ReadPixelsTest, ReadPixels) { state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; state.rasterState.culling = CullingMode::NONE; - getDriverApi().draw(state, triangle.getRenderPrimitive(), 1); + getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1); getDriverApi().endRenderPass(); @@ -438,7 +438,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) { // Render some content, just so we don't read back uninitialized data. getDriverApi().beginRenderPass(renderTarget, params); - getDriverApi().draw(state, triangle.getRenderPrimitive(), 1); + getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1); getDriverApi().endRenderPass(); PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4, diff --git a/filament/backend/test/test_RenderExternalImage.cpp b/filament/backend/test/test_RenderExternalImage.cpp index 2039bb2aae..9e8d1d5fa7 100644 --- a/filament/backend/test/test_RenderExternalImage.cpp +++ b/filament/backend/test/test_RenderExternalImage.cpp @@ -120,7 +120,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) { // Render a triangle. getDriverApi().beginRenderPass(defaultRenderTarget, params); - getDriverApi().draw(state, triangle.getRenderPrimitive(), 1); + getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1); getDriverApi().endRenderPass(); getDriverApi().flush(); @@ -242,7 +242,7 @@ TEST_F(BackendTest, RenderExternalImage) { // Render a triangle. getDriverApi().beginRenderPass(defaultRenderTarget, params); - getDriverApi().draw(state, triangle.getRenderPrimitive(), 1); + getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1); getDriverApi().endRenderPass(); getDriverApi().flush(); diff --git a/filament/backend/test/test_Scissor.cpp b/filament/backend/test/test_Scissor.cpp index a7fea104bd..c048e4ccf6 100644 --- a/filament/backend/test/test_Scissor.cpp +++ b/filament/backend/test/test_Scissor.cpp @@ -140,7 +140,7 @@ TEST_F(BackendTest, ScissorViewportRegion) { api.beginFrame(0, 0); api.beginRenderPass(srcRenderTarget, params); - api.draw(ps, triangle.getRenderPrimitive(), 1); + api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); readPixelsAndAssertHash("scissor", kSrcTexWidth >> 1, kSrcTexHeight >> 1, fullRenderTarget, @@ -231,14 +231,14 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) { api.beginFrame(0, 0); api.beginRenderPass(renderTarget, params); - api.draw(ps, triangle.getRenderPrimitive(), 1); + api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); params.viewport = topLeftViewport; params.flags.clear = TargetBufferFlags::NONE; params.flags.discardStart = TargetBufferFlags::NONE; api.beginRenderPass(renderTarget, params); - api.draw(ps, triangle.getRenderPrimitive(), 1); + api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); readPixelsAndAssertHash( diff --git a/filament/backend/test/test_StencilBuffer.cpp b/filament/backend/test/test_StencilBuffer.cpp index 6222371310..e574bc18e9 100644 --- a/filament/backend/test/test_StencilBuffer.cpp +++ b/filament/backend/test/test_StencilBuffer.cpp @@ -113,7 +113,7 @@ public: api.beginFrame(0, 0); api.beginRenderPass(renderTarget, params); - api.draw(ps, smallTriangle.getRenderPrimitive(), 1); + api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); // Step 2: Render a larger triangle with the stencil test enabled. @@ -126,7 +126,7 @@ public: ps.stencilState.front.ref = 0u; api.beginRenderPass(renderTarget, params); - api.draw(ps, triangle.getRenderPrimitive(), 1); + api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); api.commit(swapChain); @@ -240,7 +240,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) { api.beginFrame(0, 0); api.beginRenderPass(renderTarget0, params); - api.draw(ps, smallTriangle.getRenderPrimitive(), 1); + api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); // Step 2: Render a larger triangle with the stencil test enabled. @@ -255,7 +255,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) { ps.stencilState.front.ref = 0u; api.beginRenderPass(renderTarget1, params); - api.draw(ps, triangle.getRenderPrimitive(), 1); + api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); api.commit(swapChain); diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index 6024fd2e59..bec39e4af0 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -583,8 +583,6 @@ public: VertexBuffer* UTILS_NULLABLE vertices = nullptr; IndexBuffer* UTILS_NULLABLE indices = nullptr; size_t offset = 0; - size_t minIndex = 0; - size_t maxIndex = 0; size_t count = 0; MaterialInstance const* UTILS_NULLABLE materialInstance = nullptr; PrimitiveType type = PrimitiveType::TRIANGLES; diff --git a/filament/src/HwRenderPrimitiveFactory.cpp b/filament/src/HwRenderPrimitiveFactory.cpp index 2e996e2595..635407d31b 100644 --- a/filament/src/HwRenderPrimitiveFactory.cpp +++ b/filament/src/HwRenderPrimitiveFactory.cpp @@ -27,15 +27,7 @@ bool operator<(HwRenderPrimitiveFactory::Key const& lhs, HwRenderPrimitiveFactory::Key const& rhs) noexcept { if (lhs.vbh == rhs.vbh) { if (lhs.ibh == rhs.ibh) { - if (lhs.offset == rhs.offset) { - if (lhs.count == rhs.count) { - return lhs.type < rhs.type; - } else { - return lhs.count < rhs.count; - } - } else { - return lhs.offset < rhs.offset; - } + return lhs.type < rhs.type; } else { return lhs.ibh < rhs.ibh; } @@ -69,17 +61,16 @@ HwRenderPrimitiveFactory::HwRenderPrimitiveFactory() HwRenderPrimitiveFactory::~HwRenderPrimitiveFactory() noexcept = default; -void HwRenderPrimitiveFactory::terminate(DriverApi& driver) noexcept { +void HwRenderPrimitiveFactory::terminate(DriverApi&) noexcept { assert_invariant(mMap.empty()); assert_invariant(mSet.empty()); } RenderPrimitiveHandle HwRenderPrimitiveFactory::create(DriverApi& driver, VertexBufferHandle vbh, IndexBufferHandle ibh, - PrimitiveType type, uint32_t offset, uint32_t minIndex, uint32_t maxIndex, - uint32_t count) noexcept { + PrimitiveType type) noexcept { - const Key key = { vbh, ibh, offset, count, type }; + const Key key = { vbh, ibh, type }; // see if we already have seen this RenderPrimitive auto pos = mSet.find(key); @@ -87,8 +78,7 @@ RenderPrimitiveHandle HwRenderPrimitiveFactory::create(DriverApi& driver, // the common case is that we've never seen it (i.e.: no reuse) if (UTILS_LIKELY(pos == mSet.end())) { // create the backend object - auto handle = driver.createRenderPrimitive(vbh, ibh, - type, offset, minIndex, maxIndex, count); + auto handle = driver.createRenderPrimitive(vbh, ibh, type); // insert key/handle in our set with a refcount of 1 // IMPORTANT: std::set<> doesn't invalidate iterators in insert/erase auto [ipos, _] = mSet.insert({ key, handle, 1 }); diff --git a/filament/src/HwRenderPrimitiveFactory.h b/filament/src/HwRenderPrimitiveFactory.h index 30583ed171..1ccb1e9aa0 100644 --- a/filament/src/HwRenderPrimitiveFactory.h +++ b/filament/src/HwRenderPrimitiveFactory.h @@ -26,8 +26,10 @@ #include +#include #include +#include #include namespace filament { @@ -50,11 +52,7 @@ public: backend::RenderPrimitiveHandle create(backend::DriverApi& driver, backend::VertexBufferHandle vbh, backend::IndexBufferHandle ibh, - backend::PrimitiveType type, - uint32_t offset, - uint32_t minIndex, - uint32_t maxIndex, - uint32_t count) noexcept; + backend::PrimitiveType type) noexcept; void destroy(backend::DriverApi& driver, backend::RenderPrimitiveHandle rph) noexcept; @@ -63,8 +61,6 @@ private: struct Key { // 20 bytes backend::VertexBufferHandle vbh; // 4 backend::IndexBufferHandle ibh; // 4 - uint32_t offset; // 4 - uint32_t count; // 4 backend::PrimitiveType type; // 4 }; diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index f186ee9cb6..9228d6caca 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -392,7 +392,7 @@ void PostProcessManager::render(FrameGraphResources::RenderPassInfo const& out, FEngine const& engine = mEngine; Handle const fullScreenQuad = engine.getFullScreenRenderPrimitive(); driver.beginRenderPass(out.target, out.params); - driver.draw(pipeline, fullScreenQuad, 1); + driver.draw(pipeline, fullScreenQuad, 0, 3, 1); driver.endRenderPass(); } @@ -2300,7 +2300,7 @@ void PostProcessManager::colorGradingSubpass(DriverApi& driver, PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE); driver.nextSubpass(); - driver.draw(material.getPipelineState(mEngine, variant), fullScreenRenderPrimitive, 1); + driver.draw(material.getPipelineState(mEngine, variant), fullScreenRenderPrimitive, 0, 3, 1); } void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomResolveOp op) noexcept { @@ -2319,7 +2319,7 @@ void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept { FMaterialInstance* mi = material.getMaterialInstance(mEngine); mi->use(driver); driver.nextSubpass(); - driver.draw(material.getPipelineState(mEngine), fullScreenRenderPrimitive, 1); + driver.draw(material.getPipelineState(mEngine), fullScreenRenderPrimitive, 0, 3, 1); } FrameGraphId PostProcessManager::customResolveUncompressPass(FrameGraph& fg, @@ -2764,7 +2764,7 @@ FrameGraphId PostProcessManager::taa(FrameGraph& fg, } PipelineState const pipeline(material.getPipelineState(mEngine, variant)); driver.beginRenderPass(out.target, out.params); - driver.draw(pipeline, mEngine.getFullScreenRenderPrimitive(), 1); + driver.draw(pipeline, mEngine.getFullScreenRenderPrimitive(), 0, 3, 1); if (colorGradingConfig.asSubpass) { colorGradingSubpass(driver, colorGradingConfig); } @@ -2988,8 +2988,8 @@ FrameGraphId PostProcessManager::upscale(FrameGraph& fg, bool enableTranslucentBlending(pipeline1); } driver.beginRenderPass(out.target, out.params); - driver.draw(pipeline0, fullScreenRenderPrimitive, 1); - driver.draw(pipeline1, fullScreenRenderPrimitive, 1); + driver.draw(pipeline0, fullScreenRenderPrimitive, 0, 3, 1); + driver.draw(pipeline1, fullScreenRenderPrimitive, 0, 3, 1); driver.endRenderPass(); } else { PipelineState pipeline(easuMaterial->getPipelineState(mEngine)); @@ -2997,7 +2997,7 @@ FrameGraphId PostProcessManager::upscale(FrameGraph& fg, bool enableTranslucentBlending(pipeline); } driver.beginRenderPass(out.target, out.params); - driver.draw(pipeline, fullScreenRenderPrimitive, 1); + driver.draw(pipeline, fullScreenRenderPrimitive, 0, 3, 1); driver.endRenderPass(); } }); diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index b585c4d328..481c2507e5 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -292,6 +292,8 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena) noexcept { // skinning/morphing. return lhs.primitive.mi == rhs.primitive.mi && lhs.primitive.primitiveHandle == rhs.primitive.primitiveHandle && + lhs.primitive.indexOffset == rhs.primitive.indexOffset && + lhs.primitive.indexCount == rhs.primitive.indexCount && lhs.primitive.rasterState == rhs.primitive.rasterState && lhs.primitive.skinningHandle == rhs.primitive.skinningHandle && lhs.primitive.skinningOffset == rhs.primitive.skinningOffset && @@ -640,6 +642,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla if constexpr (isColorPass) { cmdColor.primitive.primitiveHandle = primitive.getHwHandle(); + cmdColor.primitive.indexOffset = primitive.getIndexOffset(); + cmdColor.primitive.indexCount = primitive.getIndexCount(); RenderPass::setupColorCommand(cmdColor, renderableVariant, mi, inverseFrontFaces); cmdColor.primitive.skinningHandle = skinning.handle; @@ -745,6 +749,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla // unconditionally write the command cmdDepth.primitive.primitiveHandle = primitive.getHwHandle(); + cmdDepth.primitive.indexOffset = primitive.getIndexOffset(); + cmdDepth.primitive.indexCount = primitive.getIndexCount(); cmdDepth.primitive.mi = mi; cmdDepth.primitive.rasterState.culling = mi->getCullingMode(); @@ -988,7 +994,8 @@ void RenderPass::Executor::execute(FEngine& engine, info.skinningTexture); } - driver.draw(pipeline, info.primitiveHandle, instanceCount); + driver.draw(pipeline, info.primitiveHandle, + info.indexOffset, info.indexCount, instanceCount); } } diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index 646171efd5..358fa087d3 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -249,18 +249,19 @@ public: backend::Handle instanceBufferHandle; // 4 bytes uint32_t index = 0; // 4 bytes uint32_t skinningOffset = 0; // 4 bytes + uint32_t indexOffset; // 4 bytes + uint32_t indexCount; // 4 bytes uint16_t instanceCount; // 2 bytes [MSb: user] Variant materialVariant; // 1 byte static const uint16_t USER_INSTANCE_MASK = 0x8000u; static const uint16_t INSTANCE_COUNT_MASK = 0x7fffu; }; - static_assert(sizeof(PrimitiveInfo) == 48); + static_assert(sizeof(PrimitiveInfo) == 56); struct alignas(8) Command { // 64 bytes CommandKey key = 0; // 8 bytes PrimitiveInfo primitive; // 48 bytes - uint64_t reserved[1] = {}; // 8 bytes bool operator < (Command const& rhs) const noexcept { return key < rhs.key; } // placement new declared as "throw" to avoid the compiler's null-check inline void* operator new (size_t, void* ptr) { diff --git a/filament/src/RenderPrimitive.cpp b/filament/src/RenderPrimitive.cpp index 88d95380b2..6a523b8b6e 100644 --- a/filament/src/RenderPrimitive.cpp +++ b/filament/src/RenderPrimitive.cpp @@ -23,6 +23,9 @@ #include +#include +#include + namespace filament { void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver, @@ -37,15 +40,16 @@ void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverAp FVertexBuffer* vertexBuffer = downcast(entry.vertices); FIndexBuffer* indexBuffer = downcast(entry.indices); - AttributeBitset enabledAttributes = vertexBuffer->getDeclaredAttributes(); + AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes(); auto const& ebh = vertexBuffer->getHwHandle(); auto const& ibh = indexBuffer->getHwHandle(); - mHandle = factory.create(driver, ebh, ibh, entry.type, (uint32_t)entry.offset, - (uint32_t)entry.minIndex, (uint32_t)entry.maxIndex, (uint32_t)entry.count); + mHandle = factory.create(driver, ebh, ibh, entry.type); mPrimitiveType = entry.type; + mIndexOffset = entry.offset; + mIndexCount = entry.count; mEnabledAttributes = enabledAttributes; } } @@ -58,22 +62,21 @@ 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 minIndex, size_t maxIndex, size_t count) noexcept { - - AttributeBitset enabledAttributes = vertices->getDeclaredAttributes(); - - auto const& ebh = vertices->getHwHandle(); - auto const& ibh = indices->getHwHandle(); - + FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset, size_t count) noexcept { if (mHandle) { factory.destroy(driver, mHandle); } - mHandle = factory.create(driver, ebh, ibh, type, - (uint32_t)offset, (uint32_t)minIndex, (uint32_t)maxIndex, (uint32_t)count); + AttributeBitset const enabledAttributes = vertices->getDeclaredAttributes(); + + auto const& ebh = vertices->getHwHandle(); + auto const& ibh = indices->getHwHandle(); + + mHandle = factory.create(driver, ebh, ibh, type); mPrimitiveType = type; + mIndexOffset = offset; + mIndexCount = count; mEnabledAttributes = enabledAttributes; } diff --git a/filament/src/RenderPrimitive.h b/filament/src/RenderPrimitive.h index 7e77c13cb1..c85e999d2d 100644 --- a/filament/src/RenderPrimitive.h +++ b/filament/src/RenderPrimitive.h @@ -17,13 +17,16 @@ #ifndef TNT_FILAMENT_DETAILS_RENDERPRIMITIVE_H #define TNT_FILAMENT_DETAILS_RENDERPRIMITIVE_H +#include + #include "components/RenderableManager.h" #include "details/MaterialInstance.h" +#include #include -#include +#include namespace filament { @@ -43,13 +46,15 @@ public: void set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver, RenderableManager::PrimitiveType type, FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset, - size_t minIndex, size_t maxIndex, size_t count) noexcept; + 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; } + uint32_t getIndexOffset() const noexcept { return mIndexOffset; } + uint32_t getIndexCount() const noexcept { return mIndexCount; } backend::PrimitiveType getPrimitiveType() const noexcept { return mPrimitiveType; } AttributeBitset getEnabledAttributes() const noexcept { return mEnabledAttributes; } uint16_t getBlendOrder() const noexcept { return mBlendOrder; } @@ -72,7 +77,8 @@ private: uint16_t mBlendOrder = 0; bool mGlobalBlendOrderEnabled = false; backend::PrimitiveType mPrimitiveType = backend::PrimitiveType::TRIANGLES; - UTILS_UNUSED uint8_t reserved[4] = {}; + uint32_t mIndexOffset = 0; + uint32_t mIndexCount = 0; }; } // namespace filament diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index 1ef4c9dc12..a7f3fee3f2 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -109,14 +109,12 @@ RenderableManager::Builder& RenderableManager::Builder::geometry(size_t index, RenderableManager::Builder& RenderableManager::Builder::geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, - size_t offset, size_t minIndex, size_t maxIndex, size_t count) noexcept { + size_t offset, UTILS_UNUSED size_t minIndex, UTILS_UNUSED size_t maxIndex, size_t count) noexcept { std::vector& entries = mImpl->mEntries; if (index < entries.size()) { entries[index].vertices = vertices; entries[index].indices = indices; entries[index].offset = offset; - entries[index].minIndex = minIndex; - entries[index].maxIndex = maxIndex; entries[index].count = count; entries[index].type = type; } @@ -436,11 +434,6 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng entity.getId(), i, entry.offset, entry.count, entry.indices->getIndexCount()); - ASSERT_PRECONDITION(entry.minIndex <= entry.maxIndex, - "[entity=%u, primitive @ %u] minIndex (%u) > maxIndex (%u)", - entity.getId(), i, - entry.minIndex, entry.maxIndex); - // this can't be an error because (1) those values are not immutable, so the caller // could fix later, and (2) the material's shader will work (i.e. compile), and // use the default values for this attribute, which maybe be acceptable. @@ -816,7 +809,7 @@ void FRenderableManager::setGeometryAt(Instance instance, uint8_t level, size_t Slice& primitives = getRenderPrimitives(instance, level); if (primitiveIndex < primitives.size()) { primitives[primitiveIndex].set(mHwRenderPrimitiveFactory, mEngine.getDriverApi(), - type, vertices, indices, offset, 0, vertices->getVertexCount() - 1, count); + type, vertices, indices, offset, count); } } } diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 82b9399ec6..688c0a804b 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -273,7 +273,7 @@ void FEngine::init() { mFullScreenTriangleRph = driverApi.createRenderPrimitive( mFullScreenTriangleVb->getHwHandle(), mFullScreenTriangleIb->getHwHandle(), - PrimitiveType::TRIANGLES, 0, 0, 2, (uint32_t)mFullScreenTriangleIb->getIndexCount()); + PrimitiveType::TRIANGLES); // Compute a clip-space [-1 to 1] to texture space [0 to 1] matrix, taking into account // backend differences. From 142b73d9d7a503ce07d02e2a7c3314e220ba8602 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Thu, 15 Feb 2024 16:21:00 +0000 Subject: [PATCH 03/33] Add OpenGL extension for multiview (#7569) * Add OpenGL extension for multiview This extension is going to be used for multiview implementation in OpenGL. Now the API isStereoSupported takes a stereo type as a parameter. --- .../backend/include/private/backend/DriverAPI.inc | 2 +- filament/backend/src/metal/MetalDriver.mm | 10 ++++++++-- filament/backend/src/noop/NoopDriver.cpp | 2 +- filament/backend/src/opengl/OpenGLContext.cpp | 2 ++ filament/backend/src/opengl/OpenGLContext.h | 1 + filament/backend/src/opengl/OpenGLDriver.cpp | 14 +++++++++++--- filament/backend/src/vulkan/VulkanDriver.cpp | 10 ++++++++-- filament/include/filament/Engine.h | 6 +++--- filament/src/Engine.cpp | 4 ++-- filament/src/details/Engine.h | 4 +++- filament/src/details/Material.cpp | 3 ++- filament/src/details/View.cpp | 2 +- 12 files changed, 43 insertions(+), 17 deletions(-) diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 47e7320b29..ffa67ad19e 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -294,7 +294,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchMultiSampleSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported) -DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported) +DECL_DRIVER_API_SYNCHRONOUS_N(bool, isStereoSupported, backend::StereoscopicType, stereoscopicType) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported) DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index e11307503a..4d63644d39 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -754,8 +754,14 @@ bool MetalDriver::isSRGBSwapChainSupported() { return false; } -bool MetalDriver::isStereoSupported() { - return true; +bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) { + switch (stereoscopicType) { + case backend::StereoscopicType::INSTANCED: + return true; + case backend::StereoscopicType::MULTIVIEW: + // TODO: implement multiview feature in Metal. + return false; + } } bool MetalDriver::isParallelShaderCompileSupported() { diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 5c5f0e7395..428c4e75c1 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -174,7 +174,7 @@ bool NoopDriver::isSRGBSwapChainSupported() { return false; } -bool NoopDriver::isStereoSupported() { +bool NoopDriver::isStereoSupported(backend::StereoscopicType) { return false; } diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp index 6d2b08eddc..8d7a6a4b22 100644 --- a/filament/backend/src/opengl/OpenGLContext.cpp +++ b/filament/backend/src/opengl/OpenGLContext.cpp @@ -622,6 +622,7 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor ext->OES_standard_derivatives = exts.has("GL_OES_standard_derivatives"sv); ext->OES_texture_npot = exts.has("GL_OES_texture_npot"sv); ext->OES_vertex_array_object = exts.has("GL_OES_vertex_array_object"sv); + ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv); ext->WEBGL_compressed_texture_etc = exts.has("WEBGL_compressed_texture_etc"sv); ext->WEBGL_compressed_texture_s3tc = exts.has("WEBGL_compressed_texture_s3tc"sv); ext->WEBGL_compressed_texture_s3tc_srgb = exts.has("WEBGL_compressed_texture_s3tc_srgb"sv); @@ -686,6 +687,7 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor) ext->OES_standard_derivatives = true; ext->OES_texture_npot = true; ext->OES_vertex_array_object = true; + ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv); ext->WEBGL_compressed_texture_etc = false; ext->WEBGL_compressed_texture_s3tc = false; ext->WEBGL_compressed_texture_s3tc_srgb = false; diff --git a/filament/backend/src/opengl/OpenGLContext.h b/filament/backend/src/opengl/OpenGLContext.h index 0db583344c..315f949031 100644 --- a/filament/backend/src/opengl/OpenGLContext.h +++ b/filament/backend/src/opengl/OpenGLContext.h @@ -205,6 +205,7 @@ public: bool OES_standard_derivatives; bool OES_texture_npot; bool OES_vertex_array_object; + bool OVR_multiview2; bool WEBGL_compressed_texture_etc; bool WEBGL_compressed_texture_s3tc; bool WEBGL_compressed_texture_s3tc_srgb; diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 6bfeb66b8b..a553bd2a52 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -1893,12 +1893,20 @@ bool OpenGLDriver::isSRGBSwapChainSupported() { return mPlatform.isSRGBSwapChainSupported(); } -bool OpenGLDriver::isStereoSupported() { - // Stereo requires instancing and EXT_clip_cull_distance. +bool OpenGLDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) { + // Instanced-stereo requires instancing and EXT_clip_cull_distance. + // Multiview-stereo requires ES 3.0 and OVR_multiview2. if (UTILS_UNLIKELY(mContext.isES2())) { return false; } - return mContext.ext.EXT_clip_cull_distance; + switch (stereoscopicType) { + case backend::StereoscopicType::INSTANCED: + return mContext.ext.EXT_clip_cull_distance; + case backend::StereoscopicType::MULTIVIEW: + return mContext.ext.OVR_multiview2; + default: + return false; + } } bool OpenGLDriver::isParallelShaderCompileSupported() { diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 28764a4e12..4dcd9bfa79 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -797,8 +797,14 @@ bool VulkanDriver::isSRGBSwapChainSupported() { return mIsSRGBSwapChainSupported; } -bool VulkanDriver::isStereoSupported() { - return true; +bool VulkanDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) { + switch (stereoscopicType) { + case backend::StereoscopicType::INSTANCED: + return true; + case backend::StereoscopicType::MULTIVIEW: + // TODO: implement multiview feature in Vulkan. + return false; + } } bool VulkanDriver::isParallelShaderCompileSupported() { diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 90cff6283d..493573cc3b 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -590,12 +590,12 @@ public: size_t getMaxAutomaticInstances() const noexcept; /** - * Queries the device and platform for instanced stereo rendering support. + * Queries the device and platform for support of the given stereoscopic type. * - * @return true if stereo rendering is supported, false otherwise + * @return true if the given stereo rendering is supported, false otherwise * @see View::setStereoscopicOptions */ - bool isStereoSupported() const noexcept; + bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept; /** * Retrieves the configuration settings of this Engine. diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index 661f183425..380b7e55ad 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -331,8 +331,8 @@ const Engine::Config& Engine::getConfig() const noexcept { return downcast(this)->getConfig(); } -bool Engine::isStereoSupported() const noexcept { - return downcast(this)->isStereoSupported(); +bool Engine::isStereoSupported(StereoscopicType stereoscopicType) const noexcept { + return downcast(this)->isStereoSupported(stereoscopicType); } size_t Engine::getMaxStereoscopicEyes() noexcept { diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 03de2ce841..d656f7f1a0 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -182,7 +182,9 @@ public: return CONFIG_MAX_INSTANCES; } - bool isStereoSupported() const noexcept { return getDriver().isStereoSupported(); } + bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept { + return getDriver().isStereoSupported(stereoscopicType); + } static size_t getMaxStereoscopicEyes() noexcept { return CONFIG_MAX_STEREOSCOPIC_EYES; diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 8c707bf0e4..93745bf642 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -532,7 +532,8 @@ void FMaterial::compile(CompilerPriorityQueue priority, utils::Invocable&& callback) noexcept { // Turn off the STE variant if stereo is not supported. - if (!mEngine.getDriverApi().isStereoSupported()) { + const StereoscopicType stereoscopicType = mEngine.getConfig().stereoscopicType; + if (!mEngine.getDriverApi().isStereoSupported(stereoscopicType)) { variantSpec &= ~UserVariantFilterMask(UserVariantFilterBit::STE); } diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index 8188390f7b..9ce255332d 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -60,7 +60,7 @@ static constexpr float PID_CONTROLLER_Kd = 0.0f; FView::FView(FEngine& engine) : mFroxelizer(engine), mFogEntity(engine.getEntityManager().create()), - mIsStereoSupported(engine.getDriverApi().isStereoSupported()), + mIsStereoSupported(engine.getDriverApi().isStereoSupported(engine.getConfig().stereoscopicType)), mPerViewUniforms(engine) { DriverApi& driver = engine.getDriverApi(); From 5d9337e6c23f062dc2f0035934a73c8dcc623eb7 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Fri, 16 Feb 2024 13:06:01 -0800 Subject: [PATCH 04/33] geometry: properly reference memcpy usage (#7576) --- libs/geometry/src/MikktspaceImpl.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/geometry/src/MikktspaceImpl.cpp b/libs/geometry/src/MikktspaceImpl.cpp index f9f54c332c..19942ada3b 100644 --- a/libs/geometry/src/MikktspaceImpl.cpp +++ b/libs/geometry/src/MikktspaceImpl.cpp @@ -19,12 +19,13 @@ #include #include - #include #include #include +#include // memcpy + namespace filament::geometry { using namespace filament::math; @@ -98,7 +99,7 @@ void MikktspaceImpl::setTSpaceBasic(SMikkTSpaceContext const* context, float con cursor += 36; for (auto [attribArray, attribStride, attribSize]: wrapper->mInputAttribArrays) { uint8_t const* input = pointerAdd(attribArray, vertInd, attribStride); - std::memcpy(cursor, input, attribSize); + memcpy(cursor, input, attribSize); cursor += attribSize; } } From 8b249504295fb872cf6a365b598c390248d31924 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 20 Feb 2024 16:16:52 -0800 Subject: [PATCH 05/33] add the disableParallelShaderCompile option to Engine::Config --- .../filament-android/src/main/cpp/Engine.cpp | 6 +++++- .../com/google/android/filament/Engine.java | 18 ++++++++++++++++++ filament/include/filament/Engine.h | 6 ++++++ filament/src/details/Engine.cpp | 8 ++++++-- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index 9a2180e022..80409702c3 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -484,7 +484,9 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderConfig(JNIEnv*, jclass, jlong nativeBuilder, jlong commandBufferSizeMB, jlong perRenderPassArenaSizeMB, jlong driverHandleArenaSizeMB, jlong minCommandBufferSizeMB, jlong perFrameCommandsSizeMB, - jlong jobSystemThreadCount, jint stereoscopicType, jlong stereoscopicEyeCount, + jlong jobSystemThreadCount, + jlong textureUseAfterFreePoolSize, jboolean disableParallelShaderCompile, + jint stereoscopicType, jlong stereoscopicEyeCount, jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge) { Engine::Builder* builder = (Engine::Builder*) nativeBuilder; Engine::Config config = { @@ -494,6 +496,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu .minCommandBufferSizeMB = (uint32_t) minCommandBufferSizeMB, .perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB, .jobSystemThreadCount = (uint32_t) jobSystemThreadCount, + .textureUseAfterFreePoolSize = (uint32_t) textureUseAfterFreePoolSize, + .disableParallelShaderCompile = (bool) disableParallelShaderCompile, .stereoscopicType = (Engine::StereoscopicType) stereoscopicType, .stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount, .resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB, 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 393b711ac9..06f0a2b8ae 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 @@ -222,6 +222,7 @@ public class Engine { config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB, config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB, config.jobSystemThreadCount, + config.textureUseAfterFreePoolSize, config.disableParallelShaderCompile, config.stereoscopicType.ordinal(), config.stereoscopicEyeCount, config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge); return this; @@ -360,6 +361,22 @@ public class Engine { */ public long jobSystemThreadCount = 0; + /** + * Number of most-recently destroyed textures to track for use-after-free. + * + * This will cause the backend to throw an exception when a texture is freed but still bound + * to a SamplerGroup and used in a draw call. 0 disables completely. + * + * Currently only respected by the Metal backend. + */ + public long textureUseAfterFreePoolSize = 0; + + /** + * Set to `true` to forcibly disable parallel shader compilation in the backend. + * Currently only honored by the GL backend. + */ + public boolean disableParallelShaderCompile = false; + /** * The type of technique for stereoscopic rendering. * @@ -1264,6 +1281,7 @@ public class Engine { private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB, long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB, long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount, + long textureUseAfterFreePoolSize, boolean disableParallelShaderCompile, int stereoscopicType, long stereoscopicEyeCount, long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge); private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal); diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 493573cc3b..0ca5f3209d 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -298,6 +298,12 @@ public: */ size_t textureUseAfterFreePoolSize = 0; + /** + * Set to `true` to forcibly disable parallel shader compilation in the backend. + * Currently only honored by the GL backend. + */ + bool disableParallelShaderCompile = false; + /* * The type of technique for stereoscopic rendering. * diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 688c0a804b..dcf1521055 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -98,7 +98,10 @@ Engine* FEngine::create(Engine::Builder const& builder) { return nullptr; } DriverConfig const driverConfig{ - .handleArenaSize = instance->getRequestedDriverHandleArenaSize() }; + .handleArenaSize = instance->getRequestedDriverHandleArenaSize(), + .textureUseAfterFreePoolSize = instance->getConfig().textureUseAfterFreePoolSize, + .disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile + }; instance->mDriver = platform->createDriver(sharedContext, driverConfig); } else { @@ -651,7 +654,8 @@ int FEngine::loop() { DriverConfig const driverConfig { .handleArenaSize = getRequestedDriverHandleArenaSize(), - .textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize + .textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize, + .disableParallelShaderCompile = mConfig.disableParallelShaderCompile }; mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig); From dc6608350bea5fbccd7db1ef34ae0f7ba7a6caa4 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 21 Feb 2024 10:50:14 -0800 Subject: [PATCH 06/33] fix a uninitialized memory access when no renderable are visible --- filament/src/RenderPass.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 481c2507e5..9488668214 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -164,6 +164,11 @@ void RenderPass::appendCommands(FEngine& engine, // trace the number of visible renderables SYSTRACE_VALUE32("visibleRenderables", vr.size()); if (UTILS_UNLIKELY(vr.empty())) { + // no renderables, we still need the sentinel and the command buffer size should be + // exactly 1. + assert_invariant(commands.size() == 1); + Command* curr = commands.data(); + curr->key = uint64_t(Pass::SENTINEL); return; } From 9b0718199fce06e3e100be075a70a6da36c0e7cc Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Wed, 21 Feb 2024 13:55:47 -0800 Subject: [PATCH 07/33] Fix an out-of-bounds memory access when no renderables are visible (#7587) --- filament/src/RenderPass.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 9488668214..6112fb81d4 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -99,6 +99,10 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce mScissorViewport(builder.mScissorViewport), mCustomCommands(engine.getPerRenderPassArena()) { + if (mVisibleRenderables.empty()) { + return; + } + // compute the number of commands we need updateSummedPrimitiveCounts( const_cast(mRenderableSoa), mVisibleRenderables); From 486b9eef1e48545cfe2f1d344bbf8e6906ab448e Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 21 Feb 2024 15:29:31 -0800 Subject: [PATCH 08/33] vk: add debug names to shader modules (#7577) Also removed debugUtils workaround for Mesa since it's been addressed properly from the vk backend. --- filament/backend/src/vulkan/VulkanConstants.h | 22 +++- filament/backend/src/vulkan/VulkanDriver.cpp | 117 ++++++++++++------ filament/backend/src/vulkan/VulkanDriver.h | 25 +++- filament/backend/src/vulkan/VulkanHandles.cpp | 26 +++- .../src/vulkan/platform/VulkanPlatform.cpp | 33 +++-- 5 files changed, 162 insertions(+), 61 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index 429060535d..a2b62bd34c 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -39,7 +39,12 @@ // not required for validation. // FVK is short for Filament Vulkan + +// Enables Android systrace #define FVK_DEBUG_SYSTRACE 0x00000001 + +// Group markers are used to denote collection of GPU commands. It is typically at the granualarity +// of a renderpass. #define FVK_DEBUG_GROUP_MARKERS 0x00000002 #define FVK_DEBUG_TEXTURE 0x00000004 #define FVK_DEBUG_LAYOUT_TRANSITION 0x00000008 @@ -55,6 +60,9 @@ #define FVK_DEBUG_PIPELINE_CACHE 0x00002000 #define FVK_DEBUG_ALLOCATION 0x00004000 +// Enable the debug utils extension if it is available. +#define FVK_DEBUG_DEBUG_UTILS 0x00008000 + // Usefaul default combinations #define FVK_DEBUG_EVERYTHING 0xFFFFFFFF #define FVK_DEBUG_PERFORMANCE \ @@ -72,7 +80,7 @@ FVK_DEBUG_PRINT_GROUP_MARKERS #ifndef NDEBUG -#define FVK_DEBUG_FLAGS FVK_DEBUG_PERFORMANCE +#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_DEBUG_UTILS | FVK_DEBUG_VALIDATION) #else #define FVK_DEBUG_FLAGS 0 #endif @@ -80,11 +88,23 @@ #define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags)) #define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(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 +#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) +static_assert(FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_VALIDATION)); +#endif + // Ensure dependencies are met between debug options #if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) static_assert(FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)); #endif +// Only enable debug utils if validation is enabled. +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) +static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION)); +#endif + // end dependcy checks #if FVK_ENABLED(FVK_DEBUG_SYSTRACE) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 4dcd9bfa79..cfb57501aa 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -24,6 +24,7 @@ #include "VulkanHandles.h" #include "VulkanImageUtility.h" #include "VulkanMemory.h" +#include "VulkanTexture.h" #include @@ -117,7 +118,9 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags, utils::slog.e << utils::io::endl; return VK_FALSE; } +#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION) +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT* cbdata, void* pUserData) { @@ -136,9 +139,63 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla utils::slog.e << utils::io::endl; return VK_FALSE; } -#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION) +#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS) -} // anonymous namespace +}// anonymous namespace + +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) +using DebugUtils = VulkanDriver::DebugUtils; +DebugUtils* DebugUtils::mSingleton = nullptr; + +DebugUtils::DebugUtils(VkInstance instance, VkDevice device, VulkanContext const* context) + : mInstance(instance), mDevice(device), mEnabled(context->isDebugUtilsSupported()) { + +#if FVK_ENABLED(FVK_DEBUG_VALIDATION) + // Also initialize debug utils messenger here + if (mEnabled) { + VkDebugUtilsMessengerCreateInfoEXT const createInfo = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .pNext = nullptr, + .flags = 0, + .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, + .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, + .pfnUserCallback = debugUtilsCallback, + }; + VkResult result = vkCreateDebugUtilsMessengerEXT(instance, &createInfo, + VKALLOC, &mDebugMessenger); + ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger."); + } +#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION) +} + +DebugUtils* DebugUtils::get() { + assert_invariant(DebugUtils::mSingleton); + return DebugUtils::mSingleton; +} + +DebugUtils::~DebugUtils() { + if (mDebugMessenger) { + vkDestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, VKALLOC); + } +} + +void DebugUtils::setName(VkObjectType type, uint64_t handle, char const* name) { + auto impl = DebugUtils::get(); + if (!impl->mEnabled) { + return; + } + VkDebugUtilsObjectNameInfoEXT const info = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, + .pNext = nullptr, + .objectType = type, + .objectHandle = handle, + .pObjectName = name, + }; + vkSetDebugUtilsObjectNameEXT(impl->mDevice, &info); +} +#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS) using ImgUtil = VulkanImageUtility; @@ -159,35 +216,26 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex mReadPixels(mPlatform->getDevice()), mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) { +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) + DebugUtils::mSingleton = + new DebugUtils(mPlatform->getInstance(), mPlatform->getDevice(), &context); +#endif + #if FVK_ENABLED(FVK_DEBUG_VALIDATION) UTILS_UNUSED const PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback = vkCreateDebugReportCallbackEXT; - VkResult result; - if (mContext.isDebugUtilsSupported()) { - VkDebugUtilsMessengerCreateInfoEXT const createInfo = { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, - .pNext = nullptr, - .flags = 0, - .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, - .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, - .pfnUserCallback = debugUtilsCallback, - }; - result = vkCreateDebugUtilsMessengerEXT(mPlatform->getInstance(), &createInfo, VKALLOC, - &mDebugMessenger); - ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger."); - } else if (createDebugReportCallback) { + if (!context.isDebugUtilsSupported() && createDebugReportCallback) { VkDebugReportCallbackCreateInfoEXT const cbinfo = { .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, .flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, .pfnCallback = debugReportCallback, }; - result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC, + VkResult result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC, &mDebugCallback); ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug callback."); } #endif + mTimestamps = std::make_unique(mPlatform->getDevice()); mCommands = std::make_unique(mPlatform->getDevice(), mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), &mContext, @@ -286,9 +334,12 @@ void VulkanDriver::terminate() { if (mDebugCallback) { vkDestroyDebugReportCallbackEXT(mPlatform->getInstance(), mDebugCallback, VKALLOC); } - if (mDebugMessenger) { - vkDestroyDebugUtilsMessengerEXT(mPlatform->getInstance(), mDebugMessenger, VKALLOC); - } + +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) + assert_invariant(DebugUtils::mSingleton); + delete DebugUtils::mSingleton; +#endif + mPlatform->terminate(); } @@ -1219,22 +1270,14 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP } VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey); - // Assign a label to the framebuffer for debugging purposes. - #if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) - if (UTILS_UNLIKELY(mContext.isDebugUtilsSupported())) { - auto const topMarker = mCommands->getTopGroupMarker(); - if (!topMarker.empty()) { - const VkDebugUtilsObjectNameInfoEXT info = { - VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - nullptr, - VK_OBJECT_TYPE_FRAMEBUFFER, - reinterpret_cast(vkfb), - topMarker.c_str(), - }; - vkSetDebugUtilsObjectNameEXT(mPlatform->getDevice(), &info); - } +// Assign a label to the framebuffer for debugging purposes. +#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS) + auto const topMarker = mCommands->getTopGroupMarker(); + if (!topMarker.empty()) { + DebugUtils::setName(VK_OBJECT_TYPE_FRAMEBUFFER, reinterpret_cast(vkfb), + topMarker.c_str()); } - #endif +#endif // The current command buffer now owns a reference to the render target and its attachments. // Note that we must acquire parent textures, not sidecars. diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h index d76b474dcf..8e311b9bcc 100644 --- a/filament/backend/src/vulkan/VulkanDriver.h +++ b/filament/backend/src/vulkan/VulkanDriver.h @@ -45,6 +45,30 @@ public: static Driver* create(VulkanPlatform* platform, VulkanContext const& context, Platform::DriverConfig const& driverConfig) noexcept; +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) + // Encapsulates the VK_EXT_debug_utils extension. In particular, we use + // vkSetDebugUtilsObjectNameEXT and vkCreateDebugUtilsMessengerEXT + class DebugUtils { + public: + static void setName(VkObjectType type, uint64_t handle, char const* name); + + private: + static DebugUtils* get(); + + DebugUtils(VkInstance instance, VkDevice device, VulkanContext const* context); + ~DebugUtils(); + + VkInstance const mInstance; + VkDevice const mDevice; + bool const mEnabled; + VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE; + + static DebugUtils* mSingleton; + + friend class VulkanDriver; + }; +#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) + private: void debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept override; @@ -93,7 +117,6 @@ private: VulkanRenderPass mCurrentRenderPass = {}; VmaAllocator mAllocator = VK_NULL_HANDLE; VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE; - VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE; VulkanContext mContext = {}; VulkanResourceAllocator mResourceAllocator; diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index bb7f986993..53014949d8 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -17,6 +17,7 @@ #include "VulkanHandles.h" #include "VulkanConstants.h" +#include "VulkanDriver.h" #include "VulkanMemory.h" #include "spirv/VulkanSpirvUtils.h" @@ -78,6 +79,23 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept }; VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module); ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module."); + +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) + std::string name{ builder.getName().c_str(), builder.getName().size() }; + switch (static_cast(i)) { + case ShaderStage::VERTEX: + name += "_vs"; + break; + case ShaderStage::FRAGMENT: + name += "_fs"; + break; + default: + PANIC_POSTCONDITION("Unexpected stage"); + break; + } + VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SHADER_MODULE, + reinterpret_cast(module), name.c_str()); +#endif } auto& groupInfo = builder.getSamplerGroupInfo(); @@ -93,10 +111,10 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept } } - #if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) - utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex - << ", " << bundle.fragment << ")" << utils::io::endl; - #endif +#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) + utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex + << ", " << bundle.fragment << ")" << utils::io::endl; +#endif } VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs, diff --git a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp index 3f3502ce68..ab13e8859a 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp @@ -23,6 +23,7 @@ #include #include +#include #define SWAPCHAIN_RET_FUNC(func, handle, ...) \ if (mImpl->mSurfaceSwapChains.find(handle) != mImpl->mSurfaceSwapChains.end()) { \ @@ -158,7 +159,9 @@ ExtensionSet getInstanceExtensions() { VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, // Request these if available. +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) VK_EXT_DEBUG_UTILS_EXTENSION_NAME, +#endif VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, #if FVK_ENABLED(FVK_DEBUG_VALIDATION) @@ -181,7 +184,9 @@ ExtensionSet getInstanceExtensions() { ExtensionSet getDeviceExtensions(VkPhysicalDevice device) { std::string_view const TARGET_EXTS[] = { +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) VK_EXT_DEBUG_MARKER_EXTENSION_NAME, +#endif VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME, VK_KHR_MAINTENANCE1_EXTENSION_NAME, VK_KHR_MAINTENANCE2_EXTENSION_NAME, @@ -337,36 +342,22 @@ std::tuple pruneExtensions(VkPhysicalDevice device, ExtensionSet const& instExts, ExtensionSet const& deviceExts) { ExtensionSet newInstExts = instExts; ExtensionSet newDeviceExts = deviceExts; - if (vkGetPhysicalDeviceProperties2KHR) { - VkPhysicalDeviceDriverProperties driverProperties = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, - }; - VkPhysicalDeviceProperties2 physicalDeviceProperties2 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, - .pNext = &driverProperties, - }; - vkGetPhysicalDeviceProperties2KHR(device, &physicalDeviceProperties2); - char* driverInfo = driverProperties.driverInfo; - - if (newInstExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != newInstExts.end()) { - // Workaround for Mesa drivers. See issue #6192 - if ((driverInfo && strstr(driverInfo, "Mesa"))) { - newInstExts.erase(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } - } - } +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) // debugUtils and debugMarkers extensions are used mutually exclusively. if (newInstExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != newInstExts.end() && newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()) { newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); } +#endif +#if FVK_ENABLED(FVK_DEBUG_VALIDATION) // debugMarker must also request debugReport the instance extension. So check if that's present. if (newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end() && newInstExts.find(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == newInstExts.end()) { newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); } +#endif return std::tuple(newInstExts, newDeviceExts); } @@ -671,6 +662,12 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, context.mDebugMarkersSupported = deviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != deviceExts.end(); +#ifdef NDEBUG + // If we are in release build, we should not have turned on debug extensions + ASSERT_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported, + "Debug utils should not be enabled in release build."); +#endif + context.mDepthFormats = findAttachmentDepthFormats(mImpl->mPhysicalDevice); assert_invariant(context.mDepthFormats.size() > 0); From 6e5930c2a00ab32cd6bfa8e1065d1aba22e09eba Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 14 Feb 2024 23:32:07 -0800 Subject: [PATCH 09/33] Bimap is a custom bi-directional map. It is extracted from HwRenderPrimitiveFactory and cleaned-up a bit. --- filament/CMakeLists.txt | 1 + filament/src/Bimap.h | 128 ++++++++++++++++++++++ filament/src/HwRenderPrimitiveFactory.cpp | 96 +++++++--------- filament/src/HwRenderPrimitiveFactory.h | 87 ++++++++------- libs/utils/include/utils/Allocator.h | 53 ++++++++- 5 files changed, 262 insertions(+), 103 deletions(-) create mode 100644 filament/src/Bimap.h diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 3b3babcd29..38d2f42292 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -137,6 +137,7 @@ set(SRCS set(PRIVATE_HDRS src/Allocators.h + src/Bimap.h src/BufferPoolAllocator.h src/ColorSpaceUtils.h src/Culler.h diff --git a/filament/src/Bimap.h b/filament/src/Bimap.h new file mode 100644 index 0000000000..c765b542a0 --- /dev/null +++ b/filament/src/Bimap.h @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#ifndef TNT_FILAMENT_BIMAP_H +#define TNT_FILAMENT_BIMAP_H + +#include + +#include + +#include +#include +#include + +#include + +namespace filament { + +/* + * A semi-generic custom bimap. This bimap stores a key/value pair and can retrieve + * the value from the key and the key from the value. + * It is optimized for large keys and small values. The keys are stored out-of-line and are + * never moved. + */ +template, + typename ValueHash = std::hash, + typename Allocator = std::allocator> +class Bimap { + + struct KeyDelegate { + Key const* pKey = nullptr; + bool operator==(KeyDelegate const& rhs) const noexcept { + return *pKey == *rhs.pKey; + } + }; + + // KeyWrapperHash delegates the hash computation to KeyHash + struct KeyHasherDelegate { + size_t operator()(KeyDelegate const& p) const noexcept { + KeyHash const h; + return h(*p.pKey); + } + }; + + using ForwardMap = tsl::robin_map; + using BackwardMap = tsl::robin_map; + + Allocator mAllocator; + ForwardMap mForwardMap; + BackwardMap mBackwardMap; + +public: + Bimap() = default; + explicit Bimap(Allocator&& allocator) + : mAllocator(std::forward(allocator)) { + } + + void reserve(size_t capacity) { + mForwardMap.reserve(capacity); + mBackwardMap.reserve(capacity); + } + + bool empty() const noexcept { + return mForwardMap.empty() && mBackwardMap.empty(); + } + + // insert a new key/value pair + void insert(Key const& key, Value const& value) noexcept { + Key* pKey = mAllocator.allocate(1); // allocate storage for the key + new((void*)pKey) Key{ key }; // copy-construct the key + mForwardMap.insert({{ pKey }, value }); + mBackwardMap.insert({ value, { pKey }}); + } + + typename ForwardMap::iterator end() { return mForwardMap.end(); } + + // Find the value iterator from the key in O(1) + typename ForwardMap::const_iterator find(Key const& key) const { + return mForwardMap.find(KeyDelegate{ .pKey = &key }); + } + typename ForwardMap::iterator find(Key const& key) { + return mForwardMap.find(KeyDelegate{ .pKey = &key }); + } + + // Find the key iterator from the value in O(1). precondition, the value must exist. + typename BackwardMap::const_iterator find(Value const& value) const { + auto pos = mBackwardMap.find(value); + assert_invariant( pos != mBackwardMap.end() ); + return pos; + } + typename BackwardMap::iterator find(Value& value) const { + return mBackwardMap.find(value); + } + + // erase a key/value pair using an iterator to the value + void erase(typename BackwardMap::const_iterator it) { + // find the key + Key const& key = *(it->second.pKey); + // and its iterator + auto pos = find(key); + // destroy the key + it->second.pKey->~Key(); + // free its memory + mAllocator.deallocate(const_cast(it->second.pKey), 1); + // remove the entries from both maps + mForwardMap.erase(pos); + mBackwardMap.erase(it); + } +}; + +} // namespace filament + + +#endif // TNT_FILAMENT_BIMAP_H diff --git a/filament/src/HwRenderPrimitiveFactory.cpp b/filament/src/HwRenderPrimitiveFactory.cpp index 635407d31b..1845e9eb78 100644 --- a/filament/src/HwRenderPrimitiveFactory.cpp +++ b/filament/src/HwRenderPrimitiveFactory.cpp @@ -16,6 +16,16 @@ #include "HwRenderPrimitiveFactory.h" +#include +#include +#include + +#include + +#include +#include +#include + #include namespace filament { @@ -23,86 +33,60 @@ namespace filament { using namespace utils; using namespace backend; -bool operator<(HwRenderPrimitiveFactory::Key const& lhs, +size_t HwRenderPrimitiveFactory::KeyHash::operator()( + HwRenderPrimitiveFactory::Key const& p) const noexcept { + return utils::hash::combine( + p.params.vbh.getId(), + utils::hash::combine(p.params.ibh.getId(), (size_t)p.params.type)); +} + +bool operator==(HwRenderPrimitiveFactory::Key const& lhs, HwRenderPrimitiveFactory::Key const& rhs) noexcept { - if (lhs.vbh == rhs.vbh) { - if (lhs.ibh == rhs.ibh) { - return lhs.type < rhs.type; - } else { - return lhs.ibh < rhs.ibh; - } - } else { - return lhs.vbh < rhs.vbh; - } -} - -inline bool operator<(HwRenderPrimitiveFactory::Entry const& lhs, - HwRenderPrimitiveFactory::Entry const& rhs) noexcept { - return lhs.key < rhs.key; -} - -inline bool operator<(HwRenderPrimitiveFactory::Key const& lhs, - HwRenderPrimitiveFactory::Entry const& rhs) noexcept { - return lhs < rhs.key; -} - -inline bool operator<(HwRenderPrimitiveFactory::Entry const& lhs, - HwRenderPrimitiveFactory::Key const& rhs) noexcept { - return lhs.key < rhs; + return lhs.params.vbh == rhs.params.vbh && + lhs.params.ibh == rhs.params.ibh && + lhs.params.type == rhs.params.type; } // ------------------------------------------------------------------------------------------------ HwRenderPrimitiveFactory::HwRenderPrimitiveFactory() : mArena("HwRenderPrimitiveFactory::mArena", SET_ARENA_SIZE), - mSet(mArena) { - mMap.reserve(256); + mBimap(mArena) { + mBimap.reserve(256); } HwRenderPrimitiveFactory::~HwRenderPrimitiveFactory() noexcept = default; void HwRenderPrimitiveFactory::terminate(DriverApi&) noexcept { - assert_invariant(mMap.empty()); - assert_invariant(mSet.empty()); + assert_invariant(mBimap.empty()); } -RenderPrimitiveHandle HwRenderPrimitiveFactory::create(DriverApi& driver, - VertexBufferHandle vbh, IndexBufferHandle ibh, - PrimitiveType type) noexcept { - - const Key key = { vbh, ibh, type }; +auto HwRenderPrimitiveFactory::create(DriverApi& driver, + backend::VertexBufferHandle vbh, + backend::IndexBufferHandle ibh, + backend::PrimitiveType type) noexcept -> Handle { // see if we already have seen this RenderPrimitive - auto pos = mSet.find(key); + Key const key{{ vbh, ibh, type }, 1 }; + auto pos = mBimap.find(key); // the common case is that we've never seen it (i.e.: no reuse) - if (UTILS_LIKELY(pos == mSet.end())) { - // create the backend object + if (UTILS_LIKELY(pos == mBimap.end())) { auto handle = driver.createRenderPrimitive(vbh, ibh, type); - // insert key/handle in our set with a refcount of 1 - // IMPORTANT: std::set<> doesn't invalidate iterators in insert/erase - auto [ipos, _] = mSet.insert({ key, handle, 1 }); - // map the handle back to the key/payload - mMap.insert({ handle.getId(), ipos }); + mBimap.insert(key, { handle }); return handle; } - pos->refs++; - return pos->handle; + + pos->first.pKey->refs++; + return pos->second.handle; } -void HwRenderPrimitiveFactory::destroy(DriverApi& driver, RenderPrimitiveHandle rph) noexcept { +void HwRenderPrimitiveFactory::destroy(DriverApi& driver, Handle handle) noexcept { // look for this handle in our map - auto pos = mMap.find(rph.getId()); - - // it must be there - assert_invariant(pos != mMap.end()); - - // check the refcount and destroy if needed - auto ipos = pos->second; - if (--ipos->refs == 0) { - mSet.erase(ipos); - mMap.erase(pos); - driver.destroyRenderPrimitive(rph); + auto pos = mBimap.find(Value{ handle }); + if (--pos->second.pKey->refs == 0) { + mBimap.erase(pos); + driver.destroyRenderPrimitive(handle); } } diff --git a/filament/src/HwRenderPrimitiveFactory.h b/filament/src/HwRenderPrimitiveFactory.h index 1ccb1e9aa0..94ce8f4555 100644 --- a/filament/src/HwRenderPrimitiveFactory.h +++ b/filament/src/HwRenderPrimitiveFactory.h @@ -17,17 +17,15 @@ #ifndef TNT_FILAMENT_HWRENDERPRIMITIVEFACTORY_H #define TNT_FILAMENT_HWRENDERPRIMITIVEFACTORY_H +#include "Bimap.h" + +#include #include #include -#include - #include -#include - #include -#include #include #include @@ -38,6 +36,7 @@ class FEngine; class HwRenderPrimitiveFactory { public: + using Handle = backend::RenderPrimitiveHandle; HwRenderPrimitiveFactory(); ~HwRenderPrimitiveFactory() noexcept; @@ -49,64 +48,64 @@ public: void terminate(backend::DriverApi& driver) noexcept; - backend::RenderPrimitiveHandle create(backend::DriverApi& driver, - backend::VertexBufferHandle vbh, - backend::IndexBufferHandle ibh, - backend::PrimitiveType type) noexcept; - - void destroy(backend::DriverApi& driver, - backend::RenderPrimitiveHandle rph) noexcept; - -private: - struct Key { // 20 bytes + struct Parameters { // 20 bytes backend::VertexBufferHandle vbh; // 4 backend::IndexBufferHandle ibh; // 4 backend::PrimitiveType type; // 4 }; - struct Entry { // 28 bytes - Key key; // 20 - backend::RenderPrimitiveHandle handle; // 4 - mutable uint32_t refs; // 4 + Handle create(backend::DriverApi& driver, + backend::VertexBufferHandle vbh, + backend::IndexBufferHandle ibh, + backend::PrimitiveType type) noexcept; + + void destroy(backend::DriverApi& driver, Handle handle) noexcept; + +private: + struct Key { + Parameters params; + mutable uint32_t refs; // 4 bytes }; + struct KeyHash { + size_t operator()(Key const& p) const noexcept; + }; + + friend bool operator==(Key const& lhs, Key const& rhs) noexcept; + + + struct Value { // 4 bytes + Handle handle; + }; + + struct ValueHash { + size_t operator()(Value const& p) const noexcept { + std::hash const h; + return h(p.handle.getId()); + } + }; + + friend bool operator==(Value const& lhs, Value const& rhs) noexcept { + return lhs.handle == rhs.handle; + } // Size of the arena used for the "set" part of the bimap static constexpr size_t SET_ARENA_SIZE = 4 * 1024 * 1024; // Arena for the set<>, using a pool allocator inside a heap area. - using Arena = utils::Arena< - utils::PoolAllocator<64>, // this seems to work with clang and msvc + using PoolAllocatorArena = utils::Arena< + utils::PoolAllocatorWithFallback, utils::LockingPolicy::NoLock, -#ifndef NDEBUG - utils::TrackingPolicy::HighWatermark, -#else utils::TrackingPolicy::Untracked, -#endif utils::AreaPolicy::HeapArea>; - using Set = std::set< - Entry, - std::less<>, - utils::STLAllocator>; - - using Map = tsl::robin_map< - backend::RenderPrimitiveHandle::HandleId, - Set::const_iterator>; // Arena where the set memory is allocated - Arena mArena; + PoolAllocatorArena mArena; - // set of HwRenderPrimitive data - Set mSet; - - // map of RenderPrimitiveHandle to Set Entry - Map mMap; - - friend bool operator<(Key const& lhs, Key const& rhs) noexcept; - friend bool operator<(Key const& lhs, Entry const& rhs) noexcept; - friend bool operator<(Entry const& lhs, Key const& rhs) noexcept; - friend bool operator<(Entry const& lhs, Entry const& rhs) noexcept; + // The special Bimap + Bimap> mBimap; }; } // namespace filament diff --git a/libs/utils/include/utils/Allocator.h b/libs/utils/include/utils/Allocator.h index 073206f48b..7b9978f5b9 100644 --- a/libs/utils/include/utils/Allocator.h +++ b/libs/utils/include/utils/Allocator.h @@ -395,10 +395,10 @@ public: } PoolAllocator(void* begin, size_t size) noexcept - : mFreeList(begin, static_cast(begin) + size, ELEMENT_SIZE, ALIGNMENT, OFFSET) { + : PoolAllocator(begin, static_cast(begin) + size) { } - template + template explicit PoolAllocator(const AREA& area) noexcept : PoolAllocator(area.begin(), area.end()) { } @@ -424,6 +424,53 @@ private: FREELIST mFreeList; }; +template < + size_t ELEMENT_SIZE, + size_t ALIGNMENT = alignof(std::max_align_t), + typename FREELIST = FreeList> +class PoolAllocatorWithFallback : + private PoolAllocator, + private HeapAllocator { + using PoolAllocator = PoolAllocator; + void* mBegin; + void* mEnd; +public: + PoolAllocatorWithFallback(void* begin, void* end) noexcept + : PoolAllocator(begin, end), mBegin(begin), mEnd(end) { + } + + PoolAllocatorWithFallback(void* begin, size_t size) noexcept + : PoolAllocatorWithFallback(begin, static_cast(begin) + size) { + } + + template + explicit PoolAllocatorWithFallback(const AREA& area) noexcept + : PoolAllocatorWithFallback(area.begin(), area.end()) { + } + + bool isHeapAllocation(void* p) const noexcept { + return p < mBegin || p >= mEnd; + } + + // our allocator concept + void* alloc(size_t size = ELEMENT_SIZE, size_t alignment = ALIGNMENT) noexcept { + void* p = PoolAllocator::alloc(size, alignment); + if (UTILS_UNLIKELY(!p)) { + p = HeapAllocator::alloc(size, alignment); + } + assert_invariant(p); + return p; + } + + void free(void* p, size_t size) noexcept { + if (UTILS_LIKELY(!isHeapAllocation(p))) { + PoolAllocator::free(p, size); + } else { + HeapAllocator::free(p); + } + } +}; + #define UTILS_MAX(a,b) ((a) > (b) ? (a) : (b)) template @@ -884,7 +931,7 @@ public: public: // we don't make this explicit, so that we can initialize a vector using a STLAllocator - // from an Arena, avoiding to have to repeat the vector type. + // from an Arena, avoiding having to repeat the vector type. STLAllocator(ARENA& arena) : mArena(arena) { } // NOLINT(google-explicit-constructor) template From 20caeb38897a4e5a676d3ce591982d34e0ad9823 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 14 Feb 2024 11:19:59 -0800 Subject: [PATCH 10/33] Introduce HwVertexBufferInfo This new backend object holds the information needed to create the pipeline on vulkan/metal relative to draw calls. It is used to create HwVertexBuffer. --- filament/backend/include/backend/Handle.h | 26 +++++----- .../include/private/backend/DriverAPI.inc | 8 +++- .../include/private/backend/HandleAllocator.h | 2 +- filament/backend/src/DriverBase.h | 28 ++++++----- filament/backend/src/metal/MetalDriver.mm | 27 +++++++++-- filament/backend/src/metal/MetalHandles.h | 17 +++++-- filament/backend/src/metal/MetalHandles.mm | 21 ++++++--- filament/backend/src/noop/NoopDriver.cpp | 3 ++ filament/backend/src/opengl/OpenGLDriver.cpp | 47 ++++++++++++++----- filament/backend/src/opengl/OpenGLDriver.h | 16 ++++++- filament/backend/src/vulkan/VulkanDriver.cpp | 34 +++++++++++--- filament/backend/src/vulkan/VulkanHandles.cpp | 33 +++++++++---- filament/backend/src/vulkan/VulkanHandles.h | 17 +++++-- .../backend/src/vulkan/VulkanResources.cpp | 3 ++ filament/backend/src/vulkan/VulkanResources.h | 1 + filament/backend/test/TrianglePrimitive.cpp | 5 +- filament/backend/test/TrianglePrimitive.h | 2 + filament/src/details/VertexBuffer.cpp | 9 ++-- filament/src/details/VertexBuffer.h | 2 + 19 files changed, 222 insertions(+), 79 deletions(-) diff --git a/filament/backend/include/backend/Handle.h b/filament/backend/include/backend/Handle.h index ffc16133fd..4b63607a1c 100644 --- a/filament/backend/include/backend/Handle.h +++ b/filament/backend/include/backend/Handle.h @@ -39,6 +39,7 @@ struct HwStream; struct HwSwapChain; struct HwTexture; struct HwTimerQuery; +struct HwVertexBufferInfo; struct HwVertexBuffer; /* @@ -114,18 +115,19 @@ private: // Types used by the command stream // (we use this renaming because the macro-system doesn't deal well with "<" and ">") -using BufferObjectHandle = Handle; -using FenceHandle = Handle; -using IndexBufferHandle = Handle; -using ProgramHandle = Handle; -using RenderPrimitiveHandle = Handle; -using RenderTargetHandle = Handle; -using SamplerGroupHandle = Handle; -using StreamHandle = Handle; -using SwapChainHandle = Handle; -using TextureHandle = Handle; -using TimerQueryHandle = Handle; -using VertexBufferHandle = Handle; +using BufferObjectHandle = Handle; +using FenceHandle = Handle; +using IndexBufferHandle = Handle; +using ProgramHandle = Handle; +using RenderPrimitiveHandle = Handle; +using RenderTargetHandle = Handle; +using SamplerGroupHandle = Handle; +using StreamHandle = Handle; +using SwapChainHandle = Handle; +using TextureHandle = Handle; +using TimerQueryHandle = Handle; +using VertexBufferHandle = Handle; +using VertexBufferInfoHandle = Handle; } // namespace filament::backend diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index ffa67ad19e..b29551153c 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -167,12 +167,15 @@ DECL_DRIVER_API_0(resetState) * ----------------------- */ -DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer, +DECL_DRIVER_API_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo, uint8_t, bufferCount, uint8_t, attributeCount, - uint32_t, vertexCount, backend::AttributeArray, attributes) +DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer, + uint32_t, vertexCount, + backend::VertexBufferInfoHandle, vbih) + DECL_DRIVER_API_R_N(backend::IndexBufferHandle, createIndexBuffer, backend::ElementType, elementType, uint32_t, indexCount, @@ -260,6 +263,7 @@ DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery) */ DECL_DRIVER_API_N(destroyVertexBuffer, backend::VertexBufferHandle, vbh) +DECL_DRIVER_API_N(destroyVertexBufferInfo,backend::VertexBufferInfoHandle, vbih) DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh) DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh) DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph) diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index 04e66d8577..14e70fd06e 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -37,7 +37,7 @@ #include #include -#define HandleAllocatorGL HandleAllocator<16, 64, 208> // ~3640 / pool / MiB +#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 diff --git a/filament/backend/src/DriverBase.h b/filament/backend/src/DriverBase.h index 3887ceb9b6..18a7e4043f 100644 --- a/filament/backend/src/DriverBase.h +++ b/filament/backend/src/DriverBase.h @@ -49,24 +49,28 @@ struct AcquiredImage; struct HwBase { }; -struct HwVertexBuffer : public HwBase { - AttributeArray attributes{}; // 8 * MAX_VERTEX_ATTRIBUTE_COUNT - uint32_t vertexCount{}; // 4 + +struct HwVertexBufferInfo : public HwBase { uint8_t bufferCount{}; // 1 uint8_t attributeCount{}; // 1 - bool padding{}; // 1 - uint8_t bufferObjectsVersion{}; // 1 -> total struct is 136 bytes - - HwVertexBuffer() noexcept = default; - HwVertexBuffer(uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount, - AttributeArray const& attributes) noexcept - : attributes(attributes), - vertexCount(elementCount), - bufferCount(bufferCount), + bool padding[2]{}; // 2 + HwVertexBufferInfo() noexcept = default; + HwVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount) noexcept + : bufferCount(bufferCount), attributeCount(attributeCount) { } }; +struct HwVertexBuffer : public HwBase { + uint32_t vertexCount{}; // 4 + uint8_t bufferObjectsVersion{}; // 1 + bool padding[3]{}; // 2 + HwVertexBuffer() noexcept = default; + explicit HwVertexBuffer(uint32_t vertextCount) noexcept + : vertexCount(vertextCount) { + } +}; + struct HwBufferObject : public HwBase { uint32_t byteCount{}; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 4d63644d39..df8414a9af 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -279,10 +279,16 @@ void MetalDriver::finish(int) { [oneOffBuffer waitUntilCompleted]; } -void MetalDriver::createVertexBufferR(Handle vbh, uint8_t bufferCount, - uint8_t attributeCount, uint32_t vertexCount, AttributeArray attributes) { - construct_handle(vbh, *mContext, bufferCount, - attributeCount, vertexCount, attributes); +void MetalDriver::createVertexBufferInfoR(Handle vbih, uint8_t bufferCount, + uint8_t attributeCount, AttributeArray attributes) { + construct_handle(vbih, *mContext, + bufferCount, attributeCount, attributes); +} + +void MetalDriver::createVertexBufferR(Handle vbh, + uint32_t vertexCount, Handle vbih) { + MetalVertexBufferInfo const* const vbi = handle_cast(vbih); + construct_handle(vbh, *mContext, vertexCount, vbi->bufferCount, vbih); } void MetalDriver::createIndexBufferR(Handle ibh, ElementType elementType, @@ -438,6 +444,10 @@ void MetalDriver::createTimerQueryR(Handle tqh, int) { // nothing to do, timer query was constructed in createTimerQueryS } +Handle MetalDriver::createVertexBufferInfoS() noexcept { + return alloc_handle(); +} + Handle MetalDriver::createVertexBufferS() noexcept { return alloc_handle(); } @@ -502,6 +512,12 @@ Handle MetalDriver::createTimerQueryS() noexcept { return alloc_and_construct_handle(); } +void MetalDriver::destroyVertexBufferInfo(Handle vbih) { + if (vbih) { + destruct_handle(vbih); + } +} + void MetalDriver::destroyVertexBuffer(Handle vbh) { if (vbh) { destruct_handle(vbh); @@ -1072,7 +1088,8 @@ void MetalDriver::setRenderPrimitiveBuffer(Handle rph, Primit auto primitive = handle_cast(rph); auto vertexBuffer = handle_cast(vbh); auto indexBuffer = handle_cast(ibh); - primitive->setBuffers(vertexBuffer, indexBuffer); + MetalVertexBufferInfo const* const vbi = handle_cast(vertexBuffer->vbih); + primitive->setBuffers(vbi, vertexBuffer, indexBuffer); primitive->type = pt; } diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index fdc4a5e6a9..534d75044a 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -145,10 +145,18 @@ private: MetalBuffer buffer; }; -struct MetalVertexBuffer : public HwVertexBuffer { - MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount, - uint32_t vertexCount, AttributeArray const& attributes); +struct MetalVertexBufferInfo : public HwVertexBufferInfo { + MetalVertexBufferInfo(MetalContext& context, + uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes); + AttributeArray attributes; +}; + +struct MetalVertexBuffer : public HwVertexBuffer { + MetalVertexBuffer(MetalContext& context, + uint32_t vertexCount, uint32_t bufferCount, Handle vbih); + + Handle vbih; utils::FixedCapacityVector buffers; }; @@ -161,7 +169,8 @@ struct MetalIndexBuffer : public HwIndexBuffer { struct MetalRenderPrimitive : public HwRenderPrimitive { MetalRenderPrimitive(); - void setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer); + void setBuffers(MetalVertexBufferInfo const* const vbi, + MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer); // The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak". // The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive. diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index e28689ba4f..eef4087cc5 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -340,9 +340,16 @@ void MetalBufferObject::updateBufferUnsynchronized(void* data, size_t size, uint buffer.copyIntoBufferUnsynchronized(data, size, byteOffset); } -MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, - uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes) - : HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes), buffers(bufferCount, nullptr) {} +MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount, + uint8_t attributeCount, AttributeArray const& attributes) + : HwVertexBufferInfo(bufferCount, attributeCount), + attributes(attributes) { +} + +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), @@ -351,12 +358,12 @@ MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uin MetalRenderPrimitive::MetalRenderPrimitive() : bufferMapping(utils::FixedCapacityVector::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {} -void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* - indexBuffer) { +void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi, + MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) { this->vertexBuffer = vertexBuffer; this->indexBuffer = indexBuffer; - const size_t attributeCount = vertexBuffer->attributes.size(); + const size_t attributeCount = vbi->attributes.size(); auto& mapping = bufferMapping; mapping.clear(); @@ -397,7 +404,7 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde }; for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) { - const auto& attribute = vertexBuffer->attributes[attributeIndex]; + const auto& attribute = vbi->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. diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 428c4e75c1..c8b5bd4397 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -77,6 +77,9 @@ void NoopDriver::finish(int) { void NoopDriver::destroyRenderPrimitive(Handle rph) { } +void NoopDriver::destroyVertexBufferInfo(Handle vbih) { +} + void NoopDriver::destroyVertexBuffer(Handle vbh) { } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index a553bd2a52..e47047c150 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -94,22 +94,24 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform, // GLSamplerGroup : 16 few // GLSwapChain : 16 few // GLTimerQuery : 16 few - // -- less than or equal 16 bytes + // GLRenderPrimitive : 20 many // GLFence : 24 few + // -- less than or equal 24 bytes // GLBufferObject : 32 many - // GLRenderPrimitive : 40 many // OpenGLProgram : 56 moderate // GLTexture : 64 moderate // -- less than or equal 64 bytes + // GLVertexBuffer : 76 moderate // GLStream : 104 few // GLRenderTarget : 112 few - // GLVertexBuffer : 200 moderate - // -- less than or equal to 208 bytes + // GLVertexBufferInfo : 132 moderate + // -- less than or equal to 136 bytes slog.d << "\nGLSwapChain: " << sizeof(GLSwapChain) << "\nGLBufferObject: " << sizeof(GLBufferObject) << "\nGLVertexBuffer: " << sizeof(GLVertexBuffer) + << "\nGLVertexBufferInfo: " << sizeof(GLVertexBufferInfo) << "\nGLIndexBuffer: " << sizeof(GLIndexBuffer) << "\nGLSamplerGroup: " << sizeof(GLSamplerGroup) << "\nGLRenderPrimitive: " << sizeof(GLRenderPrimitive) @@ -386,6 +388,10 @@ void OpenGLDriver::setStencilState(StencilState ss) noexcept { // Creating driver objects // ------------------------------------------------------------------------------------------------ +Handle OpenGLDriver::createVertexBufferInfoS() noexcept { + return initHandle(); +} + Handle OpenGLDriver::createVertexBufferS() noexcept { return initHandle(); } @@ -446,14 +452,21 @@ Handle OpenGLDriver::createTimerQueryS() noexcept { return initHandle(); } -void OpenGLDriver::createVertexBufferR( - Handle vbh, +void OpenGLDriver::createVertexBufferInfoR( + Handle vbih, uint8_t bufferCount, uint8_t attributeCount, - uint32_t elementCount, AttributeArray attributes) { DEBUG_MARKER() - construct(vbh, bufferCount, attributeCount, elementCount, attributes); + construct(vbih, bufferCount, attributeCount, attributes); +} + +void OpenGLDriver::createVertexBufferR( + Handle vbh, + uint32_t vertexCount, + Handle vbih) { + DEBUG_MARKER() + construct(vbh, vertexCount, vbih); } void OpenGLDriver::createIndexBufferR( @@ -506,7 +519,7 @@ void OpenGLDriver::createRenderPrimitiveR(Handle rph, auto& gl = mContext; - GLVertexBuffer const* const eb = handle_cast(vbh); + GLVertexBuffer const* const vb = handle_cast(vbh); GLIndexBuffer const* const ib = handle_cast(ibh); assert_invariant(ib->elementSize == 2 || ib->elementSize == 4); @@ -520,7 +533,7 @@ void OpenGLDriver::createRenderPrimitiveR(Handle rph, gl.bindVertexArray(&rp->gl); // update the VBO bindings in the VAO - updateVertexArrayObject(rp, eb); + updateVertexArrayObject(rp, vb); // this records the index buffer into the currently bound VAO gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer); @@ -857,8 +870,10 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer // that it's always reset in draw } - for (size_t i = 0, n = vb->attributes.size(); i < n; i++) { - const auto& attribute = vb->attributes[i]; + GLVertexBufferInfo const* const vbi = handle_cast(vb->vbih); + + for (size_t i = 0, n = vbi->attributes.size(); i < n; i++) { + const auto& attribute = vbi->attributes[i]; const uint8_t bi = attribute.buffer; // Invoking glVertexAttribPointer without a bound VBO is an invalid operation, so we must @@ -1404,6 +1419,14 @@ void OpenGLDriver::createTimerQueryR(Handle tqh, int) { // Destroying driver objects // ------------------------------------------------------------------------------------------------ +void OpenGLDriver::destroyVertexBufferInfo(Handle vbih) { + DEBUG_MARKER() + if (vbih) { + GLVertexBufferInfo const* vbi = handle_cast(vbih); + destruct(vbih, vbi); + } +} + void OpenGLDriver::destroyVertexBuffer(Handle vbh) { DEBUG_MARKER() if (vbh) { diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index 74f311a962..5365811b01 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -94,8 +94,22 @@ public: uint16_t age = 0; }; + struct GLVertexBufferInfo : public HwVertexBufferInfo { + GLVertexBufferInfo() noexcept = default; + GLVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, + AttributeArray const& attributes) + : HwVertexBufferInfo(bufferCount, attributeCount), + attributes(attributes) { + } + AttributeArray attributes; + }; + struct GLVertexBuffer : public HwVertexBuffer { - using HwVertexBuffer::HwVertexBuffer; + GLVertexBuffer() noexcept = default; + GLVertexBuffer(uint32_t vertexCount, Handle vbih) + : HwVertexBuffer(vertexCount), vbih(vbih) { + } + Handle vbih; struct { // 4 * MAX_VERTEX_ATTRIBUTE_COUNT bytes std::array buffers{}; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index cfb57501aa..2157cabc2c 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -424,10 +424,26 @@ void VulkanDriver::destroyRenderPrimitive(Handle rph) { mResourceManager.release(rp); } -void VulkanDriver::createVertexBufferR(Handle vbh, uint8_t bufferCount, - uint8_t attributeCount, uint32_t elementCount, AttributeArray attributes) { - auto vertexBuffer = mResourceAllocator.construct(vbh, mContext, mStagePool, - &mResourceAllocator, bufferCount, attributeCount, elementCount, attributes); +void VulkanDriver::createVertexBufferInfoR(Handle vbih, uint8_t bufferCount, + uint8_t attributeCount, AttributeArray attributes) { + auto vbi = mResourceAllocator.construct(vbih, + bufferCount, attributeCount, attributes); + mResourceManager.acquire(vbi); +} + +void VulkanDriver::destroyVertexBufferInfo(Handle vbih) { + if (!vbih) { + return; + } + auto vbi = mResourceAllocator.handle_cast(vbih); + mResourceManager.release(vbi); +} + + +void VulkanDriver::createVertexBufferR(Handle vbh, + uint32_t vertexCount, Handle vbih) { + auto vertexBuffer = mResourceAllocator.construct(vbh, + mContext, mStagePool, &mResourceAllocator, vertexCount, vbih); mResourceManager.acquire(vertexBuffer); } @@ -634,6 +650,10 @@ void VulkanDriver::createTimerQueryR(Handle tqh, int) { // nothing to do, timer query was constructed in createTimerQueryS } +Handle VulkanDriver::createVertexBufferInfoS() noexcept { + return mResourceAllocator.allocHandle(); +} + Handle VulkanDriver::createVertexBufferS() noexcept { return mResourceAllocator.allocHandle(); } @@ -954,7 +974,7 @@ void VulkanDriver::setVertexBufferObject(Handle vbh, uint32_t in auto bo = mResourceAllocator.handle_cast(boh); assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX); - vb->setBuffer(bo, index); + vb->setBuffer(mResourceAllocator, bo, index); } void VulkanDriver::updateIndexBuffer(Handle ibh, BufferDescriptor&& p, @@ -1735,7 +1755,9 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r }; // Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers. - uint32_t const bufferCount = prim.vertexBuffer->attributes.size(); + 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(); diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 53014949d8..7712115082 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -16,6 +16,7 @@ #include "VulkanHandles.h" +#include "VulkanDriver.h" #include "VulkanConstants.h" #include "VulkanDriver.h" #include "VulkanMemory.h" @@ -280,13 +281,23 @@ 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) { +} + VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, - VulkanResourceAllocator* allocator, uint8_t bufferCount, uint8_t attributeCount, - uint32_t elementCount, AttributeArray const& attribs) - : HwVertexBuffer(bufferCount, attributeCount, elementCount, attribs), + VulkanResourceAllocator* allocator, uint32_t vertexCount, Handle vbih) + : HwVertexBuffer(vertexCount), VulkanResource(VulkanResourceType::VERTEX_BUFFER), - mInfo(new PipelineInfo(attribs.size())), + vbih(vbih), mResources(allocator) { + + 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(); @@ -294,8 +305,8 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& std::fill(mInfo->mSoa.begin(), mInfo->mSoa.end(), -1); - for (uint32_t attribIndex = 0; attribIndex < attribs.size(); attribIndex++) { - Attribute attrib = attribs[attribIndex]; + for (uint32_t attribIndex = 0; attribIndex < vbi->attributes.size(); attribIndex++) { + Attribute attrib = vbi->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); @@ -307,7 +318,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 = attribs[0]; + attrib = vbi->attributes[0]; } offsets[attribIndex] = attrib.offset; attribDesc[attribIndex] = { @@ -327,8 +338,12 @@ VulkanVertexBuffer::~VulkanVertexBuffer() { delete mInfo; } -void VulkanVertexBuffer::setBuffer(VulkanBufferObject* bufferObject, uint32_t index) { - size_t count = attributes.size(); +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(); for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) { diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 34cfda45c3..9e1d51b537 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -129,14 +129,21 @@ private: struct VulkanBufferObject; +struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource { + VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, + AttributeArray const& attributes); + AttributeArray attributes; +}; + struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource { VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, - VulkanResourceAllocator* allocator, uint8_t bufferCount, uint8_t attributeCount, - uint32_t elementCount, AttributeArray const& attributes); + VulkanResourceAllocator* allocator, + uint32_t vertexCount, Handle vbih); ~VulkanVertexBuffer(); - void setBuffer(VulkanBufferObject* bufferObject, uint32_t index); + void setBuffer(VulkanResourceAllocator const& allocator, + VulkanBufferObject* bufferObject, uint32_t index); inline VkVertexInputAttributeDescription const* getAttribDescriptions() { return mInfo->mSoa.data(); @@ -154,6 +161,8 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource { return mInfo->mSoa.data(); } + Handle vbih; + private: struct PipelineInfo { PipelineInfo(size_t size) @@ -161,7 +170,7 @@ private: mSoa.resize(size); } - // These corresponds to the index of the element in the SoA + // 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; diff --git a/filament/backend/src/vulkan/VulkanResources.cpp b/filament/backend/src/vulkan/VulkanResources.cpp index 5eef8e0aac..4cd88f2bb0 100644 --- a/filament/backend/src/vulkan/VulkanResources.cpp +++ b/filament/backend/src/vulkan/VulkanResources.cpp @@ -53,6 +53,9 @@ void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType t case VulkanResourceType::TIMER_QUERY: allocator->destruct(Handle(id)); break; + case VulkanResourceType::VERTEX_BUFFER_INFO: + allocator->destruct(Handle(id)); + break; case VulkanResourceType::VERTEX_BUFFER: allocator->destruct(Handle(id)); break; diff --git a/filament/backend/src/vulkan/VulkanResources.h b/filament/backend/src/vulkan/VulkanResources.h index 9421e11a14..1c37e4237f 100644 --- a/filament/backend/src/vulkan/VulkanResources.h +++ b/filament/backend/src/vulkan/VulkanResources.h @@ -43,6 +43,7 @@ enum class VulkanResourceType : uint8_t { TEXTURE, TIMER_QUERY, VERTEX_BUFFER, + VERTEX_BUFFER_INFO, // Below are resources that are managed manually (i.e. not ref counted). FENCE, diff --git a/filament/backend/test/TrianglePrimitive.cpp b/filament/backend/test/TrianglePrimitive.cpp index f6c6337996..d4028057c3 100644 --- a/filament/backend/test/TrianglePrimitive.cpp +++ b/filament/backend/test/TrianglePrimitive.cpp @@ -48,7 +48,9 @@ TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi, const size_t size = sizeof(math::float2) * 3; mBufferObject = mDriverApi.createBufferObject(size, BufferObjectBinding::VERTEX, BufferUsage::STATIC); - mVertexBuffer = mDriverApi.createVertexBuffer(1, 1, mVertexCount, attributes); + mVertexBufferInfo = mDriverApi.createVertexBufferInfo(1, 1, attributes); + mVertexBuffer = mDriverApi.createVertexBuffer(mVertexCount, mVertexBufferInfo); + mDriverApi.setVertexBufferObject(mVertexBuffer, 0, mBufferObject); BufferDescriptor vertexBufferDesc(gVertices, size); mDriverApi.updateBufferObject(mBufferObject, std::move(vertexBufferDesc), 0); @@ -102,6 +104,7 @@ void TrianglePrimitive::updateIndices(const index_type* indices, int count, int TrianglePrimitive::~TrianglePrimitive() { mDriverApi.destroyBufferObject(mBufferObject); mDriverApi.destroyVertexBuffer(mVertexBuffer); + mDriverApi.destroyVertexBufferInfo(mVertexBufferInfo); mDriverApi.destroyIndexBuffer(mIndexBuffer); mDriverApi.destroyRenderPrimitive(mRenderPrimitive); } diff --git a/filament/backend/test/TrianglePrimitive.h b/filament/backend/test/TrianglePrimitive.h index aa1f6c41de..0ea272c935 100644 --- a/filament/backend/test/TrianglePrimitive.h +++ b/filament/backend/test/TrianglePrimitive.h @@ -40,6 +40,7 @@ public: using PrimitiveHandle = filament::backend::Handle; using BufferObjectHandle = filament::backend::Handle; + using VertexInfoHandle = filament::backend::Handle; using VertexHandle = filament::backend::Handle; using IndexHandle = filament::backend::Handle; @@ -63,6 +64,7 @@ private: PrimitiveHandle mRenderPrimitive; BufferObjectHandle mBufferObject; + VertexInfoHandle mVertexBufferInfo; VertexHandle mVertexBuffer; IndexHandle mIndexBuffer; diff --git a/filament/src/details/VertexBuffer.cpp b/filament/src/details/VertexBuffer.cpp index f557e25d23..a3e558fd85 100644 --- a/filament/src/details/VertexBuffer.cpp +++ b/filament/src/details/VertexBuffer.cpp @@ -213,8 +213,11 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build FEngine::DriverApi& driver = engine.getDriverApi(); - mHandle = driver.createVertexBuffer( - mBufferCount, attributeCount, mVertexCount, attributeArray); + // TODO: this should use a cache + mVertexBufferInfoHandle = driver.createVertexBufferInfo( + mBufferCount, attributeCount, attributeArray); + + mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle); // If buffer objects are not enabled at the API level, then we create them internally. if (!mBufferObjectsEnabled) { @@ -238,7 +241,6 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build } } } - } void FVertexBuffer::terminate(FEngine& engine) { @@ -249,6 +251,7 @@ void FVertexBuffer::terminate(FEngine& engine) { } } driver.destroyVertexBuffer(mHandle); + driver.destroyVertexBufferInfo(mVertexBufferInfoHandle); } size_t FVertexBuffer::getVertexCount() const noexcept { diff --git a/filament/src/details/VertexBuffer.h b/filament/src/details/VertexBuffer.h index d9e7883518..77f610afab 100644 --- a/filament/src/details/VertexBuffer.h +++ b/filament/src/details/VertexBuffer.h @@ -40,6 +40,7 @@ class FEngine; class FVertexBuffer : public VertexBuffer { public: + using VertexBufferInfoHandle = backend::VertexBufferInfoHandle; using VertexBufferHandle = backend::VertexBufferHandle; using BufferObjectHandle = backend::BufferObjectHandle; @@ -74,6 +75,7 @@ private: AttributeData() : backend::Attribute{ .type = backend::ElementType::FLOAT4 } {} }; + VertexBufferInfoHandle mVertexBufferInfoHandle; VertexBufferHandle mHandle; std::array mAttributes; std::array mBufferObjects; From 3fab93bf3d5c2755b7fd797300f438133ff6d492 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 16 Feb 2024 14:47:08 -0800 Subject: [PATCH 11/33] Add a HwVertexBufferInfo cache --- filament/CMakeLists.txt | 2 + filament/src/HwVertexBufferInfoFactory.cpp | 94 ++++++++++++++++ filament/src/HwVertexBufferInfoFactory.h | 119 +++++++++++++++++++++ filament/src/details/Engine.h | 6 ++ filament/src/details/VertexBuffer.cpp | 7 +- 5 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 filament/src/HwVertexBufferInfoFactory.cpp create mode 100644 filament/src/HwVertexBufferInfoFactory.h diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 38d2f42292..1855eec649 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -66,6 +66,7 @@ set(SRCS src/Froxelizer.cpp src/Frustum.cpp src/HwRenderPrimitiveFactory.cpp + src/HwVertexBufferInfoFactory.cpp src/IndexBuffer.cpp src/IndirectLight.cpp src/InstanceBuffer.cpp @@ -148,6 +149,7 @@ set(PRIVATE_HDRS src/FrameSkipper.h src/Froxelizer.h src/HwRenderPrimitiveFactory.h + src/HwVertexBufferInfoFactory.h src/Intersections.h src/MaterialParser.h src/PerViewUniforms.h diff --git a/filament/src/HwVertexBufferInfoFactory.cpp b/filament/src/HwVertexBufferInfoFactory.cpp new file mode 100644 index 0000000000..9219a710da --- /dev/null +++ b/filament/src/HwVertexBufferInfoFactory.cpp @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#include "HwVertexBufferInfoFactory.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace filament { + +using namespace utils; +using namespace backend; + +size_t HwVertexBufferInfoFactory::Parameters::hash() const noexcept { + static_assert((sizeof(*this) % sizeof(uint32_t)) == 0); + return utils::hash::murmur3( + reinterpret_cast(this), sizeof(Parameters) / sizeof(uint32_t), 0); +} + +bool operator==(HwVertexBufferInfoFactory::Parameters const& lhs, + HwVertexBufferInfoFactory::Parameters const& rhs) noexcept { + return !memcmp(&lhs, &rhs, sizeof(HwVertexBufferInfoFactory::Parameters)); +} + +// ------------------------------------------------------------------------------------------------ + +HwVertexBufferInfoFactory::HwVertexBufferInfoFactory() + : mArena("HwVertexBufferInfoFactory::mArena", SET_ARENA_SIZE), + mBimap(mArena) { + mBimap.reserve(256); +} + +HwVertexBufferInfoFactory::~HwVertexBufferInfoFactory() noexcept = default; + +void HwVertexBufferInfoFactory::terminate(DriverApi&) noexcept { + assert_invariant(mBimap.empty()); +} + +auto HwVertexBufferInfoFactory::create(DriverApi& driver, + uint8_t bufferCount, + uint8_t attributeCount, + backend::AttributeArray attributes) noexcept -> Handle { + + Key const key{{ bufferCount, attributeCount, {}, attributes }, 1 }; + auto pos = mBimap.find(key); + + // the common case is that we've never seen it (i.e.: no reuse) + if (UTILS_LIKELY(pos == mBimap.end())) { + auto handle = driver.createVertexBufferInfo( + bufferCount, attributeCount, + attributes); + mBimap.insert(key, { handle }); + return handle; + } + + ++(pos->first.pKey->refs); + return pos->second.handle; +} + +void HwVertexBufferInfoFactory::destroy(DriverApi& driver, Handle handle) noexcept { + // look for this handle in our map + auto pos = mBimap.find(Value{ handle }); + if (--(pos->second.pKey->refs) == 0) { + mBimap.erase(pos); + driver.destroyVertexBufferInfo(handle); + } +} + +} // namespace filament diff --git a/filament/src/HwVertexBufferInfoFactory.h b/filament/src/HwVertexBufferInfoFactory.h new file mode 100644 index 0000000000..7e745dcb26 --- /dev/null +++ b/filament/src/HwVertexBufferInfoFactory.h @@ -0,0 +1,119 @@ +/* + * 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. + */ + +#ifndef TNT_FILAMENT_HWVERTEXBUFFERINFOFACTORY_H +#define TNT_FILAMENT_HWVERTEXBUFFERINFOFACTORY_H + +#include "Bimap.h" + +#include +#include +#include + +#include + +#include + +#include +#include + +namespace filament { + +class FEngine; + +class HwVertexBufferInfoFactory { +public: + using Handle = backend::VertexBufferInfoHandle; + + HwVertexBufferInfoFactory(); + ~HwVertexBufferInfoFactory() noexcept; + + HwVertexBufferInfoFactory(HwVertexBufferInfoFactory const& rhs) = delete; + HwVertexBufferInfoFactory(HwVertexBufferInfoFactory&& rhs) noexcept = delete; + HwVertexBufferInfoFactory& operator=(HwVertexBufferInfoFactory const& rhs) = delete; + HwVertexBufferInfoFactory& operator=(HwVertexBufferInfoFactory&& rhs) noexcept = delete; + + void terminate(backend::DriverApi& driver) noexcept; + + struct Parameters { // 136 bytes + uint8_t bufferCount; + uint8_t attributeCount; + uint8_t padding[2] = {}; + backend::AttributeArray attributes; + size_t hash() const noexcept; + }; + + friend bool operator==(Parameters const& lhs, Parameters const& rhs) noexcept; + + Handle create(backend::DriverApi& driver, + uint8_t bufferCount, + uint8_t attributeCount, + backend::AttributeArray attributes) noexcept; + + void destroy(backend::DriverApi& driver, Handle handle) noexcept; + +private: + struct Key { // 140 bytes + Parameters params; + mutable uint32_t refs; // 4 bytes + bool operator==(Key const& rhs) const noexcept { + return params == rhs.params; + } + }; + + struct KeyHasher { + size_t operator()(Key const& p) const noexcept { + return p.params.hash(); + } + }; + + struct Value { + Handle handle; + }; + + struct ValueHasher { + size_t operator()(Value v) const noexcept { + std::hash const hasher; + return hasher(v.handle.getId()); + } + }; + + friend bool operator==(Value const& lhs, Value const& rhs) noexcept { + return lhs.handle == rhs.handle; + } + + // Size of the arena used for the "set" part of the bimap + static constexpr size_t SET_ARENA_SIZE = 4 * 1024 * 1024; + + // Arena for the set<>, using a pool allocator inside a heap area. + using PoolAllocatorArena = utils::Arena< + utils::PoolAllocatorWithFallback, + utils::LockingPolicy::NoLock, + utils::TrackingPolicy::Untracked, + utils::AreaPolicy::HeapArea>; + + + // Arena where the set memory is allocated + PoolAllocatorArena mArena; + + // The special Bimap + Bimap> mBimap; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_HWVERTEXBUFFERINFOFACTORY_H diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index d656f7f1a0..9946edbb70 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -23,6 +23,7 @@ #include "DFG.h" #include "PostProcessManager.h" #include "ResourceList.h" +#include "HwVertexBufferInfoFactory.h" #include "components/CameraManager.h" #include "components/LightManager.h" @@ -405,6 +406,10 @@ public: return mAutomaticInstancingEnabled; } + HwVertexBufferInfoFactory& getVertexBufferInfoFactory() noexcept { + return mHwVertexBufferInfoFactory; + } + backend::Handle getOneTexture() const { return mDummyOneTexture; } backend::Handle getZeroTexture() const { return mDummyZeroTexture; } backend::Handle getOneTextureArray() const { return mDummyOneTextureArray; } @@ -473,6 +478,7 @@ private: FLightManager mLightManager; FCameraManager mCameraManager; ResourceAllocator* mResourceAllocator = nullptr; + HwVertexBufferInfoFactory mHwVertexBufferInfoFactory; ResourceList mBufferObjects{ "BufferObject" }; ResourceList mRenderers{ "Renderer" }; diff --git a/filament/src/details/VertexBuffer.cpp b/filament/src/details/VertexBuffer.cpp index a3e558fd85..3e0aaa7abf 100644 --- a/filament/src/details/VertexBuffer.cpp +++ b/filament/src/details/VertexBuffer.cpp @@ -153,7 +153,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build mDeclaredAttributes = builder->mDeclaredAttributes; uint8_t const attributeCount = (uint8_t)mDeclaredAttributes.count(); - AttributeArray attributeArray; + AttributeArray attributeArray{}; static_assert(attributeArray.size() == MAX_VERTEX_ATTRIBUTE_COUNT, "Attribute and Builder::Attribute arrays must match"); @@ -213,8 +213,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build FEngine::DriverApi& driver = engine.getDriverApi(); - // TODO: this should use a cache - mVertexBufferInfoHandle = driver.createVertexBufferInfo( + mVertexBufferInfoHandle = engine.getVertexBufferInfoFactory().create(driver, mBufferCount, attributeCount, attributeArray); mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle); @@ -251,7 +250,7 @@ void FVertexBuffer::terminate(FEngine& engine) { } } driver.destroyVertexBuffer(mHandle); - driver.destroyVertexBufferInfo(mVertexBufferInfoHandle); + engine.getVertexBufferInfoFactory().destroy(driver, mVertexBufferInfoHandle); } size_t FVertexBuffer::getVertexCount() const noexcept { From 11a3c06418dfbfa5a5cfac8248b26b6af48633b1 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 16 Feb 2024 15:26:31 -0800 Subject: [PATCH 12/33] PrimitiveInfo is running out of space So we now access some of its members through a pointer, this is not ideal, but we can re-optimize this later. We will need more space in it soon. --- filament/src/RenderPass.cpp | 33 +++++++++++++-------------------- filament/src/RenderPass.h | 12 ++++++------ filament/src/RenderPrimitive.h | 14 ++++++++++---- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 6112fb81d4..c9074b829f 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -217,7 +217,7 @@ void RenderPass::appendCommands(FEngine& engine, // This must be done from the main thread. for (Command const* first = curr, *last = curr + commandCount ; first != last ; ++first) { if (UTILS_LIKELY((first->key & CUSTOM_MASK) == uint64_t(CustomCommand::PASS))) { - auto ma = first->primitive.mi->getMaterial(); + auto ma = first->primitive.primitive->getMaterialInstance()->getMaterial(); ma->prepareProgram(first->primitive.materialVariant); } } @@ -299,10 +299,7 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena) noexcept { [lhs = *curr](Command const& rhs) { // primitives must be identical to be instanced. Currently, instancing doesn't support // skinning/morphing. - return lhs.primitive.mi == rhs.primitive.mi && - lhs.primitive.primitiveHandle == rhs.primitive.primitiveHandle && - lhs.primitive.indexOffset == rhs.primitive.indexOffset && - lhs.primitive.indexCount == rhs.primitive.indexCount && + return lhs.primitive.primitive == rhs.primitive.primitive && lhs.primitive.rasterState == rhs.primitive.rasterState && lhs.primitive.skinningHandle == rhs.primitive.skinningHandle && lhs.primitive.skinningOffset == rhs.primitive.skinningOffset && @@ -431,7 +428,6 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant, cmdDraw.primitive.rasterState.colorWrite = mi->isColorWriteEnabled(); cmdDraw.primitive.rasterState.depthWrite = mi->isDepthWriteEnabled(); cmdDraw.primitive.rasterState.depthFunc = mi->getDepthFunc(); - cmdDraw.primitive.mi = mi; cmdDraw.primitive.materialVariant = variant; // we keep "RasterState::colorWrite" to the value set by material (could be disabled) } @@ -588,7 +584,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla cmdColor.key = makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT); cmdColor.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT); - cmdColor.primitive.index = (uint16_t)i; + cmdColor.primitive.index = i; cmdColor.primitive.instanceCount = soaInstanceInfo[i].count | PrimitiveInfo::USER_INSTANCE_MASK; cmdColor.primitive.instanceBufferHandle = soaInstanceInfo[i].handle; @@ -615,7 +611,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla cmdDepth.key |= makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT); cmdDepth.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT); cmdDepth.key |= makeField(distanceBits >> 22u, Z_BUCKET_MASK, Z_BUCKET_SHIFT); - cmdDepth.primitive.index = (uint16_t)i; + cmdDepth.primitive.index = i; cmdDepth.primitive.instanceCount = soaInstanceInfo[i].count | PrimitiveInfo::USER_INSTANCE_MASK; cmdDepth.primitive.instanceBufferHandle = soaInstanceInfo[i].handle; @@ -650,9 +646,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla FMaterial const* const ma = mi->getMaterial(); if constexpr (isColorPass) { - cmdColor.primitive.primitiveHandle = primitive.getHwHandle(); - cmdColor.primitive.indexOffset = primitive.getIndexOffset(); - cmdColor.primitive.indexCount = primitive.getIndexCount(); + cmdColor.primitive.primitive = &primitive; RenderPass::setupColorCommand(cmdColor, renderableVariant, mi, inverseFrontFaces); cmdColor.primitive.skinningHandle = skinning.handle; @@ -757,10 +751,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla cmdDepth.key |= mi->getSortingKey(); // already all set-up for direct or'ing // unconditionally write the command - cmdDepth.primitive.primitiveHandle = primitive.getHwHandle(); - cmdDepth.primitive.indexOffset = primitive.getIndexOffset(); - cmdDepth.primitive.indexCount = primitive.getIndexCount(); - cmdDepth.primitive.mi = mi; + cmdDepth.primitive.primitive = &primitive; cmdDepth.primitive.rasterState.culling = mi->getCullingMode(); cmdDepth.primitive.skinningHandle = skinning.handle; @@ -895,17 +886,18 @@ void RenderPass::Executor::execute(FEngine& engine, } // primitiveHandle may be invalid if no geometry was set on the renderable. - if (UTILS_UNLIKELY(!first->primitive.primitiveHandle)) { + if (UTILS_UNLIKELY(!first->primitive.primitive->getHwHandle())) { continue; } // per-renderable uniform const PrimitiveInfo info = first->primitive; pipeline.rasterState = info.rasterState; + //pipeline.vertexBufferInfo = info.vertexBufferInfo; - if (UTILS_UNLIKELY(mi != info.mi)) { + if (UTILS_UNLIKELY(mi != info.primitive->getMaterialInstance())) { // this is always taken the first time - mi = info.mi; + mi = info.primitive->getMaterialInstance(); assert_invariant(mi); ma = mi->getMaterial(); @@ -1003,8 +995,9 @@ void RenderPass::Executor::execute(FEngine& engine, info.skinningTexture); } - driver.draw(pipeline, info.primitiveHandle, - info.indexOffset, info.indexCount, instanceCount); + driver.draw(pipeline, info.primitive->getHwHandle(), + info.primitive->getIndexOffset(), info.primitive->getIndexCount(), + instanceCount); } } diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index 358fa087d3..077779371d 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -53,6 +53,7 @@ class CommandBufferQueue; } class FMaterialInstance; +class FRenderPrimitive; class RenderPassBuilder; class RenderPass { @@ -235,13 +236,13 @@ public: return boolish ? value : uint64_t(0); } - struct PrimitiveInfo { // 48 bytes + struct PrimitiveInfo { // 56 bytes union { - FMaterialInstance const* mi; - uint64_t padding = {}; // ensures mi is 8 bytes on all archs + FRenderPrimitive const* primitive; // 8 bytes; + uint64_t padding = {}; // ensures primitive is 8 bytes on all archs }; // 8 bytes + uint64_t rfu0; // 8 bytes backend::RasterState rasterState; // 4 bytes - backend::Handle primitiveHandle; // 4 bytes backend::Handle skinningHandle; // 4 bytes backend::Handle skinningTexture; // 4 bytes backend::Handle morphWeightBuffer; // 4 bytes @@ -249,10 +250,9 @@ public: backend::Handle instanceBufferHandle; // 4 bytes uint32_t index = 0; // 4 bytes uint32_t skinningOffset = 0; // 4 bytes - uint32_t indexOffset; // 4 bytes - uint32_t indexCount; // 4 bytes uint16_t instanceCount; // 2 bytes [MSb: user] Variant materialVariant; // 1 byte + uint8_t rfu1; // 1 byte static const uint16_t USER_INSTANCE_MASK = 0x8000u; static const uint16_t INSTANCE_COUNT_MASK = 0x7fffu; diff --git a/filament/src/RenderPrimitive.h b/filament/src/RenderPrimitive.h index c85e999d2d..bc1c897484 100644 --- a/filament/src/RenderPrimitive.h +++ b/filament/src/RenderPrimitive.h @@ -55,6 +55,7 @@ public: backend::Handle getHwHandle() const noexcept { return mHandle; } uint32_t getIndexOffset() const noexcept { return mIndexOffset; } uint32_t getIndexCount() const noexcept { return mIndexCount; } + backend::PrimitiveType getPrimitiveType() const noexcept { return mPrimitiveType; } AttributeBitset getEnabledAttributes() const noexcept { return mEnabledAttributes; } uint16_t getBlendOrder() const noexcept { return mBlendOrder; } @@ -71,14 +72,19 @@ public: } private: - FMaterialInstance const* mMaterialInstance = nullptr; - backend::Handle mHandle = {}; + // These first fields are dereferences from PrimitiveInfo, keep them together + struct { + FMaterialInstance const* mMaterialInstance = nullptr; + backend::Handle mHandle = {}; + UTILS_UNUSED uint8_t padding[4]= {}; + uint32_t mIndexOffset = 0; + uint32_t mIndexCount = 0; + }; + AttributeBitset mEnabledAttributes = {}; uint16_t mBlendOrder = 0; bool mGlobalBlendOrderEnabled = false; backend::PrimitiveType mPrimitiveType = backend::PrimitiveType::TRIANGLES; - uint32_t mIndexOffset = 0; - uint32_t mIndexCount = 0; }; } // namespace filament From 859c5edb49d3883568fe79254e6aba374d3ce44f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 21 Feb 2024 23:51:57 -0800 Subject: [PATCH 13/33] Better fix for OOB when we have no renderable The OOB would happen is the scene never had any renderables, in that case the scene's SoA would stay unallocated, but the summedAreaTable code relies on it have at least a capacity of 1. It was incorrect to skip the RenderPass entirely because it might have had some custom commands that needed to be executed (e.g. for applying post-processing in subpass mode). --- filament/src/RenderPass.cpp | 4 ---- filament/src/details/Scene.cpp | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index c9074b829f..a9e3bdbb5b 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -99,10 +99,6 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce mScissorViewport(builder.mScissorViewport), mCustomCommands(engine.getPerRenderPassArena()) { - if (mVisibleRenderables.empty()) { - return; - } - // compute the number of commands we need updateSummedPrimitiveCounts( const_cast(mRenderableSoa), mVisibleRenderables); diff --git a/filament/src/details/Scene.cpp b/filament/src/details/Scene.cpp index 21840fa60c..ff6af6293a 100644 --- a/filament/src/details/Scene.cpp +++ b/filament/src/details/Scene.cpp @@ -148,7 +148,7 @@ void FScene::prepare(utils::JobSystem& js, // TODO: the resize below could happen in a job - if (sceneData.size() != renderableInstances.size()) { + if (!sceneData.capacity() || sceneData.size() != renderableInstances.size()) { sceneData.clear(); if (sceneData.capacity() < renderableDataCapacity) { sceneData.setCapacity(renderableDataCapacity); From bf602516ec37244024789af261332a3a906061e9 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Thu, 22 Feb 2024 13:25:25 -0800 Subject: [PATCH 14/33] Metal: throw an NSException when attempting to draw with invalid program (#7581) Throw an NSException when a program fails to compile and then is used for drawing; this helps aid debugging compiler errors in production, where stdout logs are not available. --- filament/backend/src/metal/MetalDriver.mm | 2 +- .../backend/src/metal/MetalShaderCompiler.h | 73 ++++++++++++++----- .../backend/src/metal/MetalShaderCompiler.mm | 32 +++++++- 3 files changed, 83 insertions(+), 24 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index df8414a9af..cbbaeecb37 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1610,7 +1610,7 @@ void MetalDriver::draw(PipelineState ps, Handle rph, return; } - ASSERT_PRECONDITION(bool(functions), "Attempting to draw with an invalid Metal program."); + functions.validate(); auto [fragment, vertex] = functions.getRasterFunctions(); diff --git a/filament/backend/src/metal/MetalShaderCompiler.h b/filament/backend/src/metal/MetalShaderCompiler.h index c3b3d4e1b8..5d967081e4 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.h +++ b/filament/backend/src/metal/MetalShaderCompiler.h @@ -30,6 +30,8 @@ #include #include +#include +#include namespace filament::backend { @@ -41,36 +43,69 @@ class MetalShaderCompiler { public: class MetalFunctionBundle { public: - MetalFunctionBundle() = default; - MetalFunctionBundle(id fragment, id vertex) - : functions{fragment, vertex} { + using Raster = std::tuple, id>; + using Compute = id; + using Error = std::tuple; // error message, Program name + struct None {}; + + MetalFunctionBundle() : mPrograms{None{}} {} + + explicit operator bool() const { return isValid(); } + + bool isValid() const noexcept { + return std::holds_alternative(mPrograms) || + std::holds_alternative(mPrograms); + } + + /** + * Throws an NSException if this MetalFunctionBundle either contains an error or is empty. + * + * If this MetalFunctionBundle contains an error, will throw a MetalCompilationFailure + * NSException with the error string and program name passed to + * MetalFunctionBundle::error(NSString*, NSString*). + * + * If this MetalFunctionBundle is empty, will throw a MetalEmptyFunctionBundle NSException. + */ + void validate() const; + + Raster getRasterFunctions() const { + assert_invariant(std::holds_alternative(mPrograms)); + return std::get(mPrograms); + } + + Compute getComputeFunction() const { + assert_invariant(std::holds_alternative(mPrograms)); + return std::get(mPrograms); + } + + static MetalFunctionBundle none() { + return MetalFunctionBundle(None{}); + } + + static MetalFunctionBundle raster(id fragment, id vertex) { assert_invariant(fragment && vertex); assert_invariant(fragment.functionType == MTLFunctionTypeFragment); assert_invariant(vertex.functionType == MTLFunctionTypeVertex); + return MetalFunctionBundle(Raster{fragment, vertex}); } - explicit MetalFunctionBundle(id compute) : functions{compute, nil} { + + static MetalFunctionBundle compute(id compute) { assert_invariant(compute); assert_invariant(compute.functionType == MTLFunctionTypeKernel); + return MetalFunctionBundle(Compute{compute}); } - std::pair, id> getRasterFunctions() const noexcept { - assert_invariant(functions[0].functionType == MTLFunctionTypeFragment); - assert_invariant(functions[1].functionType == MTLFunctionTypeVertex); - return {functions[0], functions[1]}; + static MetalFunctionBundle error(NSString* errorMessage, NSString* programName) { + return MetalFunctionBundle(Error{errorMessage, programName}); } - id getComputeFunction() const noexcept { - assert_invariant(functions[0].functionType == MTLFunctionTypeKernel); - return functions[0]; - } - - explicit operator bool() const { return functions[0] != nil; } - private: - // Can hold two functions, either: - // - fragment and vertex (for rasterization pipelines) - // - compute (for compute pipelines) - id functions[2] = {nil, nil}; + MetalFunctionBundle(None&& t) : mPrograms(std::move(t)) {} + MetalFunctionBundle(Raster&& t) : mPrograms(std::move(t)) {} + MetalFunctionBundle(Compute&& t) : mPrograms(std::move(t)) {} + MetalFunctionBundle(Error&& t) : mPrograms(std::move(t)) {} + + std::variant mPrograms; }; using program_token_t = std::shared_ptr; diff --git a/filament/backend/src/metal/MetalShaderCompiler.mm b/filament/backend/src/metal/MetalShaderCompiler.mm index 7bea1e2103..7af9c65944 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.mm +++ b/filament/backend/src/metal/MetalShaderCompiler.mm @@ -118,13 +118,16 @@ void MetalShaderCompiler::terminate() noexcept { options:options error:&error]; if (library == nil) { + NSString* errorMessage = @"unknown error"; if (error) { auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding]; utils::slog.w << description << utils::io::endl; + errorMessage = error.localizedDescription; } PANIC_LOG("Failed to compile Metal program."); - return {}; + NSString* programName = [NSString stringWithFormat:@"%s", program.getName().c_str_safe()]; + return MetalFunctionBundle::error(errorMessage, programName); } MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new]; @@ -160,14 +163,15 @@ void MetalShaderCompiler::terminate() noexcept { assert_invariant(isRasterizationProgram != isComputeProgram); if (isRasterizationProgram) { - return {fragmentFunction, vertexFunction}; + return MetalFunctionBundle::raster(fragmentFunction, vertexFunction); } if (isComputeProgram) { - return MetalFunctionBundle{computeFunction}; + return MetalFunctionBundle::compute(computeFunction); } - return {}; + // Should never reach here. + return MetalFunctionBundle::none(); } MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram( @@ -225,4 +229,24 @@ void MetalShaderCompiler::notifyWhenAllProgramsAreReady( mCallbackManager.setCallback(handler, callback, user); } +UTILS_NOINLINE +void MetalShaderCompiler::MetalFunctionBundle::validate() const { + if (UTILS_UNLIKELY(std::holds_alternative(mPrograms))) { + auto [errorMessage, programName] = std::get(mPrograms); + NSString* reason = + [NSString stringWithFormat: + @"Attempting to draw with an id that failed to compile.\n" + @"Program: %@\n" + @"%@", programName, errorMessage]; + [[NSException exceptionWithName:@"MetalCompilationFailure" + reason:reason + userInfo:nil] raise]; + } else if (UTILS_UNLIKELY(std::holds_alternative(mPrograms))) { + NSString* reason = @"Attempting to draw with an empty id."; + [[NSException exceptionWithName:@"MetalEmptyFunctionBundle" + reason:reason + userInfo:nil] raise]; + } +} + } // namespace filament::backend From 654a38c3bf49214c6f7d3d3d6b9b437d3e05a19a Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 22 Feb 2024 14:10:38 -0800 Subject: [PATCH 15/33] Fix broken Android build (#7590) The math for PrimitiveInfo's size is incorrect. We correct the padding. See commit 11a3c06418dfbfa5a5cfac8248b26b6af48633b1 to see where the padding error originated. --- filament/src/RenderPass.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index 077779371d..1184740bfe 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -253,6 +253,7 @@ public: uint16_t instanceCount; // 2 bytes [MSb: user] Variant materialVariant; // 1 byte uint8_t rfu1; // 1 byte + uint32_t rfu2; // 4 byte static const uint16_t USER_INSTANCE_MASK = 0x8000u; static const uint16_t INSTANCE_COUNT_MASK = 0x7fffu; @@ -261,7 +262,7 @@ public: struct alignas(8) Command { // 64 bytes CommandKey key = 0; // 8 bytes - PrimitiveInfo primitive; // 48 bytes + PrimitiveInfo primitive; // 56 bytes bool operator < (Command const& rhs) const noexcept { return key < rhs.key; } // placement new declared as "throw" to avoid the compiler's null-check inline void* operator new (size_t, void* ptr) { From d83b3858b3dbc97b2a5e039ae9fb5d170dd95907 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Thu, 22 Feb 2024 23:33:05 +0000 Subject: [PATCH 16/33] Add missing changes for NDK version update (#7591) These are missing parts from the commit 111ad96134dc171a9ca225821ed4f304ff9e1572. NDK 26.1.10909125 is used by default Minimum API level on Android is now API 21 instead of API 19. This allows the use of OpenGL ES 3.1 --- NEW_RELEASE_NOTES.md | 3 +++ android/build.gradle | 2 +- android/samples/sample-gltf-viewer/build.gradle | 2 +- build/android/ndk.version | 2 +- build/toolchain-arm7-linux-android.cmake | 2 +- build/toolchain-x86-linux-android.cmake | 2 +- .../backend/src/opengl/platforms/PlatformEGLAndroid.cpp | 8 +++----- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 4a1a9c7fa7..b9947d8a8e 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,3 +7,6 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut + +- android: NDK 26.1.10909125 is used by default +- android: Minimum API level on Android is now API 21 instead of API 19. This allows the use of OpenGL ES 3.1 diff --git a/android/build.gradle b/android/build.gradle index 960c67d4e8..4d6ebb91f8 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -80,7 +80,7 @@ buildscript { ext.versions = [ 'jdk': 17, - 'minSdk': 19, + 'minSdk': 21, 'targetSdk': 34, 'compileSdk': 34, 'kotlin': '1.9.21', diff --git a/android/samples/sample-gltf-viewer/build.gradle b/android/samples/sample-gltf-viewer/build.gradle index f934f01901..c2d0699017 100644 --- a/android/samples/sample-gltf-viewer/build.gradle +++ b/android/samples/sample-gltf-viewer/build.gradle @@ -30,7 +30,7 @@ android { compileSdkVersion versions.compileSdk defaultConfig { applicationId "com.google.android.filament.gltf" - minSdkVersion 19 + minSdkVersion versions.minSdk targetSdkVersion versions.targetSdk } diff --git a/build/android/ndk.version b/build/android/ndk.version index 46e2cdd860..365a3c4156 100644 --- a/build/android/ndk.version +++ b/build/android/ndk.version @@ -1 +1 @@ -25.1.8937393 \ No newline at end of file +26.1.10909125 \ No newline at end of file diff --git a/build/toolchain-arm7-linux-android.cmake b/build/toolchain-arm7-linux-android.cmake index ce94ab5659..a3f6036c32 100644 --- a/build/toolchain-arm7-linux-android.cmake +++ b/build/toolchain-arm7-linux-android.cmake @@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_VERSION 1) # android -set(API_LEVEL 19) +set(API_LEVEL 21) # architecture set(ARCH armv7a-linux-androideabi) diff --git a/build/toolchain-x86-linux-android.cmake b/build/toolchain-x86-linux-android.cmake index 88e1b1a79a..8c50712337 100644 --- a/build/toolchain-x86-linux-android.cmake +++ b/build/toolchain-x86-linux-android.cmake @@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_VERSION 1) # android -set(API_LEVEL 19) +set(API_LEVEL 21) # architecture set(ARCH i686-linux-android) diff --git a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp index b61bbeab9a..c705062238 100644 --- a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp +++ b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp @@ -29,11 +29,9 @@ #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. -#if __ANDROID_API__ < 19 -# error "__ANDROID_API__ must be at least 19" +// We require filament to be built with a API 21 toolchain, before that, OpenGLES 3.1 didn't exist +#if __ANDROID_API__ < 21 +# error "__ANDROID_API__ must be at least 21" #endif using namespace utils; From 2260794a559c6fd926402bd694bcec91ad0663c1 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 20 Feb 2024 13:48:38 -0800 Subject: [PATCH 17/33] remove scissor from PipelineState We do this to better match Gl, Vulkan and Metal, which don't need to specify the scissor in the pipeline. In practice, this will also allow us to set the scissor less often, saving a bit of CPU. --- .../backend/include/backend/PipelineState.h | 1 - .../include/private/backend/DriverAPI.inc | 3 + filament/backend/src/metal/MetalDriver.mm | 62 +++++++------ filament/backend/src/noop/NoopDriver.cpp | 4 + filament/backend/src/opengl/OpenGLDriver.cpp | 6 +- filament/backend/src/vulkan/VulkanDriver.cpp | 49 +++++----- filament/backend/test/test_MipLevels.cpp | 3 +- filament/backend/test/test_Scissor.cpp | 5 +- filament/src/PostProcessManager.cpp | 91 ++++++++++--------- filament/src/PostProcessManager.h | 10 +- filament/src/RenderPass.cpp | 78 ++++++++-------- filament/src/RenderPass.h | 4 + 12 files changed, 178 insertions(+), 138 deletions(-) diff --git a/filament/backend/include/backend/PipelineState.h b/filament/backend/include/backend/PipelineState.h index aa98dda606..04001345d8 100644 --- a/filament/backend/include/backend/PipelineState.h +++ b/filament/backend/include/backend/PipelineState.h @@ -33,7 +33,6 @@ struct PipelineState { RasterState rasterState; StencilState stencilState; PolygonOffset polygonOffset; - Viewport scissor{ 0, 0, (uint32_t)INT32_MAX, (uint32_t)INT32_MAX }; }; } // namespace filament::backend diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index b29551153c..d3f9dd43ca 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -502,6 +502,9 @@ DECL_DRIVER_API_N(dispatchCompute, backend::ProgramHandle, program, math::uint3, workGroupCount) +DECL_DRIVER_API_N(scissor, + Viewport, scissor) + #pragma clang diagnostic pop diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index cbbaeecb37..1bf3fc9337 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1731,36 +1731,6 @@ void MetalDriver::draw(PipelineState ps, Handle rph, mContext->currentPolygonOffset = ps.polygonOffset; } - // Set scissor-rectangle. - // In order to do this, we compute the intersection between: - // 1. the scissor rectangle - // 2. the render target attachment dimensions (important, as the scissor can't be set larger) - // fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion - // coordinates are doubles. - MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(ps.scissor); - const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width; - const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height; - - // Attachment extent - const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize(); - const float aleft = 0.0f, atop = 0.0f; - const float aright = static_cast(attachmentSize.x); - const float abottom = static_cast(attachmentSize.y); - - const auto left = std::fmax(sleft, aleft); - const auto right = std::fmin(sright, aright); - const auto top = std::fmax(stop, atop); - const auto bottom = std::fmin(sbottom, abottom); - - MTLScissorRect scissorRect = { - .x = static_cast(left), - .y = static_cast(top), - .width = static_cast(right - left), - .height = static_cast(bottom - top) - }; - - [mContext->currentRenderPassEncoder setScissorRect:scissorRect]; - // Bind uniform buffers. MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil }; NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 }; @@ -1914,6 +1884,38 @@ void MetalDriver::dispatchCompute(Handle program, math::uint3 workGro [computeEncoder endEncoding]; } +void MetalDriver::scissor(Viewport scissorBox) { + // Set scissor-rectangle. + // In order to do this, we compute the intersection between: + // 1. the scissor rectangle + // 2. the render target attachment dimensions (important, as the scissor can't be set larger) + // fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion + // coordinates are doubles. + MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(scissorBox); + const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width; + const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height; + + // Attachment extent + const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize(); + const float aleft = 0.0f, atop = 0.0f; + const float aright = static_cast(attachmentSize.x); + const float abottom = static_cast(attachmentSize.y); + + const auto left = std::fmax(sleft, aleft); + const auto right = std::fmin(sright, aright); + const auto top = std::fmax(stop, atop); + const auto bottom = std::fmin(sbottom, abottom); + + MTLScissorRect scissorRect = { + .x = static_cast(left), + .y = static_cast(top), + .width = static_cast(right - left), + .height = static_cast(bottom - top) + }; + + [mContext->currentRenderPassEncoder setScissorRect:scissorRect]; +} + void MetalDriver::beginTimerQuery(Handle tqh) { ASSERT_PRECONDITION(!isInRenderPass(mContext), "beginTimerQuery must be called outside of a render pass."); diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index c8b5bd4397..797a1a61b0 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -349,6 +349,10 @@ void NoopDriver::draw(PipelineState pipelineState, Handle rph void NoopDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) { } +void NoopDriver::scissor( + Viewport scissor) { +} + void NoopDriver::beginTimerQuery(Handle tqh) { } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index e47047c150..dc1744186b 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -3759,8 +3759,6 @@ void OpenGLDriver::draw(PipelineState state, Handle rph, gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant); - setScissor(state.scissor); - if (UTILS_LIKELY(instanceCount <= 1)) { glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(), reinterpret_cast(indexOffset * rp->gl.indicesSize)); @@ -3781,6 +3779,10 @@ void OpenGLDriver::draw(PipelineState state, Handle rph, #endif } +void OpenGLDriver::scissor(Viewport scissor) { + setScissor(scissor); +} + void OpenGLDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) { getShaderCompilerService().tick(); diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 2157cabc2c..740995e7b5 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1716,7 +1716,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r Handle programHandle = pipelineState.program; RasterState rasterState = pipelineState.rasterState; PolygonOffset depthOffset = pipelineState.polygonOffset; - Viewport const& scissorBox = pipelineState.scissor; auto* program = mResourceAllocator.handle_cast(programHandle); commands->acquire(program); @@ -1850,27 +1849,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r return; } - // Set scissoring. - // clamp left-bottom to 0,0 and avoid overflows - constexpr int32_t maxvali = std::numeric_limits::max(); - constexpr uint32_t maxvalu = std::numeric_limits::max(); - int32_t l = scissorBox.left; - int32_t b = scissorBox.bottom; - uint32_t w = std::min(maxvalu, scissorBox.width); - uint32_t h = std::min(maxvalu, scissorBox.height); - int32_t r = (l > int32_t(maxvalu - w)) ? maxvali : l + int32_t(w); - int32_t t = (b > int32_t(maxvalu - h)) ? maxvali : b + int32_t(h); - l = std::max(0, l); - b = std::max(0, b); - assert_invariant(r >= l && t >= b); - VkRect2D scissor{ - .offset = { l, b }, - .extent = { uint32_t(r - l), uint32_t(t - b) } - }; - - rt->transformClientRectToPlatform(&scissor); - mPipelineCache.bindScissor(cmdbuffer, scissor); - // 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. @@ -1898,6 +1876,33 @@ void VulkanDriver::dispatchCompute(Handle program, math::uint3 workGr // FIXME: implement me } +void VulkanDriver::scissor(Viewport scissorBox) { + VulkanCommandBuffer* commands = &mCommands->get(); + VkCommandBuffer cmdbuffer = commands->buffer(); + + // Set scissoring. + // clamp left-bottom to 0,0 and avoid overflows + constexpr int32_t maxvali = std::numeric_limits::max(); + constexpr uint32_t maxvalu = std::numeric_limits::max(); + int32_t l = scissorBox.left; + int32_t b = scissorBox.bottom; + uint32_t w = std::min(maxvalu, scissorBox.width); + uint32_t h = std::min(maxvalu, scissorBox.height); + int32_t r = (l > int32_t(maxvalu - w)) ? maxvali : l + int32_t(w); + int32_t t = (b > int32_t(maxvalu - h)) ? maxvali : b + int32_t(h); + l = std::max(0, l); + b = std::max(0, b); + assert_invariant(r >= l && t >= b); + VkRect2D scissor{ + .offset = { l, b }, + .extent = { uint32_t(r - l), uint32_t(t - b) } + }; + + const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; + rt->transformClientRectToPlatform(&scissor); + mPipelineCache.bindScissor(cmdbuffer, scissor); +} + void VulkanDriver::beginTimerQuery(Handle tqh) { VulkanTimerQuery* vtq = mResourceAllocator.handle_cast(tqh); mTimestamps->beginQuery(&(mCommands->get()), vtq); diff --git a/filament/backend/test/test_MipLevels.cpp b/filament/backend/test/test_MipLevels.cpp index 32b88f0a6b..42b4d4f695 100644 --- a/filament/backend/test/test_MipLevels.cpp +++ b/filament/backend/test/test_MipLevels.cpp @@ -182,7 +182,6 @@ TEST_F(BackendTest, SetMinMaxLevel) { params.flags.discardEnd = TargetBufferFlags::NONE; PipelineState state; - state.scissor = params.viewport; state.program = textureProgram; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; @@ -203,6 +202,7 @@ TEST_F(BackendTest, SetMinMaxLevel) { // Because the min level is 1, the result color should be the white triangle drawn in the // previous pass. api.beginRenderPass(defaultRenderTarget, params); + api.scissor(params.viewport); api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); @@ -221,6 +221,7 @@ TEST_F(BackendTest, SetMinMaxLevel) { params.flags.clear = TargetBufferFlags::NONE; params.flags.discardStart = TargetBufferFlags::NONE; api.beginRenderPass(defaultRenderTarget, params); + api.scissor(params.viewport); api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); diff --git a/filament/backend/test/test_Scissor.cpp b/filament/backend/test/test_Scissor.cpp index c048e4ccf6..ec27e58ce3 100644 --- a/filament/backend/test/test_Scissor.cpp +++ b/filament/backend/test/test_Scissor.cpp @@ -134,12 +134,12 @@ TEST_F(BackendTest, ScissorViewportRegion) { ps.program = program; ps.rasterState.colorWrite = true; ps.rasterState.depthWrite = false; - ps.scissor = scissor; api.makeCurrent(swapChain, swapChain); api.beginFrame(0, 0); api.beginRenderPass(srcRenderTarget, params); + api.scissor(scissor); api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); @@ -225,12 +225,12 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) { ps.program = program; ps.rasterState.colorWrite = true; ps.rasterState.depthWrite = false; - ps.scissor = scissor; api.makeCurrent(swapChain, swapChain); api.beginFrame(0, 0); api.beginRenderPass(renderTarget, params); + api.scissor(scissor); api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); @@ -238,6 +238,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) { params.flags.clear = TargetBufferFlags::NONE; params.flags.discardStart = TargetBufferFlags::NONE; api.beginRenderPass(renderTarget, params); + api.scissor(scissor); api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 9228d6caca..eb4fcf55f8 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -184,15 +184,15 @@ FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine) } UTILS_NOINLINE -PipelineState PostProcessManager::PostProcessMaterial::getPipelineState( +std::pair + PostProcessManager::PostProcessMaterial::getPipelineState( FEngine& engine, Variant::type_t variantKey) const noexcept { FMaterial* const material = getMaterial(engine); material->prepareProgram(Variant{ variantKey }); - return { - .program = material->getProgram(Variant{ variantKey }), - .rasterState = material->getRasterState(), - .scissor = material->getDefaultInstance()->getScissor() - }; + return {{ + .program = material->getProgram(Variant{ variantKey }), + .rasterState = material->getRasterState() }, + material->getDefaultInstance()->getScissor() }; } UTILS_NOINLINE @@ -381,7 +381,7 @@ backend::Handle PostProcessManager::getZeroTextureArray() co UTILS_NOINLINE void PostProcessManager::render(FrameGraphResources::RenderPassInfo const& out, - backend::PipelineState const& pipeline, + backend::PipelineState const& pipeline, backend::Viewport const& scissor, DriverApi& driver) const noexcept { assert_invariant( @@ -392,6 +392,7 @@ void PostProcessManager::render(FrameGraphResources::RenderPassInfo const& out, FEngine const& engine = mEngine; Handle const fullScreenQuad = engine.getFullScreenRenderPrimitive(); driver.beginRenderPass(out.target, out.params); + driver.scissor(scissor); driver.draw(pipeline, fullScreenQuad, 0, 3, 1); driver.endRenderPass(); } @@ -878,8 +879,8 @@ FrameGraphId PostProcessManager::screenSpaceAmbientOcclusion( mi->commit(driver); mi->use(driver); - PipelineState pipeline(material.getPipelineState(mEngine)); - pipeline.rasterState.depthFunc = RasterState::DepthFunc::L; + auto pipeline = material.getPipelineState(mEngine); + pipeline.first.rasterState.depthFunc = RasterState::DepthFunc::L; assert_invariant(ssao.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH); render(ssao, pipeline, driver); }); @@ -993,8 +994,8 @@ FrameGraphId PostProcessManager::bilateralBlurPass(FrameGraph mi->commit(driver); mi->use(driver); - PipelineState pipeline(material.getPipelineState(mEngine)); - pipeline.rasterState.depthFunc = RasterState::DepthFunc::L; + auto pipeline = material.getPipelineState(mEngine); + pipeline.first.rasterState.depthFunc = RasterState::DepthFunc::L; render(blurred, pipeline, driver); }); @@ -1653,7 +1654,7 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, mi->setParameter("coc", inOutCoc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); mi->use(driver); - const PipelineState pipeline(material.getPipelineState(mEngine, variant)); + auto const pipeline = material.getPipelineState(mEngine, variant); for (size_t level = 0 ; level < mipmapCount - 1u ; level++) { const float w = FTexture::valueForLevel(level, desc.width); @@ -2100,7 +2101,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg, mi13->commit(driver); // PipelineState for both materials should be the same - const PipelineState pipeline(material9.getPipelineState(mEngine)); + auto const pipeline = material9.getPipelineState(mEngine); for (size_t i = 1; i < inoutBloomOptions.levels; i++) { auto hwDstRT = resources.getRenderPassInfo(data.outRT[i]); @@ -2144,9 +2145,9 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg, .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST}); mi->use(driver); - PipelineState pipeline(material.getPipelineState(mEngine)); - pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE; - pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE; + auto pipeline = material.getPipelineState(mEngine); + pipeline.first.rasterState.blendFunctionSrcRGB = BlendFunction::ONE; + pipeline.first.rasterState.blendFunctionDstRGB = BlendFunction::ONE; for (size_t j = inoutBloomOptions.levels, i = j - 1; i >= 1; i--, j++) { auto hwDstRT = resources.getRenderPassInfo(data.outRT[i - 1]); @@ -2299,8 +2300,11 @@ void PostProcessManager::colorGradingSubpass(DriverApi& driver, const Variant::type_t variant = Variant::type_t(colorGradingConfig.translucent ? PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE); + auto const pipeline = material.getPipelineState(mEngine, variant); + driver.nextSubpass(); - driver.draw(material.getPipelineState(mEngine, variant), fullScreenRenderPrimitive, 0, 3, 1); + driver.scissor(pipeline.second); + driver.draw(pipeline.first, fullScreenRenderPrimitive, 0, 3, 1); } void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomResolveOp op) noexcept { @@ -2316,10 +2320,12 @@ void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept { Handle const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive(); auto const& material = getPostProcessMaterial("customResolveAsSubpass"); // the UBO has been set and committed in colorGradingPrepareSubpass() + auto const pipeline = material.getPipelineState(mEngine); FMaterialInstance* mi = material.getMaterialInstance(mEngine); mi->use(driver); driver.nextSubpass(); - driver.draw(material.getPipelineState(mEngine), fullScreenRenderPrimitive, 0, 3, 1); + driver.scissor(pipeline.second); + driver.draw(pipeline.first, fullScreenRenderPrimitive, 0, 3, 1); } FrameGraphId PostProcessManager::customResolveUncompressPass(FrameGraph& fg, @@ -2762,9 +2768,10 @@ FrameGraphId PostProcessManager::taa(FrameGraph& fg, if (colorGradingConfig.asSubpass) { out.params.subpassMask = 1; } - PipelineState const pipeline(material.getPipelineState(mEngine, variant)); + auto const pipeline = material.getPipelineState(mEngine, variant); driver.beginRenderPass(out.target, out.params); - driver.draw(pipeline, mEngine.getFullScreenRenderPrimitive(), 0, 3, 1); + driver.scissor(pipeline.second); + driver.draw(pipeline.first, mEngine.getFullScreenRenderPrimitive(), 0, 3, 1); if (colorGradingConfig.asSubpass) { colorGradingSubpass(driver, colorGradingConfig); } @@ -2838,7 +2845,7 @@ FrameGraphId PostProcessManager::rcas( const uint8_t variant = uint8_t( translucent ? PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE); - PipelineState const pipeline(material.getPipelineState(mEngine, variant)); + auto const pipeline = material.getPipelineState(mEngine, variant); render(out, pipeline, driver); }); @@ -2980,25 +2987,25 @@ FrameGraphId PostProcessManager::upscale(FrameGraph& fg, bool auto out = resources.getRenderPassInfo(); if (UTILS_UNLIKELY(twoPassesEASU)) { - PipelineState pipeline0(splitEasuMaterial->getPipelineState(mEngine)); - PipelineState pipeline1(easuMaterial->getPipelineState(mEngine)); - pipeline1.rasterState.depthFunc = backend::SamplerCompareFunc::NE; + auto pipeline0 = splitEasuMaterial->getPipelineState(mEngine); + auto pipeline1 = easuMaterial->getPipelineState(mEngine); + pipeline1.first.rasterState.depthFunc = backend::SamplerCompareFunc::NE; if (translucent) { - enableTranslucentBlending(pipeline0); - enableTranslucentBlending(pipeline1); + enableTranslucentBlending(pipeline0.first); + enableTranslucentBlending(pipeline1.first); } driver.beginRenderPass(out.target, out.params); - driver.draw(pipeline0, fullScreenRenderPrimitive, 0, 3, 1); - driver.draw(pipeline1, fullScreenRenderPrimitive, 0, 3, 1); + driver.scissor(pipeline0.second); + driver.draw(pipeline0.first, fullScreenRenderPrimitive, 0, 3, 1); + driver.scissor(pipeline1.second); + driver.draw(pipeline1.first, fullScreenRenderPrimitive, 0, 3, 1); driver.endRenderPass(); } else { - PipelineState pipeline(easuMaterial->getPipelineState(mEngine)); + auto pipeline = easuMaterial->getPipelineState(mEngine); if (translucent) { - enableTranslucentBlending(pipeline); + enableTranslucentBlending(pipeline.first); } - driver.beginRenderPass(out.target, out.params); - driver.draw(pipeline, fullScreenRenderPrimitive, 0, 3, 1); - driver.endRenderPass(); + render(out, pipeline, driver); } }); @@ -3067,12 +3074,12 @@ FrameGraphId PostProcessManager::blit(FrameGraph& fg, bool tr mi->commit(driver); mi->use(driver); - PipelineState pipeline(material.getPipelineState(mEngine)); + auto pipeline = material.getPipelineState(mEngine); if (translucent) { - pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE; - pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE; - pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA; - pipeline.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA; + pipeline.first.rasterState.blendFunctionSrcRGB = BlendFunction::ONE; + pipeline.first.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE; + pipeline.first.rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA; + pipeline.first.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA; } render(out, pipeline, driver); }); @@ -3218,8 +3225,8 @@ FrameGraphId PostProcessManager::vsmMipmapPass(FrameGraph& fg // When generating shadow map mip levels, we want to preserve the 1 texel border. // (note clearing never respects the scissor in Filament) - PipelineState pipeline(material.getPipelineState(mEngine)); - pipeline.scissor = { 1u, 1u, dim - 2u, dim - 2u }; + auto const [pipeline, _] = material.getPipelineState(mEngine); + backend::Viewport const scissor = { 1u, 1u, dim - 2u, dim - 2u }; FMaterialInstance* const mi = material.getMaterialInstance(mEngine); mi->setParameter("color", in, { @@ -3230,7 +3237,7 @@ FrameGraphId PostProcessManager::vsmMipmapPass(FrameGraph& fg mi->setParameter("uvscale", 1.0f / float(dim)); mi->commit(driver); mi->use(driver); - render(out, pipeline, driver); + render(out, pipeline, scissor, driver); if (finalize) { driver.setMinMaxLevels(in, 0, level); @@ -3263,7 +3270,7 @@ FrameGraphId PostProcessManager::debugShadowCascades(FrameGra auto depth = resources.getTexture(data.depth); auto out = resources.getRenderPassInfo(); auto& material = getPostProcessMaterial("debugShadowCascades"); - PipelineState const pipeline(material.getPipelineState(mEngine)); + auto const pipeline = material.getPipelineState(mEngine); FMaterialInstance* mi = material.getMaterialInstance(mEngine); mi->setParameter("color", color, {}); // nearest mi->setParameter("depth", depth, {}); // nearest diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index 12b211dc23..badf5b9a8c 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -311,7 +311,7 @@ public: FMaterial* getMaterial(FEngine& engine) const noexcept; FMaterialInstance* getMaterialInstance(FEngine& engine) const noexcept; - backend::PipelineState getPipelineState(FEngine& engine, + std::pair getPipelineState(FEngine& engine, Variant::type_t variantKey = 0u) const noexcept; private: @@ -339,9 +339,15 @@ public: backend::DriverApi& driver) const noexcept; void render(FrameGraphResources::RenderPassInfo const& out, - backend::PipelineState const& pipeline, + backend::PipelineState const& pipeline, backend::Viewport const& scissor, backend::DriverApi& driver) const noexcept; + void render(FrameGraphResources::RenderPassInfo const& out, + std::pair const& combo, + backend::DriverApi& driver) const noexcept { + render(out, combo.first, combo.second, driver); + } + private: FEngine& mEngine; diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index a9e3bdbb5b..cde9c1ffb4 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -812,6 +812,28 @@ void RenderPass::Executor::execute(FEngine& engine, const char*) const noexcept execute(engine, mCommands.begin(), mCommands.end()); } +UTILS_NOINLINE // no need to be inlined +backend::Viewport RenderPass::Executor::applyScissorViewport( + backend::Viewport const& scissorViewport, backend::Viewport const& scissor) noexcept { + // scissor is set, we need to apply the offset/clip + // clang vectorizes this! + constexpr int32_t maxvali = std::numeric_limits::max(); + // compute new left/bottom, assume no overflow + int32_t l = scissor.left + scissorViewport.left; + int32_t b = scissor.bottom + scissorViewport.bottom; + // compute right/top without overflowing, scissor.width/height guaranteed + // to convert to int32 + int32_t r = (l > maxvali - int32_t(scissor.width)) ? maxvali : l + int32_t(scissor.width); + int32_t t = (b > maxvali - int32_t(scissor.height)) ? maxvali : b + int32_t(scissor.height); + // clip to the viewport + l = std::max(l, scissorViewport.left); + b = std::max(b, scissorViewport.bottom); + r = std::min(r, scissorViewport.left + int32_t(scissorViewport.width)); + t = std::min(t, scissorViewport.bottom + int32_t(scissorViewport.height)); + assert_invariant(r >= l && t >= b); + return { l, b, uint32_t(r - l), uint32_t(t - b) }; +} + UTILS_NOINLINE // no need to be inlined void RenderPass::Executor::execute(FEngine& engine, const Command* first, const Command* last) const noexcept { @@ -826,16 +848,17 @@ void RenderPass::Executor::execute(FEngine& engine, if (first != last) { SYSTRACE_VALUE32("commandCount", last - first); + bool const scissorOverride = mScissorOverride; + if (UTILS_UNLIKELY(scissorOverride)) { + // initialize with scissor overide + driver.scissor(mScissor); + } + + bool const polygonOffsetOverride = mPolygonOffsetOverride; PipelineState pipeline{ + // initialize with polygon offset override .polygonOffset = mPolygonOffset, - .scissor = mScissor - }, dummyPipeline; - - auto* const pPipelinePolygonOffset = - mPolygonOffsetOverride ? &dummyPipeline.polygonOffset : &pipeline.polygonOffset; - - auto* const pScissor = - mScissorOverride ? &dummyPipeline.scissor : &pipeline.scissor; + }; FMaterialInstance const* UTILS_RESTRICT mi = nullptr; FMaterial const* UTILS_RESTRICT ma = nullptr; @@ -887,7 +910,7 @@ void RenderPass::Executor::execute(FEngine& engine, } // per-renderable uniform - const PrimitiveInfo info = first->primitive; + PrimitiveInfo const info = first->primitive; pipeline.rasterState = info.rasterState; //pipeline.vertexBufferInfo = info.vertexBufferInfo; @@ -898,34 +921,17 @@ void RenderPass::Executor::execute(FEngine& engine, ma = mi->getMaterial(); - auto const& scissor = mi->getScissor(); - if (UTILS_UNLIKELY(mi->hasScissor())) { - // scissor is set, we need to apply the offset/clip - // clang vectorizes this! - constexpr int32_t maxvali = std::numeric_limits::max(); - const backend::Viewport scissorViewport = mScissorViewport; - // compute new left/bottom, assume no overflow - int32_t l = scissor.left + scissorViewport.left; - int32_t b = scissor.bottom + scissorViewport.bottom; - // compute right/top without overflowing, scissor.width/height guaranteed - // to convert to int32 - int32_t r = (l > maxvali - int32_t(scissor.width)) ? - maxvali : l + int32_t(scissor.width); - int32_t t = (b > maxvali - int32_t(scissor.height)) ? - maxvali : b + int32_t(scissor.height); - // clip to the viewport - l = std::max(l, scissorViewport.left); - b = std::max(b, scissorViewport.bottom); - r = std::min(r, scissorViewport.left + int32_t(scissorViewport.width)); - t = std::min(t, scissorViewport.bottom + int32_t(scissorViewport.height)); - assert_invariant(r >= l && t >= b); - *pScissor = { l, b, uint32_t(r - l), uint32_t(t - b) }; - } else { - // no scissor set (common case), 'scissor' has its default value, use that. - *pScissor = scissor; - } + if (UTILS_LIKELY(!scissorOverride)) { + backend::Viewport scissor = mi->getScissor(); + if (UTILS_UNLIKELY(mi->hasScissor())) { + scissor = applyScissorViewport(mScissorViewport, scissor); + } + driver.scissor(scissor); + } - *pPipelinePolygonOffset = mi->getPolygonOffset(); + if (UTILS_LIKELY(!polygonOffsetOverride)) { + pipeline.polygonOffset = mi->getPolygonOffset(); + } pipeline.stencilState = mi->getStencilState(); mi->use(driver); } diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index 1184740bfe..a2ae834c00 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -331,6 +331,10 @@ public: void execute(FEngine& engine, const Command* first, const Command* last) const noexcept; + static backend::Viewport applyScissorViewport( + backend::Viewport const& scissorViewport, + backend::Viewport const& scissor) noexcept; + public: Executor() = default; Executor(Executor const& rhs); From a9793b3cf619394dc738e3483222a7cff6b81dcf Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 22 Feb 2024 21:29:58 -0800 Subject: [PATCH 18/33] vk: remove subpasses to simplify descriptor set refactor (#7592) - Also did some clean up of debug code --- filament/backend/src/vulkan/VulkanConstants.h | 12 ++- filament/backend/src/vulkan/VulkanContext.h | 1 - filament/backend/src/vulkan/VulkanDriver.cpp | 34 +------- .../backend/src/vulkan/VulkanFboCache.cpp | 65 ++------------ filament/backend/src/vulkan/VulkanHandles.cpp | 5 +- .../src/vulkan/VulkanPipelineCache.cpp | 85 +++---------------- .../backend/src/vulkan/VulkanPipelineCache.h | 47 +++++----- 7 files changed, 50 insertions(+), 199 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index a2b62bd34c..b0a05d6100 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -66,8 +66,7 @@ // 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,18 +79,17 @@ 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)) +#define FVK_ENABLED_BOOL(flags) FVK_ENABLED(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 -#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) +#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) static_assert(FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_VALIDATION)); #endif diff --git a/filament/backend/src/vulkan/VulkanContext.h b/filament/backend/src/vulkan/VulkanContext.h index c9a4fa2c4e..88b9904276 100644 --- a/filament/backend/src/vulkan/VulkanContext.h +++ b/filament/backend/src/vulkan/VulkanContext.h @@ -83,7 +83,6 @@ struct VulkanRenderPass { VulkanRenderTarget* renderTarget; VkRenderPass renderPass; RenderPassParams params; - int currentSubpass; }; // This is a collection of immutable data about the vulkan context. This actual handles to the diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 740995e7b5..f732160947 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -848,7 +848,7 @@ bool VulkanDriver::isRenderTargetFormatSupported(TextureFormat format) { } bool VulkanDriver::isFrameBufferFetchSupported() { - return true; + return false; } bool VulkanDriver::isFrameBufferFetchMultiSampleSupported() { @@ -1247,7 +1247,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP } VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey); - mPipelineCache.bindRenderPass(renderPass, 0); + mPipelineCache.bindRenderPass(renderPass); // Create the VkFramebuffer or fetch it from cache. VulkanFboCache::FboKey fbkey { @@ -1372,7 +1372,6 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP .renderTarget = rt, .renderPass = renderPassInfo.renderPass, .params = params, - .currentSubpass = 0, }; FVK_SYSTRACE_END(); } @@ -1415,40 +1414,13 @@ void VulkanDriver::endRenderPass(int) { 0, 1, &barrier, 0, nullptr, 0, nullptr); } - if (mCurrentRenderPass.currentSubpass > 0) { - for (uint32_t i = 0; i < VulkanPipelineCache::INPUT_ATTACHMENT_COUNT; i++) { - mPipelineCache.bindInputAttachment(i, {}); - } - mCurrentRenderPass.currentSubpass = 0; - } mCurrentRenderPass.renderTarget = nullptr; mCurrentRenderPass.renderPass = VK_NULL_HANDLE; FVK_SYSTRACE_END(); } void VulkanDriver::nextSubpass(int) { - ASSERT_PRECONDITION(mCurrentRenderPass.currentSubpass == 0, - "Only two subpasses are currently supported."); - - VulkanRenderTarget* renderTarget = mCurrentRenderPass.renderTarget; - assert_invariant(renderTarget); - assert_invariant(mCurrentRenderPass.params.subpassMask); - - vkCmdNextSubpass(mCommands->get().buffer(), VK_SUBPASS_CONTENTS_INLINE); - - mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass, - ++mCurrentRenderPass.currentSubpass); - - for (uint32_t i = 0; i < VulkanPipelineCache::INPUT_ATTACHMENT_COUNT; i++) { - if ((1 << i) & mCurrentRenderPass.params.subpassMask) { - VulkanAttachment subpassInput = renderTarget->getColor(i); - VkDescriptorImageInfo info = { - .imageView = subpassInput.getImageView(VK_IMAGE_ASPECT_COLOR_BIT), - .imageLayout = ImgUtil::getVkLayout(subpassInput.getLayout()), - }; - mPipelineCache.bindInputAttachment(i, info); - } - } + PANIC_POSTCONDITION("Subpasses are unsupported"); } void VulkanDriver::setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt, diff --git a/filament/backend/src/vulkan/VulkanFboCache.cpp b/filament/backend/src/vulkan/VulkanFboCache.cpp index 0546ffd04f..200ea23e02 100644 --- a/filament/backend/src/vulkan/VulkanFboCache.cpp +++ b/filament/backend/src/vulkan/VulkanFboCache.cpp @@ -129,7 +129,6 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { iter.value().timestamp = mCurrentTime; return iter->second.handle; } - const bool hasSubpasses = config.subpassMask != 0; // Set up some const aliases for terseness. const VkAttachmentLoadOp kClear = VK_ATTACHMENT_LOAD_OP_CLEAR; @@ -141,26 +140,18 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { // In Vulkan, the subpass desc specifies the layout to transition to at the start of the render // pass, and the attachment description specifies the layout to transition to at the end. - VkAttachmentReference inputAttachmentRef[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; VkAttachmentReference colorAttachmentRefs[2][MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; VkAttachmentReference resolveAttachmentRef[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; VkAttachmentReference depthAttachmentRef = {}; const bool hasDepth = config.depthFormat != VK_FORMAT_UNDEFINED; - VkSubpassDescription subpasses[2] = {{ + VkSubpassDescription subpasses[1] = {{ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .pInputAttachments = nullptr, .pColorAttachments = colorAttachmentRefs[0], .pResolveAttachments = resolveAttachmentRef, .pDepthStencilAttachment = hasDepth ? &depthAttachmentRef : nullptr - }, - { - .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, - .pInputAttachments = inputAttachmentRef, - .pColorAttachments = colorAttachmentRefs[1], - .pResolveAttachments = resolveAttachmentRef, - .pDepthStencilAttachment = hasDepth ? &depthAttachmentRef : nullptr }}; // The attachment list contains: Color Attachments, Resolve Attachments, and Depth Attachment. @@ -168,29 +159,20 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { // Note that this needs to have the same ordering as the corollary array in getFramebuffer. VkAttachmentDescription attachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 1] = {}; - // We support 2 subpasses, which means we need to supply 1 dependency struct. - VkSubpassDependency dependencies[1] = {{ - .srcSubpass = 0, - .dstSubpass = 1, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, - }}; - VkRenderPassCreateInfo renderPassInfo { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .attachmentCount = 0u, .pAttachments = attachments, - .subpassCount = hasSubpasses ? 2u : 1u, + .subpassCount = 1u, .pSubpasses = subpasses, - .dependencyCount = hasSubpasses ? 1u : 0u, - .pDependencies = dependencies + .dependencyCount = 0u, + .pDependencies = nullptr, }; int attachmentIndex = 0; + assert_invariant(config.subpassMask == 0 && "Subpass is not supported"); + // Populate the Color Attachments. for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { if (config.colorFormat[i] == VK_FORMAT_UNDEFINED) { @@ -199,36 +181,9 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { const VkImageLayout subpassLayout = ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT); uint32_t index; - if (!hasSubpasses) { - index = subpasses[0].colorAttachmentCount++; - colorAttachmentRefs[0][index].layout = subpassLayout; - colorAttachmentRefs[0][index].attachment = attachmentIndex; - } else { - - // The Driver API consolidates all color attachments from the first and second subpasses - // into a single list, and uses a bitmask to mark attachments that belong only to the - // second subpass and should be available as inputs. All color attachments in the first - // subpass are automatically made available to the second subpass. - - // If there are subpasses, we require the input attachment to be the first attachment. - // Breaking this assumption would likely require enhancements to the Driver API in order - // to supply Vulkan with all the information needed. - assert_invariant(config.subpassMask == 1); - - if (config.subpassMask & (1 << i)) { - index = subpasses[0].colorAttachmentCount++; - colorAttachmentRefs[0][index].layout = subpassLayout; - colorAttachmentRefs[0][index].attachment = attachmentIndex; - - index = subpasses[1].inputAttachmentCount++; - inputAttachmentRef[index].layout = subpassLayout; - inputAttachmentRef[index].attachment = attachmentIndex; - } - - index = subpasses[1].colorAttachmentCount++; - colorAttachmentRefs[1][index].layout = subpassLayout; - colorAttachmentRefs[1][index].attachment = attachmentIndex; - } + index = subpasses[0].colorAttachmentCount++; + colorAttachmentRefs[0][index].layout = subpassLayout; + colorAttachmentRefs[0][index].attachment = attachmentIndex; const TargetBufferFlags flag = TargetBufferFlags(int(TargetBufferFlags::COLOR0) << i); const bool clear = any(config.clear & flag); @@ -252,8 +207,6 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { if (subpasses[0].colorAttachmentCount == 0) { subpasses[0].pColorAttachments = nullptr; subpasses[0].pResolveAttachments = nullptr; - subpasses[1].pColorAttachments = nullptr; - subpasses[1].pResolveAttachments = nullptr; } // Populate the Resolve Attachments. diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 7712115082..c3ed89aa41 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -273,10 +273,7 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) co if (!mColor[i].texture) { continue; } - // NOTE: This must be consistent with VkRenderPass construction (see VulkanFboCache). - if (!(pass.params.subpassMask & (1 << i)) || pass.currentSubpass == 1) { - count++; - } + count++; } return count; } diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp index 2d976f66ff..659de0092c 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp +++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp @@ -81,16 +81,6 @@ VulkanPipelineCache::VulkanPipelineCache(VulkanResourceAllocator* allocator) mDummyBufferWriteInfo.pImageInfo = nullptr; mDummyBufferWriteInfo.pBufferInfo = &mDummyBufferInfo; mDummyBufferWriteInfo.pTexelBufferView = nullptr; - - mDummyTargetInfo.imageLayout = VulkanImageUtility::getVkLayout(VulkanLayout::READ_ONLY); - mDummyTargetWriteInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - mDummyTargetWriteInfo.pNext = nullptr; - mDummyTargetWriteInfo.dstArrayElement = 0; - mDummyTargetWriteInfo.descriptorCount = 1; - mDummyTargetWriteInfo.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - mDummyTargetWriteInfo.pImageInfo = &mDummyTargetInfo; - mDummyTargetWriteInfo.pBufferInfo = nullptr; - mDummyTargetWriteInfo.pTexelBufferView = nullptr; } VulkanPipelineCache::~VulkanPipelineCache() { @@ -256,9 +246,7 @@ VulkanPipelineCache::DescriptorCacheEntry* VulkanPipelineCache::createDescriptor // Rewrite every binding in the new descriptor sets. VkDescriptorBufferInfo descriptorBuffers[UBUFFER_BINDING_COUNT]; VkDescriptorImageInfo descriptorSamplers[SAMPLER_BINDING_COUNT]; - VkDescriptorImageInfo descriptorInputAttachments[INPUT_ATTACHMENT_COUNT]; - VkWriteDescriptorSet descriptorWrites[UBUFFER_BINDING_COUNT + SAMPLER_BINDING_COUNT + - INPUT_ATTACHMENT_COUNT]; + VkWriteDescriptorSet descriptorWrites[UBUFFER_BINDING_COUNT + SAMPLER_BINDING_COUNT]; uint32_t nwrites = 0; VkWriteDescriptorSet* writes = descriptorWrites; nwrites = 0; @@ -308,23 +296,6 @@ VulkanPipelineCache::DescriptorCacheEntry* VulkanPipelineCache::createDescriptor writeInfo.dstBinding = binding; } } - for (uint32_t binding = 0; binding < INPUT_ATTACHMENT_COUNT; binding++) { - if (mDescriptorRequirements.inputAttachments[binding].imageView) { - VkWriteDescriptorSet& writeInfo = writes[nwrites++]; - VkDescriptorImageInfo& imageInfo = descriptorInputAttachments[binding]; - imageInfo = mDescriptorRequirements.inputAttachments[binding]; - writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeInfo.pNext = nullptr; - writeInfo.dstArrayElement = 0; - writeInfo.descriptorCount = 1; - writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - writeInfo.pImageInfo = &imageInfo; - writeInfo.pBufferInfo = nullptr; - writeInfo.pTexelBufferView = nullptr; - writeInfo.dstSet = descriptorCacheEntry.handles[2]; - writeInfo.dstBinding = binding; - } - } vkUpdateDescriptorSets(mDevice, nwrites, writes, 0, nullptr); @@ -402,15 +373,15 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n const bool hasFragmentShader = shaderStages[1].module != VK_NULL_HANDLE; - VkGraphicsPipelineCreateInfo pipelineCreateInfo = {}; - pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineCreateInfo.layout = layout->handle; - pipelineCreateInfo.renderPass = mPipelineRequirements.renderPass; - pipelineCreateInfo.subpass = mPipelineRequirements.subpassIndex; - pipelineCreateInfo.stageCount = hasFragmentShader ? SHADER_MODULE_COUNT : 1; - pipelineCreateInfo.pStages = shaderStages; - pipelineCreateInfo.pVertexInputState = &vertexInputState; - pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; + VkGraphicsPipelineCreateInfo pipelineCreateInfo = { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .stageCount = hasFragmentShader ? SHADER_MODULE_COUNT : 1, + .pStages = shaderStages, + .pVertexInputState = &vertexInputState, + .pInputAssemblyState = &inputAssemblyState, + .layout = layout->handle, + .renderPass = mPipelineRequirements.renderPass, + }; VkPipelineRasterizationStateCreateInfo vkRaster = {}; vkRaster.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; @@ -535,18 +506,6 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP dlinfo.pBindings = sbindings; vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &cacheEntry.descriptorSetLayouts[1]); - // Next create the descriptor set layout for input attachments. - VkDescriptorSetLayoutBinding tbindings[INPUT_ATTACHMENT_COUNT]; - binding.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - for (uint32_t i = 0; i < INPUT_ATTACHMENT_COUNT; i++) { - binding.binding = i; - tbindings[i] = binding; - } - dlinfo.bindingCount = INPUT_ATTACHMENT_COUNT; - dlinfo.pBindings = tbindings; - vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &cacheEntry.descriptorSetLayouts[2]); - // Create VkPipelineLayout based on how to resources are bounded. VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {}; pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; @@ -569,9 +528,8 @@ void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexce mPipelineRequirements.rasterState = rasterState; } -void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept { +void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass) noexcept { mPipelineRequirements.renderPass = renderPass; - mPipelineRequirements.subpassIndex = subpassIndex; } void VulkanPipelineCache::bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept { @@ -619,11 +577,6 @@ void VulkanPipelineCache::unbindImageView(VkImageView imageView) noexcept { sampler = {}; } } - for (auto& target : mDescriptorRequirements.inputAttachments) { - if (target.imageView == imageView) { - target = {}; - } - } } void VulkanPipelineCache::bindUniformBufferObject(uint32_t bindingIndex, @@ -662,14 +615,6 @@ void VulkanPipelineCache::bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BI mPipelineRequirements.layout = flags; } -void VulkanPipelineCache::bindInputAttachment(uint32_t bindingIndex, - VkDescriptorImageInfo targetInfo) noexcept { - ASSERT_POSTCONDITION(bindingIndex < INPUT_ATTACHMENT_COUNT, - "Input attachment bindings overflow: index = %d, capacity = %d.", - bindingIndex, INPUT_ATTACHMENT_COUNT); - mDescriptorRequirements.inputAttachments[bindingIndex] = targetInfo; -} - void VulkanPipelineCache::terminate() noexcept { // Symmetric to createLayoutsAndDescriptors. destroyLayoutsAndDescriptors(); @@ -793,8 +738,6 @@ VkDescriptorPool VulkanPipelineCache::createDescriptorPool(uint32_t size) const poolSizes[0].descriptorCount = poolInfo.maxSets * UBUFFER_BINDING_COUNT; poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; poolSizes[1].descriptorCount = poolInfo.maxSets * SAMPLER_BINDING_COUNT; - poolSizes[2].type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - poolSizes[2].descriptorCount = poolInfo.maxSets * INPUT_ATTACHMENT_COUNT; VkDescriptorPool pool; const UTILS_UNUSED VkResult result = vkCreateDescriptorPool(mDevice, &poolInfo, VKALLOC, &pool); @@ -905,12 +848,6 @@ bool VulkanPipelineCache::DescEqual::operator()(const DescriptorKey& k1, return false; } } - for (uint32_t i = 0; i < INPUT_ATTACHMENT_COUNT; i++) { - if (k1.inputAttachments[i].imageView != k2.inputAttachments[i].imageView || - k1.inputAttachments[i].imageLayout != k2.inputAttachments[i].imageLayout) { - return false; - } - } return true; } diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.h b/filament/backend/src/vulkan/VulkanPipelineCache.h index a28327c5d9..26f17b0751 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.h +++ b/filament/backend/src/vulkan/VulkanPipelineCache.h @@ -64,15 +64,11 @@ public: static constexpr uint32_t UBUFFER_BINDING_COUNT = Program::UNIFORM_BINDING_COUNT; static constexpr uint32_t SAMPLER_BINDING_COUNT = MAX_SAMPLER_COUNT; - // We assume only one possible input attachment between two subpasses. See also the subpasses - // definition in VulkanFboCache. - static constexpr uint32_t INPUT_ATTACHMENT_COUNT = 1; - static constexpr uint32_t SHADER_MODULE_COUNT = 2; static constexpr uint32_t VERTEX_ATTRIBUTE_COUNT = MAX_VERTEX_ATTRIBUTE_COUNT; - // Three descriptor set layouts: uniforms, combined image samplers, and input attachments. - static constexpr uint32_t DESCRIPTOR_TYPE_COUNT = 3; + // Three descriptor set layouts: uniforms, and combined image samplers. + static constexpr uint32_t DESCRIPTOR_TYPE_COUNT = 2; static constexpr uint32_t INITIAL_DESCRIPTOR_SET_POOL_SIZE = 512; // The VertexArray POD is an array of buffer targets and an array of attributes that refer to @@ -153,7 +149,7 @@ public: // Each of the following methods are fast and do not make Vulkan calls. void bindProgram(VulkanProgram* program) noexcept; void bindRasterState(const RasterState& rasterState) noexcept; - void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept; + void bindRenderPass(VkRenderPass renderPass) noexcept; void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept; void bindUniformBufferObject(uint32_t bindingIndex, VulkanBufferObject* bufferObject, VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) noexcept; @@ -161,7 +157,6 @@ public: VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) noexcept; void bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BINDING_COUNT], VulkanTexture* textures[SAMPLER_BINDING_COUNT], UsageFlags flags) noexcept; - void bindInputAttachment(uint32_t bindingIndex, VkDescriptorImageInfo imageInfo) noexcept; void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc, VkVertexInputBindingDescription const* bufferDesc, uint8_t count); @@ -258,16 +253,17 @@ private: // The pipeline key is a POD that represents all currently bound states that form the immutable // VkPipeline object. The size:offset comments below are expressed in bytes. - struct PipelineKey { // size : offset - VkShaderModule shaders[SHADER_MODULE_COUNT]; // 16 : 0 - VkRenderPass renderPass; // 8 : 16 - uint16_t topology; // 2 : 24 - uint16_t subpassIndex; // 2 : 26 - VertexInputAttributeDescription vertexAttributes[VERTEX_ATTRIBUTE_COUNT]; // 128 : 28 - VertexInputBindingDescription vertexBuffers[VERTEX_ATTRIBUTE_COUNT]; // 128 : 156 - RasterState rasterState; // 16 : 284 - uint32_t padding; // 4 : 300 - PipelineLayoutKey layout; // 16 : 304 + struct PipelineKey { // size : offset + VkShaderModule shaders[SHADER_MODULE_COUNT]; // 16 : 0 + VkRenderPass renderPass; // 8 : 16 + uint16_t topology; // 2 : 24 + uint16_t padding0; // 2 : 26 + VertexInputAttributeDescription vertexAttributes[VERTEX_ATTRIBUTE_COUNT]; // 128 : 28 + VertexInputBindingDescription vertexBuffers[VERTEX_ATTRIBUTE_COUNT]; // 128 : 156 + RasterState rasterState; // 16 : 284 + uint32_t padding1; // 4 : 300 + PipelineLayoutKey layout; // 16 : 304 + // total : 320 }; static_assert(sizeof(PipelineKey) == 320, "PipelineKey must not have implicit padding."); @@ -308,15 +304,15 @@ private: struct DescriptorKey { VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 80 0 DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 1488 80 - DescriptorImageInfo inputAttachments[INPUT_ATTACHMENT_COUNT]; // 24 1568 - uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 40 1592 - uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 40 1632 + uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 40 1568 + uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 40 1608 + //total 1648 }; + static_assert(offsetof(DescriptorKey, samplers) == 80); - static_assert(offsetof(DescriptorKey, inputAttachments) == 1568); - static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1592); - static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1632); - static_assert(sizeof(DescriptorKey) == 1672, "DescriptorKey must not have implicit padding."); + static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1568); + static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1608); + static_assert(sizeof(DescriptorKey) == 1648, "DescriptorKey must not have implicit padding."); using DescHashFn = utils::hash::MurmurHashFn; @@ -439,7 +435,6 @@ private: VkDescriptorBufferInfo mDummyBufferInfo = {}; VkWriteDescriptorSet mDummyBufferWriteInfo = {}; VkDescriptorImageInfo mDummyTargetInfo = {}; - VkWriteDescriptorSet mDummyTargetWriteInfo = {}; VkBuffer mDummyBuffer; VmaAllocation mDummyMemory; From 7e96216b6ce5859b2c997b49cba3451beb28d7bf Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Fri, 23 Feb 2024 11:52:05 -0800 Subject: [PATCH 19/33] vk: fix window minimize on Win (#7596) Fixes #7481 --- filament/backend/src/vulkan/VulkanDriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index f732160947..8e2d0ed460 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1138,7 +1138,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP VulkanRenderTarget* const rt = mResourceAllocator.handle_cast(rth); const VkExtent2D extent = rt->getExtent(); - assert_invariant(extent.width > 0 && extent.height > 0); + assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0); // Filament has the expectation that the contents of the swap chain are not preserved on the // first render pass. Note however that its contents are often preserved on subsequent render From 36120106cdfe4610bd09620a5ae6006423e7f928 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Fri, 23 Feb 2024 14:33:26 -0800 Subject: [PATCH 20/33] Add new PBR Neutral tone mapper (#7597) This tone mapper was designed to preserve the color apperance of materials. It provides good saturation and contrast while controlling the highlights. --- NEW_RELEASE_NOTES.md | 1 + .../src/main/cpp/ToneMapper.cpp | 5 ++ .../google/android/filament/ToneMapper.java | 15 +++- filament/include/filament/ToneMapper.h | 20 ++++-- filament/src/ToneMapper.cpp | 68 +++++++++++++------ libs/viewer/include/viewer/Settings.h | 3 +- libs/viewer/src/Settings.cpp | 11 +-- libs/viewer/src/ViewerGui.cpp | 5 +- 8 files changed, 95 insertions(+), 33 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index b9947d8a8e..7e1350a537 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -10,3 +10,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). - android: NDK 26.1.10909125 is used by default - android: Minimum API level on Android is now API 21 instead of API 19. This allows the use of OpenGL ES 3.1 +- rendering: New PBR Neutral tone mapper, designed to preserve materials color appearance diff --git a/android/filament-android/src/main/cpp/ToneMapper.cpp b/android/filament-android/src/main/cpp/ToneMapper.cpp index 21c7e7ab24..860b95508b 100644 --- a/android/filament-android/src/main/cpp/ToneMapper.cpp +++ b/android/filament-android/src/main/cpp/ToneMapper.cpp @@ -47,6 +47,11 @@ Java_com_google_android_filament_ToneMapper_nCreateFilmicToneMapper(JNIEnv*, jcl return (jlong) new FilmicToneMapper(); } +extern "C" JNIEXPORT jlong JNICALL +Java_com_google_android_filament_ToneMapper_nCreatePBRNeutralToneMapper(JNIEnv*, jclass) { + return (jlong) new PBRNeutralToneMapper(); +} + extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_ToneMapper_nCreateAgxToneMapper(JNIEnv*, jclass, jint look) { return (jlong) new AgxToneMapper(AgxToneMapper::AgxLook(look)); diff --git a/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java b/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java index 58ffc501ec..c3d3490441 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java +++ b/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java @@ -16,12 +16,14 @@ package com.google.android.filament; *
  • Configurable tone mapping operators
  • *
      *
    • GenericToneMapper
    • + *
    • AgXToneMapper
    • *
    *
  • Fixed-aesthetic tone mapping operators
  • *
      *
    • ACESToneMapper
    • *
    • ACESLegacyToneMapper
    • *
    • FilmicToneMapper
    • + *
    • PBRNeutralToneMapper
    • *
    *
  • Debug/validation tone mapping operators
  • *
      @@ -100,11 +102,21 @@ public class ToneMapper { } } + /** + * Khronos PBR Neutral tone mapping operator. This tone mapper was designed + * to preserve the appearance of materials across lighting conditions while + * avoiding artifacts in the highlights in high dynamic range conditions. + */ + public static class PBRNeutralToneMapper extends ToneMapper { + public PBRNeutralToneMapper() { + super(nCreatePBRNeutralToneMapper()); + } + } + /** * AgX tone mapping operator. */ public static class Agx extends ToneMapper { - public enum AgxLook { /** * Base contrast with no look applied @@ -233,6 +245,7 @@ public class ToneMapper { private static native long nCreateACESToneMapper(); private static native long nCreateACESLegacyToneMapper(); private static native long nCreateFilmicToneMapper(); + private static native long nCreatePBRNeutralToneMapper(); private static native long nCreateAgxToneMapper(int look); private static native long nCreateGenericToneMapper( float contrast, float midGrayIn, float midGrayOut, float hdrMax); diff --git a/filament/include/filament/ToneMapper.h b/filament/include/filament/ToneMapper.h index 74e2661438..e89e702c99 100644 --- a/filament/include/filament/ToneMapper.h +++ b/filament/include/filament/ToneMapper.h @@ -21,7 +21,7 @@ #include -#include +#include namespace filament { @@ -39,10 +39,12 @@ namespace filament { * * - Configurable tone mapping operators * - GenericToneMapper + * - AgXToneMapper * - Fixed-aesthetic tone mapping operators * - ACESToneMapper * - ACESLegacyToneMapper * - FilmicToneMapper + * - PBRNeutralToneMapper * - Debug/validation tone mapping operators * - LinearToneMapper * - DisplayRangeToneMapper @@ -117,11 +119,22 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper { math::float3 operator()(math::float3 x) const noexcept override; }; +/** + * Khronos PBR Neutral tone mapping operator. This tone mapper was designed + * to preserve the appearance of materials across lighting conditions while + * avoiding artifacts in the highlights in high dynamic range conditions. + */ +struct UTILS_PUBLIC PBRNeutralToneMapper final : public ToneMapper { + PBRNeutralToneMapper() noexcept; + ~PBRNeutralToneMapper() noexcept final; + + math::float3 operator()(math::float3 x) const noexcept override; +}; + /** * AgX tone mapping operator. */ struct UTILS_PUBLIC AgxToneMapper final : public ToneMapper { - enum class AgxLook : uint8_t { NONE = 0, //!< Base contrast with no look applied PUNCHY, //!< A punchy and more chroma laden look for sRGB displays @@ -185,9 +198,6 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper { /** Returns the contrast of the curve as a strictly positive value. */ float getContrast() const noexcept; - /** Returns how fast scene referred values map to output white as a value between 0.0 and 1.0. */ - float getShoulder() const noexcept; - /** Returns the middle gray point for input values as a value between 0.0 and 1.0. */ float getMidGrayIn() const noexcept; diff --git a/filament/src/ToneMapper.cpp b/filament/src/ToneMapper.cpp index b24f253e0a..2afa58f4be 100644 --- a/filament/src/ToneMapper.cpp +++ b/filament/src/ToneMapper.cpp @@ -230,6 +230,32 @@ float3 FilmicToneMapper::operator()(math::float3 x) const noexcept { return (x * (a * x + b)) / (x * (c * x + d) + e); } +//------------------------------------------------------------------------------ +// PBR Neutral tone mapper +//------------------------------------------------------------------------------ + +DEFAULT_CONSTRUCTORS(PBRNeutralToneMapper) + +float3 PBRNeutralToneMapper::operator()(math::float3 color) const noexcept { + // PBR Tone Mapping, https://modelviewer.dev/examples/tone-mapping.html + constexpr float startCompression = 0.8f - 0.04f; + constexpr float desaturation = 0.15f; + + float x = min(color.r, min(color.g, color.b)); + float offset = x < 0.08f ? x - 6.25f * x * x : 0.04f; + color -= offset; + + float peak = max(color.r, max(color.g, color.b)); + if (peak < startCompression) return color; + + float d = 1.0f - startCompression; + float newPeak = 1.0f - d * d / (peak + d - startCompression); + color *= newPeak / peak; + + float g = 1.0f - 1.0f / (desaturation * (peak - newPeak) + 1.0f); + return mix(color, float3(1.0f), g); +} + //------------------------------------------------------------------------------ // AgX tone mapper //------------------------------------------------------------------------------ @@ -262,14 +288,14 @@ float3 agxDefaultContrastApprox(float3 x) { float3 x2 = x * x; float3 x4 = x2 * x2; float3 x6 = x4 * x2; - return - 17.86 * x6 * x - + 78.01 * x6 - - 126.7 * x4 * x - + 92.06 * x4 - - 28.72 * x2 * x - + 4.361 * x2 - - 0.1718 * x - + 0.002857; + return - 17.86f * x6 * x + + 78.01f * x6 + - 126.7f * x4 * x + + 92.06f * x4 + - 28.72f * x2 * x + + 4.361f * x2 + - 0.1718f * x + + 0.002857f; } // Adapted from https://iolite-engine.com/blog_posts/minimal_agx_implementation @@ -278,23 +304,23 @@ float3 agxLook(float3 val, AgxToneMapper::AgxLook look) { return val; } - const float3 lw = float3(0.2126, 0.7152, 0.0722); + const float3 lw = float3(0.2126f, 0.7152f, 0.0722f); float luma = dot(val, lw); // Default - float3 offset = float3(0.0); - float3 slope = float3(1.0); - float3 power = float3(1.0); - float sat = 1.0; + float3 offset = float3(0.0f); + float3 slope = float3(1.0f); + float3 power = float3(1.0f); + float sat = 1.0f; if (look == AgxToneMapper::AgxLook::GOLDEN) { - slope = float3(1.0, 0.9, 0.5); - power = float3(0.8); + slope = float3(1.0f, 0.9f, 0.5f); + power = float3(0.8f); sat = 1.3; } if (look == AgxToneMapper::AgxLook::PUNCHY) { - slope = float3(1.0); - power = float3(1.35, 1.35, 1.35); + slope = float3(1.0f); + power = float3(1.35f, 1.35f, 1.35f); sat = 1.4; } @@ -305,16 +331,16 @@ float3 agxLook(float3 val, AgxToneMapper::AgxLook look) { float3 AgxToneMapper::operator()(float3 v) const noexcept { // Ensure no negative values - v = max(float3(0.0), v); + v = max(float3(0.0f), v); v = AgXInsetMatrix * v; // Log2 encoding - v = max(v, 1E-10); // avoid 0 or negative numbers for log2 + v = max(v, 1E-10f); // avoid 0 or negative numbers for log2 v = log2(v); v = (v - AgxMinEv) / (AgxMaxEv - AgxMinEv); - v = clamp(v, 0, 1); + v = clamp(v, 0.0f, 1.0f); // Apply sigmoid v = agxDefaultContrastApprox(v); @@ -325,7 +351,7 @@ float3 AgxToneMapper::operator()(float3 v) const noexcept { v = AgXOutsetMatrix * v; // Linearize - v = pow(max(float3(0.0), v), 2.2); + v = pow(max(float3(0.0f), v), 2.2f); return v; } diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h index 6682260c3e..2094296bd7 100644 --- a/libs/viewer/include/viewer/Settings.h +++ b/libs/viewer/include/viewer/Settings.h @@ -59,7 +59,8 @@ enum class ToneMapping : uint8_t { FILMIC = 3, AGX = 4, GENERIC = 5, - DISPLAY_RANGE = 6, + PBR_NEUTRAL = 6, + DISPLAY_RANGE = 7, }; using AmbientOcclusionOptions = filament::View::AmbientOcclusionOptions; diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index 453f9beccc..639d07a2c9 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -88,6 +88,7 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ToneMapp else if (0 == compare(tokens[i], jsonChunk, "FILMIC")) { *out = ToneMapping::FILMIC; } else if (0 == compare(tokens[i], jsonChunk, "AGX")) { *out = ToneMapping::AGX; } else if (0 == compare(tokens[i], jsonChunk, "GENERIC")) { *out = ToneMapping::GENERIC; } + else if (0 == compare(tokens[i], jsonChunk, "PBR_NEUTRAL")) { *out = ToneMapping::PBR_NEUTRAL; } else if (0 == compare(tokens[i], jsonChunk, "DISPLAY_RANGE")) { *out = ToneMapping::DISPLAY_RANGE; } else { slog.w << "Invalid ToneMapping: '" << STR(tokens[i], jsonChunk) << "'" << io::endl; @@ -679,11 +680,12 @@ constexpr ToneMapper* createToneMapper(const ColorGradingSettings& settings) noe case ToneMapping::FILMIC: return new FilmicToneMapper; case ToneMapping::AGX: return new AgxToneMapper(settings.agxToneMapper.look); case ToneMapping::GENERIC: return new GenericToneMapper( - settings.genericToneMapper.contrast, - settings.genericToneMapper.midGrayIn, - settings.genericToneMapper.midGrayOut, - settings.genericToneMapper.hdrMax + settings.genericToneMapper.contrast, + settings.genericToneMapper.midGrayIn, + settings.genericToneMapper.midGrayOut, + settings.genericToneMapper.hdrMax ); + case ToneMapping::PBR_NEUTRAL: return new PBRNeutralToneMapper; case ToneMapping::DISPLAY_RANGE: return new DisplayRangeToneMapper; } } @@ -734,6 +736,7 @@ static std::ostream& operator<<(std::ostream& out, ToneMapping in) { case ToneMapping::FILMIC: return out << "\"FILMIC\""; case ToneMapping::AGX: return out << "\"AGX\""; case ToneMapping::GENERIC: return out << "\"GENERIC\""; + case ToneMapping::PBR_NEUTRAL: return out << "\"PBR_NEUTRAL\""; case ToneMapping::DISPLAY_RANGE: return out << "\"DISPLAY_RANGE\""; } return out << "\"INVALID\""; diff --git a/libs/viewer/src/ViewerGui.cpp b/libs/viewer/src/ViewerGui.cpp index 9d352e3600..216288d3f8 100644 --- a/libs/viewer/src/ViewerGui.cpp +++ b/libs/viewer/src/ViewerGui.cpp @@ -145,6 +145,9 @@ static void computeToneMapPlot(ColorGradingSettings& settings, float* plot) { ); hdrMax = settings.genericToneMapper.hdrMax; break; + case ToneMapping::PBR_NEUTRAL: + mapper = new PBRNeutralToneMapper; + break; case ToneMapping::DISPLAY_RANGE: mapper = new DisplayRangeToneMapper; break; @@ -196,7 +199,7 @@ static void colorGradingUI(Settings& settings, float* rangePlot, float* curvePlo int toneMapping = (int) colorGrading.toneMapping; ImGui::Combo("Tone-mapping", &toneMapping, - "Linear\0ACES (legacy)\0ACES\0Filmic\0AgX\0Generic\0Display Range\0\0"); + "Linear\0ACES (legacy)\0ACES\0Filmic\0AgX\0Generic\0PBR Neutral\0Display Range\0\0"); colorGrading.toneMapping = (decltype(colorGrading.toneMapping)) toneMapping; if (colorGrading.toneMapping == ToneMapping::GENERIC) { if (ImGui::CollapsingHeader("Tonemap parameters")) { From a3131a64b6cef2306965eec35a2f9a348601fc00 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 22 Feb 2024 17:00:22 -0800 Subject: [PATCH 21/33] cleanup of attributes management - added many precondition checks and asserts to VertexBuffer creation - simplified code in VertexBuffer as well - enforce BONE_INDICES to integer when specified by user since that's what shaders expect. - better comments about *always* setting BONE_INDICES to integer - some code simplification in RenderPass + some comments about skinning - in the GL backend we no longer set the vertex buffer objects at renderprimitive creation time, because they might not be available yet. Instead, we let the natural age mechanism update them next time it's needed. This allows us to add some asserts about the declared buffer being present --- filament/backend/src/DriverBase.h | 2 +- filament/backend/src/opengl/OpenGLDriver.cpp | 49 ++-- filament/src/RenderPass.cpp | 36 +-- filament/src/details/VertexBuffer.cpp | 234 ++++++++++++------- filament/src/details/VertexBuffer.h | 7 +- 5 files changed, 198 insertions(+), 130 deletions(-) diff --git a/filament/backend/src/DriverBase.h b/filament/backend/src/DriverBase.h index 18a7e4043f..43cd3ef90d 100644 --- a/filament/backend/src/DriverBase.h +++ b/filament/backend/src/DriverBase.h @@ -63,7 +63,7 @@ struct HwVertexBufferInfo : public HwBase { struct HwVertexBuffer : public HwBase { uint32_t vertexCount{}; // 4 - uint8_t bufferObjectsVersion{}; // 1 + uint8_t bufferObjectsVersion{0xff}; // 1 bool padding[3]{}; // 2 HwVertexBuffer() noexcept = default; explicit HwVertexBuffer(uint32_t vertextCount) noexcept diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index dc1744186b..98031d6ff8 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -519,7 +519,6 @@ void OpenGLDriver::createRenderPrimitiveR(Handle rph, auto& gl = mContext; - GLVertexBuffer const* const vb = handle_cast(vbh); GLIndexBuffer const* const ib = handle_cast(ibh); assert_invariant(ib->elementSize == 2 || ib->elementSize == 4); @@ -532,8 +531,9 @@ void OpenGLDriver::createRenderPrimitiveR(Handle rph, gl.bindVertexArray(&rp->gl); - // update the VBO bindings in the VAO - updateVertexArrayObject(rp, vb); + // Note: we don't update the vertex buffer bindings in the VAO just yet, we will do that + // later in draw() or bindRenderPrimitive(). At this point, the HwVertexBuffer might not + // have all its buffers set. // this records the index buffer into the currently bound VAO gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer); @@ -856,7 +856,6 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer // NOTE: this is called from draw() and must be as efficient as possible. - if (UTILS_LIKELY(gl.ext.OES_vertex_array_object)) { // The VAO for the given render primitive must already be bound. #ifndef NDEBUG @@ -875,38 +874,44 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer for (size_t i = 0, n = vbi->attributes.size(); i < n; i++) { const auto& attribute = vbi->attributes[i]; const uint8_t bi = attribute.buffer; + if (bi != Attribute::BUFFER_UNUSED) { + // if a buffer is defined it must not be invalid. + assert_invariant(vb->gl.buffers[bi]); - // Invoking glVertexAttribPointer without a bound VBO is an invalid operation, so we must - // take care to avoid it. This can occur when VertexBuffer is only partially populated with - // BufferObject items. - if (bi != Attribute::BUFFER_UNUSED && UTILS_LIKELY(vb->gl.buffers[bi] != 0)) { - + // if w're on ES2, the user shouldn't use FLAG_INTEGER_TARGET assert_invariant(!(gl.isES2() && (attribute.flags & Attribute::FLAG_INTEGER_TARGET))); gl.bindBuffer(GL_ARRAY_BUFFER, vb->gl.buffers[bi]); + GLuint const index = i; + GLint const size = getComponentCount(attribute.type); + GLenum const type = getComponentType(attribute.type); + GLboolean const normalized = getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED); + GLsizei const stride = attribute.stride; + void const* pointer = reinterpret_cast(attribute.offset); + #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) { - glVertexAttribIPointer(GLuint(i), - (GLint)getComponentCount(attribute.type), - getComponentType(attribute.type), - attribute.stride, - (void*) uintptr_t(attribute.offset)); + // integer attributes can't be floats + assert_invariant(type == GL_BYTE || type == GL_UNSIGNED_BYTE || type == GL_SHORT || + type == GL_UNSIGNED_SHORT || type == GL_INT || type == GL_UNSIGNED_INT); + glVertexAttribIPointer(index, size, type, stride, pointer); } else #endif { - glVertexAttribPointer(GLuint(i), - (GLint)getComponentCount(attribute.type), - getComponentType(attribute.type), - getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED), - attribute.stride, - (void*) uintptr_t(attribute.offset)); + glVertexAttribPointer(index, size, type, normalized, stride, pointer); } gl.enableVertexAttribArray(GLuint(i)); } else { - // In some OpenGL implementations, we must supply a properly-typed placeholder for - // every integer input that is declared in the vertex shader, even if disabled. + // every integer input that is declared in the vertex shader. + // Note that the corresponding doesn't have to be enabled and in fact won't be. If it + // was enabled, it would indicate a user-error (providing the wrong type of array). + // With a disabled array, the vertex shader gets the attribute from glVertexAttrib, + // and must have the proper intergerness. + // But at this point, we don't know what the shader requirements are, and so we must + // rely on the attribute. + #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) { if (!gl.isES2()) { diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index cde9c1ffb4..ffaeb5b8f2 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -576,7 +576,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla // calculate the per-primitive face winding order inversion const bool inverseFrontFaces = viewInverseFrontFaces ^ soaVisibility[i].reversedWindingOrder; const bool hasMorphing = soaVisibility[i].morphing; - const bool hasSkinningOrMorphing = soaVisibility[i].skinning || hasMorphing; + const bool hasSkinning = soaVisibility[i].skinning; + const bool hasSkinningOrMorphing = hasSkinning || hasMorphing; cmdColor.key = makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT); cmdColor.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT); @@ -601,6 +602,9 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla Variant::isSSRVariant(variant) || (soaVisibility[i].receiveShadows & hasShadowing)); renderableVariant.setSkinning(hasSkinningOrMorphing); + const FRenderableManager::SkinningBindingInfo& skinning = soaSkinning[i]; + const FRenderableManager::MorphingBindingInfo& morphing = soaMorphing[i]; + if constexpr (isDepthPass) { cmdDepth.key = uint64_t(Pass::DEPTH); cmdDepth.key |= uint64_t(CustomCommand::PASS); @@ -614,6 +618,11 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla cmdDepth.primitive.materialVariant.setSkinning(hasSkinningOrMorphing); cmdDepth.primitive.rasterState.inverseFrontFaces = inverseFrontFaces; + cmdDepth.primitive.skinningHandle = skinning.handle; + cmdDepth.primitive.skinningOffset = skinning.offset; + cmdDepth.primitive.skinningTexture = skinning.handleSampler; + cmdDepth.primitive.morphWeightBuffer = morphing.handle; + if (UTILS_UNLIKELY(hasInstancedStereo)) { cmdColor.primitive.instanceCount = (soaInstanceInfo[i].count * instancedStereoEyeCount) | @@ -622,15 +631,17 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla } if constexpr (isColorPass) { renderableVariant.setFog(soaVisibility[i].fog && Variant::isFogVariant(variant)); + + cmdColor.primitive.skinningHandle = skinning.handle; + cmdColor.primitive.skinningOffset = skinning.offset; + cmdColor.primitive.skinningTexture = skinning.handleSampler; + cmdColor.primitive.morphWeightBuffer = morphing.handle; } const bool shadowCaster = soaVisibility[i].castShadows & hasShadowing; const bool writeDepthForShadowCasters = depthContainsShadowCasters & shadowCaster; const Slice& primitives = soaPrimitives[i]; - const FRenderableManager::SkinningBindingInfo& skinning = soaSkinning[i]; - const FRenderableManager::MorphingBindingInfo& morphing = soaMorphing[i]; - /* * This is our hot loop. It's written to avoid branches. * When modifying this code, always ensure it stays efficient. @@ -641,15 +652,13 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla FMaterialInstance const* const mi = primitive.getMaterialInstance(); FMaterial const* const ma = mi->getMaterial(); + // TODO: we should disable the SKN variant if this primitive doesn't have either + // skinning or morphing. + if constexpr (isColorPass) { cmdColor.primitive.primitive = &primitive; RenderPass::setupColorCommand(cmdColor, renderableVariant, mi, inverseFrontFaces); - cmdColor.primitive.skinningHandle = skinning.handle; - cmdColor.primitive.skinningOffset = skinning.offset; - cmdColor.primitive.skinningTexture = skinning.handleSampler; - - cmdColor.primitive.morphWeightBuffer = morphing.handle; cmdColor.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle(); const bool blendPass = Pass(cmdColor.key & PASS_MASK) == Pass::BLENDED; @@ -744,17 +753,14 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla const bool translucent = (blendingMode != BlendingMode::OPAQUE && blendingMode != BlendingMode::MASKED); + // TODO: we should disable the SKN variant if this primitive doesn't have either + // skinning or morphing. + cmdDepth.key |= mi->getSortingKey(); // already all set-up for direct or'ing // unconditionally write the command cmdDepth.primitive.primitive = &primitive; cmdDepth.primitive.rasterState.culling = mi->getCullingMode(); - - cmdDepth.primitive.skinningHandle = skinning.handle; - cmdDepth.primitive.skinningOffset = skinning.offset; - cmdDepth.primitive.skinningTexture = skinning.handleSampler; - - cmdDepth.primitive.morphWeightBuffer = morphing.handle; cmdDepth.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle(); // FIXME: should writeDepthForShadowCasters take precedence over mi->getDepthWrite()? diff --git a/filament/src/details/VertexBuffer.cpp b/filament/src/details/VertexBuffer.cpp index 3e0aaa7abf..868ca60e92 100644 --- a/filament/src/details/VertexBuffer.cpp +++ b/filament/src/details/VertexBuffer.cpp @@ -21,18 +21,41 @@ #include "FilamentAPI-impl.h" -#include -#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include + +#include +#include + namespace filament { using namespace backend; using namespace filament::math; struct VertexBuffer::BuilderDetails { - FVertexBuffer::AttributeData mAttributes[MAX_VERTEX_ATTRIBUTE_COUNT]; + struct AttributeData : backend::Attribute { + AttributeData() : backend::Attribute{ .type = backend::ElementType::FLOAT4 } { + static_assert(sizeof(Attribute) == sizeof(AttributeData), + "Attribute and Builder::Attribute must match"); + } + }; + std::array mAttributes{}; AttributeBitset mDeclaredAttributes; uint32_t mVertexCount = 0; uint8_t mBufferCount = 0; @@ -88,11 +111,16 @@ VertexBuffer::Builder& VertexBuffer::Builder::attribute(VertexAttribute attribut } #endif - FVertexBuffer::AttributeData& entry = mImpl->mAttributes[attribute]; + auto& entry = mImpl->mAttributes[attribute]; entry.buffer = bufferIndex; entry.offset = byteOffset; entry.stride = byteStride; entry.type = attributeType; + if (attribute == VertexAttribute::BONE_INDICES) { + // BONE_INDICES must always be an integer type + entry.flags |= Attribute::FLAG_INTEGER_TARGET; + } + mImpl->mDeclaredAttributes.set(attribute); } else { utils::slog.w << "Ignoring VertexBuffer attribute, the limit of " << @@ -104,7 +132,7 @@ VertexBuffer::Builder& VertexBuffer::Builder::attribute(VertexAttribute attribut VertexBuffer::Builder& VertexBuffer::Builder::normalized(VertexAttribute attribute, bool normalized) noexcept { if (size_t(attribute) < MAX_VERTEX_ATTRIBUTE_COUNT) { - FVertexBuffer::AttributeData& entry = mImpl->mAttributes[attribute]; + auto& entry = mImpl->mAttributes[attribute]; if (normalized) { entry.flags |= Attribute::FLAG_NORMALIZED; } else { @@ -130,15 +158,47 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) { auto const& declaredAttributes = mImpl->mDeclaredAttributes; auto const& attributes = mImpl->mAttributes; utils::bitset32 attributedBuffers; - for (size_t j = 0; j < MAX_VERTEX_ATTRIBUTE_COUNT; ++j) { - if (declaredAttributes[j]) { - attributedBuffers.set(attributes[j].buffer); + + declaredAttributes.forEachSetBit([&](size_t j){ + // update set of used buffers + attributedBuffers.set(attributes[j].buffer); + + if (engine.getActiveFeatureLevel() == backend::FeatureLevel::FEATURE_LEVEL_0) { + ASSERT_PRECONDITION(!(attributes[j].flags & Attribute::FLAG_INTEGER_TARGET), + "Attribute::FLAG_INTEGER_TARGET not supported at FEATURE_LEVEL_0"); } - } + + // also checks that we don't use an invalid type with integer attributes + if (attributes[j].flags & Attribute::FLAG_INTEGER_TARGET) { + using ET = ElementType; + constexpr uint32_t const invalidIntegerTypes = + (1 << (int)ET::FLOAT) | + (1 << (int)ET::FLOAT2) | + (1 << (int)ET::FLOAT3) | + (1 << (int)ET::FLOAT4) | + (1 << (int)ET::HALF) | + (1 << (int)ET::HALF2) | + (1 << (int)ET::HALF3) | + (1 << (int)ET::HALF4); + ASSERT_PRECONDITION(!(invalidIntegerTypes & (1 << (int)attributes[j].type)), + "invalid integer vertex attribute type %d", attributes[j].type); + } + }); ASSERT_PRECONDITION(attributedBuffers.count() == mImpl->mBufferCount, "At least one buffer slot was never assigned to an attribute."); + if (mImpl->mAdvancedSkinningEnabled) { + ASSERT_PRECONDITION(!mImpl->mDeclaredAttributes[VertexAttribute::BONE_INDICES], + "Vertex buffer attribute BONE_INDICES is already defined, " + "no advanced skinning is allowed"); + ASSERT_PRECONDITION(!mImpl->mDeclaredAttributes[VertexAttribute::BONE_WEIGHTS], + "Vertex buffer attribute BONE_WEIGHTS is already defined, " + "no advanced skinning is allowed"); + ASSERT_PRECONDITION(mImpl->mBufferCount < (MAX_VERTEX_BUFFER_COUNT - 2), + "Vertex buffer uses to many buffers (%u)", mImpl->mBufferCount); + } + return downcast(engine).createVertexBuffer(*this); } @@ -149,80 +209,81 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build mBufferObjectsEnabled(builder->mBufferObjectsEnabled), mAdvancedSkinningEnabled(builder->mAdvancedSkinningEnabled){ std::copy(std::begin(builder->mAttributes), std::end(builder->mAttributes), mAttributes.begin()); - mDeclaredAttributes = builder->mDeclaredAttributes; - uint8_t const attributeCount = (uint8_t)mDeclaredAttributes.count(); - - AttributeArray attributeArray{}; - - static_assert(attributeArray.size() == MAX_VERTEX_ATTRIBUTE_COUNT, - "Attribute and Builder::Attribute arrays must match"); - - static_assert(sizeof(Attribute) == sizeof(AttributeData), - "Attribute and Builder::Attribute must match"); if (mAdvancedSkinningEnabled) { - ASSERT_PRECONDITION(!mDeclaredAttributes[VertexAttribute::BONE_INDICES], - "Vertex buffer attribute BONE_INDICES is already defined, no advanced skinning is allowed"); - ASSERT_PRECONDITION(!mDeclaredAttributes[VertexAttribute::BONE_WEIGHTS], - "Vertex buffer attribute BONE_WEIGHTS is already defined, no advanced skinning is allowed"); - ASSERT_PRECONDITION(mBufferCount < (MAX_VERTEX_BUFFER_COUNT - 2), - "Vertex buffer uses to many buffers (%u)", mBufferCount); + mAttributes[VertexAttribute::BONE_INDICES] = { + .offset = 0, + .stride = 8, + .buffer = mBufferCount, + .type = VertexBuffer::AttributeType::USHORT4, + .flags = Attribute::FLAG_INTEGER_TARGET, + }; mDeclaredAttributes.set(VertexAttribute::BONE_INDICES); - mAttributes[VertexAttribute::BONE_INDICES].offset = 0; - mAttributes[VertexAttribute::BONE_INDICES].stride = 8; - mAttributes[VertexAttribute::BONE_INDICES].buffer = mBufferCount; - mAttributes[VertexAttribute::BONE_INDICES].type = VertexBuffer::AttributeType::USHORT4; - mAttributes[VertexAttribute::BONE_INDICES].flags = Attribute::FLAG_INTEGER_TARGET; mBufferCount++; + + mAttributes[VertexAttribute::BONE_WEIGHTS] = { + .offset = 0, + .stride = 16, + .buffer = mBufferCount, + .type = VertexBuffer::AttributeType::FLOAT4, + .flags = 0, + }; mDeclaredAttributes.set(VertexAttribute::BONE_WEIGHTS); - mAttributes[VertexAttribute::BONE_WEIGHTS].offset = 0; - mAttributes[VertexAttribute::BONE_WEIGHTS].stride = 16; - mAttributes[VertexAttribute::BONE_WEIGHTS].buffer = mBufferCount; - mAttributes[VertexAttribute::BONE_WEIGHTS].type = VertexBuffer::AttributeType::FLOAT4; - mAttributes[VertexAttribute::BONE_WEIGHTS].flags = 0; mBufferCount++; + } else { + // Because the Material's SKN variant supports both skinning and morphing, it expects + // all attributes related to *both* to be present. In turn, this means that a VertexBuffer + // used for skinning and/or morphing, needs to provide all related attributes. + // Currently, the backend must handle disabled arrays in the VertexBuffer that are declared + // in the shader. In GL this happens automatically, in vulkan/metal, the backends have to + // use dummy buffers. + // - A complication is that backends need to know if an attribute is declared as float or + // integer in the shader, regardless of if the attribute is enabled or not in the + // VertexBuffer (e.g. the morphing attributes could be disabled because we're only using + // skinning). + // - Another complication is that the SKN variant is selected by the renderable + // (as opposed to the RenderPrimitive), so it's possible and valid for a primitive + // that isn't skinned nor morphed to be rendered with the SKN variant (morphing/skinning + // will then be disabled dynamically). + // + // Because of that we need to set FLAG_INTEGER_TARGET on all attributes that we know are + // integer in the shader and the bottom line is that BONE_INDICES always needs to be set to + // FLAG_INTEGER_TARGET. + mAttributes[BONE_INDICES].flags |= Attribute::FLAG_INTEGER_TARGET; } - size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {}; - - auto const& declaredAttributes = mDeclaredAttributes; - auto const& attributes = mAttributes; - #pragma nounroll - for (size_t i = 0, n = attributeArray.size(); i < n; ++i) { - if (declaredAttributes[i]) { - const uint32_t offset = attributes[i].offset; - const uint8_t stride = attributes[i].stride; - const uint8_t slot = attributes[i].buffer; - - attributeArray[i].offset = offset; - attributeArray[i].stride = stride; - attributeArray[i].buffer = slot; - attributeArray[i].type = attributes[i].type; - attributeArray[i].flags = attributes[i].flags; - - const size_t end = offset + mVertexCount * stride; - bufferSizes[slot] = math::max(bufferSizes[slot], end); - } - } - - // Backends do not (and should not) know the semantics of each vertex attribute, but they - // need to know whether the vertex shader consumes them as integers or as floats. - // NOTE: This flag needs to be set regardless of whether the attribute is actually declared. - attributeArray[BONE_INDICES].flags |= Attribute::FLAG_INTEGER_TARGET; - FEngine::DriverApi& driver = engine.getDriverApi(); mVertexBufferInfoHandle = engine.getVertexBufferInfoFactory().create(driver, - mBufferCount, attributeCount, attributeArray); + mBufferCount, mDeclaredAttributes.count(), mAttributes); mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle); - // If buffer objects are not enabled at the API level, then we create them internally. + + // calculate buffer sizes + size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {}; + #pragma nounroll + for (size_t i = 0, n = mAttributes.size(); i < n; ++i) { + if (mDeclaredAttributes[i]) { + const uint32_t offset = mAttributes[i].offset; + const uint8_t stride = mAttributes[i].stride; + const uint8_t slot = mAttributes[i].buffer; + const size_t end = offset + mVertexCount * stride; + if (slot != Attribute::BUFFER_UNUSED) { + assert_invariant(slot < MAX_VERTEX_BUFFER_COUNT); + bufferSizes[slot] = std::max(bufferSizes[slot], end); + } + } + } + if (!mBufferObjectsEnabled) { + // If buffer objects are not enabled at the API level, then we create them internally. #pragma nounroll - for (size_t i = 0; i < MAX_VERTEX_BUFFER_COUNT; ++i) { - if (bufferSizes[i] > 0) { + for (size_t index = 0; index < MAX_VERTEX_BUFFER_COUNT; ++index) { + size_t const i = mAttributes[index].buffer; + if (i != Attribute::BUFFER_UNUSED) { + assert_invariant(bufferSizes[i] > 0); BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i], backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC); driver.setVertexBufferObject(mHandle, i, bo); @@ -230,13 +291,17 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build } } } else { - // add buffer objects for indices and weights + // in advanced skinning mode, we manage the BONE_INDICES and BONE_WEIGHTS arrays ourselves, + // so we have to set the corresponding buffer objects. if (mAdvancedSkinningEnabled) { - for (size_t i = mBufferCount - 2; i < mBufferCount; ++i) { - BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i], - backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC); - driver.setVertexBufferObject(mHandle, i, bo); - mBufferObjects[i] = bo; + for (auto index : { VertexAttribute::BONE_INDICES, VertexAttribute::BONE_WEIGHTS }) { + size_t const i = mAttributes[index].buffer; + assert_invariant(i != Attribute::BUFFER_UNUSED); + assert_invariant(bufferSizes[i] > 0); + BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i], + backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC); + driver.setVertexBufferObject(mHandle, i, bo); + mBufferObjects[i] = bo; } } } @@ -286,25 +351,22 @@ void FVertexBuffer::setBufferObjectAt(FEngine& engine, uint8_t bufferIndex, } void FVertexBuffer::updateBoneIndicesAndWeights(FEngine& engine, - std::unique_ptr skinJoints, - std::unique_ptr skinWeights) { - + std::unique_ptr skinJoints, + std::unique_ptr skinWeights) { ASSERT_PRECONDITION(mAdvancedSkinningEnabled, "No advanced skinning enabled"); auto jointsData = skinJoints.release(); - auto bdJoints = BufferDescriptor( + engine.getDriverApi().updateBufferObject(mBufferObjects[mBufferCount - 2], { jointsData, mVertexCount * 8, - [](void *buffer, size_t size, void *user) { - delete[] static_cast(buffer); }); - engine.getDriverApi().updateBufferObject(mBufferObjects[mBufferCount - 2], - std::move(bdJoints), 0); + [](void* buffer, size_t, void*) { + delete[] static_cast(buffer); + }}, 0); auto weightsData = skinWeights.release(); - auto bdWeights = BufferDescriptor( + engine.getDriverApi().updateBufferObject(mBufferObjects[mBufferCount - 1], { weightsData, mVertexCount * 16, - [](void *buffer, size_t size, void *user) { - delete[] static_cast(buffer); }); - engine.getDriverApi().updateBufferObject(mBufferObjects[mBufferCount - 1], - std::move(bdWeights), 0); - + [](void* buffer, size_t, void*) { + delete[] static_cast(buffer); + }}, 0); } + } // namespace filament diff --git a/filament/src/details/VertexBuffer.h b/filament/src/details/VertexBuffer.h index 77f610afab..56c5f5bb61 100644 --- a/filament/src/details/VertexBuffer.h +++ b/filament/src/details/VertexBuffer.h @@ -70,14 +70,9 @@ public: private: friend class VertexBuffer; - - struct AttributeData : backend::Attribute { - AttributeData() : backend::Attribute{ .type = backend::ElementType::FLOAT4 } {} - }; - VertexBufferInfoHandle mVertexBufferInfoHandle; VertexBufferHandle mHandle; - std::array mAttributes; + backend::AttributeArray mAttributes; std::array mBufferObjects; AttributeBitset mDeclaredAttributes; uint32_t mVertexCount = 0; From f0f7e299d2ce1a5f3f62821b058353fe00c2015f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 23 Feb 2024 10:09:13 -0800 Subject: [PATCH 22/33] PlatformEGL cleanup --- .../include/backend/platforms/PlatformEGL.h | 7 ++ .../backend/src/opengl/OpenGLPlatform.cpp | 8 +++ .../src/opengl/platforms/PlatformEGL.cpp | 70 ++++++++++++++----- 3 files changed, 66 insertions(+), 19 deletions(-) diff --git a/filament/backend/include/backend/platforms/PlatformEGL.h b/filament/backend/include/backend/platforms/PlatformEGL.h index cb1246657b..61b7ec8c45 100644 --- a/filament/backend/include/backend/platforms/PlatformEGL.h +++ b/filament/backend/include/backend/platforms/PlatformEGL.h @@ -151,6 +151,13 @@ protected: } egl; } ext; + struct SwapChainEGL : public Platform::SwapChain { + EGLSurface sur = EGL_NO_SURFACE; + Config attribs{}; + EGLNativeWindowType nativeWindow{}; + EGLConfig config{}; + }; + void initializeGlExtensions() noexcept; protected: diff --git a/filament/backend/src/opengl/OpenGLPlatform.cpp b/filament/backend/src/opengl/OpenGLPlatform.cpp index 837faecdc4..ccffdfa6d4 100644 --- a/filament/backend/src/opengl/OpenGLPlatform.cpp +++ b/filament/backend/src/opengl/OpenGLPlatform.cpp @@ -18,6 +18,14 @@ #include "OpenGLDriverFactory.h" +#include +#include +#include + +#include + +#include + namespace filament::backend { Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform, diff --git a/filament/backend/src/opengl/platforms/PlatformEGL.cpp b/filament/backend/src/opengl/platforms/PlatformEGL.cpp index 76abd8d554..c05fa09786 100644 --- a/filament/backend/src/opengl/platforms/PlatformEGL.cpp +++ b/filament/backend/src/opengl/platforms/PlatformEGL.cpp @@ -18,16 +18,31 @@ #include "opengl/GLUtils.h" +#include + +#include +#include + #include #include - #if defined(__ANDROID__) + #include #endif - #include + +#include #include -#include +#include + +#include +#include +#include +#include + +#include +#include +#include #ifndef EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE # define EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE 0x3483 @@ -48,7 +63,6 @@ UTILS_PRIVATE PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = {}; } using namespace glext; - // --------------------------------------------------------------------------------------------- // Utilities // --------------------------------------------------------------------------------------------- @@ -137,7 +151,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon ext.egl.KHR_gl_colorspace = extensions.has("EGL_KHR_gl_colorspace"); ext.egl.KHR_create_context = extensions.has("EGL_KHR_create_context"); ext.egl.KHR_no_config_context = extensions.has("EGL_KHR_no_config_context"); - ext.egl.KHR_surfaceless_context = extensions.has("KHR_surfaceless_context"); + ext.egl.KHR_surfaceless_context = extensions.has("EGL_KHR_surfaceless_context"); if (ext.egl.KHR_create_context) { // KHR_create_context implies KHR_surfaceless_context for ES3.x contexts ext.egl.KHR_surfaceless_context = true; @@ -461,7 +475,13 @@ Platform::SwapChain* PlatformEGL::createSwapChain( // this is not fatal eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED); - return (SwapChain*)sur; + SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({ + .sur = sur, + .attribs = std::move(attribs), + .nativeWindow = (EGLNativeWindowType)nativeWindow, + .config = config + }); + return sc; } Platform::SwapChain* PlatformEGL::createSwapChain( @@ -495,30 +515,42 @@ Platform::SwapChain* PlatformEGL::createSwapChain( logEglError("eglCreatePbufferSurface"); return nullptr; } - return (SwapChain*)sur; + + SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({ + .sur = sur, + .attribs = std::move(attribs), + .config = config + }); + return sc; } void PlatformEGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept { - EGLSurface sur = (EGLSurface) swapChain; - if (sur != EGL_NO_SURFACE) { - makeCurrent(mEGLDummySurface, mEGLDummySurface); - eglDestroySurface(mEGLDisplay, sur); + if (swapChain) { + SwapChainEGL const* const sc = static_cast(swapChain); + if (sc->sur != EGL_NO_SURFACE) { + makeCurrent(mEGLDummySurface, mEGLDummySurface); + eglDestroySurface(mEGLDisplay, sc->sur); + delete sc; + } } } void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain, Platform::SwapChain* readSwapChain) noexcept { - EGLSurface drawSur = (EGLSurface) drawSwapChain; - EGLSurface readSur = (EGLSurface) readSwapChain; - if (drawSur != EGL_NO_SURFACE || readSur != EGL_NO_SURFACE) { - makeCurrent(drawSur, readSur); + SwapChainEGL const* const dsc = static_cast(drawSwapChain); + SwapChainEGL const* const rsc = static_cast(readSwapChain); + if (UTILS_UNLIKELY(dsc->sur == EGL_NO_SURFACE && rsc->sur == EGL_NO_SURFACE)) { + return; } + makeCurrent(dsc->sur, rsc->sur); } void PlatformEGL::commit(Platform::SwapChain* swapChain) noexcept { - EGLSurface sur = (EGLSurface) swapChain; - if (sur != EGL_NO_SURFACE) { - eglSwapBuffers(mEGLDisplay, sur); + if (swapChain) { + SwapChainEGL const* const sc = static_cast(swapChain); + if (sc->sur != EGL_NO_SURFACE) { + eglSwapBuffers(mEGLDisplay, sc->sur); + } } } @@ -561,7 +593,7 @@ FenceStatus PlatformEGL::waitFence( } OpenGLPlatform::ExternalTexture* PlatformEGL::createExternalImageTexture() noexcept { - ExternalTexture* outTexture = new ExternalTexture{}; + ExternalTexture* outTexture = new(std::nothrow) ExternalTexture{}; glGenTextures(1, &outTexture->id); if (UTILS_LIKELY(ext.gl.OES_EGL_image_external_essl3)) { outTexture->target = GL_TEXTURE_EXTERNAL_OES; From 1ae82d325c3ce0f23bb76027e898202e30a9eb85 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Mon, 26 Feb 2024 17:05:46 +0000 Subject: [PATCH 23/33] Fix shader compilation in threadpool mode (#7602) When shader compilation happens in threadpool mode, shader source code isn't stored correctly in the token. This leads to empty error messages if a shader has problems later on. --- filament/backend/src/opengl/ShaderCompilerService.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp index cf5c900c9d..3c1f57f832 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.cpp +++ b/filament/backend/src/opengl/ShaderCompilerService.cpp @@ -261,12 +261,11 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( [this, &gl, program = std::move(program), token]() mutable { // compile the shaders std::array shaders{}; - std::array shaderSourceCode; compileShaders(gl, std::move(program.getShadersSource()), program.getSpecializationConstants(), shaders, - shaderSourceCode); + token->shaderSourceCode); // link the program GLuint const glProgram = linkProgram(gl, shaders, token->attributes); From d05c61fe9a982035caecfea179896b4111fb28f3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 26 Feb 2024 05:16:10 -0800 Subject: [PATCH 24/33] fix VulkanResourceBase size on Windows VulkanResourceBase was intended to be 8 bytes, however, bitfields are not packed by msvc if they don't use the same type. --- filament/backend/src/vulkan/VulkanResources.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanResources.h b/filament/backend/src/vulkan/VulkanResources.h index 1c37e4237f..c5b7440ffc 100644 --- a/filament/backend/src/vulkan/VulkanResources.h +++ b/filament/backend/src/vulkan/VulkanResources.h @@ -63,16 +63,16 @@ struct VulkanResourceBase { protected: explicit VulkanResourceBase(VulkanResourceType type) : mRefCount(IS_HEAP_ALLOC_TYPE(type) ? 1 : 0), - mType(type), + mType(uint32_t(type)), mHandleId(0) { } private: - inline VulkanResourceType getType() { - return mType; + inline VulkanResourceType getType() const noexcept { + return VulkanResourceType(mType); } - inline HandleBase::HandleId getId() { + inline HandleBase::HandleId getId() const noexcept { return mHandleId; } @@ -81,7 +81,7 @@ private: } inline void ref() noexcept { - if (IS_HEAP_ALLOC_TYPE(mType)) { + if (IS_HEAP_ALLOC_TYPE(getType())) { return; } assert_invariant(mRefCount < ((1<<24) - 1)); @@ -89,19 +89,19 @@ private: } inline void deref() noexcept { - if (IS_HEAP_ALLOC_TYPE(mType)) { + if (IS_HEAP_ALLOC_TYPE(getType())) { return; } assert_invariant(mRefCount > 0); --mRefCount; } - inline size_t refcount() noexcept { + inline size_t refcount() const noexcept { return mRefCount; } uint32_t mRefCount : 24; // 16M is enough for the refcount - VulkanResourceType mType : 8; + uint32_t mType : 8; // must be uint32_t or MSVC doesn't pack it. no codegen impact w/ clang. HandleBase::HandleId mHandleId; friend struct VulkanThreadSafeResource; @@ -111,6 +111,8 @@ private: friend class VulkanResourceManagerImpl; }; +static_assert(sizeof(VulkanResourceBase) == 8, "VulkanResourceBase should be 8 bytes"); + struct VulkanThreadSafeResource { protected: explicit VulkanThreadSafeResource(VulkanResourceType type) From c2b5f08bc7e1ae29f89121f7292d247c287862c8 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Mon, 26 Feb 2024 14:54:18 +0000 Subject: [PATCH 25/33] Fix Renderer FrameRateOptions interval default From https://github.com/google/filament/issues/7539 And I got to the same conclusion based on https://github.com/google/filament/blob/f0f7e299d2ce1a5f3f62821b058353fe00c2015f/filament/src/details/View.cpp#L191 --- .../src/main/java/com/google/android/filament/Renderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/filament-android/src/main/java/com/google/android/filament/Renderer.java b/android/filament-android/src/main/java/com/google/android/filament/Renderer.java index 3a9b6ab94d..ab70e9ed38 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Renderer.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Renderer.java @@ -101,7 +101,7 @@ public class Renderer { /** * Desired frame interval in unit of 1 / DisplayInfo.refreshRate. */ - public float interval = 1.0f / 60.0f; + public float interval = 1.0f; /** * Additional headroom for the GPU as a ratio of the targetFrameTime. From 4836f946354701818daef3927224297e3b5ae063 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Mon, 26 Feb 2024 15:00:55 +0000 Subject: [PATCH 26/33] Update NEW_RELEASE_NOTES.md --- NEW_RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 7e1350a537..649758ec1b 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -11,3 +11,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). - android: NDK 26.1.10909125 is used by default - android: Minimum API level on Android is now API 21 instead of API 19. This allows the use of OpenGL ES 3.1 - rendering: New PBR Neutral tone mapper, designed to preserve materials color appearance +- android: Change default frameRateOptions.interval to 1.0 From ef488fdf5709ea6bbf76111f1a7653b950d906ac Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Mon, 26 Feb 2024 13:02:58 -0800 Subject: [PATCH 27/33] Add blit array shader This shader takes an array texture and a layer index to draw to the current render target. This will be used for debugging purpose to combine an array texture rendered from the multiview feature that is going to be implemented later, so that we can verify the feature properly performed. --- filament/CMakeLists.txt | 1 + filament/src/PostProcessManager.cpp | 79 ++++++++++++++++++++++++++++ filament/src/PostProcessManager.h | 9 ++++ filament/src/materials/blitArray.mat | 38 +++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 filament/src/materials/blitArray.mat diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 1855eec649..698c139332 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -215,6 +215,7 @@ set(MATERIAL_SRCS src/materials/antiAliasing/fxaa.mat src/materials/antiAliasing/taa.mat src/materials/blitLow.mat + src/materials/blitArray.mat src/materials/bloom/bloomDownsample.mat src/materials/bloom/bloomDownsample2x.mat src/materials/bloom/bloomDownsample9.mat diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index eb4fcf55f8..3a1b2b18a3 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -262,6 +262,7 @@ static const PostProcessManager::MaterialInfo sMaterialListFeatureLevel0[] = { static const PostProcessManager::MaterialInfo sMaterialList[] = { { "bilateralBlur", MATERIAL(BILATERALBLUR) }, { "bilateralBlurBentNormals", MATERIAL(BILATERALBLURBENTNORMALS) }, + { "blitArray", MATERIAL(BLITARRAY) }, { "bloomDownsample", MATERIAL(BLOOMDOWNSAMPLE) }, { "bloomDownsample2x", MATERIAL(BLOOMDOWNSAMPLE2X) }, { "bloomDownsample9", MATERIAL(BLOOMDOWNSAMPLE9) }, @@ -3283,6 +3284,84 @@ FrameGraphId PostProcessManager::debugShadowCascades(FrameGra return debugShadowCascadePass->output; } +FrameGraphId PostProcessManager::debugCombineArrayTexture(FrameGraph& fg, + bool translucent, FrameGraphId input, + filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc, + SamplerMagFilter filterMag, + SamplerMinFilter filterMin) noexcept { + + assert_invariant(fg.getDescriptor(input).depth > 1); + assert_invariant(fg.getDescriptor(input).type == SamplerType::SAMPLER_2D_ARRAY); + + // TODO: add support for sub-resources + assert_invariant(fg.getSubResourceDescriptor(input).layer == 0); + assert_invariant(fg.getSubResourceDescriptor(input).level == 0); + + struct QuadBlitData { + FrameGraphId input; + FrameGraphId output; + }; + + auto& ppQuadBlit = fg.addPass("combining array tex", + [&](FrameGraph::Builder& builder, auto& data) { + data.input = builder.sample(input); + data.output = builder.createTexture("upscaled output", outDesc); + data.output = builder.write(data.output, + FrameGraphTexture::Usage::COLOR_ATTACHMENT); + builder.declareRenderPass(builder.getName(data.output), { + .attachments = {.color = { data.output }}, + .clearFlags = TargetBufferFlags::DEPTH }); + }, + [=](FrameGraphResources const& resources, + auto const& data, DriverApi& driver) { + auto color = resources.getTexture(data.input); + auto const& inputDesc = resources.getDescriptor(data.input); + auto out = resources.getRenderPassInfo(); + + // -------------------------------------------------------------------------------- + // set uniforms + + PostProcessMaterial const& material = getPostProcessMaterial("blitArray"); + auto* mi = material.getMaterialInstance(mEngine); + mi->setParameter("color", color, { + .filterMag = filterMag, + .filterMin = filterMin + }); + mi->setParameter("layerIndex", 0); + mi->setParameter("viewport", float4{ + float(vp.left) / inputDesc.width, + float(vp.bottom) / inputDesc.height, + float(vp.width) / inputDesc.width, + float(vp.height) / inputDesc.height + }); + mi->commit(driver); + mi->use(driver); + + auto pipeline = material.getPipelineState(mEngine); + if (translucent) { + pipeline.first.rasterState.blendFunctionSrcRGB = BlendFunction::ONE; + pipeline.first.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE; + pipeline.first.rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA; + pipeline.first.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA; + } + + // Blit the scene rendered to the first layer to the left half of the screen. + out.params.viewport.width /= 2; + render(out, pipeline, driver); + + // Blit the scene rendered to the second layer to the right half of the screen. + // Don't clear or discard the target to keep the previously rendered left half image. + mi->setParameter("layerIndex", 1); + mi->commit(driver); + out.params.flags.clear = filament::backend::TargetBufferFlags::NONE; + out.params.flags.discardStart = filament::backend::TargetBufferFlags::NONE; + out.params.viewport.left += out.params.viewport.width; + render(out, pipeline, driver); + }); + + return ppQuadBlit->output; +} + FrameGraphId PostProcessManager::debugDisplayShadowTexture( FrameGraph& fg, FrameGraphId input, diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index badf5b9a8c..432a9e3fff 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -288,6 +288,15 @@ public: FrameGraphId shadowmap, float scale, uint8_t layer, uint8_t level, uint8_t channel, float power) noexcept; + // Combine an array texture pointed to by `input` into a single image, then return it. + // This is only useful to check the multiview rendered scene as a debugging purpose, thus this + // is not expected to be used in normal cases. + FrameGraphId debugCombineArrayTexture(FrameGraph& fg, bool translucent, + FrameGraphId input, + filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc, + backend::SamplerMagFilter filterMag, + backend::SamplerMinFilter filterMin) noexcept; + backend::Handle getOneTexture() const; backend::Handle getZeroTexture() const; backend::Handle getOneTextureArray() const; diff --git a/filament/src/materials/blitArray.mat b/filament/src/materials/blitArray.mat new file mode 100644 index 0000000000..2d7cfc4da3 --- /dev/null +++ b/filament/src/materials/blitArray.mat @@ -0,0 +1,38 @@ +material { + name : blitArray, + parameters : [ + { + type : sampler2dArray, + name : color, + precision: medium + }, + { + type : int, + name : layerIndex + }, + { + type : float4, + name : viewport, + precision: high + } + ], + variables : [ + vertex + ], + depthWrite : false, + depthCulling : false, + domain: postprocess +} + +vertex { + void postProcessVertex(inout PostProcessVertexInputs postProcess) { + postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw; + postProcess.vertex.xy = uvToRenderTargetUV(postProcess.vertex.xy); + } +} + +fragment { + void postProcess(inout PostProcessInputs postProcess) { + postProcess.color = textureLod(materialParams_color, vec3(variable_vertex.xy, materialParams.layerIndex), 0.0); + } +} From 02d2e2f644288340573f28628fc9d94218866351 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Tue, 27 Feb 2024 13:00:24 -0800 Subject: [PATCH 28/33] Rename InstancedStereo as Stereo (#7608) We're going to add a new implementation of stereoscopic rendering using multiview. Thus we want to remove the word `Instanced` from all methods and properties. --- filament/src/RenderPass.cpp | 18 +++++++++--------- filament/src/details/Renderer.cpp | 4 ++-- filament/src/details/View.h | 2 +- .../include/private/filament/Variant.h | 2 +- libs/filamat/src/MaterialBuilder.cpp | 2 +- libs/filamat/src/shaders/CodeGenerator.cpp | 2 +- libs/filamat/src/shaders/ShaderGenerator.cpp | 8 ++++---- libs/filamat/src/shaders/ShaderGenerator.h | 2 +- shaders/src/common_getters.glsl | 2 +- shaders/src/main.vs | 6 +++--- shaders/src/varyings.glsl | 4 ++-- 11 files changed, 26 insertions(+), 26 deletions(-) diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index ffaeb5b8f2..016c9976a4 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -434,7 +434,7 @@ void RenderPass::generateCommands(CommandTypeFlags commandTypeFlags, Command* co FScene::RenderableSoa const& soa, Range range, Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask, float3 cameraPosition, float3 cameraForward, - uint8_t instancedStereoEyeCount) noexcept { + uint8_t stereoEyeCount) noexcept { SYSTRACE_CALL(); @@ -466,12 +466,12 @@ void RenderPass::generateCommands(CommandTypeFlags commandTypeFlags, Command* co case CommandTypeFlags::COLOR: curr = generateCommandsImpl(commandTypeFlags, curr, soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward, - instancedStereoEyeCount); + stereoEyeCount); break; case CommandTypeFlags::DEPTH: curr = generateCommandsImpl(commandTypeFlags, curr, soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward, - instancedStereoEyeCount); + stereoEyeCount); break; default: // we should never end-up here @@ -494,7 +494,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla Command* UTILS_RESTRICT curr, FScene::RenderableSoa const& UTILS_RESTRICT soa, Range range, Variant const variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask, - float3 cameraPosition, float3 cameraForward, uint8_t instancedStereoEyeCount) noexcept { + float3 cameraPosition, float3 cameraForward, uint8_t stereoEyeCount) noexcept { // generateCommands() writes both the draw and depth commands simultaneously such that // we go throw the list of renderables just once. @@ -520,7 +520,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla const bool hasShadowing = renderFlags & HAS_SHADOWING; const bool viewInverseFrontFaces = renderFlags & HAS_INVERSE_FRONT_FACES; - const bool hasInstancedStereo = renderFlags & IS_STEREOSCOPIC; + const bool hasStereo = renderFlags & IS_STEREOSCOPIC; Command cmdColor; @@ -589,9 +589,9 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla // soaInstanceInfo[i].count is the number of instances the user has requested, either for // manual or hybrid instancing. Instanced stereo multiplies the number of instances by the // eye count. - if (UTILS_UNLIKELY(hasInstancedStereo)) { + if (UTILS_UNLIKELY(hasStereo)) { cmdColor.primitive.instanceCount = - (soaInstanceInfo[i].count * instancedStereoEyeCount) | + (soaInstanceInfo[i].count * stereoEyeCount) | PrimitiveInfo::USER_INSTANCE_MASK; } @@ -623,9 +623,9 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla cmdDepth.primitive.skinningTexture = skinning.handleSampler; cmdDepth.primitive.morphWeightBuffer = morphing.handle; - if (UTILS_UNLIKELY(hasInstancedStereo)) { + if (UTILS_UNLIKELY(hasStereo)) { cmdColor.primitive.instanceCount = - (soaInstanceInfo[i].count * instancedStereoEyeCount) | + (soaInstanceInfo[i].count * stereoEyeCount) | PrimitiveInfo::USER_INSTANCE_MASK; } } diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index c014be6eb6..65f2bbc48d 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -679,7 +679,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { RenderPass::RenderFlags renderFlags = 0; if (view.hasShadowing()) renderFlags |= RenderPass::HAS_SHADOWING; if (view.isFrontFaceWindingInverted()) renderFlags |= RenderPass::HAS_INVERSE_FRONT_FACES; - if (view.hasInstancedStereo()) renderFlags |= RenderPass::IS_STEREOSCOPIC; + if (view.hasStereo()) renderFlags |= RenderPass::IS_STEREOSCOPIC; RenderPassBuilder passBuilder(commandArena); passBuilder.renderFlags(renderFlags); @@ -689,7 +689,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { variant.setDynamicLighting(view.hasDynamicLighting()); variant.setFog(view.hasFog()); variant.setVsm(view.hasShadowing() && view.getShadowType() != ShadowType::PCF); - variant.setStereo(view.hasInstancedStereo()); + variant.setStereo(view.hasStereo()); /* * Frame graph diff --git a/filament/src/details/View.h b/filament/src/details/View.h index 6680306296..d707001a42 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -173,7 +173,7 @@ public: bool hasDPCF() const noexcept { return mShadowType == ShadowType::DPCF; } bool hasPCSS() const noexcept { return mShadowType == ShadowType::PCSS; } bool hasPicking() const noexcept { return mActivePickingQueriesList != nullptr; } - bool hasInstancedStereo() const noexcept { + bool hasStereo() const noexcept { return mIsStereoSupported && mStereoscopicOptions.enabled; } diff --git a/libs/filabridge/include/private/filament/Variant.h b/libs/filabridge/include/private/filament/Variant.h index 2c3af27e4c..fc94dec7f6 100644 --- a/libs/filabridge/include/private/filament/Variant.h +++ b/libs/filabridge/include/private/filament/Variant.h @@ -115,7 +115,7 @@ struct Variant { inline bool hasDirectionalLighting() const noexcept { return key & DIR; } inline bool hasDynamicLighting() const noexcept { return key & DYN; } inline bool hasSkinningOrMorphing() const noexcept { return key & SKN; } - inline bool hasInstancedStereo() const noexcept { return key & STE; } + inline bool hasStereo() const noexcept { return key & STE; } inline void setDirectionalLighting(bool v) noexcept { set(v, DIR); } inline void setDynamicLighting(bool v) noexcept { set(v, DYN); } diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index f9da56bd76..492c898c69 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -894,7 +894,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0; } -bool ShaderGenerator::hasInstancedStereo( +bool ShaderGenerator::hasStereo( filament::Variant variant, MaterialBuilder::FeatureLevel featureLevel) noexcept { - return variant.hasInstancedStereo() + return variant.hasStereo() // HACK(exv): Ignore stereo variant when targeting ESSL 1.0. We should properly build a // system in matc which allows the set of included variants to differ per-feature level. && featureLevel > MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0; diff --git a/libs/filamat/src/shaders/ShaderGenerator.h b/libs/filamat/src/shaders/ShaderGenerator.h index 40c7847d2c..59b7f2685a 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.h +++ b/libs/filamat/src/shaders/ShaderGenerator.h @@ -115,7 +115,7 @@ private: filament::Variant variant, MaterialBuilder::FeatureLevel featureLevel) noexcept; - static bool hasInstancedStereo( + static bool hasStereo( filament::Variant variant, MaterialBuilder::FeatureLevel featureLevel) noexcept; diff --git a/shaders/src/common_getters.glsl b/shaders/src/common_getters.glsl index 718ea05c5b..eaa2521d85 100644 --- a/shaders/src/common_getters.glsl +++ b/shaders/src/common_getters.glsl @@ -24,7 +24,7 @@ highp mat4 getViewFromClipMatrix() { /** @public-api */ highp mat4 getClipFromWorldMatrix() { -#if defined(VARIANT_HAS_INSTANCED_STEREO) +#if defined(VARIANT_HAS_STEREO) int eye = instance_index % CONFIG_STEREO_EYE_COUNT; return frameUniforms.clipFromWorldMatrix[eye]; #else diff --git a/shaders/src/main.vs b/shaders/src/main.vs index 6856d21a89..5ef0ebbe20 100644 --- a/shaders/src/main.vs +++ b/shaders/src/main.vs @@ -23,7 +23,7 @@ void main() { logical_instance_index = instance_index; #endif -#if defined(VARIANT_HAS_INSTANCED_STEREO) +#if defined(VARIANT_HAS_STEREO) #if !defined(FILAMENT_HAS_FEATURE_INSTANCING) #error Instanced stereo not supported at this feature level #endif @@ -215,7 +215,7 @@ void main() { // this must happen before we compensate for vulkan below vertex_position = position; -#if defined(VARIANT_HAS_INSTANCED_STEREO) +#if defined(VARIANT_HAS_STEREO) // We're transforming a vertex whose x coordinate is within the range (-w to w). // To move it to the correct portion of the viewport, we need to modify the x coordinate. // It's important to do this after computing vertex_position. @@ -253,7 +253,7 @@ void main() { // some PowerVR drivers crash when gl_Position is written more than once gl_Position = position; -#if defined(VARIANT_HAS_INSTANCED_STEREO) +#if defined(VARIANT_HAS_STEREO) // Fragment shaders filter out the stereo variant, so we need to set instance_index here. instance_index = logical_instance_index; #endif diff --git a/shaders/src/varyings.glsl b/shaders/src/varyings.glsl index fe5c24d247..a7a48a302f 100644 --- a/shaders/src/varyings.glsl +++ b/shaders/src/varyings.glsl @@ -34,7 +34,7 @@ LAYOUT_LOCATION(11) VARYING highp vec4 vertex_lightSpacePosition; // Note that fragColor is an output and is not declared here; see main.fs and depth_main.fs -#if defined(VARIANT_HAS_INSTANCED_STEREO) +#if defined(VARIANT_HAS_STEREO) #if defined(GL_ES) && defined(FILAMENT_GLSLANG) // On ES, gl_ClipDistance is not a built-in, so we have to rely on EXT_clip_cull_distance // However, this extension is not supported by glslang, so we instead write to @@ -46,4 +46,4 @@ LAYOUT_LOCATION(100) out float filament_gl_ClipDistance[2]; // If we're on Desktop GL (or not running shaders through glslang), we're free to use gl_ClipDistance #define FILAMENT_CLIPDISTANCE gl_ClipDistance #endif // GL_ES && FILAMENT_GLSLANG -#endif // VARIANT_HAS_INSTANCED_STEREO +#endif // VARIANT_HAS_STEREO From b425d63b95f04c910dbd61d0b9c68f4450ea9036 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 27 Feb 2024 14:14:49 -0800 Subject: [PATCH 29/33] Release Filament 1.50.4 --- NEW_RELEASE_NOTES.md | 5 ----- README.md | 4 ++-- RELEASE_NOTES.md | 7 +++++++ android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 649758ec1b..4a1a9c7fa7 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,8 +7,3 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - -- android: NDK 26.1.10909125 is used by default -- android: Minimum API level on Android is now API 21 instead of API 19. This allows the use of OpenGL ES 3.1 -- rendering: New PBR Neutral tone mapper, designed to preserve materials color appearance -- android: Change default frameRateOptions.interval to 1.0 diff --git a/README.md b/README.md index bb2c074eb1..66f0976391 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.50.3' + implementation 'com.google.android.filament:filament-android:1.50.4' } ``` @@ -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.3' +pod 'Filament', '~> 1.50.4' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f7fa37e7f5..f538299884 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,13 @@ 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.50.5 + +- android: NDK 26.1.10909125 is used by default +- android: Minimum API level on Android is now API 21 instead of API 19. This allows the use of OpenGL ES 3.1 +- rendering: New PBR Neutral tone mapper, designed to preserve materials color appearance +- android: Change default frameRateOptions.interval to 1.0 + ## v1.50.4 diff --git a/android/gradle.properties b/android/gradle.properties index 53e64758c2..34340a20b8 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.50.3 +VERSION_NAME=1.50.4 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 5b53805fa6..df0911beb7 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.3" + spec.version = "1.50.4" 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.3/filament-v1.50.3-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.4/filament-v1.50.4-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/web/filament-js/package.json b/web/filament-js/package.json index 7ebc0a0ff4..52b661bd10 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.50.3", + "version": "1.50.4", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From ab0063bc6befe33a66cb742829e2c9e0d1870080 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 27 Feb 2024 14:15:04 -0800 Subject: [PATCH 30/33] Bump version to 1.50.5 --- README.md | 4 ++-- android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 66f0976391..38792b3936 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.50.4' + implementation 'com.google.android.filament:filament-android:1.50.5' } ``` @@ -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.4' +pod 'Filament', '~> 1.50.5' ``` ### Snapshots diff --git a/android/gradle.properties b/android/gradle.properties index 34340a20b8..0c82646753 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.50.4 +VERSION_NAME=1.50.5 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index df0911beb7..29df33ce0f 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.4" + spec.version = "1.50.5" 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.4/filament-v1.50.4-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.5/filament-v1.50.5-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/web/filament-js/package.json b/web/filament-js/package.json index 52b661bd10..ff16aeadc9 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.50.4", + "version": "1.50.5", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From 8c31f46683a111bbaa4c66776ecf46b1b2f8d2f7 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Tue, 27 Feb 2024 16:11:33 -0800 Subject: [PATCH 31/33] Keep supporting API level 19 (#7609) This is a partial rollback from d83b3858b3dbc97b2a5e039ae9fb5d170dd95907. Keep supporting API level 19 for some of our clients. --- .../backend/src/opengl/platforms/PlatformEGLAndroid.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp index c705062238..b61bbeab9a 100644 --- a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp +++ b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp @@ -29,9 +29,11 @@ #include -// We require filament to be built with a API 21 toolchain, before that, OpenGLES 3.1 didn't exist -#if __ANDROID_API__ < 21 -# error "__ANDROID_API__ must be at least 21" +// 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. +#if __ANDROID_API__ < 19 +# error "__ANDROID_API__ must be at least 19" #endif using namespace utils; From 04c7f84c6fc63d882883b68c135b33b0b578d6d9 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 5 Mar 2024 12:09:16 -0800 Subject: [PATCH 32/33] engine: avoid leaking vertex buffer (#7628) Previous commit [1] changed the semantic of the index to mBufferObjects. Here we just make sure that if a buffer has been allocated, we don't allocate another (otherwise, we'd leak). Also cleaned up `updateBoneIndicesAndWeights` indexing [1]: a3131a64b6cef2306965eec35a2f9a348601fc00 --- filament/src/details/VertexBuffer.cpp | 40 +++++++++++++++------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/filament/src/details/VertexBuffer.cpp b/filament/src/details/VertexBuffer.cpp index 868ca60e92..d718bbe0a8 100644 --- a/filament/src/details/VertexBuffer.cpp +++ b/filament/src/details/VertexBuffer.cpp @@ -284,10 +284,12 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build size_t const i = mAttributes[index].buffer; if (i != Attribute::BUFFER_UNUSED) { assert_invariant(bufferSizes[i] > 0); - BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i], - backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC); - driver.setVertexBufferObject(mHandle, i, bo); - mBufferObjects[i] = bo; + if (!mBufferObjects[i]) { + BufferObjectHandle bo = driver.createBufferObject(bufferSizes[i], + backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC); + driver.setVertexBufferObject(mHandle, i, bo); + mBufferObjects[i] = bo; + } } } } else { @@ -298,10 +300,12 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build size_t const i = mAttributes[index].buffer; assert_invariant(i != Attribute::BUFFER_UNUSED); assert_invariant(bufferSizes[i] > 0); - BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i], - backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC); - driver.setVertexBufferObject(mHandle, i, bo); - mBufferObjects[i] = bo; + if (!mBufferObjects[i]) { + BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i], + backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC); + driver.setVertexBufferObject(mHandle, i, bo); + mBufferObjects[i] = bo; + } } } } @@ -355,18 +359,18 @@ void FVertexBuffer::updateBoneIndicesAndWeights(FEngine& engine, std::unique_ptr skinWeights) { ASSERT_PRECONDITION(mAdvancedSkinningEnabled, "No advanced skinning enabled"); auto jointsData = skinJoints.release(); - engine.getDriverApi().updateBufferObject(mBufferObjects[mBufferCount - 2], { - jointsData, mVertexCount * 8, - [](void* buffer, size_t, void*) { - delete[] static_cast(buffer); - }}, 0); + uint8_t const indicesIndex = mAttributes[VertexAttribute::BONE_INDICES].buffer; + engine.getDriverApi().updateBufferObject(mBufferObjects[indicesIndex], + {jointsData, mVertexCount * 8, + [](void* buffer, size_t, void*) { delete[] static_cast(buffer); }}, + 0); auto weightsData = skinWeights.release(); - engine.getDriverApi().updateBufferObject(mBufferObjects[mBufferCount - 1], { - weightsData, mVertexCount * 16, - [](void* buffer, size_t, void*) { - delete[] static_cast(buffer); - }}, 0); + uint8_t const weightsIndex = mAttributes[VertexAttribute::BONE_WEIGHTS].buffer; + engine.getDriverApi().updateBufferObject(mBufferObjects[weightsIndex], + {weightsData, mVertexCount * 16, + [](void* buffer, size_t, void*) { delete[] static_cast(buffer); }}, + 0); } } // namespace filament From 4e648b224f7df7abbe406557590e50825a760fdc Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 5 Mar 2024 17:01:20 -0800 Subject: [PATCH 33/33] Revert "vk: remove subpasses to simplify descriptor set refactor (#7592)" (#7630) This reverts commit a9793b3cf619394dc738e3483222a7cff6b81dcf. Due to change in output for swiftshader --- filament/backend/src/vulkan/VulkanConstants.h | 12 +-- filament/backend/src/vulkan/VulkanContext.h | 1 + filament/backend/src/vulkan/VulkanDriver.cpp | 34 +++++++- .../backend/src/vulkan/VulkanFboCache.cpp | 65 ++++++++++++-- filament/backend/src/vulkan/VulkanHandles.cpp | 5 +- .../src/vulkan/VulkanPipelineCache.cpp | 85 ++++++++++++++++--- .../backend/src/vulkan/VulkanPipelineCache.h | 47 +++++----- 7 files changed, 199 insertions(+), 50 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index b0a05d6100..a2b62bd34c 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -66,7 +66,8 @@ // Usefaul default combinations #define FVK_DEBUG_EVERYTHING 0xFFFFFFFF #define FVK_DEBUG_PERFORMANCE \ - FVK_DEBUG_SYSTRACE + FVK_DEBUG_SYSTRACE | \ + FVK_DEBUG_GROUP_MARKERS #define FVK_DEBUG_CORRECTNESS \ FVK_DEBUG_VALIDATION | \ @@ -79,17 +80,18 @@ FVK_DEBUG_PRINT_GROUP_MARKERS #ifndef NDEBUG -#define FVK_DEBUG_FLAGS FVK_DEBUG_PERFORMANCE +#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_DEBUG_UTILS | FVK_DEBUG_VALIDATION) #else #define FVK_DEBUG_FLAGS 0 #endif -#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags)) -#define FVK_ENABLED_BOOL(flags) FVK_ENABLED(flags) +#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags)) +#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(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 -#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) +#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) static_assert(FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_VALIDATION)); #endif diff --git a/filament/backend/src/vulkan/VulkanContext.h b/filament/backend/src/vulkan/VulkanContext.h index 88b9904276..c9a4fa2c4e 100644 --- a/filament/backend/src/vulkan/VulkanContext.h +++ b/filament/backend/src/vulkan/VulkanContext.h @@ -83,6 +83,7 @@ struct VulkanRenderPass { VulkanRenderTarget* renderTarget; VkRenderPass renderPass; RenderPassParams params; + int currentSubpass; }; // This is a collection of immutable data about the vulkan context. This actual handles to the diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 8e2d0ed460..05a892dedc 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -848,7 +848,7 @@ bool VulkanDriver::isRenderTargetFormatSupported(TextureFormat format) { } bool VulkanDriver::isFrameBufferFetchSupported() { - return false; + return true; } bool VulkanDriver::isFrameBufferFetchMultiSampleSupported() { @@ -1247,7 +1247,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP } VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey); - mPipelineCache.bindRenderPass(renderPass); + mPipelineCache.bindRenderPass(renderPass, 0); // Create the VkFramebuffer or fetch it from cache. VulkanFboCache::FboKey fbkey { @@ -1372,6 +1372,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP .renderTarget = rt, .renderPass = renderPassInfo.renderPass, .params = params, + .currentSubpass = 0, }; FVK_SYSTRACE_END(); } @@ -1414,13 +1415,40 @@ void VulkanDriver::endRenderPass(int) { 0, 1, &barrier, 0, nullptr, 0, nullptr); } + if (mCurrentRenderPass.currentSubpass > 0) { + for (uint32_t i = 0; i < VulkanPipelineCache::INPUT_ATTACHMENT_COUNT; i++) { + mPipelineCache.bindInputAttachment(i, {}); + } + mCurrentRenderPass.currentSubpass = 0; + } mCurrentRenderPass.renderTarget = nullptr; mCurrentRenderPass.renderPass = VK_NULL_HANDLE; FVK_SYSTRACE_END(); } void VulkanDriver::nextSubpass(int) { - PANIC_POSTCONDITION("Subpasses are unsupported"); + ASSERT_PRECONDITION(mCurrentRenderPass.currentSubpass == 0, + "Only two subpasses are currently supported."); + + VulkanRenderTarget* renderTarget = mCurrentRenderPass.renderTarget; + assert_invariant(renderTarget); + assert_invariant(mCurrentRenderPass.params.subpassMask); + + vkCmdNextSubpass(mCommands->get().buffer(), VK_SUBPASS_CONTENTS_INLINE); + + mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass, + ++mCurrentRenderPass.currentSubpass); + + for (uint32_t i = 0; i < VulkanPipelineCache::INPUT_ATTACHMENT_COUNT; i++) { + if ((1 << i) & mCurrentRenderPass.params.subpassMask) { + VulkanAttachment subpassInput = renderTarget->getColor(i); + VkDescriptorImageInfo info = { + .imageView = subpassInput.getImageView(VK_IMAGE_ASPECT_COLOR_BIT), + .imageLayout = ImgUtil::getVkLayout(subpassInput.getLayout()), + }; + mPipelineCache.bindInputAttachment(i, info); + } + } } void VulkanDriver::setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt, diff --git a/filament/backend/src/vulkan/VulkanFboCache.cpp b/filament/backend/src/vulkan/VulkanFboCache.cpp index 200ea23e02..0546ffd04f 100644 --- a/filament/backend/src/vulkan/VulkanFboCache.cpp +++ b/filament/backend/src/vulkan/VulkanFboCache.cpp @@ -129,6 +129,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { iter.value().timestamp = mCurrentTime; return iter->second.handle; } + const bool hasSubpasses = config.subpassMask != 0; // Set up some const aliases for terseness. const VkAttachmentLoadOp kClear = VK_ATTACHMENT_LOAD_OP_CLEAR; @@ -140,18 +141,26 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { // In Vulkan, the subpass desc specifies the layout to transition to at the start of the render // pass, and the attachment description specifies the layout to transition to at the end. + VkAttachmentReference inputAttachmentRef[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; VkAttachmentReference colorAttachmentRefs[2][MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; VkAttachmentReference resolveAttachmentRef[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; VkAttachmentReference depthAttachmentRef = {}; const bool hasDepth = config.depthFormat != VK_FORMAT_UNDEFINED; - VkSubpassDescription subpasses[1] = {{ + VkSubpassDescription subpasses[2] = {{ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .pInputAttachments = nullptr, .pColorAttachments = colorAttachmentRefs[0], .pResolveAttachments = resolveAttachmentRef, .pDepthStencilAttachment = hasDepth ? &depthAttachmentRef : nullptr + }, + { + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .pInputAttachments = inputAttachmentRef, + .pColorAttachments = colorAttachmentRefs[1], + .pResolveAttachments = resolveAttachmentRef, + .pDepthStencilAttachment = hasDepth ? &depthAttachmentRef : nullptr }}; // The attachment list contains: Color Attachments, Resolve Attachments, and Depth Attachment. @@ -159,20 +168,29 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { // Note that this needs to have the same ordering as the corollary array in getFramebuffer. VkAttachmentDescription attachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 1] = {}; + // We support 2 subpasses, which means we need to supply 1 dependency struct. + VkSubpassDependency dependencies[1] = {{ + .srcSubpass = 0, + .dstSubpass = 1, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, + }}; + VkRenderPassCreateInfo renderPassInfo { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .attachmentCount = 0u, .pAttachments = attachments, - .subpassCount = 1u, + .subpassCount = hasSubpasses ? 2u : 1u, .pSubpasses = subpasses, - .dependencyCount = 0u, - .pDependencies = nullptr, + .dependencyCount = hasSubpasses ? 1u : 0u, + .pDependencies = dependencies }; int attachmentIndex = 0; - assert_invariant(config.subpassMask == 0 && "Subpass is not supported"); - // Populate the Color Attachments. for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { if (config.colorFormat[i] == VK_FORMAT_UNDEFINED) { @@ -181,9 +199,36 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { const VkImageLayout subpassLayout = ImgUtil::getVkLayout(VulkanLayout::COLOR_ATTACHMENT); uint32_t index; - index = subpasses[0].colorAttachmentCount++; - colorAttachmentRefs[0][index].layout = subpassLayout; - colorAttachmentRefs[0][index].attachment = attachmentIndex; + if (!hasSubpasses) { + index = subpasses[0].colorAttachmentCount++; + colorAttachmentRefs[0][index].layout = subpassLayout; + colorAttachmentRefs[0][index].attachment = attachmentIndex; + } else { + + // The Driver API consolidates all color attachments from the first and second subpasses + // into a single list, and uses a bitmask to mark attachments that belong only to the + // second subpass and should be available as inputs. All color attachments in the first + // subpass are automatically made available to the second subpass. + + // If there are subpasses, we require the input attachment to be the first attachment. + // Breaking this assumption would likely require enhancements to the Driver API in order + // to supply Vulkan with all the information needed. + assert_invariant(config.subpassMask == 1); + + if (config.subpassMask & (1 << i)) { + index = subpasses[0].colorAttachmentCount++; + colorAttachmentRefs[0][index].layout = subpassLayout; + colorAttachmentRefs[0][index].attachment = attachmentIndex; + + index = subpasses[1].inputAttachmentCount++; + inputAttachmentRef[index].layout = subpassLayout; + inputAttachmentRef[index].attachment = attachmentIndex; + } + + index = subpasses[1].colorAttachmentCount++; + colorAttachmentRefs[1][index].layout = subpassLayout; + colorAttachmentRefs[1][index].attachment = attachmentIndex; + } const TargetBufferFlags flag = TargetBufferFlags(int(TargetBufferFlags::COLOR0) << i); const bool clear = any(config.clear & flag); @@ -207,6 +252,8 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { if (subpasses[0].colorAttachmentCount == 0) { subpasses[0].pColorAttachments = nullptr; subpasses[0].pResolveAttachments = nullptr; + subpasses[1].pColorAttachments = nullptr; + subpasses[1].pResolveAttachments = nullptr; } // Populate the Resolve Attachments. diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index c3ed89aa41..7712115082 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -273,7 +273,10 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) co if (!mColor[i].texture) { continue; } - count++; + // NOTE: This must be consistent with VkRenderPass construction (see VulkanFboCache). + if (!(pass.params.subpassMask & (1 << i)) || pass.currentSubpass == 1) { + count++; + } } return count; } diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp index 659de0092c..2d976f66ff 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp +++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp @@ -81,6 +81,16 @@ VulkanPipelineCache::VulkanPipelineCache(VulkanResourceAllocator* allocator) mDummyBufferWriteInfo.pImageInfo = nullptr; mDummyBufferWriteInfo.pBufferInfo = &mDummyBufferInfo; mDummyBufferWriteInfo.pTexelBufferView = nullptr; + + mDummyTargetInfo.imageLayout = VulkanImageUtility::getVkLayout(VulkanLayout::READ_ONLY); + mDummyTargetWriteInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + mDummyTargetWriteInfo.pNext = nullptr; + mDummyTargetWriteInfo.dstArrayElement = 0; + mDummyTargetWriteInfo.descriptorCount = 1; + mDummyTargetWriteInfo.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + mDummyTargetWriteInfo.pImageInfo = &mDummyTargetInfo; + mDummyTargetWriteInfo.pBufferInfo = nullptr; + mDummyTargetWriteInfo.pTexelBufferView = nullptr; } VulkanPipelineCache::~VulkanPipelineCache() { @@ -246,7 +256,9 @@ VulkanPipelineCache::DescriptorCacheEntry* VulkanPipelineCache::createDescriptor // Rewrite every binding in the new descriptor sets. VkDescriptorBufferInfo descriptorBuffers[UBUFFER_BINDING_COUNT]; VkDescriptorImageInfo descriptorSamplers[SAMPLER_BINDING_COUNT]; - VkWriteDescriptorSet descriptorWrites[UBUFFER_BINDING_COUNT + SAMPLER_BINDING_COUNT]; + VkDescriptorImageInfo descriptorInputAttachments[INPUT_ATTACHMENT_COUNT]; + VkWriteDescriptorSet descriptorWrites[UBUFFER_BINDING_COUNT + SAMPLER_BINDING_COUNT + + INPUT_ATTACHMENT_COUNT]; uint32_t nwrites = 0; VkWriteDescriptorSet* writes = descriptorWrites; nwrites = 0; @@ -296,6 +308,23 @@ VulkanPipelineCache::DescriptorCacheEntry* VulkanPipelineCache::createDescriptor writeInfo.dstBinding = binding; } } + for (uint32_t binding = 0; binding < INPUT_ATTACHMENT_COUNT; binding++) { + if (mDescriptorRequirements.inputAttachments[binding].imageView) { + VkWriteDescriptorSet& writeInfo = writes[nwrites++]; + VkDescriptorImageInfo& imageInfo = descriptorInputAttachments[binding]; + imageInfo = mDescriptorRequirements.inputAttachments[binding]; + writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeInfo.pNext = nullptr; + writeInfo.dstArrayElement = 0; + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + writeInfo.pImageInfo = &imageInfo; + writeInfo.pBufferInfo = nullptr; + writeInfo.pTexelBufferView = nullptr; + writeInfo.dstSet = descriptorCacheEntry.handles[2]; + writeInfo.dstBinding = binding; + } + } vkUpdateDescriptorSets(mDevice, nwrites, writes, 0, nullptr); @@ -373,15 +402,15 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n const bool hasFragmentShader = shaderStages[1].module != VK_NULL_HANDLE; - VkGraphicsPipelineCreateInfo pipelineCreateInfo = { - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .stageCount = hasFragmentShader ? SHADER_MODULE_COUNT : 1, - .pStages = shaderStages, - .pVertexInputState = &vertexInputState, - .pInputAssemblyState = &inputAssemblyState, - .layout = layout->handle, - .renderPass = mPipelineRequirements.renderPass, - }; + VkGraphicsPipelineCreateInfo pipelineCreateInfo = {}; + pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCreateInfo.layout = layout->handle; + pipelineCreateInfo.renderPass = mPipelineRequirements.renderPass; + pipelineCreateInfo.subpass = mPipelineRequirements.subpassIndex; + pipelineCreateInfo.stageCount = hasFragmentShader ? SHADER_MODULE_COUNT : 1; + pipelineCreateInfo.pStages = shaderStages; + pipelineCreateInfo.pVertexInputState = &vertexInputState; + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; VkPipelineRasterizationStateCreateInfo vkRaster = {}; vkRaster.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; @@ -506,6 +535,18 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP dlinfo.pBindings = sbindings; vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &cacheEntry.descriptorSetLayouts[1]); + // Next create the descriptor set layout for input attachments. + VkDescriptorSetLayoutBinding tbindings[INPUT_ATTACHMENT_COUNT]; + binding.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + for (uint32_t i = 0; i < INPUT_ATTACHMENT_COUNT; i++) { + binding.binding = i; + tbindings[i] = binding; + } + dlinfo.bindingCount = INPUT_ATTACHMENT_COUNT; + dlinfo.pBindings = tbindings; + vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &cacheEntry.descriptorSetLayouts[2]); + // Create VkPipelineLayout based on how to resources are bounded. VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {}; pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; @@ -528,8 +569,9 @@ void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexce mPipelineRequirements.rasterState = rasterState; } -void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass) noexcept { +void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept { mPipelineRequirements.renderPass = renderPass; + mPipelineRequirements.subpassIndex = subpassIndex; } void VulkanPipelineCache::bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept { @@ -577,6 +619,11 @@ void VulkanPipelineCache::unbindImageView(VkImageView imageView) noexcept { sampler = {}; } } + for (auto& target : mDescriptorRequirements.inputAttachments) { + if (target.imageView == imageView) { + target = {}; + } + } } void VulkanPipelineCache::bindUniformBufferObject(uint32_t bindingIndex, @@ -615,6 +662,14 @@ void VulkanPipelineCache::bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BI mPipelineRequirements.layout = flags; } +void VulkanPipelineCache::bindInputAttachment(uint32_t bindingIndex, + VkDescriptorImageInfo targetInfo) noexcept { + ASSERT_POSTCONDITION(bindingIndex < INPUT_ATTACHMENT_COUNT, + "Input attachment bindings overflow: index = %d, capacity = %d.", + bindingIndex, INPUT_ATTACHMENT_COUNT); + mDescriptorRequirements.inputAttachments[bindingIndex] = targetInfo; +} + void VulkanPipelineCache::terminate() noexcept { // Symmetric to createLayoutsAndDescriptors. destroyLayoutsAndDescriptors(); @@ -738,6 +793,8 @@ VkDescriptorPool VulkanPipelineCache::createDescriptorPool(uint32_t size) const poolSizes[0].descriptorCount = poolInfo.maxSets * UBUFFER_BINDING_COUNT; poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; poolSizes[1].descriptorCount = poolInfo.maxSets * SAMPLER_BINDING_COUNT; + poolSizes[2].type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + poolSizes[2].descriptorCount = poolInfo.maxSets * INPUT_ATTACHMENT_COUNT; VkDescriptorPool pool; const UTILS_UNUSED VkResult result = vkCreateDescriptorPool(mDevice, &poolInfo, VKALLOC, &pool); @@ -848,6 +905,12 @@ bool VulkanPipelineCache::DescEqual::operator()(const DescriptorKey& k1, return false; } } + for (uint32_t i = 0; i < INPUT_ATTACHMENT_COUNT; i++) { + if (k1.inputAttachments[i].imageView != k2.inputAttachments[i].imageView || + k1.inputAttachments[i].imageLayout != k2.inputAttachments[i].imageLayout) { + return false; + } + } return true; } diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.h b/filament/backend/src/vulkan/VulkanPipelineCache.h index 26f17b0751..a28327c5d9 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.h +++ b/filament/backend/src/vulkan/VulkanPipelineCache.h @@ -64,11 +64,15 @@ public: static constexpr uint32_t UBUFFER_BINDING_COUNT = Program::UNIFORM_BINDING_COUNT; static constexpr uint32_t SAMPLER_BINDING_COUNT = MAX_SAMPLER_COUNT; + // We assume only one possible input attachment between two subpasses. See also the subpasses + // definition in VulkanFboCache. + static constexpr uint32_t INPUT_ATTACHMENT_COUNT = 1; + static constexpr uint32_t SHADER_MODULE_COUNT = 2; static constexpr uint32_t VERTEX_ATTRIBUTE_COUNT = MAX_VERTEX_ATTRIBUTE_COUNT; - // Three descriptor set layouts: uniforms, and combined image samplers. - static constexpr uint32_t DESCRIPTOR_TYPE_COUNT = 2; + // Three descriptor set layouts: uniforms, combined image samplers, and input attachments. + static constexpr uint32_t DESCRIPTOR_TYPE_COUNT = 3; static constexpr uint32_t INITIAL_DESCRIPTOR_SET_POOL_SIZE = 512; // The VertexArray POD is an array of buffer targets and an array of attributes that refer to @@ -149,7 +153,7 @@ public: // Each of the following methods are fast and do not make Vulkan calls. void bindProgram(VulkanProgram* program) noexcept; void bindRasterState(const RasterState& rasterState) noexcept; - void bindRenderPass(VkRenderPass renderPass) noexcept; + void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept; void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept; void bindUniformBufferObject(uint32_t bindingIndex, VulkanBufferObject* bufferObject, VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) noexcept; @@ -157,6 +161,7 @@ public: VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) noexcept; void bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BINDING_COUNT], VulkanTexture* textures[SAMPLER_BINDING_COUNT], UsageFlags flags) noexcept; + void bindInputAttachment(uint32_t bindingIndex, VkDescriptorImageInfo imageInfo) noexcept; void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc, VkVertexInputBindingDescription const* bufferDesc, uint8_t count); @@ -253,17 +258,16 @@ private: // The pipeline key is a POD that represents all currently bound states that form the immutable // VkPipeline object. The size:offset comments below are expressed in bytes. - struct PipelineKey { // size : offset - VkShaderModule shaders[SHADER_MODULE_COUNT]; // 16 : 0 - VkRenderPass renderPass; // 8 : 16 - uint16_t topology; // 2 : 24 - uint16_t padding0; // 2 : 26 - VertexInputAttributeDescription vertexAttributes[VERTEX_ATTRIBUTE_COUNT]; // 128 : 28 - VertexInputBindingDescription vertexBuffers[VERTEX_ATTRIBUTE_COUNT]; // 128 : 156 - RasterState rasterState; // 16 : 284 - uint32_t padding1; // 4 : 300 - PipelineLayoutKey layout; // 16 : 304 - // total : 320 + struct PipelineKey { // size : offset + VkShaderModule shaders[SHADER_MODULE_COUNT]; // 16 : 0 + VkRenderPass renderPass; // 8 : 16 + uint16_t topology; // 2 : 24 + uint16_t subpassIndex; // 2 : 26 + VertexInputAttributeDescription vertexAttributes[VERTEX_ATTRIBUTE_COUNT]; // 128 : 28 + VertexInputBindingDescription vertexBuffers[VERTEX_ATTRIBUTE_COUNT]; // 128 : 156 + RasterState rasterState; // 16 : 284 + uint32_t padding; // 4 : 300 + PipelineLayoutKey layout; // 16 : 304 }; static_assert(sizeof(PipelineKey) == 320, "PipelineKey must not have implicit padding."); @@ -304,15 +308,15 @@ private: struct DescriptorKey { VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 80 0 DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 1488 80 - uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 40 1568 - uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 40 1608 - //total 1648 + DescriptorImageInfo inputAttachments[INPUT_ATTACHMENT_COUNT]; // 24 1568 + uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 40 1592 + uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 40 1632 }; - static_assert(offsetof(DescriptorKey, samplers) == 80); - static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1568); - static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1608); - static_assert(sizeof(DescriptorKey) == 1648, "DescriptorKey must not have implicit padding."); + static_assert(offsetof(DescriptorKey, inputAttachments) == 1568); + static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1592); + static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1632); + static_assert(sizeof(DescriptorKey) == 1672, "DescriptorKey must not have implicit padding."); using DescHashFn = utils::hash::MurmurHashFn; @@ -435,6 +439,7 @@ private: VkDescriptorBufferInfo mDummyBufferInfo = {}; VkWriteDescriptorSet mDummyBufferWriteInfo = {}; VkDescriptorImageInfo mDummyTargetInfo = {}; + VkWriteDescriptorSet mDummyTargetWriteInfo = {}; VkBuffer mDummyBuffer; VmaAllocation mDummyMemory;