fix(renderer): vulkan rotation, mali triangle padding and wgsl bounds for script gpu passes (#14485) 4c2d79894f * fix(renderer): let the compositor rotate our Vulkan swapchains * fix(evi): rebuild the Vulkan swapchain when an Android surface rotates * fix(ore): report half float render targets on Vulkan, D3D and WebGPU * fix(renderer): enable robust buffer access so script shaders keep WGSL bounds * fix(scripting): clamp out of range accesses in script shaders as WGSL requires * fix(renderer): keep glsl texel fetches lowering, limit robust access to arm and surface polling to android * fix(renderer): name the interior triangulation read as the reason for robust access on mali * fix(renderer): zero the triangle buffer tail mali shades past a draw, drop robust access * fix(renderer): start each flush's triangles on a mali vertex group of 4 Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
diff --git a/.rive_head b/.rive_head index 48e9d66..2ef8e90 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -4057f908b214181e224a7c9e9d9339641f6b7dc3 +4c2d79894f8dfd8dd8cca0aa6d4a44504cdc51b9
diff --git a/renderer/include/rive/renderer/gpu.hpp b/renderer/include/rive/renderer/gpu.hpp index 9e09429..f3b6558 100644 --- a/renderer/include/rive/renderer/gpu.hpp +++ b/renderer/include/rive/renderer/gpu.hpp
@@ -1886,6 +1886,10 @@ }; static_assert(sizeof(TriangleVertex) == sizeof(float) * 3); +// Mali shades vertices in index aligned groups of this size, loads included, so +// a draw also runs up to 3 vertices on either side of its range. +constexpr static size_t kTriangleVertexGroupSize = 4; + enum class BoundVertexInstanceType { none, @@ -2154,6 +2158,12 @@ } } void skip_back() { push(); } + void zero_unwritten() + { + memset(static_cast<void*>(m_nextMappedItem), + 0, + (m_mappingEnd - m_nextMappedItem) * sizeof(T)); + } private: RIVE_ALWAYS_INLINE T& push()
diff --git a/renderer/rive_vk_bootstrap/src/vulkan_swapchain.cpp b/renderer/rive_vk_bootstrap/src/vulkan_swapchain.cpp index 258cd69..475d808 100644 --- a/renderer/rive_vk_bootstrap/src/vulkan_swapchain.cpp +++ b/renderer/rive_vk_bootstrap/src/vulkan_swapchain.cpp
@@ -88,6 +88,13 @@ device.getSurfaceCapabilities(surface, &surfaceCaps), "Failed to get Vulkan device surface capabilities"); + // We never draw prerotated, so the compositor has to rotate our frames. + VkSurfaceTransformFlagBitsKHR preTransform = + (surfaceCaps.supportedTransforms & + VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) + ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR + : surfaceCaps.currentTransform; + VkSwapchainCreateInfoKHR swapchainCreateInfo = { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .surface = surface, @@ -98,7 +105,7 @@ .imageExtent = surfaceCaps.currentExtent, .imageArrayLayers = 1, .imageUsage = opts.imageUsageFlags, - .preTransform = surfaceCaps.currentTransform, + .preTransform = preTransform, .compositeAlpha = compositeAlphaFlags, .presentMode = presentMode.value(), .clipped = true,
diff --git a/renderer/src/ore/d3d11/ore_context_d3d11.cpp b/renderer/src/ore/d3d11/ore_context_d3d11.cpp index 9d753e5..a1bdbb3 100644 --- a/renderer/src/ore/d3d11/ore_context_d3d11.cpp +++ b/renderer/src/ore/d3d11/ore_context_d3d11.cpp
@@ -1527,6 +1527,7 @@ // Populate features for D3D11 feature level 11.0. Features& f = ctx->m_features; f.colorBufferFloat = true; + f.colorBufferHalfFloat = true; f.perTargetBlend = true; f.perTargetWriteMask = true; f.textureViewSampling = true;
diff --git a/renderer/src/ore/d3d12/ore_context_d3d12.cpp b/renderer/src/ore/d3d12/ore_context_d3d12.cpp index f46006f..3603d30 100644 --- a/renderer/src/ore/d3d12/ore_context_d3d12.cpp +++ b/renderer/src/ore/d3d12/ore_context_d3d12.cpp
@@ -538,6 +538,7 @@ // --- Features --- Features& f = ctx->m_features; f.colorBufferFloat = true; + f.colorBufferHalfFloat = true; f.perTargetBlend = true; f.perTargetWriteMask = true; f.textureViewSampling = true;
diff --git a/renderer/src/ore/vulkan/ore_context_vulkan.cpp b/renderer/src/ore/vulkan/ore_context_vulkan.cpp index 7d40f29..61ca546 100644 --- a/renderer/src/ore/vulkan/ore_context_vulkan.cpp +++ b/renderer/src/ore/vulkan/ore_context_vulkan.cpp
@@ -342,6 +342,7 @@ Features& f = ctx->m_features; f.colorBufferFloat = true; // All Vulkan 1.1+ support rgba16f attachments. + f.colorBufferHalfFloat = true; f.perTargetBlend = enabled.independentBlend; f.perTargetWriteMask = enabled.independentBlend; f.textureViewSampling = true;
diff --git a/renderer/src/ore/wgpu/ore_context_wgpu.cpp b/renderer/src/ore/wgpu/ore_context_wgpu.cpp index bab08d5..4c08b3f 100644 --- a/renderer/src/ore/wgpu/ore_context_wgpu.cpp +++ b/renderer/src/ore/wgpu/ore_context_wgpu.cpp
@@ -454,6 +454,7 @@ // Populate features with WebGPU-level capabilities. Features& f = ctx->m_features; f.colorBufferFloat = true; + f.colorBufferHalfFloat = true; f.perTargetBlend = true; f.perTargetWriteMask = true; f.textureViewSampling = true;
diff --git a/renderer/src/render_context.cpp b/renderer/src/render_context.cpp index 57bcb58..ceac5c2 100644 --- a/renderer/src/render_context.cpp +++ b/renderer/src/render_context.cpp
@@ -883,7 +883,11 @@ .tessSpanBufferCount = totalFrameResourceCounts.maxTessellatedSegmentCount, .triangleVertexBufferCount = - totalFrameResourceCounts.maxTriangleVertexCount, + totalFrameResourceCounts.maxTriangleVertexCount > 0 + ? totalFrameResourceCounts.maxTriangleVertexCount + + (m_logicalFlushes.size() + 1) * + (kTriangleVertexGroupSize - 1) + : 0, .imageRectInstanceBufferCount = totalFrameResourceCounts.imageRectCount, .imageMeshInstanceBufferCount = totalFrameResourceCounts.imageMeshCount, .gradTextureHeight = layoutCounts.maxGradTextureHeight, @@ -1079,7 +1083,8 @@ assert(m_tessSpanData.elementsWritten() <= totalFrameResourceCounts.maxTessellatedSegmentCount); assert(m_triangleVertexData.elementsWritten() <= - totalFrameResourceCounts.maxTriangleVertexCount); + totalFrameResourceCounts.maxTriangleVertexCount + + m_logicalFlushes.size() * (kTriangleVertexGroupSize - 1)); unmapResourceBuffers(resourceRequirements); @@ -1533,6 +1538,18 @@ // Exact tessSpan/triangleVertex counts aren't known until after their data // is written out. size_t firstTessVertexSpan = m_ctx->m_tessSpanData.elementsWritten(); + if (m_ctx->m_triangleVertexData) + { + // Start on a vertex group, so neither this flush nor the one before it + // shades the other's vertices through the wrong path offsets. + const size_t groupPadding = + math::padding_to_align_up<kTriangleVertexGroupSize>( + m_ctx->m_triangleVertexData.elementsWritten()); + for (size_t i = 0; i < groupPadding; ++i) + { + m_ctx->m_triangleVertexData.emplace_back(Vec2D{}, 0, 0); + } + } size_t initialTriangleVertexDataSize = m_ctx->m_triangleVertexData.bytesWritten(); @@ -3136,6 +3153,7 @@ } if (m_triangleVertexData) { + m_triangleVertexData.zero_unwritten(); m_triangleVertexData.unmapElements( m_impl.get(), &RenderContextImpl::unmapTriangleVertexBuffer,