diff --git a/lib/gpu/lib/src/render_pass.dart b/lib/gpu/lib/src/render_pass.dart index c2f712c001b6a..7f60d100a1635 100644 --- a/lib/gpu/lib/src/render_pass.dart +++ b/lib/gpu/lib/src/render_pass.dart @@ -212,6 +212,14 @@ base class RenderPass extends NativeFieldWrapperClass1 { _setDepthCompareOperation(compareFunction.index); } + void setStencilReference(int referenceValue) { + if (referenceValue < 0 || referenceValue > 0xFFFFFFFF) { + throw Exception( + "The stencil reference value must be in the range [0, 2^32 - 1]"); + } + _setStencilReference(referenceValue); + } + void draw() { if (!_draw()) { throw Exception("Failed to append draw"); @@ -335,6 +343,10 @@ base class RenderPass extends NativeFieldWrapperClass1 { symbol: 'InternalFlutterGpu_RenderPass_SetDepthCompareOperation') external void _setDepthCompareOperation(int compareOperation); + @Native, Int)>( + symbol: 'InternalFlutterGpu_RenderPass_SetStencilReference') + external void _setStencilReference(int referenceValue); + @Native)>( symbol: 'InternalFlutterGpu_RenderPass_Draw') external bool _draw(); diff --git a/lib/gpu/render_pass.cc b/lib/gpu/render_pass.cc index 1bbbb92108bc4..052cc66f56e9f 100644 --- a/lib/gpu/render_pass.cc +++ b/lib/gpu/render_pass.cc @@ -511,6 +511,13 @@ void InternalFlutterGpu_RenderPass_SetDepthCompareOperation( flutter::gpu::ToImpellerCompareFunction(compare_operation); } +void InternalFlutterGpu_RenderPass_SetStencilReference( + flutter::gpu::RenderPass* wrapper, + int stencil_reference) { + auto& command = wrapper->GetCommand(); + command.stencil_reference = static_cast(stencil_reference); +} + bool InternalFlutterGpu_RenderPass_Draw(flutter::gpu::RenderPass* wrapper) { return wrapper->Draw(); } diff --git a/lib/gpu/render_pass.h b/lib/gpu/render_pass.h index 94b7d5faebcca..2d8b099a62d90 100644 --- a/lib/gpu/render_pass.h +++ b/lib/gpu/render_pass.h @@ -221,6 +221,11 @@ extern void InternalFlutterGpu_RenderPass_SetDepthCompareOperation( flutter::gpu::RenderPass* wrapper, int compare_operation); +FLUTTER_GPU_EXPORT +extern void InternalFlutterGpu_RenderPass_SetStencilReference( + flutter::gpu::RenderPass* wrapper, + int stencil_reference); + FLUTTER_GPU_EXPORT extern bool InternalFlutterGpu_RenderPass_Draw( flutter::gpu::RenderPass* wrapper); diff --git a/testing/dart/gpu_test.dart b/testing/dart/gpu_test.dart index c35b554378aa9..f9d6e601e010a 100644 --- a/testing/dart/gpu_test.dart +++ b/testing/dart/gpu_test.dart @@ -32,6 +32,37 @@ gpu.RenderPipeline createUnlitRenderPipeline() { return gpu.gpuContext.createRenderPipeline(vertex!, fragment!); } +class RenderPassState { + RenderPassState(this.renderTexture, this.commandBuffer, this.renderPass); + + final gpu.Texture renderTexture; + final gpu.CommandBuffer commandBuffer; + final gpu.RenderPass renderPass; +} + +/// Create a simple RenderPass with simple color and depth-stencil attachments. +RenderPassState createSimpleRenderPass() { + final gpu.Texture? renderTexture = + gpu.gpuContext.createTexture(gpu.StorageMode.devicePrivate, 100, 100); + assert(renderTexture != null); + + final gpu.Texture? depthStencilTexture = gpu.gpuContext.createTexture( + gpu.StorageMode.deviceTransient, 100, 100, + format: gpu.gpuContext.defaultDepthStencilFormat); + + final gpu.CommandBuffer commandBuffer = gpu.gpuContext.createCommandBuffer(); + + final gpu.RenderTarget renderTarget = gpu.RenderTarget.singleColor( + gpu.ColorAttachment(texture: renderTexture!), + depthStencilAttachment: + gpu.DepthStencilAttachment(texture: depthStencilTexture!)); + + final gpu.RenderPass renderPass = + commandBuffer.createRenderPass(renderTarget); + + return RenderPassState(renderTexture, commandBuffer, renderPass); +} + void main() async { final ImageComparer comparer = await ImageComparer.create(); @@ -203,25 +234,44 @@ void main() async { } }, skip: !impellerEnabled); - test('Can render triangle', () async { - final gpu.Texture? renderTexture = - gpu.gpuContext.createTexture(gpu.StorageMode.devicePrivate, 100, 100); - assert(renderTexture != null); + test('RenderPass.setStencilReference doesnt throw for valid values', + () async { + final state = createSimpleRenderPass(); - final gpu.CommandBuffer commandBuffer = - gpu.gpuContext.createCommandBuffer(); + state.renderPass.setStencilReference(0); + state.renderPass.setStencilReference(2 << 30); + }, skip: !impellerEnabled); - final gpu.RenderTarget renderTarget = gpu.RenderTarget.singleColor( - gpu.ColorAttachment(texture: renderTexture!), - ); - final gpu.RenderPass encoder = commandBuffer.createRenderPass(renderTarget); + test('RenderPass.setStencilReference throws for invalid values', () async { + final state = createSimpleRenderPass(); + + try { + state.renderPass.setStencilReference(-1); + fail('Exception not thrown for out of bounds stencil reference.'); + } catch (e) { + expect(e.toString(), + contains('The stencil reference value must be in the range')); + } + + try { + state.renderPass.setStencilReference(2 << 31); + fail('Exception not thrown for out of bounds stencil reference.'); + } catch (e) { + expect(e.toString(), + contains('The stencil reference value must be in the range')); + } + }, skip: !impellerEnabled); + + // Renders a green triangle pointing downwards. + test('Can render triangle', () async { + final state = createSimpleRenderPass(); final gpu.RenderPipeline pipeline = createUnlitRenderPipeline(); - encoder.bindPipeline(pipeline); + state.renderPass.bindPipeline(pipeline); // Configure blending with defaults (just to test the bindings). - encoder.setColorBlendEnable(true); - encoder.setColorBlendEquation(gpu.ColorBlendEquation()); + state.renderPass.setColorBlendEnable(true); + state.renderPass.setColorBlendEquation(gpu.ColorBlendEquation()); final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer(); final gpu.BufferView vertices = transients.emplace(float32([ @@ -236,16 +286,16 @@ void main() async { 0, 0, 0, 1, // mvp 0, 1, 0, 1, // color ])); - encoder.bindVertexBuffer(vertices, 3); + state.renderPass.bindVertexBuffer(vertices, 3); final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); - encoder.bindUniform(vertInfo, vertInfoData); - encoder.draw(); + state.renderPass.bindUniform(vertInfo, vertInfoData); + state.renderPass.draw(); - commandBuffer.submit(); + state.commandBuffer.submit(); - final ui.Image image = renderTexture.asImage(); + final ui.Image image = state.renderTexture.asImage(); await comparer.addGoldenImage(image, 'flutter_gpu_test_triangle.png'); }, skip: !impellerEnabled); }