Source/WebCore:
authorjustin_fan@apple.com <justin_fan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Apr 2019 19:59:55 +0000 (19:59 +0000)
committerjustin_fan@apple.com <justin_fan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Apr 2019 19:59:55 +0000 (19:59 +0000)
[Web GPU] Implement blend states and color write mask for GPUColorStateDescriptor
https://bugs.webkit.org/show_bug.cgi?id=196474

Reviewed by Myles C. Maxfield.

Blend states and color write masks must now be specified on GPUColorStateDescriptor instead of
relying on underlying MTLRenderPipelineColorAttachmentDescriptor defaults.

Test: webgpu/blend-triangle-strip.html, webgpu/color-write-mask-triangle-strip.html

* CMakeLists.txt:
* DerivedSources-input.xcfilelist:
* DerivedSources-output.xcfilelist:
* DerivedSources.make:
* Modules/webgpu/GPUBlendDescriptor.idl:
* Modules/webgpu/GPUColorStateDescriptor.idl:
* Modules/webgpu/GPUColorWriteBits.idl:
* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/WebCoreBuiltinNames.h:
* platform/graphics/gpu/GPUBlendDescriptor.h:
* platform/graphics/gpu/GPUColorStateDescriptor.h:
* platform/graphics/gpu/GPUColorWriteBits.h:
* platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm:
(WebCore::mtlColorWriteMaskForGPUColorWriteFlags):
(WebCore::mtlBlendOperationForGPUBlendOperation):
(WebCore::mtlBlendFactorForGPUBlendFactor):
(WebCore::setColorStatesForColorAttachmentArray):
(WebCore::tryCreateMtlRenderPipelineState):
(WebCore::trySetColorStatesForColorAttachmentArray): Deleted.

LayoutTests:
[Web GPU] Implement blend states and color write mask for  GPUColorStateDescriptor
https://bugs.webkit.org/show_bug.cgi?id=196474

Reviewed by Myles C. Maxfield.

Add blend-triangle-strip to test color blending and color-write-mask-triangle-strip.html to test color write mask.
Update other tests to specify blend states when creating a GPURenderPipeline.

* webgpu/blend-triangle-strip-expected.html: Added.
* webgpu/blend-triangle-strip.html: Added.
* webgpu/buffer-command-buffer-races.html:
* webgpu/buffer-resource-triangles.html:
* webgpu/color-write-mask-triangle-strip-expected.html: Added.
* webgpu/color-write-mask-triangle-strip.html: Added.
* webgpu/depth-enabled-triangle-strip.html:
* webgpu/js/webgpu-functions.js:
* webgpu/render-pipelines.html:
* webgpu/texture-triangle-strip.html:
* webgpu/vertex-buffer-triangle-strip.html:
* webgpu/whlsl.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@243755 268f45cc-cd09-0410-ab3c-d52691b4dbfc

28 files changed:
LayoutTests/ChangeLog
LayoutTests/webgpu/blend-triangle-strip-expected.html [new file with mode: 0644]
LayoutTests/webgpu/blend-triangle-strip.html [new file with mode: 0644]
LayoutTests/webgpu/buffer-command-buffer-races.html
LayoutTests/webgpu/buffer-resource-triangles.html
LayoutTests/webgpu/color-write-mask-triangle-strip-expected.html [new file with mode: 0644]
LayoutTests/webgpu/color-write-mask-triangle-strip.html [new file with mode: 0644]
LayoutTests/webgpu/depth-enabled-triangle-strip.html
LayoutTests/webgpu/js/webgpu-functions.js
LayoutTests/webgpu/render-pipelines.html
LayoutTests/webgpu/texture-triangle-strip.html
LayoutTests/webgpu/vertex-buffer-triangle-strip.html
LayoutTests/webgpu/whlsl.html
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/DerivedSources-input.xcfilelist
Source/WebCore/DerivedSources-output.xcfilelist
Source/WebCore/DerivedSources.make
Source/WebCore/Modules/webgpu/GPUBlendDescriptor.idl [new file with mode: 0644]
Source/WebCore/Modules/webgpu/GPUColorStateDescriptor.idl
Source/WebCore/Modules/webgpu/GPUColorWriteBits.idl [new file with mode: 0644]
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/WebCoreBuiltinNames.h
Source/WebCore/platform/graphics/gpu/GPUBlendDescriptor.h [new file with mode: 0644]
Source/WebCore/platform/graphics/gpu/GPUColorStateDescriptor.h
Source/WebCore/platform/graphics/gpu/GPUColorWriteBits.h [new file with mode: 0644]
Source/WebCore/platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm

index e18bbd9..b9fcb6b 100644 (file)
@@ -1,3 +1,26 @@
+2019-04-02  Justin Fan  <justin_fan@apple.com>
+
+        [Web GPU] Implement blend states and color write mask for  GPUColorStateDescriptor
+        https://bugs.webkit.org/show_bug.cgi?id=196474
+
+        Reviewed by Myles C. Maxfield.
+
+        Add blend-triangle-strip to test color blending and color-write-mask-triangle-strip.html to test color write mask.
+        Update other tests to specify blend states when creating a GPURenderPipeline.
+
+        * webgpu/blend-triangle-strip-expected.html: Added.
+        * webgpu/blend-triangle-strip.html: Added.
+        * webgpu/buffer-command-buffer-races.html:
+        * webgpu/buffer-resource-triangles.html:
+        * webgpu/color-write-mask-triangle-strip-expected.html: Added.
+        * webgpu/color-write-mask-triangle-strip.html: Added.
+        * webgpu/depth-enabled-triangle-strip.html:
+        * webgpu/js/webgpu-functions.js:
+        * webgpu/render-pipelines.html:
+        * webgpu/texture-triangle-strip.html:
+        * webgpu/vertex-buffer-triangle-strip.html:
+        * webgpu/whlsl.html:
+
 2019-04-02  Zalan Bujtas  <zalan@apple.com>
 
         [ContentChangeObserver] Ignore reconstructed renderers when checking for visibility change
