From 7cb953142068e831943ffb488e57c11dbe1c677d Mon Sep 17 00:00:00 2001 From: Athul Joseph Date: Fri, 1 Nov 2024 17:55:30 +0530 Subject: [PATCH 1/8] Added support to set scissor. --- lib/gpu/lib/src/render_pass.dart | 21 +++++++++++++++ lib/gpu/render_pass.cc | 6 +++++ lib/gpu/render_pass.h | 9 +++++++ testing/dart/gpu_test.dart | 46 ++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) diff --git a/lib/gpu/lib/src/render_pass.dart b/lib/gpu/lib/src/render_pass.dart index 15389579b7f6c..62e042b974889 100644 --- a/lib/gpu/lib/src/render_pass.dart +++ b/lib/gpu/lib/src/render_pass.dart @@ -150,6 +150,12 @@ base class SamplerOptions { SamplerAddressMode heightAddressMode; } +base class Scissor { + Scissor({this.x = 0, this.y = 0, this.width = 0, this.height = 0}); + + int x, y, width, height; +} + base class RenderTarget { const RenderTarget( {this.colorAttachments = const [], @@ -214,6 +220,9 @@ base class RenderPass extends NativeFieldWrapperClass1 { if (error != null) { throw Exception(error); } + + final texture = renderTarget.depthStencilAttachment!.texture; + _setScissor(0, 0, texture.width, texture.height); } error = _begin(commandBuffer); if (error != null) { @@ -326,6 +335,10 @@ base class RenderPass extends NativeFieldWrapperClass1 { targetFace.index); } + void setScissor(Scissor scissor) { + _setScissor(scissor.x, scissor.y, scissor.width, scissor.height); + } + void setCullMode(CullMode cullMode) { _setCullMode(cullMode.index); } @@ -477,6 +490,14 @@ base class RenderPass extends NativeFieldWrapperClass1 { int readMask, int writeMask, int target_face); + + @Native, Int, Int, Int, Int)>( + symbol: 'InternalFlutterGpu_RenderPass_SetScissor') + external void _setScissor( + int x, + int y, + int width, + int height); @Native, Int)>( symbol: 'InternalFlutterGpu_RenderPass_SetCullMode') diff --git a/lib/gpu/render_pass.cc b/lib/gpu/render_pass.cc index 637f7446e5266..dc7f1a4850075 100644 --- a/lib/gpu/render_pass.cc +++ b/lib/gpu/render_pass.cc @@ -206,6 +206,7 @@ bool RenderPass::Draw() { render_pass_->SetElementCount(element_count); render_pass_->SetStencilReference(stencil_reference); + render_pass_->SetScissor(scissor); bool result = render_pass_->Draw().ok(); @@ -536,6 +537,11 @@ void InternalFlutterGpu_RenderPass_SetStencilReference( wrapper->stencil_reference = static_cast(stencil_reference); } +void InternalFlutterGpu_RenderPass_SetScissor( + flutter::gpu::RenderPass* wrapper, int x, int y, int width, int height) { + wrapper->scissor = impeller::TRect::MakeXYWH(x, y, width, height); +} + void InternalFlutterGpu_RenderPass_SetStencilConfig( flutter::gpu::RenderPass* wrapper, int stencil_compare_operation, diff --git a/lib/gpu/render_pass.h b/lib/gpu/render_pass.h index 1b33a5ebaef3d..1bb93d6155fff 100644 --- a/lib/gpu/render_pass.h +++ b/lib/gpu/render_pass.h @@ -74,6 +74,7 @@ class RenderPass : public RefCountedDartWrappable { size_t element_count = 0; uint32_t stencil_reference = 0; + impeller::TRect scissor; // Helper flag to determine whether the vertex_count should override the // element count. The index count takes precedent. @@ -234,6 +235,14 @@ extern void InternalFlutterGpu_RenderPass_SetStencilConfig( int write_mask, int target); +FLUTTER_GPU_EXPORT +extern void InternalFlutterGpu_RenderPass_SetScissor( + flutter::gpu::RenderPass* wrapper, + int x, + int y, + int width, + int height); + FLUTTER_GPU_EXPORT extern void InternalFlutterGpu_RenderPass_SetCullMode( flutter::gpu::RenderPass* wrapper, diff --git a/testing/dart/gpu_test.dart b/testing/dart/gpu_test.dart index c1d45183f9c79..21f4cb0880ef1 100644 --- a/testing/dart/gpu_test.dart +++ b/testing/dart/gpu_test.dart @@ -736,4 +736,50 @@ void main() async { await comparer.addGoldenImage( image, 'flutter_gpu_test_hexgon_line_strip.png'); }, skip: !impellerEnabled); + + // Renders the middle part triangle using scissor. + test('Can render portion of the triangle using scissor', () 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 scissor. + state.renderPass.setScissor(gpu.Scissor(x: 25, y: 0, width: 50, height: 100)); + + final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer(); + final gpu.BufferView vertices = transients.emplace(float32([ + -1.0, + -1.0, + 0.0, + 1.0, + 1.0, + -1.0])); + final gpu.BufferView vertInfoData = transients.emplace(float32([ + 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_scissor.png'); + }, skip: !impellerEnabled); } From 64f51e31ce496ff55657c78af754cfee0f6ff1d8 Mon Sep 17 00:00:00 2001 From: Athul Joseph Date: Fri, 1 Nov 2024 18:00:07 +0530 Subject: [PATCH 2/8] Added support to set scissor. --- lib/gpu/lib/src/render_pass.dart | 2 +- lib/gpu/render_pass.cc | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/gpu/lib/src/render_pass.dart b/lib/gpu/lib/src/render_pass.dart index 62e042b974889..34c67d88a72b4 100644 --- a/lib/gpu/lib/src/render_pass.dart +++ b/lib/gpu/lib/src/render_pass.dart @@ -490,7 +490,7 @@ base class RenderPass extends NativeFieldWrapperClass1 { int readMask, int writeMask, int target_face); - + @Native, Int, Int, Int, Int)>( symbol: 'InternalFlutterGpu_RenderPass_SetScissor') external void _setScissor( diff --git a/lib/gpu/render_pass.cc b/lib/gpu/render_pass.cc index dc7f1a4850075..5b9292d188c9d 100644 --- a/lib/gpu/render_pass.cc +++ b/lib/gpu/render_pass.cc @@ -537,8 +537,11 @@ void InternalFlutterGpu_RenderPass_SetStencilReference( wrapper->stencil_reference = static_cast(stencil_reference); } -void InternalFlutterGpu_RenderPass_SetScissor( - flutter::gpu::RenderPass* wrapper, int x, int y, int width, int height) { +void InternalFlutterGpu_RenderPass_SetScissor(flutter::gpu::RenderPass* wrapper, + int x, + int y, + int width, + int height) { wrapper->scissor = impeller::TRect::MakeXYWH(x, y, width, height); } From 6cb6c0b9ca731bb90d4852f39e12369cc3baecc0 Mon Sep 17 00:00:00 2001 From: AthulJoseph <55750689+AthulJoseph27@users.noreply.github.com> Date: Fri, 1 Nov 2024 21:24:24 +0530 Subject: [PATCH 3/8] Update gpu_test.dart --- testing/dart/gpu_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/dart/gpu_test.dart b/testing/dart/gpu_test.dart index 21f4cb0880ef1..b3e982fa77823 100644 --- a/testing/dart/gpu_test.dart +++ b/testing/dart/gpu_test.dart @@ -752,7 +752,7 @@ void main() async { state.renderPass.setPrimitiveType(gpu.PrimitiveType.triangle); // Set scissor. - state.renderPass.setScissor(gpu.Scissor(x: 25, y: 0, width: 50, height: 100)); + state.renderPass.setScissor(gpu.Scissor(x: 25, width: 50, height: 100)); final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer(); final gpu.BufferView vertices = transients.emplace(float32([ From 5f661528cfefb0611d0452afae4586929297e44c Mon Sep 17 00:00:00 2001 From: AthulJoseph <55750689+AthulJoseph27@users.noreply.github.com> Date: Wed, 13 Nov 2024 18:32:53 +0530 Subject: [PATCH 4/8] Update render_pass.dart --- lib/gpu/lib/src/render_pass.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/gpu/lib/src/render_pass.dart b/lib/gpu/lib/src/render_pass.dart index 1d3e0a164d050..6dd60ab548a85 100644 --- a/lib/gpu/lib/src/render_pass.dart +++ b/lib/gpu/lib/src/render_pass.dart @@ -339,7 +339,10 @@ base class RenderPass extends NativeFieldWrapperClass1 { } void setScissor(Scissor scissor) { - scissor._validate(); + assert(() { + scissor._validate(); + return true; + }()); _setScissor(scissor.x, scissor.y, scissor.width, scissor.height); } From 2d0d884971a780c4da6bdbba37267f99999e3b31 Mon Sep 17 00:00:00 2001 From: Athul Joseph Date: Wed, 20 Nov 2024 08:48:50 +0530 Subject: [PATCH 5/8] amend --- testing/dart/gpu_test.dart | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/testing/dart/gpu_test.dart b/testing/dart/gpu_test.dart index b3e982fa77823..1317a5fbb4f6e 100644 --- a/testing/dart/gpu_test.dart +++ b/testing/dart/gpu_test.dart @@ -782,4 +782,32 @@ void main() async { await comparer.addGoldenImage( image, 'flutter_gpu_test_scissor.png'); }, skip: !impellerEnabled); + + test('RenderPass.setScissor doesnt throw for valid values', + () async { + final state = createSimpleRenderPass(); + + state.renderPass.setScissor(gpu.Scissor(x: 25, width: 50, height: 100)); + state.renderPass.setScissor(gpu.Scissor(width: 50, height: 100)); + }, skip: !impellerEnabled); + + test('RenderPass.setScissor throws for invalid values', () async { + final state = createSimpleRenderPass(); + + try { + state.renderPass.setScissor(gpu.Scissor(x: -1, width: 50, height: 100)); + fail('Exception not thrown for invalid scissor.'); + } catch (e) { + expect(e.toString(), + contains('Invalid values for scissor. All values should be positive.')); + } + + try { + state.renderPass.setScissor(gpu.Scissor(width: 50, height: -100)); + fail('Exception not thrown for invalid scissor.'); + } catch (e) { + expect(e.toString(), + contains('Invalid values for scissor. All values should be positive.')); + } + }, skip: !impellerEnabled); } From 3bebecea8d02826317b34a3ff81aa6c46b14e0ea Mon Sep 17 00:00:00 2001 From: Athul Joseph Date: Wed, 25 Dec 2024 12:27:14 +0530 Subject: [PATCH 6/8] Added option to set viewport --- lib/gpu/lib/src/render_pass.dart | 39 ++++++++++++++++ lib/gpu/render_pass.cc | 24 ++++++++++ lib/gpu/render_pass.h | 11 +++++ testing/dart/gpu_test.dart | 77 +++++++++++++++++++++++++++++++- 4 files changed, 150 insertions(+), 1 deletion(-) diff --git a/lib/gpu/lib/src/render_pass.dart b/lib/gpu/lib/src/render_pass.dart index 6dd60ab548a85..0d5adb8f56258 100644 --- a/lib/gpu/lib/src/render_pass.dart +++ b/lib/gpu/lib/src/render_pass.dart @@ -162,6 +162,27 @@ base class Scissor { } } +base class DepthRange { + DepthRange({this.zNear = 0.0, this.zFar = 0.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 [], @@ -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); } @@ -505,6 +534,16 @@ base class RenderPass extends NativeFieldWrapperClass1 { int y, int width, int height); + + @Native, 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, Int)>( symbol: 'InternalFlutterGpu_RenderPass_SetCullMode') diff --git a/lib/gpu/render_pass.cc b/lib/gpu/render_pass.cc index 3987d823f5a22..3f55c0ef3875e 100644 --- a/lib/gpu/render_pass.cc +++ b/lib/gpu/render_pass.cc @@ -207,6 +207,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()); } @@ -548,6 +552,26 @@ void InternalFlutterGpu_RenderPass_SetScissor(flutter::gpu::RenderPass* wrapper, wrapper->scissor = impeller::TRect::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::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, diff --git a/lib/gpu/render_pass.h b/lib/gpu/render_pass.h index 08dd2ee902b4c..44ee5d7edaff9 100644 --- a/lib/gpu/render_pass.h +++ b/lib/gpu/render_pass.h @@ -75,6 +75,7 @@ class RenderPass : public RefCountedDartWrappable { uint32_t stencil_reference = 0; std::optional> scissor; + std::optional viewport; // Helper flag to determine whether the vertex_count should override the // element count. The index count takes precedent. @@ -243,6 +244,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, diff --git a/testing/dart/gpu_test.dart b/testing/dart/gpu_test.dart index 1317a5fbb4f6e..79599759469e6 100644 --- a/testing/dart/gpu_test.dart +++ b/testing/dart/gpu_test.dart @@ -783,7 +783,7 @@ void main() async { image, 'flutter_gpu_test_scissor.png'); }, skip: !impellerEnabled); - test('RenderPass.setScissor doesnt throw for valid values', + test('RenderPass.setScissor doesnt throw for valid values', () async { final state = createSimpleRenderPass(); @@ -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([ + -1.0, + -1.0, + 0.0, + 1.0, + 1.0, + -1.0])); + final gpu.BufferView vertInfoData = transients.emplace(float32([ + 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); } From abaf554c24555e5c3eb98e0256fc9599f7fdd72e Mon Sep 17 00:00:00 2001 From: Athul Joseph Date: Wed, 25 Dec 2024 12:29:22 +0530 Subject: [PATCH 7/8] Added option to set viewport --- lib/gpu/lib/src/render_pass.dart | 2 +- lib/gpu/render_pass.cc | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/gpu/lib/src/render_pass.dart b/lib/gpu/lib/src/render_pass.dart index 0d5adb8f56258..383abd1674a12 100644 --- a/lib/gpu/lib/src/render_pass.dart +++ b/lib/gpu/lib/src/render_pass.dart @@ -534,7 +534,7 @@ base class RenderPass extends NativeFieldWrapperClass1 { int y, int width, int height); - + @Native, Int, Int, Int, Int, Float, Float)>( symbol: 'InternalFlutterGpu_RenderPass_SetViewport') external void _setViewport( diff --git a/lib/gpu/render_pass.cc b/lib/gpu/render_pass.cc index 3f55c0ef3875e..94b9ede82290c 100644 --- a/lib/gpu/render_pass.cc +++ b/lib/gpu/render_pass.cc @@ -552,18 +552,19 @@ void InternalFlutterGpu_RenderPass_SetScissor(flutter::gpu::RenderPass* wrapper, wrapper->scissor = impeller::TRect::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) { +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::MakeXYWH(x, y, width, height); auto depth_range = impeller::DepthRange(); depth_range.z_near = z_near; - depth_range.z_far = z_far; + depth_range.z_far = z_far; auto viewport = impeller::Viewport(); viewport.rect = rect; From 29e214cba08fd873fc30b62d64c24baa30307853 Mon Sep 17 00:00:00 2001 From: Athul Joseph Date: Wed, 25 Dec 2024 12:43:58 +0530 Subject: [PATCH 8/8] Update branch --- lib/gpu/lib/src/render_pass.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpu/lib/src/render_pass.dart b/lib/gpu/lib/src/render_pass.dart index 383abd1674a12..92e498eb55346 100644 --- a/lib/gpu/lib/src/render_pass.dart +++ b/lib/gpu/lib/src/render_pass.dart @@ -163,7 +163,7 @@ base class Scissor { } base class DepthRange { - DepthRange({this.zNear = 0.0, this.zFar = 0.0}); + DepthRange({this.zNear = 0.0, this.zFar = 1.0}); double zNear; double zFar;