fix(webgpu): Fix Y orientation on GL zero and nonzero FBOs (#10623) 454b0d2db1

Co-authored-by: Chris Dalton <99840794+csmartdalton@users.noreply.github.com>
This commit is contained in:
csmartdalton
2025-09-19 02:18:27 +00:00
parent 724e157c07
commit e54883d909
7 changed files with 59 additions and 55 deletions

View File

@@ -1 +1 @@
ec8b00c206d9842b618d1368617155cb0bfaae62
454b0d2db1370c47b3eafbc325d9295200b4e803

View File

@@ -20,14 +20,7 @@ class RenderContextWebGPUImpl : public RenderContextHelperImpl
{
public:
struct ContextOptions
{
// Invert Y when drawing to client-provided RenderTargets.
// TODO: We may need to eventually make this configurable
// per-RenderTarget.
bool invertRenderTargetY = false;
// Invert the front face when drawing to client-provied RenderTargets.
bool invertRenderTargetFrontFace = false;
};
{};
enum class PixelLocalStorageType
{
@@ -102,12 +95,6 @@ private:
wgpu::LoadOp,
const wgpu::Color& clearColor);
wgpu::FrontFace frontFaceForRenderTargetDraws() const
{
return m_contextOptions.invertRenderTargetFrontFace
? wgpu::FrontFace::CCW
: wgpu::FrontFace::CW;
}
wgpu::PipelineLayout drawPipelineLayout() const
{
return m_drawPipelineLayout;
@@ -118,10 +105,6 @@ private:
void generateMipmaps(wgpu::Texture);
// PLS always expects a clockwise front face.
constexpr static wgpu::FrontFace kFrontFaceForOffscreenDraws =
wgpu::FrontFace::CW;
std::unique_ptr<BufferRing> makeUniformBufferRing(
size_t capacityInBytes) override;
std::unique_ptr<BufferRing> makeStorageBufferRing(
@@ -211,7 +194,7 @@ public:
return m_framebufferFormat;
}
void setTargetTextureView(wgpu::TextureView);
void setTargetTextureView(wgpu::TextureView, wgpu::Texture);
protected:
RenderTargetWebGPU(wgpu::Device device,
@@ -225,6 +208,7 @@ private:
const wgpu::TextureFormat m_framebufferFormat;
wgpu::Texture m_targetTexture;
wgpu::Texture m_coverageTexture;
wgpu::Texture m_clipTexture;
wgpu::Texture m_scratchColorTexture;
@@ -244,6 +228,7 @@ public:
m_textureView(m_texture.CreateView())
{}
wgpu::Texture texture() const { return m_texture; }
wgpu::TextureView textureView() const { return m_textureView; }
private:

View File

@@ -283,7 +283,8 @@ public:
m_currentSurfaceTextureView =
wgpuTextureCreateView(m_currentSurfaceTexture.texture,
&textureViewDesc);
m_renderTarget->setTargetTextureView(m_currentSurfaceTextureView);
m_renderTarget->setTargetTextureView(m_currentSurfaceTextureView,
m_currentSurfaceTexture.texture);
m_renderContext->beginFrame(std::move(frameDescriptor));
}

View File

@@ -72,6 +72,9 @@ IMAGE_MESH_VERTEX_MAIN(@drawVertexMain,
}
#endif // ENABLE_CLIP_RECT
float4 pos = RENDER_TARGET_COORD_TO_CLIP_COORD(vertexPosition);
#ifdef @POST_INVERT_Y
pos.y = -pos.y;
#endif
#ifdef @RENDER_MODE_MSAA
pos.z = normalize_z_index(imageDrawUniforms.zIndex);
#endif

View File

@@ -54,6 +54,8 @@ using TextureDataLayout = TexelCopyBufferLayout;
}; // namespace wgpu
#endif
constexpr static auto RIVE_FRONT_FACE = wgpu::FrontFace::CW;
#ifdef RIVE_WAGYU
#include <webgpu/webgpu_wagyu.h>
@@ -199,7 +201,7 @@ public:
.primitive =
{
.topology = wgpu::PrimitiveTopology::TriangleStrip,
.frontFace = context->frontFaceForRenderTargetDraws(),
.frontFace = RIVE_FRONT_FACE,
.cullMode = wgpu::CullMode::None,
},
.fragment = &fragmentState,
@@ -339,7 +341,7 @@ public:
.primitive =
{
.topology = wgpu::PrimitiveTopology::TriangleStrip,
.frontFace = kFrontFaceForOffscreenDraws,
.frontFace = RIVE_FRONT_FACE,
.cullMode = wgpu::CullMode::None,
},
.fragment = &fragmentState,
@@ -497,7 +499,7 @@ public:
.primitive =
{
.topology = wgpu::PrimitiveTopology::TriangleList,
.frontFace = kFrontFaceForOffscreenDraws,
.frontFace = RIVE_FRONT_FACE,
.cullMode = wgpu::CullMode::None,
},
.fragment = &fragmentState,
@@ -677,7 +679,7 @@ public:
.primitive =
{
.topology = wgpu::PrimitiveTopology::TriangleList,
.frontFace = kFrontFaceForOffscreenDraws,
.frontFace = RIVE_FRONT_FACE,
.cullMode = wgpu::CullMode::Back,
},
.fragment = &fragmentState,
@@ -709,7 +711,8 @@ class RenderContextWebGPUImpl::DrawPipeline
public:
DrawPipeline(RenderContextWebGPUImpl* context,
DrawType drawType,
gpu::ShaderFeatures shaderFeatures)
gpu::ShaderFeatures shaderFeatures,
bool targetIsGLFBO0)
{
wgpu::ShaderModule vertexShader, fragmentShader;
#ifdef RIVE_WAGYU
@@ -730,7 +733,7 @@ public:
{
language = WGPUWagyuShaderLanguage_GLSLRAW;
versionString = "#version 310 es";
if (context->m_contextOptions.invertRenderTargetY)
if (!targetIsGLFBO0)
{
addDefine(GLSL_POST_INVERT_Y);
}
@@ -1403,10 +1406,6 @@ void RenderContextWebGPUImpl::initGPUObjects()
glsl << "#define gl_VertexID gl_VertexIndex\n";
glsl << "#endif\n";
glsl << "#define " GLSL_ENABLE_CLIPPING " true\n";
if (m_contextOptions.invertRenderTargetY)
{
glsl << "#define " GLSL_POST_INVERT_Y " true\n";
}
BuildLoadStoreEXTGLSL(glsl, LoadStoreActionsEXT::none);
m_loadStoreEXTVertexShader =
compile_shader_module_wagyu(m_device,
@@ -1545,8 +1544,10 @@ RenderTargetWebGPU::RenderTargetWebGPU(
m_scratchColorTextureView = m_scratchColorTexture.CreateView();
}
void RenderTargetWebGPU::setTargetTextureView(wgpu::TextureView textureView)
void RenderTargetWebGPU::setTargetTextureView(wgpu::TextureView textureView,
wgpu::Texture texture)
{
m_targetTexture = texture;
m_targetTextureView = textureView;
}
@@ -2300,8 +2301,7 @@ wgpu::RenderPipeline RenderContextWebGPUImpl::makeDrawPipeline(
.primitive =
{
.topology = WGPUPrimitiveTopology_TriangleList,
.frontFace =
static_cast<WGPUFrontFace>(frontFaceForRenderTargetDraws()),
.frontFace = static_cast<WGPUFrontFace>(RIVE_FRONT_FACE),
.cullMode = DrawTypeIsImageDraw(drawType) ? WGPUCullMode_None
: WGPUCullMode_Back,
},
@@ -2930,16 +2930,28 @@ void RenderContextWebGPUImpl::flush(const FlushDescriptor& desc)
}
// Setup the pipeline for this specific drawType and shaderFeatures.
bool targetIsGLFBO0 = false;
#ifdef RIVE_WAGYU
if (m_capabilities.backendType == wgpu::BackendType::OpenGLES)
{
targetIsGLFBO0 = wgpuWagyuTextureIsSwapchain(
renderTarget->m_targetTexture.Get());
}
#endif
uint32_t webgpuShaderKey =
(gpu::ShaderUniqueKey(drawType,
batch.shaderFeatures,
gpu::InterlockMode::rasterOrdering,
gpu::ShaderMiscFlags::none)
<< 1) |
static_cast<uint32_t>(targetIsGLFBO0);
const DrawPipeline& drawPipeline =
m_drawPipelines
.try_emplace(
gpu::ShaderUniqueKey(drawType,
batch.shaderFeatures,
gpu::InterlockMode::rasterOrdering,
gpu::ShaderMiscFlags::none),
this,
drawType,
batch.shaderFeatures)
.try_emplace(webgpuShaderKey,
this,
drawType,
batch.shaderFeatures,
targetIsGLFBO0)
.first->second;
drawPass.SetPipeline(
drawPipeline.renderPipeline(renderTarget->framebufferFormat()));

View File

@@ -226,7 +226,7 @@ extern "C" EM_BOOL animationFrame(double time, void* userData)
scene->draw(renderer.get());
renderer->restore();
renderTarget->setTargetTextureView(textureView);
renderTarget->setTargetTextureView(textureView, texture);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
renderContext->flush({
.renderTarget = renderTarget.get(),

View File

@@ -130,7 +130,7 @@ private:
width,
height,
renderContextImpl->device().CreateTexture(&textureDesc));
setTargetTextureView(texture->textureView());
setTargetTextureView(texture->textureView(), texture->texture());
m_renderImage =
rive::make_rcp<rive::RiveRenderImage>(std::move(texture));
}
@@ -326,14 +326,16 @@ public:
m_renderTarget =
m_renderContext->static_impl_cast<RenderContextWebGPUImpl>()
->makeRenderTarget(m_format, m_width, m_height);
m_renderTarget->setTargetTextureView(m_overflowTextureView);
m_renderTarget->setTargetTextureView(m_overflowTextureView,
m_overflowTexture);
}
else
{
m_renderTarget =
m_renderContext->static_impl_cast<RenderContextWebGPUImpl>()
->makeRenderTarget(m_format, surfaceWidth, surfaceHeight);
m_renderTarget->setTargetTextureView(m_currentCanvasTextureView);
m_renderTarget->setTargetTextureView(m_currentCanvasTextureView,
m_currentCanvasTexture);
}
rive::gpu::RenderContext::FrameDescriptor frameDescriptor = {
@@ -400,16 +402,16 @@ public:
{
assert(m_format == wgpu::TextureFormat::RGBA8Unorm ||
m_format == wgpu::TextureFormat::BGRA8Unorm);
const uint32_t rowBytesInReadBuff =
math::round_up_to_multiple_of<256>(m_width * 4);
bool invertY = false;
#ifdef RIVE_WAGYU
if (impl()->capabilities().backendType ==
wgpu::BackendType::OpenGLES)
{
invertY = true;
}
invertY =
impl()->capabilities().backendType ==
wgpu::BackendType::OpenGLES &&
wgpuWagyuTextureIsSwapchain(m_currentCanvasTexture.Get()) &&
m_overflowTexture == nullptr;
#endif
const uint32_t rowBytesInReadBuff =
math::round_up_to_multiple_of<256>(m_width * 4);
// Create a buffer to receive the pixels.
if (!m_pixelReadBuff)
@@ -426,8 +428,9 @@ public:
// Blit the framebuffer into m_pixelReadBuff.
wgpu::CommandEncoder readEncoder = m_device.CreateCommandEncoder();
wgpu::TexelCopyTextureInfo srcTexture = {
.texture = m_overflowTexture ? m_overflowTexture
: m_currentCanvasTexture,
.texture = m_overflowTexture != nullptr
? m_overflowTexture
: m_currentCanvasTexture,
.origin = {0,
invertY ? m_renderTarget->height() - m_height : 0,
0},