[WebGPU] Remove GPUBuffer.setSubData and implement GPUDevice.createBufferMapped
authorjustin_fan@apple.com <justin_fan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 7 Jun 2019 22:24:55 +0000 (22:24 +0000)
committerjustin_fan@apple.com <justin_fan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 7 Jun 2019 22:24:55 +0000 (22:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=198591

Reviewed by Myles C. Maxfield.

Source/WebCore:

Remove GPUBuffer.setSubData from the WebGPU API.
Add GPUDevice.createBufferMapped to the WebGPU API.

Existing tests have been updated.

* Modules/webgpu/WebGPUBuffer.cpp:
(WebCore::WebGPUBuffer::setSubData): Deleted.
* Modules/webgpu/WebGPUBuffer.h:
* Modules/webgpu/WebGPUBuffer.idl:
* Modules/webgpu/WebGPUDevice.cpp:
(WebCore::WebGPUDevice::createBufferMapped const):
* Modules/webgpu/WebGPUDevice.h:
* Modules/webgpu/WebGPUDevice.idl:
* platform/graphics/gpu/GPUBuffer.h:
* platform/graphics/gpu/GPUCommandBuffer.h:
* platform/graphics/gpu/GPUDevice.cpp:
(WebCore::GPUDevice::tryCreateBuffer):
* platform/graphics/gpu/GPUDevice.h:
* platform/graphics/gpu/cocoa/GPUBufferMetal.mm:
(WebCore::GPUBuffer::tryCreate):
(WebCore::GPUBuffer::GPUBuffer):
(WebCore::GPUBuffer::state const):
(WebCore::GPUBuffer::mapOnCreation):
(WebCore::GPUBuffer::commandBufferCompleted):
(WebCore::GPUBuffer::copyStagingBufferToGPU):
        Required to unmap GPUBuffers created with GPU-private storage.
(WebCore::GPUBuffer::unmap):
(WebCore::GPUBuffer::setSubData): Deleted.
(WebCore::GPUBuffer::reuseSubDataBuffer): Deleted.

LayoutTests:

GPUBuffer.setSubData has been removed from the WebGPU implementation.
GPUDevice.createBufferMapped has been added to the WebGPU implementation.
Replace all setSubData calls with appropriate replacements.

* webgpu/blend-triangle-strip.html:
* webgpu/blit-commands.html:
* webgpu/buffer-command-buffer-races.html:
* webgpu/buffer-resource-triangles.html:
* webgpu/compute-squares.html:
* webgpu/depth-enabled-triangle-strip.html:
* webgpu/draw-indexed-triangles.html:
* webgpu/js/webgpu-functions.js:
(createBufferWithData):
(async.mapWriteDataToBuffer):
* webgpu/map-read-buffers-expected.txt:
* webgpu/map-read-buffers.html:
* webgpu/texture-triangle-strip.html:
* webgpu/vertex-buffer-triangle-strip.html:

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

25 files changed:
LayoutTests/ChangeLog
LayoutTests/webgpu/blend-triangle-strip.html
LayoutTests/webgpu/blit-commands.html
LayoutTests/webgpu/buffer-command-buffer-races.html
LayoutTests/webgpu/buffer-resource-triangles.html
LayoutTests/webgpu/compute-squares.html
LayoutTests/webgpu/depth-enabled-triangle-strip.html
LayoutTests/webgpu/draw-indexed-triangles.html
LayoutTests/webgpu/js/webgpu-functions.js
LayoutTests/webgpu/map-read-buffers-expected.txt
LayoutTests/webgpu/map-read-buffers.html
LayoutTests/webgpu/texture-triangle-strip.html
LayoutTests/webgpu/vertex-buffer-triangle-strip.html
Source/WebCore/ChangeLog
Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp
Source/WebCore/Modules/webgpu/WebGPUBuffer.h
Source/WebCore/Modules/webgpu/WebGPUBuffer.idl
Source/WebCore/Modules/webgpu/WebGPUDevice.cpp
Source/WebCore/Modules/webgpu/WebGPUDevice.h
Source/WebCore/Modules/webgpu/WebGPUDevice.idl
Source/WebCore/platform/graphics/gpu/GPUBuffer.h
Source/WebCore/platform/graphics/gpu/GPUCommandBuffer.h
Source/WebCore/platform/graphics/gpu/GPUDevice.cpp
Source/WebCore/platform/graphics/gpu/GPUDevice.h
Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm

index 52ed1da..1605a3b 100644 (file)
@@ -1,3 +1,29 @@
+2019-06-07  Justin Fan  <justin_fan@apple.com>
+
+        [WebGPU] Remove GPUBuffer.setSubData and implement GPUDevice.createBufferMapped
+        https://bugs.webkit.org/show_bug.cgi?id=198591
+
+        Reviewed by Myles C. Maxfield.
+
+        GPUBuffer.setSubData has been removed from the WebGPU implementation.
+        GPUDevice.createBufferMapped has been added to the WebGPU implementation.
+        Replace all setSubData calls with appropriate replacements.
+
+        * webgpu/blend-triangle-strip.html:
+        * webgpu/blit-commands.html:
+        * webgpu/buffer-command-buffer-races.html:
+        * webgpu/buffer-resource-triangles.html:
+        * webgpu/compute-squares.html:
+        * webgpu/depth-enabled-triangle-strip.html:
+        * webgpu/draw-indexed-triangles.html:
+        * webgpu/js/webgpu-functions.js:
+        (createBufferWithData):
+        (async.mapWriteDataToBuffer):
+        * webgpu/map-read-buffers-expected.txt:
+        * webgpu/map-read-buffers.html:
+        * webgpu/texture-triangle-strip.html:
+        * webgpu/vertex-buffer-triangle-strip.html:
+
 2019-06-07  Per Arne Vollan  <pvollan@apple.com>
 
         Layout Test fast/events/fire-mousedown-while-pressing-mouse-button.html is failing
index dd7fda6..cabef69 100644 (file)
@@ -75,8 +75,7 @@ async function test() {
         1, 1, 0, 1,
         1, -1, 0, 1
     ]);