diff --git a/LayoutTests/webgpu/blend-triangle-strip-expected.html b/LayoutTests/webgpu/blend-triangle-strip-expected.html
new file mode 100644 (file)
index 0000000..886f13e
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Reference File</title>
+<p>Pass if square canvas below is completely green.</p>
+<canvas width="400" height="400"></canvas>
+<script>
+const canvas = document.querySelector("canvas");
+const context = canvas.getContext('2d');
+
+context.fillStyle = 'rgb(0, 255, 0)';
+context.fillRect(0, 0, canvas.width, canvas.height);
+</script>
\ No newline at end of file
diff --git a/LayoutTests/webgpu/blend-triangle-strip.html b/LayoutTests/webgpu/blend-triangle-strip.html
new file mode 100644 (file)
index 0000000..4c67c2e
--- /dev/null
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebGPU Hello Triangles</title>
+<meta name="assert" content="WebGPU correctly renders a green canvas.">
+<link rel="match" href="blend-triangle-strip-expected.html">
+<p>Pass if square canvas below is completely green.</p>
+<canvas width="400" height="400"></canvas>
+<script src="js/webgpu-functions.js"></script>
+<script>
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
+const positionAttributeNum = 0;
+
+const shaderCode = `
+#include <metal_stdlib>
+    
+using namespace metal;
+
+struct VertexIn
+{
+    float4 position [[attribute(${positionAttributeNum})]];
+};
+
+struct VertexOut
+{
+    float4 position [[position]];
+    float4 color;
+};
+
+vertex VertexOut vertex_main(VertexIn vertexIn [[stage_in]])
+{
+    VertexOut vOut;
+    vOut.position = vertexIn.position;
+    vOut.color = float4(0, 0.5, 0, 0.5);
+    return vOut;
+}
+
+fragment float4 fragment_main(VertexOut v [[stage_in]])
+{
+    return v.color;
+}
+`;
+
+window.addEventListener("load", test, false);
+
+async function test() {
+    const device = await getBasicDevice();
+    const canvas = document.querySelector("canvas");
+
+    const shaderModule = device.createShaderModule({ code: shaderCode });
+
+    const colorStates = [{
+        format: "bgra8unorm",
+        alphaBlend: {
+            srcFactor: "one",
+            dstFactor: "one",
+            operation: "add"
+        },
+        colorBlend: {
+            srcFactor: "one",
+            dstFactor: "one",
+            operation: "add"
+        },
+        writeMask: GPUColorWriteBits.ALL
+    }];
+
+    const inputStateDescriptor = {
+        indexFormat: "uint32",
+        attributes: [{
+            shaderLocation: positionAttributeNum,
+            inputSlot: 0,
+            offset: 0,
+            format: "float4"
+        }],
+        inputs: [{
+            inputSlot: 0,
+            stride: 4 * 4,
+            stepMode: "vertex"
+        }]
+    };
+
+    const pipeline = createBasicPipeline(shaderModule, device, colorStates, null, inputStateDescriptor);
+
+    const vertexData = new Float32Array([
+        -1, 1, 0, 1,
+        -1, -1, 0, 1,
+        1, 1, 0, 1,
+        1, -1, 0, 1
+    ]);
+    const vertexBuffer = device.createBuffer({ size: vertexData.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
+    vertexBuffer.setSubData(0, vertexData.buffer);
+
+    const context = canvas.getContext("gpu");
+    const swapChain = device.createSwapChain({ context: context, format: "bgra8unorm" });
+    const colorAttachment = {
+        attachment: swapChain.getCurrentTexture().createDefaultView(),
+        loadOp: "clear",
+        storeOp: "store",
+        clearColor: { r: 0, g: 0, b: 0, a: 0 }
+    };
+
+    const commandEncoder = device.createCommandEncoder();
+    const passEncoder = commandEncoder.beginRenderPass({ colorAttachments: [colorAttachment] });
+    passEncoder.setPipeline(pipeline);
+    passEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    passEncoder.draw(4, 1, 0, 0);
+    passEncoder.draw(4, 1, 0, 0);
+    passEncoder.endPass();
+
+    device.getQueue().submit([commandEncoder.finish()]);
+
+    requestAnimationFrame(() => { 
+        if (window.testRunner)
+            testRunner.notifyDone();
+    });
+}
+</script>
\ No newline at end of file
index 3edb64b..a3c1e13 100644 (file)
@@ -92,7 +92,7 @@ async function test() {
     // FIXME: Replace with non-MSL shaders.
     const shaderModule = device.createShaderModule({ code: shaderCode });
     const inputStateDescriptor = createInputStateDescriptor();
-    const pipeline = createBasicPipeline(shaderModule, device, null, inputStateDescriptor);
+    const pipeline = createBasicPipeline(shaderModule, device, null, null, inputStateDescriptor);
 
     const upperLeftBuffer = createAndSetVertexBuffer(device, [-1, 1, -1, -1, 0, 1]);
     const middleBuffer = createAndSetVertexBuffer(device, [0, 1, -1, -1, 1, -1]);
index 63fbf3a..135ad75 100644 (file)
@@ -165,7 +165,7 @@ async function test() {
 
     // WebGPUPipelineLayout and WebGPURenderPipeline
     const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [leftTriangleBGLayout, rightTriangleBGLayout] });
-    const pipeline = createBasicPipeline(shaderModule, device, pipelineLayout, inputState, null, "triangle-list");
+    const pipeline = createBasicPipeline(shaderModule, device, null, pipelineLayout, inputState, null, "triangle-list");
 
     // WebGPUBufferBindings
     const bindingUL = createBufferBinding(upperLeft);
diff --git a/LayoutTests/webgpu/color-write-mask-triangle-strip-expected.html b/LayoutTests/webgpu/color-write-mask-triangle-strip-expected.html
new file mode 100644 (file)
index 0000000..886f13e
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Reference File</title>
+<p>Pass if square canvas below is completely green.</p>
+<canvas width="400" height="400"></canvas>
+<script>
+const canvas = document.querySelector("canvas");
+const context = canvas.getContext('2d');
+
+context.fillStyle = 'rgb(0, 255, 0)';
+context.fillRect(0, 0, canvas.width, canvas.height);
+</script>
\ No newline at end of file
diff --git a/LayoutTests/webgpu/color-write-mask-triangle-strip.html b/LayoutTests/webgpu/color-write-mask-triangle-strip.html
new file mode 100644 (file)
index 0000000..f17f00f
--- /dev/null
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebGPU Hello Triangles</title>
+<meta name="assert" content="WebGPU correctly renders a green canvas.">
+<link rel="match" href="color-write-mask-triangle-strip-expected.html">
+<p>Pass if square canvas below is completely green.</p>
+<canvas width="400" height="400"></canvas>
+<script src="js/webgpu-functions.js"></script>
+<script>
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
+const shaderCode = `
+#include <metal_stdlib>
+    
+using namespace metal;
+
+struct Vertex
+{
+    float4 position [[position]];
+};
+
+vertex Vertex vertex_main(uint vid [[vertex_id]])
+{
+    Vertex v;
+    switch (vid) {
+    case 0:
+        v.position = float4(-1, 1, 0, 1);
+        break;
+    case 1:
+        v.position = float4(-1, -1, 0, 1);
+        break;
+    case 2:
+        v.position = float4(1, 1, 0, 1);
+        break;
+    default:
+        v.position = float4(1, -1, 0, 1);
+    }
+    return v;
+}
+
+fragment float4 fragment_main()
+{
+    return float4(1, 1, 1, 1);
+}
+`
+
+async function test() {
+    const device = await getBasicDevice();
+    const canvas = document.querySelector("canvas");
+    const swapChain = createBasicSwapChain(canvas, device);
+    const shaderModule = device.createShaderModule({ code: shaderCode });
+    const colorStates = [{
+        format: "bgra8unorm",
+        alphaBlend: {
+            srcFactor: "one",
+            dstFactor: "zero",
+            operation: "add"
+        },
+        colorBlend: {
+            srcFactor: "one",
+            dstFactor: "zero",
+            operation: "add"
+        },
+        writeMask: GPUColorWriteBits.GREEN | GPUColorWriteBits.ALPHA
+    }];
+    const pipeline = createBasicPipeline(shaderModule, device, colorStates);
+    const commandEncoder = device.createCommandEncoder();
+    const colorAttachment = {
+        attachment: swapChain.getCurrentTexture().createDefaultView(),
+        loadOp: "clear",
+        storeOp: "store",
+        clearColor: { r: 0, g: 0, b: 0, a: 0 }
+    };
+    const passEncoder = commandEncoder.beginRenderPass({ colorAttachments: [colorAttachment] });
+    encodeBasicCommands(passEncoder, pipeline);
+    const queue = device.getQueue();
+
+    queue.submit([commandEncoder.finish()]);
+
+    requestAnimationFrame(() => { 
+        if (window.testRunner)
+            testRunner.notifyDone();
+    });
+}
+
+test();
+</script>
\ No newline at end of file
index 9353b2c..118960a 100644 (file)
@@ -88,7 +88,7 @@ async function test() {
     const vertexBuffer = createVertexBuffer(device);
     const inputStateDescriptor = createInputStateDescriptor();
     const depthStateDescriptor = createBasicDepthStateDescriptor();
-    const pipeline = createBasicPipeline(shaderModule, device, null, inputStateDescriptor, depthStateDescriptor);
+    const pipeline = createBasicPipeline(shaderModule, device, null, null, inputStateDescriptor, depthStateDescriptor);
     const commandEncoder = device.createCommandEncoder();
 
     const basicAttachment = {
index c6007ed..3790721 100644 (file)
@@ -34,7 +34,7 @@ function createBasicDepthTexture(canvas, device) {
     });
 }
 
-function createBasicPipeline(shaderModule, device, pipelineLayout, inputStateDescriptor, depthStateDescriptor, primitiveTopology = "triangle-strip") {
+function createBasicPipeline(shaderModule, device, colorStates, pipelineLayout, inputStateDescriptor, depthStateDescriptor, primitiveTopology = "triangle-strip") {
     const vertexStageDescriptor = {
         module: shaderModule,
         entryPoint: "vertex_main" 
@@ -45,13 +45,28 @@ function createBasicPipeline(shaderModule, device, pipelineLayout, inputStateDes
         entryPoint: "fragment_main"
     };
 
-    const basicColorState = { format: "bgra8unorm" };
+    if (!colorStates) {
+        colorStates = [{ 
+            format: "bgra8unorm",
+            alphaBlend: {
+                srcFactor: "one",
+                dstFactor: "zero",
+                operation: "add"
+            },
+            colorBlend: {
+                srcFactor: "one",
+                dstFactor: "zero",
+                operation: "add"
+            },
+            writeMask: GPUColorWriteBits.ALL
+        }];
+    }
 
     const pipelineDescriptor = {
         vertexStage: vertexStageDescriptor,
         fragmentStage: fragmentStageDescriptor,
         primitiveTopology: primitiveTopology,
-        colorStates: [basicColorState]
+        colorStates: colorStates
     };
 
     if (pipelineLayout)
index 94e36a5..7cc37c2 100644 (file)
@@ -47,7 +47,7 @@ promise_test(async () => {
     const bindGroupLayout = device.createBindGroupLayout({ bindings: [layoutBinding] });
     const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] });
 
-    const pipeline = createBasicPipeline(shaderModule, device, pipelineLayout);
+    const pipeline = createBasicPipeline(shaderModule, device, null, pipelineLayout);
     assert_true(pipeline instanceof WebGPURenderPipeline, "Successfully created WebGPURenderPipeline");
 }, "Create basic WebGPURenderPipeline");
 </script>
index 2f04694..854038f 100644 (file)
@@ -185,7 +185,7 @@ async function test() {
     const bindGroup = device.createBindGroup(bindGroupDescriptor);
 
     // Pipeline and render
-    const pipeline = createBasicPipeline(shaderModule, device, pipelineLayout, inputStateDescriptor);
+    const pipeline = createBasicPipeline(shaderModule, device, null, pipelineLayout, inputStateDescriptor);
     const commandEncoder = device.createCommandEncoder();
 
     const bufferCopyView = {
index 2a50c1a..5c4d0eb 100644 (file)
@@ -90,7 +90,7 @@ async function test() {
     const shaderModule = device.createShaderModule({ code: shaderCode });
     const vertexBuffer = createVertexBuffer(device);
     const inputStateDescriptor = createInputStateDescriptor();
-    const pipeline = createBasicPipeline(shaderModule, device, null, inputStateDescriptor);
+    const pipeline = createBasicPipeline(shaderModule, device, null, null, inputStateDescriptor);
     const commandEncoder = device.createCommandEncoder();
     const passEncoder = beginBasicRenderPass(swapChain, commandEncoder);
     encodeBasicCommands(passEncoder, pipeline, vertexBuffer);
index 2b468f4..14629de 100644 (file)
@@ -23,8 +23,8 @@ async function start() {
     const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
     const primitiveTopology = "triangle-strip";
     const rasterizationState = {frontFace: "cw", cullMode: "none"};
-    const alphaBlend = {srcFactor: "zero", dstFactor: "one", operation: "add"};
-    const colorBlend = {srcFactor: "zero", dstFactor: "one", operation: "add"};
+    const alphaBlend = {srcFactor: "one", dstFactor: "zero", operation: "add"};
+    const colorBlend = {srcFactor: "one", dstFactor: "zero", operation: "add"};
     const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
     const depthStencilState = null;
     
index b211ad5..bd10389 100644 (file)
@@ -465,11 +465,13 @@ set(WebCore_NON_SVG_IDL_FILES
     Modules/webgpu/DOMWindowWebGPU.idl
     Modules/webgpu/GPUBindGroupLayoutBinding.idl
     Modules/webgpu/GPUBindGroupLayoutDescriptor.idl
+    Modules/webgpu/GPUBlendDescriptor.idl
     Modules/webgpu/GPUBufferDescriptor.idl
     Modules/webgpu/GPUBufferUsage.idl
     Modules/webgpu/GPUCanvasContext.idl
     Modules/webgpu/GPUColor.idl
     Modules/webgpu/GPUColorStateDescriptor.idl
+    Modules/webgpu/GPUColorWriteBits.idl
     Modules/webgpu/GPUCompareFunction.idl
     Modules/webgpu/GPUDepthStencilStateDescriptor.idl
     Modules/webgpu/GPUExtent3D.idl
index 73484fe..bc427dc 100644 (file)
@@ -1,3 +1,36 @@
+2019-04-02  Justin Fan  <justin_fan@apple.com>
+
+        [Web GPU] Implement blend states and color write mask for GPUColorStateDescriptor
+        https://bugs.webkit.org/show_bug.cgi?id=196474
+
+        Reviewed by Myles C. Maxfield.
+
+        Blend states and color write masks must now be specified on GPUColorStateDescriptor instead of 
+        relying on underlying MTLRenderPipelineColorAttachmentDescriptor defaults.
+
+        Test: webgpu/blend-triangle-strip.html, webgpu/color-write-mask-triangle-strip.html
+
+        * CMakeLists.txt:
+        * DerivedSources-input.xcfilelist:
+        * DerivedSources-output.xcfilelist:
+        * DerivedSources.make:
+        * Modules/webgpu/GPUBlendDescriptor.idl: 
+        * Modules/webgpu/GPUColorStateDescriptor.idl:
+        * Modules/webgpu/GPUColorWriteBits.idl: 
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/WebCoreBuiltinNames.h:
+        * platform/graphics/gpu/GPUBlendDescriptor.h:
+        * platform/graphics/gpu/GPUColorStateDescriptor.h:
+        * platform/graphics/gpu/GPUColorWriteBits.h:
+        * platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm:
+        (WebCore::mtlColorWriteMaskForGPUColorWriteFlags):
+        (WebCore::mtlBlendOperationForGPUBlendOperation):
+        (WebCore::mtlBlendFactorForGPUBlendFactor):
+        (WebCore::setColorStatesForColorAttachmentArray):
+        (WebCore::tryCreateMtlRenderPipelineState):
+        (WebCore::trySetColorStatesForColorAttachmentArray): Deleted.
+
 2019-04-02  Zalan Bujtas  <zalan@apple.com>
 
         [ContentChangeObserver] Ignore reconstructed renderers when checking for visibility change
index c1e8f34..1240061 100644 (file)
@@ -330,11 +330,13 @@ $(PROJECT_DIR)/Modules/webdriver/NavigatorWebDriver.idl
 $(PROJECT_DIR)/Modules/webgpu/DOMWindowWebGPU.idl
 $(PROJECT_DIR)/Modules/webgpu/GPUBindGroupLayoutBinding.idl
 $(PROJECT_DIR)/Modules/webgpu/GPUBindGroupLayoutDescriptor.idl
+$(PROJECT_DIR)/Modules/webgpu/GPUBlendDescriptor.idl
 $(PROJECT_DIR)/Modules/webgpu/GPUBufferDescriptor.idl
 $(PROJECT_DIR)/Modules/webgpu/GPUBufferUsage.idl
 $(PROJECT_DIR)/Modules/webgpu/GPUCanvasContext.idl
 $(PROJECT_DIR)/Modules/webgpu/GPUColor.idl
 $(PROJECT_DIR)/Modules/webgpu/GPUColorStateDescriptor.idl
+$(PROJECT_DIR)/Modules/webgpu/GPUColorWriteBits.idl
 $(PROJECT_DIR)/Modules/webgpu/GPUCompareFunction.idl
 $(PROJECT_DIR)/Modules/webgpu/GPUDepthStencilStateDescriptor.idl
 $(PROJECT_DIR)/Modules/webgpu/GPUExtent3D.idl
index feeb61e..5bbe2a0 100644 (file)
@@ -587,6 +587,8 @@ $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUBindGroupLayoutBinding.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUBindGroupLayoutBinding.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUBindGroupLayoutDescriptor.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUBindGroupLayoutDescriptor.h
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUBlendDescriptor.cpp
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUBlendDescriptor.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUBufferDescriptor.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUBufferDescriptor.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUBufferUsage.cpp
@@ -597,6 +599,8 @@ $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUColor.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUColor.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUColorStateDescriptor.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUColorStateDescriptor.h
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUColorWriteBits.cpp
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUColorWriteBits.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUCompareFunction.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUCompareFunction.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSGPUDepthStencilStateDescriptor.cpp
index 187402f..2e0b444 100644 (file)
@@ -378,8 +378,10 @@ JS_BINDING_IDLS = \
     $(WebCore)/Modules/webgpu/GPUCanvasContext.idl \
     $(WebCore)/Modules/webgpu/GPUColor.idl \
     $(WebCore)/Modules/webgpu/GPUColorStateDescriptor.idl \
+    $(WebCore)/Modules/webgpu/GPUColorWriteBits.idl \
     $(WebCore)/Modules/webgpu/GPUBindGroupLayoutBinding.idl \
     $(WebCore)/Modules/webgpu/GPUBindGroupLayoutDescriptor.idl \
+    $(WebCore)/Modules/webgpu/GPUBlendDescriptor.idl \
     $(WebCore)/Modules/webgpu/GPUBufferDescriptor.idl \
     $(WebCore)/Modules/webgpu/GPUBufferUsage.idl \
     $(WebCore)/Modules/webgpu/GPUCompareFunction.idl \
diff --git a/Source/WebCore/Modules/webgpu/GPUBlendDescriptor.idl b/Source/WebCore/Modules/webgpu/GPUBlendDescriptor.idl
new file mode 100644 (file)
index 0000000..4167780
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
+
+[
+    ImplementedAs=GPUBlendFactor
+] enum GPUBlendFactor {
+    "zero",
+    "one",
+    "src-color",
+    "one-minus-src-color",
+    "src-alpha",
+    "one-minus-src-alpha",
+    "dst-color",
+    "one-minus-dst-color",
+    "dst-alpha",
+    "one-minus-dst-alpha",
+    "src-alpha-saturated",
+    "blend-color",
+    "one-minus-blend-color"
+};
+
+[
+    ImplementedAs=GPUBlendOperation
+] enum GPUBlendOperation {
+    "add",
+    "subtract",
+    "reverse-subtract",
+    "min",
+    "max"
+};
+
+[
+    Conditional=WEBGPU,
+    EnabledAtRuntime=WebGPU
+] dictionary GPUBlendDescriptor {
+    GPUBlendFactor srcFactor;
+    GPUBlendFactor dstFactor;
+    GPUBlendOperation operation;
+};
index 67ae2ee..8c966db 100644 (file)
  */
 // https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
 
+typedef unsigned long GPUColorWriteFlags;
+
 [
     Conditional=WEBGPU,
     EnabledAtRuntime=WebGPU
 ] dictionary GPUColorStateDescriptor {
     GPUTextureFormat format;
 
-    // Not yet implemented.
-    // GPUBlendDescriptor alphaBlend;
-    // GPUBlendDescriptor colorBlend;
-    // GPUColorWriteFlags writeMask;
+    GPUBlendDescriptor alphaBlend;
+    GPUBlendDescriptor colorBlend;
+    GPUColorWriteFlags writeMask;
 };
diff --git a/Source/WebCore/Modules/webgpu/GPUColorWriteBits.idl b/Source/WebCore/Modules/webgpu/GPUColorWriteBits.idl
new file mode 100644 (file)
index 0000000..c9e0ef2
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
+
+typedef unsigned long u32;
+
+[
+    Conditional=WEBGPU,
+    DoNotCheckConstants,
+    EnabledAtRuntime=WebGPU,
+    ImplementationLacksVTable
+] interface GPUColorWriteBits {
+    const u32 NONE = 0;
+    const u32 RED = 1;
+    const u32 GREEN = 2;
+    const u32 BLUE = 4;
+    const u32 ALPHA = 8;
+    const u32 ALL = 15;
+};
index ea38fd3..2d9d3c1 100644 (file)
@@ -2720,8 +2720,10 @@ JSFontFaceSet.cpp
 JSGPUCanvasContext.cpp
 JSGPUColor.cpp
 JSGPUColorStateDescriptor.cpp
+JSGPUColorWriteBits.cpp
 JSGPUBindGroupLayoutBinding.cpp
 JSGPUBindGroupLayoutDescriptor.cpp
+JSGPUBlendDescriptor.cpp
 JSGPUBufferDescriptor.cpp
 JSGPUBufferUsage.cpp
 JSGPUCompareFunction.cpp
index dfd8678..2dab246 100644 (file)
                D0ADB28C2237842E00A22935 /* GPUColorStateDescriptor.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = GPUColorStateDescriptor.idl; sourceTree = "<group>"; };
                D0B0556609C6700100307E43 /* CreateLinkCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CreateLinkCommand.h; sourceTree = "<group>"; };
                D0B0556709C6700100307E43 /* CreateLinkCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CreateLinkCommand.cpp; sourceTree = "<group>"; };
+               D0B56EAA224EC6240061049C /* GPUBlendDescriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPUBlendDescriptor.h; sourceTree = "<group>"; };
+               D0B56EAB224EC6240061049C /* GPUBlendDescriptor.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = GPUBlendDescriptor.idl; sourceTree = "<group>"; };
+               D0B56EAD224ECE200061049C /* GPUColorWriteBits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPUColorWriteBits.h; sourceTree = "<group>"; };
+               D0B56EAE224ECE200061049C /* GPUColorWriteBits.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = GPUColorWriteBits.idl; sourceTree = "<group>"; };
                D0B8BB0121C46E78000C7681 /* GPUBindGroupLayoutBinding.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPUBindGroupLayoutBinding.h; sourceTree = "<group>"; };
                D0BC54481443AC4A00E105DA /* CachedStyleSheetClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedStyleSheetClient.h; sourceTree = "<group>"; };
                D0BD4F5A1408850F006839B6 /* DictationCommandIOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DictationCommandIOS.cpp; sourceTree = "<group>"; };
                                D0BE104E21E695E200E42A89 /* GPUBindGroupBinding.h */,
                                D0BE105121E6A70E00E42A89 /* GPUBindGroupDescriptor.h */,
                                D02454D021C4A41C00B73628 /* GPUBindGroupLayout.h */,
+                               D0B56EAA224EC6240061049C /* GPUBlendDescriptor.h */,
                                D084033A221CBF5400007205 /* GPUBuffer.cpp */,
                                D0D8649221B760F2003C983C /* GPUBuffer.h */,
                                D0BE104A21E6872F00E42A89 /* GPUBufferBinding.h */,
                                D01B811D2213636E00627B6C /* GPUBufferUsage.h */,
                                D001D9AB21B0C7BF0023B9BC /* GPUColor.h */,
                                D0ADB28B2237842E00A22935 /* GPUColorStateDescriptor.h */,
+                               D0B56EAD224ECE200061049C /* GPUColorWriteBits.h */,
                                312FF8BD21A4C2F100EB199D /* GPUCommandBuffer.h */,
                                D03C849C21FFC7FC0002227F /* GPUCompareFunction.h */,
                                D089033F2241CE4600F3F440 /* GPUComputePassEncoder.h */,
                                D0D69C9F222E015E0032927E /* GPUBindGroupLayoutBinding.idl */,
                                D0D69C9C222E00C20032927E /* GPUBindGroupLayoutDescriptor.h */,
                                D0D69C9D222E00C20032927E /* GPUBindGroupLayoutDescriptor.idl */,
+                               D0B56EAB224EC6240061049C /* GPUBlendDescriptor.idl */,
                                D01B811922135EB900627B6C /* GPUBufferDescriptor.idl */,
                                D01B811C2213627300627B6C /* GPUBufferUsage.idl */,
                                D093D2292179541600329217 /* GPUCanvasContext.cpp */,
                                D093D227217951D400329217 /* GPUCanvasContext.idl */,
                                D01B811222125AFC00627B6C /* GPUColor.idl */,
                                D0ADB28C2237842E00A22935 /* GPUColorStateDescriptor.idl */,
+                               D0B56EAE224ECE200061049C /* GPUColorWriteBits.idl */,
                                D03C849E21FFCF000002227F /* GPUCompareFunction.idl */,
                                D03C84A221FFD7230002227F /* GPUDepthStencilStateDescriptor.idl */,
                                D026F480220A2B7000AC5F49 /* GPUExtent3D.idl */,
index 190e69f..6888714 100644 (file)
@@ -83,11 +83,12 @@ namespace WebCore {
     macro(GamepadEvent) \
     macro(GPUBufferUsage) \
     macro(GPUCanvasContext) \
-    macro(GPUShaderModule) \
+    macro(GPUColorWriteBits) \
     macro(GPUCommandBuffer) \
     macro(GPUCommandEncoder) \
     macro(GPUComputePassEncoder) \
     macro(GPUComputePipeline) \
+    macro(GPUShaderModule) \
     macro(GPUShaderStageBit) \
     macro(GPUSwapChain) \
     macro(GPUTextureUsage) \
diff --git a/Source/WebCore/platform/graphics/gpu/GPUBlendDescriptor.h b/Source/WebCore/platform/graphics/gpu/GPUBlendDescriptor.h
new file mode 100644 (file)
index 0000000..e4133ac
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBGPU)
+
+namespace WebCore {
+
+enum class GPUBlendFactor {
+    Zero,
+    One,
+    SrcColor,
+    OneMinusSrcColor,
+    SrcAlpha,
+    OneMinusSrcAlpha,
+    DstColor,
+    OneMinusDstColor,
+    DstAlpha,
+    OneMinusDstAlpha,
+    SrcAlphaSaturated,
+    BlendColor,
+    OneMinusBlendColor,
+};
+
+enum class GPUBlendOperation {
+    Add,
+    Subtract,
+    ReverseSubtract,
+    Min,
+    Max,
+};
+
+struct GPUBlendDescriptor {
+    GPUBlendFactor srcFactor;
+    GPUBlendFactor dstFactor;
+    GPUBlendOperation operation;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEBGPU)
index bd07e3b..e001705 100644 (file)
 
 #if ENABLE(WEBGPU)
 
+#include "GPUBlendDescriptor.h"
+#include "GPUColorWriteBits.h"
 #include "GPUTextureFormat.h"
 
 namespace WebCore {
 
 struct GPUColorStateDescriptor {
     GPUTextureFormat format;
+
+    GPUBlendDescriptor alphaBlend;
+    GPUBlendDescriptor colorBlend;
+    GPUColorWriteFlags writeMask;
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/gpu/GPUColorWriteBits.h b/Source/WebCore/platform/graphics/gpu/GPUColorWriteBits.h
new file mode 100644 (file)
index 0000000..90ad929
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBGPU)
+
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+using GPUColorWriteFlags = unsigned;
+
+class GPUColorWriteBits : public RefCounted<GPUColorWriteBits> {
+public:
+    enum class Flags : GPUColorWriteFlags {
+        None = 0,
+        Red = 1 << 0,
+        Green = 1 << 1,
+        Blue = 1 << 2,
+        Alpha = 1 << 3,
+        All = (1 << 4) - 1,
+    };
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEBGPU)
index f3fc53a..fbe2460 100644 (file)
@@ -36,6 +36,7 @@
 #import "WHLSLVertexBufferIndexCalculator.h"
 #import <Metal/Metal.h>
 #import <wtf/BlockObjCExceptions.h>
+#import <wtf/OptionSet.h>
 #import <wtf/Optional.h>
 
 namespace WebCore {
@@ -367,13 +368,93 @@ static bool trySetInputStateForPipelineDescriptor(const char* const functionName
     return true;
 }
 
-static bool trySetColorStatesForColorAttachmentArray(MTLRenderPipelineColorAttachmentDescriptorArray* array, const Vector<GPUColorStateDescriptor>& colorStates)
+static MTLColorWriteMask mtlColorWriteMaskForGPUColorWriteFlags(GPUColorWriteFlags flags)
 {
-    // FIXME: Implement and validate the rest of GPUColorStateDescriptor.
-    for (unsigned i = 0; i < colorStates.size(); ++i)
-        [array[i] setPixelFormat:static_cast<MTLPixelFormat>(platformTextureFormatForGPUTextureFormat(colorStates[i].format))];
+    if (flags == static_cast<GPUColorWriteFlags>(GPUColorWriteBits::Flags::All))
+        return MTLColorWriteMaskAll;
+
+    auto options = OptionSet<GPUColorWriteBits::Flags>::fromRaw(flags);
+
+    MTLColorWriteMask mask = MTLColorWriteMaskNone;
+    if (options & GPUColorWriteBits::Flags::Red)
+        mask |= MTLColorWriteMaskRed;
+    if (options & GPUColorWriteBits::Flags::Green)
+        mask |= MTLColorWriteMaskGreen;
+    if (options & GPUColorWriteBits::Flags::Blue)
+        mask |= MTLColorWriteMaskBlue;
+    if (options & GPUColorWriteBits::Flags::Alpha)
+        mask |= MTLColorWriteMaskAlpha;
+
+    return mask;
+}
 
-    return true;
+static MTLBlendOperation mtlBlendOperationForGPUBlendOperation(GPUBlendOperation op)
+{
+    switch (op) {
+    case GPUBlendOperation::Add:
+        return MTLBlendOperationAdd;
+    case GPUBlendOperation::Subtract:
+        return MTLBlendOperationSubtract;
+    case GPUBlendOperation::ReverseSubtract:
+        return MTLBlendOperationReverseSubtract;
+    case GPUBlendOperation::Min:
+        return MTLBlendOperationMin;
+    case GPUBlendOperation::Max:
+        return MTLBlendOperationMax;
+    }
+
+    ASSERT_NOT_REACHED();
+}
+
+static MTLBlendFactor mtlBlendFactorForGPUBlendFactor(GPUBlendFactor factor)
+{
+    switch (factor) {
+    case GPUBlendFactor::Zero:
+        return MTLBlendFactorZero;
+    case GPUBlendFactor::One:
+        return MTLBlendFactorOne;
+    case GPUBlendFactor::SrcColor:
+        return MTLBlendFactorSourceColor;
+    case GPUBlendFactor::OneMinusSrcColor:
+        return MTLBlendFactorOneMinusSourceColor;
+    case GPUBlendFactor::SrcAlpha:
+        return MTLBlendFactorSourceAlpha;
+    case GPUBlendFactor::OneMinusSrcAlpha:
+        return MTLBlendFactorOneMinusSourceAlpha;
+    case GPUBlendFactor::DstColor:
+        return MTLBlendFactorDestinationColor;
+    case GPUBlendFactor::OneMinusDstColor:
+        return MTLBlendFactorOneMinusDestinationColor;
+    case GPUBlendFactor::DstAlpha:
+        return MTLBlendFactorDestinationAlpha;
+    case GPUBlendFactor::OneMinusDstAlpha:
+        return MTLBlendFactorOneMinusDestinationAlpha;
+    case GPUBlendFactor::SrcAlphaSaturated:
+        return MTLBlendFactorSourceAlpha;
+    case GPUBlendFactor::BlendColor:
+        return MTLBlendFactorBlendColor;
+    case GPUBlendFactor::OneMinusBlendColor:
+        return MTLBlendFactorOneMinusBlendColor;
+    }
+
+    ASSERT_NOT_REACHED();
+}
+
+static void setColorStatesForColorAttachmentArray(MTLRenderPipelineColorAttachmentDescriptorArray* array, const Vector<GPUColorStateDescriptor>& colorStates)
+{
+    for (unsigned i = 0; i < colorStates.size(); ++i) {
+        auto& state = colorStates[i];
+        auto descriptor = retainPtr([array objectAtIndexedSubscript:i]);
+        [descriptor setPixelFormat:static_cast<MTLPixelFormat>(platformTextureFormatForGPUTextureFormat(state.format))];
+        [descriptor setWriteMask:mtlColorWriteMaskForGPUColorWriteFlags(state.writeMask)];
+        [descriptor setBlendingEnabled:YES];
+        [descriptor setAlphaBlendOperation:mtlBlendOperationForGPUBlendOperation(state.alphaBlend.operation)];
+        [descriptor setRgbBlendOperation:mtlBlendOperationForGPUBlendOperation(state.colorBlend.operation)];
+        [descriptor setDestinationAlphaBlendFactor:mtlBlendFactorForGPUBlendFactor(state.alphaBlend.dstFactor)];
+        [descriptor setDestinationRGBBlendFactor:mtlBlendFactorForGPUBlendFactor(state.colorBlend.dstFactor)];
+        [descriptor setSourceAlphaBlendFactor:mtlBlendFactorForGPUBlendFactor(state.alphaBlend.srcFactor)];
+        [descriptor setSourceRGBBlendFactor:mtlBlendFactorForGPUBlendFactor(state.colorBlend.srcFactor)];
+    }
 }
 
 static RetainPtr<MTLRenderPipelineState> tryCreateMtlRenderPipelineState(const char* const functionName, const GPURenderPipelineDescriptor& descriptor, const GPUDevice& device)
@@ -391,17 +472,17 @@ static RetainPtr<MTLRenderPipelineState> tryCreateMtlRenderPipelineState(const c
         return nullptr;
     }
 
-    bool didSetFunctions = false, didSetInputState = false, didSetColorStates = false;
+    bool didSetFunctions = false, didSetInputState = false;
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
     didSetFunctions = trySetFunctionsForPipelineDescriptor(functionName, mtlDescriptor.get(), descriptor, device);
     didSetInputState = trySetInputStateForPipelineDescriptor(functionName, mtlDescriptor.get(), descriptor.inputState);
-    didSetColorStates = trySetColorStatesForColorAttachmentArray(mtlDescriptor.get().colorAttachments, descriptor.colorStates);
+    setColorStatesForColorAttachmentArray(mtlDescriptor.get().colorAttachments, descriptor.colorStates);
 
     END_BLOCK_OBJC_EXCEPTIONS;
 
-    if (!didSetFunctions || !didSetInputState || !didSetColorStates)
+    if (!didSetFunctions || !didSetInputState)
         return nullptr;
 
     RetainPtr<MTLRenderPipelineState> pipeline;