Added support to set viewport (#162602)

Resolves #157201.
This commit is contained in:
AthulJoseph 2025-02-07 00:37:01 +05:30 committed by GitHub
parent c783ce2344
commit f6b0598945
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 158 additions and 0 deletions

View File

@ -168,6 +168,34 @@ base class Scissor {
}
}
base class DepthRange {
DepthRange({this.zNear = 0.0, this.zFar = 1.0});
double zNear;
double zFar;
}
base class Viewport {
Viewport({
this.x = 0,
this.y = 0,
this.width = 0,
this.height = 0,
DepthRange? depthRange = null,
}) : this.depthRange = depthRange ?? DepthRange();
int x, y, width, height;
DepthRange depthRange;
void _validate() {
if (x < 0 || y < 0 || width < 0 || height < 0) {
throw Exception(
"Invalid values for viewport. All values should be positive.",
);
}
}
}
base class RenderTarget {
const RenderTarget({
this.colorAttachments = const <ColorAttachment>[],
@ -346,6 +374,21 @@ base class RenderPass extends NativeFieldWrapperClass1 {
_setDepthWriteEnable(enable);
}
void setViewport(Viewport viewport) {
assert(() {
viewport._validate();
return true;
}());
_setViewport(
viewport.x,
viewport.y,
viewport.width,
viewport.height,
viewport.depthRange.zNear,
viewport.depthRange.zFar,
);
}
void setDepthCompareOperation(CompareFunction compareFunction) {
_setDepthCompareOperation(compareFunction.index);
}
@ -508,6 +551,18 @@ base class RenderPass extends NativeFieldWrapperClass1 {
int lengthInBytes,
);
@Native<Void Function(Pointer<Void>, Int, Int, Int, Int, Float, Float)>(
symbol: 'InternalFlutterGpu_RenderPass_SetViewport',
)
external void _setViewport(
int x,
int y,
int width,
int height,
double depthRangeZNear,
double depthRangeZFar,
);
@Native<
Bool Function(
Pointer<Void>,

View File

@ -218,6 +218,10 @@ bool RenderPass::Draw() {
render_pass_->SetStencilReference(stencil_reference);
if (viewport.has_value()) {
render_pass_->SetViewport(viewport.value());
}
if (scissor.has_value()) {
render_pass_->SetScissor(scissor.value());
}
@ -559,6 +563,27 @@ void InternalFlutterGpu_RenderPass_SetScissor(flutter::gpu::RenderPass* wrapper,
wrapper->scissor = impeller::TRect<int64_t>::MakeXYWH(x, y, width, height);
}
void InternalFlutterGpu_RenderPass_SetViewport(
flutter::gpu::RenderPass* wrapper,
int x,
int y,
int width,
int height,
float z_near,
float z_far) {
auto rect = impeller::TRect<float>::MakeXYWH(x, y, width, height);
auto depth_range = impeller::DepthRange();
depth_range.z_near = z_near;
depth_range.z_far = z_far;
auto viewport = impeller::Viewport();
viewport.rect = rect;
viewport.depth_range = depth_range;
wrapper->viewport = viewport;
}
void InternalFlutterGpu_RenderPass_SetStencilConfig(
flutter::gpu::RenderPass* wrapper,
int stencil_compare_operation,

View File

@ -81,6 +81,7 @@ class RenderPass : public RefCountedDartWrappable<RenderPass> {
uint32_t stencil_reference = 0;
std::optional<impeller::TRect<int64_t>> scissor;
std::optional<impeller::Viewport> viewport;
// Helper flag to determine whether the vertex_count should override the
// element count. The index count takes precedent.
@ -249,6 +250,16 @@ extern void InternalFlutterGpu_RenderPass_SetScissor(
int width,
int height);
FLUTTER_GPU_EXPORT
extern void InternalFlutterGpu_RenderPass_SetViewport(
flutter::gpu::RenderPass* wrapper,
int x,
int y,
int width,
int height,
float z_near,
float z_far);
FLUTTER_GPU_EXPORT
extern void InternalFlutterGpu_RenderPass_SetCullMode(
flutter::gpu::RenderPass* wrapper,

View File

@ -810,4 +810,71 @@ void main() async {
expect(e.toString(), contains('Invalid values for scissor. All values should be positive.'));
}
}, skip: !impellerEnabled);
test('RenderPass.setViewport doesnt throw for valid values', () async {
final state = createSimpleRenderPass();
state.renderPass.setViewport(gpu.Viewport(x: 25, width: 50, height: 100));
state.renderPass.setViewport(gpu.Viewport(width: 50, height: 100));
}, skip: !impellerEnabled);
test('RenderPass.setViewport throws for invalid values', () async {
final state = createSimpleRenderPass();
try {
state.renderPass.setViewport(gpu.Viewport(x: -1, width: 50, height: 100));
fail('Exception not thrown for invalid viewport.');
} catch (e) {
expect(e.toString(), contains('Invalid values for viewport. All values should be positive.'));
}
try {
state.renderPass.setViewport(gpu.Viewport(width: 50, height: -100));
fail('Exception not thrown for invalid viewport.');
} catch (e) {
expect(e.toString(), contains('Invalid values for viewport. All values should be positive.'));
}
}, skip: !impellerEnabled);
// Renders the middle part triangle using viewport.
test('Can render portion of the triangle using viewport', () async {
final state = createSimpleRenderPass();
final gpu.RenderPipeline pipeline = createUnlitRenderPipeline();
state.renderPass.bindPipeline(pipeline);
// Configure blending with defaults (just to test the bindings).
state.renderPass.setColorBlendEnable(true);
state.renderPass.setColorBlendEquation(gpu.ColorBlendEquation());
// Set primitive type.
state.renderPass.setPrimitiveType(gpu.PrimitiveType.triangle);
// Set viewport.
state.renderPass.setViewport(gpu.Viewport(x: 25, width: 50, height: 100));
final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer();
final gpu.BufferView vertices = transients.emplace(
float32(<double>[-1.0, -1.0, 0.0, 1.0, 1.0, -1.0]),
);
final gpu.BufferView vertInfoData = transients.emplace(
float32(<double>[
1, 0, 0, 0, // mvp
0, 1, 0, 0, // mvp
0, 0, 1, 0, // mvp
0, 0, 0, 1, // mvp
0, 1, 0, 1, // color
]),
);
state.renderPass.bindVertexBuffer(vertices, 3);
final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo');
state.renderPass.bindUniform(vertInfo, vertInfoData);
state.renderPass.draw();
state.commandBuffer.submit();
final ui.Image image = state.renderTexture.asImage();
await comparer.addGoldenImage(image, 'flutter_gpu_test_viewport.png');
}, skip: !impellerEnabled);
}