[Web GPU] GPURenderPassEncoder updates: setBlendColor, setViewport, setScissorRect
authorjustin_fan@apple.com <justin_fan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 9 Apr 2019 20:03:18 +0000 (20:03 +0000)
committerjustin_fan@apple.com <justin_fan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 9 Apr 2019 20:03:18 +0000 (20:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196719

Reviewed by Myles C. Maxfield.

Source/WebCore:

Implement setBlendColor, setViewport, and setScissorRect for GPURenderPassEncoder.

Tests: webgpu/viewport-scissor-rect-triangle-strip.html, webgpu/blend-color-triangle-strip.html

* Modules/webgpu/WebGPURenderPassEncoder.cpp:
(WebCore::WebGPURenderPassEncoder::setBlendColor):
(WebCore::WebGPURenderPassEncoder::setViewport):
(WebCore::WebGPURenderPassEncoder::setScissorRect):
* Modules/webgpu/WebGPURenderPassEncoder.h:
* Modules/webgpu/WebGPURenderPassEncoder.idl:
* platform/graphics/gpu/GPURenderPassEncoder.h:
* platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm:
(WebCore::GPURenderPassEncoder::setBlendColor):
(WebCore::GPURenderPassEncoder::setViewport):
(WebCore::GPURenderPassEncoder::setScissorRect):

LayoutTests:

Add blend-color-triangle-strip to set and blend with a custom blend color on the renderpass encoder.
Add viewport-scissor-rect-triangle-strip to draw a checkerboard by restricting the drawing viewport or scissor rectangle.

* webgpu/blend-color-triangle-strip.html: Added.
* webgpu/js/webgpu-functions.js:
(beginBasicRenderPass):
* webgpu/viewport-scissor-rect-triangle-strip-expected.html: Added.
* webgpu/viewport-scissor-rect-triangle-strip.html: Added.

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

12 files changed:
LayoutTests/ChangeLog
LayoutTests/webgpu/blend-color-triangle-strip-expected.html [new file with mode: 0644]
LayoutTests/webgpu/blend-color-triangle-strip.html [new file with mode: 0644]
LayoutTests/webgpu/js/webgpu-functions.js
LayoutTests/webgpu/viewport-scissor-rect-triangle-strip-expected.html [new file with mode: 0644]
LayoutTests/webgpu/viewport-scissor-rect-triangle-strip.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.cpp
Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.h
Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.idl
Source/WebCore/platform/graphics/gpu/GPURenderPassEncoder.h
Source/WebCore/platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm

index 62f0b7f..180dd8f 100644 (file)
@@ -1,3 +1,19 @@
+2019-04-09  Justin Fan  <justin_fan@apple.com>
+
+        [Web GPU] GPURenderPassEncoder updates: setBlendColor, setViewport, setScissorRect
+        https://bugs.webkit.org/show_bug.cgi?id=196719
+
+        Reviewed by Myles C. Maxfield.
+
+        Add blend-color-triangle-strip to set and blend with a custom blend color on the renderpass encoder.
+        Add viewport-scissor-rect-triangle-strip to draw a checkerboard by restricting the drawing viewport or scissor rectangle.
+
+        * webgpu/blend-color-triangle-strip.html: Added.
+        * webgpu/js/webgpu-functions.js:
+        (beginBasicRenderPass):
+        * webgpu/viewport-scissor-rect-triangle-strip-expected.html: Added.
+        * webgpu/viewport-scissor-rect-triangle-strip.html: Added.
+
 2019-04-09  Devin Rousso  <drousso@apple.com>
 
         Unreviewed, fix test failures after r239698.
diff --git a/LayoutTests/webgpu/blend-color-triangle-strip-expected.html b/LayoutTests/webgpu/blend-color-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-color-triangle-strip.html b/LayoutTests/webgpu/blend-color-triangle-strip.html
new file mode 100644 (file)
index 0000000..2a56379
--- /dev/null
@@ -0,0 +1,86 @@
+<!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-color-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(0, 1, 1, 1);
+}
+`;
+
+async function test() {
+    const device = await getBasicDevice();
+    const canvas = document.querySelector("canvas");
+    const swapChain = createBasicSwapChain(canvas, device);
+    // FIXME: Replace with non-MSL shaders.
+    const shaderModule = device.createShaderModule({ code: shaderCode });
+
+    const colorStates = [{
+        format: "bgra8unorm",
+        alphaBlend: {
+            srcFactor: "blend-color",
+            dstFactor: "blend-color",
+            operation: "add"
+        },
+        colorBlend: {
+            srcFactor: "blend-color",
+            dstFactor: "blend-color",
+            operation: "add"
+        },
+        writeMask: GPUColorWriteBits.ALL
+    }];
+
+    const pipeline = createBasicPipeline(shaderModule, device, colorStates);
+    const commandEncoder = device.createCommandEncoder();
+    const passEncoder = beginBasicRenderPass(swapChain, commandEncoder);
+    passEncoder.setBlendColor({ r: 0, g: 1, b: 0, a: 1 });
+    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 3790721..9776b4f 100644 (file)
@@ -87,7 +87,7 @@ function beginBasicRenderPass(swapChain, commandEncoder) {
         loadOp: "clear",
         storeOp: "store",
         clearColor: { r: 1.0, g: 0, b: 0, a: 1.0 }
-    }
+    };
 
     // FIXME: Flesh out the rest of WebGPURenderPassDescriptor. 
     return commandEncoder.beginRenderPass({ colorAttachments : [basicAttachment] });
diff --git a/LayoutTests/webgpu/viewport-scissor-rect-triangle-strip-expected.html b/LayoutTests/webgpu/viewport-scissor-rect-triangle-strip-expected.html
new file mode 100644 (file)
index 0000000..b69a9c0
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Reference File</title>
+<p>Pass if square canvas below is a 4 by 4 blue/green checkerboard.</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);
+
+const numColumns = 4;
+const numRows = 4;
+context.beginPath();
+context.fillStyle = 'rgb(0, 0, 255)';
+for (let x = 0; x < numColumns; ++x) {
+    for (let y = 0; y < numRows; ++y) {
+        if (x % 2 == 0 && y % 2 == 0 || x % 2 == 1 && y % 2 == 1)
+            context.rect(
+                x * canvas.width / numColumns, 
+                y * canvas.height / numRows, 
+                canvas.width / numColumns, 
+                canvas.height / numRows
+                );
+    }
+}
+context.fill();
+</script>
\ No newline at end of file
diff --git a/LayoutTests/webgpu/viewport-scissor-rect-triangle-strip.html b/LayoutTests/webgpu/viewport-scissor-rect-triangle-strip.html
new file mode 100644 (file)
index 0000000..2315adf
--- /dev/null
@@ -0,0 +1,105 @@
+<!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="viewport-scissor-rect-triangle-strip-expected.html">
+<p>Pass if square canvas below is a 4 by 4 blue/green checkerboard.</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(Vertex vertexIn [[stage_in]])
+{
+    return float4(0.0, 1.0, 0.0, 1.0);
+}
+`;
+
+async function test() {
+    const device = await getBasicDevice();
+    const canvas = document.querySelector("canvas");
+    const swapChain = createBasicSwapChain(canvas, device);
+    // FIXME: Replace with non-MSL shaders.
+    const shaderModule = device.createShaderModule({ code: shaderCode });
+    const pipeline = createBasicPipeline(shaderModule, device);
+    const commandEncoder = device.createCommandEncoder();
+    const blueAttachment = {
+        attachment: swapChain.getCurrentTexture().createDefaultView(),
+        loadOp: "clear",
+        storeOp: "store",
+        clearColor: { r: 0, g: 0, b: 1, a: 1 }
+    };
+    const passEncoder = commandEncoder.beginRenderPass({ colorAttachments: [blueAttachment] });
+    passEncoder.setPipeline(pipeline);
+
+    function drawViewport(x, y) {
+        passEncoder.setViewport(x, y, 100, 100, 0, 1);
+        passEncoder.draw(4, 1, 0, 0);
+    }
+
+    function drawScissorRect(x, y) {
+        passEncoder.setScissorRect(x, y, 100, 100);
+        passEncoder.draw(4, 1, 0, 0);
+    }
+
+    passEncoder.setScissorRect(0, 0, 200, 400);
+    drawViewport(100, 0);
+    drawViewport(0, 100);
+    drawViewport(100, 200);
+    drawViewport(0, 300);
+
+    // Draw outside of scissor rect should not appear.
+    drawViewport(200, 100);
+
+    passEncoder.setViewport(200, 0, 200, 400, 0, 1);
+    drawScissorRect(300, 0);
+    drawScissorRect(200, 100);
+    drawScissorRect(300, 200);
+    drawScissorRect(200, 300);
+
+    // Draw outside of viewport should not appear.
+    drawScissorRect(0, 0);
+
+    passEncoder.endPass();
+    const queue = device.getQueue();
+
+    queue.submit([commandEncoder.finish()]);
+
+    requestAnimationFrame(() => { 
+        if (window.testRunner)
+            testRunner.notifyDone();
+    });
+}
+
+test();
+</script>
\ No newline at end of file
index 97a860e..e8ad4e6 100644 (file)
@@ -1,3 +1,26 @@
+2019-04-09  Justin Fan  <justin_fan@apple.com>
+
+        [Web GPU] GPURenderPassEncoder updates: setBlendColor, setViewport, setScissorRect
+        https://bugs.webkit.org/show_bug.cgi?id=196719
+
+        Reviewed by Myles C. Maxfield.
+
+        Implement setBlendColor, setViewport, and setScissorRect for GPURenderPassEncoder.
+
+        Tests: webgpu/viewport-scissor-rect-triangle-strip.html, webgpu/blend-color-triangle-strip.html
+
+        * Modules/webgpu/WebGPURenderPassEncoder.cpp:
+        (WebCore::WebGPURenderPassEncoder::setBlendColor):
+        (WebCore::WebGPURenderPassEncoder::setViewport):
+        (WebCore::WebGPURenderPassEncoder::setScissorRect):
+        * Modules/webgpu/WebGPURenderPassEncoder.h:
+        * Modules/webgpu/WebGPURenderPassEncoder.idl:
+        * platform/graphics/gpu/GPURenderPassEncoder.h:
+        * platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm:
+        (WebCore::GPURenderPassEncoder::setBlendColor):
+        (WebCore::GPURenderPassEncoder::setViewport):
+        (WebCore::GPURenderPassEncoder::setScissorRect):
+
 2019-04-09  Andy Estes  <aestes@apple.com>
 
         [Apple Pay] Add release logging to PaymentCoordinator
