chore(renderer): split ImageDrawInstance (#13746) 636e181568 As a step towards getting image paint working for atomic mode, new elements need to be added for image rects specifically, but we don't want to add them for image meshes as well, so this splits the struct into two structs. Co-authored-by: Josh Jersild <joshua@rive.app>
diff --git a/.rive_head b/.rive_head index 17d18e7..9e5f012 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -0613f5b2624c3aa03e8158a9daf4996864305240 +636e181568efe063143289c67fda6cab49143f0c
diff --git a/renderer/include/rive/renderer/d3d12/d3d12_pipeline_manager.hpp b/renderer/include/rive/renderer/d3d12/d3d12_pipeline_manager.hpp index 37da315..a49d6ce 100644 --- a/renderer/include/rive/renderer/d3d12/d3d12_pipeline_manager.hpp +++ b/renderer/include/rive/renderer/d3d12/d3d12_pipeline_manager.hpp
@@ -39,8 +39,7 @@ // states struct D3D12DrawVertexShader { - StackVector<D3D12_INPUT_ELEMENT_DESC, - gpu::ImageDrawInstance::LastAttribIdx + 1> + StackVector<D3D12_INPUT_ELEMENT_DESC, gpu::MaxVertexAttributeCount> m_layoutDesc; ComPtr<ID3DBlob> m_shader; };
diff --git a/renderer/include/rive/renderer/d3d12/render_context_d3d12_impl.hpp b/renderer/include/rive/renderer/d3d12/render_context_d3d12_impl.hpp index c5e4cc1..fc89576 100644 --- a/renderer/include/rive/renderer/d3d12/render_context_d3d12_impl.hpp +++ b/renderer/include/rive/renderer/d3d12/render_context_d3d12_impl.hpp
@@ -172,7 +172,8 @@ IMPLEMENT_RIVE_BUFFER(GradSpanBuffer, m_gradSpanBuffer) IMPLEMENT_RIVE_BUFFER(TessVertexSpanBuffer, m_tessSpanBuffer) IMPLEMENT_RIVE_BUFFER(TriangleVertexBuffer, m_triangleBuffer) - IMPLEMENT_RIVE_BUFFER(ImageDrawInstanceBuffer, m_imageDrawInstanceBuffer) + IMPLEMENT_RIVE_BUFFER(ImageRectInstanceBuffer, m_imageRectInstanceBuffer) + IMPLEMENT_RIVE_BUFFER(ImageMeshInstanceBuffer, m_imageMeshInstanceBuffer) #undef IMPLEMENT_RIVE_BUFFER #undef IMPLEMENT_RIVE_STRUCTURED_BUFFER @@ -246,7 +247,8 @@ D3D12VolatileBufferPool m_gradSpanBufferPool; D3D12VolatileBufferPool m_tessSpanBufferPool; D3D12VolatileBufferPool m_triangleBufferPool; - D3D12VolatileBufferPool m_imageDrawInstanceBufferPool; + D3D12VolatileBufferPool m_imageRectInstanceBufferPool; + D3D12VolatileBufferPool m_imageMeshInstanceBufferPool; // this have to be re created every frame, rtv heaps do not D3D12DescriptorHeapPool m_srvUavCbvHeapPool; @@ -262,7 +264,8 @@ rcp<D3D12VolatileBuffer> m_gradSpanBuffer; rcp<D3D12VolatileBuffer> m_tessSpanBuffer; rcp<D3D12VolatileBuffer> m_triangleBuffer; - rcp<D3D12VolatileBuffer> m_imageDrawInstanceBuffer; + rcp<D3D12VolatileBuffer> m_imageRectInstanceBuffer; + rcp<D3D12VolatileBuffer> m_imageMeshInstanceBuffer; rcp<D3D12DescriptorHeap> m_srvUavCbvHeap; // mirrors m_srvUavCbvHeap, needed for clearing resourcess via ClearUAV
diff --git a/renderer/include/rive/renderer/gpu.hpp b/renderer/include/rive/renderer/gpu.hpp index e34ec01..e3f4a89 100644 --- a/renderer/include/rive/renderer/gpu.hpp +++ b/renderer/include/rive/renderer/gpu.hpp
@@ -15,6 +15,7 @@ #include "rive/renderer/trivial_block_allocator.hpp" #include "rive/shapes/paint/image_sampler.hpp" +#include <algorithm> #include <functional> #include <optional> @@ -1743,23 +1744,94 @@ }; static_assert(sizeof(TriangleVertex) == sizeof(float) * 3); +enum class BoundVertexInstanceType +{ + none, + imageRect, + imageMesh, +}; + +// TODO: This was intentionally made identical to the VertexFormat enum in Ore, +// and they could/should be merged. +enum class VertexElementFormat : uint8_t +{ + float1, + float2, + float3, + float4, + uint8x4, + sint8x4, + unorm8x4, + snorm8x4, + uint16x2, + sint16x2, + unorm16x2, + snorm16x2, + uint16x4, + sint16x4, + float16x2, + float16x4, + uint32, +}; + +// NOTE: This would ideally contain the semantic names that D3D wants as well +// (like "GLSL_a_imageDrawViewMatrix"), but we don't want to include that +// generated header here, and having the attributes be constexpr is really +// convenient (and is taken advantage of in the Vulkan renderer), so for now, +// the D3Ds have to handle the semantic themselves. +struct VertexAttribute +{ + VertexElementFormat format; + uint32_t attributeIndex; + uint32_t byteOffset; + const char* semanticName; +}; + // Per-draw instanced attributes used by imageMeshes and imageRects. -struct ImageDrawInstance +class ImageDrawInstanceBase { public: - // This data is bound to image shaders as 4 tightly-packed instanced - // attributes. The vertex shader unpacks them. - // attr 2 (float4): viewMatrix (2x2) - // attr 3 (float4): clipRectInverseMatrix (2x2) - // attr 4 (float4): translates for view & clipRectInverseMatrix - // attr 5 (uint4) : opacity (uintBitsToFloat), clipID, blendMode, zIndex - constexpr static size_t FirstAttribIdx = 2; - constexpr static size_t LastAttribIdx = 5; - constexpr static size_t AttribCount = LastAttribIdx + 1 - FirstAttribIdx; + static constexpr size_t FirstAttribIdx = 2; + static constexpr size_t AttributeCount = 7; + static constexpr size_t LastAttribIdx = FirstAttribIdx + AttributeCount - 1; - ImageDrawInstance() = default; + static const std::array<VertexAttribute, AttributeCount>& getAttributes(); - ImageDrawInstance(const Mat2D&, + ImageDrawInstanceBase() = default; + + ImageDrawInstanceBase(const Mat2D&, + float opacity, + const ClipRectInverseMatrix*, + uint32_t clipID, + BlendMode, + uint32_t zIndex); + +private: + WRITEONLY float m_viewMatrix[4]; + WRITEONLY float m_clipRectInverseMatrix[4]; + + WRITEONLY float m_translate[2]; + WRITEONLY float m_clipRectInverseTranslate[2]; + + WRITEONLY float m_opacity; + WRITEONLY uint32_t m_clipID; + WRITEONLY uint32_t m_blendMode; + WRITEONLY uint32_t m_zIndex; +}; + +class ImageRectInstance +{ +public: + constexpr static size_t FirstAttribIdx = + ImageDrawInstanceBase::FirstAttribIdx; + static constexpr size_t AttributeCount = 7; + static constexpr size_t LastAttribIdx = FirstAttribIdx + AttributeCount - 1; + + static const std::array<VertexAttribute, AttributeCount>& getAttributes(); + + ImageRectInstance() = default; + + ImageRectInstance(const Mat2D&, float opacity, const ClipRectInverseMatrix*, uint32_t clipID, @@ -1767,18 +1839,45 @@ uint32_t zIndex); private: - WRITEONLY float m_viewMatrix[4]; - WRITEONLY float m_clipRectInverseMatrix[4]; - WRITEONLY float m_translate[2]; - WRITEONLY float m_clipRectInverseTranslate[2]; - WRITEONLY float m_opacity; - WRITEONLY uint32_t m_clipID; - WRITEONLY uint32_t m_blendMode; - WRITEONLY uint32_t m_zIndex; + ImageDrawInstanceBase m_commons; + + // Nothing additional yet +}; + +class ImageMeshInstance +{ +public: + constexpr static size_t FirstAttribIdx = + ImageDrawInstanceBase::FirstAttribIdx; + static constexpr size_t AttributeCount = 7; + static constexpr size_t LastAttribIdx = FirstAttribIdx + AttributeCount - 1; + + static const std::array<VertexAttribute, AttributeCount>& getAttributes(); + + ImageMeshInstance() = default; + + ImageMeshInstance(const Mat2D&, + float opacity, + const ClipRectInverseMatrix*, + uint32_t clipID, + BlendMode, + uint32_t zIndex); + +private: + ImageDrawInstanceBase m_commons; + + // Nothing additional yet }; #undef WRITEONLY +constexpr size_t MaxVertexAttributeCount = + std::max(ImageMeshInstance::LastAttribIdx, + ImageRectInstance::LastAttribIdx) + + 1; +constexpr size_t MaxImageDrawInstanceAttributeCount = + MaxVertexAttributeCount - ImageDrawInstanceBase::FirstAttribIdx; + // The maximum number of storage buffers we will ever use in a vertex or // fragment shader. constexpr static size_t kMaxStorageBuffers = 4;
diff --git a/renderer/include/rive/renderer/render_context.hpp b/renderer/include/rive/renderer/render_context.hpp index 89d77cd..f739f24 100644 --- a/renderer/include/rive/renderer/render_context.hpp +++ b/renderer/include/rive/renderer/render_context.hpp
@@ -354,7 +354,7 @@ // LogicalFlush::LayoutCounters. struct ResourceAllocationCounts { - constexpr static int NUM_ELEMENTS = 19; + constexpr static int NUM_ELEMENTS = 20; using VecType = simd::gvec<size_t, NUM_ELEMENTS>; RIVE_ALWAYS_INLINE VecType toVec() const @@ -384,7 +384,8 @@ size_t gradSpanBufferCount = 0; size_t tessSpanBufferCount = 0; size_t triangleVertexBufferCount = 0; - size_t imageDrawInstanceBufferCount = 0; + size_t imageRectInstanceBufferCount = 0; + size_t imageMeshInstanceBufferCount = 0; size_t gradTextureHeight = 0; size_t tessTextureHeight = 0; size_t featherAtlasTextureWidth = 0; @@ -473,7 +474,8 @@ WriteOnlyMappedMemory<gpu::GradientSpan> m_gradSpanData; WriteOnlyMappedMemory<gpu::TessVertexSpan> m_tessSpanData; WriteOnlyMappedMemory<gpu::TriangleVertex> m_triangleVertexData; - WriteOnlyMappedMemory<gpu::ImageDrawInstance> m_imageDrawInstanceData; + WriteOnlyMappedMemory<gpu::ImageRectInstance> m_imageRectInstanceData; + WriteOnlyMappedMemory<gpu::ImageMeshInstance> m_imageMeshInstanceData; // Simple allocator for trivially-destructible data that needs to persist // until the current frame has completed. All memory in this allocator is @@ -599,7 +601,7 @@ // allocated in the render context's various GPU buffers. struct ResourceCounters { - constexpr static int NUM_ELEMENTS = 7; + constexpr static int NUM_ELEMENTS = 8; using VecType = simd::gvec<size_t, NUM_ELEMENTS>; VecType toVec() const @@ -627,7 +629,8 @@ // lines, curves, lone joins, emulated caps, etc. size_t maxTessellatedSegmentCount = 0; size_t maxTriangleVertexCount = 0; - size_t imageDrawCount = 0; // imageRect or imageMesh. + size_t imageRectCount = 0; + size_t imageMeshCount = 0; }; // Additional counters for layout state that don't need to be tracked by
diff --git a/renderer/include/rive/renderer/render_context_helper_impl.hpp b/renderer/include/rive/renderer/render_context_helper_impl.hpp index 2fc091d..d8862ab 100644 --- a/renderer/include/rive/renderer/render_context_helper_impl.hpp +++ b/renderer/include/rive/renderer/render_context_helper_impl.hpp
@@ -26,7 +26,8 @@ void resizeGradSpanBuffer(size_t sizeInBytes) override; void resizeTessVertexSpanBuffer(size_t sizeInBytes) override; void resizeTriangleVertexBuffer(size_t sizeInBytes) override; - void resizeImageDrawInstanceBuffer(size_t sizeInBytes) override; + void resizeImageRectInstanceBuffer(size_t sizeInBytes) override; + void resizeImageMeshInstanceBuffer(size_t sizeInBytes) override; void* mapFlushUniformBuffer(size_t mapSizeInBytes) override; void* mapPathBuffer(size_t mapSizeInBytes) override; @@ -36,7 +37,8 @@ void* mapGradSpanBuffer(size_t mapSizeInBytes) override; void* mapTessVertexSpanBuffer(size_t mapSizeInBytes) override; void* mapTriangleVertexBuffer(size_t mapSizeInBytes) override; - void* mapImageDrawInstanceBuffer(size_t mapSizeInBytes) override; + void* mapImageRectInstanceBuffer(size_t mapSizeInBytes) override; + void* mapImageMeshInstanceBuffer(size_t mapSizeInBytes) override; void unmapFlushUniformBuffer(size_t mapSizeInBytes) override; void unmapPathBuffer(size_t mapSizeInBytes) override; @@ -46,7 +48,8 @@ void unmapGradSpanBuffer(size_t mapSizeInBytes) override; void unmapTessVertexSpanBuffer(size_t mapSizeInBytes) override; void unmapTriangleVertexBuffer(size_t mapSizeInBytes) override; - void unmapImageDrawInstanceBuffer(size_t mapSizeInBytes) override; + void unmapImageRectInstanceBuffer(size_t mapSizeInBytes) override; + void unmapImageMeshInstanceBuffer(size_t mapSizeInBytes) override; double secondsNow() const override { @@ -63,9 +66,13 @@ BufferRing* gradSpanBufferRing() { return m_gradSpanBuffer.get(); } BufferRing* tessSpanBufferRing() { return m_tessSpanBuffer.get(); } BufferRing* triangleBufferRing() { return m_triangleBuffer.get(); } - BufferRing* imageDrawInstanceBufferRing() + BufferRing* imageRectInstanceBufferRing() { - return m_imageDrawInstanceBuffer.get(); + return m_imageRectInstanceBuffer.get(); + } + BufferRing* imageMeshInstanceBufferRing() + { + return m_imageMeshInstanceBuffer.get(); } virtual std::unique_ptr<BufferRing> makeUniformBufferRing( @@ -85,7 +92,8 @@ std::unique_ptr<BufferRing> m_gradSpanBuffer; std::unique_ptr<BufferRing> m_tessSpanBuffer; std::unique_ptr<BufferRing> m_triangleBuffer; - std::unique_ptr<BufferRing> m_imageDrawInstanceBuffer; + std::unique_ptr<BufferRing> m_imageRectInstanceBuffer; + std::unique_ptr<BufferRing> m_imageMeshInstanceBuffer; std::chrono::steady_clock::time_point m_localEpoch = std::chrono::steady_clock::now(); };
diff --git a/renderer/include/rive/renderer/render_context_impl.hpp b/renderer/include/rive/renderer/render_context_impl.hpp index 56a5be2..932ddae 100644 --- a/renderer/include/rive/renderer/render_context_impl.hpp +++ b/renderer/include/rive/renderer/render_context_impl.hpp
@@ -117,7 +117,8 @@ virtual void resizeGradSpanBuffer(size_t sizeInBytes) = 0; virtual void resizeTessVertexSpanBuffer(size_t sizeInBytes) = 0; virtual void resizeTriangleVertexBuffer(size_t sizeInBytes) = 0; - virtual void resizeImageDrawInstanceBuffer(size_t sizeInBytes) = 0; + virtual void resizeImageRectInstanceBuffer(size_t sizeInBytes) = 0; + virtual void resizeImageMeshInstanceBuffer(size_t sizeInBytes) = 0; virtual void preBeginFrame(RenderContext*) {} @@ -160,7 +161,8 @@ virtual void* mapGradSpanBuffer(size_t mapSizeInBytes) = 0; virtual void* mapTessVertexSpanBuffer(size_t mapSizeInBytes) = 0; virtual void* mapTriangleVertexBuffer(size_t mapSizeInBytes) = 0; - virtual void* mapImageDrawInstanceBuffer(size_t mapSizeInBytes) = 0; + virtual void* mapImageRectInstanceBuffer(size_t mapSizeInBytes) = 0; + virtual void* mapImageMeshInstanceBuffer(size_t mapSizeInBytes) = 0; // Unmap GPU buffers. All buffers will be unmapped before flush(). virtual void unmapFlushUniformBuffer(size_t mapSizeInBytes) = 0; @@ -171,7 +173,8 @@ virtual void unmapGradSpanBuffer(size_t mapSizeInBytes) = 0; virtual void unmapTessVertexSpanBuffer(size_t mapSizeInBytes) = 0; virtual void unmapTriangleVertexBuffer(size_t mapSizeInBytes) = 0; - virtual void unmapImageDrawInstanceBuffer(size_t mapSizeInBytes) = 0; + virtual void unmapImageRectInstanceBuffer(size_t mapSizeInBytes) = 0; + virtual void unmapImageMeshInstanceBuffer(size_t mapSizeInBytes) = 0; // Allocate resources that are updated and used during flush(). virtual void resizeGradientTexture(uint32_t width, uint32_t height) = 0;
diff --git a/renderer/include/rive/renderer/vulkan/render_context_vulkan_impl.hpp b/renderer/include/rive/renderer/vulkan/render_context_vulkan_impl.hpp index 14560e1..d42e8c7 100644 --- a/renderer/include/rive/renderer/vulkan/render_context_vulkan_impl.hpp +++ b/renderer/include/rive/renderer/vulkan/render_context_vulkan_impl.hpp
@@ -179,7 +179,8 @@ IMPLEMENT_PLS_BUFFER(GradSpanBuffer, m_gradSpanBuffer) IMPLEMENT_PLS_BUFFER(TessVertexSpanBuffer, m_tessSpanBuffer) IMPLEMENT_PLS_BUFFER(TriangleVertexBuffer, m_triangleBuffer) - IMPLEMENT_PLS_BUFFER(ImageDrawInstanceBuffer, m_imageDrawInstanceBuffer) + IMPLEMENT_PLS_BUFFER(ImageRectInstanceBuffer, m_imageRectInstanceBuffer) + IMPLEMENT_PLS_BUFFER(ImageMeshInstanceBuffer, m_imageMeshInstanceBuffer) #undef IMPLEMENT_PLS_BUFFER #undef IMPLEMENT_PLS_STRUCTURED_BUFFER @@ -402,7 +403,8 @@ vkutil::BufferPool m_gradSpanBufferPool; vkutil::BufferPool m_tessSpanBufferPool; vkutil::BufferPool m_triangleBufferPool; - vkutil::BufferPool m_imageDrawInstanceBufferPool; + vkutil::BufferPool m_imageRectInstanceBufferPool; + vkutil::BufferPool m_imageMeshInstanceBufferPool; // Specific Rive buffers that have been acquired for the current frame. // When the frame ends, these get recycled back in their respective pools. @@ -414,7 +416,8 @@ rcp<vkutil::Buffer> m_gradSpanBuffer; rcp<vkutil::Buffer> m_tessSpanBuffer; rcp<vkutil::Buffer> m_triangleBuffer; - rcp<vkutil::Buffer> m_imageDrawInstanceBuffer; + rcp<vkutil::Buffer> m_imageRectInstanceBuffer; + rcp<vkutil::Buffer> m_imageMeshInstanceBuffer; std::chrono::steady_clock::time_point m_localEpoch = std::chrono::steady_clock::now();
diff --git a/renderer/src/d3d11/render_context_d3d_impl.cpp b/renderer/src/d3d11/render_context_d3d_impl.cpp index e9cafde..d8ac94a 100644 --- a/renderer/src/d3d11/render_context_d3d_impl.cpp +++ b/renderer/src/d3d11/render_context_d3d_impl.cpp
@@ -247,10 +247,70 @@ return true; } +inline DXGI_FORMAT getDXGIFormat(VertexElementFormat format) +{ + switch (format) + { + case VertexElementFormat::float1: + return DXGI_FORMAT_R32_FLOAT; + case VertexElementFormat::float2: + return DXGI_FORMAT_R32G32_FLOAT; + case VertexElementFormat::float3: + return DXGI_FORMAT_R32G32B32_FLOAT; + case VertexElementFormat::float4: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case VertexElementFormat::uint8x4: + return DXGI_FORMAT_R8G8B8A8_UINT; + case VertexElementFormat::sint8x4: + return DXGI_FORMAT_R8G8B8A8_SINT; + case VertexElementFormat::unorm8x4: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case VertexElementFormat::snorm8x4: + return DXGI_FORMAT_R8G8B8A8_SNORM; + case VertexElementFormat::uint16x2: + return DXGI_FORMAT_R16G16_UINT; + case VertexElementFormat::sint16x2: + return DXGI_FORMAT_R16G16_SINT; + case VertexElementFormat::unorm16x2: + return DXGI_FORMAT_R16G16_UNORM; + case VertexElementFormat::snorm16x2: + return DXGI_FORMAT_R16G16_SNORM; + case VertexElementFormat::uint16x4: + return DXGI_FORMAT_R16G16B16A16_UINT; + case VertexElementFormat::sint16x4: + return DXGI_FORMAT_R16G16B16A16_SINT; + case VertexElementFormat::float16x2: + return DXGI_FORMAT_R16G16_FLOAT; + case VertexElementFormat::float16x4: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + case VertexElementFormat::uint32: + return DXGI_FORMAT_R32_UINT; + } + + RIVE_UNREACHABLE(); +} + +template <typename ImageDrawInstance, uint32_t N> +void addInstanceElements(StackVector<D3D11_INPUT_ELEMENT_DESC, N>& layoutDesc) +{ + for (auto& attribute : ImageDrawInstance::getAttributes()) + { + layoutDesc.push_back({ + .SemanticName = attribute.semanticName, + .SemanticIndex = 0, + .Format = getDXGIFormat(attribute.format), + .InputSlot = IMAGE_DRAW_INSTANCE_DATA_SLOT, + .AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT, + .InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA, + .InstanceDataStepRate = 1, + }); + } +} std::unique_ptr<D3D11DrawVertexShader> D3D11PipelineManager:: compileVertexShaderBlobToFinalType(DrawType drawType, ComPtr<ID3DBlob> blob) { - StackVector<D3D11_INPUT_ELEMENT_DESC, IMAGE_LAST_ATTRIB_IDX + 1> layoutDesc; + StackVector<D3D11_INPUT_ELEMENT_DESC, MaxVertexAttributeCount + 1> + layoutDesc; switch (drawType) { case DrawType::midpointFanPatches: @@ -289,6 +349,7 @@ 0, D3D11_INPUT_PER_VERTEX_DATA, 0}); + addInstanceElements<ImageRectInstance>(layoutDesc); break; case DrawType::imageMesh: layoutDesc.push_back({GLSL_a_position, @@ -305,6 +366,7 @@ D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}); + addInstanceElements<ImageMeshInstance>(layoutDesc); break; case DrawType::renderPassResolve: break; @@ -326,40 +388,6 @@ RIVE_UNREACHABLE(); } - // Image draws receive their data as instanced vertex attributes, all from - // IMAGE_DRAW_INSTANCE_DATA_SLOT. See gpu::ImageDrawInstance. - if (gpu::DrawTypeIsImageDraw(drawType)) - { - layoutDesc.push_back({GLSL_a_imageDrawViewMatrix, - 0, - DXGI_FORMAT_R32G32B32A32_FLOAT, - IMAGE_DRAW_INSTANCE_DATA_SLOT, - D3D11_APPEND_ALIGNED_ELEMENT, - D3D11_INPUT_PER_INSTANCE_DATA, - 1}); - layoutDesc.push_back({GLSL_a_imageDrawClipRectInverseMatrix, - 0, - DXGI_FORMAT_R32G32B32A32_FLOAT, - IMAGE_DRAW_INSTANCE_DATA_SLOT, - D3D11_APPEND_ALIGNED_ELEMENT, - D3D11_INPUT_PER_INSTANCE_DATA, - 1}); - layoutDesc.push_back({GLSL_a_imageDrawTranslates, - 0, - DXGI_FORMAT_R32G32B32A32_FLOAT, - IMAGE_DRAW_INSTANCE_DATA_SLOT, - D3D11_APPEND_ALIGNED_ELEMENT, - D3D11_INPUT_PER_INSTANCE_DATA, - 1}); - layoutDesc.push_back({GLSL_a_imageDrawPacked, - 0, - DXGI_FORMAT_R32G32B32A32_UINT, - IMAGE_DRAW_INSTANCE_DATA_SLOT, - D3D11_APPEND_ALIGNED_ELEMENT, - D3D11_INPUT_PER_INSTANCE_DATA, - 1}); - } - auto result = std::make_unique<D3D11DrawVertexShader>(); VERIFY_OK(device()->CreateInputLayout(layoutDesc.data(), @@ -2059,22 +2087,6 @@ 1, m_featherAtlasTextureSRV.GetAddressOf()); - if (imageDrawInstanceBufferRing() != nullptr) - { - // Bind the image-draw-attribute records as a per-instance vertex buffer - // once; each image draw selects its record via the draw call's base - // instance (batch.baseElement). - ID3D11Buffer* imageDrawInstanceBuffer = - flush_buffer(m_gpuContext.Get(), imageDrawInstanceBufferRing()); - UINT instanceStride = sizeof(gpu::ImageDrawInstance); - UINT instanceOffset = 0; - m_gpuContext->IASetVertexBuffers(IMAGE_DRAW_INSTANCE_DATA_SLOT, - 1, - &imageDrawInstanceBuffer, - &instanceStride, - &instanceOffset); - } - bool renderPassHasCoalescedResolveAndTransfer = desc.interlockMode == gpu::InterlockMode::atomics && !desc.fixedFunctionColorOutput && @@ -2087,10 +2099,51 @@ // rectangle we get doesn't match it. auto currentScissorRect = AABBu16{0xffff, 0xffff, 0, 0}; + auto boundVertexInstanceType = BoundVertexInstanceType::none; + for (const DrawBatch& batch : *desc.drawList) { DrawType drawType = batch.drawType; + if (drawType == DrawType::imageRect && + boundVertexInstanceType != BoundVertexInstanceType::imageRect) + { + assert(imageRectInstanceBufferRing() != nullptr); + + // Bind the image-rect-attribute records as a per-instance vertex + // buffer once; each image rect selects its record via the draw + // call's base instance (batch.baseElement). + ID3D11Buffer* imageRectInstanceBuffer = + flush_buffer(m_gpuContext.Get(), imageRectInstanceBufferRing()); + UINT instanceStride = sizeof(gpu::ImageRectInstance); + UINT instanceOffset = 0; + m_gpuContext->IASetVertexBuffers(IMAGE_DRAW_INSTANCE_DATA_SLOT, + 1, + &imageRectInstanceBuffer, + &instanceStride, + &instanceOffset); + boundVertexInstanceType = BoundVertexInstanceType::imageRect; + } + else if (drawType == DrawType::imageMesh && + boundVertexInstanceType != BoundVertexInstanceType::imageMesh) + { + assert(imageMeshInstanceBufferRing() != nullptr); + + // Bind the image-mesh-attribute records as a per-instance vertex + // buffer once; each image mesh selects its record via the draw + // call's base instance (batch.baseElement). + ID3D11Buffer* imageMeshInstanceBuffer = + flush_buffer(m_gpuContext.Get(), imageMeshInstanceBufferRing()); + UINT instanceStride = sizeof(gpu::ImageMeshInstance); + UINT instanceOffset = 0; + m_gpuContext->IASetVertexBuffers(IMAGE_DRAW_INSTANCE_DATA_SLOT, + 1, + &imageMeshInstanceBuffer, + &instanceStride, + &instanceOffset); + boundVertexInstanceType = BoundVertexInstanceType::imageMesh; + } + auto shaderFeatures = desc.interlockMode == gpu::InterlockMode::atomics ? desc.combinedShaderFeatures : batch.shaderFeatures;
diff --git a/renderer/src/d3d12/d3d12_pipeline_manager.cpp b/renderer/src/d3d12/d3d12_pipeline_manager.cpp index 9a80dcf..b734ef0 100644 --- a/renderer/src/d3d12/d3d12_pipeline_manager.cpp +++ b/renderer/src/d3d12/d3d12_pipeline_manager.cpp
@@ -60,6 +60,65 @@ IID_PPV_ARGS(&m_rootSignature))); } +inline DXGI_FORMAT getDXGIFormat(VertexElementFormat format) +{ + switch (format) + { + case VertexElementFormat::float1: + return DXGI_FORMAT_R32_FLOAT; + case VertexElementFormat::float2: + return DXGI_FORMAT_R32G32_FLOAT; + case VertexElementFormat::float3: + return DXGI_FORMAT_R32G32B32_FLOAT; + case VertexElementFormat::float4: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case VertexElementFormat::uint8x4: + return DXGI_FORMAT_R8G8B8A8_UINT; + case VertexElementFormat::sint8x4: + return DXGI_FORMAT_R8G8B8A8_SINT; + case VertexElementFormat::unorm8x4: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case VertexElementFormat::snorm8x4: + return DXGI_FORMAT_R8G8B8A8_SNORM; + case VertexElementFormat::uint16x2: + return DXGI_FORMAT_R16G16_UINT; + case VertexElementFormat::sint16x2: + return DXGI_FORMAT_R16G16_SINT; + case VertexElementFormat::unorm16x2: + return DXGI_FORMAT_R16G16_UNORM; + case VertexElementFormat::snorm16x2: + return DXGI_FORMAT_R16G16_SNORM; + case VertexElementFormat::uint16x4: + return DXGI_FORMAT_R16G16B16A16_UINT; + case VertexElementFormat::sint16x4: + return DXGI_FORMAT_R16G16B16A16_SINT; + case VertexElementFormat::float16x2: + return DXGI_FORMAT_R16G16_FLOAT; + case VertexElementFormat::float16x4: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + case VertexElementFormat::uint32: + return DXGI_FORMAT_R32_UINT; + } + RIVE_UNREACHABLE(); +} + +template <typename ImageDrawInstance, uint32_t N> +void addInstanceElements(StackVector<D3D12_INPUT_ELEMENT_DESC, N>& layoutDesc) +{ + for (auto& attribute : ImageDrawInstance::getAttributes()) + { + layoutDesc.push_back({ + .SemanticName = attribute.semanticName, + .SemanticIndex = 0, + .Format = getDXGIFormat(attribute.format), + .InputSlot = IMAGE_DRAW_INSTANCE_DATA_SLOT, + .AlignedByteOffset = D3D12_APPEND_ALIGNED_ELEMENT, + .InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, + .InstanceDataStepRate = 1, + }); + } +} + std::unique_ptr<D3D12DrawVertexShader> D3D12PipelineManager:: compileVertexShaderBlobToFinalType(DrawType drawType, ComPtr<ID3DBlob> blob) { @@ -110,6 +169,7 @@ D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0, }); + addInstanceElements<ImageRectInstance>(result->m_layoutDesc); break; case DrawType::imageMesh: result->m_layoutDesc.push_back({ @@ -130,6 +190,7 @@ D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0, }); + addInstanceElements<ImageMeshInstance>(result->m_layoutDesc); break; case DrawType::renderPassResolve: break; @@ -151,44 +212,6 @@ RIVE_UNREACHABLE(); } - // Image draws receive their data as instanced vertex attributes, all from - // IMAGE_DRAW_INSTANCE_DATA_SLOT. See gpu::ImageDrawInstance. - if (gpu::DrawTypeIsImageDraw(drawType)) - { - result->m_layoutDesc.push_back( - {GLSL_a_imageDrawViewMatrix, - 0, - DXGI_FORMAT_R32G32B32A32_FLOAT, - IMAGE_DRAW_INSTANCE_DATA_SLOT, - D3D12_APPEND_ALIGNED_ELEMENT, - D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, - 1}); - result->m_layoutDesc.push_back( - {GLSL_a_imageDrawClipRectInverseMatrix, - 0, - DXGI_FORMAT_R32G32B32A32_FLOAT, - IMAGE_DRAW_INSTANCE_DATA_SLOT, - D3D12_APPEND_ALIGNED_ELEMENT, - D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, - 1}); - result->m_layoutDesc.push_back( - {GLSL_a_imageDrawTranslates, - 0, - DXGI_FORMAT_R32G32B32A32_FLOAT, - IMAGE_DRAW_INSTANCE_DATA_SLOT, - D3D12_APPEND_ALIGNED_ELEMENT, - D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, - 1}); - result->m_layoutDesc.push_back( - {GLSL_a_imageDrawPacked, - 0, - DXGI_FORMAT_R32G32B32A32_UINT, - IMAGE_DRAW_INSTANCE_DATA_SLOT, - D3D12_APPEND_ALIGNED_ELEMENT, - D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, - 1}); - } - result->m_shader = blob; return result; } @@ -307,7 +330,6 @@ void D3D12PipelineManager::compileTesselationPipeline() { - D3D12_INPUT_ELEMENT_DESC layoutDesc[] = { {GLSL_a_p0p1_, 0, @@ -384,7 +406,6 @@ void D3D12PipelineManager::compileGradientPipeline() { - D3D12_INPUT_ELEMENT_DESC layoutDesc = { GLSL_a_span, 0, @@ -440,7 +461,6 @@ void D3D12PipelineManager::compileFeatherAtlasPipeline() { - D3D12_INPUT_ELEMENT_DESC layoutDesc[2]; layoutDesc[0] = {GLSL_a_patchVertexData, 0,
diff --git a/renderer/src/d3d12/render_context_d3d12_impl.cpp b/renderer/src/d3d12/render_context_d3d12_impl.cpp index 900a8e3..e221efb 100644 --- a/renderer/src/d3d12/render_context_d3d12_impl.cpp +++ b/renderer/src/d3d12/render_context_d3d12_impl.cpp
@@ -639,7 +639,8 @@ m_gradSpanBufferPool(m_resourceManager), m_tessSpanBufferPool(m_resourceManager), m_triangleBufferPool(m_resourceManager), - m_imageDrawInstanceBufferPool(m_resourceManager, sizeof(ImageDrawInstance)), + m_imageRectInstanceBufferPool(m_resourceManager, sizeof(ImageRectInstance)), + m_imageMeshInstanceBufferPool(m_resourceManager, sizeof(ImageMeshInstance)), // this is 64kb which is the smallest heap that can be sent to gpu m_srvUavCbvHeapPool(m_resourceManager, MAX_DESCRIPTOR_HEAPS_PER_FLUSH, @@ -1014,7 +1015,8 @@ assert(m_gradSpanBuffer == nullptr); assert(m_tessSpanBuffer == nullptr); assert(m_triangleBuffer == nullptr); - assert(m_imageDrawInstanceBuffer == nullptr); + assert(m_imageRectInstanceBuffer == nullptr); + assert(m_imageMeshInstanceBuffer == nullptr); assert(m_srvUavCbvHeap == nullptr); assert(m_cpuSrvUavCbvHeap == nullptr); @@ -1040,7 +1042,8 @@ m_gradSpanBuffer = m_gradSpanBufferPool.acquire(); m_tessSpanBuffer = m_tessSpanBufferPool.acquire(); m_triangleBuffer = m_triangleBufferPool.acquire(); - m_imageDrawInstanceBuffer = m_imageDrawInstanceBufferPool.acquire(); + m_imageRectInstanceBuffer = m_imageRectInstanceBufferPool.acquire(); + m_imageMeshInstanceBuffer = m_imageMeshInstanceBufferPool.acquire(); m_srvUavCbvHeap = m_srvUavCbvHeapPool.acquire(); m_cpuSrvUavCbvHeap = m_cpuSrvUavCbvHeapPool.acquire(); @@ -1054,7 +1057,8 @@ VNAME_D3D12_OBJECT(m_gradSpanBuffer); VNAME_D3D12_OBJECT(m_tessSpanBuffer); VNAME_D3D12_OBJECT(m_triangleBuffer); - VNAME_D3D12_OBJECT(m_imageDrawInstanceBuffer); + VNAME_D3D12_OBJECT(m_imageRectInstanceBuffer); + VNAME_D3D12_OBJECT(m_imageMeshInstanceBuffer); m_isFirstFlushOfFrame = true; @@ -1150,7 +1154,8 @@ m_triangleBuffer->sync(copyCmdList, 0); } - m_imageDrawInstanceBuffer->sync(copyCmdList, 0); + m_imageRectInstanceBuffer->sync(copyCmdList, 0); + m_imageMeshInstanceBuffer->sync(copyCmdList, 0); // mark all UAVS, thse only need to happen the fist time since they // won't change per logical flush @@ -1280,18 +1285,6 @@ m_flushUniformBuffer->resource()->getGPUVirtualAddress() + desc.flushUniformDataOffsetInBytes); - // Bind the image-draw-attribute records as a per-instance vertex buffer - // once; each image draw selects its record via the draw call's base - // instance (batch.baseElement). - // NOTE: Even if there are no image draws, we have still allocated a - // single-element instance buffer. - D3D12_VERTEX_BUFFER_VIEW imageDrawInstanceVBV = - m_imageDrawInstanceBuffer->resource()->vertexBufferView( - sizeof(gpu::ImageDrawInstance)); - cmdList->IASetVertexBuffers(IMAGE_DRAW_INSTANCE_DATA_SLOT, - 1, - &imageDrawInstanceVBV); - if (desc.pathCount > 0) { m_cpuSrvUavCbvHeap->markSrvToIndex( @@ -1747,6 +1740,8 @@ auto currentScissorRect = AABBu16{0xffff, 0xffff, 0, 0}; RIVE_PROF_GPUNAME_L(1, "DrawList"); + + auto boundVertexInstanceType = BoundVertexInstanceType::none; for (const DrawBatch& batch : *desc.drawList) { assert(batch.elementCount != 0); @@ -1764,6 +1759,35 @@ gpu::ShaderMiscFlags::coalescedResolveAndTransfer; } + if (drawType == gpu::DrawType::imageRect && + boundVertexInstanceType != BoundVertexInstanceType::imageRect) + { + // Bind the image-draw-attribute records as a per-instance vertex + // buffer once; each image draw selects its record via the draw + // call's base instance (batch.baseElement). + D3D12_VERTEX_BUFFER_VIEW imageRectInstanceVBV = + m_imageRectInstanceBuffer->resource()->vertexBufferView( + sizeof(gpu::ImageRectInstance)); + cmdList->IASetVertexBuffers(IMAGE_DRAW_INSTANCE_DATA_SLOT, + 1, + &imageRectInstanceVBV); + boundVertexInstanceType = BoundVertexInstanceType::imageRect; + } + else if (drawType == gpu::DrawType::imageMesh && + boundVertexInstanceType != BoundVertexInstanceType::imageMesh) + { + // Bind the image-draw-attribute records as a per-instance vertex + // buffer once; each image draw selects its record via the draw + // call's base instance (batch.baseElement). + D3D12_VERTEX_BUFFER_VIEW imageMeshInstanceVBV = + m_imageMeshInstanceBuffer->resource()->vertexBufferView( + sizeof(gpu::ImageMeshInstance)); + cmdList->IASetVertexBuffers(IMAGE_DRAW_INSTANCE_DATA_SLOT, + 1, + &imageMeshInstanceVBV); + boundVertexInstanceType = BoundVertexInstanceType::imageMesh; + } + auto* pipeline = m_pipelineManager.tryGetPipeline( { .drawType = drawType, @@ -2100,7 +2124,8 @@ m_gradSpanBufferPool.recycle(std::move(m_gradSpanBuffer)); m_tessSpanBufferPool.recycle(std::move(m_tessSpanBuffer)); m_triangleBufferPool.recycle(std::move(m_triangleBuffer)); - m_imageDrawInstanceBufferPool.recycle(std::move(m_imageDrawInstanceBuffer)); + m_imageRectInstanceBufferPool.recycle(std::move(m_imageRectInstanceBuffer)); + m_imageMeshInstanceBufferPool.recycle(std::move(m_imageMeshInstanceBuffer)); m_srvUavCbvHeapPool.recycle(std::move(m_srvUavCbvHeap)); m_cpuSrvUavCbvHeapPool.recycle(std::move(m_cpuSrvUavCbvHeap));
diff --git a/renderer/src/draw.cpp b/renderer/src/draw.cpp index 4e5921c..257650f 100644 --- a/renderer/src/draw.cpp +++ b/renderer/src/draw.cpp
@@ -2681,7 +2681,7 @@ // If we support image paints for paths, the client should draw a // rectangular path with an image paint instead of using this draw. assert(!context->frameSupportsImagePaintForPaths()); - m_resourceCounts.imageDrawCount = 1; + m_resourceCounts.imageRectCount = 1; } gpu::DrawBatch* ImageRectDraw::pushToRenderContext( @@ -2719,7 +2719,7 @@ assert(m_vertexBufferRef != nullptr); assert(m_uvBufferRef != nullptr); assert(m_indexBufferRef != nullptr); - m_resourceCounts.imageDrawCount = 1; + m_resourceCounts.imageMeshCount = 1; } gpu::DrawBatch* ImageMeshDraw::pushToRenderContext(
diff --git a/renderer/src/gl/render_context_gl_impl.cpp b/renderer/src/gl/render_context_gl_impl.cpp index d5a49d7..e51c760 100644 --- a/renderer/src/gl/render_context_gl_impl.cpp +++ b/renderer/src/gl/render_context_gl_impl.cpp
@@ -404,7 +404,7 @@ // We draw imageRects when in atomic mode. m_state->bindVAO(m_imageRectVAO); glEnableVertexAttribArray(0); - for (GLuint loc = IMAGE_FIRST_ATTRIB_IDX; loc <= IMAGE_LAST_ATTRIB_IDX; + for (GLuint loc = IMAGE_FIRST_ATTRIB_IDX; loc <= IMAGE_RECT_LAST_ATTRIB_IDX; ++loc) { glEnableVertexAttribArray(loc); @@ -433,7 +433,7 @@ m_state->bindVAO(m_imageMeshVAO); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); - for (GLuint loc = IMAGE_FIRST_ATTRIB_IDX; loc <= IMAGE_LAST_ATTRIB_IDX; + for (GLuint loc = IMAGE_FIRST_ATTRIB_IDX; loc <= IMAGE_MESH_LAST_ATTRIB_IDX; ++loc) { glEnableVertexAttribArray(loc); @@ -2140,38 +2140,89 @@ return static_cast<const BufferRingGLImpl*>(bufferRing)->bufferID(); } -// Setup the per-draw gpu::ImageDrawInstance buffer. GL has no portable -// baseInstance, so point the instance attributes at this draw's record via a -// byteOffset instead. -static void setImageDrawInstanceAttribs(size_t byteOffset) +enum class CallType { - glVertexAttribPointer(IMAGE_VIEW_MATRIX_ATTRIB_IDX, - 4, - GL_FLOAT, - GL_FALSE, - sizeof(gpu::ImageDrawInstance), - reinterpret_cast<const void*>(byteOffset)); - glVertexAttribPointer( - IMAGE_CLIP_RECT_INVERSE_MATRIX_ATTRIB_IDX, - 4, - GL_FLOAT, - GL_FALSE, - sizeof(gpu::ImageDrawInstance), - reinterpret_cast<const void*>(byteOffset + sizeof(uint32_t) * 4)); - glVertexAttribPointer( - IMAGE_TRANSLATES_ATTRIB_IDX, - 4, - GL_FLOAT, - GL_FALSE, - sizeof(gpu::ImageDrawInstance), - reinterpret_cast<const void*>(byteOffset + sizeof(uint32_t) * 8)); - glVertexAttribIPointer( - IMAGE_PACKED_ATTRIBS_IDX, - 4, - GL_UNSIGNED_INT, - sizeof(gpu::ImageDrawInstance), - reinterpret_cast<const void*>(byteOffset + sizeof(uint32_t) * 12)); - static_assert(sizeof(gpu::ImageDrawInstance) == sizeof(uint32_t) * 16); + UnnormalizedFloat, + NormalizedFloat, + Int, +}; +struct GLVertexElementFormat +{ + GLenum componentType; + GLint componentCount; + CallType callType; +}; +inline GLVertexElementFormat getGLVertexElementFormat( + VertexElementFormat format) +{ + switch (format) + { + case VertexElementFormat::float1: + return {GL_FLOAT, 1, CallType::UnnormalizedFloat}; + case VertexElementFormat::float2: + return {GL_FLOAT, 2, CallType::UnnormalizedFloat}; + case VertexElementFormat::float3: + return {GL_FLOAT, 3, CallType::UnnormalizedFloat}; + case VertexElementFormat::float4: + return {GL_FLOAT, 4, CallType::UnnormalizedFloat}; + case VertexElementFormat::uint8x4: + return {GL_UNSIGNED_BYTE, 4, CallType::Int}; + case VertexElementFormat::sint8x4: + return {GL_BYTE, 4, CallType::Int}; + case VertexElementFormat::unorm8x4: + return {GL_UNSIGNED_BYTE, 4, CallType::NormalizedFloat}; + case VertexElementFormat::snorm8x4: + return {GL_BYTE, 4, CallType::NormalizedFloat}; + case VertexElementFormat::uint16x2: + return {GL_UNSIGNED_SHORT, 2, CallType::Int}; + case VertexElementFormat::sint16x2: + return {GL_SHORT, 2, CallType::Int}; + case VertexElementFormat::unorm16x2: + return {GL_UNSIGNED_SHORT, 2, CallType::NormalizedFloat}; + case VertexElementFormat::snorm16x2: + return {GL_SHORT, 2, CallType::NormalizedFloat}; + case VertexElementFormat::uint16x4: + return {GL_UNSIGNED_SHORT, 4, CallType::Int}; + case VertexElementFormat::sint16x4: + return {GL_SHORT, 4, CallType::Int}; + case VertexElementFormat::float16x2: + return {GL_HALF_FLOAT, 2, CallType::UnnormalizedFloat}; + case VertexElementFormat::float16x4: + return {GL_HALF_FLOAT, 4, CallType::UnnormalizedFloat}; + case VertexElementFormat::uint32: + return {GL_UNSIGNED_INT, 1, CallType::Int}; + } + + RIVE_UNREACHABLE(); +} + +template <typename ImageDrawInstance> +inline void setInstanceAttribs(size_t baseByteOffset) +{ + for (const auto& attr : ImageDrawInstance::getAttributes()) + { + auto fmt = getGLVertexElementFormat(attr.format); + if (fmt.callType == CallType::Int) + { + glVertexAttribIPointer(attr.attributeIndex, + fmt.componentCount, + fmt.componentType, + sizeof(ImageDrawInstance), + reinterpret_cast<const void*>( + baseByteOffset + attr.byteOffset)); + } + else + { + bool isNormalized = (fmt.callType == CallType::NormalizedFloat); + glVertexAttribPointer(attr.attributeIndex, + fmt.componentCount, + fmt.componentType, + isNormalized, + sizeof(ImageDrawInstance), + reinterpret_cast<const void*>( + baseByteOffset + attr.byteOffset)); + } + } } static void bind_storage_buffer(const GLCapabilities& capabilities, @@ -3071,9 +3122,9 @@ m_state->bindVAO(m_imageRectVAO); m_state->bindBuffer( GL_ARRAY_BUFFER, - gl_buffer_id(imageDrawInstanceBufferRing())); - setImageDrawInstanceAttribs(batch.baseElement * - sizeof(gpu::ImageDrawInstance)); + gl_buffer_id(imageRectInstanceBufferRing())); + setInstanceAttribs<ImageRectInstance>( + batch.baseElement * sizeof(gpu::ImageRectInstance)); glDrawElementsInstanced(GL_TRIANGLES, batch.indexCountPerInstance, GL_UNSIGNED_SHORT, @@ -3101,9 +3152,9 @@ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr); m_state->bindBuffer( GL_ARRAY_BUFFER, - gl_buffer_id(imageDrawInstanceBufferRing())); - setImageDrawInstanceAttribs(batch.baseElement * - sizeof(gpu::ImageDrawInstance)); + gl_buffer_id(imageMeshInstanceBufferRing())); + setInstanceAttribs<ImageMeshInstance>( + batch.baseElement * sizeof(gpu::ImageMeshInstance)); m_state->bindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer->bufferID()); if (desc.interlockMode == gpu::InterlockMode::rasterOrdering)
diff --git a/renderer/src/gpu.cpp b/renderer/src/gpu.cpp index 784b499..1fe379c 100644 --- a/renderer/src/gpu.cpp +++ b/renderer/src/gpu.cpp
@@ -9,6 +9,7 @@ #include "rive/renderer/render_target.hpp" #include "rive/renderer/texture.hpp" #include "gradient.hpp" +#include "image_draw_attributes.hpp" #include "rive_render_paint.hpp" #include "generated/shaders/draw_path.vert.exports.h" @@ -1062,7 +1063,12 @@ } } -ImageDrawInstance::ImageDrawInstance( +#define STATIC_ASSERT_ATTRIB(Class, member, attribIdx) \ + static_assert( \ + offsetof(Class, member) == \ + Class##Attributes[attribIdx - IMAGE_FIRST_ATTRIB_IDX].byteOffset) + +ImageDrawInstanceBase::ImageDrawInstanceBase( const Mat2D& matrix, float opacity, const ClipRectInverseMatrix* clipRectInverseMatrix, @@ -1070,45 +1076,36 @@ BlendMode blendMode, uint32_t zIndex) { - // The backends bind the 4 attributes at byte offset i*16, relying on this - // grouping. - static_assert(ImageDrawInstance::FirstAttribIdx == IMAGE_FIRST_ATTRIB_IDX); - static_assert(ImageDrawInstance::LastAttribIdx == IMAGE_LAST_ATTRIB_IDX); - static_assert(offsetof(ImageDrawInstance, m_viewMatrix) == - (IMAGE_VIEW_MATRIX_ATTRIB_IDX - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4); - static_assert( - offsetof(ImageDrawInstance, m_clipRectInverseMatrix) == - (IMAGE_CLIP_RECT_INVERSE_MATRIX_ATTRIB_IDX - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4); - static_assert(offsetof(ImageDrawInstance, m_translate) == - (IMAGE_TRANSLATES_ATTRIB_IDX - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4); - static_assert(offsetof(ImageDrawInstance, m_opacity) == - (IMAGE_PACKED_ATTRIBS_IDX - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4); + static_assert(FirstAttribIdx == IMAGE_FIRST_ATTRIB_IDX); + static_assert(LastAttribIdx == IMAGE_COMMON_LAST_ATTRIB_IDX); - // When SPLIT_UINT4_ATTRIBUTES is set (Unreal RHI, whose shader compiler - // mishandles a uint4 vertex attribute), the packed uint4 is bound as four - // separate uint attributes at consecutive locations. - static_assert(IMAGE_SPLIT_OPACITY_ATTRIB_IDX == IMAGE_PACKED_ATTRIBS_IDX); - static_assert( - offsetof(ImageDrawInstance, m_clipID) == - ((IMAGE_PACKED_ATTRIBS_IDX - IMAGE_FIRST_ATTRIB_IDX) * 4 + - (IMAGE_SPLIT_CLIP_ID_ATTRIB_IDX - IMAGE_SPLIT_OPACITY_ATTRIB_IDX)) * - sizeof(uint32_t)); - static_assert( - offsetof(ImageDrawInstance, m_blendMode) == - ((IMAGE_PACKED_ATTRIBS_IDX - IMAGE_FIRST_ATTRIB_IDX) * 4 + - (IMAGE_SPLIT_BLEND_MODE_ATTRIB_IDX - IMAGE_SPLIT_OPACITY_ATTRIB_IDX)) * - sizeof(uint32_t)); - static_assert( - offsetof(ImageDrawInstance, m_zIndex) == - ((IMAGE_PACKED_ATTRIBS_IDX - IMAGE_FIRST_ATTRIB_IDX) * 4 + - (IMAGE_SPLIT_ZINDEX_ATTRIB_IDX - IMAGE_SPLIT_OPACITY_ATTRIB_IDX)) * - sizeof(uint32_t)); - static_assert(sizeof(ImageDrawInstance) == - IMAGE_ATTRIB_COUNT * sizeof(uint32_t) * 4); + // Check that our attributes start in the right places + STATIC_ASSERT_ATTRIB(ImageDrawInstanceBase, + m_viewMatrix, + IMAGE_VIEW_MATRIX_ATTRIB_IDX); + STATIC_ASSERT_ATTRIB(ImageDrawInstanceBase, + m_clipRectInverseMatrix, + IMAGE_CLIP_RECT_INVERSE_MATRIX_ATTRIB_IDX); + STATIC_ASSERT_ATTRIB(ImageDrawInstanceBase, + m_translate, + IMAGE_TRANSLATES_ATTRIB_IDX); + STATIC_ASSERT_ATTRIB(ImageDrawInstanceBase, + m_opacity, + IMAGE_OPACITY_ATTRIB_IDX); + STATIC_ASSERT_ATTRIB(ImageDrawInstanceBase, + m_clipID, + IMAGE_CLIP_ID_ATTRIB_IDX); + STATIC_ASSERT_ATTRIB(ImageDrawInstanceBase, + m_blendMode, + IMAGE_BLEND_MODE_ATTRIB_IDX); + STATIC_ASSERT_ATTRIB(ImageDrawInstanceBase, + m_zIndex, + IMAGE_ZINDEX_ATTRIB_IDX); + + // Now check that the packed values are at the correct offsets + static_assert(offsetof(ImageDrawInstanceBase, m_clipRectInverseTranslate) == + offsetof(ImageDrawInstanceBase, m_translate) + + 2 * sizeof(float)); const Mat2D clipRectInverseMatrixToWrite = clipRectInverseMatrix != nullptr @@ -1125,6 +1122,48 @@ m_zIndex = zIndex; } +const std::array<VertexAttribute, ImageDrawInstanceBase::AttributeCount>& +ImageDrawInstanceBase::getAttributes() +{ + return ImageDrawInstanceBaseAttributes; +} + +ImageRectInstance::ImageRectInstance( + const Mat2D& matrix, + float opacity, + const ClipRectInverseMatrix* clipRectInverseMatrix, + uint32_t clipID, + BlendMode blendMode, + uint32_t zIndex) : + m_commons{matrix, opacity, clipRectInverseMatrix, clipID, blendMode, zIndex} +{ + static_assert(offsetof(ImageRectInstance, m_commons) == 0); +} + +const std::array<VertexAttribute, ImageRectInstance::AttributeCount>& +ImageRectInstance::getAttributes() +{ + return ImageRectInstanceAttributes; +} + +ImageMeshInstance::ImageMeshInstance( + const Mat2D& matrix, + float opacity, + const ClipRectInverseMatrix* clipRectInverseMatrix, + uint32_t clipID, + BlendMode blendMode, + uint32_t zIndex) : + m_commons{matrix, opacity, clipRectInverseMatrix, clipID, blendMode, zIndex} +{ + static_assert(offsetof(ImageMeshInstance, m_commons) == 0); +} + +const std::array<VertexAttribute, ImageMeshInstance::AttributeCount>& +ImageMeshInstance::getAttributes() +{ + return ImageMeshInstanceAttributes; +} + std::tuple<uint32_t, uint32_t> StorageTextureSize( size_t bufferSizeInBytes, StorageBufferStructure bufferStructure)
diff --git a/renderer/src/image_draw_attributes.hpp b/renderer/src/image_draw_attributes.hpp new file mode 100644 index 0000000..8bb1f5e --- /dev/null +++ b/renderer/src/image_draw_attributes.hpp
@@ -0,0 +1,79 @@ +#pragma once + +#include <array> +#include "generated/shaders/tessellate.glsl.exports.h" + +namespace rive::gpu +{ +static constexpr auto ImageDrawInstanceBaseAttributes = std::array{ + VertexAttribute{ + // m_viewMatrix (2x2) + VertexElementFormat::float4, + ImageDrawInstanceBase::FirstAttribIdx + 0, + 0, + GLSL_a_imageDrawViewMatrix, + }, + VertexAttribute{ + // m_clipRectInverseMatrix (2x2) + VertexElementFormat::float4, + ImageDrawInstanceBase::FirstAttribIdx + 1, + 4 * sizeof(float), + GLSL_a_imageDrawClipRectInverseMatrix, + }, + VertexAttribute{ + // packed: m_translate (2), m_clipRectInverseTranslate(2) + VertexElementFormat::float4, + ImageDrawInstanceBase::FirstAttribIdx + 2, + 8 * sizeof(float), + GLSL_a_imageDrawTranslates, + }, + VertexAttribute{ + // m_opacity/m_modulatedColor + VertexElementFormat::float1, + ImageDrawInstanceBase::FirstAttribIdx + 3, + 12 * sizeof(float), + GLSL_a_imageDrawOpacity, + }, + VertexAttribute{ + // m_clipID + VertexElementFormat::uint32, + ImageDrawInstanceBase::FirstAttribIdx + 4, + 13 * sizeof(float), + GLSL_a_imageDrawClipID, + }, + VertexAttribute{ + // m_blendMode + VertexElementFormat::uint32, + ImageDrawInstanceBase::FirstAttribIdx + 5, + 14 * sizeof(float), + GLSL_a_imageDrawBlendMode, + }, + VertexAttribute{ + // m_zIndex + VertexElementFormat::uint32, + ImageDrawInstanceBase::FirstAttribIdx + 6, + 15 * sizeof(float), + GLSL_a_imageDrawZIndex, + }, +}; + +static_assert(std::size(ImageDrawInstanceBaseAttributes) == + ImageDrawInstanceBase::AttributeCount); +static_assert(std::size(ImageDrawInstanceBaseAttributes) == + IMAGE_COMMON_ATTRIB_COUNT); + +// No additional attributes yet. +static constexpr auto ImageRectInstanceAttributes = + ImageDrawInstanceBaseAttributes; + +static_assert(std::size(ImageRectInstanceAttributes) == + IMAGE_RECT_ATTRIB_COUNT); + +// No additional attributes yet. +static constexpr auto ImageMeshInstanceAttributes = + ImageDrawInstanceBaseAttributes; + +static_assert(std::size(ImageMeshInstanceAttributes) == + IMAGE_MESH_ATTRIB_COUNT); + +} // namespace rive::gpu \ No newline at end of file
diff --git a/renderer/src/metal/render_context_metal_impl.mm b/renderer/src/metal/render_context_metal_impl.mm index 399b5ab..ef2002a 100644 --- a/renderer/src/metal/render_context_metal_impl.mm +++ b/renderer/src/metal/render_context_metal_impl.mm
@@ -1960,52 +1960,54 @@ break; } case DrawType::imageRect: + { + [encoder setRenderPipelineState:drawPipelineState]; + [encoder + setVertexBuffer:mtl_buffer(imageRectInstanceBufferRing()) + offset:batch.baseElement * + sizeof(gpu::ImageRectInstance) + atIndex:2]; + [encoder setCullMode:MTLCullModeNone]; + assert(desc.interlockMode == gpu::InterlockMode::atomics); + [encoder setVertexBuffer:m_imageRectVertexBuffer + offset:0 + atIndex:0]; + [encoder + drawIndexedPrimitives:MTLPrimitiveTypeTriangle + indexCount:batch.indexCountPerInstance + indexType:MTLIndexTypeUInt16 + indexBuffer:m_imageRectIndexBuffer + indexBufferOffset:batch.baseIndex * sizeof(uint16_t) + instanceCount:batch.elementCount]; + break; + } case DrawType::imageMesh: { [encoder setRenderPipelineState:drawPipelineState]; [encoder - setVertexBuffer:mtl_buffer(imageDrawInstanceBufferRing()) + setVertexBuffer:mtl_buffer(imageMeshInstanceBufferRing()) offset:batch.baseElement * - sizeof(gpu::ImageDrawInstance) + sizeof(gpu::ImageMeshInstance) atIndex:2]; [encoder setCullMode:MTLCullModeNone]; - if (drawType == DrawType::imageRect) - { - assert(desc.interlockMode == gpu::InterlockMode::atomics); - [encoder setVertexBuffer:m_imageRectVertexBuffer - offset:0 - atIndex:0]; - [encoder - drawIndexedPrimitives:MTLPrimitiveTypeTriangle - indexCount:batch.indexCountPerInstance - indexType:MTLIndexTypeUInt16 - indexBuffer:m_imageRectIndexBuffer - indexBufferOffset:batch.baseIndex * sizeof(uint16_t) - instanceCount:batch.elementCount]; - } - else - { - LITE_RTTI_CAST_OR_BREAK(vertexBuffer, - RenderBufferMetalImpl*, - batch.vertexBuffer); - LITE_RTTI_CAST_OR_BREAK( - uvBuffer, RenderBufferMetalImpl*, batch.uvBuffer); - LITE_RTTI_CAST_OR_BREAK( - indexBuffer, RenderBufferMetalImpl*, batch.indexBuffer); - [encoder setVertexBuffer:vertexBuffer->submittedBuffer() - offset:0 - atIndex:0]; - [encoder setVertexBuffer:uvBuffer->submittedBuffer() - offset:0 - atIndex:1]; - [encoder - drawIndexedPrimitives:MTLPrimitiveTypeTriangle - indexCount:batch.indexCountPerInstance - indexType:MTLIndexTypeUInt16 - indexBuffer:indexBuffer->submittedBuffer() - indexBufferOffset:batch.baseIndex * - sizeof(uint16_t)]; - } + LITE_RTTI_CAST_OR_BREAK( + vertexBuffer, RenderBufferMetalImpl*, batch.vertexBuffer); + LITE_RTTI_CAST_OR_BREAK( + uvBuffer, RenderBufferMetalImpl*, batch.uvBuffer); + LITE_RTTI_CAST_OR_BREAK( + indexBuffer, RenderBufferMetalImpl*, batch.indexBuffer); + [encoder setVertexBuffer:vertexBuffer->submittedBuffer() + offset:0 + atIndex:0]; + [encoder setVertexBuffer:uvBuffer->submittedBuffer() + offset:0 + atIndex:1]; + [encoder + drawIndexedPrimitives:MTLPrimitiveTypeTriangle + indexCount:batch.indexCountPerInstance + indexType:MTLIndexTypeUInt16 + indexBuffer:indexBuffer->submittedBuffer() + indexBufferOffset:batch.baseIndex * sizeof(uint16_t)]; break; } case DrawType::renderPassInitialize:
diff --git a/renderer/src/render_context.cpp b/renderer/src/render_context.cpp index 8574db6..ef96c48 100644 --- a/renderer/src/render_context.cpp +++ b/renderer/src/render_context.cpp
@@ -809,7 +809,8 @@ totalFrameResourceCounts.maxTessellatedSegmentCount, .triangleVertexBufferCount = totalFrameResourceCounts.maxTriangleVertexCount, - .imageDrawInstanceBufferCount = totalFrameResourceCounts.imageDrawCount, + .imageRectInstanceBufferCount = totalFrameResourceCounts.imageRectCount, + .imageMeshInstanceBufferCount = totalFrameResourceCounts.imageMeshCount, .gradTextureHeight = layoutCounts.maxGradTextureHeight, .tessTextureHeight = layoutCounts.maxTessTextureHeight, .featherAtlasTextureWidth = layoutCounts.maxFeatherAtlasWidth, @@ -875,7 +876,8 @@ .gradSpanBufferCount = 5, // 125% .tessSpanBufferCount = 5, // 125% .triangleVertexBufferCount = 5, // 125% - .imageDrawInstanceBufferCount = 5, // 125% + .imageRectInstanceBufferCount = 5, // 125% + .imageMeshInstanceBufferCount = 5, // 125% .gradTextureHeight = 5, // 125% .tessTextureHeight = 5, // 125% .featherAtlasTextureWidth = 5, // 125% @@ -927,7 +929,8 @@ .gradSpanBufferCount = 2, // 66.7% .tessSpanBufferCount = 2, // 66.7% .triangleVertexBufferCount = 2, // 66.7% - .imageDrawInstanceBufferCount = 2, // 66.7% + .imageRectInstanceBufferCount = 2, // 66.7% + .imageMeshInstanceBufferCount = 2, // 66.7% .gradTextureHeight = 2, // 66.7% .tessTextureHeight = 2, // 66.7% .featherAtlasTextureWidth = 2, // 66.7% @@ -980,8 +983,10 @@ } assert(m_flushUniformData.elementsWritten() == m_logicalFlushes.size()); - assert(m_imageDrawInstanceData.elementsWritten() == - totalFrameResourceCounts.imageDrawCount); + assert(m_imageRectInstanceData.elementsWritten() == + totalFrameResourceCounts.imageRectCount); + assert(m_imageMeshInstanceData.elementsWritten() == + totalFrameResourceCounts.imageMeshCount); assert(m_pathData.elementsWritten() == totalFrameResourceCounts.pathCount + layoutCounts.pathPaddingCount); @@ -2716,15 +2721,26 @@ sizeof(gpu::TriangleVertex)); } - LOG_BUFFER_RING_SIZE(imageDrawInstanceBufferCount, - sizeof(gpu::ImageDrawInstance)); - if (allocs.imageDrawInstanceBufferCount != - m_currentResourceAllocations.imageDrawInstanceBufferCount || + LOG_BUFFER_RING_SIZE(imageRectInstanceBufferCount, + sizeof(gpu::ImageRectInstance)); + if (allocs.imageRectInstanceBufferCount != + m_currentResourceAllocations.imageRectInstanceBufferCount || forceRealloc) { - m_impl->resizeImageDrawInstanceBuffer( - allocs.imageDrawInstanceBufferCount * - sizeof(gpu::ImageDrawInstance)); + m_impl->resizeImageRectInstanceBuffer( + allocs.imageRectInstanceBufferCount * + sizeof(gpu::ImageRectInstance)); + } + + LOG_BUFFER_RING_SIZE(imageMeshInstanceBufferCount, + sizeof(gpu::ImageMeshInstance)); + if (allocs.imageMeshInstanceBufferCount != + m_currentResourceAllocations.imageMeshInstanceBufferCount || + forceRealloc) + { + m_impl->resizeImageMeshInstanceBuffer( + allocs.imageMeshInstanceBufferCount * + sizeof(gpu::ImageMeshInstance)); } assert(allocs.gradTextureHeight <= kMaxTextureHeight); @@ -2922,15 +2938,25 @@ assert( m_triangleVertexData.hasRoomFor(mapCounts.triangleVertexBufferCount)); - if (mapCounts.imageDrawInstanceBufferCount > 0) + if (mapCounts.imageRectInstanceBufferCount > 0) { - HANDLE_MAP_FAILURE(m_imageDrawInstanceData.mapElements( + HANDLE_MAP_FAILURE(m_imageRectInstanceData.mapElements( m_impl.get(), - &RenderContextImpl::mapImageDrawInstanceBuffer, - mapCounts.imageDrawInstanceBufferCount)); + &RenderContextImpl::mapImageRectInstanceBuffer, + mapCounts.imageRectInstanceBufferCount)); } - assert(m_imageDrawInstanceData.hasRoomFor( - mapCounts.imageDrawInstanceBufferCount > 0)); + assert(m_imageRectInstanceData.hasRoomFor( + mapCounts.imageRectInstanceBufferCount)); + + if (mapCounts.imageMeshInstanceBufferCount > 0) + { + HANDLE_MAP_FAILURE(m_imageMeshInstanceData.mapElements( + m_impl.get(), + &RenderContextImpl::mapImageMeshInstanceBuffer, + mapCounts.imageMeshInstanceBufferCount)); + } + assert(m_imageMeshInstanceData.hasRoomFor( + mapCounts.imageMeshInstanceBufferCount)); #undef HANDLE_MAP_FAILURE return true; @@ -2991,12 +3017,19 @@ &RenderContextImpl::unmapTriangleVertexBuffer, mapCounts.triangleVertexBufferCount); } - if (m_imageDrawInstanceData) + if (m_imageRectInstanceData) { - m_imageDrawInstanceData.unmapElements( + m_imageRectInstanceData.unmapElements( m_impl.get(), - &RenderContextImpl::unmapImageDrawInstanceBuffer, - mapCounts.imageDrawInstanceBufferCount); + &RenderContextImpl::unmapImageRectInstanceBuffer, + mapCounts.imageRectInstanceBufferCount); + } + if (m_imageMeshInstanceData) + { + m_imageMeshInstanceData.unmapElements( + m_impl.get(), + &RenderContextImpl::unmapImageMeshInstanceBuffer, + mapCounts.imageMeshInstanceBufferCount); } } @@ -3548,10 +3581,10 @@ // with an image paint instead of calling this method. assert(!m_ctx->frameSupportsImagePaintForPaths()); - const uint32_t imageDrawBaseInstance = + const uint32_t imageRectBaseInstance = math::lossless_numeric_cast<uint32_t>( - m_ctx->m_imageDrawInstanceData.elementsWritten()); - m_ctx->m_imageDrawInstanceData.emplace_back(draw->imageMatrix(), + m_ctx->m_imageRectInstanceData.elementsWritten()); + m_ctx->m_imageRectInstanceData.emplace_back(draw->imageMatrix(), draw->opacity(), draw->clipRectInverseMatrix(), draw->clipID(), @@ -3563,7 +3596,7 @@ m_baselineShaderMiscFlags, PaintType::solidColor, 1, - imageDrawBaseInstance); + imageRectBaseInstance); return batch; } @@ -3573,10 +3606,10 @@ RIVE_PROF_SCOPE_L(2) assert(m_hasDoneLayout); - const uint32_t imageDrawBaseInstance = + const uint32_t imageMeshBaseInstance = math::lossless_numeric_cast<uint32_t>( - m_ctx->m_imageDrawInstanceData.elementsWritten()); - m_ctx->m_imageDrawInstanceData.emplace_back(draw->imageMatrix(), + m_ctx->m_imageMeshInstanceData.elementsWritten()); + m_ctx->m_imageMeshInstanceData.emplace_back(draw->imageMatrix(), draw->opacity(), draw->clipRectInverseMatrix(), draw->clipID(), @@ -3588,7 +3621,7 @@ m_baselineShaderMiscFlags, PaintType::solidColor, 1, // one instance (the mesh) - imageDrawBaseInstance); + imageMeshBaseInstance); batch.indexCountPerInstance = draw->indexCount(); batch.vertexBuffer = draw->vertexBuffer(); batch.uvBuffer = draw->uvBuffer(); @@ -4031,13 +4064,6 @@ shaderFeatures |= ShaderFeatures::ENABLE_DITHER; } - if (draw->imageTexture() != nullptr && - m_ctx->frameInterlockMode() != gpu::InterlockMode::atomics && - drawType != DrawType::imageRect && drawType != DrawType::imageMesh) - { - shaderFeatures |= ShaderFeatures::ENABLE_MODULATED_IMAGE; - } - if (paintType != PaintType::clipUpdate && !enums::is_flag_set(shaderMiscFlags, gpu::ShaderMiscFlags::borrowedCoveragePass)) @@ -4069,14 +4095,14 @@ break; } } - batch->shaderFeatures |= shaderFeatures & m_ctx->m_frameShaderFeaturesMask; - assert( - (batch->shaderFeatures & - gpu::ShaderFeaturesMaskFor(drawType, m_ctx->frameInterlockMode())) == - batch->shaderFeatures); - if (draw->imageTexture() != nullptr) { + if (m_ctx->frameInterlockMode() != gpu::InterlockMode::atomics && + drawType != DrawType::imageRect && drawType != DrawType::imageMesh) + { + shaderFeatures |= ShaderFeatures::ENABLE_MODULATED_IMAGE; + } + if (batch->imageTexture == nullptr) { // We merged in with a batch that did not already have an image so @@ -4091,6 +4117,12 @@ assert(batch->imageTexture == draw->imageTexture()); } + batch->shaderFeatures |= shaderFeatures & m_ctx->m_frameShaderFeaturesMask; + assert( + (batch->shaderFeatures & + gpu::ShaderFeaturesMaskFor(drawType, m_ctx->frameInterlockMode())) == + batch->shaderFeatures); + assert(draw->imageTexture() == nullptr || batch->imageSampler == draw->imageSampler());
diff --git a/renderer/src/render_context_helper_impl.cpp b/renderer/src/render_context_helper_impl.cpp index 21b3670..2ebd13d 100644 --- a/renderer/src/render_context_helper_impl.cpp +++ b/renderer/src/render_context_helper_impl.cpp
@@ -62,9 +62,14 @@ m_triangleBuffer = makeVertexBufferRing(sizeInBytes); } -void RenderContextHelperImpl::resizeImageDrawInstanceBuffer(size_t sizeInBytes) +void RenderContextHelperImpl::resizeImageRectInstanceBuffer(size_t sizeInBytes) { - m_imageDrawInstanceBuffer = makeVertexBufferRing(sizeInBytes); + m_imageRectInstanceBuffer = makeVertexBufferRing(sizeInBytes); +} + +void RenderContextHelperImpl::resizeImageMeshInstanceBuffer(size_t sizeInBytes) +{ + m_imageMeshInstanceBuffer = makeVertexBufferRing(sizeInBytes); } void* RenderContextHelperImpl::mapFlushUniformBuffer(size_t mapSizeInBytes) @@ -107,9 +112,14 @@ return m_triangleBuffer->mapBuffer(mapSizeInBytes); } -void* RenderContextHelperImpl::mapImageDrawInstanceBuffer(size_t mapSizeInBytes) +void* RenderContextHelperImpl::mapImageRectInstanceBuffer(size_t mapSizeInBytes) { - return m_imageDrawInstanceBuffer->mapBuffer(mapSizeInBytes); + return m_imageRectInstanceBuffer->mapBuffer(mapSizeInBytes); +} + +void* RenderContextHelperImpl::mapImageMeshInstanceBuffer(size_t mapSizeInBytes) +{ + return m_imageMeshInstanceBuffer->mapBuffer(mapSizeInBytes); } void RenderContextHelperImpl::unmapFlushUniformBuffer(size_t mapSizeInBytes) @@ -160,10 +170,17 @@ m_triangleBuffer->unmapAndSubmitBuffer(); } -void RenderContextHelperImpl::unmapImageDrawInstanceBuffer( +void RenderContextHelperImpl::unmapImageRectInstanceBuffer( size_t mapSizeInBytes) { - assert(m_imageDrawInstanceBuffer->mapSizeInBytes() == mapSizeInBytes); - m_imageDrawInstanceBuffer->unmapAndSubmitBuffer(); + assert(m_imageRectInstanceBuffer->mapSizeInBytes() == mapSizeInBytes); + m_imageRectInstanceBuffer->unmapAndSubmitBuffer(); +} + +void RenderContextHelperImpl::unmapImageMeshInstanceBuffer( + size_t mapSizeInBytes) +{ + assert(m_imageMeshInstanceBuffer->mapSizeInBytes() == mapSizeInBytes); + m_imageMeshInstanceBuffer->unmapAndSubmitBuffer(); } } // namespace rive::gpu
diff --git a/renderer/src/shaders/atomic_draw.glsl b/renderer/src/shaders/atomic_draw.glsl index af79f13..e8b46f1 100644 --- a/renderer/src/shaders/atomic_draw.glsl +++ b/renderer/src/shaders/atomic_draw.glsl
@@ -135,14 +135,10 @@ float4, @a_imageDrawClipRectInverseMatrix); ATTR(IMAGE_TRANSLATES_ATTRIB_IDX, float4, @a_imageDrawTranslates); -#ifdef SPLIT_UINT4_ATTRIBUTES -ATTR(IMAGE_SPLIT_OPACITY_ATTRIB_IDX, uint, @a_imageDrawOpacity); -ATTR(IMAGE_SPLIT_CLIP_ID_ATTRIB_IDX, uint, @a_imageDrawClipID); -ATTR(IMAGE_SPLIT_BLEND_MODE_ATTRIB_IDX, uint, @a_imageDrawBlendMode); -ATTR(IMAGE_SPLIT_ZINDEX_ATTRIB_IDX, uint, @a_imageDrawZIndex); -#else -ATTR(IMAGE_PACKED_ATTRIBS_IDX, uint4, @a_imageDrawPacked); -#endif +ATTR(IMAGE_OPACITY_ATTRIB_IDX, float, @a_imageDrawOpacity); +ATTR(IMAGE_CLIP_ID_ATTRIB_IDX, uint, @a_imageDrawClipID); +ATTR(IMAGE_BLEND_MODE_ATTRIB_IDX, uint, @a_imageDrawBlendMode); +ATTR(IMAGE_ZINDEX_ATTRIB_IDX, uint, @a_imageDrawZIndex); ATTR_BLOCK_END #endif @@ -177,18 +173,10 @@ @a_imageDrawClipRectInverseMatrix, float4); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawTranslates, float4); -#ifdef SPLIT_UINT4_ATTRIBUTES - ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawOpacity, uint); + ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawOpacity, float); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawClipID, uint); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawBlendMode, uint); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawZIndex, uint); - uint4 @a_imageDrawPacked = uint4(@a_imageDrawOpacity, - @a_imageDrawClipID, - @a_imageDrawBlendMode, - @a_imageDrawZIndex); -#else - ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawPacked, uint4); -#endif VARYING_INIT(v_texCoord, float2); VARYING_INIT(v_edgeCoverage, half); @@ -260,12 +248,12 @@ } #endif - v_imageOpacity = uintBitsToFloat(@a_imageDrawPacked.x); + v_imageOpacity = @a_imageDrawOpacity; #ifdef @ENABLE_CLIPPING - v_imageClipID = cast_uint_to_ushort(@a_imageDrawPacked.y); + v_imageClipID = cast_uint_to_ushort(@a_imageDrawClipID); #endif #ifdef @ENABLE_ADVANCED_BLEND - v_imageBlendMode = cast_uint_to_ushort(@a_imageDrawPacked.z); + v_imageBlendMode = cast_uint_to_ushort(@a_imageDrawBlendMode); #endif float4 pos = RENDER_TARGET_COORD_TO_CLIP_COORD(vertexPosition); @@ -302,14 +290,10 @@ float4, @a_imageDrawClipRectInverseMatrix); ATTR(IMAGE_TRANSLATES_ATTRIB_IDX, float4, @a_imageDrawTranslates); -#ifdef SPLIT_UINT4_ATTRIBUTES -ATTR(IMAGE_SPLIT_OPACITY_ATTRIB_IDX, uint, @a_imageDrawOpacity); -ATTR(IMAGE_SPLIT_CLIP_ID_ATTRIB_IDX, uint, @a_imageDrawClipID); -ATTR(IMAGE_SPLIT_BLEND_MODE_ATTRIB_IDX, uint, @a_imageDrawBlendMode); -ATTR(IMAGE_SPLIT_ZINDEX_ATTRIB_IDX, uint, @a_imageDrawZIndex); -#else -ATTR(IMAGE_PACKED_ATTRIBS_IDX, uint4, @a_imageDrawPacked); -#endif +ATTR(IMAGE_OPACITY_ATTRIB_IDX, float, @a_imageDrawOpacity); +ATTR(IMAGE_CLIP_ID_ATTRIB_IDX, uint, @a_imageDrawClipID); +ATTR(IMAGE_BLEND_MODE_ATTRIB_IDX, uint, @a_imageDrawBlendMode); +ATTR(IMAGE_ZINDEX_ATTRIB_IDX, uint, @a_imageDrawZIndex); ATTR_BLOCK_END #endif @@ -345,18 +329,10 @@ @a_imageDrawClipRectInverseMatrix, float4); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawTranslates, float4); -#ifdef SPLIT_UINT4_ATTRIBUTES - ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawOpacity, uint); + ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawOpacity, float); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawClipID, uint); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawBlendMode, uint); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawZIndex, uint); - uint4 @a_imageDrawPacked = uint4(@a_imageDrawOpacity, - @a_imageDrawClipID, - @a_imageDrawBlendMode, - @a_imageDrawZIndex); -#else - ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawPacked, uint4); -#endif VARYING_INIT(v_texCoord, float2); #ifdef @ENABLE_CLIP_RECT @@ -384,12 +360,12 @@ } #endif - v_imageOpacity = uintBitsToFloat(@a_imageDrawPacked.x); + v_imageOpacity = @a_imageDrawOpacity; #ifdef @ENABLE_CLIPPING - v_imageClipID = cast_uint_to_ushort(@a_imageDrawPacked.y); + v_imageClipID = cast_uint_to_ushort(@a_imageDrawClipID); #endif #ifdef @ENABLE_ADVANCED_BLEND - v_imageBlendMode = cast_uint_to_ushort(@a_imageDrawPacked.z); + v_imageBlendMode = cast_uint_to_ushort(@a_imageDrawBlendMode); #endif float4 pos = RENDER_TARGET_COORD_TO_CLIP_COORD(vertexPosition);
diff --git a/renderer/src/shaders/constants.glsl b/renderer/src/shaders/constants.glsl index 733907a..de9a675 100644 --- a/renderer/src/shaders/constants.glsl +++ b/renderer/src/shaders/constants.glsl
@@ -196,21 +196,27 @@ #define COALESCED_ATOMIC_RESOLVE_IDX SCRATCH_COLOR_PLANE_IDX // Index of each instanced attribute for image draws. +// First, the common attributes: #define IMAGE_FIRST_ATTRIB_IDX 2 #define IMAGE_VIEW_MATRIX_ATTRIB_IDX 2 #define IMAGE_CLIP_RECT_INVERSE_MATRIX_ATTRIB_IDX 3 #define IMAGE_TRANSLATES_ATTRIB_IDX 4 -#define IMAGE_PACKED_ATTRIBS_IDX 5 -#define IMAGE_LAST_ATTRIB_IDX 5 -#define IMAGE_ATTRIB_COUNT (IMAGE_LAST_ATTRIB_IDX + 1 - IMAGE_FIRST_ATTRIB_IDX) +#define IMAGE_OPACITY_ATTRIB_IDX 5 +#define IMAGE_CLIP_ID_ATTRIB_IDX 6 +#define IMAGE_BLEND_MODE_ATTRIB_IDX 7 +#define IMAGE_ZINDEX_ATTRIB_IDX 8 -// When SPLIT_UINT4_ATTRIBUTES is set (Unreal RHI, whose shader compiler -// mishandles a uint4 vertex attribute), the packed uint4 is bound as four -// separate uint attributes at these consecutive locations. -#define IMAGE_SPLIT_OPACITY_ATTRIB_IDX 5 -#define IMAGE_SPLIT_CLIP_ID_ATTRIB_IDX 6 -#define IMAGE_SPLIT_BLEND_MODE_ATTRIB_IDX 7 -#define IMAGE_SPLIT_ZINDEX_ATTRIB_IDX 8 +#define IMAGE_COMMON_LAST_ATTRIB_IDX 8 +#define IMAGE_COMMON_ATTRIB_COUNT \ + (IMAGE_COMMON_LAST_ATTRIB_IDX + 1 - IMAGE_FIRST_ATTRIB_IDX) + +#define IMAGE_RECT_LAST_ATTRIB_IDX IMAGE_COMMON_LAST_ATTRIB_IDX +#define IMAGE_RECT_ATTRIB_COUNT \ + (IMAGE_RECT_LAST_ATTRIB_IDX + 1 - IMAGE_FIRST_ATTRIB_IDX) + +#define IMAGE_MESH_LAST_ATTRIB_IDX IMAGE_COMMON_LAST_ATTRIB_IDX +#define IMAGE_MESH_ATTRIB_COUNT \ + (IMAGE_MESH_LAST_ATTRIB_IDX + 1 - IMAGE_FIRST_ATTRIB_IDX) // MSAA attaches different resources to the framebuffer instead of PLS planes. #define MSAA_DEPTH_STENCIL_IDX 1u
diff --git a/renderer/src/shaders/draw_image_mesh.vert b/renderer/src/shaders/draw_image_mesh.vert index 5d8d83b..a2709a9 100644 --- a/renderer/src/shaders/draw_image_mesh.vert +++ b/renderer/src/shaders/draw_image_mesh.vert
@@ -17,14 +17,10 @@ float4, @a_imageDrawClipRectInverseMatrix); ATTR(IMAGE_TRANSLATES_ATTRIB_IDX, float4, @a_imageDrawTranslates); -#ifdef SPLIT_UINT4_ATTRIBUTES -ATTR(IMAGE_SPLIT_OPACITY_ATTRIB_IDX, uint, @a_imageDrawOpacity); -ATTR(IMAGE_SPLIT_CLIP_ID_ATTRIB_IDX, uint, @a_imageDrawClipID); -ATTR(IMAGE_SPLIT_BLEND_MODE_ATTRIB_IDX, uint, @a_imageDrawBlendMode); -ATTR(IMAGE_SPLIT_ZINDEX_ATTRIB_IDX, uint, @a_imageDrawZIndex); -#else -ATTR(IMAGE_PACKED_ATTRIBS_IDX, uint4, @a_imageDrawPacked); -#endif +ATTR(IMAGE_OPACITY_ATTRIB_IDX, float, @a_imageDrawOpacity); +ATTR(IMAGE_CLIP_ID_ATTRIB_IDX, uint, @a_imageDrawClipID); +ATTR(IMAGE_BLEND_MODE_ATTRIB_IDX, uint, @a_imageDrawBlendMode); +ATTR(IMAGE_ZINDEX_ATTRIB_IDX, uint, @a_imageDrawZIndex); ATTR_BLOCK_END #endif @@ -63,18 +59,10 @@ @a_imageDrawClipRectInverseMatrix, float4); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawTranslates, float4); -#ifdef SPLIT_UINT4_ATTRIBUTES - ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawOpacity, uint); + ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawOpacity, float); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawClipID, uint); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawBlendMode, uint); ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawZIndex, uint); - uint4 @a_imageDrawPacked = uint4(@a_imageDrawOpacity, - @a_imageDrawClipID, - @a_imageDrawBlendMode, - @a_imageDrawZIndex); -#else - ATTR_UNPACK(_instanceID, imageDrawAttrs, @a_imageDrawPacked, uint4); -#endif VARYING_INIT(v_imageTexCoord, float2); #ifdef @ENABLE_CLIPPING @@ -96,7 +84,7 @@ if (@ENABLE_CLIPPING) { v_clipID = - id_bits_to_f16(@a_imageDrawPacked.y, uniforms.pathIDGranularity); + id_bits_to_f16(@a_imageDrawClipID, uniforms.pathIDGranularity); } #endif #ifdef @ENABLE_CLIP_RECT @@ -120,12 +108,12 @@ pos.y = -pos.y; #endif #ifdef @RENDER_MODE_DEPTH_STENCIL - pos.z = normalize_z_index(@a_imageDrawPacked.w); + pos.z = normalize_z_index(@a_imageDrawZIndex); #endif - v_imageOpacity = uintBitsToFloat(@a_imageDrawPacked.x); + v_imageOpacity = @a_imageDrawOpacity; #ifdef @ENABLE_ADVANCED_BLEND - v_imageBlendMode = cast_uint_to_ushort(@a_imageDrawPacked.z); + v_imageBlendMode = cast_uint_to_ushort(@a_imageDrawBlendMode); #endif VARYING_PACK(v_imageTexCoord);
diff --git a/renderer/src/shaders/draw_path.vert b/renderer/src/shaders/draw_path.vert index 07ea508..d83d5f4 100644 --- a/renderer/src/shaders/draw_path.vert +++ b/renderer/src/shaders/draw_path.vert
@@ -289,45 +289,42 @@ STORAGE_BUFFER_LOAD4(@paintAuxBuffer, pathID * PAINT_AUX_ENTRY_ELEMENT_COUNT + 1u); float2 paintCoord = MUL(paintMatrix, fragCoord) + paintTranslate.xy; - if (paintType == LINEAR_GRADIENT_PAINT_TYPE || - paintType == RADIAL_GRADIENT_PAINT_TYPE) + + // v_paint.a contains "-row" of the gradient ramp at texel center, + // in normalized space. + v_paint.a = -uintBitsToFloat(paintData.y); + // abs(v_paint.b) contains either: + // - 2 if the gradient ramp spans an entire row. + // - x0 of the gradient ramp in normalized space, if it's a simple + // 2-texel ramp. + float gradientSpan = paintTranslate.z; + // gradientSpan is either ~1 (complex gradients span the whole width + // of the texture minus 1px), or 1/GRAD_TEXTURE_WIDTH (simple + // gradients span 1px). + if (gradientSpan > .9) { - // v_paint.a contains "-row" of the gradient ramp at texel center, - // in normalized space. - v_paint.a = -uintBitsToFloat(paintData.y); - // abs(v_paint.b) contains either: - // - 2 if the gradient ramp spans an entire row. - // - x0 of the gradient ramp in normalized space, if it's a simple - // 2-texel ramp. - float gradientSpan = paintTranslate.z; - // gradientSpan is either ~1 (complex gradients span the whole width - // of the texture minus 1px), or 1/GRAD_TEXTURE_WIDTH (simple - // gradients span 1px). - if (gradientSpan > .9) - { - // Complex ramps span an entire row. Set it to 2 to convey this. - v_paint.b = 2.; - } - else - { - // This is a simple ramp. - v_paint.b = paintTranslate.w; - } - if (paintType == LINEAR_GRADIENT_PAINT_TYPE) - { - // The paint is a linear gradient. - v_paint.g = .0; - v_paint.r = paintCoord.x; - } - else - { - // The paint is a radial gradient. Mark v_paint.b negative to - // indicate this to the fragment shader. (v_paint.b can't be - // zero because the gradient ramp is aligned on pixel centers, - // so negating it will always produce a negative number.) - v_paint.b = -v_paint.b; - v_paint.rg = paintCoord.xy; - } + // Complex ramps span an entire row. Set it to 2 to convey this. + v_paint.b = 2.; + } + else + { + // This is a simple ramp. + v_paint.b = paintTranslate.w; + } + if (paintType == LINEAR_GRADIENT_PAINT_TYPE) + { + // The paint is a linear gradient. + v_paint.g = .0; + v_paint.r = paintCoord.x; + } + else + { + // The paint is a radial gradient. Mark v_paint.b negative to + // indicate this to the fragment shader. (v_paint.b can't be + // zero because the gradient ramp is aligned on pixel centers, + // so negating it will always produce a negative number.) + v_paint.b = -v_paint.b; + v_paint.rg = paintCoord.xy; } } #ifdef @EMULATE_DYNAMIC_COLOR_WRITE_DISABLE @@ -343,17 +340,19 @@ #if defined(@ENABLE_MODULATED_IMAGE) if (@ENABLE_MODULATED_IMAGE && (paintData.x & PAINT_FLAG_HAS_IMAGE) != 0u) { - float2x2 paintMatrix = make_float2x2( + float2x2 imageMatrix = make_float2x2( STORAGE_BUFFER_LOAD4(@paintAuxBuffer, pathID * PAINT_AUX_ENTRY_ELEMENT_COUNT + 4u)); float4 paintTranslateAndLOD = STORAGE_BUFFER_LOAD4(@paintAuxBuffer, pathID * PAINT_AUX_ENTRY_ELEMENT_COUNT + 5u); - float2 paintCoord = - MUL(paintMatrix, fragCoord) + paintTranslateAndLOD.xy; + float2 imageCoord = + MUL(imageMatrix, fragCoord) + paintTranslateAndLOD.xy; + // Add 1 to the LOD because a z value of 0 means "we don't have an + // image" v_image = - float3(paintCoord.x, paintCoord.y, 1. + paintTranslateAndLOD.z); + float3(imageCoord.x, imageCoord.y, 1. + paintTranslateAndLOD.z); } else {
diff --git a/renderer/src/shaders/metal.glsl b/renderer/src/shaders/metal.glsl index 732c7ff..a7ed364 100644 --- a/renderer/src/shaders/metal.glsl +++ b/renderer/src/shaders/metal.glsl
@@ -213,7 +213,7 @@ #endif // imageDrawAttrs is $device, not $constant: the host binds it at a per-draw -// offset of baseElement * sizeof(ImageDrawInstance), and macOS requires +// offset of baseElement * sizeof(ImageRectInstance), and macOS requires // constant-address-space offsets to be 256-byte aligned. #define IMAGE_RECT_VERTEX_MAIN(NAME, \ Attrs, \
diff --git a/renderer/src/vulkan/common_layouts.hpp b/renderer/src/vulkan/common_layouts.hpp index 93303ad..3547be8 100644 --- a/renderer/src/vulkan/common_layouts.hpp +++ b/renderer/src/vulkan/common_layouts.hpp
@@ -1,6 +1,8 @@ #include "rive/renderer/gpu.hpp" #include "rive/renderer/vulkan/vkutil.hpp" #include "shaders/constants.glsl" +#include "image_draw_attributes.hpp" + #include <array> #include <vulkan/vulkan.h> @@ -62,46 +64,89 @@ .pVertexAttributeDescriptions = INTERIOR_TRI_VERTEX_ATTRIBS, }; -// Concatenates the given geometryAttribs with Rive's ImageDrawInstance attribs -// (bound at 'binding'). -template <typename... GeometryAttribs> -constexpr std::array<VkVertexInputAttributeDescription, - sizeof...(GeometryAttribs) + IMAGE_ATTRIB_COUNT> -appendImageDrawInstanceAttribs(uint32_t binding, - GeometryAttribs... geometryAttribs) +constexpr VkFormat getVkFormat(VertexElementFormat format) { - return {{ - geometryAttribs..., - { - .location = IMAGE_VIEW_MATRIX_ATTRIB_IDX, + switch (format) + { + case VertexElementFormat::float1: + return VK_FORMAT_R32_SFLOAT; + case VertexElementFormat::float2: + return VK_FORMAT_R32G32_SFLOAT; + case VertexElementFormat::float3: + return VK_FORMAT_R32G32B32_SFLOAT; + case VertexElementFormat::float4: + return VK_FORMAT_R32G32B32A32_SFLOAT; + case VertexElementFormat::uint8x4: + return VK_FORMAT_R8G8B8A8_UINT; + case VertexElementFormat::sint8x4: + return VK_FORMAT_R8G8B8A8_SINT; + case VertexElementFormat::unorm8x4: + return VK_FORMAT_R8G8B8A8_UNORM; + case VertexElementFormat::snorm8x4: + return VK_FORMAT_R8G8B8A8_SNORM; + case VertexElementFormat::uint16x2: + return VK_FORMAT_R16G16_UINT; + case VertexElementFormat::sint16x2: + return VK_FORMAT_R16G16_SINT; + case VertexElementFormat::unorm16x2: + return VK_FORMAT_R16G16_UNORM; + case VertexElementFormat::snorm16x2: + return VK_FORMAT_R16G16_SNORM; + case VertexElementFormat::uint16x4: + return VK_FORMAT_R16G16B16A16_UINT; + case VertexElementFormat::sint16x4: + return VK_FORMAT_R16G16B16A16_SINT; + case VertexElementFormat::float16x2: + return VK_FORMAT_R16G16_SFLOAT; + case VertexElementFormat::float16x4: + return VK_FORMAT_R16G16B16A16_SFLOAT; + case VertexElementFormat::uint32: + return VK_FORMAT_R32_UINT; + } +} + +// Grab these from image_draw_attributes.hpp so that it's constexpr +template <typename ImageDrawInstance> constexpr auto getImageDrawAttributes() +{ + if constexpr (std::is_same_v<ImageDrawInstance, ImageRectInstance>) + { + return ImageRectInstanceAttributes; + } + else + { + static_assert(std::is_same_v<ImageDrawInstance, ImageMeshInstance>); + return ImageMeshInstanceAttributes; + } +} + +// Concatenates the given geometryAttribs with Rive's ImageRect/MeshInstance +// attribs (bound at 'binding'). +template <typename ImageDrawInstance, typename... GeometryAttribs> +constexpr auto appendImageDrawInstanceAttribs( + uint32_t binding, + GeometryAttribs... geometryAttribs) +{ + constexpr auto Attributes = getImageDrawAttributes<ImageDrawInstance>(); + + constexpr auto ArrayLength = + sizeof...(GeometryAttribs) + std::size(Attributes); + std::array<VkVertexInputAttributeDescription, ArrayLength> attrs = { + geometryAttribs...}; + + for (auto i = 0u; i < std::size(Attributes); i++) + { + const auto& src = Attributes[i]; + auto& dest = attrs[i + sizeof...(GeometryAttribs)]; + + dest = { + .location = src.attributeIndex, .binding = binding, - .format = VK_FORMAT_R32G32B32A32_SFLOAT, - .offset = (IMAGE_VIEW_MATRIX_ATTRIB_IDX - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4, - }, - { - .location = IMAGE_CLIP_RECT_INVERSE_MATRIX_ATTRIB_IDX, - .binding = binding, - .format = VK_FORMAT_R32G32B32A32_SFLOAT, - .offset = (IMAGE_CLIP_RECT_INVERSE_MATRIX_ATTRIB_IDX - - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4, - }, - { - .location = IMAGE_TRANSLATES_ATTRIB_IDX, - .binding = binding, - .format = VK_FORMAT_R32G32B32A32_SFLOAT, - .offset = (IMAGE_TRANSLATES_ATTRIB_IDX - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4, - }, - { - .location = IMAGE_PACKED_ATTRIBS_IDX, - .binding = binding, - .format = VK_FORMAT_R32G32B32A32_UINT, - .offset = (IMAGE_PACKED_ATTRIBS_IDX - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4, - }, - }}; + .format = getVkFormat(src.format), + .offset = src.byteOffset, + }; + } + + return attrs; } constexpr uint32_t ImageRectGeometryBufferBinding = 0; @@ -114,18 +159,19 @@ }, { .binding = ImageRectImageAttribBufferBinding, - .stride = sizeof(rive::gpu::ImageDrawInstance), + .stride = sizeof(rive::gpu::ImageRectInstance), .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE, }, }; -constexpr auto ImageRectVertexAttribs = appendImageDrawInstanceAttribs( - ImageRectImageAttribBufferBinding, - VkVertexInputAttributeDescription{ - .location = 0, - .binding = ImageRectGeometryBufferBinding, - .format = VK_FORMAT_R32G32B32A32_SFLOAT, - .offset = 0, - }); +constexpr auto ImageRectVertexAttribs = + appendImageDrawInstanceAttribs<ImageRectInstance>( + ImageRectImageAttribBufferBinding, + VkVertexInputAttributeDescription{ + .location = 0, + .binding = ImageRectGeometryBufferBinding, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = 0, + }); constexpr VkPipelineVertexInputStateCreateInfo IMAGE_RECT_VERTEX_INPUT_STATE = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .vertexBindingDescriptionCount = std::size(ImageRectInputBindings), @@ -150,24 +196,25 @@ }, { .binding = ImageMeshImageAttribBufferBinding, - .stride = sizeof(rive::gpu::ImageDrawInstance), + .stride = sizeof(rive::gpu::ImageMeshInstance), .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE, }, }; constexpr auto ImageMeshVertexAttribs = - appendImageDrawInstanceAttribs(ImageMeshImageAttribBufferBinding, - VkVertexInputAttributeDescription{ - .location = 0, - .binding = ImageMeshVertexBufferBinding, - .format = VK_FORMAT_R32G32_SFLOAT, - .offset = 0, - }, - VkVertexInputAttributeDescription{ - .location = 1, - .binding = ImageMeshUVBufferBinding, - .format = VK_FORMAT_R32G32_SFLOAT, - .offset = 0, - }); + appendImageDrawInstanceAttribs<ImageMeshInstance>( + ImageMeshImageAttribBufferBinding, + VkVertexInputAttributeDescription{ + .location = 0, + .binding = ImageMeshVertexBufferBinding, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = 0, + }, + VkVertexInputAttributeDescription{ + .location = 1, + .binding = ImageMeshUVBufferBinding, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = 0, + }); constexpr VkPipelineVertexInputStateCreateInfo IMAGE_MESH_VERTEX_INPUT_STATE = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .vertexBindingDescriptionCount = std::size(ImageMeshInputBindings),
diff --git a/renderer/src/vulkan/render_context_vulkan_impl.cpp b/renderer/src/vulkan/render_context_vulkan_impl.cpp index 8a88254..3f4ef16 100644 --- a/renderer/src/vulkan/render_context_vulkan_impl.cpp +++ b/renderer/src/vulkan/render_context_vulkan_impl.cpp
@@ -1115,7 +1115,8 @@ m_gradSpanBufferPool(m_vk, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), m_tessSpanBufferPool(m_vk, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), m_triangleBufferPool(m_vk, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), - m_imageDrawInstanceBufferPool(m_vk, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), + m_imageRectInstanceBufferPool(m_vk, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), + m_imageMeshInstanceBufferPool(m_vk, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), m_descriptorSetPoolPool(make_rcp<DescriptorSetPoolPool>(m_vk)) { const auto& physicalDeviceProps = m_vk->physicalDeviceProperties; @@ -1450,7 +1451,8 @@ assert(m_gradSpanBuffer == nullptr); assert(m_tessSpanBuffer == nullptr); assert(m_triangleBuffer == nullptr); - assert(m_imageDrawInstanceBuffer == nullptr); + assert(m_imageRectInstanceBuffer == nullptr); + assert(m_imageMeshInstanceBuffer == nullptr); if (m_canvasCommandPool != VK_NULL_HANDLE) { @@ -1971,7 +1973,8 @@ assert(m_gradSpanBuffer == nullptr); assert(m_tessSpanBuffer == nullptr); assert(m_triangleBuffer == nullptr); - assert(m_imageDrawInstanceBuffer == nullptr); + assert(m_imageRectInstanceBuffer == nullptr); + assert(m_imageMeshInstanceBuffer == nullptr); // Advance the context frame and delete resources that are no longer // referenced by in-flight command buffers. @@ -1993,7 +1996,8 @@ m_gradSpanBuffer = m_gradSpanBufferPool.acquire(); m_tessSpanBuffer = m_tessSpanBufferPool.acquire(); m_triangleBuffer = m_triangleBufferPool.acquire(); - m_imageDrawInstanceBuffer = m_imageDrawInstanceBufferPool.acquire(); + m_imageRectInstanceBuffer = m_imageRectInstanceBufferPool.acquire(); + m_imageMeshInstanceBuffer = m_imageMeshInstanceBufferPool.acquire(); } namespace descriptor_pool_limits @@ -4095,7 +4099,7 @@ commandBuffer, layout::ImageRectImageAttribBufferBinding, 1, - m_imageDrawInstanceBuffer->vkBufferAddressOf(), + m_imageRectInstanceBuffer->vkBufferAddressOf(), ZERO_OFFSET); m_vk->CmdBindIndexBuffer(commandBuffer, *m_imageRectIndexBuffer, @@ -4140,7 +4144,7 @@ commandBuffer, layout::ImageMeshImageAttribBufferBinding, 1, - m_imageDrawInstanceBuffer->vkBufferAddressOf(), + m_imageMeshInstanceBuffer->vkBufferAddressOf(), ZERO_OFFSET); m_vk->CmdBindIndexBuffer(commandBuffer, *indexBuffer->currentBuffer(), @@ -4193,7 +4197,8 @@ m_gradSpanBufferPool.recycle(std::move(m_gradSpanBuffer)); m_tessSpanBufferPool.recycle(std::move(m_tessSpanBuffer)); m_triangleBufferPool.recycle(std::move(m_triangleBuffer)); - m_imageDrawInstanceBufferPool.recycle(std::move(m_imageDrawInstanceBuffer)); + m_imageRectInstanceBufferPool.recycle(std::move(m_imageRectInstanceBuffer)); + m_imageMeshInstanceBufferPool.recycle(std::move(m_imageMeshInstanceBuffer)); } void RenderContextVulkanImpl::hotloadShaders(
diff --git a/renderer/src/webgpu/render_context_webgpu_impl.cpp b/renderer/src/webgpu/render_context_webgpu_impl.cpp index 6245881..649a5f9 100644 --- a/renderer/src/webgpu/render_context_webgpu_impl.cpp +++ b/renderer/src/webgpu/render_context_webgpu_impl.cpp
@@ -3018,38 +3018,62 @@ return m_atomicPLSCoverageBuffer; } -// Appends Rive's ImageDrawInstance attribs. The caller is responsible for +inline auto getWGPUVertexFormat(VertexElementFormat format) +{ + switch (format) + { + case VertexElementFormat::float1: + return WGPUVertexFormat_Float32; + case VertexElementFormat::float2: + return WGPUVertexFormat_Float32x2; + case VertexElementFormat::float3: + return WGPUVertexFormat_Float32x3; + case VertexElementFormat::float4: + return WGPUVertexFormat_Float32x4; + case VertexElementFormat::uint8x4: + return WGPUVertexFormat_Uint8x4; + case VertexElementFormat::sint8x4: + return WGPUVertexFormat_Sint8x4; + case VertexElementFormat::unorm8x4: + return WGPUVertexFormat_Unorm8x4; + case VertexElementFormat::snorm8x4: + return WGPUVertexFormat_Snorm8x4; + case VertexElementFormat::uint16x2: + return WGPUVertexFormat_Uint16x2; + case VertexElementFormat::sint16x2: + return WGPUVertexFormat_Sint16x2; + case VertexElementFormat::unorm16x2: + return WGPUVertexFormat_Unorm16x2; + case VertexElementFormat::snorm16x2: + return WGPUVertexFormat_Snorm16x2; + case VertexElementFormat::uint16x4: + return WGPUVertexFormat_Uint16x4; + case VertexElementFormat::sint16x4: + return WGPUVertexFormat_Sint16x4; + case VertexElementFormat::float16x2: + return WGPUVertexFormat_Float16x2; + case VertexElementFormat::float16x4: + return WGPUVertexFormat_Float16x4; + case VertexElementFormat::uint32: + return WGPUVertexFormat_Uint32; + } +} + +// Appends Rive's ImageRect/MeshInstance attribs. The caller is responsible for // placing these in a vertex buffer layout with WGPUVertexStepMode_Instance and -// arrayStride = sizeof(gpu::ImageDrawInstance). -template <uint32_t Capacity> +// arrayStride = sizeof(gpu::ImageRect/MeshInstance). +template <typename ImageDrawInstance, uint32_t Capacity> static void appendImageDrawInstanceAttribs( StackVector<WGPUVertexAttribute, Capacity>& attrs) { - attrs.push_back({ - .format = WGPUVertexFormat_Float32x4, - .offset = (IMAGE_VIEW_MATRIX_ATTRIB_IDX - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4, - .shaderLocation = IMAGE_VIEW_MATRIX_ATTRIB_IDX, - }); - attrs.push_back({ - .format = WGPUVertexFormat_Float32x4, - .offset = (IMAGE_CLIP_RECT_INVERSE_MATRIX_ATTRIB_IDX - - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4, - .shaderLocation = IMAGE_CLIP_RECT_INVERSE_MATRIX_ATTRIB_IDX, - }); - attrs.push_back({ - .format = WGPUVertexFormat_Float32x4, - .offset = (IMAGE_TRANSLATES_ATTRIB_IDX - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4, - .shaderLocation = IMAGE_TRANSLATES_ATTRIB_IDX, - }); // translations - attrs.push_back({ - .format = WGPUVertexFormat_Uint32x4, - .offset = (IMAGE_PACKED_ATTRIBS_IDX - IMAGE_FIRST_ATTRIB_IDX) * - sizeof(uint32_t) * 4, - .shaderLocation = IMAGE_PACKED_ATTRIBS_IDX, - }); + for (const auto& attr : ImageDrawInstance::getAttributes()) + { + attrs.push_back({ + .format = getWGPUVertexFormat(attr.format), + .offset = attr.byteOffset, + .shaderLocation = attr.attributeIndex, + }); + } } static WGPUBlendOperation wgpuBlendOp(gpu::BlendEquation blendEquation) @@ -3164,7 +3188,7 @@ StackVector<WGPUVertexBufferLayout, 3> vertexBufferLayouts; // The image-draw attribs come last, so the most vertex attribs used across // all 3 buffers is determined by the final image attrib. - StackVector<WGPUVertexAttribute, IMAGE_LAST_ATTRIB_IDX + 1> attrs; + StackVector<WGPUVertexAttribute, MaxVertexAttributeCount + 1> attrs; WGPUPrimitiveTopology topology; switch (drawType) { @@ -3231,7 +3255,7 @@ .offset = 0, .shaderLocation = 0, }); - appendImageDrawInstanceAttribs(attrs); + appendImageDrawInstanceAttribs<ImageRectInstance>(attrs); vertexBufferLayouts.push_back_n(2, WGPU_VERTEX_BUFFER_LAYOUT_INIT); vertexBufferLayouts[0].attributeCount = 1; @@ -3239,10 +3263,10 @@ vertexBufferLayouts[0].arrayStride = sizeof(gpu::ImageRectVertex); vertexBufferLayouts[0].stepMode = WGPUVertexStepMode_Vertex; - assert(attrs.size() == 1 + IMAGE_ATTRIB_COUNT); - vertexBufferLayouts[1].attributeCount = IMAGE_ATTRIB_COUNT; + assert(attrs.size() == 1 + IMAGE_RECT_ATTRIB_COUNT); + vertexBufferLayouts[1].attributeCount = IMAGE_RECT_ATTRIB_COUNT; vertexBufferLayouts[1].attributes = &attrs[1]; - vertexBufferLayouts[1].arrayStride = sizeof(gpu::ImageDrawInstance); + vertexBufferLayouts[1].arrayStride = sizeof(gpu::ImageRectInstance); vertexBufferLayouts[1].stepMode = WGPUVertexStepMode_Instance; topology = WGPUPrimitiveTopology_TriangleList; @@ -3260,7 +3284,7 @@ .offset = 0, .shaderLocation = 1, }); - appendImageDrawInstanceAttribs(attrs); + appendImageDrawInstanceAttribs<ImageMeshInstance>(attrs); vertexBufferLayouts.push_back_n(3, WGPU_VERTEX_BUFFER_LAYOUT_INIT); @@ -3274,10 +3298,10 @@ vertexBufferLayouts[1].arrayStride = sizeof(float) * 2; vertexBufferLayouts[1].stepMode = WGPUVertexStepMode_Vertex; - assert(attrs.size() == 2 + IMAGE_ATTRIB_COUNT); - vertexBufferLayouts[2].attributeCount = IMAGE_ATTRIB_COUNT; + assert(attrs.size() == 2 + IMAGE_MESH_ATTRIB_COUNT); + vertexBufferLayouts[2].attributeCount = IMAGE_MESH_ATTRIB_COUNT; vertexBufferLayouts[2].attributes = &attrs[2]; - vertexBufferLayouts[2].arrayStride = sizeof(gpu::ImageDrawInstance); + vertexBufferLayouts[2].arrayStride = sizeof(gpu::ImageMeshInstance); vertexBufferLayouts[2].stepMode = WGPUVertexStepMode_Instance; topology = WGPUPrimitiveTopology_TriangleList; @@ -4802,8 +4826,8 @@ // on GL, and we have seen drivers with bugs in their emulation. drawEncoder.SetVertexBuffer( 1, - webgpu_buffer(imageDrawInstanceBufferRing()), - batch.baseElement * sizeof(gpu::ImageDrawInstance)); + webgpu_buffer(imageRectInstanceBufferRing()), + batch.baseElement * sizeof(gpu::ImageRectInstance)); drawEncoder.SetIndexBuffer(m_imageRectIndexBuffer, wgpu::IndexFormat::Uint16); drawEncoder.DrawIndexed(batch.indexCountPerInstance, @@ -4830,8 +4854,8 @@ // on GL, and we have seen drivers with bugs in their emulation. drawEncoder.SetVertexBuffer( 2, - webgpu_buffer(imageDrawInstanceBufferRing()), - batch.baseElement * sizeof(gpu::ImageDrawInstance)); + webgpu_buffer(imageMeshInstanceBufferRing()), + batch.baseElement * sizeof(gpu::ImageMeshInstance)); drawEncoder.SetIndexBuffer(indexBuffer->submittedBuffer(), wgpu::IndexFormat::Uint16); drawEncoder.DrawIndexed(batch.indexCountPerInstance,
diff --git a/tests/gm/ore_blend_stencil.cpp b/tests/gm/ore_blend_stencil.cpp index d2a818e..b2f6b9b 100644 --- a/tests/gm/ore_blend_stencil.cpp +++ b/tests/gm/ore_blend_stencil.cpp
@@ -186,7 +186,7 @@ if (!shader.vsModule) return; - VertexAttribute attrs[] = { + ore::VertexAttribute attrs[] = { {offsetof(BSVertex, x), 0, VertexFormat::float2}, {offsetof(BSVertex, r), 1, VertexFormat::float4}, };
diff --git a/tests/gm/ore_depth.cpp b/tests/gm/ore_depth.cpp index 0cc7dc5..6f0b7cf 100644 --- a/tests/gm/ore_depth.cpp +++ b/tests/gm/ore_depth.cpp
@@ -141,7 +141,7 @@ return; // Vertex layout: float3 position + float4 color, interleaved. - VertexAttribute attrs[] = { + ore::VertexAttribute attrs[] = { {offsetof(DepthVertex, x), 0, VertexFormat::float3}, {offsetof(DepthVertex, r), 1, VertexFormat::float4}, };
diff --git a/tests/gm/ore_depth_only_pipeline.cpp b/tests/gm/ore_depth_only_pipeline.cpp index 8e1545d..9a80ef6 100644 --- a/tests/gm/ore_depth_only_pipeline.cpp +++ b/tests/gm/ore_depth_only_pipeline.cpp
@@ -69,7 +69,7 @@ // scope: Pipeline shallow-copies PipelineDesc (mirrors Lua's // `ownedVertexLayoutData`), so these must outlive the rcp below. // VertexAttribute = { format, offset, shaderSlot }. - VertexAttribute attrs[] = { + ore::VertexAttribute attrs[] = { {0, 0, VertexFormat::float2}, {8, 1, VertexFormat::float4}, };
diff --git a/tests/gm/ore_msaa_resolve.cpp b/tests/gm/ore_msaa_resolve.cpp index 31d4127..7839dfa 100644 --- a/tests/gm/ore_msaa_resolve.cpp +++ b/tests/gm/ore_msaa_resolve.cpp
@@ -166,7 +166,7 @@ return; } - VertexAttribute attrs[] = { + ore::VertexAttribute attrs[] = { {offsetof(MsaaVertex, x), 0, VertexFormat::float2}, {offsetof(MsaaVertex, r), 1, VertexFormat::float4}, };
diff --git a/tests/gm/ore_triangle.cpp b/tests/gm/ore_triangle.cpp index 13674b6..185cacd 100644 --- a/tests/gm/ore_triangle.cpp +++ b/tests/gm/ore_triangle.cpp
@@ -97,7 +97,7 @@ return; // Vertex layout: float2 position + float4 color, interleaved. - VertexAttribute attrs[] = { + ore::VertexAttribute attrs[] = { {offsetof(Vertex, x), 0, VertexFormat::float2}, // position {offsetof(Vertex, r), 1, VertexFormat::float4}, // color };
diff --git a/tests/unit_tests/renderer/pls_render_context_test.cpp b/tests/unit_tests/renderer/pls_render_context_test.cpp index 401f287..975b80f 100644 --- a/tests/unit_tests/renderer/pls_render_context_test.cpp +++ b/tests/unit_tests/renderer/pls_render_context_test.cpp
@@ -22,7 +22,7 @@ using Super = RenderContextNULL; public: - static constexpr auto MAP_COUNT = 9u; + static constexpr auto MAP_COUNT = 10u; RenderContextNULLTestImplForMapFail(uint32_t failingMapIndex) : Super(), m_failingMapIndex(failingMapIndex) @@ -31,6 +31,7 @@ size_t totalMapCount() const { return m_totalMapCount; } #define MAKE_MAP_UNMAP(index, Name) \ + static_assert(index < MAP_COUNT); \ void* map##Name(size_t mapSizeInBytes) override \ { \ static_assert(index < MAP_COUNT); \ @@ -64,7 +65,8 @@ MAKE_MAP_UNMAP(5, GradSpanBuffer) MAKE_MAP_UNMAP(6, TessVertexSpanBuffer) MAKE_MAP_UNMAP(7, TriangleVertexBuffer) - MAKE_MAP_UNMAP(8, ImageDrawInstanceBuffer) + MAKE_MAP_UNMAP(8, ImageRectInstanceBuffer) + MAKE_MAP_UNMAP(9, ImageMeshInstanceBuffer) #undef MAKE_MAP_UNMAP @@ -121,9 +123,10 @@ allocs.gradSpanBufferCount = 4; allocs.tessSpanBufferCount = 5; allocs.triangleVertexBufferCount = 6; - allocs.imageDrawInstanceBufferCount = 7; - allocs.gradTextureHeight = 8; - allocs.tessTextureHeight = 9; + allocs.imageRectInstanceBufferCount = 7; + allocs.imageMeshInstanceBufferCount = 8; + allocs.gradTextureHeight = 9; + allocs.tessTextureHeight = 10; allocs = ResourceAllocationCounts::FromVec(allocs.toVec() * 2); CHECK(allocs.pathBufferCount == 2); @@ -131,17 +134,19 @@ CHECK(allocs.gradSpanBufferCount == 8); CHECK(allocs.tessSpanBufferCount == 10); CHECK(allocs.triangleVertexBufferCount == 12); - CHECK(allocs.imageDrawInstanceBufferCount == 14); - CHECK(allocs.gradTextureHeight == 16); - CHECK(allocs.tessTextureHeight == 18); + CHECK(allocs.imageRectInstanceBufferCount == 14); + CHECK(allocs.imageMeshInstanceBufferCount == 16); + CHECK(allocs.gradTextureHeight == 18); + CHECK(allocs.tessTextureHeight == 20); RenderContextTest::ResourceAllocationCounts testAllocs; testAllocs.pathBufferCount = 18; testAllocs.contourBufferCount = 16; - testAllocs.gradSpanBufferCount = 12; - testAllocs.tessSpanBufferCount = 10; - testAllocs.triangleVertexBufferCount = 8; - testAllocs.imageDrawInstanceBufferCount = 6; + testAllocs.gradSpanBufferCount = 14; + testAllocs.tessSpanBufferCount = 12; + testAllocs.triangleVertexBufferCount = 10; + testAllocs.imageRectInstanceBufferCount = 8; + testAllocs.imageMeshInstanceBufferCount = 6; testAllocs.gradTextureHeight = 4; testAllocs.tessTextureHeight = 2; @@ -151,12 +156,13 @@ allocs.toVec() * size_t(5))); CHECK(allocs.pathBufferCount == 18); CHECK(allocs.contourBufferCount == 16); - CHECK(allocs.gradSpanBufferCount == 12); - CHECK(allocs.tessSpanBufferCount == 10); + CHECK(allocs.gradSpanBufferCount == 14); + CHECK(allocs.tessSpanBufferCount == 12); CHECK(allocs.triangleVertexBufferCount == 12 * 5); - CHECK(allocs.imageDrawInstanceBufferCount == 14 * 5); - CHECK(allocs.gradTextureHeight == 16 * 5); - CHECK(allocs.tessTextureHeight == 18 * 5); + CHECK(allocs.imageRectInstanceBufferCount == 14 * 5); + CHECK(allocs.imageMeshInstanceBufferCount == 16 * 5); + CHECK(allocs.gradTextureHeight == 18 * 5); + CHECK(allocs.tessTextureHeight == 20 * 5); allocs = ResourceAllocationCounts::FromVec( simd::if_then_else(testAllocs.toVec() * size_t(2) <= allocs.toVec(), @@ -164,12 +170,13 @@ allocs.toVec())); CHECK(allocs.pathBufferCount == 18); CHECK(allocs.contourBufferCount == 16); - CHECK(allocs.gradSpanBufferCount == 12); - CHECK(allocs.tessSpanBufferCount == 10); + CHECK(allocs.gradSpanBufferCount == 14); + CHECK(allocs.tessSpanBufferCount == 12); CHECK(allocs.triangleVertexBufferCount == 6 * 5); - CHECK(allocs.imageDrawInstanceBufferCount == 7 * 5); - CHECK(allocs.gradTextureHeight == 8 * 5); - CHECK(allocs.tessTextureHeight == 9 * 5); + CHECK(allocs.imageRectInstanceBufferCount == 7 * 5); + CHECK(allocs.imageMeshInstanceBufferCount == 8 * 5); + CHECK(allocs.gradTextureHeight == 9 * 5); + CHECK(allocs.tessTextureHeight == 10 * 5); } constexpr static double RESOURCE_EXPIRATION_TIME = 5.0001;