Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flutter GPU] Added support to set viewport. #57263

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions lib/gpu/lib/src/render_pass.dart
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,27 @@ 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>[],
Expand Down Expand Up @@ -346,6 +367,14 @@ base class RenderPass extends NativeFieldWrapperClass1 {
_setScissor(scissor.x, scissor.y, scissor.width, scissor.height);
}

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 setCullMode(CullMode cullMode) {
_setCullMode(cullMode.index);
}
Expand Down Expand Up @@ -506,6 +535,16 @@ base class RenderPass extends NativeFieldWrapperClass1 {
int width,
int height);

@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<Void Function(Pointer<Void>, Int)>(
symbol: 'InternalFlutterGpu_RenderPass_SetCullMode')
external void _setCullMode(int cullMode);
Expand Down
25 changes: 25 additions & 0 deletions lib/gpu/render_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 12 additions & 0 deletions lib/gpu/render_pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -250,6 +251,17 @@ extern void InternalFlutterGpu_RenderPass_SetScissor(
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,
int cull_mode);
Expand Down
75 changes: 75 additions & 0 deletions testing/dart/gpu_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -810,4 +810,79 @@ void main() async {
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);
}
Loading