create static method

GpuRenderPipeline create({
  1. required GpuShader vertexShader,
  2. required GpuShader fragmentShader,
  3. required List<VertexBufferLayout> bufferLayouts,
  4. BlendMode blendMode = BlendMode.opaque,
  5. bool enableDepth = false,
  6. WGPUTextureFormat depthFormat = WGPUTextureFormat.WGPUTextureFormat_Depth24Plus,
  7. String vertexEntryPoint = "main",
  8. String fragmentEntryPoint = "main",
  9. int sampleCount = 1,
  10. WGPUTextureFormat? targetFormat,
  11. WGPUPrimitiveTopology topology = WGPUPrimitiveTopology.WGPUPrimitiveTopology_TriangleStrip,
  12. WGPUCullMode cullMode = WGPUCullMode.WGPUCullMode_None,
  13. WGPUFrontFace frontFace = WGPUFrontFace.WGPUFrontFace_CCW,
})

Implementation

static GpuRenderPipeline create({
  required GpuShader vertexShader,
  required GpuShader fragmentShader,
  required List<VertexBufferLayout> bufferLayouts,
  BlendMode blendMode = BlendMode.opaque,
  bool enableDepth = false,
  WGPUTextureFormat depthFormat =
      WGPUTextureFormat.WGPUTextureFormat_Depth24Plus,
  String vertexEntryPoint = "main",
  String fragmentEntryPoint = "main",
  int sampleCount = 1,
  WGPUTextureFormat? targetFormat,
  WGPUPrimitiveTopology topology =
      WGPUPrimitiveTopology.WGPUPrimitiveTopology_TriangleStrip,
  WGPUCullMode cullMode = WGPUCullMode.WGPUCullMode_None,
  WGPUFrontFace frontFace = WGPUFrontFace.WGPUFrontFace_CCW,
}) {
  final wgpu = WebgpuRend.instance.wgpu;
  final format = targetFormat ?? kPreferredTextureFormat;

  return using((arena) {
    // Vertex State Setup
    final vertexState = arena<WGPUVertexState>();
    vertexState.ref.module = vertexShader.handle.cast();
    vertexState.ref.entryPoint = _createStringView(arena, vertexEntryPoint);
    vertexState.ref.constantCount = 0;

    if (bufferLayouts.isNotEmpty) {
      final layouts = arena<WGPUVertexBufferLayout>(bufferLayouts.length);
      int totalAttrs = 0;
      for (var l in bufferLayouts) {
        totalAttrs += l.attributes.length;
      }
      final attrs = arena<WGPUVertexAttribute>(totalAttrs);

      int attrIdx = 0;

      for (int i = 0; i < bufferLayouts.length; i++) {
        final def = bufferLayouts[i];
        final layout = layouts.elementAt(i);
        layout.ref.arrayStride = def.arrayStride;
        layout.ref.stepMode = def.stepMode;
        layout.ref.attributeCount = def.attributes.length;
        layout.ref.attributes = attrs.elementAt(attrIdx);

        for (int j = 0; j < def.attributes.length; j++) {
          final dartAttr = def.attributes[j];
          final nativeAttr = attrs.elementAt(attrIdx + j);
          nativeAttr.ref.format = dartAttr.format;
          nativeAttr.ref.offset = dartAttr.offset;
          nativeAttr.ref.shaderLocation = dartAttr.shaderLocation;
        }
        attrIdx += def.attributes.length;
      }
      vertexState.ref.bufferCount = bufferLayouts.length;
      vertexState.ref.buffers = layouts;
    } else {
      vertexState.ref.bufferCount = 0;
      vertexState.ref.buffers = nullptr;
    }

    // Fragment State Setup
    final fragmentState = arena<WGPUFragmentState>();
    fragmentState.ref.module = fragmentShader.handle.cast();
    fragmentState.ref.entryPoint = _createStringView(arena, fragmentEntryPoint);
    fragmentState.ref.constantCount = 0;
    fragmentState.ref.targetCount = 1;

    final target = arena<WGPUColorTargetState>();
    target.ref.format = format;
    target.ref.writeMask = WGPUColorWriteMask_All;

    if (blendMode == BlendMode.opaque) {
      target.ref.blend = nullptr;
    } else {
      final blend = arena<WGPUBlendState>();

      // Default assignments
      var srcFactorColor = WGPUBlendFactor.WGPUBlendFactor_One;
      var dstFactorColor = WGPUBlendFactor.WGPUBlendFactor_Zero;
      var opColor = WGPUBlendOperation.WGPUBlendOperation_Add;

      var srcFactorAlpha = WGPUBlendFactor.WGPUBlendFactor_One;
      var dstFactorAlpha = WGPUBlendFactor.WGPUBlendFactor_Zero;
      var opAlpha = WGPUBlendOperation.WGPUBlendOperation_Add;

      switch (blendMode) {
        case BlendMode.alpha:
          // Final = (Src * SrcAlpha) + (Dst * (1 - SrcAlpha))
          srcFactorColor = WGPUBlendFactor.WGPUBlendFactor_SrcAlpha;
          dstFactorColor = WGPUBlendFactor.WGPUBlendFactor_OneMinusSrcAlpha;
          srcFactorAlpha = WGPUBlendFactor.WGPUBlendFactor_One;
          dstFactorAlpha = WGPUBlendFactor.WGPUBlendFactor_OneMinusSrcAlpha;
          break;

        case BlendMode.add:
          // Final = Src + Dst
          srcFactorColor = WGPUBlendFactor.WGPUBlendFactor_One;
          dstFactorColor = WGPUBlendFactor.WGPUBlendFactor_One;
          srcFactorAlpha = WGPUBlendFactor.WGPUBlendFactor_One;
          dstFactorAlpha = WGPUBlendFactor.WGPUBlendFactor_One;
          break;

        case BlendMode.max:
          // Final = Max(Src, Dst) - Factors are ignored in Max/Min
          opColor = WGPUBlendOperation.WGPUBlendOperation_Max;
          opAlpha = WGPUBlendOperation.WGPUBlendOperation_Max;
          break;

        case BlendMode.min:
          // Final = Min(Src, Dst)
          opColor = WGPUBlendOperation.WGPUBlendOperation_Min;
          opAlpha = WGPUBlendOperation.WGPUBlendOperation_Min;
          break;

        case BlendMode.erase:
          // Final = Dst * (1 - SrcAlpha)
          srcFactorColor = WGPUBlendFactor.WGPUBlendFactor_Zero;
          dstFactorColor = WGPUBlendFactor.WGPUBlendFactor_OneMinusSrcAlpha;
          srcFactorAlpha = WGPUBlendFactor.WGPUBlendFactor_Zero;
          dstFactorAlpha = WGPUBlendFactor.WGPUBlendFactor_OneMinusSrcAlpha;
          break;

        case BlendMode.opaque:
          break; // Handled above
      }

      blend.ref.color.srcFactor = srcFactorColor;
      blend.ref.color.dstFactor = dstFactorColor;
      blend.ref.color.operation = opColor;
      blend.ref.alpha.srcFactor = srcFactorAlpha;
      blend.ref.alpha.dstFactor = dstFactorAlpha;
      blend.ref.alpha.operation = opAlpha;

      target.ref.blend = blend;
    }

    fragmentState.ref.targets = target;

    // Pipeline Descriptor
    final desc = arena<WGPURenderPipelineDescriptor>();
    desc.ref.label.data = nullptr;
    desc.ref.label.length = 0;
    desc.ref.layout = nullptr;
    desc.ref.vertex = vertexState.ref;
    desc.ref.fragment = fragmentState;

    desc.ref.primitive.topology = topology;
    desc.ref.primitive.stripIndexFormat = WGPUIndexFormat.WGPUIndexFormat_Undefined;
    desc.ref.primitive.frontFace = frontFace;
    desc.ref.primitive.cullMode = cullMode;

    if (enableDepth) {
      final ds = arena<WGPUDepthStencilState>();
      ds.ref.format = depthFormat;
      ds.ref.depthWriteEnabled = WGPUOptionalBool.WGPUOptionalBool_True;
      ds.ref.depthCompare = WGPUCompareFunction.WGPUCompareFunction_Less;
      ds.ref.stencilReadMask = 0;
      ds.ref.stencilWriteMask = 0;
      desc.ref.depthStencil = ds;
    } else {
      desc.ref.depthStencil = nullptr;
    }

    desc.ref.multisample.count = sampleCount;
    desc.ref.multisample.mask = 0xFFFFFFFF;
    desc.ref.multisample.alphaToCoverageEnabled = 0;

    final handle = wgpu.wgpuDeviceCreateRenderPipeline(WebgpuRend.instance.device, desc);
    return GpuRenderPipeline._(handle.cast());
  });
}