[Flutter GPU] Add CullMode. (flutter/engine#55409)

Part of https://github.com/flutter/flutter/issues/155636.

New golden `flutter_gpu_test_cull_mode` will draw a red triangle if the CullMode isn't working:
![flutter_gpu_test_cull_mode](https://github.com/user-attachments/assets/cbdf804e-1608-4352-9aa1-d5d9223f3c1a)
This commit is contained in:
Brandon DeRosier 2024-09-24 17:52:11 -07:00 committed by GitHub
parent 9f1cee211c
commit e496b1ed26
6 changed files with 108 additions and 0 deletions

View File

@ -501,6 +501,27 @@ constexpr impeller::StencilOperation ToImpellerStencilOperation(int value) {
static_cast<FlutterGPUStencilOperation>(value));
}
enum class FlutterGPUCullMode {
kNone,
kFrontFace,
kBackFace,
};
constexpr impeller::CullMode ToImpellerCullMode(FlutterGPUCullMode value) {
switch (value) {
case FlutterGPUCullMode::kNone:
return impeller::CullMode::kNone;
case FlutterGPUCullMode::kFrontFace:
return impeller::CullMode::kFrontFace;
case FlutterGPUCullMode::kBackFace:
return impeller::CullMode::kBackFace;
}
}
constexpr impeller::CullMode ToImpellerCullMode(int value) {
return ToImpellerCullMode(static_cast<FlutterGPUCullMode>(value));
}
} // namespace gpu
} // namespace flutter

View File

@ -135,6 +135,12 @@ enum PrimitiveType {
point,
}
enum CullMode {
none,
frontFace,
backFace,
}
enum CompareFunction {
/// Comparison test never passes.
never,

View File

@ -264,6 +264,10 @@ base class RenderPass extends NativeFieldWrapperClass1 {
targetFace.index);
}
void setCullMode(CullMode cullMode) {
_setCullMode(cullMode.index);
}
void draw() {
if (!_draw()) {
throw Exception("Failed to append draw");
@ -402,6 +406,10 @@ base class RenderPass extends NativeFieldWrapperClass1 {
int writeMask,
int target_face);
@Native<Void Function(Pointer<Void>, Int)>(
symbol: 'InternalFlutterGpu_RenderPass_SetCullMode')
external void _setCullMode(int cullMode);
@Native<Bool Function(Pointer<Void>)>(
symbol: 'InternalFlutterGpu_RenderPass_Draw')
external bool _draw();

View File

@ -76,6 +76,10 @@ impeller::VertexBuffer& RenderPass::GetVertexBuffer() {
return vertex_buffer_;
}
impeller::PipelineDescriptor& RenderPass::GetPipelineDescriptor() {
return pipeline_descriptor_;
}
bool RenderPass::Begin(flutter::gpu::CommandBuffer& command_buffer) {
render_pass_ =
command_buffer.GetCommandBuffer()->CreateRenderPass(render_target_);
@ -558,6 +562,14 @@ void InternalFlutterGpu_RenderPass_SetStencilConfig(
}
}
void InternalFlutterGpu_RenderPass_SetCullMode(
flutter::gpu::RenderPass* wrapper,
int cull_mode) {
impeller::PipelineDescriptor& pipeline_descriptor =
wrapper->GetPipelineDescriptor();
pipeline_descriptor.SetCullMode(flutter::gpu::ToImpellerCullMode(cull_mode));
}
bool InternalFlutterGpu_RenderPass_Draw(flutter::gpu::RenderPass* wrapper) {
return wrapper->Draw();
}

View File

@ -52,6 +52,8 @@ class RenderPass : public RefCountedDartWrappable<RenderPass> {
impeller::VertexBuffer& GetVertexBuffer();
impeller::PipelineDescriptor& GetPipelineDescriptor();
bool Begin(flutter::gpu::CommandBuffer& command_buffer);
void SetPipeline(fml::RefPtr<RenderPipeline> pipeline);
@ -241,6 +243,11 @@ extern void InternalFlutterGpu_RenderPass_SetStencilConfig(
int write_mask,
int target);
FLUTTER_GPU_EXPORT
extern void InternalFlutterGpu_RenderPass_SetCullMode(
flutter::gpu::RenderPass* wrapper,
int cull_mode);
FLUTTER_GPU_EXPORT
extern bool InternalFlutterGpu_RenderPass_Draw(
flutter::gpu::RenderPass* wrapper);

View File

@ -11,6 +11,7 @@ import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:test/test.dart';
import 'package:vector_math/vector_math.dart';
import '../../lib/gpu/lib/gpu.dart' as gpu;
@ -21,6 +22,16 @@ ByteData float32(List<double> values) {
return Float32List.fromList(values).buffer.asByteData();
}
ByteData unlitUBO(Matrix4 mvp, Vector4 color) {
return float32(<double>[
mvp[0], mvp[1], mvp[2], mvp[3], //
mvp[4], mvp[5], mvp[6], mvp[7], //
mvp[8], mvp[9], mvp[10], mvp[11], //
mvp[12], mvp[13], mvp[14], mvp[15], //
color.r, color.g, color.b, color.a,
]);
}
gpu.RenderPipeline createUnlitRenderPipeline() {
final gpu.ShaderLibrary? library =
gpu.ShaderLibrary.fromAsset('test.shaderbundle');
@ -425,4 +436,47 @@ void main() async {
await comparer.addGoldenImage(
image, 'flutter_gpu_test_triangle_stencil.png');
}, skip: !impellerEnabled);
test('Drawing respects cull mode', () async {
final state = createSimpleRenderPass();
final gpu.RenderPipeline pipeline = createUnlitRenderPipeline();
state.renderPass.bindPipeline(pipeline);
state.renderPass.setColorBlendEnable(true);
state.renderPass.setColorBlendEquation(gpu.ColorBlendEquation());
final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer();
// Counter-clockwise triangle.
final List<double> triangle = [
-0.5, 0.5, //
0.0, -0.5, //
0.5, 0.5, //
];
final gpu.BufferView vertices = transients.emplace(float32(triangle));
void drawTriangle(Vector4 color) {
final gpu.BufferView vertInfoUboFront =
transients.emplace(unlitUBO(Matrix4.identity(), color));
final gpu.UniformSlot vertInfo =
pipeline.vertexShader.getUniformSlot('VertInfo');
// TODO(bdero): Overwrite bindings with the same slot so we don't need to clear.
// https://github.com/flutter/flutter/issues/155335
state.renderPass.clearBindings();
state.renderPass.bindVertexBuffer(vertices, 3);
state.renderPass.bindUniform(vertInfo, vertInfoUboFront);
state.renderPass.draw();
}
state.renderPass.setCullMode(gpu.CullMode.frontFace);
drawTriangle(Colors.lime);
state.renderPass.setCullMode(gpu.CullMode.backFace);
drawTriangle(Colors.red);
state.commandBuffer.submit();
final ui.Image image = state.renderTexture.asImage();
await comparer.addGoldenImage(image, 'flutter_gpu_test_cull_mode.png');
}, skip: !impellerEnabled);
}