Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
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
12 changes: 12 additions & 0 deletions lib/gpu/lib/src/render_pass.dart
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ base class RenderPass extends NativeFieldWrapperClass1 {
_setDepthCompareOperation(compareFunction.index);
}

void setStencilReference(int referenceValue) {
if (referenceValue < 0 || referenceValue > 0xFFFFFFFF) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in an assert? Not sure what the design philosophy is for validations in Flutter GPU.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm yeah... I agree stuff like this aught to be enforced with asserts instead. I'll go through and fix it everywhere as part of flutter/flutter#143891.

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");
Expand Down Expand Up @@ -335,6 +343,10 @@ base class RenderPass extends NativeFieldWrapperClass1 {
symbol: 'InternalFlutterGpu_RenderPass_SetDepthCompareOperation')
external void _setDepthCompareOperation(int compareOperation);

@Native<Void Function(Pointer<Void>, Int)>(
symbol: 'InternalFlutterGpu_RenderPass_SetStencilReference')
external void _setStencilReference(int referenceValue);

@Native<Bool Function(Pointer<Void>)>(
symbol: 'InternalFlutterGpu_RenderPass_Draw')
external bool _draw();
Expand Down
7 changes: 7 additions & 0 deletions lib/gpu/render_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint32_t>(stencil_reference);
}

bool InternalFlutterGpu_RenderPass_Draw(flutter::gpu::RenderPass* wrapper) {
return wrapper->Draw();
}
5 changes: 5 additions & 0 deletions lib/gpu/render_pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
86 changes: 68 additions & 18 deletions testing/dart/gpu_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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(<double>[
Expand All @@ -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);
}