Title: [242148] trunk
Revision
242148
Author
justin_...@apple.com
Date
2019-02-27 13:10:24 -0800 (Wed, 27 Feb 2019)

Log Message

[Web GPU] Buffer updates part 2: setSubData, GPU/CPU synchronization
https://bugs.webkit.org/show_bug.cgi?id=195077
<rdar://problem/47805229>

Reviewed by Dean Jackson.

Source/WebCore:

Implement GPUBuffer.setSubData and prevent the resolving of mapping promises if the buffer is scheduled to be
used on the GPU, and add handlers to resolve such promises after command buffer execution completes. In addition,
update buffer sizes to u64 (unsigned long in C++) as per the updated Web GPU API.

Tests: webgpu/buffer-command-buffer-races.html
       webgpu/map-read-buffers.html

* Modules/webgpu/WebGPUBindGroup.h:
(WebCore::WebGPUBindGroup::bindGroup const):
* Modules/webgpu/WebGPUBindGroupDescriptor.cpp:
(WebCore::WebGPUBindGroupDescriptor::asGPUBindGroupDescriptor const): Rename binding -> bufferBinding to reduce confusion.
* Modules/webgpu/WebGPUBuffer.cpp: Small tweaks.
(WebCore::WebGPUBuffer::setSubData):
(WebCore::WebGPUBuffer::unmap): Correctly fail if buffer is destroyed.
(WebCore::WebGPUBuffer::destroy):
(WebCore::WebGPUBuffer::rejectOrRegisterPromiseCallback):
* Modules/webgpu/WebGPUBuffer.h:
(WebCore::WebGPUBuffer::buffer const): Returned buffer is no longer const so that it can be used in completed handler callbacks.
* Modules/webgpu/WebGPUBuffer.idl: Enable setSubData.
* Modules/webgpu/WebGPUCommandBuffer.cpp:
(WebCore::WebGPUCommandBuffer::beginRenderPass):
* Modules/webgpu/WebGPUProgrammablePassEncoder.cpp:
(WebCore::WebGPUProgrammablePassEncoder::setBindGroup const):
* Modules/webgpu/WebGPUProgrammablePassEncoder.h:
* Modules/webgpu/WebGPURenderPassEncoder.cpp:
(WebCore::WebGPURenderPassEncoder::setVertexBuffers):
* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* platform/graphics/gpu/GPUBuffer.h:
(WebCore::GPUBuffer::isTransferDst const): Added various state and flag getters.
(WebCore::GPUBuffer::isVertex const):
(WebCore::GPUBuffer::isUniform const):
(WebCore::GPUBuffer::isStorage const):
(WebCore::GPUBuffer::isReadOnly const):
(WebCore::GPUBuffer::isMappable const):
(WebCore::GPUBuffer::isMapWrite const):
(WebCore::GPUBuffer::isMapRead const):
(WebCore::GPUBuffer::isMapWriteable const):
(WebCore::GPUBuffer::isMapReadable const):
* platform/graphics/gpu/GPUBufferBinding.h:
* platform/graphics/gpu/GPUCommandBuffer.h:
(WebCore::GPUCommandBuffer::usedBuffers const):
(WebCore::GPUCommandBuffer::useBuffer):
* platform/graphics/gpu/GPUDevice.cpp:
(WebCore::GPUDevice::tryCreateBuffer): Pass Ref of itself for Buffer to request the Queue later, if needed.
(WebCore::GPUDevice::tryCreateBuffer const): Deleted.
* platform/graphics/gpu/GPUDevice.h:
* platform/graphics/gpu/GPUProgrammablePassEncoder.cpp: Retain the encoder's commandBuffer to reference its used resource buffers.
(WebCore::GPUProgrammablePassEncoder::GPUProgrammablePassEncoder):
* platform/graphics/gpu/GPUProgrammablePassEncoder.h:
(WebCore::GPUProgrammablePassEncoder::commandBuffer const):
* platform/graphics/gpu/GPURenderPassEncoder.h:
* platform/graphics/gpu/cocoa/GPUBufferMetal.mm:
(WebCore::GPUBuffer::validateBufferCreate): Move validation out of tryCreate.
(WebCore::GPUBuffer::tryCreate): Create both shared and private buffers, depending on usage.
(WebCore::GPUBuffer::GPUBuffer):
(WebCore::GPUBuffer::~GPUBuffer): Call destroy instead of just unmap.
(WebCore::GPUBuffer::state const):
(WebCore::GPUBuffer::setSubData): Uses a cached collection of staging MTLBuffers to encode data copies to the implementation MTLBuffer.
(WebCore::GPUBuffer::commandBufferCommitted): Register on the GPUBuffer that the provided MTLCommandBuffer uses it, and is about to be committed.
(WebCore::GPUBuffer::commandBufferCompleted): MTLCommandBuffer's onCompletedHandler calls this.
(WebCore::GPUBuffer::reuseSubDataBuffer): SetSubData's blit command buffers call this to return a staging buffer to the pool.
(WebCore::GPUBuffer::registerMappingCallback):
(WebCore::GPUBuffer::runMappingCallback):
(WebCore::GPUBuffer::unmap):
(WebCore::GPUBuffer::destroy):
(WebCore::GPUBuffer::tryCreateSharedBuffer): Deleted.
* platform/graphics/gpu/cocoa/GPUProgrammablePassEncoderMetal.mm:
(WebCore::GPUProgrammablePassEncoder::setResourceAsBufferOnEncoder):
(WebCore::GPUProgrammablePassEncoder::setBindGroup):
* platform/graphics/gpu/cocoa/GPUQueueMetal.mm:
(WebCore::GPUQueue::submit): Ensure submitted buffers are in the correct state, and add completed handlers for synchronization.
* platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm:
(WebCore::GPURenderPassEncoder::tryCreate):
(WebCore::GPURenderPassEncoder::GPURenderPassEncoder): Retain the commandBuffer for later reference.
(WebCore::GPURenderPassEncoder::setVertexBuffers): Mark vertex buffers as used before submission.
(WebCore::GPURenderPassEncoder::create): Deleted.

Buffer size updates in the IDL:
* Modules/webgpu/GPUBufferDescriptor.idl:
* Modules/webgpu/WebGPUBuffer.idl:
* Modules/webgpu/WebGPUBufferBinding.idl:
* Modules/webgpu/WebGPUCommandBuffer.idl:
* Modules/webgpu/WebGPURenderPassEncoder.idl:
* Modules/webgpu/WebGPUVertexAttributeDescriptor.idl:
* Modules/webgpu/WebGPUVertexInputDescriptor.idl:

LayoutTests:

Add tests for mapReadAysnc and setSubData calls. Nofity testRunner when done on some drawing tests
that may take more time.

* webgpu/buffer-command-buffer-races-expected.html: Added.
* webgpu/buffer-command-buffer-races.html: Added.
* webgpu/buffer-resource-triangles.html: Use setSubData.
* webgpu/depth-enabled-triangle-strip.html: Ditto.
* webgpu/map-read-buffers-expected.txt: Added.
* webgpu/map-read-buffers.html: Added.
* webgpu/vertex-buffer-triangle-strip.html: Use setSubData.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (242147 => 242148)


--- trunk/LayoutTests/ChangeLog	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/LayoutTests/ChangeLog	2019-02-27 21:10:24 UTC (rev 242148)
@@ -1,3 +1,22 @@
+2019-02-27  Justin Fan  <justin_...@apple.com>
+
+        [Web GPU] Buffer updates part 2: setSubData, GPU/CPU synchronization
+        https://bugs.webkit.org/show_bug.cgi?id=195077
+        <rdar://problem/47805229>
+
+        Reviewed by Dean Jackson.
+
+        Add tests for mapReadAysnc and setSubData calls. Nofity testRunner when done on some drawing tests
+        that may take more time.
+
+        * webgpu/buffer-command-buffer-races-expected.html: Added.
+        * webgpu/buffer-command-buffer-races.html: Added.
+        * webgpu/buffer-resource-triangles.html: Use setSubData.
+        * webgpu/depth-enabled-triangle-strip.html: Ditto.
+        * webgpu/map-read-buffers-expected.txt: Added.
+        * webgpu/map-read-buffers.html: Added.
+        * webgpu/vertex-buffer-triangle-strip.html: Use setSubData.
+
 2019-02-27  Antoine Quint  <grao...@apple.com>
 
         Support Pointer Events on macOS

Added: trunk/LayoutTests/webgpu/buffer-command-buffer-races-expected.html (0 => 242148)


--- trunk/LayoutTests/webgpu/buffer-command-buffer-races-expected.html	                        (rev 0)
+++ trunk/LayoutTests/webgpu/buffer-command-buffer-races-expected.html	2019-02-27 21:10:24 UTC (rev 242148)
@@ -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

Added: trunk/LayoutTests/webgpu/buffer-command-buffer-races.html (0 => 242148)


