[Flutter GPU] Add WindingOrder. (#55413)

Resolves https://github.com/flutter/flutter/issues/155636.

(Extends test in https://github.com/flutter/engine/pull/55409)
diff --git a/lib/gpu/formats.h b/lib/gpu/formats.h
index a70d608..cd9ac85 100644
--- a/lib/gpu/formats.h
+++ b/lib/gpu/formats.h
@@ -522,6 +522,25 @@
   return ToImpellerCullMode(static_cast<FlutterGPUCullMode>(value));
 }
 
+enum class FlutterGPUWindingOrder {
+  kClockwise,
+  kCounterClockwise,
+};
+
+constexpr impeller::WindingOrder ToImpellerWindingOrder(
+    FlutterGPUWindingOrder value) {
+  switch (value) {
+    case FlutterGPUWindingOrder::kClockwise:
+      return impeller::WindingOrder::kClockwise;
+    case FlutterGPUWindingOrder::kCounterClockwise:
+      return impeller::WindingOrder::kCounterClockwise;
+  }
+}
+
+constexpr impeller::WindingOrder ToImpellerWindingOrder(int value) {
+  return ToImpellerWindingOrder(static_cast<FlutterGPUWindingOrder>(value));
+}
+
 }  // namespace gpu
 }  // namespace flutter
 
diff --git a/lib/gpu/lib/src/formats.dart b/lib/gpu/lib/src/formats.dart
index 2bd1b5d..ec6e703 100644
--- a/lib/gpu/lib/src/formats.dart
+++ b/lib/gpu/lib/src/formats.dart
@@ -141,6 +141,11 @@
   backFace,
 }
 
+enum WindingOrder {
+  clockwise,
+  counterClockwise,
+}
+
 enum CompareFunction {
   /// Comparison test never passes.
   never,
diff --git a/lib/gpu/lib/src/render_pass.dart b/lib/gpu/lib/src/render_pass.dart
index 18718bb..f690210 100644
--- a/lib/gpu/lib/src/render_pass.dart
+++ b/lib/gpu/lib/src/render_pass.dart
@@ -262,6 +262,10 @@
     _setCullMode(cullMode.index);
   }
 
+  void setWindingOrder(WindingOrder windingOrder) {
+    _setWindingOrder(windingOrder.index);
+  }
+
   void draw() {
     if (!_draw()) {
       throw Exception("Failed to append draw");
@@ -416,6 +420,10 @@
       symbol: 'InternalFlutterGpu_RenderPass_SetCullMode')
   external void _setCullMode(int cullMode);
 
+  @Native<Void Function(Pointer<Void>, Int)>(
+      symbol: 'InternalFlutterGpu_RenderPass_SetWindingOrder')
+  external void _setWindingOrder(int windingOrder);
+
   @Native<Bool Function(Pointer<Void>)>(
       symbol: 'InternalFlutterGpu_RenderPass_Draw')
   external bool _draw();
diff --git a/lib/gpu/render_pass.cc b/lib/gpu/render_pass.cc
index 69eec79..26fcf54 100644
--- a/lib/gpu/render_pass.cc
+++ b/lib/gpu/render_pass.cc
@@ -567,6 +567,15 @@
   pipeline_descriptor.SetCullMode(flutter::gpu::ToImpellerCullMode(cull_mode));
 }
 
+void InternalFlutterGpu_RenderPass_SetWindingOrder(
+    flutter::gpu::RenderPass* wrapper,
+    int winding_order) {
+  impeller::PipelineDescriptor& pipeline_descriptor =
+      wrapper->GetPipelineDescriptor();
+  pipeline_descriptor.SetWindingOrder(
+      flutter::gpu::ToImpellerWindingOrder(winding_order));
+}
+
 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 94f6627..4069d37 100644
--- a/lib/gpu/render_pass.h
+++ b/lib/gpu/render_pass.h
@@ -252,6 +252,11 @@
     int cull_mode);
 
 FLUTTER_GPU_EXPORT
+extern void InternalFlutterGpu_RenderPass_SetWindingOrder(
+    flutter::gpu::RenderPass* wrapper,
+    int winding_order);
+
+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 05ba001..e8ead29 100644
--- a/testing/dart/gpu_test.dart
+++ b/testing/dart/gpu_test.dart
@@ -479,11 +479,21 @@
       state.renderPass.draw();
     }
 
+    // Draw the green rectangle.
+    // Defaults to clockwise winding order. So frontface culling should not
+    // impact the green triangle.
     state.renderPass.setCullMode(gpu.CullMode.frontFace);
     drawTriangle(Colors.lime);
+
+    // Backface cull a red triangle.
     state.renderPass.setCullMode(gpu.CullMode.backFace);
     drawTriangle(Colors.red);
 
+    // Invert the winding mode and frontface cull a red rectangle.
+    state.renderPass.setWindingOrder(gpu.WindingOrder.counterClockwise);
+    state.renderPass.setCullMode(gpu.CullMode.frontFace);
+    drawTriangle(Colors.red);
+
     state.commandBuffer.submit();
 
     final ui.Image image = state.renderTexture.asImage();