index 08678bc..30591f6 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(WEBGPU)
 
+#include "GPUColor.h"
 #include "GPULimits.h"
 #include "GPUProgrammablePassEncoder.h"
 #include "GPURenderPassEncoder.h"
@@ -60,6 +61,37 @@ void WebGPURenderPassEncoder::setPipeline(const WebGPURenderPipeline& pipeline)
     m_passEncoder->setPipeline(makeRef(*pipeline.renderPipeline()));
 }
 
+void WebGPURenderPassEncoder::setBlendColor(const GPUColor& color)
+{
+    if (!m_passEncoder) {
+        LOG(WebGPU, "GPURenderPassEncoder::setBlendColor(): Invalid operation!");
+        return;
+    }
+    m_passEncoder->setBlendColor(color);
+}
+
+void WebGPURenderPassEncoder::setViewport(float x, float y, float width, float height, float minDepth, float maxDepth)
+{
+    if (!m_passEncoder) {
+        LOG(WebGPU, "GPURenderPassEncoder::setViewport(): Invalid operation!");
+        return;
+    }
+    m_passEncoder->setViewport(x, y, width, height, minDepth, maxDepth);
+}
+
+void WebGPURenderPassEncoder::setScissorRect(unsigned x, unsigned y, unsigned width, unsigned height)
+{
+    if (!m_passEncoder) {
+        LOG(WebGPU, "GPURenderPassEncoder::setScissorRect(): Invalid operation!");
+        return;
+    }
+    if (!width || !height) {
+        LOG(WebGPU, "GPURenderPassEncoder::setScissorRect(): Width or height must be greater than 0!");
+        return;
+    }
+    m_passEncoder->setScissorRect(x, y, width, height);
+}
+
 void WebGPURenderPassEncoder::setVertexBuffers(unsigned startSlot, Vector<RefPtr<WebGPUBuffer>>&& buffers, Vector<uint64_t>&& offsets)
 {
 #if !LOG_DISABLED
index cc88375..662240f 100644 (file)
@@ -38,11 +38,16 @@ class GPURenderPassEncoder;
 class WebGPUBuffer;
 class WebGPURenderPipeline;
 
+struct GPUColor;
+
 class WebGPURenderPassEncoder final : public WebGPUProgrammablePassEncoder {
 public:
     static Ref<WebGPURenderPassEncoder> create(RefPtr<GPURenderPassEncoder>&&);
 
     void setPipeline(const WebGPURenderPipeline&);
+    void setBlendColor(const GPUColor&);
+    void setViewport(float x, float y, float width, float height, float minDepth, float maxDepth);
+    void setScissorRect(unsigned x, unsigned y, unsigned width, unsigned height);
     void setVertexBuffers(unsigned, Vector<RefPtr<WebGPUBuffer>>&&, Vector<uint64_t>&&);
     void draw(unsigned vertexCount, unsigned instanceCount, unsigned firstVertex, unsigned firstInstance);
 
index 0091327..a6e819a 100644 (file)
@@ -33,13 +33,20 @@ typedef unsigned long long u64;
     JSGenerateToJSObject
 ] interface WebGPURenderPassEncoder : WebGPUProgrammablePassEncoder {
     void setPipeline(WebGPURenderPipeline pipeline);
+    void setBlendColor(GPUColor color);
+
+    // The default viewport is (0.0, 0.0, w, h, 0.0, 1.0), where w and h are the dimensions of back buffer
+    void setViewport(float x, float y, float width, float height, float minDepth, float maxDepth);
+
+    // The default scissor rectangle is (0, 0, w, h), where w and h are the dimensions of back buffer.
+    // Width and height must be greater than 0. Otherwise, an error will be generated.
+    void setScissorRect(u32 x, u32 y, u32 width, u32 height);
 
     void setVertexBuffers(u32 startSlot, sequence<WebGPUBuffer> buffers, sequence<u64> offsets);
 
     void draw(u32 vertexCount, u32 instanceCount, u32 firstVertex, u32 firstInstance);
 
 /* Not Yet Implemented
-    void setBlendColor(float r, float g, float b, float a);
     void setIndexBuffer(WebGPUBuffer buffer, u64 offset);
 
     void drawIndexed(u32 indexCount, u32 instanceCount, u32 firstIndex, i32 baseVertex, u32 firstInstance);
index 8425b5f..bdc650c 100644 (file)
@@ -42,6 +42,7 @@ namespace WebCore {
 class GPUBuffer;
 class GPUCommandBuffer;
 
+struct GPUColor;
 struct GPURenderPassDescriptor;
 
 using PlatformRenderPassEncoder = MTLRenderCommandEncoder;
@@ -52,6 +53,9 @@ public:
     static RefPtr<GPURenderPassEncoder> tryCreate(Ref<GPUCommandBuffer>&&, GPURenderPassDescriptor&&);
 
     void setPipeline(Ref<const GPURenderPipeline>&&);
+    void setBlendColor(const GPUColor&);
+    void setViewport(float x, float y, float width, float height, float minDepth, float maxDepth);
+    void setScissorRect(unsigned x, unsigned y, unsigned width, unsigned height);
     void setVertexBuffers(unsigned, Vector<Ref<GPUBuffer>>&&, Vector<uint64_t>&&);
     void draw(unsigned vertexCount, unsigned instanceCount, unsigned firstVertex, unsigned firstInstance);
 
index d99415f..a6821d0 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(WEBGPU)
 
 #import "GPUBuffer.h"
+#import "GPUColor.h"
 #import "GPUCommandBuffer.h"
 #import "GPURenderPassDescriptor.h"
 #import "GPURenderPipeline.h"
@@ -204,6 +205,42 @@ void GPURenderPassEncoder::setPipeline(Ref<const GPURenderPipeline>&& pipeline)
     m_pipeline = WTFMove(pipeline);
 }
 
+void GPURenderPassEncoder::setBlendColor(const GPUColor& color)
+{
+    if (!m_platformRenderPassEncoder) {
+        LOG(WebGPU, "GPURenderPassEncoder::setBlendColor(): Invalid operation: Encoding is ended!");
+        return;
+    }
+
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    [m_platformRenderPassEncoder setBlendColorRed:color.r green:color.g blue:color.b alpha:color.a];
+    END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+void GPURenderPassEncoder::setViewport(float x, float y, float width, float height, float minDepth, float maxDepth)
+{
+    if (!m_platformRenderPassEncoder) {
+        LOG(WebGPU, "GPURenderPassEncoder::setViewport(): Invalid operation: Encoding is ended!");
+        return;
+    }
+
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    [m_platformRenderPassEncoder setViewport: { x, y, width, height, minDepth, maxDepth }];
+    END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+void GPURenderPassEncoder::setScissorRect(unsigned x, unsigned y, unsigned width, unsigned height)
+{
+    if (!m_platformRenderPassEncoder) {
+        LOG(WebGPU, "GPURenderPassEncoder::setScissorRect(): Invalid operation: Encoding is ended!");
+        return;
+    }
+
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    [m_platformRenderPassEncoder setScissorRect: { x, y, width, height }];
+    END_BLOCK_OBJC_EXCEPTIONS;
+}
+
 void GPURenderPassEncoder::setVertexBuffers(unsigned index, Vector<Ref<GPUBuffer>>&& buffers, Vector<uint64_t>&& offsets)
 {
     if (!m_platformRenderPassEncoder) {