--- trunk/LayoutTests/webgpu/buffer-command-buffer-races.html	                        (rev 0)
+++ trunk/LayoutTests/webgpu/buffer-command-buffer-races.html	2019-02-27 21:10:24 UTC (rev 242148)
@@ -0,0 +1,159 @@
+<!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=""
+<p>Pass if square canvas below is completely green.</p>
+<canvas width="400" height="400"></canvas>
+<script src=""
+<script>
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
+const shaderCode = `
+#include <metal_stdlib>
+    
+using namespace metal;
+
+struct VertexIn
+{
+    float2 xy [[attribute(0)]];
+    float3 rgb [[attribute(1)]];
+};
+
+struct VertexOut
+{
+    float4 position [[position]];
+    float4 color;
+};
+
+vertex VertexOut vertex_main(VertexIn vertexIn [[stage_in]])
+{
+    VertexOut vOut;
+    vOut.position = float4(vertexIn.xy, 0, 1);
+    vOut.color = float4(vertexIn.rgb, 1);
+
+    return vOut;
+}
+
+fragment float4 fragment_main(VertexOut v [[stage_in]])
+{
+    return v.color;
+}
+`
+
+function createInputStateDescriptor() {
+    return {
+        indexFormat: WebGPUIndexFormat.UINT32,
+        attributes: [{
+            shaderLocation: 0,
+            inputSlot: 0,
+            offset: 0,
+            format: WebGPUVertexFormat.FLOAT_R32_G32
+        }, {
+            shaderLocation: 1,
+            inputSlot: 1,
+            offset: 0,
+            format: WebGPUVertexFormat.FLOAT_R32_G32_B32
+        }],
+        inputs: [{
+            inputSlot: 0,
+            stride: 4 * 2,
+            stepMode: WebGPUInputStepMode.VERTEX
+        }, {
+            inputSlot: 1,
+            stride: 4 * 3,
+            stepMode: WebGPUInputStepMode.INSTANCE
+        }]
+    }
+}
+
+function createAndSetVertexBuffer(device, vertices) {
+    const floatArray = new Float32Array(vertices);
+    const buffer = device.createBuffer({ size: floatArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
+    buffer.setSubData(0, floatArray.buffer);
+    return buffer;
+}
+
+function drawAndSubmitCommands(device, pipeline, attachment, vertexBuffer, colorBuffer) {
+    const commandBuffer = device.createCommandBuffer();
+    const encoder = commandBuffer.beginRenderPass({ colorAttachments: [attachment] });
+    encoder.setVertexBuffers(0, [vertexBuffer, colorBuffer], [0, 0]);
+    encoder.setPipeline(pipeline);
+    encoder.draw(3, 1, 0, 0);
+    encoder.endPass();
+    device.getQueue().submit([commandBuffer]);
+}
+
+async function test() {
+    const device = await getBasicDevice();
+    const canvas = document.querySelector("canvas");
+    const context = createBasicContext(canvas, device);
+    // FIXME: Replace with non-MSL shaders.
+    const shaderModule = device.createShaderModule({ code: shaderCode });
+    const inputStateDescriptor = createInputStateDescriptor();
+    const pipeline = createBasicPipeline(shaderModule, device, null, inputStateDescriptor);
+
+    const upperLeftBuffer = createAndSetVertexBuffer(device, [-1, 1, -1, -1, 0, 1]);
+    const middleBuffer = createAndSetVertexBuffer(device, [0, 1, -1, -1, 1, -1]);
+    const upperRightBuffer = createAndSetVertexBuffer(device, [0, 1, 1, 1, 1, -1]);
+
+    const green = [0, 1, 0];
+    const blue = [0, 0, 1];
+    const greenArray = new Float32Array(green);
+    const blueArray = new Float32Array(blue);
+
+    const colorBuffer = device.createBuffer({ size: greenArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST | GPUBufferUsage.MAP_WRITE });
+    colorBuffer.setSubData(0, greenArray.buffer);
+
+    const attachment = {
+        attachment: context.getNextTexture().createDefaultTextureView(),
+        loadOp: "load",
+        storeOp: "store",
+        clearColor: { r: 1, g: 0, b: 0, a: 1 }
+    };
+
+    /* mapWriteAsync should resolve after GPU commands are complete, so triangle should be green. */
+    drawAndSubmitCommands(device, pipeline, attachment, upperLeftBuffer, colorBuffer);
+    await colorBuffer.mapWriteAsync().then(ab => {
+        let array = new Float32Array(ab);
+        array.set(blue);
+        colorBuffer.unmap();
+    });
+
+    await colorBuffer.mapWriteAsync().then(ab => {
+        let array = new Float32Array(ab);
+        array.set(green);
+    });
+
+    /* colorBuffer that is still mapped should be not submitted to draw a blue triangle. */
+    drawAndSubmitCommands(device, pipeline, attachment, upperLeftBuffer, colorBuffer);
+
+    /* colorBuffer does not actually contain "green" again until this call. */
+    colorBuffer.unmap();
+
+    /* setSubData immediately after a submit should not affect the preceding draw call. */
+    drawAndSubmitCommands(device, pipeline, attachment, middleBuffer, colorBuffer);
+    colorBuffer.setSubData(0, blueArray.buffer);
+
+    /* destroy right after a submit should not affect the draw call. */
+    colorBuffer.setSubData(0, greenArray.buffer);
+    drawAndSubmitCommands(device, pipeline, attachment, upperRightBuffer, colorBuffer);
+    upperRightBuffer.destroy();
+
+    /* draw command with a destroyed buffer should fail */
+    colorBuffer.destroy();
+    drawAndSubmitCommands(device, pipeline, attachment, middleBuffer, colorBuffer);
+
+    upperLeftBuffer.destroy();
+    middleBuffer.destroy();
+    upperRightBuffer.destroy();
+    
+    context.present();
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+test();
+</script>
\ No newline at end of file

Modified: trunk/LayoutTests/webgpu/buffer-resource-triangles.html (242147 => 242148)


--- trunk/LayoutTests/webgpu/buffer-resource-triangles.html	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/LayoutTests/webgpu/buffer-resource-triangles.html	2019-02-27 21:10:24 UTC (rev 242148)
@@ -7,6 +7,9 @@
 <canvas width="400" height="400"></canvas>
 <script src=""
 <script>
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
 const shaderCode = `
 #include <metal_stdlib>
     
@@ -79,20 +82,15 @@
 
 const vertexSize = 4 * 4;
 const verticesBufferSize = vertexSize * 3;
-function createAndUploadVerticesBuffer(device, promises) {
-    const buffer = device.createBuffer({ size:verticesBufferSize, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE });
+function createAndUploadVerticesBuffer(device) {
+    const buffer = device.createBuffer({ size:verticesBufferSize, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
+    const arrayBuffer = new Float32Array([
+        0, 1, 0, 1,
+        -1, -1, 0, 1,
+        1, -1, 0, 1
+    ]).buffer;
 
-    const promise = buffer.mapWriteAsync().then(mapping => {
-        const mappedArray = new Float32Array(mapping);
-        mappedArray.set([
-            0, 1, 0, 1,
-            -1, -1, 0, 1,
-            1, -1, 0, 1
-        ]);
-        buffer.unmap();
-    });
-
-    promises.push(promise);
+    buffer.setSubData(0, arrayBuffer);
     return buffer;
 }
 
@@ -125,10 +123,9 @@
     const shaderModule = device.createShaderModule({ code: shaderCode });
 
     // Create vertex data WebGPUBuffers.
+    const verticesBuffer = createAndUploadVerticesBuffer(device);
+
     let bufferPromises = [];
-
-    const verticesBuffer = createAndUploadVerticesBuffer(device, bufferPromises);
-
     const upperLeft = createFloat4Buffer(device, -1, 1, bufferPromises);
     const upperMiddle = createFloat4Buffer(device, 0, 1, bufferPromises);
     const upperRight = createFloat4Buffer(device, 1, 1, bufferPromises);
@@ -212,6 +209,9 @@
         const queue = device.getQueue();
         queue.submit([endCommandBuffer]);
         context.present();
+
+        if (window.testRunner)
+            testRunner.notifyDone();
     });
 }
 

Modified: trunk/LayoutTests/webgpu/depth-enabled-triangle-strip.html (242147 => 242148)


--- trunk/LayoutTests/webgpu/depth-enabled-triangle-strip.html	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/LayoutTests/webgpu/depth-enabled-triangle-strip.html	2019-02-27 21:10:24 UTC (rev 242148)
@@ -7,6 +7,9 @@
 <canvas width="400" height="400"></canvas>
 <script src=""
 <script>
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
 const shaderCode = `
 #include <metal_stdlib>
     
@@ -44,23 +47,18 @@
 }
 `
 
-async function createVertexBuffer(device) {
+function createVertexBuffer(device) {
     const bufferSize = 4 * 4 * 4;
-    const buffer = device.createBuffer({ size: bufferSize, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE });
+    const buffer = device.createBuffer({ size: bufferSize, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
+    const arrayBuffer = new Float32Array([
+        // float4 xyzw
+        -1, 1, 0, 1,
+        -1, -1, 0, 1,
+        1, 1, 0, 1,
+        1, -1, 0, 1
+    ]).buffer;
     
-    let floatArray = new Float32Array(buffer.mapping);
-    await buffer.mapWriteAsync().then(mapping => {
-        let mappedArray = new Float32Array(mapping);
-        mappedArray.set([
-            // float4 xyzw
-            -1, 1, 0, 1,
-            -1, -1, 0, 1,
-            1, 1, 0, 1,
-            1, -1, 0, 1
-        ]);
-        buffer.unmap();
-    });
-
+    buffer.setSubData(0, arrayBuffer);
     return buffer;
 }
 
@@ -87,7 +85,7 @@
     const context = createBasicContext(canvas, device);
     // FIXME: Replace with non-MSL shaders.
     const shaderModule = device.createShaderModule({ code: shaderCode });
-    const vertexBuffer =  await createVertexBuffer(device);
+    const vertexBuffer = createVertexBuffer(device);
     const inputStateDescriptor = createInputStateDescriptor();
     const depthStateDescriptor = createBasicDepthStateDescriptor();
     const pipeline = createBasicPipeline(shaderModule, device, null, inputStateDescriptor, depthStateDescriptor);
@@ -119,6 +117,9 @@
 
     device.getQueue().submit([commandBuffer]);
     context.present();
+
+    if (window.testRunner)
+        testRunner.notifyDone();
 }
 
 test();

Added: trunk/LayoutTests/webgpu/map-read-buffers-expected.txt (0 => 242148)


--- trunk/LayoutTests/webgpu/map-read-buffers-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webgpu/map-read-buffers-expected.txt	2019-02-27 21:10:24 UTC (rev 242148)
@@ -0,0 +1,6 @@
+
+PASS setSubData, mapReadAsync, unmap, and destroy on a GPUBuffer. 
+PASS Reject a map read on a buffer not created with MAP_READ usage. 
+PASS Reject a map read on a mapped GPUBuffer. 
+PASS Reject a pending map read if GPUBuffer is unmapped. 
+

Added: trunk/LayoutTests/webgpu/map-read-buffers.html (0 => 242148)


--- trunk/LayoutTests/webgpu/map-read-buffers.html	                        (rev 0)
+++ trunk/LayoutTests/webgpu/map-read-buffers.html	2019-02-27 21:10:24 UTC (rev 242148)
@@ -0,0 +1,86 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:WebGPUEnabled=true ] -->
+<meta charset=utf-8>
+<title>Tests for setSubData and mapReadAsync on a GPUBuffer.</title>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script>
+async function runTests() {
+    const device =  await getBasicDevice();
+
+    // Basic mapReadAsync functionality
+    promise_test(async () => {
+        const buffer = device.createBuffer({ size: 16, usage: GPUBufferUsage.TRANSFER_DST | GPUBufferUsage.MAP_READ });
+        assert_true(buffer instanceof WebGPUBuffer, "createBuffer returned a WebGPUBuffer");
+
+        let array = new Float32Array([1, 2, 3, 4]);
+        buffer.setSubData(0, array.buffer);
+
+        let arrayBuffer = await buffer.mapReadAsync();
+        assert_true(arrayBuffer instanceof ArrayBuffer, "first mapReadAsync resolved successfully");
+
+        const readArray = new Float32Array(arrayBuffer);
+        assert_equals(readArray[3], 4, "successfully map-read value set by setSubData");
+
+        buffer.unmap();
+
+        buffer.setSubData(4 * 3, array.slice(0, 1).buffer);
+        let arrayBuffer1 = await buffer.mapReadAsync();
+        const readArray1 = new Float32Array(arrayBuffer1);
+        assert_equals(readArray[3], 1, "successfully setSubData with an offset");
+
+        buffer.unmap();
+
+        const promise = buffer.mapReadAsync(); // This will eventually reject due to buffer.destroy()
+
+        buffer.destroy();
+
+        await promise.then(() => {
+            assert_unreached("Buffer was destroyed!");
+        }, () => {});
+
+    }, "setSubData, mapReadAsync, unmap, and destroy on a GPUBuffer.");
+
+    /* Basic validation */
+    // FIXME: Test invalid combinations of GPUBufferUsage after implementing error handling.
+
+    promise_test(async () => {
+        const buffer = device.createBuffer({ size: 16, usage: GPUBufferUsage.MAP_WRITE });
+
+        await buffer.mapReadAsync().then(() => {
+            assert_unreached("Buffer was not created with MAP_READ!");
+        }, () => {});
+    }, "Reject a map read on a buffer not created with MAP_READ usage.");
+
+    /* Extended unmap/destroy and promise settling testing */
+
+    promise_test(async () => {
+        const buffer = device.createBuffer({ size: 16, usage: GPUBufferUsage.MAP_READ });
+
+        buffer.mapReadAsync().then(() => {
+            buffer.unmap();
+        }, () => { 
+            assert_unreached(); 
+        });
+
+        await buffer.mapReadAsync().then(() => {
+            assert_unreached("Map operation was invalid!"); // buffer was still in mapped state during promise creation
+        }, () => {});
+    }, "Reject a map read on a mapped GPUBuffer.");
+
+    promise_test(async () => {
+        const buffer = device.createBuffer({ size: 16, usage: GPUBufferUsage.MAP_READ });
+
+        const promise = buffer.mapReadAsync();
+        buffer.unmap();
+
+        await promise.then(() => {
+            assert_unreached("Buffer was unmapped!"); // buffer was unmapped, which rejects pending promises
+        },() => {});
+    }, "Reject a pending map read if GPUBuffer is unmapped.");
+}
+
+runTests();
+</script>
+</body>

Modified: trunk/LayoutTests/webgpu/vertex-buffer-triangle-strip.html (242147 => 242148)


--- trunk/LayoutTests/webgpu/vertex-buffer-triangle-strip.html	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/LayoutTests/webgpu/vertex-buffer-triangle-strip.html	2019-02-27 21:10:24 UTC (rev 242148)
@@ -7,6 +7,9 @@
 <canvas width="400" height="400"></canvas>
 <script src=""
 <script>
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
 const shaderCode = `
 #include <metal_stdlib>
     
@@ -38,22 +41,22 @@
 }
 `
 
-async function createVertexBuffer(device) {
+function createVertexBuffer(device) {
     const bufferSize = 4 * 5 * 4;
-    const buffer = device.createBuffer({ size: bufferSize, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE });
-    
-    await buffer.mapWriteAsync().then(mapping => {
-        let mappedArray = new Float32Array(mapping);
-        mappedArray.set([
-            // float4 xyzw, float g
-            -1, 1, 0, 1, 1,
-            -1, -1, 0, 1, 1,
-            1, 1, 0, 1, 1,
-            1, -1, 0, 1, 1
-        ]);
-        buffer.unmap();
-    });
+    const buffer = device.createBuffer({ size: bufferSize, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
+    const vertexArrayBuffer0 = new Float32Array([
+        // float4 xyzw, float g
+        -1, 1, 0, 1, 1,
+        -1, -1, 0, 1, 1
+    ]).buffer;
+    const vertexArrayBuffer1 = new Float32Array([
+        1, 1, 0, 1, 1,
+        1, -1, 0, 1, 1
+    ]).buffer;
 
+    buffer.setSubData(0, vertexArrayBuffer0);
+    buffer.setSubData(4 * 5 * 2, vertexArrayBuffer1);
+
     return buffer;
 }
 
@@ -85,7 +88,7 @@
     const context = createBasicContext(canvas, device);
     // FIXME: Replace with non-MSL shaders.
     const shaderModule = device.createShaderModule({ code: shaderCode });
-    const vertexBuffer = await createVertexBuffer(device);
+    const vertexBuffer = createVertexBuffer(device);
     const inputStateDescriptor = createInputStateDescriptor();
     const pipeline = createBasicPipeline(shaderModule, device, null, inputStateDescriptor);
     const commandBuffer = device.createCommandBuffer();
@@ -92,9 +95,12 @@
     const passEncoder = beginBasicRenderPass(context, commandBuffer);
     const endCommandBuffer = encodeBasicCommands(passEncoder, pipeline, vertexBuffer);
     const queue = device.getQueue();
-
     queue.submit([endCommandBuffer]);
+    vertexBuffer.destroy();
     context.present();
+
+    if (window.testRunner)
+        testRunner.notifyDone();
 }
 
 test();

Modified: trunk/Source/WebCore/ChangeLog (242147 => 242148)


--- trunk/Source/WebCore/ChangeLog	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/ChangeLog	2019-02-27 21:10:24 UTC (rev 242148)
@@ -1,3 +1,98 @@
+2019-02-27  Justin Fan  <justin_...@apple.com>
+
+        [Web GPU] Buffer updates part 2: setSubData, GPU/CPU synchronization
+        https://bugs.webkit.org/show_bug.cgi?id=195077
+        <rdar://problem/47805229>
+
+        Reviewed by Dean Jackson.
+
+        Implement GPUBuffer.setSubData and prevent the resolving of mapping promises if the buffer is scheduled to be 
+        used on the GPU, and add handlers to resolve such promises after command buffer execution completes. In addition,
+        update buffer sizes to u64 (unsigned long in C++) as per the updated Web GPU API.
+
+        Tests: webgpu/buffer-command-buffer-races.html
+               webgpu/map-read-buffers.html
+
+        * Modules/webgpu/WebGPUBindGroup.h:
+        (WebCore::WebGPUBindGroup::bindGroup const):
+        * Modules/webgpu/WebGPUBindGroupDescriptor.cpp:
+        (WebCore::WebGPUBindGroupDescriptor::asGPUBindGroupDescriptor const): Rename binding -> bufferBinding to reduce confusion.
+        * Modules/webgpu/WebGPUBuffer.cpp: Small tweaks.
+        (WebCore::WebGPUBuffer::setSubData):
+        (WebCore::WebGPUBuffer::unmap): Correctly fail if buffer is destroyed.
+        (WebCore::WebGPUBuffer::destroy):
+        (WebCore::WebGPUBuffer::rejectOrRegisterPromiseCallback):
+        * Modules/webgpu/WebGPUBuffer.h:
+        (WebCore::WebGPUBuffer::buffer const): Returned buffer is no longer const so that it can be used in completed handler callbacks.
+        * Modules/webgpu/WebGPUBuffer.idl: Enable setSubData.
+        * Modules/webgpu/WebGPUCommandBuffer.cpp:
+        (WebCore::WebGPUCommandBuffer::beginRenderPass):
+        * Modules/webgpu/WebGPUProgrammablePassEncoder.cpp:
+        (WebCore::WebGPUProgrammablePassEncoder::setBindGroup const):
+        * Modules/webgpu/WebGPUProgrammablePassEncoder.h:
+        * Modules/webgpu/WebGPURenderPassEncoder.cpp:
+        (WebCore::WebGPURenderPassEncoder::setVertexBuffers):
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/graphics/gpu/GPUBuffer.h:
+        (WebCore::GPUBuffer::isTransferDst const): Added various state and flag getters.
+        (WebCore::GPUBuffer::isVertex const):
+        (WebCore::GPUBuffer::isUniform const):
+        (WebCore::GPUBuffer::isStorage const):
+        (WebCore::GPUBuffer::isReadOnly const):
+        (WebCore::GPUBuffer::isMappable const):
+        (WebCore::GPUBuffer::isMapWrite const):
+        (WebCore::GPUBuffer::isMapRead const):
+        (WebCore::GPUBuffer::isMapWriteable const):
+        (WebCore::GPUBuffer::isMapReadable const):
+        * platform/graphics/gpu/GPUBufferBinding.h:
+        * platform/graphics/gpu/GPUCommandBuffer.h:
+        (WebCore::GPUCommandBuffer::usedBuffers const):
+        (WebCore::GPUCommandBuffer::useBuffer):
+        * platform/graphics/gpu/GPUDevice.cpp:
+        (WebCore::GPUDevice::tryCreateBuffer): Pass Ref of itself for Buffer to request the Queue later, if needed.
+        (WebCore::GPUDevice::tryCreateBuffer const): Deleted.
+        * platform/graphics/gpu/GPUDevice.h:
+        * platform/graphics/gpu/GPUProgrammablePassEncoder.cpp: Retain the encoder's commandBuffer to reference its used resource buffers.
+        (WebCore::GPUProgrammablePassEncoder::GPUProgrammablePassEncoder):
+        * platform/graphics/gpu/GPUProgrammablePassEncoder.h:
+        (WebCore::GPUProgrammablePassEncoder::commandBuffer const):
+        * platform/graphics/gpu/GPURenderPassEncoder.h:
+        * platform/graphics/gpu/cocoa/GPUBufferMetal.mm:
+        (WebCore::GPUBuffer::validateBufferCreate): Move validation out of tryCreate.
+        (WebCore::GPUBuffer::tryCreate): Create both shared and private buffers, depending on usage.
+        (WebCore::GPUBuffer::GPUBuffer):
+        (WebCore::GPUBuffer::~GPUBuffer): Call destroy instead of just unmap.
+        (WebCore::GPUBuffer::state const):
+        (WebCore::GPUBuffer::setSubData): Uses a cached collection of staging MTLBuffers to encode data copies to the implementation MTLBuffer.
+        (WebCore::GPUBuffer::commandBufferCommitted): Register on the GPUBuffer that the provided MTLCommandBuffer uses it, and is about to be committed.
+        (WebCore::GPUBuffer::commandBufferCompleted): MTLCommandBuffer's onCompletedHandler calls this.
+        (WebCore::GPUBuffer::reuseSubDataBuffer): SetSubData's blit command buffers call this to return a staging buffer to the pool.
+        (WebCore::GPUBuffer::registerMappingCallback):
+        (WebCore::GPUBuffer::runMappingCallback):
+        (WebCore::GPUBuffer::unmap):
+        (WebCore::GPUBuffer::destroy):
+        (WebCore::GPUBuffer::tryCreateSharedBuffer): Deleted.
+        * platform/graphics/gpu/cocoa/GPUProgrammablePassEncoderMetal.mm:
+        (WebCore::GPUProgrammablePassEncoder::setResourceAsBufferOnEncoder):
+        (WebCore::GPUProgrammablePassEncoder::setBindGroup):
+        * platform/graphics/gpu/cocoa/GPUQueueMetal.mm:
+        (WebCore::GPUQueue::submit): Ensure submitted buffers are in the correct state, and add completed handlers for synchronization.
+        * platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm:
+        (WebCore::GPURenderPassEncoder::tryCreate):
+        (WebCore::GPURenderPassEncoder::GPURenderPassEncoder): Retain the commandBuffer for later reference.
+        (WebCore::GPURenderPassEncoder::setVertexBuffers): Mark vertex buffers as used before submission.
+        (WebCore::GPURenderPassEncoder::create): Deleted.
+
+        Buffer size updates in the IDL:
+        * Modules/webgpu/GPUBufferDescriptor.idl:
+        * Modules/webgpu/WebGPUBuffer.idl:
+        * Modules/webgpu/WebGPUBufferBinding.idl:
+        * Modules/webgpu/WebGPUCommandBuffer.idl:
+        * Modules/webgpu/WebGPURenderPassEncoder.idl:
+        * Modules/webgpu/WebGPUVertexAttributeDescriptor.idl:
+        * Modules/webgpu/WebGPUVertexInputDescriptor.idl:
+
 2019-02-27  Youenn Fablet  <you...@apple.com>
 
         Remove LeetCode FetchRequest quirk

Modified: trunk/Source/WebCore/Modules/webgpu/GPUBufferDescriptor.idl (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/GPUBufferDescriptor.idl	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/GPUBufferDescriptor.idl	2019-02-27 21:10:24 UTC (rev 242148)
@@ -24,13 +24,13 @@
  */
 // https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
 
-typedef unsigned long u32;
-typedef u32 GPUBufferUsageFlags;
+typedef unsigned long long u64;
+typedef unsigned long GPUBufferUsageFlags;
 
 [
     Conditional=WEBGPU,
     EnabledAtRuntime=WebGPU
 ] dictionary GPUBufferDescriptor {
-    u32 size;
+    u64 size;
     GPUBufferUsageFlags usage;
 };

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUBindGroup.h (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUBindGroup.h	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUBindGroup.h	2019-02-27 21:10:24 UTC (rev 242148)
@@ -37,7 +37,7 @@
 public:
     static Ref<WebGPUBindGroup> create(RefPtr<GPUBindGroup>&&);
 
-    const GPUBindGroup* bindGroup() const { return m_bindGroup.get(); }
+    GPUBindGroup* bindGroup() const { return m_bindGroup.get(); }
 
 private:
     explicit WebGPUBindGroup(RefPtr<GPUBindGroup>&&);

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUBindGroupDescriptor.cpp (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUBindGroupDescriptor.cpp	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUBindGroupDescriptor.cpp	2019-02-27 21:10:24 UTC (rev 242148)
@@ -86,7 +86,7 @@
             return WTF::nullopt;
         }
 
-        auto layoutBinding = iterator->value;
+        const auto layoutBinding = iterator->value;
 
         auto bindingResourceVisitor = WTF::makeVisitor([] (RefPtr<WebGPUTextureView> view) -> Optional<GPUBindingResource> {
             // FIXME: Validate binding type with the texture's usage flags.
@@ -94,15 +94,15 @@
                 return WTF::nullopt;
 
             return static_cast<GPUBindingResource>(view->texture());
-        }, [&layoutBinding, functionName] (const WebGPUBufferBinding& binding) -> Optional<GPUBindingResource> {
-            if (!binding.buffer || !binding.buffer->buffer()) {
+        }, [&layoutBinding, functionName] (WebGPUBufferBinding bufferBinding) -> Optional<GPUBindingResource> {
+            if (!bufferBinding.buffer || !bufferBinding.buffer->buffer()) {
                 LOG(WebGPU, "%s: Invalid GPUBufferBinding for binding %lu in GPUBindGroupBindings!", functionName, layoutBinding.binding);
                 return WTF::nullopt;
             }
-            if (!validateBufferBindingType(binding.buffer->buffer().get(), layoutBinding, functionName))
+            if (!validateBufferBindingType(bufferBinding.buffer->buffer().get(), layoutBinding, functionName))
                 return WTF::nullopt;
 
-            return static_cast<GPUBindingResource>(GPUBufferBinding { binding.buffer->buffer().releaseNonNull(), binding.offset, binding.size });
+            return static_cast<GPUBindingResource>(GPUBufferBinding { bufferBinding.buffer->buffer().releaseNonNull(), bufferBinding.offset, bufferBinding.size });
         });
 
         auto bindingResource = WTF::visit(bindingResourceVisitor, binding.resource);

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp	2019-02-27 21:10:24 UTC (rev 242148)
@@ -42,6 +42,14 @@
 {
 }
 
+void WebGPUBuffer::setSubData(unsigned long offset, const JSC::ArrayBuffer& data)
+{
+    if (!m_buffer)
+        LOG(WebGPU, "GPUBuffer::setSubData(): Invalid operation!");
+    else
+        m_buffer->setSubData(offset, data);
+}
+
 void WebGPUBuffer::mapReadAsync(BufferMappingPromise&& promise)
 {
     rejectOrRegisterPromiseCallback(WTFMove(promise), true);
@@ -54,7 +62,9 @@
 
 void WebGPUBuffer::unmap()
 {
-    if (m_buffer)
+    if (!m_buffer)
+        LOG(WebGPU, "GPUBuffer::unmap(): Invalid operation!");
+    else
         m_buffer->unmap();
 }
 
@@ -64,7 +74,6 @@
         LOG(WebGPU, "GPUBuffer::destroy(): Invalid operation!");
     else {
         m_buffer->destroy();
-        // FIXME: Ensure that GPUBuffer is kept alive by resource bindings if still being used by GPU.
         m_buffer = nullptr;
     }
 }
@@ -78,12 +87,10 @@
     }
 
     m_buffer->registerMappingCallback([promise = WTFMove(promise)] (JSC::ArrayBuffer* arrayBuffer) mutable {
-        if (!arrayBuffer) {
+        if (arrayBuffer)
+            promise.resolve(*arrayBuffer);
+        else
             promise.reject();
-            return;
-        }
-
-        promise.resolve(arrayBuffer);
     }, isRead);
 }
 

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUBuffer.h (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUBuffer.h	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUBuffer.h	2019-02-27 21:10:24 UTC (rev 242148)
@@ -45,9 +45,10 @@
 public:
     static Ref<WebGPUBuffer> create(RefPtr<GPUBuffer>&&);
 
-    RefPtr<const GPUBuffer> buffer() const { return m_buffer; }
+    RefPtr<GPUBuffer> buffer() const { return m_buffer; }
 
-    using BufferMappingPromise = DOMPromiseDeferred<IDLInterface<JSC::ArrayBuffer*>>;
+    void setSubData(unsigned long, const JSC::ArrayBuffer&);
+    using BufferMappingPromise = DOMPromiseDeferred<IDLInterface<JSC::ArrayBuffer>>;
     void mapReadAsync(BufferMappingPromise&&);
     void mapWriteAsync(BufferMappingPromise&&);
     void unmap();

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUBuffer.idl (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUBuffer.idl	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUBuffer.idl	2019-02-27 21:10:24 UTC (rev 242148)
@@ -22,13 +22,16 @@
  * 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 long u64;
+
 [
     Conditional=WEBGPU,
     EnabledAtRuntime=WebGPU,
     ImplementationLacksVTable
 ] interface WebGPUBuffer {
-    //void setSubData(u32 offset, ArrayBuffer data);
+    void setSubData(u64 offset, ArrayBuffer data);
 
     Promise<ArrayBuffer> mapReadAsync();
     Promise<ArrayBuffer> mapWriteAsync();

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUBufferBinding.idl (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUBufferBinding.idl	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUBufferBinding.idl	2019-02-27 21:10:24 UTC (rev 242148)
@@ -24,7 +24,7 @@
  */
 // https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
 
-typedef unsigned long u32;
+typedef unsigned long long u64;
 
 [
     Conditional=WEBGPU,
@@ -31,6 +31,6 @@
     EnabledAtRuntime=WebGPU
 ] dictionary WebGPUBufferBinding {
     WebGPUBuffer buffer;
-    u32 offset;
-    u32 size;
+    u64 offset;
+    u64 size;
 };

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUCommandBuffer.cpp (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUCommandBuffer.cpp	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUCommandBuffer.cpp	2019-02-27 21:10:24 UTC (rev 242148)
@@ -51,7 +51,7 @@
     if (!gpuDescriptor)
         return nullptr;
 
-    if (auto encoder = GPURenderPassEncoder::create(m_commandBuffer.get(), WTFMove(*gpuDescriptor)))
+    if (auto encoder = GPURenderPassEncoder::tryCreate(m_commandBuffer.copyRef(), WTFMove(*gpuDescriptor)))
         return WebGPURenderPassEncoder::create(*this, encoder.releaseNonNull());
     return nullptr;
 }

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUCommandBuffer.idl (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUCommandBuffer.idl	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUCommandBuffer.idl	2019-02-27 21:10:24 UTC (rev 242148)
@@ -37,10 +37,10 @@
     // Commands allowed outside of "passes"
         void copyBufferToBuffer(
         WebGPUBuffer src,
-        u32 srcOffset,
+        u64 srcOffset,
         WebGPUBuffer dst,
-        u32 dstOffset,
-        u32 size);
+        u64 dstOffset,
+        u64 size);
 
     void copyBufferToTexture(
         WebGPUBufferCopyView source,

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUProgrammablePassEncoder.cpp (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUProgrammablePassEncoder.cpp	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUProgrammablePassEncoder.cpp	2019-02-27 21:10:24 UTC (rev 242148)
@@ -45,7 +45,7 @@
     return m_commandBuffer.copyRef();
 }
 
-void WebGPUProgrammablePassEncoder::setBindGroup(unsigned long index, const WebGPUBindGroup& bindGroup) const
+void WebGPUProgrammablePassEncoder::setBindGroup(unsigned long index, WebGPUBindGroup& bindGroup) const
 {
     // Maximum number of bind groups supported in Web GPU.
     if (index >= 4) {

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUProgrammablePassEncoder.h (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUProgrammablePassEncoder.h	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUProgrammablePassEncoder.h	2019-02-27 21:10:24 UTC (rev 242148)
@@ -41,7 +41,7 @@
     virtual ~WebGPUProgrammablePassEncoder() = default;
 
     Ref<WebGPUCommandBuffer> endPass();
-    void setBindGroup(unsigned long, const WebGPUBindGroup&) const;
+    void setBindGroup(unsigned long, WebGPUBindGroup&) const;
     void setPipeline(Ref<WebGPURenderPipeline>&&);
 
 protected:

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.cpp (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.cpp	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.cpp	2019-02-27 21:10:24 UTC (rev 242148)
@@ -62,7 +62,7 @@
         return;
     }
 
-    Vector<Ref<const GPUBuffer>> gpuBuffers;
+    Vector<Ref<GPUBuffer>> gpuBuffers;
     gpuBuffers.reserveCapacity(buffers.size());
 
     for (const auto& buffer : buffers) {

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.idl (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.idl	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPURenderPassEncoder.idl	2019-02-27 21:10:24 UTC (rev 242148)
@@ -25,6 +25,7 @@
 // https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
 
 typedef unsigned long u32;
+typedef unsigned long long u64;
 
 [
     Conditional=WEBGPU,
@@ -31,14 +32,14 @@
     EnabledAtRuntime=WebGPU,
     JSGenerateToJSObject
 ] interface WebGPURenderPassEncoder : WebGPUProgrammablePassEncoder {
-    // FIXME: (<rdar://problem/47717832>) Last argument should be sequence<unsigned long>, but bindings generates Vector<unsigned int>.
-    void setVertexBuffers(u32 startSlot, sequence<WebGPUBuffer> buffers, sequence<unsigned long long> offsets);
+    // FIXME: (<rdar://problem/47717832>) Last argument should be generated to Vector<unsigned long>, but bindings generates Vector<unsigned long long>.
+    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, u32 offset);
+    void setIndexBuffer(WebGPUBuffer buffer, u64 offset);
 
     void drawIndexed(u32 indexCount, u32 instanceCount, u32 firstIndex, i32 baseVertex, u32 firstInstance);
 

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUVertexAttributeDescriptor.idl (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUVertexAttributeDescriptor.idl	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUVertexAttributeDescriptor.idl	2019-02-27 21:10:24 UTC (rev 242148)
@@ -25,6 +25,7 @@
 // https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
 
 typedef unsigned long u32;
+typedef unsigned long long u64;
 typedef u32 WebGPUVertexFormatEnum;
 
 [
@@ -33,6 +34,6 @@
 ] dictionary WebGPUVertexAttributeDescriptor {
     u32 shaderLocation;
     u32 inputSlot;
-    u32 offset;
+    u64 offset;
     WebGPUVertexFormatEnum format;
 };

Modified: trunk/Source/WebCore/Modules/webgpu/WebGPUVertexInputDescriptor.idl (242147 => 242148)


--- trunk/Source/WebCore/Modules/webgpu/WebGPUVertexInputDescriptor.idl	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Modules/webgpu/WebGPUVertexInputDescriptor.idl	2019-02-27 21:10:24 UTC (rev 242148)
@@ -25,6 +25,7 @@
 // https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
 
 typedef unsigned long u32;
+typedef unsigned long long u64;
 typedef u32 WebGPUInputStepModeEnum;
 
 [
@@ -32,6 +33,6 @@
     EnabledAtRuntime=WebGPU
 ] dictionary WebGPUVertexInputDescriptor {
     u32 inputSlot;
-    u32 stride;
+    u64 stride;
     WebGPUInputStepModeEnum stepMode;
 };

Modified: trunk/Source/WebCore/Sources.txt (242147 => 242148)


--- trunk/Source/WebCore/Sources.txt	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/Sources.txt	2019-02-27 21:10:24 UTC (rev 242148)
@@ -1811,6 +1811,7 @@
 platform/graphics/gpu/GPUBindGroup.cpp
 platform/graphics/gpu/GPUDevice.cpp
 platform/graphics/gpu/GPUPipelineLayout.cpp
+platform/graphics/gpu/GPUProgrammablePassEncoder.cpp
 platform/graphics/gpu/legacy/GPULegacyBuffer.cpp
 platform/graphics/gpu/legacy/GPULegacyCommandBuffer.cpp
 platform/graphics/gpu/legacy/GPULegacyCommandQueue.cpp

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (242147 => 242148)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2019-02-27 21:10:24 UTC (rev 242148)
@@ -13939,6 +13939,7 @@
 		D003288421C9A20D00622AA6 /* GPUPipelineLayoutDescriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPUPipelineLayoutDescriptor.h; sourceTree = "<group>"; };
 		D003288621C9A4E500622AA6 /* GPUPipelineLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPUPipelineLayout.h; sourceTree = "<group>"; };
 		D003288721C9A4E500622AA6 /* GPUPipelineLayout.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = GPUPipelineLayout.cpp; sourceTree = "<group>"; };
+		D003914122248D400098998C /* GPUProgrammablePassEncoder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = GPUProgrammablePassEncoder.cpp; sourceTree = "<group>"; };
 		D00F5940216ECC7A000D71DB /* DOMWindowWebGPU.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DOMWindowWebGPU.h; sourceTree = "<group>"; };
 		D00F5941216ECC7A000D71DB /* DOMWindowWebGPU.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DOMWindowWebGPU.cpp; sourceTree = "<group>"; };
 		D00F5942216ECC7A000D71DB /* DOMWindowWebGPU.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = DOMWindowWebGPU.idl; sourceTree = "<group>"; };
@@ -18482,6 +18483,7 @@
 				D003288621C9A4E500622AA6 /* GPUPipelineLayout.h */,
 				D003288421C9A20D00622AA6 /* GPUPipelineLayoutDescriptor.h */,
 				312FF8C221A4C2F300EB199D /* GPUPipelineStageDescriptor.h */,
+				D003914122248D400098998C /* GPUProgrammablePassEncoder.cpp */,
 				D03211CF21AC954E00763CF2 /* GPUProgrammablePassEncoder.h */,
 				312FF8C121A4C2F200EB199D /* GPUQueue.h */,
 				D03211D021AC954F00763CF2 /* GPURenderPassDescriptor.h */,

Modified: trunk/Source/WebCore/platform/graphics/gpu/GPUBuffer.h (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/GPUBuffer.h	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/GPUBuffer.h	2019-02-27 21:10:24 UTC (rev 242148)
@@ -33,8 +33,12 @@
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RetainPtr.h>
+#include <wtf/Vector.h>
 
+#if USE(METAL)
 OBJC_PROTOCOL(MTLBuffer);
+OBJC_PROTOCOL(MTLCommandBuffer);
+#endif
 
 namespace JSC {
 class ArrayBuffer;
@@ -46,21 +50,42 @@
 
 struct GPUBufferDescriptor;
 
+#if USE(METAL)
 using PlatformBuffer = MTLBuffer;
-using PlatformBufferSmartPtr = RetainPtr<MTLBuffer>;
+#else
+using PlatformBuffer = void;
+#endif
+using PlatformBufferSmartPtr = RetainPtr<PlatformBuffer>;
 
 class GPUBuffer : public RefCounted<GPUBuffer> {
 public:
+    enum class State {
+        Mapped,
+        Unmapped,
+        Destroyed
+    };
+
     ~GPUBuffer();
 
-    static RefPtr<GPUBuffer> tryCreate(const GPUDevice&, GPUBufferDescriptor&&);
+    static RefPtr<GPUBuffer> tryCreate(Ref<GPUDevice>&&, GPUBufferDescriptor&&);
 
     PlatformBuffer *platformBuffer() const { return m_platformBuffer.get(); }
-    bool isVertex() const { return m_isVertex; }
-    bool isUniform() const { return m_isUniform; }
-    bool isStorage() const { return m_isStorage; }
-    bool isReadOnly() const { return m_isReadOnly; }
+    bool isTransferDst() const { return m_usage & GPUBufferUsage::TransferDst; }
+    bool isVertex() const { return m_usage & GPUBufferUsage::Vertex; }
+    bool isUniform() const { return m_usage & GPUBufferUsage::Uniform; }
+    bool isStorage() const { return m_usage & GPUBufferUsage::Storage; }
+    bool isReadOnly() const;
+    bool isMappable() const { return m_usage & (GPUBufferUsage::MapWrite | GPUBufferUsage::MapRead); }
+    State state() const;
 
+#if USE(METAL)
+    void commandBufferCommitted(MTLCommandBuffer *);
+    void commandBufferCompleted();
+
+    void reuseSubDataBuffer(RetainPtr<MTLBuffer>&&);
+#endif
+
+    void setSubData(unsigned long, const JSC::ArrayBuffer&);
     using MappingCallback = WTF::Function<void(JSC::ArrayBuffer*)>;
     void registerMappingCallback(MappingCallback&&, bool);
     void unmap();
@@ -79,31 +104,33 @@
         PendingMappingCallback(MappingCallback&&);
     };
 
-    GPUBuffer(PlatformBufferSmartPtr&&, const GPUBufferDescriptor&);
+    static bool validateBufferCreate(const GPUDevice&, const GPUBufferDescriptor&);
 
-    static RefPtr<GPUBuffer> tryCreateSharedBuffer(const GPUDevice&, const GPUBufferDescriptor&);
+    GPUBuffer(PlatformBufferSmartPtr&&, const GPUBufferDescriptor&, Ref<GPUDevice>&&);
+
     JSC::ArrayBuffer* stagingBufferForRead();
     JSC::ArrayBuffer* stagingBufferForWrite();
+    void runMappingCallback();
 
-    bool isMappable() const { return m_isMapWrite || m_isMapRead; }
-    bool isMapWriteable() const { return m_isMapWrite && !m_pendingCallback; }
-    bool isMapReadable() const { return m_isMapRead && !m_pendingCallback; }
+    bool isMapWrite() const { return m_usage & GPUBufferUsage::MapWrite; }
+    bool isMapRead() const { return m_usage & GPUBufferUsage::MapRead; }
+    bool isMapWriteable() const { return isMapWrite() && state() == State::Unmapped; }
+    bool isMapReadable() const { return isMapRead() && state() == State::Unmapped; }
 
     PlatformBufferSmartPtr m_platformBuffer;
+    Ref<GPUDevice> m_device;
 
+#if USE(METAL)
+    Vector<RetainPtr<MTLBuffer>> m_subDataBuffers;
+#endif
+
     RefPtr<JSC::ArrayBuffer> m_stagingBuffer;
-    RefPtr<PendingMappingCallback> m_pendingCallback;
+    RefPtr<PendingMappingCallback> m_mappingCallback;
     DeferrableTask<Timer> m_mappingCallbackTask;
 
     unsigned long m_byteLength;
+    GPUBufferUsage::Flags m_usage;
     unsigned m_numScheduledCommandBuffers = 0;
-    bool m_isMapWrite;
-    bool m_isMapRead;
-    bool m_isDestroyed = false;
-    bool m_isVertex;
-    bool m_isUniform;
-    bool m_isStorage;
-    bool m_isReadOnly;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/gpu/GPUBufferBinding.h (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/GPUBufferBinding.h	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/GPUBufferBinding.h	2019-02-27 21:10:24 UTC (rev 242148)
@@ -33,7 +33,7 @@
 namespace WebCore {
 
 struct GPUBufferBinding {
-    Ref<const GPUBuffer> buffer;
+    Ref<GPUBuffer> buffer;
     unsigned long offset;
     unsigned long size;
 };

Modified: trunk/Source/WebCore/platform/graphics/gpu/GPUCommandBuffer.h (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/GPUCommandBuffer.h	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/GPUCommandBuffer.h	2019-02-27 21:10:24 UTC (rev 242148)
@@ -30,11 +30,13 @@
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
 #include <wtf/RetainPtr.h>
+#include <wtf/Vector.h>
 
 OBJC_PROTOCOL(MTLCommandBuffer);
 
 namespace WebCore {
 
+class GPUBuffer;
 class GPUDevice;
 
 using PlatformCommandBuffer = MTLCommandBuffer;
@@ -45,11 +47,15 @@
     static RefPtr<GPUCommandBuffer> create(GPUDevice&);
 
     PlatformCommandBuffer* platformCommandBuffer() const { return m_platformCommandBuffer.get(); }
+    const Vector<Ref<GPUBuffer>>& usedBuffers() const { return m_usedBuffers; }
 
+    void useBuffer(Ref<GPUBuffer>&& buffer) { m_usedBuffers.append(WTFMove(buffer)); }
+
 private:
     GPUCommandBuffer(PlatformCommandBufferSmartPtr&&);
 
     PlatformCommandBufferSmartPtr m_platformCommandBuffer;
+    Vector<Ref<GPUBuffer>> m_usedBuffers;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/gpu/GPUDevice.cpp (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/GPUDevice.cpp	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/GPUDevice.cpp	2019-02-27 21:10:24 UTC (rev 242148)
@@ -44,9 +44,9 @@
 
 namespace WebCore {
 
-RefPtr<GPUBuffer> GPUDevice::tryCreateBuffer(GPUBufferDescriptor&& descriptor) const
+RefPtr<GPUBuffer> GPUDevice::tryCreateBuffer(GPUBufferDescriptor&& descriptor)
 {
-    return GPUBuffer::tryCreate(*this, WTFMove(descriptor));
+    return GPUBuffer::tryCreate(makeRef(*this), WTFMove(descriptor));
 }
 
 RefPtr<GPUTexture> GPUDevice::tryCreateTexture(GPUTextureDescriptor&& descriptor) const

Modified: trunk/Source/WebCore/platform/graphics/gpu/GPUDevice.h (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/GPUDevice.h	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/GPUDevice.h	2019-02-27 21:10:24 UTC (rev 242148)
@@ -60,7 +60,7 @@
 public:
     static RefPtr<GPUDevice> create(Optional<GPURequestAdapterOptions>&&);
 
-    RefPtr<GPUBuffer> tryCreateBuffer(GPUBufferDescriptor&&) const;
+    RefPtr<GPUBuffer> tryCreateBuffer(GPUBufferDescriptor&&);
     RefPtr<GPUTexture> tryCreateTexture(GPUTextureDescriptor&&) const;
 
     RefPtr<GPUBindGroupLayout> tryCreateBindGroupLayout(GPUBindGroupLayoutDescriptor&&) const;

Copied: trunk/Source/WebCore/platform/graphics/gpu/GPUProgrammablePassEncoder.cpp (from rev 242145, trunk/Source/WebCore/platform/graphics/gpu/GPUBufferBinding.h) (0 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/GPUProgrammablePassEncoder.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/gpu/GPUProgrammablePassEncoder.cpp	2019-02-27 21:10:24 UTC (rev 242148)
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "GPUProgrammablePassEncoder.h"
+
+#if ENABLE(WEBGPU)
+
+#include "GPUCommandBuffer.h"
+
+namespace WebCore {
+
+GPUProgrammablePassEncoder::GPUProgrammablePassEncoder(Ref<GPUCommandBuffer>&& commandBuffer)
+    : m_commandBuffer(WTFMove(commandBuffer))
+{
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEBGPU)

Modified: trunk/Source/WebCore/platform/graphics/gpu/GPUProgrammablePassEncoder.h (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/GPUProgrammablePassEncoder.h	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/GPUProgrammablePassEncoder.h	2019-02-27 21:10:24 UTC (rev 242148)
@@ -40,6 +40,7 @@
 namespace WebCore {
 
 class GPUBindGroup;
+class GPUCommandBuffer;
 class GPURenderPipeline;
 
 using PlatformProgrammablePassEncoder = MTLCommandEncoder;
@@ -49,12 +50,15 @@
     virtual ~GPUProgrammablePassEncoder() = default;
 
     void endPass();
-    void setBindGroup(unsigned long, const GPUBindGroup&);
+    void setBindGroup(unsigned long, GPUBindGroup&);
     virtual void setPipeline(Ref<GPURenderPipeline>&&) = 0;
 
 protected:
+    GPUProgrammablePassEncoder(Ref<GPUCommandBuffer>&&);
     virtual PlatformProgrammablePassEncoder* platformPassEncoder() const = 0;
 
+    GPUCommandBuffer& commandBuffer() const { return m_commandBuffer.get(); }
+
 private:
 #if USE(METAL)
     void setResourceAsBufferOnEncoder(MTLArgumentEncoder *, const GPUBindingResource&, unsigned long, const char* const);
@@ -65,6 +69,7 @@
     virtual void setFragmentBuffer(MTLBuffer *, unsigned long, unsigned long) { }
 #endif // USE(METAL)
 
+    Ref<GPUCommandBuffer> m_commandBuffer;
     bool m_isEncoding { true };
 };
 

Modified: trunk/Source/WebCore/platform/graphics/gpu/GPURenderPassEncoder.h (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/GPURenderPassEncoder.h	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/GPURenderPassEncoder.h	2019-02-27 21:10:24 UTC (rev 242148)
@@ -49,15 +49,15 @@
 
 class GPURenderPassEncoder : public GPUProgrammablePassEncoder {
 public:
-    static RefPtr<GPURenderPassEncoder> create(const GPUCommandBuffer&, GPURenderPassDescriptor&&);
+    static RefPtr<GPURenderPassEncoder> tryCreate(Ref<GPUCommandBuffer>&&, GPURenderPassDescriptor&&);
 
     void setPipeline(Ref<GPURenderPipeline>&&) final;
 
-    void setVertexBuffers(unsigned long, Vector<Ref<const GPUBuffer>>&&, Vector<unsigned long long>&&);
+    void setVertexBuffers(unsigned long, Vector<Ref<GPUBuffer>>&&, Vector<unsigned long long>&&);
     void draw(unsigned long vertexCount, unsigned long instanceCount, unsigned long firstVertex, unsigned long firstInstance);
 
 private:
-    GPURenderPassEncoder(PlatformRenderPassEncoderSmartPtr&&);
+    GPURenderPassEncoder(Ref<GPUCommandBuffer>&&, PlatformRenderPassEncoderSmartPtr&&);
     ~GPURenderPassEncoder() { endPass(); } // Ensure that encoding has ended before release.
 
     PlatformProgrammablePassEncoder* platformPassEncoder() const final;

Modified: trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm	2019-02-27 21:10:24 UTC (rev 242148)
@@ -34,18 +34,51 @@
 #import <_javascript_Core/ArrayBuffer.h>
 #import <Metal/Metal.h>
 #import <wtf/BlockObjCExceptions.h>
+#import <wtf/CheckedArithmetic.h>
+#import <wtf/MainThread.h>
 
 namespace WebCore {
 
-RefPtr<GPUBuffer> GPUBuffer::tryCreateSharedBuffer(const GPUDevice& device, const GPUBufferDescriptor& descriptor)
+static const auto readOnlyMask = GPUBufferUsage::Index | GPUBufferUsage::Vertex | GPUBufferUsage::Uniform | GPUBufferUsage::TransferSrc;
+
+
+bool GPUBuffer::validateBufferCreate(const GPUDevice& device, const GPUBufferDescriptor& descriptor)
 {
-    ASSERT(device.platformDevice());
+    if (!device.platformDevice()) {
+        LOG(WebGPU, "GPUBuffer::create(): Invalid GPUDevice!");
+        return false;
+    }
 
+    if ((descriptor.usage & GPUBufferUsage::MapWrite) && (descriptor.usage & GPUBufferUsage::MapRead)) {
+        LOG(WebGPU, "GPUBuffer::create(): Buffer cannot have both MAP_READ and MAP_WRITE usage!");
+        return false;
+    }
+
+    if ((descriptor.usage & readOnlyMask) && (descriptor.usage & GPUBufferUsage::Storage)) {
+        LOG(WebGPU, "GPUBuffer::create(): Buffer cannot have both STORAGE and a read-only usage!");
+        return false;
+    }
+
+    return true;
+}
+
+RefPtr<GPUBuffer> GPUBuffer::tryCreate(Ref<GPUDevice>&& device, GPUBufferDescriptor&& descriptor)
+{
+    if (!validateBufferCreate(device.get(), descriptor))
+        return nullptr;
+
+    // FIXME: Metal best practices: Read-only one-time-use data less than 4 KB should not allocate a MTLBuffer and be used in [MTLCommandEncoder set*Bytes] calls instead.
+
+    MTLResourceOptions resourceOptions = MTLResourceCPUCacheModeDefaultCache;
+
+    // Mappable buffers use shared storage allocation.
+    resourceOptions |= (descriptor.usage & (GPUBufferUsage::MapWrite | GPUBufferUsage::MapRead)) ? MTLResourceStorageModeShared : MTLResourceStorageModePrivate;
+
     RetainPtr<MTLBuffer> mtlBuffer;
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
-    mtlBuffer = adoptNS([device.platformDevice() newBufferWithLength:descriptor.size options: MTLResourceCPUCacheModeDefaultCache]);
+    mtlBuffer = adoptNS([device->platformDevice() newBufferWithLength:descriptor.size options:resourceOptions]);
 
     END_BLOCK_OBJC_EXCEPTIONS;
 
@@ -54,53 +87,116 @@
         return nullptr;
     }
 
-    return adoptRef(*new GPUBuffer(WTFMove(mtlBuffer), descriptor));
+    return adoptRef(*new GPUBuffer(WTFMove(mtlBuffer), descriptor, WTFMove(device)));
 }
 
-static const auto readOnlyMask = GPUBufferUsage::Index | GPUBufferUsage::Vertex | GPUBufferUsage::Uniform | GPUBufferUsage::TransferSrc;
+GPUBuffer::GPUBuffer(RetainPtr<MTLBuffer>&& buffer, const GPUBufferDescriptor& descriptor, Ref<GPUDevice>&& device)
+    : m_platformBuffer(WTFMove(buffer))
+    , m_device(WTFMove(device))
+    , m_byteLength(descriptor.size)
+    , m_usage(static_cast<GPUBufferUsage::Flags>(descriptor.usage))
+{
+}
 
-RefPtr<GPUBuffer> GPUBuffer::tryCreate(const GPUDevice& device, GPUBufferDescriptor&& descriptor)
+GPUBuffer::~GPUBuffer()
 {
-    if (!device.platformDevice()) {
-        LOG(WebGPU, "GPUBuffer::create(): Invalid GPUDevice!");
-        return nullptr;
+    destroy();
+}
+
+bool GPUBuffer::isReadOnly() const
+{
+    return m_usage & readOnlyMask;
+}
+
+GPUBuffer::State GPUBuffer::state() const
+{
+    if (!m_platformBuffer)
+        return State::Destroyed;
+    if (m_mappingCallback)
+        return State::Mapped;
+
+    return State::Unmapped;
+}
+
+void GPUBuffer::setSubData(unsigned long offset, const JSC::ArrayBuffer& data)
+{
+    if (!isTransferDst() || state() != State::Unmapped) {
+        LOG(WebGPU, "GPUBuffer::setSubData(): Invalid operation!");
+        return;
     }
+    auto subDataLength = checkedSum<unsigned long>(data.byteLength(), offset);
+    if (subDataLength.hasOverflowed() || subDataLength.unsafeGet() > m_byteLength) {
+        LOG(WebGPU, "GPUBuffer::setSubData(): Invalid offset or data size!");
+        return;
+    }
 
-    if ((descriptor.usage & GPUBufferUsage::MapWrite) && (descriptor.usage & GPUBufferUsage::MapRead)) {
-        LOG(WebGPU, "GPUBuffer::create(): Buffer cannot have both MAP_READ and MAP_WRITE usage!");
-        return nullptr;
+    // FIXME: Add alignment checks once specified.
+
+    if (m_subDataBuffers.isEmpty()) {
+        BEGIN_BLOCK_OBJC_EXCEPTIONS;
+        m_subDataBuffers.append(adoptNS([m_platformBuffer.get().device newBufferWithLength:m_byteLength options:MTLResourceCPUCacheModeDefaultCache]));
+        END_BLOCK_OBJC_EXCEPTIONS;
     }
 
-    if ((descriptor.usage & readOnlyMask) && (descriptor.usage & GPUBufferUsage::Storage)) {
-        LOG(WebGPU, "GPUBuffer::create(): Buffer cannot have both STORAGE and a read-only usage!");
-        return nullptr;
+    __block auto stagingMtlBuffer = m_subDataBuffers.takeLast();
+
+    if (!stagingMtlBuffer || stagingMtlBuffer.get().length < data.byteLength()) {
+        LOG(WebGPU, "GPUBuffer::setSubData(): Unable to get staging buffer for provided data!");
+        return;
     }
 
-    // Mappable buffers need (default) shared storage allocation.
-    if (descriptor.usage & (GPUBufferUsage::MapWrite | GPUBufferUsage::MapRead))
-        return tryCreateSharedBuffer(device, descriptor);
+    memcpy(stagingMtlBuffer.get().contents, data.data(), data.byteLength());
 
-    LOG(WebGPU, "GPUBuffer::create(): Support for non-mapped buffers not implemented!");
-    return nullptr;
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+    auto commandBuffer = retainPtr([m_device->getQueue()->platformQueue() commandBuffer]);
+    auto blitEncoder = retainPtr([commandBuffer blitCommandEncoder]);
+
+    [blitEncoder copyFromBuffer:stagingMtlBuffer.get() sourceOffset:0 toBuffer:m_platformBuffer.get() destinationOffset:offset size:stagingMtlBuffer.get().length];
+    [blitEncoder endEncoding];
+
+    if (isMappable())
+        commandBufferCommitted(commandBuffer.get());
+
+    auto protectedThis = makeRefPtr(this);
+    [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
+        protectedThis->reuseSubDataBuffer(WTFMove(stagingMtlBuffer));
+    }];
+    [commandBuffer commit];
+
+    END_BLOCK_OBJC_EXCEPTIONS;
 }
 
-GPUBuffer::GPUBuffer(RetainPtr<MTLBuffer>&& buffer, const GPUBufferDescriptor& descriptor)
-    : m_platformBuffer(WTFMove(buffer))
-    , m_byteLength(descriptor.size)
-    , m_isMapWrite(descriptor.usage & GPUBufferUsage::MapWrite)
-    , m_isMapRead(descriptor.usage & GPUBufferUsage::MapRead)
-    , m_isVertex(descriptor.usage & GPUBufferUsage::Vertex)
-    , m_isUniform(descriptor.usage & GPUBufferUsage::Uniform)
-    , m_isStorage(descriptor.usage & GPUBufferUsage::Storage)
-    , m_isReadOnly(descriptor.usage & readOnlyMask)
+#if USE(METAL)
+void GPUBuffer::commandBufferCommitted(MTLCommandBuffer *commandBuffer)
 {
+    ++m_numScheduledCommandBuffers;
+
+    auto protectedThis = makeRefPtr(this);
+    [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
+        protectedThis->commandBufferCompleted();
+    }];
 }
 
-GPUBuffer::~GPUBuffer()
+void GPUBuffer::commandBufferCompleted()
 {
-    unmap();
+    ASSERT(m_numScheduledCommandBuffers);
+
+    if (m_numScheduledCommandBuffers == 1 && state() == State::Mapped) {
+        callOnMainThread([this, protectedThis = makeRef(*this)] () {
+            runMappingCallback();
+        });
+    }
+
+    --m_numScheduledCommandBuffers;
 }
 
+void GPUBuffer::reuseSubDataBuffer(RetainPtr<MTLBuffer>&& buffer)
+{
+    m_subDataBuffers.append(WTFMove(buffer));
+}
+#endif // USE(METAL)
+
 void GPUBuffer::registerMappingCallback(MappingCallback&& callback, bool isRead)
 {
     // Reject if request is invalid.
@@ -115,21 +211,25 @@
         return;
     }
 
-    ASSERT(!m_pendingCallback && !m_mappingCallbackTask.hasPendingTask());
+    ASSERT(!m_mappingCallback && !m_mappingCallbackTask.hasPendingTask());
 
     // An existing callback means this buffer is in the mapped state.
-    m_pendingCallback = PendingMappingCallback::create(WTFMove(callback));
+    m_mappingCallback = PendingMappingCallback::create(WTFMove(callback));
 
     // If GPU is not using this buffer, run the callback ASAP.
     if (!m_numScheduledCommandBuffers) {
         m_mappingCallbackTask.scheduleTask([this, protectedThis = makeRef(*this)] () mutable {
-            ASSERT(m_pendingCallback);
-
-            m_pendingCallback->callback(m_isMapRead ? stagingBufferForRead() : stagingBufferForWrite());
+            runMappingCallback();
         });
     }
 }
 
+void GPUBuffer::runMappingCallback()
+{
+    if (m_mappingCallback)
+        m_mappingCallback->callback(isMapRead() ? stagingBufferForRead() : stagingBufferForWrite());
+}
+
 JSC::ArrayBuffer* GPUBuffer::stagingBufferForRead()
 {
     if (!m_stagingBuffer)
@@ -153,28 +253,25 @@
         return;
     }
 
-    if (m_stagingBuffer && m_isMapWrite) {
+    if (m_stagingBuffer && isMapWrite()) {
+        ASSERT(m_platformBuffer);
         memcpy(m_platformBuffer.get().contents, m_stagingBuffer->data(), m_byteLength);
         m_stagingBuffer = nullptr;
     }
 
-    if (m_pendingCallback) {
+    if (m_mappingCallback) {
         m_mappingCallbackTask.cancelTask();
-        m_pendingCallback->callback(nullptr);
-        m_pendingCallback = nullptr;
+        m_mappingCallback->callback(nullptr);
+        m_mappingCallback = nullptr;
     }
 }
 
 void GPUBuffer::destroy()
 {
-    if (isMappable())
+    if (state() == State::Mapped)
         unmap();
 
-    m_isDestroyed = true;
-
-    // FIXME: If GPU is still using the MTLBuffer, it will be released after all relevant commands have executed.
-    if (!m_numScheduledCommandBuffers)
-        m_platformBuffer = nullptr;
+    m_platformBuffer = nullptr;
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPUProgrammablePassEncoderMetal.mm (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPUProgrammablePassEncoderMetal.mm	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPUProgrammablePassEncoderMetal.mm	2019-02-27 21:10:24 UTC (rev 242148)
@@ -61,7 +61,8 @@
     }
 
     auto& bufferBinding = WTF::get<GPUBufferBinding>(resource);
-    auto mtlBuffer = bufferBinding.buffer->platformBuffer();
+    auto& bufferRef = bufferBinding.buffer;
+    auto mtlBuffer = bufferRef->platformBuffer();
 
     if (!mtlBuffer) {
         LOG(WebGPU, "%s: Invalid MTLBuffer in GPUBufferBinding!", functionName);
@@ -71,12 +72,14 @@
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
     [argumentEncoder setBuffer:mtlBuffer offset:bufferBinding.offset atIndex:index];
-    useResource(mtlBuffer, bufferBinding.buffer->isReadOnly() ? MTLResourceUsageRead : MTLResourceUsageRead | MTLResourceUsageWrite);
+    useResource(mtlBuffer, bufferRef->isReadOnly() ? MTLResourceUsageRead : MTLResourceUsageRead | MTLResourceUsageWrite);
 
     END_BLOCK_OBJC_EXCEPTIONS;
+
+    m_commandBuffer->useBuffer(bufferRef.copyRef());
 }
 
-void GPUProgrammablePassEncoder::setBindGroup(unsigned long index, const GPUBindGroup& bindGroup)
+void GPUProgrammablePassEncoder::setBindGroup(unsigned long index, GPUBindGroup& bindGroup)
 {
     const char* const functionName = "GPUProgrammablePassEncoder::setBindGroup()";
 #if LOG_DISABLED

Modified: trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPUQueueMetal.mm (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPUQueueMetal.mm	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPUQueueMetal.mm	2019-02-27 21:10:24 UTC (rev 242148)
@@ -70,10 +70,20 @@
 {
 }
 
-void GPUQueue::submit(Vector<Ref<const GPUCommandBuffer>>&& buffers)
+void GPUQueue::submit(Vector<Ref<const GPUCommandBuffer>>&& commandBuffers)
 {
-    for (auto& buffer : buffers)
-        [buffer->platformCommandBuffer() commit];
+    for (auto& commandBuffer : commandBuffers) {
+        // Prevent any buffer mapping callbacks from executing until command buffer is complete.
+        for (auto& buffer : commandBuffer->usedBuffers()) {
+            if (buffer->state() != GPUBuffer::State::Unmapped) {
+                LOG(WebGPU, "GPUQueue::submit(): Invalid GPUBuffer set on a GPUCommandBuffer!");
+                return;
+            }
+            buffer->commandBufferCommitted(commandBuffer->platformCommandBuffer());
+        }
+
+        [commandBuffer->platformCommandBuffer() commit];
+    }
 }
 
 String GPUQueue::label() const

Modified: trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm (242147 => 242148)


--- trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm	2019-02-27 21:05:39 UTC (rev 242147)
+++ trunk/Source/WebCore/platform/graphics/gpu/cocoa/GPURenderPassEncoderMetal.mm	2019-02-27 21:10:24 UTC (rev 242148)
@@ -113,9 +113,9 @@
     return true;
 }
 
-RefPtr<GPURenderPassEncoder> GPURenderPassEncoder::create(const GPUCommandBuffer& buffer, GPURenderPassDescriptor&& descriptor)
+RefPtr<GPURenderPassEncoder> GPURenderPassEncoder::tryCreate(Ref<GPUCommandBuffer>&& buffer, GPURenderPassDescriptor&& descriptor)
 {
-    const char* const functionName = "GPURenderPassEncoder::create()";
+    const char* const functionName = "GPURenderPassEncoder::tryCreate()";
 
     RetainPtr<MTLRenderPassDescriptor> mtlDescriptor;
 
@@ -137,11 +137,11 @@
         && !populateMtlDepthStencilAttachment(mtlDescriptor.get().depthAttachment, *descriptor.depthStencilAttachment, functionName))
         return nullptr;
 
-    PlatformRenderPassEncoderSmartPtr mtlEncoder;
+    RetainPtr<MTLRenderCommandEncoder> mtlEncoder;
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
-    mtlEncoder = retainPtr([buffer.platformCommandBuffer() renderCommandEncoderWithDescriptor:mtlDescriptor.get()]);
+    mtlEncoder = retainPtr([buffer->platformCommandBuffer() renderCommandEncoderWithDescriptor:mtlDescriptor.get()]);
 
     END_BLOCK_OBJC_EXCEPTIONS;
 
@@ -150,11 +150,12 @@
         return nullptr;
     }
 
-    return adoptRef(new GPURenderPassEncoder(WTFMove(mtlEncoder)));
+    return adoptRef(new GPURenderPassEncoder(WTFMove(buffer), WTFMove(mtlEncoder)));
 }
 
-GPURenderPassEncoder::GPURenderPassEncoder(PlatformRenderPassEncoderSmartPtr&& encoder)
-    : m_platformRenderPassEncoder(WTFMove(encoder))
+GPURenderPassEncoder::GPURenderPassEncoder(Ref<GPUCommandBuffer>&& commandBuffer, RetainPtr<MTLRenderCommandEncoder>&& encoder)
+    : GPUProgrammablePassEncoder(WTFMove(commandBuffer))
+    , m_platformRenderPassEncoder(WTFMove(encoder))
 {
 }
 
@@ -177,13 +178,14 @@
     m_pipeline = WTFMove(pipeline);
 }
 
-void GPURenderPassEncoder::setVertexBuffers(unsigned long index, Vector<Ref<const GPUBuffer>>&& buffers, Vector<unsigned long long>&& offsets)
+void GPURenderPassEncoder::setVertexBuffers(unsigned long index, Vector<Ref<GPUBuffer>>&& buffers, Vector<unsigned long long>&& offsets)
 {
     ASSERT(buffers.size() && offsets.size() == buffers.size());
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
-    auto mtlBuffers = buffers.map([] (const auto& buffer) {
+    auto mtlBuffers = buffers.map([this] (auto& buffer) {
+        commandBuffer().useBuffer(buffer.copyRef());
         return buffer->platformBuffer();
     });
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to