-    const vertexBuffer = device.createBuffer({ size: vertexData.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
-    vertexBuffer.setSubData(0, vertexData.buffer);
+    const vertexBuffer = createBufferWithData(device, { size: vertexData.byteLength, usage: GPUBufferUsage.VERTEX }, vertexData.buffer);
 
     const context = canvas.getContext("gpu");
     const swapChain = context.configureSwapChain({ device: device, format: "bgra8unorm" });
index d7ea225..8deaa19 100644 (file)
@@ -30,11 +30,11 @@ async function test(image) {
 
     const bufferDescriptor = {
         size: imageData.data.byteLength,
-        usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.TRANSFER_DST
+        usage: GPUBufferUsage.TRANSFER_SRC
     };
-    const bufferA = device.createBuffer(bufferDescriptor);
-    bufferA.setSubData(0, imageData.data.buffer);
+    bufferA = createBufferWithData(device, bufferDescriptor, imageData.data.buffer);
 
+    bufferDescriptor.usage |= GPUBufferUsage.TRANSFER_DST;
     const bufferB = device.createBuffer(bufferDescriptor);
     const bufferViewB = {
         buffer: bufferB,
index 2aecda4..864c5ef 100644 (file)
@@ -63,10 +63,8 @@ function createVertexInputDescriptor() {
 }
 
 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;
+    const vertexArray = new Float32Array(vertices)
+    return createBufferWithData(device, { size: vertexArray.byteLength, usage: GPUBufferUsage.VERTEX }, vertexArray.buffer);
 }
 
 function drawAndSubmitCommands(device, pipeline, attachment, vertexBuffer, colorBuffer) {
@@ -97,8 +95,7 @@ async function test() {
     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 colorBuffer = createBufferWithData(device, { size: greenArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE }, greenArray.buffer);
 
     const attachment = {
         attachment: swapChain.getCurrentTexture().createDefaultView(),
@@ -126,12 +123,12 @@ async function test() {
     /* colorBuffer does not actually contain "green" again until this call. */
     colorBuffer.unmap();
 
-    /* setSubData immediately after a submit should not affect the preceding draw call. */
+    /* Writing data immediately after a submit should not affect the preceding draw call. */
     drawAndSubmitCommands(device, pipeline, attachment, middleBuffer, colorBuffer);
-    colorBuffer.setSubData(0, blueArray.buffer);
+    await mapWriteDataToBuffer(colorBuffer, blueArray.buffer);
 
     /* destroy right after a submit should not affect the draw call. */
-    colorBuffer.setSubData(0, greenArray.buffer);
+    await mapWriteDataToBuffer(colorBuffer, greenArray.buffer);
     drawAndSubmitCommands(device, pipeline, attachment, upperRightBuffer, colorBuffer);
     upperRightBuffer.destroy();
 
index bd06c45..e2e6d34 100644 (file)
@@ -81,17 +81,13 @@ function createUniformBufferBindGroupLayout(bindNum, stage = GPUShaderStageBit.V
 }
 
 const vertexSize = 4 * 4;
-const verticesBufferSize = vertexSize * 3;
 function createAndUploadVerticesBuffer(device) {
-    const buffer = device.createBuffer({ size:verticesBufferSize, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
-    const arrayBuffer = new Float32Array([
+    const vertexArray = new Float32Array([
         0, 1, 0, 1,
         -1, -1, 0, 1,
         1, -1, 0, 1
-    ]).buffer;
-
-    buffer.setSubData(0, arrayBuffer);
-    return buffer;
+    ]);
+    return createBufferWithData(device, { size: vertexArray.byteLength, usage: GPUBufferUsage.VERTEX }, vertexArray.buffer);
 }
 
 function createFloat4Buffer(device, a, b, promises) {
index fcacf92..39bffc0 100644 (file)
@@ -39,8 +39,7 @@ promise_test(async () => {
     const computeStageDescriptor = { module: shaderModule, entryPoint: "compute" };
     const pipeline = device.createComputePipeline({ computeStage: computeStageDescriptor });
     
-    const dataBuffer = device.createBuffer({ size: data.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.TRANSFER_DST | GPUBufferUsage.MAP_READ });
-    dataBuffer.setSubData(0, data.buffer);
+    const dataBuffer = createBufferWithData(device, { size: data.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.MAP_READ }, data.buffer);
     
     const bgLayoutBinding = { binding: dataBinding, visibility: GPUShaderStageBit.COMPUTE, type: "storage-buffer" };
     const bgLayout = device.createBindGroupLayout({ bindings: [bgLayoutBinding] });
index 925c4c8..e903b1b 100644 (file)
@@ -48,18 +48,14 @@ fragment float4 fragment_main(VertexOut v [[stage_in]])
 `
 
 function createVertexBuffer(device) {
-    const bufferSize = 4 * 4 * 4;
-    const buffer = device.createBuffer({ size: bufferSize, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
-    const arrayBuffer = new Float32Array([
+    const vertexArray = new Float32Array([
         // float4 xyzw
         -1, 1, 0, 1,
         -1, -1, 0, 1,
         1, 1, 0, 1,
         1, -1, 0, 1
-    ]).buffer;
-    
-    buffer.setSubData(0, arrayBuffer);
-    return buffer;
+    ]);
+    return createBufferWithData(device, { size: vertexArray.byteLength, usage: GPUBufferUsage.VERTEX }, vertexArray.buffer);
 }
 
 function createVertexInputDescriptor() {
index 289e3bf..3731f0b 100644 (file)
@@ -53,10 +53,7 @@ function createVertexBuffer(device) {
         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;
+    return createBufferWithData(device, { size: vertexArray.byteLength, usage: GPUBufferUsage.VERTEX }, vertexArray.buffer);
 }
 
 const indexBufferOffset = 2048; // Test a buffer offset for index array.
@@ -65,8 +62,15 @@ 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);
+    const buffer = createBufferWithData(
+        device, 
+        { 
+            size: indexArray.byteLength + indexBufferOffset, 
+            usage: GPUBufferUsage.INDEX 
+        },
+        indexArray.buffer,
+        indexBufferOffset
+    );
 
     return buffer;
 }
index 1cc6d6e..8a53bd7 100644 (file)
@@ -87,4 +87,19 @@ function encodeBasicCommands(renderPassEncoder, renderPipeline, vertexBuffer) {
     renderPassEncoder.setPipeline(renderPipeline);
     renderPassEncoder.draw(4, 1, 0, 0);
     renderPassEncoder.endPass();
+}
+
+function createBufferWithData(device, descriptor, data, offset = 0) {
+    const mappedBuffer = device.createBufferMapped(descriptor);
+    const dataArray = new Uint8Array(mappedBuffer[1]);
+    dataArray.set(new Uint8Array(data), offset);
+    mappedBuffer[0].unmap();
+    return mappedBuffer[0];
+}
+
+async function mapWriteDataToBuffer(buffer, data, offset = 0) {
+    const arrayBuffer = await buffer.mapWriteAsync();
+    const writeArray = new Uint8Array(arrayBuffer);
+    writeArray.set(new Uint8Array(data), offset);
+    buffer.unmap();
 }
\ No newline at end of file
index e379579..6015e0c 100644 (file)
@@ -1,5 +1,6 @@
 
-PASS setSubData, mapReadAsync, unmap, and destroy on a GPUBuffer. 
+PASS mapReadAsync, unmap, and destroy on a GPUBuffer. 
+PASS GPUBuffer.mapReadAsync on a buffer created via GPUDevice.createBufferMapped. 
 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. 
index 58ce091..442549d 100644 (file)
@@ -1,6 +1,6 @@
 <!DOCTYPE html><!-- webkit-test-runner [ experimental:WebGPUEnabled=true ] -->
 <meta charset=utf-8>
-<title>Tests for setSubData and mapReadAsync on a GPUBuffer.</title>
+<title>Tests for createBufferMapped and mapReadAsync on a GPUBuffer.</title>
 <body>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
@@ -11,24 +11,14 @@ async function runTests() {
 
     // Basic mapReadAsync functionality
     promise_test(async () => {
-        const buffer = device.createBuffer({ size: 16, usage: GPUBufferUsage.TRANSFER_DST | GPUBufferUsage.MAP_READ });
+        const buffer = device.createBuffer({ size: 16, usage: GPUBufferUsage.MAP_READ });
         assert_true(buffer instanceof GPUBuffer, "createBuffer returned a GPUBuffer");
 
-        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");
+        assert_equals(readArray[0], 0, "successfully access a value from a map read");
 
         buffer.unmap();
 
@@ -40,7 +30,41 @@ async function runTests() {
             assert_unreached("Buffer was destroyed!");
         }, () => {});
 
-    }, "setSubData, mapReadAsync, unmap, and destroy on a GPUBuffer.");
+    }, "mapReadAsync, unmap, and destroy on a GPUBuffer.");
+
+    // GPUDevice.createBufferMapped
+    promise_test(async () => {
+        const bufferResult = device.createBufferMapped({ size: 16, usage: GPUBufferUsage.MAP_READ });
+        const buffer = bufferResult[0];
+        const arrayBuffer = bufferResult[1];
+
+        assert_true(buffer instanceof GPUBuffer, "createBufferMapped returned a GPUBuffer");
+        assert_true(arrayBuffer instanceof ArrayBuffer, "createBufferMapped returned an ArrayBuffer");
+
+        let array = new Float32Array(arrayBuffer);
+        array.set([1, 2, 3, 4]);
+
+        // Buffer should already be "mapped".
+        await buffer.mapReadAsync().then(() => {
+            assert_unreached("GPUBuffer created via GPUBufferMapped cannot be mapped until after first unmap!");
+        }, () => {});
+
+        buffer.unmap();
+
+        // Buffer should not be re-mappable for writes.
+        await buffer.mapWriteAsync().then(() => {
+            assert_unreached("Buffer was not created with MAP_WRITE!");
+        }, () => {});
+
+        // Read results of original writes.
+        let resultArrayBuffer = await buffer.mapReadAsync();
+        const resultArray = new Float32Array(resultArrayBuffer);
+        resultArray.forEach((v, i) => {
+            assert_equals(v, array[i], "Successfully map-read value written to GPUBuffer mapped on creation");
+        })
+        
+        buffer.destroy();
+    }, "GPUBuffer.mapReadAsync on a buffer created via GPUDevice.createBufferMapped.");
 
     /* Basic validation */
     // FIXME: Test invalid combinations of GPUBufferUsage after implementing error handling.
index 33a0e65..88e2cad 100644 (file)
@@ -89,8 +89,7 @@ async function test() {
         1, 1, 0, 1, 
         1, -1, 0, 1
     ]);
-    const positionBuffer = device.createBuffer({ size: positionArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
-    positionBuffer.setSubData(0, positionArray.buffer);
+    const positionBuffer = createBufferWithData(device, { size: positionArray.byteLength, usage: GPUBufferUsage.VERTEX }, positionArray.buffer);
 
     const texCoordsArray = new Float32Array([
         // float2 texCoords
@@ -99,8 +98,7 @@ async function test() {
         1, 0,
         1, 1
     ]);
-    const textureCoordBuffer = device.createBuffer({ size: texCoordsArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
-    textureCoordBuffer.setSubData(0, texCoordsArray.buffer);
+    const textureCoordBuffer = createBufferWithData(device, { size: texCoordsArray.byteLength, usage: GPUBufferUsage.VERTEX }, texCoordsArray.buffer);
 
     const vertexInputDescriptor = createVertexInputDescriptor();
 
@@ -122,10 +120,9 @@ async function test() {
 
     const textureBufferDescriptor = {
         size: imageData.data.length,
-        usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.TRANSFER_DST
+        usage: GPUBufferUsage.TRANSFER_SRC
     };
-    const textureBuffer = device.createBuffer(textureBufferDescriptor);
-    textureBuffer.setSubData(0, imageData.data.buffer);
+    const textureBuffer = createBufferWithData(device, textureBufferDescriptor, imageData.data.buffer);
 
     // Create GPUTexture
     const textureSize = {
index acd4cee..f1fcaa9 100644 (file)
@@ -42,22 +42,22 @@ fragment float4 fragment_main(VertexOut v [[stage_in]])
 `
 
 function createVertexBuffer(device) {
-    const bufferSize = 4 * 5 * 4;
-    const buffer = device.createBuffer({ size: bufferSize, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
-    const vertexArrayBuffer0 = new Float32Array([
+    const vertexArray = 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,
         1, -1, 0, 1, 1
-    ]).buffer;
+    ]);
 
-    buffer.setSubData(0, vertexArrayBuffer0);
-    buffer.setSubData(4 * 5 * 2, vertexArrayBuffer1);
-
-    return buffer;
+    return createBufferWithData(
+        device, 
+        { 
+            size: vertexArray.byteLength, 
+            usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE
+        },
+        vertexArray.buffer
+    );
 }
 
 function createVertexInputDescriptor() {
index 09c4db6..52b32f7 100644 (file)
@@ -1,3 +1,40 @@
+2019-06-07  Justin Fan  <justin_fan@apple.com>
+
+        [WebGPU] Remove GPUBuffer.setSubData and implement GPUDevice.createBufferMapped
+        https://bugs.webkit.org/show_bug.cgi?id=198591
+
+        Reviewed by Myles C. Maxfield.
+
+        Remove GPUBuffer.setSubData from the WebGPU API.
+        Add GPUDevice.createBufferMapped to the WebGPU API. 
+
+        Existing tests have been updated.
+
+        * Modules/webgpu/WebGPUBuffer.cpp:
+        (WebCore::WebGPUBuffer::setSubData): Deleted.
+        * Modules/webgpu/WebGPUBuffer.h:
+        * Modules/webgpu/WebGPUBuffer.idl:
+        * Modules/webgpu/WebGPUDevice.cpp:
+        (WebCore::WebGPUDevice::createBufferMapped const):
+        * Modules/webgpu/WebGPUDevice.h:
+        * Modules/webgpu/WebGPUDevice.idl:
+        * platform/graphics/gpu/GPUBuffer.h:
+        * platform/graphics/gpu/GPUCommandBuffer.h:
+        * platform/graphics/gpu/GPUDevice.cpp:
+        (WebCore::GPUDevice::tryCreateBuffer):
+        * platform/graphics/gpu/GPUDevice.h:
+        * platform/graphics/gpu/cocoa/GPUBufferMetal.mm:
+        (WebCore::GPUBuffer::tryCreate):
+        (WebCore::GPUBuffer::GPUBuffer):
+        (WebCore::GPUBuffer::state const):
+        (WebCore::GPUBuffer::mapOnCreation):
+        (WebCore::GPUBuffer::commandBufferCompleted):
+        (WebCore::GPUBuffer::copyStagingBufferToGPU):
+                Required to unmap GPUBuffers created with GPU-private storage.
+        (WebCore::GPUBuffer::unmap):
+        (WebCore::GPUBuffer::setSubData): Deleted.
+        (WebCore::GPUBuffer::reuseSubDataBuffer): Deleted.
+
 2019-06-07  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         Fix non-iOS build after r246205
index 1f082ec..c222e35 100644 (file)
@@ -42,14 +42,6 @@ WebGPUBuffer::WebGPUBuffer(RefPtr<GPUBuffer>&& buffer)
 {
 }
 
-void WebGPUBuffer::setSubData(uint64_t 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);
index d41e917..587970d 100644 (file)
@@ -48,7 +48,6 @@ public:
     GPUBuffer* buffer() { return m_buffer.get(); }
     const GPUBuffer* buffer() const { return m_buffer.get(); }
 
-    void setSubData(uint64_t, const JSC::ArrayBuffer&);
     using BufferMappingPromise = DOMPromiseDeferred<IDLInterface<JSC::ArrayBuffer>>;
     void mapReadAsync(BufferMappingPromise&&);
     void mapWriteAsync(BufferMappingPromise&&);
index a870547..7ef8cba 100644 (file)
@@ -32,8 +32,6 @@ typedef unsigned long long u64;
     ImplementationLacksVTable,
     InterfaceName=GPUBuffer
 ] interface WebGPUBuffer {
-    void setSubData(u64 offset, ArrayBuffer data);
-
     Promise<ArrayBuffer> mapReadAsync();
     Promise<ArrayBuffer> mapWriteAsync();
     void unmap();
index a478529..f059ad8 100644 (file)
 #include "GPUSamplerDescriptor.h"
 #include "GPUShaderModuleDescriptor.h"
 #include "GPUTextureDescriptor.h"
+#include "JSDOMConvertBufferSource.h"
+#include "JSWebGPUBuffer.h"
 #include "Logging.h"
 #include "WebGPUBindGroup.h"
 #include "WebGPUBindGroupBinding.h"
 #include "WebGPUBindGroupDescriptor.h"
 #include "WebGPUBindGroupLayout.h"
-#include "WebGPUBuffer.h"
 #include "WebGPUBufferBinding.h"
 #include "WebGPUCommandEncoder.h"
 #include "WebGPUComputePipeline.h"
@@ -84,6 +85,22 @@ Ref<WebGPUBuffer> WebGPUDevice::createBuffer(const GPUBufferDescriptor& descript
     return WebGPUBuffer::create(WTFMove(buffer));
 }
 
+Vector<JSC::JSValue> WebGPUDevice::createBufferMapped(JSC::ExecState& state, const GPUBufferDescriptor& descriptor) const
+{
+    JSC::JSValue wrappedArrayBuffer = JSC::jsNull();
+
+    auto buffer = m_device->tryCreateBuffer(descriptor, true);
+    if (buffer) {
+        auto arrayBuffer = buffer->mapOnCreation();
+        wrappedArrayBuffer = toJS(&state, JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject()), arrayBuffer);
+    }
+
+    auto webBuffer = WebGPUBuffer::create(WTFMove(buffer));
+    auto wrappedWebBuffer = toJS(&state, JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject()), webBuffer);
+
+    return { wrappedWebBuffer, wrappedArrayBuffer };
+}
+
 Ref<WebGPUTexture> WebGPUDevice::createTexture(const GPUTextureDescriptor& descriptor) const
 {
     auto texture = m_device->tryCreateTexture(descriptor);
index 653f076..21d371a 100644 (file)
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace JSC {
+class ArrayBuffer;
+class JSValue;
+}
 
 namespace WebCore {
 
@@ -69,6 +75,7 @@ public:
     const GPUDevice& device() const { return m_device.get(); }
 
     Ref<WebGPUBuffer> createBuffer(const GPUBufferDescriptor&) const;
+    Vector<JSC::JSValue> createBufferMapped(JSC::ExecState&, const GPUBufferDescriptor&) const;
     Ref<WebGPUTexture> createTexture(const GPUTextureDescriptor&) const;
     Ref<WebGPUSampler> createSampler(const GPUSamplerDescriptor&) const;
 
index 057260e..d8314e7 100644 (file)
@@ -24,6 +24,8 @@
  */
 // https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
 
+typedef sequence<any> GPUMappedBuffer;  // [GPUBuffer, ArrayBuffer]
+
 [
     Conditional=WEBGPU,
     EnabledAtRuntime=WebGPU,
@@ -33,6 +35,7 @@
     readonly attribute WebGPUAdapter adapter;
 
     WebGPUBuffer createBuffer(GPUBufferDescriptor descriptor);
+    [CallWith=ExecState] GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor);
     WebGPUTexture createTexture(GPUTextureDescriptor descriptor);
     WebGPUSampler createSampler(GPUSamplerDescriptor descriptor);
 
index e46a9a4..42508bc 100644 (file)
@@ -68,7 +68,7 @@ public:
 
     ~GPUBuffer();
 
-    static RefPtr<GPUBuffer> tryCreate(Ref<GPUDevice>&&, const GPUBufferDescriptor&);
+    static RefPtr<GPUBuffer> tryCreate(Ref<GPUDevice>&&, const GPUBufferDescriptor&, bool isMappedOnCreation);
 
     PlatformBuffer *platformBuffer() const { return m_platformBuffer.get(); }
     size_t byteLength() const { return m_byteLength; }
@@ -82,14 +82,13 @@ public:
     bool isMappable() const { return m_usage.containsAny({ GPUBufferUsage::Flags::MapWrite, GPUBufferUsage::Flags::MapRead }); }
     State state() const;
 
+    JSC::ArrayBuffer* mapOnCreation();
+
 #if USE(METAL)
     void commandBufferCommitted(MTLCommandBuffer *);
     void commandBufferCompleted();
-
-    void reuseSubDataBuffer(RetainPtr<MTLBuffer>&&);
 #endif
 
-    void setSubData(uint64_t, const JSC::ArrayBuffer&);
     using MappingCallback = WTF::Function<void(JSC::ArrayBuffer*)>;
     void registerMappingCallback(MappingCallback&&, bool);
     void unmap();
@@ -108,13 +107,13 @@ private:
         PendingMappingCallback(MappingCallback&&);
     };
 
+    GPUBuffer(PlatformBufferSmartPtr&&, Ref<GPUDevice>&&, size_t, OptionSet<GPUBufferUsage::Flags>, bool);
     static bool validateBufferUsage(const GPUDevice&, OptionSet<GPUBufferUsage::Flags>);
 
-    GPUBuffer(PlatformBufferSmartPtr&&, size_t, OptionSet<GPUBufferUsage::Flags>, Ref<GPUDevice>&&);
-
     JSC::ArrayBuffer* stagingBufferForRead();
     JSC::ArrayBuffer* stagingBufferForWrite();
     void runMappingCallback();
+    void copyStagingBufferToGPU();
 
     bool isMapWrite() const { return m_usage.contains(GPUBufferUsage::Flags::MapWrite); }
     bool isMapRead() const { return m_usage.contains(GPUBufferUsage::Flags::MapRead); }
@@ -124,10 +123,6 @@ private:
     PlatformBufferSmartPtr m_platformBuffer;
     Ref<GPUDevice> m_device;
 
-#if USE(METAL)
-    Vector<RetainPtr<MTLBuffer>> m_subDataBuffers;
-#endif
-
     RefPtr<JSC::ArrayBuffer> m_stagingBuffer;
     RefPtr<PendingMappingCallback> m_mappingCallback;
     DeferrableTask<Timer> m_mappingCallbackTask;
@@ -135,6 +130,7 @@ private:
     size_t m_byteLength;
     OptionSet<GPUBufferUsage::Flags> m_usage;
     unsigned m_numScheduledCommandBuffers { 0 };
+    bool m_isMappedFromCreation { false };
 };
 
 } // namespace WebCore
index afb5277..94c4633 100644 (file)
@@ -108,7 +108,7 @@ private:
     PlatformCommandBufferSmartPtr m_platformCommandBuffer;
     Vector<Ref<GPUBuffer>> m_usedBuffers;
     Vector<Ref<GPUTexture>> m_usedTextures;
-    bool m_isEncodingPass = false;
+    bool m_isEncodingPass { false };
 #if USE(METAL)
     MTLBlitCommandEncoder *blitEncoder() const;
     mutable RetainPtr<MTLBlitCommandEncoder> m_blitEncoder;
index 083a77c..a649a60 100644 (file)
@@ -50,9 +50,9 @@
 
 namespace WebCore {
 
-RefPtr<GPUBuffer> GPUDevice::tryCreateBuffer(const GPUBufferDescriptor& descriptor)
+RefPtr<GPUBuffer> GPUDevice::tryCreateBuffer(const GPUBufferDescriptor& descriptor, bool isMappedOnCreation)
 {
-    return GPUBuffer::tryCreate(makeRef(*this), descriptor);
+    return GPUBuffer::tryCreate(makeRef(*this), descriptor, isMappedOnCreation);
 }
 
 RefPtr<GPUTexture> GPUDevice::tryCreateTexture(const GPUTextureDescriptor& descriptor) const
index c913def..a713954 100644 (file)
@@ -65,7 +65,7 @@ class GPUDevice : public RefCounted<GPUDevice>, public CanMakeWeakPtr<GPUDevice>
 public:
     static RefPtr<GPUDevice> tryCreate(const Optional<GPURequestAdapterOptions>&);
 
-    RefPtr<GPUBuffer> tryCreateBuffer(const GPUBufferDescriptor&);
+    RefPtr<GPUBuffer> tryCreateBuffer(const GPUBufferDescriptor&, bool isMappedOnCreation = false);
     RefPtr<GPUTexture> tryCreateTexture(const GPUTextureDescriptor&) const;
     RefPtr<GPUSampler> tryCreateSampler(const GPUSamplerDescriptor&) const;
 
index 8a3ed32..59a07ff 100644 (file)
@@ -62,7 +62,7 @@ bool GPUBuffer::validateBufferUsage(const GPUDevice& device, OptionSet<GPUBuffer
     return true;
 }
 
-RefPtr<GPUBuffer> GPUBuffer::tryCreate(Ref<GPUDevice>&& device, const GPUBufferDescriptor& descriptor)
+RefPtr<GPUBuffer> GPUBuffer::tryCreate(Ref<GPUDevice>&& device, const GPUBufferDescriptor& descriptor, bool isMappedOnCreation)
 {
     // MTLBuffer size (NSUInteger) is 32 bits on some platforms.
     NSUInteger size = 0;
@@ -75,6 +75,17 @@ RefPtr<GPUBuffer> GPUBuffer::tryCreate(Ref<GPUDevice>&& device, const GPUBufferD
     if (!validateBufferUsage(device.get(), usage))
         return nullptr;
 
+#if PLATFORM(MAC)
+    // copyBufferToBuffer calls require 4-byte alignment. "Unmapping" a mapped-on-creation GPUBuffer
+    // that is otherwise unmappable requires such a copy to upload data.
+    if (isMappedOnCreation
+        && !usage.containsAny({ GPUBufferUsage::Flags::MapWrite, GPUBufferUsage::Flags::MapRead })
+        && descriptor.size % 4) {
+        LOG(WebGPU, "GPUBuffer::tryCreate(): Data must be aligned to a multiple of 4 bytes!");
+        return nullptr;
+    }
+#endif
+
     // 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;
@@ -95,14 +106,15 @@ RefPtr<GPUBuffer> GPUBuffer::tryCreate(Ref<GPUDevice>&& device, const GPUBufferD
         return nullptr;
     }
 
-    return adoptRef(*new GPUBuffer(WTFMove(mtlBuffer), size, usage, WTFMove(device)));
+    return adoptRef(*new GPUBuffer(WTFMove(mtlBuffer), WTFMove(device), size, usage, isMappedOnCreation));
 }
 
-GPUBuffer::GPUBuffer(RetainPtr<MTLBuffer>&& buffer, size_t size, OptionSet<GPUBufferUsage::Flags> usage, Ref<GPUDevice>&& device)
+GPUBuffer::GPUBuffer(RetainPtr<MTLBuffer>&& buffer, Ref<GPUDevice>&& device, size_t size, OptionSet<GPUBufferUsage::Flags> usage, bool isMapped)
     : m_platformBuffer(WTFMove(buffer))
     , m_device(WTFMove(device))
     , m_byteLength(size)
     , m_usage(usage)
+    , m_isMappedFromCreation(isMapped)
 {
 }
 
@@ -120,69 +132,16 @@ GPUBuffer::State GPUBuffer::state() const
 {
     if (!m_platformBuffer)
         return State::Destroyed;
-    if (m_mappingCallback)
+    if (m_isMappedFromCreation || m_mappingCallback)
         return State::Mapped;
 
     return State::Unmapped;
 }
 
-void GPUBuffer::setSubData(uint64_t offset, const JSC::ArrayBuffer& data)
+JSC::ArrayBuffer* GPUBuffer::mapOnCreation()
 {
-    MTLCommandQueue *queue;
-    if (!m_device->tryGetQueue() || !(queue = m_device->tryGetQueue()->platformQueue()))
-        return;
-    
-    if (!isTransferDestination() || state() != State::Unmapped) {
-        LOG(WebGPU, "GPUBuffer::setSubData(): Invalid operation!");
-        return;
-    }
-
-#if PLATFORM(MAC)
-    if (offset % 4 || data.byteLength() % 4) {
-        LOG(WebGPU, "GPUBuffer::setSubData(): Data must be aligned to a multiple of 4 bytes!");
-        return;
-    }
-#endif
-    // MTLBuffer size (NSUInteger) is 32 bits on some platforms.
-    auto subDataLength = checkedSum<NSUInteger>(data.byteLength(), offset);
-    if (subDataLength.hasOverflowed() || subDataLength.unsafeGet() > m_byteLength) {
-        LOG(WebGPU, "GPUBuffer::setSubData(): Invalid offset or data size!");
-        return;
-    }
-
-    if (m_subDataBuffers.isEmpty()) {
-        BEGIN_BLOCK_OBJC_EXCEPTIONS;
-        m_subDataBuffers.append(adoptNS([m_platformBuffer.get().device newBufferWithLength:static_cast<NSUInteger>(m_byteLength) options:MTLResourceCPUCacheModeDefaultCache]));
-        END_BLOCK_OBJC_EXCEPTIONS;
-    }
-
-    __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;
-    }
-
-    memcpy(stagingMtlBuffer.get().contents, data.data(), data.byteLength());
-
-    BEGIN_BLOCK_OBJC_EXCEPTIONS;
-
-    auto commandBuffer = retainPtr([queue commandBuffer]);
-    auto blitEncoder = retainPtr([commandBuffer blitCommandEncoder]);
-
-    [blitEncoder copyFromBuffer:stagingMtlBuffer.get() sourceOffset:0 toBuffer:m_platformBuffer.get() destinationOffset:static_cast<NSUInteger>(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;
+    ASSERT(m_isMappedFromCreation);
+    return stagingBufferForWrite();
 }
 
 #if USE(METAL)
@@ -210,11 +169,6 @@ void GPUBuffer::commandBufferCompleted()
 
     --m_numScheduledCommandBuffers;
 }
-
-void GPUBuffer::reuseSubDataBuffer(RetainPtr<MTLBuffer>&& buffer)
-{
-    m_subDataBuffers.append(WTFMove(buffer));
-}
 #endif // USE(METAL)
 
 void GPUBuffer::registerMappingCallback(MappingCallback&& callback, bool isRead)
@@ -266,16 +220,54 @@ JSC::ArrayBuffer* GPUBuffer::stagingBufferForWrite()
     return m_stagingBuffer.get();
 }
 
+void GPUBuffer::copyStagingBufferToGPU()
+{
+    MTLCommandQueue *queue;
+    if (!m_device->tryGetQueue() || !(queue = m_device->tryGetQueue()->platformQueue()))
+        return;
+
+    RetainPtr<MTLBuffer> stagingMtlBuffer;
+
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    // GPUBuffer creation validation ensures m_byteSize fits in NSUInteger.
+    stagingMtlBuffer = adoptNS([m_device->platformDevice() newBufferWithLength:static_cast<NSUInteger>(m_byteLength) options:MTLResourceCPUCacheModeDefaultCache]);
+    END_BLOCK_OBJC_EXCEPTIONS;
+
+    if (!stagingMtlBuffer) {
+        LOG(WebGPU, "GPUBuffer::unmap(): Unable to create staging buffer!");
+        return;
+    }
+
+    memcpy(stagingMtlBuffer.get().contents, m_stagingBuffer->data(), m_byteLength);
+
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+    auto commandBuffer = retainPtr([queue commandBuffer]);
+    auto blitEncoder = retainPtr([commandBuffer blitCommandEncoder]);
+
+    [blitEncoder copyFromBuffer:stagingMtlBuffer.get() sourceOffset:0 toBuffer:m_platformBuffer.get() destinationOffset:0 size:static_cast<NSUInteger>(m_byteLength)];
+    [blitEncoder endEncoding];
+    [commandBuffer commit];
+
+    END_BLOCK_OBJC_EXCEPTIONS;
+}
+
 void GPUBuffer::unmap()
 {
-    if (!isMappable()) {
-        LOG(WebGPU, "GPUBuffer::unmap(): Buffer is not mappable!");
+    if (!m_isMappedFromCreation && !isMappable()) {
+        LOG(WebGPU, "GPUBuffer::unmap(): Invalid operation: buffer is not mappable!");
         return;
     }
 
-    if (m_stagingBuffer && isMapWrite()) {
-        ASSERT(m_platformBuffer);
-        memcpy(m_platformBuffer.get().contents, m_stagingBuffer->data(), m_byteLength);
+    if (m_stagingBuffer) {
+        if (isMappable()) {
+            // MAP_WRITE and MAP_READ buffers have shared, CPU-accessible storage.
+            ASSERT(m_platformBuffer && m_platformBuffer.get().contents);
+            memcpy(m_platformBuffer.get().contents, m_stagingBuffer->data(), m_byteLength);
+        } else if (m_isMappedFromCreation)
+            copyStagingBufferToGPU();
+
+        m_isMappedFromCreation = false;
         m_stagingBuffer = nullptr;
     }