[Web GPU] Indexed drawing and GPUCommandEncoder crash prevention
authorjustin_fan@apple.com <justin_fan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Apr 2019 20:48:38 +0000 (20:48 +0000)
committerjustin_fan@apple.com <justin_fan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Apr 2019 20:48:38 +0000 (20:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196758

Reviewed by Dean Jackson.

Source/WebCore:

Test: webgpu/draw-indexed-triangles.html

Implement GPURenderPassEncoder::setIndexBuffer and GPURenderPassEncoder::drawIndexed to enable indexed drawing.
Disable GPUCommandEncoders with active pass encoders from being submitted or encoding blits.

Prevent active GPUCommandEncoders from being submitted or encoding blit commands:
* Modules/webgpu/WebGPUCommandEncoder.cpp:
(WebCore::WebGPUCommandEncoder::finish):
* platform/graphics/gpu/cocoa/GPUCommandBufferMetal.mm:
(WebCore::GPUCommandBuffer::copyBufferToBuffer):
(WebCore::GPUCommandBuffer::copyBufferToTexture):
(WebCore::GPUCommandBuffer::copyTextureToBuffer):
(WebCore::GPUCommandBuffer::copyTextureToTexture):

Implement GPURenderPassEncoder::setIndexBuffer and GPURenderPassEncoder::drawIndexed:
* Modules/webgpu/WebGPURenderPassEncoder.cpp:
(WebCore::WebGPURenderPassEncoder::setIndexBuffer):
(WebCore::WebGPURenderPassEncoder::setVertexBuffers): Remove unnecessary move operations.
(WebCore::WebGPURenderPassEncoder::drawIndexed): Added.
* Modules/webgpu/WebGPURenderPassEncoder.h:
* Modules/webgpu/WebGPURenderPassEncoder.idl:
* platform/graphics/gpu/GPUBuffer.h:
(WebCore::GPUBuffer::isIndex const):
* platform/graphics/gpu/GPUInputStateDescriptor.h:
* platform/graphics/gpu/GPURenderPassEncoder.h: Cache the index buffer, as Metal does not set the index buffer separate from the draw call.
* platform/graphics/gpu/GPURenderPipeline.h:
(WebCore::GPURenderPipeline::indexFormat const):
* platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm:
(WebCore::GPURenderPassEncoder::setIndexBuffer):
(WebCore::GPURenderPassEncoder::setVertexBuffers):
(WebCore::mtlPrimitiveTypeForGPUPrimitiveTopology):
(WebCore::GPURenderPassEncoder::draw):
(WebCore::mtlIndexTypeForGPUIndexFormat): Added.
(WebCore::GPURenderPassEncoder::drawIndexed): Added.
(WebCore::primitiveTypeForGPUPrimitiveTopology): Deleted.
* platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm:
(WebCore::GPURenderPipeline::tryCreate):
(WebCore::GPURenderPipeline::GPURenderPipeline):

LayoutTests:

Add draw-indexed-triangles to test drawing a green square using GPURenderPassEncoder::setIndexBuffer and drawIndexed.

* webgpu/draw-indexed-triangles-expected.html: Added.
* webgpu/draw-indexed-triangles.html: Added.

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

15 files changed:
LayoutTests/ChangeLog
LayoutTests/webgpu/draw-indexed-triangles-expected.html [new file with mode: 0644]
LayoutTests/webgpu/draw-indexed-triangles.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/webgpu/WebGPUCommandEncoder.cpp
Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.cpp
Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.h
Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.idl
Source/WebCore/platform/graphics/gpu/GPUBuffer.h
Source/WebCore/platform/graphics/gpu/GPUInputStateDescriptor.h
Source/WebCore/platform/graphics/gpu/GPURenderPassEncoder.h
Source/WebCore/platform/graphics/gpu/GPURenderPipeline.h
Source/WebCore/platform/graphics/gpu/cocoa/GPUCommandBufferMetal.mm
Source/WebCore/platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm
Source/WebCore/platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm

index 9aa7847..9a3afbc 100644 (file)
@@ -1,3 +1,15 @@
+2019-04-10  Justin Fan  <justin_fan@apple.com>
+
+        [Web GPU] Indexed drawing and GPUCommandEncoder crash prevention
+        https://bugs.webkit.org/show_bug.cgi?id=196758
+
+        Reviewed by Dean Jackson.
+
+        Add draw-indexed-triangles to test drawing a green square using GPURenderPassEncoder::setIndexBuffer and drawIndexed.
+
+        * webgpu/draw-indexed-triangles-expected.html: Added.
+        * webgpu/draw-indexed-triangles.html: Added.
+
 2019-04-10  Megan Gardner  <megan_gardner@apple.com>
 
         Fix text autoscrolling when typing in modern webkit
 2019-04-10  Megan Gardner  <megan_gardner@apple.com>
 
         Fix text autoscrolling when typing in modern webkit
diff --git a/LayoutTests/webgpu/draw-indexed-triangles-expected.html b/LayoutTests/webgpu/draw-indexed-triangles-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/draw-indexed-triangles.html b/LayoutTests/webgpu/draw-indexed-triangles.html
new file mode 100644 (file)
index 0000000..4db5dfc
--- /dev/null
@@ -0,0 +1,122 @@
+<!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="draw-indexed-triangles-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 VertexIn
+{
+    float4 position [[attribute(0)]];
+    float green [[attribute(1)]];
+};
+
+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, vertexIn.green, 0, 1);
+    return vOut;
+}
+
+fragment float4 fragment_main(VertexOut v [[stage_in]])
+{
+    return v.color;
+}
+`
+
+function createVertexBuffer(device) {
+    const vertexArray = new Float32Array([
+        // float4 xyzw, float g
+        -1, 1, 0, 1, 0,
+        -1, 1, 0, 1, 1,
+        -1, -1, 0, 1, 0,
+        -1, -1, 0, 1, 1,
+        1, 1, 0, 1, 0,
+        1, 1, 0, 1, 1,
+        1, -1, 0, 1, 0,
+        1, -1, 0, 1, 1
+    ]);
+    const buffer = device.createBuffer({ size: vertexArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
+    buffer.setSubData(0, vertexArray.buffer);
+
+    return buffer;
+}
+
+const indexBufferOffset = 2048; // Test a buffer offset for index array.
+const indexOffset = -9001; // Test a base index to add to index array values.
+
+function createIndexBuffer(device) {
+    const offsetArray = [1, 3, 5, 3, 5, 7].map(v => { return v - indexOffset; });
+    const indexArray = new Uint32Array([1, 3, 5, 3, 5, 7].map(v => { return v - indexOffset; }));
+    const buffer = device.createBuffer({ size: indexArray.byteLength + indexBufferOffset, usage: GPUBufferUsage.INDEX | GPUBufferUsage.TRANSFER_DST });
+    buffer.setSubData(indexBufferOffset, indexArray.buffer);
+
+    return buffer;
+}
+
+function createInputStateDescriptor() {
+    return {
+        indexFormat: "uint32",
+        attributes: [{
+            shaderLocation: 0,
+            inputSlot: 0,
+            offset: 0,
+            format: "float4"
+        }, {
+            shaderLocation: 1,
+            inputSlot: 0,
+            offset: 4 * 4,
+            format: "float"
+        }],
+        inputs: [{
+            inputSlot: 0,
+            stride: 4 * 5,
+            stepMode: "vertex"
+        }]
+    };
+}
+
+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 vertexBuffer = createVertexBuffer(device);
+    const indexBuffer = createIndexBuffer(device);
+    const pipeline = createBasicPipeline(shaderModule, device, null, null, createInputStateDescriptor(), null, "triangle-list");
+    const commandEncoder = device.createCommandEncoder();
+    const passEncoder = beginBasicRenderPass(swapChain, commandEncoder);
+
+    passEncoder.setIndexBuffer(indexBuffer, indexBufferOffset);
+    passEncoder.setVertexBuffers(0, [vertexBuffer], [0]);
+    passEncoder.setPipeline(pipeline);
+    passEncoder.drawIndexed(6, 1, 0, indexOffset, 0);
+    passEncoder.endPass();
+
+    device.getQueue().submit([commandEncoder.finish()]);
+    vertexBuffer.destroy();
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+test();
+</script>
\ No newline at end of file
index 902a6fd..8b50737 100644 (file)
@@ -1,3 +1,49 @@
+2019-04-10  Justin Fan  <justin_fan@apple.com>
+
+        [Web GPU] Indexed drawing and GPUCommandEncoder crash prevention
+        https://bugs.webkit.org/show_bug.cgi?id=196758
+
+        Reviewed by Dean Jackson.
+
+        Test: webgpu/draw-indexed-triangles.html
+
+        Implement GPURenderPassEncoder::setIndexBuffer and GPURenderPassEncoder::drawIndexed to enable indexed drawing.
+        Disable GPUCommandEncoders with active pass encoders from being submitted or encoding blits. 
+
+        Prevent active GPUCommandEncoders from being submitted or encoding blit commands:
+        * Modules/webgpu/WebGPUCommandEncoder.cpp:
+        (WebCore::WebGPUCommandEncoder::finish):
+        * platform/graphics/gpu/cocoa/GPUCommandBufferMetal.mm:
+        (WebCore::GPUCommandBuffer::copyBufferToBuffer):
+        (WebCore::GPUCommandBuffer::copyBufferToTexture):
+        (WebCore::GPUCommandBuffer::copyTextureToBuffer):
+        (WebCore::GPUCommandBuffer::copyTextureToTexture):
+
+        Implement GPURenderPassEncoder::setIndexBuffer and GPURenderPassEncoder::drawIndexed:
+        * Modules/webgpu/WebGPURenderPassEncoder.cpp:
+        (WebCore::WebGPURenderPassEncoder::setIndexBuffer):
+        (WebCore::WebGPURenderPassEncoder::setVertexBuffers): Remove unnecessary move operations.
+        (WebCore::WebGPURenderPassEncoder::drawIndexed): Added.
+        * Modules/webgpu/WebGPURenderPassEncoder.h:
+        * Modules/webgpu/WebGPURenderPassEncoder.idl:
+        * platform/graphics/gpu/GPUBuffer.h:
+        (WebCore::GPUBuffer::isIndex const):
+        * platform/graphics/gpu/GPUInputStateDescriptor.h:
+        * platform/graphics/gpu/GPURenderPassEncoder.h: Cache the index buffer, as Metal does not set the index buffer separate from the draw call.
+        * platform/graphics/gpu/GPURenderPipeline.h:
+        (WebCore::GPURenderPipeline::indexFormat const):
+        * platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm:
+        (WebCore::GPURenderPassEncoder::setIndexBuffer):
+        (WebCore::GPURenderPassEncoder::setVertexBuffers):
+        (WebCore::mtlPrimitiveTypeForGPUPrimitiveTopology):
+        (WebCore::GPURenderPassEncoder::draw):
+        (WebCore::mtlIndexTypeForGPUIndexFormat): Added.
+        (WebCore::GPURenderPassEncoder::drawIndexed): Added.
+        (WebCore::primitiveTypeForGPUPrimitiveTopology): Deleted.
+        * platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm:
+        (WebCore::GPURenderPipeline::tryCreate):
+        (WebCore::GPURenderPipeline::GPURenderPipeline):
+
 2019-04-09  Ryosuke Niwa  <rniwa@webkit.org>
 
         OfflineAudioDestinationNode::startRendering leaks OfflineAudioDestinationNode if offlineRender exists early
 2019-04-09  Ryosuke Niwa  <rniwa@webkit.org>
 
         OfflineAudioDestinationNode::startRendering leaks OfflineAudioDestinationNode if offlineRender exists early
index c113a98..074bd09 100644 (file)
@@ -167,7 +167,7 @@ void WebGPUCommandEncoder::copyTextureToTexture(const WebGPUTextureCopyView& src
     
 Ref<WebGPUCommandBuffer> WebGPUCommandEncoder::finish()
 {
     
 Ref<WebGPUCommandBuffer> WebGPUCommandEncoder::finish()
 {
-    if (!m_commandBuffer) {
+    if (!m_commandBuffer || m_commandBuffer->isEncodingPass()) {
         LOG(WebGPU, "WebGPUCommandEncoder::finish(): Invalid operation!");
         return WebGPUCommandBuffer::create(nullptr);
     }
         LOG(WebGPU, "WebGPUCommandEncoder::finish(): Invalid operation!");
         return WebGPUCommandBuffer::create(nullptr);
     }
index 30591f6..0bf7a0d 100644 (file)
@@ -92,7 +92,21 @@ void WebGPURenderPassEncoder::setScissorRect(unsigned x, unsigned y, unsigned wi
     m_passEncoder->setScissorRect(x, y, width, height);
 }
 
     m_passEncoder->setScissorRect(x, y, width, height);
 }
 
-void WebGPURenderPassEncoder::setVertexBuffers(unsigned startSlot, Vector<RefPtr<WebGPUBuffer>>&& buffers, Vector<uint64_t>&& offsets)
+void WebGPURenderPassEncoder::setIndexBuffer(WebGPUBuffer& buffer, uint64_t offset)
+{
+    if (!m_passEncoder) {
+        LOG(WebGPU, "GPURenderPassEncoder::setIndexBuffer(): Invalid operation!");
+        return;
+    }
+    if (!buffer.buffer() || !buffer.buffer()->isIndex()) {
+        LOG(WebGPU, "GPURenderPassEncoder::setIndexBuffer(): Invalid GPUBuffer!");
+        return;
+    }
+
+    m_passEncoder->setIndexBuffer(*buffer.buffer(), offset);
+}
+
+void WebGPURenderPassEncoder::setVertexBuffers(unsigned startSlot, const Vector<RefPtr<WebGPUBuffer>>& buffers, const Vector<uint64_t>& offsets)
 {
 #if !LOG_DISABLED
     const char* const functionName = "GPURenderPassEncoder::setVertexBuffers()";
 {
 #if !LOG_DISABLED
     const char* const functionName = "GPURenderPassEncoder::setVertexBuffers()";
@@ -113,7 +127,7 @@ void WebGPURenderPassEncoder::setVertexBuffers(unsigned startSlot, Vector<RefPtr
     Vector<Ref<GPUBuffer>> gpuBuffers;
     gpuBuffers.reserveCapacity(buffers.size());
 
     Vector<Ref<GPUBuffer>> gpuBuffers;
     gpuBuffers.reserveCapacity(buffers.size());
 
-    for (const auto& buffer : buffers) {
+    for (auto& buffer : buffers) {
         if (!buffer || !buffer->buffer()) {
             LOG(WebGPU, "%s: Invalid or destroyed buffer in list!", functionName);
             return;
         if (!buffer || !buffer->buffer()) {
             LOG(WebGPU, "%s: Invalid or destroyed buffer in list!", functionName);
             return;
@@ -127,7 +141,7 @@ void WebGPURenderPassEncoder::setVertexBuffers(unsigned startSlot, Vector<RefPtr
         gpuBuffers.uncheckedAppend(makeRef(*buffer->buffer()));
     }
 
         gpuBuffers.uncheckedAppend(makeRef(*buffer->buffer()));
     }
 
-    m_passEncoder->setVertexBuffers(startSlot, WTFMove(gpuBuffers), WTFMove(offsets));
+    m_passEncoder->setVertexBuffers(startSlot, gpuBuffers, offsets);
 }
 
 void WebGPURenderPassEncoder::draw(unsigned vertexCount, unsigned instanceCount, unsigned firstVertex, unsigned firstInstance)
 }
 
 void WebGPURenderPassEncoder::draw(unsigned vertexCount, unsigned instanceCount, unsigned firstVertex, unsigned firstInstance)
@@ -140,6 +154,16 @@ void WebGPURenderPassEncoder::draw(unsigned vertexCount, unsigned instanceCount,
     m_passEncoder->draw(vertexCount, instanceCount, firstVertex, firstInstance);
 }
 
     m_passEncoder->draw(vertexCount, instanceCount, firstVertex, firstInstance);
 }
 
+void WebGPURenderPassEncoder::drawIndexed(unsigned indexCount, unsigned instanceCount, unsigned firstIndex, int baseVertex, unsigned firstInstance)
+{
+    if (!m_passEncoder) {
+        LOG(WebGPU, "GPURenderPassEncoder::draw(): Invalid operation!");
+        return;
+    }
+    // FIXME: Add Web GPU validation.
+    m_passEncoder->drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance);
+}
+
 GPUProgrammablePassEncoder* WebGPURenderPassEncoder::passEncoder()
 {
     return m_passEncoder.get();
 GPUProgrammablePassEncoder* WebGPURenderPassEncoder::passEncoder()
 {
     return m_passEncoder.get();
index 662240f..f8c02ab 100644 (file)
@@ -48,8 +48,10 @@ public:
     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 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 setIndexBuffer(WebGPUBuffer&, uint64_t offset);
+    void setVertexBuffers(unsigned startSlot, const Vector<RefPtr<WebGPUBuffer>>&, const Vector<uint64_t>& offsets);
     void draw(unsigned vertexCount, unsigned instanceCount, unsigned firstVertex, unsigned firstInstance);
     void draw(unsigned vertexCount, unsigned instanceCount, unsigned firstVertex, unsigned firstInstance);
+    void drawIndexed(unsigned indexCount, unsigned instanceCount, unsigned firstIndex, int baseVertex, unsigned firstInstance);
 
 private:
     WebGPURenderPassEncoder(RefPtr<GPURenderPassEncoder>&&);
 
 private:
     WebGPURenderPassEncoder(RefPtr<GPURenderPassEncoder>&&);
index a6e819a..2b85591 100644 (file)
@@ -24,6 +24,7 @@
  */
 // https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
 
  */
 // https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
 
+typedef long i32;
 typedef unsigned long u32;
 typedef unsigned long long u64;
 
 typedef unsigned long u32;
 typedef unsigned long long u64;
 
@@ -42,15 +43,13 @@ typedef unsigned long long u64;
     // Width and height must be greater than 0. Otherwise, an error will be generated.
     void setScissorRect(u32 x, u32 y, u32 width, u32 height);
 
     // 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 setIndexBuffer(WebGPUBuffer buffer, u64 offset);
     void setVertexBuffers(u32 startSlot, sequence<WebGPUBuffer> buffers, sequence<u64> offsets);
 
     void draw(u32 vertexCount, u32 instanceCount, u32 firstVertex, u32 firstInstance);
     void setVertexBuffers(u32 startSlot, sequence<WebGPUBuffer> buffers, sequence<u64> offsets);
 
     void draw(u32 vertexCount, u32 instanceCount, u32 firstVertex, u32 firstInstance);
-
-/* Not Yet Implemented
-    void setIndexBuffer(WebGPUBuffer buffer, u64 offset);
-
     void drawIndexed(u32 indexCount, u32 instanceCount, u32 firstIndex, i32 baseVertex, u32 firstInstance);
 
     void drawIndexed(u32 indexCount, u32 instanceCount, u32 firstIndex, i32 baseVertex, u32 firstInstance);
 
-    // TODO add missing commands
+/* Not Yet Implemented
+    void setStencilReference(u32 reference);
 */
 };
 */
 };
index 189d8d4..55dbea9 100644 (file)
@@ -74,6 +74,7 @@ public:
     uint64_t byteLength() const { return m_byteLength; }
     bool isTransferSource() const { return m_usage.contains(GPUBufferUsage::Flags::TransferSource); }
     bool isTransferDestination() const { return m_usage.contains(GPUBufferUsage::Flags::TransferDestination); }
     uint64_t byteLength() const { return m_byteLength; }
     bool isTransferSource() const { return m_usage.contains(GPUBufferUsage::Flags::TransferSource); }
     bool isTransferDestination() const { return m_usage.contains(GPUBufferUsage::Flags::TransferDestination); }
+    bool isIndex() const { return m_usage.contains(GPUBufferUsage::Flags::Index); }
     bool isVertex() const { return m_usage.contains(GPUBufferUsage::Flags::Vertex); }
     bool isUniform() const { return m_usage.contains(GPUBufferUsage::Flags::Uniform); }
     bool isStorage() const { return m_usage.contains(GPUBufferUsage::Flags::Storage); }
     bool isVertex() const { return m_usage.contains(GPUBufferUsage::Flags::Vertex); }
     bool isUniform() const { return m_usage.contains(GPUBufferUsage::Flags::Uniform); }
     bool isStorage() const { return m_usage.contains(GPUBufferUsage::Flags::Storage); }
index 0502336..02a6538 100644 (file)
@@ -39,7 +39,7 @@ enum class GPUIndexFormat {
 };
 
 struct GPUInputStateDescriptor {
 };
 
 struct GPUInputStateDescriptor {
-    GPUIndexFormat indexFormat;
+    Optional<GPUIndexFormat> indexFormat;
 
     Vector<GPUVertexAttributeDescriptor> attributes;
     Vector<GPUVertexInputDescriptor> inputs;
 
     Vector<GPUVertexAttributeDescriptor> attributes;
     Vector<GPUVertexInputDescriptor> inputs;
index bdc650c..41e3c4b 100644 (file)
@@ -56,8 +56,10 @@ public:
     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 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 setIndexBuffer(GPUBuffer&, uint64_t offset);
+    void setVertexBuffers(unsigned index, const Vector<Ref<GPUBuffer>>&, const Vector<uint64_t>& offsets);
     void draw(unsigned vertexCount, unsigned instanceCount, unsigned firstVertex, unsigned firstInstance);
     void draw(unsigned vertexCount, unsigned instanceCount, unsigned firstVertex, unsigned firstInstance);
+    void drawIndexed(unsigned indexCount, unsigned instanceCount, unsigned firstIndex, int baseVertex, unsigned firstInstance);
 
 private:
     GPURenderPassEncoder(Ref<GPUCommandBuffer>&&, PlatformRenderPassEncoderSmartPtr&&);
 
 private:
     GPURenderPassEncoder(Ref<GPUCommandBuffer>&&, PlatformRenderPassEncoderSmartPtr&&);
@@ -70,6 +72,9 @@ private:
     void useResource(const MTLResource *, unsigned usage) final;
     void setVertexBuffer(const MTLBuffer *, unsigned offset, unsigned index) final;
     void setFragmentBuffer(const MTLBuffer *, unsigned offset, unsigned index) final;
     void useResource(const MTLResource *, unsigned usage) final;
     void setVertexBuffer(const MTLBuffer *, unsigned offset, unsigned index) final;
     void setFragmentBuffer(const MTLBuffer *, unsigned offset, unsigned index) final;
+
+    RefPtr<GPUBuffer> m_indexBuffer;
+    uint64_t m_indexBufferOffset;
 #endif // USE(METAL)
 
     PlatformRenderPassEncoderSmartPtr m_platformRenderPassEncoder;
 #endif // USE(METAL)
 
     PlatformRenderPassEncoderSmartPtr m_platformRenderPassEncoder;
index 96d81cd..6e16532 100644 (file)
@@ -28,6 +28,7 @@
 #if ENABLE(WEBGPU)
 
 #include "GPURenderPipelineDescriptor.h"
 #if ENABLE(WEBGPU)
 
 #include "GPURenderPipelineDescriptor.h"
+#include <wtf/Optional.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
 #include <wtf/RetainPtr.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
 #include <wtf/RetainPtr.h>
@@ -53,15 +54,17 @@ public:
 #endif
     PlatformRenderPipeline* platformRenderPipeline() const { return m_platformRenderPipeline.get(); }
     GPUPrimitiveTopology primitiveTopology() const { return m_primitiveTopology; }
 #endif
     PlatformRenderPipeline* platformRenderPipeline() const { return m_platformRenderPipeline.get(); }
     GPUPrimitiveTopology primitiveTopology() const { return m_primitiveTopology; }
+    Optional<GPUIndexFormat> indexFormat() const { return m_indexFormat; }
 
 private:
 #if USE(METAL)
 
 private:
 #if USE(METAL)
-    GPURenderPipeline(RetainPtr<MTLDepthStencilState>&&, PlatformRenderPipelineSmartPtr&&, GPUPrimitiveTopology);
+    GPURenderPipeline(RetainPtr<MTLDepthStencilState>&&, PlatformRenderPipelineSmartPtr&&, GPUPrimitiveTopology, Optional<GPUIndexFormat>);
 
     RetainPtr<MTLDepthStencilState> m_depthStencilState;
 #endif // USE(METAL)
     PlatformRenderPipelineSmartPtr m_platformRenderPipeline;
     GPUPrimitiveTopology m_primitiveTopology;
 
     RetainPtr<MTLDepthStencilState> m_depthStencilState;
 #endif // USE(METAL)
     PlatformRenderPipelineSmartPtr m_platformRenderPipeline;
     GPUPrimitiveTopology m_primitiveTopology;
+    Optional<GPUIndexFormat> m_indexFormat;
 };
 
 } // namespace WebCore
 };
 
 } // namespace WebCore
index da21d24..7ebd5bf 100644 (file)
@@ -95,7 +95,7 @@ void GPUCommandBuffer::endBlitEncoding()
 
 void GPUCommandBuffer::copyBufferToBuffer(Ref<GPUBuffer>&& src, uint64_t srcOffset, Ref<GPUBuffer>&& dst, uint64_t dstOffset, uint64_t size)
 {
 
 void GPUCommandBuffer::copyBufferToBuffer(Ref<GPUBuffer>&& src, uint64_t srcOffset, Ref<GPUBuffer>&& dst, uint64_t dstOffset, uint64_t size)
 {
-    if (!src->isTransferSource() || !dst->isTransferDestination()) {
+    if (isEncodingPass() || !src->isTransferSource() || !dst->isTransferDestination()) {
         LOG(WebGPU, "GPUCommandBuffer::copyBufferToBuffer(): Invalid operation!");
         return;
     }
         LOG(WebGPU, "GPUCommandBuffer::copyBufferToBuffer(): Invalid operation!");
         return;
     }
@@ -132,7 +132,7 @@ void GPUCommandBuffer::copyBufferToBuffer(Ref<GPUBuffer>&& src, uint64_t srcOffs
 
 void GPUCommandBuffer::copyBufferToTexture(GPUBufferCopyView&& srcBuffer, GPUTextureCopyView&& dstTexture, const GPUExtent3D& size)
 {
 
 void GPUCommandBuffer::copyBufferToTexture(GPUBufferCopyView&& srcBuffer, GPUTextureCopyView&& dstTexture, const GPUExtent3D& size)
 {
-    if (!srcBuffer.buffer->isTransferSource() || !dstTexture.texture->isTransferDestination()) {
+    if (isEncodingPass() || !srcBuffer.buffer->isTransferSource() || !dstTexture.texture->isTransferDestination()) {
         LOG(WebGPU, "GPUComandBuffer::copyBufferToTexture(): Invalid operation!");
         return;
     }
         LOG(WebGPU, "GPUComandBuffer::copyBufferToTexture(): Invalid operation!");
         return;
     }
@@ -170,7 +170,7 @@ void GPUCommandBuffer::copyBufferToTexture(GPUBufferCopyView&& srcBuffer, GPUTex
 
 void GPUCommandBuffer::copyTextureToBuffer(GPUTextureCopyView&& srcTexture, GPUBufferCopyView&& dstBuffer, const GPUExtent3D& size)
 {
 
 void GPUCommandBuffer::copyTextureToBuffer(GPUTextureCopyView&& srcTexture, GPUBufferCopyView&& dstBuffer, const GPUExtent3D& size)
 {
-    if (!srcTexture.texture->isTransferSource() || !dstBuffer.buffer->isTransferDestination()) {
+    if (isEncodingPass() || !srcTexture.texture->isTransferSource() || !dstBuffer.buffer->isTransferDestination()) {
         LOG(WebGPU, "GPUCommandBuffer::copyTextureToBuffer(): Invalid operation!");
         return;
     }
         LOG(WebGPU, "GPUCommandBuffer::copyTextureToBuffer(): Invalid operation!");
         return;
     }
@@ -198,7 +198,7 @@ void GPUCommandBuffer::copyTextureToBuffer(GPUTextureCopyView&& srcTexture, GPUB
 
 void GPUCommandBuffer::copyTextureToTexture(GPUTextureCopyView&& src, GPUTextureCopyView&& dst, const GPUExtent3D& size)
 {
 
 void GPUCommandBuffer::copyTextureToTexture(GPUTextureCopyView&& src, GPUTextureCopyView&& dst, const GPUExtent3D& size)
 {
-    if (!src.texture->isTransferSource() || !dst.texture->isTransferDestination()) {
+    if (isEncodingPass() || !src.texture->isTransferSource() || !dst.texture->isTransferDestination()) {
         LOG(WebGPU, "GPUCommandBuffer::copyTextureToTexture(): Invalid operation!");
         return;
     }
         LOG(WebGPU, "GPUCommandBuffer::copyTextureToTexture(): Invalid operation!");
         return;
     }
index a6821d0..4c1d609 100644 (file)
@@ -38,6 +38,7 @@
 #import <Foundation/Foundation.h>
 #import <Metal/Metal.h>
 #import <wtf/BlockObjCExceptions.h>
 #import <Foundation/Foundation.h>
 #import <Metal/Metal.h>
 #import <wtf/BlockObjCExceptions.h>
+#import <wtf/CheckedArithmetic.h>
 
 namespace WebCore {
 
 
 namespace WebCore {
 
@@ -241,7 +242,23 @@ void GPURenderPassEncoder::setScissorRect(unsigned x, unsigned y, unsigned width
     END_BLOCK_OBJC_EXCEPTIONS;
 }
 
     END_BLOCK_OBJC_EXCEPTIONS;
 }
 
-void GPURenderPassEncoder::setVertexBuffers(unsigned index, Vector<Ref<GPUBuffer>>&& buffers, Vector<uint64_t>&& offsets)
+void GPURenderPassEncoder::setIndexBuffer(GPUBuffer& buffer, uint64_t offset)
+{
+    if (!m_platformRenderPassEncoder) {
+        LOG(WebGPU, "GPURenderPassEncoder::setIndexBuffer(): Invalid operation: Encoding is ended!");
+        return;
+    }
+    if (offset >= buffer.byteLength() || offset % 4) {
+        LOG(WebGPU, "GPURenderPassEncoder::setIndexBuffer(): Invalid offset!");
+        return;
+    }
+    ASSERT(buffer.platformBuffer());
+    // Buffer must be cached to provide it to Metal via drawIndexedPrimitives.
+    m_indexBuffer = makeRefPtr(buffer);
+    m_indexBufferOffset = offset;
+}
+
+void GPURenderPassEncoder::setVertexBuffers(unsigned index, const Vector<Ref<GPUBuffer>>& buffers, const Vector<uint64_t>& offsets)
 {
     if (!m_platformRenderPassEncoder) {
         LOG(WebGPU, "GPURenderPassEncoder::setVertexBuffers(): Invalid operation: Encoding is ended!");
 {
     if (!m_platformRenderPassEncoder) {
         LOG(WebGPU, "GPURenderPassEncoder::setVertexBuffers(): Invalid operation: Encoding is ended!");
@@ -254,6 +271,7 @@ void GPURenderPassEncoder::setVertexBuffers(unsigned index, Vector<Ref<GPUBuffer
 
     auto mtlBuffers = buffers.map([this] (auto& buffer) {
         commandBuffer().useBuffer(buffer.copyRef());
 
     auto mtlBuffers = buffers.map([this] (auto& buffer) {
         commandBuffer().useBuffer(buffer.copyRef());
+        ASSERT(buffer->platformBuffer());
         return buffer->platformBuffer();
     });
 
         return buffer->platformBuffer();
     });
 
@@ -264,7 +282,7 @@ void GPURenderPassEncoder::setVertexBuffers(unsigned index, Vector<Ref<GPUBuffer
     END_BLOCK_OBJC_EXCEPTIONS;
 }
 
     END_BLOCK_OBJC_EXCEPTIONS;
 }
 
-static MTLPrimitiveType primitiveTypeForGPUPrimitiveTopology(GPUPrimitiveTopology type)
+static MTLPrimitiveType mtlPrimitiveTypeForGPUPrimitiveTopology(GPUPrimitiveTopology type)
 {
     switch (type) {
     case GPUPrimitiveTopology::PointList:
 {
     switch (type) {
     case GPUPrimitiveTopology::PointList:
@@ -288,7 +306,6 @@ void GPURenderPassEncoder::draw(unsigned vertexCount, unsigned instanceCount, un
         LOG(WebGPU, "GPURenderPassEncoder::draw(): Invalid operation: Encoding is ended!");
         return;
     }
         LOG(WebGPU, "GPURenderPassEncoder::draw(): Invalid operation: Encoding is ended!");
         return;
     }
-
     if (!m_pipeline) {
         LOG(WebGPU, "GPURenderPassEncoder::draw(): No valid GPURenderPipeline found!");
         return;
     if (!m_pipeline) {
         LOG(WebGPU, "GPURenderPassEncoder::draw(): No valid GPURenderPipeline found!");
         return;
@@ -296,7 +313,7 @@ void GPURenderPassEncoder::draw(unsigned vertexCount, unsigned instanceCount, un
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
     [m_platformRenderPassEncoder 
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
     [m_platformRenderPassEncoder 
-        drawPrimitives:primitiveTypeForGPUPrimitiveTopology(m_pipeline->primitiveTopology())
+        drawPrimitives:mtlPrimitiveTypeForGPUPrimitiveTopology(m_pipeline->primitiveTopology())
         vertexStart:firstVertex
         vertexCount:vertexCount
         instanceCount:instanceCount
         vertexStart:firstVertex
         vertexCount:vertexCount
         instanceCount:instanceCount
@@ -304,6 +321,63 @@ void GPURenderPassEncoder::draw(unsigned vertexCount, unsigned instanceCount, un
     END_BLOCK_OBJC_EXCEPTIONS;
 }
 
     END_BLOCK_OBJC_EXCEPTIONS;
 }
 
+static MTLIndexType mtlIndexTypeForGPUIndexFormat(GPUIndexFormat format)
+{
+    switch (format) {
+    case GPUIndexFormat::Uint16:
+        return MTLIndexTypeUInt16;
+    case GPUIndexFormat::Uint32:
+        return MTLIndexTypeUInt32;
+    }
+
+    ASSERT_NOT_REACHED();
+}
+
+void GPURenderPassEncoder::drawIndexed(unsigned indexCount, unsigned instanceCount, unsigned firstIndex, int baseVertex, unsigned firstInstance)
+{
+#if !LOG_DISABLED
+    const char* const functionName = "GPURenderPassEncoder::drawIndexed()";
+#endif
+    if (!m_platformRenderPassEncoder) {
+        LOG(WebGPU, "%s: Invalid operation: Encoding is ended!", functionName);
+        return;
+    }
+    if (!m_pipeline) {
+        LOG(WebGPU, "%s: No valid GPURenderPipeline found!", functionName);
+        return;
+    }
+    if (!m_pipeline->indexFormat()) {
+        LOG(WebGPU, "%s: No GPUIndexFormat specified!", functionName);
+        return;
+    }
+    if (!m_indexBuffer || !m_indexBuffer->platformBuffer()) {
+        LOG(WebGPU, "%s: No valid index buffer set!", functionName);
+        return;
+    }
+
+    auto indexByteSize = (m_pipeline->indexFormat() == GPUIndexFormat::Uint16) ? sizeof(uint16_t) : sizeof(uint32_t);
+    uint64_t firstIndexOffset = firstIndex * indexByteSize;
+    auto totalOffset = checkedSum<uint64_t>(firstIndexOffset, m_indexBufferOffset);
+    if (totalOffset.hasOverflowed() || totalOffset >= m_indexBuffer->byteLength()) {
+        LOG(WebGPU, "%s: Invalid firstIndex!", functionName);
+        return;
+    }
+
+    commandBuffer().useBuffer(makeRef(*m_indexBuffer));
+
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    [m_platformRenderPassEncoder
+        drawIndexedPrimitives:mtlPrimitiveTypeForGPUPrimitiveTopology(m_pipeline->primitiveTopology())
+        indexCount:indexCount
+        indexType:mtlIndexTypeForGPUIndexFormat(*m_pipeline->indexFormat())
+        indexBuffer:m_indexBuffer->platformBuffer()
+        indexBufferOffset:totalOffset.unsafeGet()
+        instanceCount:instanceCount
+        baseVertex:baseVertex
+        baseInstance:firstInstance];
+    END_BLOCK_OBJC_EXCEPTIONS;
+}
+
 #if USE(METAL)
 
 void GPURenderPassEncoder::useResource(const MTLResource *resource, unsigned usage)
 #if USE(METAL)
 
 void GPURenderPassEncoder::useResource(const MTLResource *resource, unsigned usage)
index fbe2460..0df1def 100644 (file)
@@ -517,13 +517,14 @@ RefPtr<GPURenderPipeline> GPURenderPipeline::tryCreate(const GPUDevice& device,
     if (!pipeline)
         return nullptr;
 
     if (!pipeline)
         return nullptr;
 
-    return adoptRef(new GPURenderPipeline(WTFMove(depthStencil), WTFMove(pipeline), descriptor.primitiveTopology));
+    return adoptRef(new GPURenderPipeline(WTFMove(depthStencil), WTFMove(pipeline), descriptor.primitiveTopology, descriptor.inputState.indexFormat));
 }
 
 }
 
-GPURenderPipeline::GPURenderPipeline(RetainPtr<MTLDepthStencilState>&& depthStencil, RetainPtr<MTLRenderPipelineState>&& pipeline, GPUPrimitiveTopology topology)
+GPURenderPipeline::GPURenderPipeline(RetainPtr<MTLDepthStencilState>&& depthStencil, RetainPtr<MTLRenderPipelineState>&& pipeline, GPUPrimitiveTopology topology, Optional<GPUIndexFormat> format)
     : m_depthStencilState(WTFMove(depthStencil))
     , m_platformRenderPipeline(WTFMove(pipeline))
     , m_primitiveTopology(topology)
     : m_depthStencilState(WTFMove(depthStencil))
     , m_platformRenderPipeline(WTFMove(pipeline))
     , m_primitiveTopology(topology)
+    , m_indexFormat(format)
 {
 }
 
 {
 }