Skip to content

[Flutter GPU] Vulkan backend: setViewport drops X and Y offsets #185885

Description

@bdero

When a Flutter GPU RenderPass.setViewport is called with a non-zero x (or y) offset, the Vulkan backend renders as if x = 0 and y = 0. The width and height are honored, but the X and Y offsets are silently dropped.

The bug was originally observed via the golden diff for the Can render portion of the triangle using viewport test in #185879, which I incorrectly attributed to the OpenGLES backend on first inspection. Investigation shows that the OpenGLES (and Metal) backends are correct, and the Vulkan backend is the one dropping the offsets.

Root cause

engine/src/flutter/impeller/renderer/backend/vulkan/render_pass_vk.cc:389-397:

void RenderPassVK::SetViewport(Viewport viewport) {
  vk::Viewport viewport_vk = vk::Viewport()
                                 .setWidth(viewport.rect.GetWidth())
                                 .setHeight(-viewport.rect.GetHeight())
                                 .setY(viewport.rect.GetHeight())
                                 .setMinDepth(0.0f)
                                 .setMaxDepth(1.0f);
  command_buffer_vk_.setViewport(0, 1, &viewport_vk);
}

The chained-setter call never invokes .setX(viewport.rect.GetX()). vk::Viewport() default-initializes x to 0.0f, so the user's X is silently dropped. Y is also effectively dropped: the code calls setY(viewport.rect.GetHeight()) for the negative-height Y-flip, never reading viewport.rect.GetY().

This has been present since e7be989feb1c ("Reland: Encode directly to command buffer.", Jonah Williams, 2024-01-19).

Repro

The Can render portion of the triangle using viewport test in engine/src/flutter/testing/dart/gpu_test.dart exercises this:

final RenderPassState state = createSimpleRenderPass();  // 100x100 render texture
state.renderPass.setViewport(gpu.Viewport(x: 25, width: 50, height: 100));
// ... draw a triangle with NDC vertices (-1,-1), (0,1), (1,-1)

The triangle should fill the middle 50px column of the texture (apex at x=50, base from x=25 to x=75). On OpenGLES and Metal, that is what renders. On Vulkan the triangle renders at x=0..50, shifted left by exactly the requested 25px X offset.

The existing Vulkan golden in Skia Gold for flutter_gpu_test_viewport.png (hash ab61456e8267e29b58d9101be5692480) shows the buggy left-shifted output. The OpenGLES output (hash 80e8823fc0e56785a5c29672186ac9b0) is the correct centered render.

How this stayed hidden

Fix

Add the two missing setters in RenderPassVK::SetViewport:

.setX(viewport.rect.GetX())
.setY(viewport.rect.GetY() + viewport.rect.GetHeight())  // for the negative-height Y-flip

Metadata

Metadata

Assignees

Labels

e: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.flutter-gputeam-fluttergpuOwned by Flutter GPU team

Type

No type
No fields configured for issues without a type.

Projects

Status
✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions