[WHLSL] Implement array references
authormmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Jun 2019 05:38:28 +0000 (05:38 +0000)
committermmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Jun 2019 05:38:28 +0000 (05:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=198163

Reviewed by Saam Barati.

Source/WebCore:

The compiler automatically generates anders for every array reference. Luckily, the infrastructure
to generate those anders and emit Metal code to represent them already exists in the compiler.
There are two pieces remaining (which this patch implements):

1. The JavaScript compiler has a behavior where anders that are called with an array reference
   as an argument don't wrap the argument in a MakePointerExpression. This is because the array
   reference is already a reference type, so it's silly to operate on a pointer to a reference.
   This patch implements this by teaching the type checker about which types should be passed
   to the ander call, and by actually constructing those types in the property resolver.
   The property resolver does this by placing the logic to construct an ander argument in a
   single function which also has logic to save the argument in a temporary if the thread ander
   will be called. The semantics about which functions are called in which situations are not
   changed; instead, we just simply don't wrap array references with MakePointerExpressions.

2. Creating a bind group from the WebGPU API has to retain information about buffer lengths for
   each buffer so the shader can properly perform bounds checks. This can be broken down into a
   few pieces:
   - Creating a bind group layout has to assign extra id indexes for each buffer which will be
     filled in to represent the buffer's length
   - Creating the bind group itself needs to fill in the buffer length into the Metal argument
     buffer
   - The shader compiler needs to emit code at the beginning of entry point to find the buffer
     lengths and pack them together into the array reference (array references correspond to
     a Metal struct with two fields: a pointer and a length).

This patch doesn't actually implement bounds checks themselves; it just hooks up the buffer
lengths so https://bugs.webkit.org/show_bug.cgi?id=198600 can implement it.

The shader compiler's API is modified to allow for this extra buffer length information to be
passed in from the WebGPU implementation.

Unfortunately, I don't think I could split this patch up into two pieces because both are
required to test the compiler with buffers.

Tests: webgpu/whlsl-buffer-fragment.html
       webgpu/whlsl-buffer-vertex.html

* Modules/webgpu/WHLSL/AST/WHLSLPropertyAccessExpression.h:
(WebCore::WHLSL::AST::PropertyAccessExpression::baseReference):
* Modules/webgpu/WHLSL/AST/WHLSLResourceSemantic.cpp:
(WebCore::WHLSL::AST::ResourceSemantic::isAcceptableType const): Arrays can't be resources
because the compiler has no way of guaranteeing if the resource is long enough to hold the
array at compile time.
* Modules/webgpu/WHLSL/Metal/WHLSLEntryPointScaffolding.cpp:
(WebCore::WHLSL::Metal::EntryPointScaffolding::EntryPointScaffolding): Generate an extra
variable name to represent the buffer length. Only do it for resources which have lengths.
(WebCore::WHLSL::Metal::EntryPointScaffolding::resourceHelperTypes):
(WebCore::WHLSL::Metal::EntryPointScaffolding::unpackResourcesAndNamedBuiltIns): Perform
the appropriate math to turn byte lengths into element counts and store the element count
in the array reference.
* Modules/webgpu/WHLSL/Metal/WHLSLEntryPointScaffolding.h:
* Modules/webgpu/WHLSL/WHLSLChecker.cpp:
(WebCore::WHLSL::resolveWithOperatorAnderIndexer): Refactor.
(WebCore::WHLSL::resolveWithOperatorLength): Ditto.
(WebCore::WHLSL::resolveWithReferenceComparator): Ditto.
(WebCore::WHLSL::resolveByInstantiation): Ditto.
(WebCore::WHLSL::argumentTypeForAndOverload): Given an ander, what should the type of the
argument be?
(WebCore::WHLSL::Checker::finishVisiting): Call argumentTypeForAndOverload(). Also, if
we couldn't find an ander, try automatically generating it, the same way that function
calls do. (This is how array references get their anders.)
(WebCore::WHLSL::Checker::visit):
* Modules/webgpu/WHLSL/WHLSLPipelineDescriptor.h: New WHLSL API to provide the length
information.
* Modules/webgpu/WHLSL/WHLSLPropertyResolver.cpp:
(WebCore::WHLSL::PropertyResolver::visit): SimplifyRightValue() can't fail any more.
(WebCore::WHLSL::wrapAnderCallArgument): If the ander argument should be wrapped in a
MakePointer or a MakeArrayReference, do that. Also, if the ander is a thread ander, save
the argument in a local variable and use that.
(WebCore::WHLSL::anderCallArgument): The equivalent of argumentTypeForAndOverload().
(WebCore::WHLSL::setterCall): Call anderCallArgument().
(WebCore::WHLSL::getterCall): Ditto.
(WebCore::WHLSL::modify): We used to have special-case code for handling pointer-to-argument
values as distinct from just the argument values themselves. However, emitting
chains of &* operators is valid and won't even make it through the Metal code generator
after https://bugs.webkit.org/show_bug.cgi?id=198600 is fixed. So, in order to simplify
wrapAnderCallArgument(), don't special case these values and just create &* chains instead.
(WebCore::WHLSL::PropertyResolver::simplifyRightValue):
(WebCore::WHLSL::LeftValueSimplifier::finishVisiting): Call anderCallArgument().
* Modules/webgpu/WHLSL/WHLSLSemanticMatcher.cpp: Update to support the new compiler API.
(WebCore::WHLSL::matchMode):
(WebCore::WHLSL::matchResources):
* Modules/webgpu/WebGPUBindGroupDescriptor.cpp: Ditto.
(WebCore::WebGPUBindGroupDescriptor::tryCreateGPUBindGroupDescriptor const):
* platform/graphics/gpu/GPUBindGroupLayout.h: Add some internal implementation data inside
the bindings object. Use a Variant to differentiate between the various bindings types, and
put the extra length field on just those members of the variant that represent buffers.
* platform/graphics/gpu/cocoa/GPUBindGroupLayoutMetal.mm: Update to support the new compiler API.
(WebCore::argumentDescriptor):
(WebCore::GPUBindGroupLayout::tryCreate):
* platform/graphics/gpu/cocoa/GPUBindGroupMetal.mm: Ditto.
(WebCore::setBufferOnEncoder):
(WebCore::GPUBindGroup::tryCreate):
* platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm: Ditto.
(WebCore::convertBindingType):
(WebCore::convertLayout):

LayoutTests:

* webgpu/buffer-resource-triangles-expected.html: Deleted. This test doens't make any sense and triggers
Metal to read out-of-bounds of a vertex buffer.
* webgpu/buffer-resource-triangles.html: Deleted.
* webgpu/whlsl-buffer-fragment-expected.html: Added.
* webgpu/whlsl-buffer-fragment.html: Added.
* webgpu/whlsl-buffer-vertex-expected.html: Added.
* webgpu/whlsl-buffer-vertex.html: Added.
* webgpu/whlsl-dont-crash-parsing-enum.html:
* webgpu/whlsl.html:

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

23 files changed:
LayoutTests/ChangeLog
LayoutTests/webgpu/buffer-resource-triangles-expected.html [deleted file]
LayoutTests/webgpu/buffer-resource-triangles.html [deleted file]
LayoutTests/webgpu/whlsl-buffer-fragment-expected.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-buffer-fragment.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-buffer-vertex-expected.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-buffer-vertex.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-dont-crash-parsing-enum.html
LayoutTests/webgpu/whlsl.html
Source/WebCore/ChangeLog
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLPropertyAccessExpression.h
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLResourceSemantic.cpp
Source/WebCore/Modules/webgpu/WHLSL/Metal/WHLSLEntryPointScaffolding.cpp
Source/WebCore/Modules/webgpu/WHLSL/Metal/WHLSLEntryPointScaffolding.h
Source/WebCore/Modules/webgpu/WHLSL/WHLSLChecker.cpp
Source/WebCore/Modules/webgpu/WHLSL/WHLSLPipelineDescriptor.h
Source/WebCore/Modules/webgpu/WHLSL/WHLSLPropertyResolver.cpp
Source/WebCore/Modules/webgpu/WHLSL/WHLSLSemanticMatcher.cpp
Source/WebCore/Modules/webgpu/WebGPUBindGroupDescriptor.cpp
Source/WebCore/platform/graphics/gpu/GPUBindGroupLayout.h
Source/WebCore/platform/graphics/gpu/cocoa/GPUBindGroupLayoutMetal.mm
Source/WebCore/platform/graphics/gpu/cocoa/GPUBindGroupMetal.mm
Source/WebCore/platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm

index 1e5c720..dd60667 100644 (file)
@@ -1,3 +1,20 @@
+2019-06-12  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [WHLSL] Implement array references
+        https://bugs.webkit.org/show_bug.cgi?id=198163
+
+        Reviewed by Saam Barati.
+
+        * webgpu/buffer-resource-triangles-expected.html: Deleted. This test doens't make any sense and triggers
+        Metal to read out-of-bounds of a vertex buffer.
+        * webgpu/buffer-resource-triangles.html: Deleted.
+        * webgpu/whlsl-buffer-fragment-expected.html: Added.
+        * webgpu/whlsl-buffer-fragment.html: Added.
+        * webgpu/whlsl-buffer-vertex-expected.html: Added.
+        * webgpu/whlsl-buffer-vertex.html: Added.
+        * webgpu/whlsl-dont-crash-parsing-enum.html:
+        * webgpu/whlsl.html:
+
 2019-06-12  Justin Fan  <justin_fan@apple.com>
 
         [WebGL] ANGLE Extension directive location incorrectly enforced for webgl 1.0
diff --git a/LayoutTests/webgpu/buffer-resource-triangles-expected.html b/LayoutTests/webgpu/buffer-resource-triangles-expected.html
deleted file mode 100644 (file)
index 886f13e..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>HTML Reference File</title>
-<p>Pass if square canvas below is completely green.</p>
-<canvas width="400" height="400"></canvas>
-<script>
-const canvas = document.querySelector("canvas");
-const context = canvas.getContext('2d');
-
-context.fillStyle = 'rgb(0, 255, 0)';
-context.fillRect(0, 0, canvas.width, canvas.height);
-</script>
\ No newline at end of file
diff --git a/LayoutTests/webgpu/buffer-resource-triangles.html b/LayoutTests/webgpu/buffer-resource-triangles.html
deleted file mode 100644 (file)
index 263626c..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-<!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="buffer-resource-triangles-expected.html">
-<p>Pass if square canvas below is completely green.</p>
-<canvas width="400" height="400"></canvas>
-<script src="js/webgpu-functions.js"></script>
-<script>
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-const shaderCode = `
-#include <metal_stdlib>
-    
-using namespace metal;
-
-struct VertexInput {
-    float4 position [[attribute(0)]];
-};
-
-struct Vertex {
-    float4 position [[position]];
-};
-
-struct VertexArguments {
-    device Vertex* v0;
-    device Vertex* v1;
-    device Vertex* v2;
-};
-
-vertex Vertex vertex_main(
-    VertexInput input [[stage_in]],
-    const device VertexArguments& args0 [[buffer(0)]],
-    const device VertexArguments& args1 [[buffer(1)]],
-    uint vid [[vertex_id]])
-{
-    switch (vid)
-    {
-        case 0:
-        case 1:
-        case 2: {
-            Vertex out;
-            out.position = input.position;
-            return out;
-        }
-        case 3: return *args0.v0;
-        case 4: return *args0.v1;
-        case 5: return *args0.v2;
-        case 6: return *args1.v0;
-        case 7: return *args1.v1;
-        default: return *args1.v2;
-    }
-}
-
-struct FragmentArguments {
-    device float4* color;
-};
-
-fragment float4 fragment_main(const device FragmentArguments& args [[buffer(0)]])
-{
-    return args.color[0];
-}
-`
-
-const bindingNums = {
-    UL: 0,
-    UM: 1,
-    UR: 2,
-    LL: 3,
-    LR: 4,
-    G: 5
-};
-
-function createUniformBufferBindGroupLayout(bindNum, stage = GPUShaderStageBit.VERTEX) {
-    return {
-        binding: bindNum,
-        visibility: stage,
-        type: "uniform-buffer"
-    };
-}
-
-const vertexSize = 4 * 4;
-function createAndUploadVerticesBuffer(device) {
-    const vertexArray = new Float32Array([
-        0, 1, 0, 1,
-        -1, -1, 0, 1,
-        1, -1, 0, 1
-    ]);
-    return createBufferWithData(device, { size: vertexArray.byteLength, usage: GPUBufferUsage.VERTEX }, vertexArray.buffer);
-}
-
-function createFloat4Buffer(device, a, b, promises) {
-    const buffer = device.createBuffer({ size: vertexSize, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE });
-
-    const promise = buffer.mapWriteAsync().then(mapping => {
-        const mappedArray = new Float32Array(mapping);
-        mappedArray.set([a, b, 0, 1]);
-        buffer.unmap();
-    });
-
-    promises.push(promise);
-    return buffer;
-}
-
-function createBufferBinding(buffer) {
-    return { buffer: buffer, size: vertexSize };
-}
-
-async function test() {
-    const device = await getBasicDevice();
-    const canvas = document.querySelector("canvas");
-    const swapChain = createBasicSwapChain(canvas, device);
-    // FIXME: Replace with non-MSL shaders.
-    const shaderModule = device.createShaderModule({ code: shaderCode });
-
-    // Create vertex data GPUBuffers.
-    const verticesBuffer = createAndUploadVerticesBuffer(device);
-
-    let bufferPromises = [];
-    const upperLeft = createFloat4Buffer(device, -1, 1, bufferPromises);
-    const upperMiddle = createFloat4Buffer(device, 0, 1, bufferPromises);
-    const upperRight = createFloat4Buffer(device, 1, 1, bufferPromises);
-    const lowerLeft = createFloat4Buffer(device, -1, -1, bufferPromises);
-    const lowerRight = createFloat4Buffer(device, 1, -1, bufferPromises);
-
-    // Color data buffer.
-    const green = createFloat4Buffer(device, 0, 1, bufferPromises);
-
-    // Create vertex input state.
-    const vertexInput = {
-        indexFormat: "uint32",
-        vertexBuffers: [{
-            stride: vertexSize,
-            attributeSet: [{
-                format: "float4",
-                shaderLocation: 0
-            }]
-        }]
-    };
-
-    // Create buffer GPUBindGroupLayoutBindings.
-    const layoutUL = createUniformBufferBindGroupLayout(bindingNums.UL);
-    const layoutUM = createUniformBufferBindGroupLayout(bindingNums.UM);
-    const layoutUR = createUniformBufferBindGroupLayout(bindingNums.UR);
-    const layoutLL = createUniformBufferBindGroupLayout(bindingNums.LL);
-    const layoutLR = createUniformBufferBindGroupLayout(bindingNums.LR);
-    const layoutG = createUniformBufferBindGroupLayout(bindingNums.G, GPUShaderStageBit.FRAGMENT);
-
-    // GPUBindGroupLayouts
-    const leftTriangleBGLayout = device.createBindGroupLayout({ bindings: [layoutUL, layoutUM, layoutLL, layoutG] });
-    const rightTriangleBGLayout = device.createBindGroupLayout({ bindings: [layoutUR, layoutUM, layoutLR] });
-
-    // GPUPipelineLayout and GPURenderPipeline
-    const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [leftTriangleBGLayout, rightTriangleBGLayout] });
-    const pipeline = createBasicPipeline(shaderModule, device, null, pipelineLayout, vertexInput, null, "triangle-list");
-
-    // GPUBufferBindings
-    const bindingUL = createBufferBinding(upperLeft);
-    const bindingUM = createBufferBinding(upperMiddle);
-    const bindingUR = createBufferBinding(upperRight);
-    const bindingLL = createBufferBinding(lowerLeft);
-    const bindingLR = createBufferBinding(lowerRight);
-    const bindingG = createBufferBinding(green);
-    
-    // GPUBindGroupBindings
-    const bgBindingUL = { binding: bindingNums.UL, resource: bindingUL };
-    const bgBindingUM = { binding: bindingNums.UM, resource: bindingUM };
-    const bgBindingUR = { binding: bindingNums.UR, resource: bindingUR };
-    const bgBindingLL = { binding: bindingNums.LL, resource: bindingLL };
-    const bgBindingLR = { binding: bindingNums.LR, resource: bindingLR };
-    const bgBindingG = { binding: bindingNums.G, resource: bindingG };
-
-    // GPUBindGroups
-    const leftTriangleBG = device.createBindGroup({ 
-        layout: leftTriangleBGLayout, 
-        bindings: [bgBindingUL, bgBindingUM, bgBindingLL, bgBindingG] 
-    });
-    const rightTriangleBG = device.createBindGroup({
-        layout: rightTriangleBGLayout,
-        bindings: [bgBindingUR, bgBindingUM, bgBindingLR]
-    });
-
-    Promise.all(bufferPromises).then(() => {
-        const commandEncoder = device.createCommandEncoder();
-        const passEncoder = beginBasicRenderPass(swapChain, commandEncoder);
-        passEncoder.setPipeline(pipeline);
-
-        // Vertex data for upper triangles.
-        passEncoder.setBindGroup(0, leftTriangleBG);
-        passEncoder.setBindGroup(1, rightTriangleBG);
-        // Lower triangle.
-        passEncoder.setVertexBuffers(0, [verticesBuffer], [0]);
-        passEncoder.draw(9, 1, 0, 0);
-
-        passEncoder.endPass();
-        const queue = device.getQueue();
-        queue.submit([commandEncoder.finish()]);
-    });
-}
-
-test().then(function() {
-    if (window.testRunner)
-        testRunner.notifyDone();
-}, function() {
-    if (window.testRunner)
-        testRunner.notifyDone();
-});
-</script>
diff --git a/LayoutTests/webgpu/whlsl-buffer-fragment-expected.html b/LayoutTests/webgpu/whlsl-buffer-fragment-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/whlsl-buffer-fragment.html b/LayoutTests/webgpu/whlsl-buffer-fragment.html
new file mode 100644 (file)
index 0000000..9bb788f
--- /dev/null
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(float4 position : attribute(0), float i : attribute(1)) : SV_Position {
+    return position;
+}
+
+fragment float4 fragmentShader(float4 position : SV_Position, constant float[] theBuffer : register(b0)) : SV_Target 0 {
+    return float4(theBuffer[0], theBuffer[0], theBuffer[0], 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+    
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const attribute1 = {shaderLocation: 1, format: "float"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const input1 = {stride: 4, attributeSet: [attribute1]};
+    const inputs = [input0, input1];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const bindGroupLayoutDescriptor = {bindings: [{binding: 0, visibility: 7, type: "uniform-buffer"}]};
+    const bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
+    const pipelineLayoutDescriptor = {bindGroupLayouts: [bindGroupLayout]};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBuffer0Descriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer0 = device.createBuffer(vertexBuffer0Descriptor);
+    const vertexBuffer0ArrayBuffer = await vertexBuffer0.mapWriteAsync();
+    const vertexBuffer0Float32Array = new Float32Array(vertexBuffer0ArrayBuffer);
+    vertexBuffer0Float32Array[0] = -0.5;
+    vertexBuffer0Float32Array[1] = -0.5;
+    vertexBuffer0Float32Array[2] = 1.0;
+    vertexBuffer0Float32Array[3] = 1;
+    vertexBuffer0Float32Array[4] = -0.5;
+    vertexBuffer0Float32Array[5] = 0.5;
+    vertexBuffer0Float32Array[6] = 1.0;
+    vertexBuffer0Float32Array[7] = 1;
+    vertexBuffer0Float32Array[8] = 0.5;
+    vertexBuffer0Float32Array[9] = -0.5;
+    vertexBuffer0Float32Array[10] = 1.0;
+    vertexBuffer0Float32Array[11] = 1;
+    vertexBuffer0Float32Array[12] = 0.5;
+    vertexBuffer0Float32Array[13] = 0.5;
+    vertexBuffer0Float32Array[14] = 1.0;
+    vertexBuffer0Float32Array[15] = 1;
+    vertexBuffer0.unmap();
+
+    const vertexBuffer1Descriptor = {size: Float32Array.BYTES_PER_ELEMENT * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer1 = device.createBuffer(vertexBuffer1Descriptor);
+    const vertexBuffer1ArrayBuffer = await vertexBuffer1.mapWriteAsync();
+    const vertexBuffer1Float32Array = new Float32Array(vertexBuffer1ArrayBuffer);
+    vertexBuffer1Float32Array[0] = 1;
+    vertexBuffer1Float32Array[1] = 1;
+    vertexBuffer1Float32Array[2] = 1;
+    vertexBuffer1Float32Array[3] = 1;
+    vertexBuffer1.unmap();
+
+    const resourceBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE};
+    const resourceBuffer = device.createBuffer(resourceBufferDescriptor);
+    const resourceBufferArrayBuffer = await resourceBuffer.mapWriteAsync();
+    const resourceBufferFloat32Array = new Float32Array(resourceBufferArrayBuffer);
+    resourceBufferFloat32Array[0] = 1;
+    resourceBuffer.unmap();
+
+    const bufferBinding = {buffer: resourceBuffer, size: 4};
+    const bindGroupBinding = {binding: 0, resource: bufferBinding};
+    const bindGroupDescriptor = {layout: bindGroupLayout, bindings: [bindGroupBinding]};
+    const bindGroup = device.createBindGroup(bindGroupDescriptor);
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setBindGroup(0, bindGroup);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer0, vertexBuffer1], [0, 0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", function() {
+    start().then(function() {
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }, function() {
+        if (window.testRunner)
+            testRunner.notifyDone();
+    });
+});
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/whlsl-buffer-vertex-expected.html b/LayoutTests/webgpu/whlsl-buffer-vertex-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/whlsl-buffer-vertex.html b/LayoutTests/webgpu/whlsl-buffer-vertex.html
new file mode 100644 (file)
index 0000000..71d4e35
--- /dev/null
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+vertex float4 vertexShader(constant float4[] buffer : register(b0), uint id : SV_VertexID) : SV_Position {
+    return buffer[id];
+}
+
+fragment float4 fragmentShader(float4 position : SV_Position) : SV_Target 0 {
+    return float4(1.0, 1.0, 1.0, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+
+    const vertexInput = {vertexBuffers: []};
+
+    const bindGroupLayoutDescriptor = {bindings: [{binding: 0, visibility: 7, type: "uniform-buffer"}]};
+    const bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
+    const pipelineLayoutDescriptor = {bindGroupLayouts: [bindGroupLayout]};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const resourceBufferDescriptor = {size: 4 * 4 * Float32Array.BYTES_PER_ELEMENT, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE};
+    const resourceBuffer = device.createBuffer(resourceBufferDescriptor);
+    const resourceBufferArrayBuffer = await resourceBuffer.mapWriteAsync();
+    const resourceBufferFloat32Array = new Float32Array(resourceBufferArrayBuffer);
+    resourceBufferFloat32Array[0] = -0.5;
+    resourceBufferFloat32Array[1] = -0.5;
+    resourceBufferFloat32Array[2] = 1.0;
+    resourceBufferFloat32Array[3] = 1;
+    resourceBufferFloat32Array[4] = -0.5;
+    resourceBufferFloat32Array[5] = 0.5;
+    resourceBufferFloat32Array[6] = 1.0;
+    resourceBufferFloat32Array[7] = 1;
+    resourceBufferFloat32Array[8] = 0.5;
+    resourceBufferFloat32Array[9] = -0.5;
+    resourceBufferFloat32Array[10] = 1.0;
+    resourceBufferFloat32Array[11] = 1;
+    resourceBufferFloat32Array[12] = 0.5;
+    resourceBufferFloat32Array[13] = 0.5;
+    resourceBufferFloat32Array[14] = 1.0;
+    resourceBufferFloat32Array[15] = 1;
+    resourceBuffer.unmap();
+
+    const bufferBinding = {buffer: resourceBuffer, size: 4};
+    const bindGroupBinding = {binding: 0, resource: bufferBinding};
+    const bindGroupDescriptor = {layout: bindGroupLayout, bindings: [bindGroupBinding]};
+    const bindGroup = device.createBindGroup(bindGroupDescriptor);
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setBindGroup(0, bindGroup);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", function() {
+    start().then(function() {
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }, function() {
+        if (window.testRunner)
+            testRunner.notifyDone();
+    });
+});
+</script>
+</body>
+</html>
index 4a2252f..8986f7f 100644 (file)
@@ -74,10 +74,10 @@ async function start() {
     const vertexBuffer1 = device.createBuffer(vertexBuffer1Descriptor);
     const vertexBuffer1ArrayBuffer = await vertexBuffer1.mapWriteAsync();
     const vertexBuffer1Float32Array = new Float32Array(vertexBuffer1ArrayBuffer);
-    vertexBuffer1Descriptor[0] = 1;
-    vertexBuffer1Descriptor[1] = 1;
-    vertexBuffer1Descriptor[2] = 1;
-    vertexBuffer1Descriptor[3] = 1;
+    vertexBuffer1Float32Array[0] = 1;
+    vertexBuffer1Float32Array[1] = 1;
+    vertexBuffer1Float32Array[2] = 1;
+    vertexBuffer1Float32Array[3] = 1;
     vertexBuffer1.unmap();
 
     const resourceBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE};
index 9f79433..676bd06 100644 (file)
@@ -69,10 +69,10 @@ async function start() {
     const vertexBuffer1 = device.createBuffer(vertexBuffer1Descriptor);
     const vertexBuffer1ArrayBuffer = await vertexBuffer1.mapWriteAsync();
     const vertexBuffer1Float32Array = new Float32Array(vertexBuffer1ArrayBuffer);
-    vertexBuffer1Descriptor[0] = 1;
-    vertexBuffer1Descriptor[1] = 1;
-    vertexBuffer1Descriptor[2] = 1;
-    vertexBuffer1Descriptor[3] = 1;
+    vertexBuffer1Float32Array[0] = 1;
+    vertexBuffer1Float32Array[1] = 1;
+    vertexBuffer1Float32Array[2] = 1;
+    vertexBuffer1Float32Array[3] = 1;
     vertexBuffer1.unmap();
 
     const resourceBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE};
index 50bbaa7..06ae4ed 100644 (file)
@@ -1,3 +1,107 @@
+2019-06-12  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [WHLSL] Implement array references
+        https://bugs.webkit.org/show_bug.cgi?id=198163
+
+        Reviewed by Saam Barati.
+
+        The compiler automatically generates anders for every array reference. Luckily, the infrastructure
+        to generate those anders and emit Metal code to represent them already exists in the compiler.
+        There are two pieces remaining (which this patch implements):
+
+        1. The JavaScript compiler has a behavior where anders that are called with an array reference
+           as an argument don't wrap the argument in a MakePointerExpression. This is because the array
+           reference is already a reference type, so it's silly to operate on a pointer to a reference.
+           This patch implements this by teaching the type checker about which types should be passed
+           to the ander call, and by actually constructing those types in the property resolver.
+           The property resolver does this by placing the logic to construct an ander argument in a
+           single function which also has logic to save the argument in a temporary if the thread ander
+           will be called. The semantics about which functions are called in which situations are not
+           changed; instead, we just simply don't wrap array references with MakePointerExpressions.
+
+        2. Creating a bind group from the WebGPU API has to retain information about buffer lengths for
+           each buffer so the shader can properly perform bounds checks. This can be broken down into a
+           few pieces:
+           - Creating a bind group layout has to assign extra id indexes for each buffer which will be
+             filled in to represent the buffer's length
+           - Creating the bind group itself needs to fill in the buffer length into the Metal argument
+             buffer
+           - The shader compiler needs to emit code at the beginning of entry point to find the buffer
+             lengths and pack them together into the array reference (array references correspond to
+             a Metal struct with two fields: a pointer and a length).
+
+        This patch doesn't actually implement bounds checks themselves; it just hooks up the buffer
+        lengths so https://bugs.webkit.org/show_bug.cgi?id=198600 can implement it.
+
+        The shader compiler's API is modified to allow for this extra buffer length information to be
+        passed in from the WebGPU implementation.
+
+        Unfortunately, I don't think I could split this patch up into two pieces because both are
+        required to test the compiler with buffers.
+
+        Tests: webgpu/whlsl-buffer-fragment.html
+               webgpu/whlsl-buffer-vertex.html
+
+        * Modules/webgpu/WHLSL/AST/WHLSLPropertyAccessExpression.h:
+        (WebCore::WHLSL::AST::PropertyAccessExpression::baseReference):
+        * Modules/webgpu/WHLSL/AST/WHLSLResourceSemantic.cpp:
+        (WebCore::WHLSL::AST::ResourceSemantic::isAcceptableType const): Arrays can't be resources
+        because the compiler has no way of guaranteeing if the resource is long enough to hold the
+        array at compile time.
+        * Modules/webgpu/WHLSL/Metal/WHLSLEntryPointScaffolding.cpp:
+        (WebCore::WHLSL::Metal::EntryPointScaffolding::EntryPointScaffolding): Generate an extra
+        variable name to represent the buffer length. Only do it for resources which have lengths.
+        (WebCore::WHLSL::Metal::EntryPointScaffolding::resourceHelperTypes):
+        (WebCore::WHLSL::Metal::EntryPointScaffolding::unpackResourcesAndNamedBuiltIns): Perform
+        the appropriate math to turn byte lengths into element counts and store the element count
+        in the array reference.
+        * Modules/webgpu/WHLSL/Metal/WHLSLEntryPointScaffolding.h:
+        * Modules/webgpu/WHLSL/WHLSLChecker.cpp:
+        (WebCore::WHLSL::resolveWithOperatorAnderIndexer): Refactor.
+        (WebCore::WHLSL::resolveWithOperatorLength): Ditto.
+        (WebCore::WHLSL::resolveWithReferenceComparator): Ditto.
+        (WebCore::WHLSL::resolveByInstantiation): Ditto.
+        (WebCore::WHLSL::argumentTypeForAndOverload): Given an ander, what should the type of the
+        argument be?
+        (WebCore::WHLSL::Checker::finishVisiting): Call argumentTypeForAndOverload(). Also, if
+        we couldn't find an ander, try automatically generating it, the same way that function
+        calls do. (This is how array references get their anders.)
+        (WebCore::WHLSL::Checker::visit):
+        * Modules/webgpu/WHLSL/WHLSLPipelineDescriptor.h: New WHLSL API to provide the length
+        information.
+        * Modules/webgpu/WHLSL/WHLSLPropertyResolver.cpp:
+        (WebCore::WHLSL::PropertyResolver::visit): SimplifyRightValue() can't fail any more.
+        (WebCore::WHLSL::wrapAnderCallArgument): If the ander argument should be wrapped in a
+        MakePointer or a MakeArrayReference, do that. Also, if the ander is a thread ander, save
+        the argument in a local variable and use that.
+        (WebCore::WHLSL::anderCallArgument): The equivalent of argumentTypeForAndOverload().
+        (WebCore::WHLSL::setterCall): Call anderCallArgument().
+        (WebCore::WHLSL::getterCall): Ditto.
+        (WebCore::WHLSL::modify): We used to have special-case code for handling pointer-to-argument
+        values as distinct from just the argument values themselves. However, emitting
+        chains of &* operators is valid and won't even make it through the Metal code generator
+        after https://bugs.webkit.org/show_bug.cgi?id=198600 is fixed. So, in order to simplify
+        wrapAnderCallArgument(), don't special case these values and just create &* chains instead.
+        (WebCore::WHLSL::PropertyResolver::simplifyRightValue):
+        (WebCore::WHLSL::LeftValueSimplifier::finishVisiting): Call anderCallArgument().
+        * Modules/webgpu/WHLSL/WHLSLSemanticMatcher.cpp: Update to support the new compiler API.
+        (WebCore::WHLSL::matchMode):
+        (WebCore::WHLSL::matchResources):
+        * Modules/webgpu/WebGPUBindGroupDescriptor.cpp: Ditto.
+        (WebCore::WebGPUBindGroupDescriptor::tryCreateGPUBindGroupDescriptor const):
+        * platform/graphics/gpu/GPUBindGroupLayout.h: Add some internal implementation data inside
+        the bindings object. Use a Variant to differentiate between the various bindings types, and
+        put the extra length field on just those members of the variant that represent buffers.
+        * platform/graphics/gpu/cocoa/GPUBindGroupLayoutMetal.mm: Update to support the new compiler API.
+        (WebCore::argumentDescriptor):
+        (WebCore::GPUBindGroupLayout::tryCreate):
+        * platform/graphics/gpu/cocoa/GPUBindGroupMetal.mm: Ditto.
+        (WebCore::setBufferOnEncoder):
+        (WebCore::GPUBindGroup::tryCreate):
+        * platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm: Ditto.
+        (WebCore::convertBindingType):
+        (WebCore::convertLayout):
+
 2019-06-12  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [cairo][SVG] If clipPath has multiple elements, clip-path doesn't work with transform
index 8f4aa0e..bb0c301 100644 (file)
@@ -99,6 +99,7 @@ public:
     }
 
     Expression& base() { return m_base; }
+    UniqueRef<Expression>& baseReference() { return m_base; }
     UniqueRef<Expression> takeBase() { return WTFMove(m_base); }
 
 private:
index 3b58c3d..e56287e 100644 (file)
@@ -49,8 +49,6 @@ bool ResourceSemantic::isAcceptableType(const UnnamedType& unnamedType, const In
             auto& referenceType = downcast<ReferenceType>(unnamedType);
             return referenceType.addressSpace() == AddressSpace::Constant || referenceType.addressSpace() == AddressSpace::Device;
         }
-        if (is<ArrayType>(unnamedType))
-            return true;
         if (is<TypeReference>(unnamedType)) {
             auto& typeReference = downcast<TypeReference>(unnamedType);
             if (is<NativeTypeDeclaration>(typeReference.resolvedType()))
@@ -71,7 +69,7 @@ bool ResourceSemantic::isAcceptableType(const UnnamedType& unnamedType, const In
     case Mode::Buffer:
         if (is<ReferenceType>(unnamedType))
             return downcast<ReferenceType>(unnamedType).addressSpace() == AddressSpace::Constant;
-        return is<ArrayType>(unnamedType);
+        return false;
     case Mode::Sampler:
         return matches(unnamedType, intrinsics.samplerType());
     }
index 21974eb..da8c057 100644 (file)
@@ -32,6 +32,7 @@
 #include "WHLSLFunctionDefinition.h"
 #include "WHLSLGatherEntryPointItems.h"
 #include "WHLSLPipelineDescriptor.h"
+#include "WHLSLReferenceType.h"
 #include "WHLSLResourceSemantic.h"
 #include "WHLSLStageInOutSemantic.h"
 #include "WHLSLStructureDefinition.h"
@@ -108,7 +109,16 @@ EntryPointScaffolding::EntryPointScaffolding(AST::FunctionDefinition& functionDe
         for (size_t j = 0; j < m_layout[i].bindings.size(); ++j) {
             NamedBinding namedBinding;
             namedBinding.elementName = m_typeNamer.generateNextStructureElementName();
-            namedBinding.index = m_layout[i].bindings[j].name; // GPUBindGroupLayout::tryCreate() makes sure these don't collide.
+            namedBinding.index = m_layout[i].bindings[j].internalName;
+            WTF::visit(WTF::makeVisitor([&](UniformBufferBinding& uniformBufferBinding) {
+                LengthInformation lengthInformation { m_typeNamer.generateNextStructureElementName(), m_generateNextVariableName(), uniformBufferBinding.lengthName };
+                namedBinding.lengthInformation = lengthInformation;
+            }, [&](SamplerBinding&) {
+            }, [&](TextureBinding&) {
+            }, [&](StorageBufferBinding& storageBufferBinding) {
+                LengthInformation lengthInformation { m_typeNamer.generateNextStructureElementName(), m_generateNextVariableName(), storageBufferBinding.lengthName };
+                namedBinding.lengthInformation = lengthInformation;
+            }), m_layout[i].bindings[j].binding);
             namedBindGroup.namedBindings.uncheckedAppend(WTFMove(namedBinding));
         }
         m_namedBindGroups.uncheckedAppend(WTFMove(namedBindGroup));
@@ -137,10 +147,16 @@ String EntryPointScaffolding::resourceHelperTypes()
             auto iterator = m_resourceMap.find(&m_layout[i].bindings[j]);
             if (iterator == m_resourceMap.end())
                 continue;
-            auto mangledTypeName = m_typeNamer.mangledNameForType(*m_entryPointItems.inputs[iterator->value].unnamedType);
+            auto& unnamedType = *m_entryPointItems.inputs[iterator->value].unnamedType;
+            ASSERT(is<AST::ReferenceType>(unnamedType));
+            auto& referenceType = downcast<AST::ReferenceType>(unnamedType);
+            auto mangledTypeName = m_typeNamer.mangledNameForType(referenceType.elementType());
+            auto addressSpace = toString(referenceType.addressSpace());
             auto elementName = m_namedBindGroups[i].namedBindings[j].elementName;
             auto index = m_namedBindGroups[i].namedBindings[j].index;
-            stringBuilder.append(makeString("    ", mangledTypeName, ' ', elementName, " [[id(", index, ")]];\n"));
+            stringBuilder.append(makeString("    ", addressSpace, " ", mangledTypeName, "* ", elementName, " [[id(", index, ")]];\n"));
+            if (auto lengthInformation = m_namedBindGroups[i].namedBindings[j].lengthInformation)
+                stringBuilder.append(makeString("    uint2 ", lengthInformation->elementName, " [[id(", lengthInformation->index, ")]];\n"));
         }
         stringBuilder.append("};\n\n");
     }
@@ -257,9 +273,28 @@ String EntryPointScaffolding::unpackResourcesAndNamedBuiltIns()
             auto iterator = m_resourceMap.find(&m_layout[i].bindings[j]);
             if (iterator == m_resourceMap.end())
                 continue;
-            auto& path = m_entryPointItems.inputs[iterator->value].path;
-            auto elementName = m_namedBindGroups[i].namedBindings[j].elementName;
-            stringBuilder.append(makeString(mangledInputPath(path), " = ", variableName, '.', elementName, ";\n"));
+            if (m_namedBindGroups[i].namedBindings[j].lengthInformation) {
+                auto& path = m_entryPointItems.inputs[iterator->value].path;
+                auto elementName = m_namedBindGroups[i].namedBindings[j].elementName;
+                auto lengthElementName = m_namedBindGroups[i].namedBindings[j].lengthInformation->elementName;
+                auto lengthTemporaryName = m_namedBindGroups[i].namedBindings[j].lengthInformation->temporaryName;
+
+                auto& unnamedType = *m_entryPointItems.inputs[iterator->value].unnamedType;
+                ASSERT(is<AST::ReferenceType>(unnamedType));
+                auto& referenceType = downcast<AST::ReferenceType>(unnamedType);
+                auto mangledTypeName = m_typeNamer.mangledNameForType(referenceType.elementType());
+
+                stringBuilder.append(makeString("size_t ", lengthTemporaryName, " = ", variableName, '.', lengthElementName, ".x;\n"));
+                stringBuilder.append(makeString(lengthTemporaryName, " = ", lengthTemporaryName, " << 32;\n"));
+                stringBuilder.append(makeString(lengthTemporaryName, " = ", lengthTemporaryName, " | ", variableName, '.', lengthElementName, ".y;\n"));
+                stringBuilder.append(makeString(lengthTemporaryName, " = ", lengthTemporaryName, " / sizeof(", mangledTypeName, ");\n"));
+                stringBuilder.append(makeString("if (", lengthTemporaryName, " > 0xFFFFFFFF) ", lengthTemporaryName, " = 0xFFFFFFFF;\n"));
+                stringBuilder.append(makeString(mangledInputPath(path), " = { ", variableName, '.', elementName, ", static_cast<uint32_t>(", lengthTemporaryName, ") };\n"));
+            } else {
+                auto& path = m_entryPointItems.inputs[iterator->value].path;
+                auto elementName = m_namedBindGroups[i].namedBindings[j].elementName;
+                stringBuilder.append(makeString(mangledInputPath(path), " = ", variableName, '.', elementName, ";\n"));
+            }
         }
     }
 
index 2467e86..664f7fb 100644 (file)
@@ -77,9 +77,15 @@ protected:
     Layout& m_layout;
     std::function<String()> m_generateNextVariableName;
 
+    struct LengthInformation {
+        String elementName;
+        String temporaryName;
+        unsigned index;
+    };
     struct NamedBinding {
         String elementName;
         unsigned index;
+        Optional<LengthInformation> lengthInformation;
     };
     struct NamedBindGroup {
         String structName;
index fd282a1..1561a4f 100644 (file)
@@ -117,29 +117,29 @@ public:
     }
 };
 
-static AST::NativeFunctionDeclaration resolveWithOperatorAnderIndexer(AST::CallExpression& callExpression, AST::ArrayReferenceType& firstArgument, const Intrinsics& intrinsics)
+static AST::NativeFunctionDeclaration resolveWithOperatorAnderIndexer(Lexer::Token origin, AST::ArrayReferenceType& firstArgument, const Intrinsics& intrinsics)
 {
     const bool isOperator = true;
-    auto returnType = makeUniqueRef<AST::PointerType>(Lexer::Token(callExpression.origin()), firstArgument.addressSpace(), firstArgument.elementType().clone());
+    auto returnType = makeUniqueRef<AST::PointerType>(Lexer::Token(origin), firstArgument.addressSpace(), firstArgument.elementType().clone());
     AST::VariableDeclarations parameters;
-    parameters.append(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(callExpression.origin()), AST::Qualifiers(), firstArgument.clone(), String(), WTF::nullopt, WTF::nullopt));
-    parameters.append(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(callExpression.origin()), AST::Qualifiers(), UniqueRef<AST::UnnamedType>(AST::TypeReference::wrap(Lexer::Token(callExpression.origin()), intrinsics.uintType())), String(), WTF::nullopt, WTF::nullopt));
-    return AST::NativeFunctionDeclaration(AST::FunctionDeclaration(Lexer::Token(callExpression.origin()), AST::AttributeBlock(), WTF::nullopt, WTFMove(returnType), String("operator&[]", String::ConstructFromLiteral), WTFMove(parameters), WTF::nullopt, isOperator));
+    parameters.append(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(origin), AST::Qualifiers(), firstArgument.clone(), String(), WTF::nullopt, WTF::nullopt));
+    parameters.append(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(origin), AST::Qualifiers(), UniqueRef<AST::UnnamedType>(AST::TypeReference::wrap(Lexer::Token(origin), intrinsics.uintType())), String(), WTF::nullopt, WTF::nullopt));
+    return AST::NativeFunctionDeclaration(AST::FunctionDeclaration(Lexer::Token(origin), AST::AttributeBlock(), WTF::nullopt, WTFMove(returnType), String("operator&[]", String::ConstructFromLiteral), WTFMove(parameters), WTF::nullopt, isOperator));
 }
 
-static AST::NativeFunctionDeclaration resolveWithOperatorLength(AST::CallExpression& callExpression, AST::UnnamedType& firstArgument, const Intrinsics& intrinsics)
+static AST::NativeFunctionDeclaration resolveWithOperatorLength(Lexer::Token origin, AST::UnnamedType& firstArgument, const Intrinsics& intrinsics)
 {
     const bool isOperator = true;
-    auto returnType = AST::TypeReference::wrap(Lexer::Token(callExpression.origin()), intrinsics.uintType());
+    auto returnType = AST::TypeReference::wrap(Lexer::Token(origin), intrinsics.uintType());
     AST::VariableDeclarations parameters;
-    parameters.append(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(callExpression.origin()), AST::Qualifiers(), firstArgument.clone(), String(), WTF::nullopt, WTF::nullopt));
-    return AST::NativeFunctionDeclaration(AST::FunctionDeclaration(Lexer::Token(callExpression.origin()), AST::AttributeBlock(), WTF::nullopt, WTFMove(returnType), String("operator.length", String::ConstructFromLiteral), WTFMove(parameters), WTF::nullopt, isOperator));
+    parameters.append(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(origin), AST::Qualifiers(), firstArgument.clone(), String(), WTF::nullopt, WTF::nullopt));
+    return AST::NativeFunctionDeclaration(AST::FunctionDeclaration(Lexer::Token(origin), AST::AttributeBlock(), WTF::nullopt, WTFMove(returnType), String("operator.length", String::ConstructFromLiteral), WTFMove(parameters), WTF::nullopt, isOperator));
 }
 
-static AST::NativeFunctionDeclaration resolveWithReferenceComparator(AST::CallExpression& callExpression, ResolvingType& firstArgument, ResolvingType& secondArgument, const Intrinsics& intrinsics)
+static AST::NativeFunctionDeclaration resolveWithReferenceComparator(Lexer::Token origin, ResolvingType& firstArgument, ResolvingType& secondArgument, const Intrinsics& intrinsics)
 {
     const bool isOperator = true;
-    auto returnType = AST::TypeReference::wrap(Lexer::Token(callExpression.origin()), intrinsics.boolType());
+    auto returnType = AST::TypeReference::wrap(Lexer::Token(origin), intrinsics.boolType());
     auto argumentType = firstArgument.visit(WTF::makeVisitor([](UniqueRef<AST::UnnamedType>& unnamedType) -> UniqueRef<AST::UnnamedType> {
         return unnamedType->clone();
     }, [&](RefPtr<ResolvableTypeReference>&) -> UniqueRef<AST::UnnamedType> {
@@ -149,13 +149,13 @@ static AST::NativeFunctionDeclaration resolveWithReferenceComparator(AST::CallEx
             // We encountered "null == null".
             // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198162 This can probably be generalized, using the "preferred type" infrastructure used by generic literals
             ASSERT_NOT_REACHED();
-            return AST::TypeReference::wrap(Lexer::Token(callExpression.origin()), intrinsics.intType());
+            return AST::TypeReference::wrap(Lexer::Token(origin), intrinsics.intType());
         }));
     }));
     AST::VariableDeclarations parameters;
-    parameters.append(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(callExpression.origin()), AST::Qualifiers(), argumentType->clone(), String(), WTF::nullopt, WTF::nullopt));
-    parameters.append(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(callExpression.origin()), AST::Qualifiers(), UniqueRef<AST::UnnamedType>(WTFMove(argumentType)), String(), WTF::nullopt, WTF::nullopt));
-    return AST::NativeFunctionDeclaration(AST::FunctionDeclaration(Lexer::Token(callExpression.origin()), AST::AttributeBlock(), WTF::nullopt, WTFMove(returnType), String("operator==", String::ConstructFromLiteral), WTFMove(parameters), WTF::nullopt, isOperator));
+    parameters.append(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(origin), AST::Qualifiers(), argumentType->clone(), String(), WTF::nullopt, WTF::nullopt));
+    parameters.append(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(origin), AST::Qualifiers(), UniqueRef<AST::UnnamedType>(WTFMove(argumentType)), String(), WTF::nullopt, WTF::nullopt));
+    return AST::NativeFunctionDeclaration(AST::FunctionDeclaration(Lexer::Token(origin), AST::AttributeBlock(), WTF::nullopt, WTFMove(returnType), String("operator==", String::ConstructFromLiteral), WTFMove(parameters), WTF::nullopt, isOperator));
 }
 
 enum class Acceptability {
@@ -164,9 +164,9 @@ enum class Acceptability {
     No
 };
 
-static Optional<AST::NativeFunctionDeclaration> resolveByInstantiation(AST::CallExpression& callExpression, const Vector<std::reference_wrapper<ResolvingType>>& types, const Intrinsics& intrinsics)
+static Optional<AST::NativeFunctionDeclaration> resolveByInstantiation(const String& name, Lexer::Token origin, const Vector<std::reference_wrapper<ResolvingType>>& types, const Intrinsics& intrinsics)
 {
-    if (callExpression.name() == "operator&[]" && types.size() == 2) {
+    if (name == "operator&[]" && types.size() == 2) {
         auto* firstArgumentArrayRef = types[0].get().visit(WTF::makeVisitor([](UniqueRef<AST::UnnamedType>& unnamedType) -> AST::ArrayReferenceType* {
             if (is<AST::ArrayReferenceType>(static_cast<AST::UnnamedType&>(unnamedType)))
                 return &downcast<AST::ArrayReferenceType>(static_cast<AST::UnnamedType&>(unnamedType));
@@ -180,8 +180,8 @@ static Optional<AST::NativeFunctionDeclaration> resolveByInstantiation(AST::Call
             return resolvableTypeReference->resolvableType().canResolve(intrinsics.uintType());
         }));
         if (firstArgumentArrayRef && secondArgumentIsUint)
-            return resolveWithOperatorAnderIndexer(callExpression, *firstArgumentArrayRef, intrinsics);
-    } else if (callExpression.name() == "operator.length" && types.size() == 1) {
+            return resolveWithOperatorAnderIndexer(origin, *firstArgumentArrayRef, intrinsics);
+    } else if (name == "operator.length" && types.size() == 1) {
         auto* firstArgumentReference = types[0].get().visit(WTF::makeVisitor([](UniqueRef<AST::UnnamedType>& unnamedType) -> AST::UnnamedType* {
             if (is<AST::ArrayReferenceType>(static_cast<AST::UnnamedType&>(unnamedType)))
                 return &unnamedType;
@@ -190,8 +190,8 @@ static Optional<AST::NativeFunctionDeclaration> resolveByInstantiation(AST::Call
             return nullptr;
         }));
         if (firstArgumentReference)
-            return resolveWithOperatorLength(callExpression, *firstArgumentReference, intrinsics);
-    } else if (callExpression.name() == "operator==" && types.size() == 2) {
+            return resolveWithOperatorLength(origin, *firstArgumentReference, intrinsics);
+    } else if (name == "operator==" && types.size() == 2) {
         auto acceptability = [](ResolvingType& resolvingType) -> Acceptability {
             return resolvingType.visit(WTF::makeVisitor([](UniqueRef<AST::UnnamedType>& unnamedType) -> Acceptability {
                 return is<AST::ReferenceType>(static_cast<AST::UnnamedType&>(unnamedType)) ? Acceptability::Yes : Acceptability::No;
@@ -210,7 +210,7 @@ static Optional<AST::NativeFunctionDeclaration> resolveByInstantiation(AST::Call
             || (leftAcceptability == Acceptability::Yes && rightAcceptability == Acceptability::Maybe))
             success = true;
         if (success)
-            return resolveWithReferenceComparator(callExpression, types[0].get(), types[1].get(), intrinsics);
+            return resolveWithReferenceComparator(origin, types[0].get(), types[1].get(), intrinsics);
     }
     return WTF::nullopt;
 }
@@ -969,6 +969,29 @@ void Checker::visit(AST::MakeArrayReferenceExpression& makeArrayReferenceExpress
     assignType(makeArrayReferenceExpression, makeUniqueRef<AST::ArrayReferenceType>(Lexer::Token(makeArrayReferenceExpression.origin()), *leftAddressSpace, leftValueType->clone()));
 }
 
+static Optional<UniqueRef<AST::UnnamedType>> argumentTypeForAndOverload(AST::UnnamedType& baseType, AST::AddressSpace addressSpace)
+{
+    auto& unifyNode = baseType.unifyNode();
+    if (is<AST::NamedType>(unifyNode)) {
+        auto& namedType = downcast<AST::NamedType>(unifyNode);
+        return { makeUniqueRef<AST::PointerType>(Lexer::Token(namedType.origin()), addressSpace, AST::TypeReference::wrap(Lexer::Token(namedType.origin()), namedType)) };
+    }
+
+    ASSERT(is<AST::UnnamedType>(unifyNode));
+    auto& unnamedType = downcast<AST::UnnamedType>(unifyNode);
+
+    if (is<AST::ArrayReferenceType>(unnamedType))
+        return unnamedType.clone();
+
+    if (is<AST::ArrayType>(unnamedType))
+        return { makeUniqueRef<AST::ArrayReferenceType>(Lexer::Token(unnamedType.origin()), addressSpace, downcast<AST::ArrayType>(unnamedType).type().clone()) };
+
+    if (is<AST::PointerType>(unnamedType))
+        return WTF::nullopt;
+
+    return { makeUniqueRef<AST::PointerType>(Lexer::Token(unnamedType.origin()), addressSpace, unnamedType.clone()) };
+}
+
 void Checker::finishVisiting(AST::PropertyAccessExpression& propertyAccessExpression, ResolvingType* additionalArgumentType)
 {
     auto baseInfo = recurseAndGetInfo(propertyAccessExpression.base());
@@ -992,23 +1015,35 @@ void Checker::finishVisiting(AST::PropertyAccessExpression& propertyAccessExpres
     AST::UnnamedType* anderReturnType = nullptr;
     auto leftAddressSpace = baseInfo->typeAnnotation.leftAddressSpace();
     if (leftAddressSpace) {
-        ResolvingType argumentType = { makeUniqueRef<AST::PointerType>(Lexer::Token(propertyAccessExpression.origin()), *leftAddressSpace, baseUnnamedType->get().clone()) };
-        Vector<std::reference_wrapper<ResolvingType>> anderArgumentTypes { argumentType };
-        if (additionalArgumentType)
-            anderArgumentTypes.append(*additionalArgumentType);
-        if ((anderFunction = resolveFunctionOverload(propertyAccessExpression.possibleAnderOverloads(), anderArgumentTypes)))
-            anderReturnType = &downcast<AST::PointerType>(anderFunction->type()).elementType(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198164 Enforce the return of anders will always be a pointer
+        if (auto argumentTypeForAndOverload = WHLSL::argumentTypeForAndOverload(*baseUnnamedType, *leftAddressSpace)) {
+            ResolvingType argumentType = { WTFMove(*argumentTypeForAndOverload) };
+            Vector<std::reference_wrapper<ResolvingType>> anderArgumentTypes { argumentType };
+            if (additionalArgumentType)
+                anderArgumentTypes.append(*additionalArgumentType);
+            if ((anderFunction = resolveFunctionOverload(propertyAccessExpression.possibleAnderOverloads(), anderArgumentTypes)))
+                anderReturnType = &downcast<AST::PointerType>(anderFunction->type()).elementType(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198164 Enforce the return of anders will always be a pointer
+            else if (auto newFunction = resolveByInstantiation(propertyAccessExpression.anderFunctionName(), propertyAccessExpression.origin(), anderArgumentTypes, m_intrinsics)) {
+                m_program.append(WTFMove(*newFunction));
+                anderFunction = &m_program.nativeFunctionDeclarations().last();
+                anderReturnType = &downcast<AST::PointerType>(anderFunction->type()).elementType(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198164 Enforce the return of anders will always be a pointer
+            }
+        }
     }
 
     AST::FunctionDeclaration* threadAnderFunction = nullptr;
     AST::UnnamedType* threadAnderReturnType = nullptr;
-    {
+    if (auto argumentTypeForAndOverload = WHLSL::argumentTypeForAndOverload(*baseUnnamedType, AST::AddressSpace::Thread)) {
         ResolvingType argumentType = { makeUniqueRef<AST::PointerType>(Lexer::Token(propertyAccessExpression.origin()), AST::AddressSpace::Thread, baseUnnamedType->get().clone()) };
         Vector<std::reference_wrapper<ResolvingType>> threadAnderArgumentTypes { argumentType };
         if (additionalArgumentType)
             threadAnderArgumentTypes.append(*additionalArgumentType);
         if ((threadAnderFunction = resolveFunctionOverload(propertyAccessExpression.possibleAnderOverloads(), threadAnderArgumentTypes)))
             threadAnderReturnType = &downcast<AST::PointerType>(threadAnderFunction->type()).elementType(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198164 Enforce the return of anders will always be a pointer
+        else if (auto newFunction = resolveByInstantiation(propertyAccessExpression.anderFunctionName(), propertyAccessExpression.origin(), threadAnderArgumentTypes, m_intrinsics)) {
+            m_program.append(WTFMove(*newFunction));
+            threadAnderFunction = &m_program.nativeFunctionDeclarations().last();
+            threadAnderReturnType = &downcast<AST::PointerType>(anderFunction->type()).elementType(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198164 Enforce the return of anders will always be a pointer
+        }
     }
 
     if (leftAddressSpace && !anderFunction && !getterFunction) {
@@ -1434,7 +1469,7 @@ void Checker::visit(AST::CallExpression& callExpression)
     ASSERT(callExpression.hasOverloads());
     auto* function = resolveFunctionOverload(*callExpression.overloads(), types, callExpression.castReturnType());
     if (!function) {
-        if (auto newFunction = resolveByInstantiation(callExpression, types, m_intrinsics)) {
+        if (auto newFunction = resolveByInstantiation(callExpression.name(), callExpression.origin(), types, m_intrinsics)) {
             m_program.append(WTFMove(*newFunction));
             function = &m_program.nativeFunctionDeclarations().last();
         }
index f8f1745..5b37e3f 100644 (file)
@@ -114,18 +114,28 @@ enum class ShaderStage : uint8_t {
     Compute = 1 << 2
 };
 
-enum class BindingType : uint8_t {
-    UniformBuffer,
-    Sampler,
-    Texture,
-    StorageBuffer,
-    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198168 Add the dynamic types
+struct UniformBufferBinding {
+    unsigned lengthName;
 };
 
+struct SamplerBinding {
+};
+
+struct TextureBinding {
+};
+
+struct StorageBufferBinding {
+    unsigned lengthName;
+};
+
+// FIXME: https://bugs.webkit.org/show_bug.cgi?id=198168 Add the dynamic types
+
 struct Binding {
+    using BindingDetails = Variant<UniformBufferBinding, SamplerBinding, TextureBinding, StorageBufferBinding>;
     OptionSet<ShaderStage> visibility;
-    BindingType bindingType;
-    unsigned name;
+    BindingDetails binding;
+    unsigned internalName;
+    unsigned externalName;
 };
 
 struct BindGroup {
index 8a5a8b3..a93e037 100644 (file)
@@ -35,6 +35,7 @@
 #include "WHLSLDotExpression.h"
 #include "WHLSLFunctionDeclaration.h"
 #include "WHLSLFunctionDefinition.h"
+#include "WHLSLMakeArrayReferenceExpression.h"
 #include "WHLSLMakePointerExpression.h"
 #include "WHLSLPointerType.h"
 #include "WHLSLReadModifyWriteExpression.h"
@@ -55,7 +56,7 @@ private:
     void visit(AST::AssignmentExpression&) override;
     void visit(AST::ReadModifyWriteExpression&) override;
 
-    bool simplifyRightValue(AST::PropertyAccessExpression&);
+    void simplifyRightValue(AST::PropertyAccessExpression&);
     bool simplifyAbstractLeftValue(AST::AssignmentExpression&, AST::DotExpression&, UniqueRef<AST::Expression>&& right);
     void simplifyLeftValue(AST::Expression&);
 
@@ -65,16 +66,14 @@ private:
 void PropertyResolver::visit(AST::DotExpression& dotExpression)
 {
     // Unless we're inside an AssignmentExpression or a ReadModifyWriteExpression, we're a right value.
-    if (!simplifyRightValue(dotExpression))
-        setError();
+    simplifyRightValue(dotExpression);
 }
 
 void PropertyResolver::visit(AST::IndexExpression& indexExpression)
 {
     checkErrorAndVisit(indexExpression.indexExpression());
     // Unless we're inside an AssignmentExpression or a ReadModifyWriteExpression, we're a right value.
-    if (!simplifyRightValue(indexExpression))
-        setError();
+    simplifyRightValue(indexExpression);
 }
 
 void PropertyResolver::visit(AST::FunctionDefinition& functionDefinition)
@@ -84,7 +83,78 @@ void PropertyResolver::visit(AST::FunctionDefinition& functionDefinition)
         functionDefinition.block().statements().insert(0, makeUniqueRef<AST::VariableDeclarationsStatement>(Lexer::Token(m_variableDeclarations[0]->origin()), WTFMove(m_variableDeclarations)));
 }
 
-static Optional<UniqueRef<AST::Expression>> setterCall(AST::PropertyAccessExpression& propertyAccessExpression, AST::FunctionDeclaration* relevantAnder, UniqueRef<AST::Expression>&& newValue, const std::function<UniqueRef<AST::Expression>()>& leftValueFactory, const std::function<UniqueRef<AST::Expression>()>& pointerToLeftValueFactory, AST::VariableDeclaration* indexVariable)
+enum class WhichAnder {
+    ThreadAnder,
+    Ander
+};
+
+struct AnderCallArgumentResult {
+    UniqueRef<AST::Expression> expression;
+    Optional<UniqueRef<AST::VariableDeclaration>> variableDeclaration;
+    WhichAnder whichAnder;
+};
+
+template <typename ExpressionConstructor, typename TypeConstructor>
+static Optional<AnderCallArgumentResult> wrapAnderCallArgument(UniqueRef<AST::Expression>& expression, bool anderFunction, bool threadAnderFunction)
+{
+    if (auto addressSpace = expression->typeAnnotation().leftAddressSpace()) {
+        if (!anderFunction)
+            return WTF::nullopt;
+        auto origin = expression->origin();
+        auto baseType = expression->resolvedType().clone();
+        auto makeArrayReference = makeUniqueRef<ExpressionConstructor>(Lexer::Token(origin), WTFMove(expression));
+        makeArrayReference->setType(makeUniqueRef<TypeConstructor>(WTFMove(origin), *addressSpace, WTFMove(baseType)));
+        makeArrayReference->setTypeAnnotation(AST::RightValue());
+        return {{ WTFMove(makeArrayReference), WTF::nullopt, WhichAnder::Ander }};
+    }
+    if (threadAnderFunction) {
+        auto origin = expression->origin();
+        auto baseType = expression->resolvedType().clone();
+        auto variableDeclaration = makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(origin), AST::Qualifiers(), baseType->clone(), String(), WTF::nullopt, WTF::nullopt);
+
+        auto variableReference1 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
+        variableReference1->setType(baseType->clone());
+        variableReference1->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+
+        auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(origin), WTFMove(variableReference1), WTFMove(expression));
+        assignmentExpression->setType(baseType->clone());
+        assignmentExpression->setTypeAnnotation(AST::RightValue());
+
+        auto variableReference2 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
+        variableReference2->setType(baseType->clone());
+        variableReference2->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+
+        auto expression = makeUniqueRef<ExpressionConstructor>(Lexer::Token(origin), WTFMove(variableReference2));
+        auto resultType = makeUniqueRef<TypeConstructor>(Lexer::Token(origin), AST::AddressSpace::Thread, WTFMove(baseType));
+        expression->setType(resultType->clone());
+        expression->setTypeAnnotation(AST::RightValue());
+
+        Vector<UniqueRef<AST::Expression>> expressions;
+        expressions.append(WTFMove(assignmentExpression));
+        expressions.append(WTFMove(expression));
+        auto commaExpression = makeUniqueRef<AST::CommaExpression>(WTFMove(origin), WTFMove(expressions));
+        commaExpression->setType(WTFMove(resultType));
+        commaExpression->setTypeAnnotation(AST::RightValue());
+        return {{ WTFMove(commaExpression), { WTFMove(variableDeclaration) }, WhichAnder::ThreadAnder}};
+    }
+    return WTF::nullopt;
+}
+
+static Optional<AnderCallArgumentResult> anderCallArgument(UniqueRef<AST::Expression>& expression, bool anderFunction, bool threadAnderFunction)
+{
+    auto& unifyNode = expression->resolvedType().unifyNode();
+    if (is<AST::UnnamedType>(unifyNode)) {
+        auto& unnamedType = downcast<AST::UnnamedType>(unifyNode);
+        ASSERT(!is<AST::PointerType>(unnamedType));
+        if (is<AST::ArrayReferenceType>(unnamedType))
+            return {{ WTFMove(expression), WTF::nullopt, WhichAnder::Ander }};
+        if (is<AST::ArrayType>(unnamedType))
+            return wrapAnderCallArgument<AST::MakeArrayReferenceExpression, AST::ArrayReferenceType>(expression, anderFunction, threadAnderFunction);
+    }
+    return wrapAnderCallArgument<AST::MakePointerExpression, AST::PointerType>(expression, anderFunction, threadAnderFunction);
+}
+
+static Optional<UniqueRef<AST::Expression>> setterCall(AST::PropertyAccessExpression& propertyAccessExpression, AST::FunctionDeclaration* relevantAnder, UniqueRef<AST::Expression>&& newValue, const std::function<UniqueRef<AST::Expression>()>& leftValueFactory, AST::VariableDeclaration* indexVariable)
 {
     auto maybeAddIndexArgument = [&](Vector<UniqueRef<AST::Expression>>& arguments) {
         if (!indexVariable)
@@ -98,8 +168,13 @@ static Optional<UniqueRef<AST::Expression>> setterCall(AST::PropertyAccessExpres
 
     if (relevantAnder) {
         // *operator&.foo(&v) = newValue
+        auto leftValue = leftValueFactory();
+        auto argument = anderCallArgument(leftValue, true, true);
+        ASSERT(argument);
+        ASSERT(!argument->variableDeclaration);
+        ASSERT(argument->whichAnder == WhichAnder::Ander);
         Vector<UniqueRef<AST::Expression>> arguments;
-        arguments.append(pointerToLeftValueFactory());
+        arguments.append(WTFMove(argument->expression));
         maybeAddIndexArgument(arguments);
 
         auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(propertyAccessExpression.origin()), String(relevantAnder->name()), WTFMove(arguments));
@@ -138,7 +213,7 @@ static Optional<UniqueRef<AST::Expression>> setterCall(AST::PropertyAccessExpres
     return UniqueRef<AST::Expression>(WTFMove(assignmentExpression));
 }
 
-static Optional<UniqueRef<AST::Expression>> getterCall(AST::PropertyAccessExpression& propertyAccessExpression, AST::FunctionDeclaration* relevantAnder, const std::function<UniqueRef<AST::Expression>()>& leftValueFactory, const std::function<UniqueRef<AST::Expression>()>& pointerToLeftValueFactory, AST::VariableDeclaration* indexVariable)
+static Optional<UniqueRef<AST::Expression>> getterCall(AST::PropertyAccessExpression& propertyAccessExpression, AST::FunctionDeclaration* relevantAnder, const std::function<UniqueRef<AST::Expression>()>& leftValueFactory, AST::VariableDeclaration* indexVariable)
 {
     auto maybeAddIndexArgument = [&](Vector<UniqueRef<AST::Expression>>& arguments) {
         if (!indexVariable)
@@ -152,8 +227,13 @@ static Optional<UniqueRef<AST::Expression>> getterCall(AST::PropertyAccessExpres
 
     if (relevantAnder) {
         // *operator&.foo(&v)
+        auto leftValue = leftValueFactory();
+        auto argument = anderCallArgument(leftValue, true, true);
+        ASSERT(argument);
+        ASSERT(!argument->variableDeclaration);
+        ASSERT(argument->whichAnder == WhichAnder::Ander);
         Vector<UniqueRef<AST::Expression>> arguments;
-        arguments.append(pointerToLeftValueFactory());
+        arguments.append(WTFMove(argument->expression));
         maybeAddIndexArgument(arguments);
 
         auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(propertyAccessExpression.origin()), String(relevantAnder->name()), WTFMove(arguments));
@@ -170,7 +250,7 @@ static Optional<UniqueRef<AST::Expression>> getterCall(AST::PropertyAccessExpres
 
     // operator.foo(v)
     ASSERT(propertyAccessExpression.getterFunction());
-    
+
     Vector<UniqueRef<AST::Expression>> arguments;
     arguments.append(leftValueFactory());
     maybeAddIndexArgument(arguments);
@@ -304,7 +384,7 @@ static Optional<ModifyResult> modify(AST::PropertyAccessExpression& propertyAcce
             variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
             return variableReference;
         }
-    
+
         auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable));
         ASSERT(pointerVariable->type());
         variableReference->setType(pointerVariable->type()->clone());
@@ -316,26 +396,6 @@ static Optional<ModifyResult> modify(AST::PropertyAccessExpression& propertyAcce
         dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(*pointerVariable->type()).addressSpace() });
         return dereferenceExpression;
     };
-    auto pointerToPreviousLeftValue = [&]() -> UniqueRef<AST::Expression> {
-        if (previous) {
-            auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*previous));
-            ASSERT(previous->type());
-            variableReference->setType(previous->type()->clone());
-            variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
-
-            auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference));
-            ASSERT(previous->type());
-            makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(propertyAccessExpression.origin()), AST::AddressSpace::Thread, previous->type()->clone()));
-            makePointerExpression->setTypeAnnotation(AST::RightValue());
-            return makePointerExpression;
-        }
-
-        auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable));
-        ASSERT(pointerVariable->type());
-        variableReference->setType(pointerVariable->type()->clone());
-        variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
-        return variableReference;
-    };
     auto appendIndexAssignment = [&](AST::PropertyAccessExpression& propertyAccessExpression, Optional<UniqueRef<AST::VariableDeclaration>>& indexVariable) {
         if (!indexVariable)
             return;
@@ -361,7 +421,7 @@ static Optional<ModifyResult> modify(AST::PropertyAccessExpression& propertyAcce
         appendIndexAssignment(propertyAccessExpression, indexVariable);
 
         AST::FunctionDeclaration* relevantAnder = i == chain.size() - 1 ? propertyAccessExpression.anderFunction() : propertyAccessExpression.threadAnderFunction();
-        auto callExpression = getterCall(propertyAccessExpression, relevantAnder, previousLeftValue, pointerToPreviousLeftValue, indexVariable ? &*indexVariable : nullptr);
+        auto callExpression = getterCall(propertyAccessExpression, relevantAnder, previousLeftValue, indexVariable ? &*indexVariable : nullptr);
 
         if (!callExpression)
             return WTF::nullopt;
@@ -376,12 +436,12 @@ static Optional<ModifyResult> modify(AST::PropertyAccessExpression& propertyAcce
         assignmentExpression->setTypeAnnotation(AST::RightValue());
 
         expressions.append(WTFMove(assignmentExpression));
-        
+
         previous = &variableDeclaration;
     }
     appendIndexAssignment(chain[0], indexVariables[0]);
     AST::FunctionDeclaration* relevantAnder = chain.size() == 1 ? propertyAccessExpression.anderFunction() : propertyAccessExpression.threadAnderFunction();
-    auto lastGetterCallExpression = getterCall(chain[0], relevantAnder, previousLeftValue, pointerToPreviousLeftValue, indexVariables[0] ? &*(indexVariables[0]) : nullptr);
+    auto lastGetterCallExpression = getterCall(chain[0], relevantAnder, previousLeftValue, indexVariables[0] ? &*(indexVariables[0]) : nullptr);
 
     // Step 3:
     auto modificationResult = modification(WTFMove(lastGetterCallExpression));
@@ -404,17 +464,6 @@ static Optional<ModifyResult> modify(AST::PropertyAccessExpression& propertyAcce
             variableReference->setType(variableDeclaration.type()->clone());
             variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
             return variableReference;
-        }, [&]() -> UniqueRef<AST::Expression> {
-            auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
-            ASSERT(variableDeclaration.type());
-            variableReference->setType(variableDeclaration.type()->clone());
-            variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
-
-            auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference));
-            ASSERT(variableDeclaration.type());
-            makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(propertyAccessExpression.origin()), AST::AddressSpace::Thread, variableDeclaration.type()->clone()));
-            makePointerExpression->setTypeAnnotation(AST::RightValue());
-            return makePointerExpression;
         }, indexVariable ? &*indexVariable : nullptr);
 
         if (!assignmentExpression)
@@ -442,12 +491,6 @@ static Optional<ModifyResult> modify(AST::PropertyAccessExpression& propertyAcce
             dereferenceExpression->setType(downcast<AST::PointerType>(*pointerVariable->type()).elementType().clone());
             dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(*pointerVariable->type()).addressSpace() });
             return dereferenceExpression;
-        }, [&]() -> UniqueRef<AST::Expression> {
-            auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable));
-            ASSERT(pointerVariable->type());
-            variableReference->setType(pointerVariable->type()->clone());
-            variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
-            return variableReference;
         }, indexVariables[indexVariables.size() - 1] ? &*(indexVariables[indexVariables.size() - 1]) : nullptr);
 
         if (!assignmentExpression)
@@ -671,94 +714,50 @@ void PropertyResolver::visit(AST::ReadModifyWriteExpression& readModifyWriteExpr
     m_variableDeclarations.append(WTFMove(newVariableDeclaration));
 }
 
-bool PropertyResolver::simplifyRightValue(AST::PropertyAccessExpression& propertyAccessExpression)
+static Optional<AnderCallArgumentResult> anderCallArgument(AST::PropertyAccessExpression& propertyAccessExpression)
+{
+    return anderCallArgument(propertyAccessExpression.baseReference(), propertyAccessExpression.anderFunction(), propertyAccessExpression.threadAnderFunction());
+}
+
+void PropertyResolver::simplifyRightValue(AST::PropertyAccessExpression& propertyAccessExpression)
 {
     Lexer::Token origin = propertyAccessExpression.origin();
 
     checkErrorAndVisit(propertyAccessExpression.base());
 
-    auto& base = propertyAccessExpression.base();
-    if (auto leftAddressSpace = base.typeAnnotation().leftAddressSpace()) {
-        if (auto* anderFunction = propertyAccessExpression.anderFunction()) {
-            auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(origin), propertyAccessExpression.takeBase());
-            makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(origin), *leftAddressSpace, base.resolvedType().clone()));
-            makePointerExpression->setTypeAnnotation(AST::RightValue());
-
-            Vector<UniqueRef<AST::Expression>> arguments;
-            arguments.append(WTFMove(makePointerExpression));
-            if (is<AST::IndexExpression>(propertyAccessExpression))
-                arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
-            auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(origin), String(anderFunction->name()), WTFMove(arguments));
-            callExpression->setType(anderFunction->type().clone());
-            callExpression->setTypeAnnotation(AST::RightValue());
-            callExpression->setFunction(*anderFunction);
-
-            auto* dereferenceExpression = AST::replaceWith<AST::DereferenceExpression>(propertyAccessExpression, WTFMove(origin), WTFMove(callExpression));
-            dereferenceExpression->setType(downcast<AST::PointerType>(anderFunction->type()).elementType().clone());
-            dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(anderFunction->type()).addressSpace() });
-            return true;
-        }
-    }
-
-    if (propertyAccessExpression.getterFunction()) {
-        auto& getterFunction = *propertyAccessExpression.getterFunction();
+    if (auto argument = anderCallArgument(propertyAccessExpression)) {
+        auto* anderFunction = argument->whichAnder == WhichAnder::ThreadAnder ? propertyAccessExpression.threadAnderFunction() : propertyAccessExpression.anderFunction();
+        ASSERT(anderFunction);
+        auto origin = propertyAccessExpression.origin();
         Vector<UniqueRef<AST::Expression>> arguments;
-        arguments.append(propertyAccessExpression.takeBase());
+        arguments.append(WTFMove(argument->expression));
         if (is<AST::IndexExpression>(propertyAccessExpression))
             arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
-        auto* callExpression = AST::replaceWith<AST::CallExpression>(propertyAccessExpression, WTFMove(origin), String(getterFunction.name()), WTFMove(arguments));
-        callExpression->setFunction(getterFunction);
-        callExpression->setType(getterFunction.type().clone());
+        auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(origin), String(anderFunction->name()), WTFMove(arguments));
+        callExpression->setType(anderFunction->type().clone());
         callExpression->setTypeAnnotation(AST::RightValue());
-        return true;
-    }
-
-    // We have an ander, but no left value to call it on. Let's save the value into a temporary variable to create a left value.
-    // This is effectively inlining the functions the spec says are generated.
-    ASSERT(propertyAccessExpression.threadAnderFunction());
-    auto* threadAnderFunction = propertyAccessExpression.threadAnderFunction();
-
-    auto variableDeclaration = makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(origin), AST::Qualifiers(), base.resolvedType().clone(), String(), WTF::nullopt, WTF::nullopt);
+        callExpression->setFunction(*anderFunction);
 
-    auto variableReference1 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
-    variableReference1->setType(base.resolvedType().clone());
-    variableReference1->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
-
-    auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(origin), WTFMove(variableReference1), propertyAccessExpression.takeBase());
-    assignmentExpression->setType(base.resolvedType().clone());
-    assignmentExpression->setTypeAnnotation(AST::RightValue());
+        auto* dereferenceExpression = AST::replaceWith<AST::DereferenceExpression>(propertyAccessExpression, WTFMove(origin), WTFMove(callExpression));
+        dereferenceExpression->setType(downcast<AST::PointerType>(anderFunction->type()).elementType().clone());
+        dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(anderFunction->type()).addressSpace() });
 
-    auto variableReference2 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
-    variableReference2->setType(base.resolvedType().clone());
-    variableReference2->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+        if (auto& variableDeclaration = argument->variableDeclaration)
+            m_variableDeclarations.append(WTFMove(*variableDeclaration));
 
-    auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(origin), WTFMove(variableReference2));
-    makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(origin), AST::AddressSpace::Thread, base.resolvedType().clone()));
-    makePointerExpression->setTypeAnnotation(AST::RightValue());
+        return;
+    }
 
+    ASSERT(propertyAccessExpression.getterFunction());
+    auto& getterFunction = *propertyAccessExpression.getterFunction();
     Vector<UniqueRef<AST::Expression>> arguments;
-    arguments.append(WTFMove(makePointerExpression));
+    arguments.append(propertyAccessExpression.takeBase());
     if (is<AST::IndexExpression>(propertyAccessExpression))
         arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
-    auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(origin), String(threadAnderFunction->name()), WTFMove(arguments));
-    callExpression->setType(threadAnderFunction->type().clone());
+    auto* callExpression = AST::replaceWith<AST::CallExpression>(propertyAccessExpression, WTFMove(origin), String(getterFunction.name()), WTFMove(arguments));
+    callExpression->setFunction(getterFunction);
+    callExpression->setType(getterFunction.type().clone());
     callExpression->setTypeAnnotation(AST::RightValue());
-    callExpression->setFunction(*threadAnderFunction);
-
-    auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(WTFMove(origin), WTFMove(callExpression));
-    dereferenceExpression->setType(downcast<AST::PointerType>(threadAnderFunction->type()).elementType().clone());
-    dereferenceExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
-
-    Vector<UniqueRef<AST::Expression>> expressions;
-    expressions.append(WTFMove(assignmentExpression));
-    expressions.append(WTFMove(dereferenceExpression));
-    auto* commaExpression = AST::replaceWith<AST::CommaExpression>(propertyAccessExpression, WTFMove(origin), WTFMove(expressions));
-    commaExpression->setType(downcast<AST::PointerType>(threadAnderFunction->type()).elementType().clone());
-    commaExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
-
-    m_variableDeclarations.append(WTFMove(variableDeclaration));
-    return true;
-
 }
 
 class LeftValueSimplifier : public Visitor {
@@ -777,14 +776,14 @@ void LeftValueSimplifier::finishVisiting(AST::PropertyAccessExpression& property
 
     Lexer::Token origin = propertyAccessExpression.origin();
     auto* anderFunction = propertyAccessExpression.anderFunction();
-    auto& base = propertyAccessExpression.base();
-    auto leftAddressSpace = *propertyAccessExpression.base().typeAnnotation().leftAddressSpace();
-    auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(origin), propertyAccessExpression.takeBase());
-    makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(origin), leftAddressSpace, base.resolvedType().clone()));
-    makePointerExpression->setTypeAnnotation(AST::RightValue());
+
+    auto argument = anderCallArgument(propertyAccessExpression);
+    ASSERT(argument);
+    ASSERT(!argument->variableDeclaration);
+    ASSERT(argument->whichAnder == WhichAnder::Ander);
 
     Vector<UniqueRef<AST::Expression>> arguments;
-    arguments.append(WTFMove(makePointerExpression));
+    arguments.append(WTFMove(argument->expression));
     if (is<AST::IndexExpression>(propertyAccessExpression))
         arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
     auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(origin), String(anderFunction->name()), WTFMove(arguments));
index 7a551bf..5002ee5 100644 (file)
@@ -55,19 +55,17 @@ static AST::FunctionDefinition* findEntryPoint(Vector<UniqueRef<AST::FunctionDef
     return &*iterator;
 };
 
-static bool matchMode(BindingType bindingType, AST::ResourceSemantic::Mode mode)
+static bool matchMode(Binding::BindingDetails bindingType, AST::ResourceSemantic::Mode mode)
 {
-    switch (bindingType) {
-    case BindingType::UniformBuffer:
+    return WTF::visit(WTF::makeVisitor([&](UniformBufferBinding) -> bool {
         return mode == AST::ResourceSemantic::Mode::Buffer;
-    case BindingType::Sampler:
+    }, [&](SamplerBinding) -> bool {
         return mode == AST::ResourceSemantic::Mode::Sampler;
-    case BindingType::Texture:
+    }, [&](TextureBinding) -> bool {
         return mode == AST::ResourceSemantic::Mode::Texture;
-    default:
-        ASSERT(bindingType == BindingType::StorageBuffer);
+    }, [&](StorageBufferBinding) -> bool {
         return mode == AST::ResourceSemantic::Mode::UnorderedAccessView;
-    }
+    }), bindingType);
 }
 
 static Optional<HashMap<Binding*, size_t>> matchResources(Vector<EntryPointItem>& entryPointItems, Layout& layout, ShaderStage shaderStage)
@@ -87,9 +85,9 @@ static Optional<HashMap<Binding*, size_t>> matchResources(Vector<EntryPointItem>
                 if (!WTF::holds_alternative<AST::ResourceSemantic>(semantic))
                     continue;
                 auto& resourceSemantic = WTF::get<AST::ResourceSemantic>(semantic);
-                if (!matchMode(binding.bindingType, resourceSemantic.mode()))
+                if (!matchMode(binding.binding, resourceSemantic.mode()))
                     continue;
-                if (binding.name != resourceSemantic.index())
+                if (binding.externalName != resourceSemantic.index())
                     continue;
                 if (space != resourceSemantic.space())
                     continue;
index 9851567..3e510c5 100644 (file)
@@ -111,7 +111,7 @@ Optional<GPUBindGroupDescriptor> WebGPUBindGroupDescriptor::tryCreateGPUBindGrou
             if (!buffer)
                 return WTF::nullopt;
 
-            if (!validateBufferBindingType(buffer, layoutBinding, functionName))
+            if (!validateBufferBindingType(buffer, layoutBinding.externalBinding, functionName))
                 return WTF::nullopt;
 
             return static_cast<GPUBindingResource>(GPUBufferBinding { makeRef(*buffer), bufferBinding.offset, bufferBinding.size });
@@ -119,7 +119,7 @@ Optional<GPUBindGroupDescriptor> WebGPUBindGroupDescriptor::tryCreateGPUBindGrou
 
         auto bindingResource = WTF::visit(bindingResourceVisitor, binding.resource);
         if (!bindingResource) {
-            LOG(WebGPU, "%s: Invalid resource for binding %u!", functionName, layoutBinding.binding);
+            LOG(WebGPU, "%s: Invalid resource for binding %u!", functionName, layoutBinding.externalBinding.binding);
             return WTF::nullopt;
         }
 
index a55b1c9..f7762c3 100644 (file)
@@ -33,6 +33,7 @@
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
 #include <wtf/RetainPtr.h>
+#include <wtf/Variant.h>
 
 #if USE(METAL)
 OBJC_PROTOCOL(MTLArgumentEncoder);
@@ -47,7 +48,37 @@ class GPUBindGroupLayout : public RefCounted<GPUBindGroupLayout> {
 public:
     static RefPtr<GPUBindGroupLayout> tryCreate(const GPUDevice&, const GPUBindGroupLayoutDescriptor&);
 
-    using BindingsMapType = HashMap<uint64_t, GPUBindGroupLayoutBinding, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>>;
+    struct UniformBuffer {
+        unsigned internalLengthName;
+    };
+
+    struct DynamicUniformBuffer {
+        unsigned internalLengthName;
+    };
+
+    struct Sampler {
+    };
+
+    struct SampledTexture {
+    };
+
+    struct StorageBuffer {
+        unsigned internalLengthName;
+    };
+
+    struct DynamicStorageBuffer {
+        unsigned internalLengthName;
+    };
+
+    using InternalBindingDetails = Variant<UniformBuffer, DynamicUniformBuffer, Sampler, SampledTexture, StorageBuffer, DynamicStorageBuffer>;
+
+    struct Binding {
+        GPUBindGroupLayoutBinding externalBinding;
+        unsigned internalName;
+        InternalBindingDetails internalBindingDetails;
+    };
+
+    using BindingsMapType = HashMap<uint64_t, Binding, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>>;
     const BindingsMapType& bindingsMap() const { return m_bindingsMap; }
 #if USE(METAL)
     MTLArgumentEncoder *vertexEncoder() const { return m_vertexEncoder.get(); }
index e2ed11a..35ae3c9 100644 (file)
@@ -77,6 +77,18 @@ static RetainPtr<MTLArgumentEncoder> tryCreateMtlArgumentEncoder(const GPUDevice
     return encoder;
 };
 
+static RetainPtr<MTLArgumentDescriptor> argumentDescriptor(MTLDataType dataType, NSUInteger index)
+{
+    RetainPtr<MTLArgumentDescriptor> mtlArgument;
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    mtlArgument = adoptNS([MTLArgumentDescriptor new]);
+    END_BLOCK_OBJC_EXCEPTIONS;
+
+    [mtlArgument setDataType:dataType];
+    [mtlArgument setIndex:index];
+    return mtlArgument;
+}
+
 RefPtr<GPUBindGroupLayout> GPUBindGroupLayout::tryCreate(const GPUDevice& device, const GPUBindGroupLayoutDescriptor& descriptor)
 {
     if (!device.platformDevice()) {
@@ -87,48 +99,73 @@ RefPtr<GPUBindGroupLayout> GPUBindGroupLayout::tryCreate(const GPUDevice& device
     ArgumentArray vertexArgsArray, fragmentArgsArray, computeArgsArray;
     BindingsMapType bindingsMap;
 
+    unsigned internalName = 0;
+    unsigned internalLengthBase = descriptor.bindings.size();
     for (const auto& binding : descriptor.bindings) {
-        if (!bindingsMap.add(binding.binding, binding)) {
+        Optional<unsigned> extraIndex;
+        auto internalDetails = ([&]() -> GPUBindGroupLayout::InternalBindingDetails {
+            switch (binding.type) {
+            case GPUBindingType::UniformBuffer:
+                extraIndex = internalLengthBase++;
+                return GPUBindGroupLayout::UniformBuffer { *extraIndex };
+            case GPUBindingType::DynamicUniformBuffer:
+                extraIndex = internalLengthBase++;
+                return GPUBindGroupLayout::DynamicUniformBuffer { *extraIndex };
+            case GPUBindingType::Sampler:
+                return GPUBindGroupLayout::Sampler { };
+            case GPUBindingType::SampledTexture:
+                return GPUBindGroupLayout::SampledTexture { };
+            case GPUBindingType::StorageBuffer:
+                extraIndex = internalLengthBase++;
+                return GPUBindGroupLayout::StorageBuffer { *extraIndex };
+            default:
+                ASSERT(binding.type == GPUBindingType::DynamicStorageBuffer);
+                extraIndex = internalLengthBase++;
+                return GPUBindGroupLayout::DynamicStorageBuffer { *extraIndex };
+            }
+        })();
+        Binding bindingDetails = { binding, internalName++, WTFMove(internalDetails) };
+        if (!bindingsMap.add(binding.binding, bindingDetails)) {
             LOG(WebGPU, "GPUBindGroupLayout::tryCreate(): Duplicate binding %u found in GPUBindGroupLayoutDescriptor!", binding.binding);
             return nullptr;
         }
 
-        RetainPtr<MTLArgumentDescriptor> mtlArgument;
+        RetainPtr<MTLArgumentDescriptor> mtlArgument = argumentDescriptor(MTLDataTypeForBindingType(binding.type), bindingDetails.internalName);
 
-        BEGIN_BLOCK_OBJC_EXCEPTIONS;
-        mtlArgument = adoptNS([MTLArgumentDescriptor new]);
-        END_BLOCK_OBJC_EXCEPTIONS;
         if (!mtlArgument) {
             LOG(WebGPU, "GPUBindGroupLayout::tryCreate(): Unable to create MTLArgumentDescriptor for binding %u!", binding.binding);
             return nullptr;
         }
 
-        [mtlArgument setDataType:MTLDataTypeForBindingType(binding.type)];
-        [mtlArgument setIndex:binding.binding];
-
-        if (binding.visibility & GPUShaderStageBit::Flags::Vertex)
-            appendArgumentToArray(vertexArgsArray, mtlArgument);
-        if (binding.visibility & GPUShaderStageBit::Flags::Fragment)
-            appendArgumentToArray(fragmentArgsArray, mtlArgument);
-        if (binding.visibility & GPUShaderStageBit::Flags::Compute)
-            appendArgumentToArray(computeArgsArray, mtlArgument);
-    }
-
-    RetainPtr<MTLArgumentEncoder> vertex, fragment, compute;
-
-    if (vertexArgsArray) {
-        if (!(vertex = tryCreateMtlArgumentEncoder(device, vertexArgsArray)))
+        auto addIndices = [&](ArgumentArray& array) -> bool {
+            appendArgumentToArray(array, mtlArgument);
+            if (extraIndex) {
+                RetainPtr<MTLArgumentDescriptor> mtlArgument = argumentDescriptor(MTLDataTypeUInt2, *extraIndex);
+                if (!mtlArgument) {
+                    LOG(WebGPU, "GPUBindGroupLayout::tryCreate(): Unable to create MTLArgumentDescriptor for binding %u!", binding.binding);
+                    return false;
+                }
+                appendArgumentToArray(array, mtlArgument);
+            }
+            return true;
+        };
+        if ((binding.visibility & GPUShaderStageBit::Flags::Vertex) && !addIndices(vertexArgsArray))
             return nullptr;
-    }
-    if (fragmentArgsArray) {
-        if (!(fragment = tryCreateMtlArgumentEncoder(device, fragmentArgsArray)))
+        if ((binding.visibility & GPUShaderStageBit::Flags::Fragment) && !addIndices(fragmentArgsArray))
             return nullptr;
-    }
-    if (computeArgsArray) {
-        if (!(compute = tryCreateMtlArgumentEncoder(device, computeArgsArray)))
+        if ((binding.visibility & GPUShaderStageBit::Flags::Compute) && !addIndices(computeArgsArray))
             return nullptr;
     }
 
+    RetainPtr<MTLArgumentEncoder> vertex, fragment, compute;
+
+    if (vertexArgsArray && !(vertex = tryCreateMtlArgumentEncoder(device, vertexArgsArray)))
+        return nullptr;
+    if (fragmentArgsArray && !(fragment = tryCreateMtlArgumentEncoder(device, fragmentArgsArray)))
+        return nullptr;
+    if (computeArgsArray && !(compute = tryCreateMtlArgumentEncoder(device, computeArgsArray)))
+        return nullptr;
+
     return adoptRef(new GPUBindGroupLayout(WTFMove(bindingsMap), WTFMove(vertex), WTFMove(fragment), WTFMove(compute)));
 }
 
index da2c20d..6262e5f 100644 (file)
@@ -72,13 +72,15 @@ static Optional<GPUBufferBinding> tryGetResourceAsBufferBinding(const GPUBinding
     return GPUBufferBinding { bufferBinding.buffer.copyRef(), bufferBinding.offset, bufferBinding.size };
 }
 
-static void setBufferOnEncoder(MTLArgumentEncoder *argumentEncoder, const GPUBufferBinding& bufferBinding, unsigned index)
+static void setBufferOnEncoder(MTLArgumentEncoder *argumentEncoder, const GPUBufferBinding& bufferBinding, unsigned name, unsigned lengthName)
 {
     ASSERT(argumentEncoder && bufferBinding.buffer->platformBuffer());
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
     // Bounds check when converting GPUBufferBinding ensures that NSUInteger cast of uint64_t offset is safe.
-    [argumentEncoder setBuffer:bufferBinding.buffer->platformBuffer() offset:static_cast<NSUInteger>(bufferBinding.offset) atIndex:index];
+    [argumentEncoder setBuffer:bufferBinding.buffer->platformBuffer() offset:static_cast<NSUInteger>(bufferBinding.offset) atIndex:name];
+    void* lengthPointer = [argumentEncoder constantDataAtIndex:lengthName];
+    memcpy(lengthPointer, &bufferBinding.size, sizeof(uint64_t));
     END_BLOCK_OBJC_EXCEPTIONS;
 }
     
@@ -171,12 +173,12 @@ RefPtr<GPUBindGroup> GPUBindGroup::tryCreate(const GPUBindGroupDescriptor& descr
             return nullptr;
         }
         auto layoutBinding = layoutIterator->value;
-        if (layoutBinding.visibility == GPUShaderStageBit::Flags::None)
+        if (layoutBinding.externalBinding.visibility == GPUShaderStageBit::Flags::None)
             continue;
 
-        bool isForVertex = layoutBinding.visibility & GPUShaderStageBit::Flags::Vertex;
-        bool isForFragment = layoutBinding.visibility & GPUShaderStageBit::Flags::Fragment;
-        bool isForCompute = layoutBinding.visibility & GPUShaderStageBit::Flags::Compute;
+        bool isForVertex = layoutBinding.externalBinding.visibility & GPUShaderStageBit::Flags::Vertex;
+        bool isForFragment = layoutBinding.externalBinding.visibility & GPUShaderStageBit::Flags::Fragment;
+        bool isForCompute = layoutBinding.externalBinding.visibility & GPUShaderStageBit::Flags::Compute;
 
         if (isForVertex && !vertexEncoder) {
             LOG(WebGPU, "%s: No vertex argument encoder found for binding %u!", functionName, index);
@@ -191,39 +193,39 @@ RefPtr<GPUBindGroup> GPUBindGroup::tryCreate(const GPUBindGroupDescriptor& descr
             return nullptr;
         }
 
-        switch (layoutBinding.type) {
-        // FIXME: Support more resource types.
-        // FIXME: We could avoid this ugly switch-on-type using virtual functions if GPUBindingResource is refactored as a base class rather than a Variant.
-        case GPUBindingType::UniformBuffer:
-        case GPUBindingType::StorageBuffer: {
+        auto handleBuffer = [&](unsigned internalLengthName) -> bool {
             auto bufferResource = tryGetResourceAsBufferBinding(resourceBinding.resource, functionName);
             if (!bufferResource)
-                return nullptr;
+                return false;
             if (isForVertex)
-                setBufferOnEncoder(vertexEncoder, *bufferResource, index);
+                setBufferOnEncoder(vertexEncoder, *bufferResource, layoutBinding.internalName, internalLengthName);
             if (isForFragment)
-                setBufferOnEncoder(fragmentEncoder, *bufferResource, index);
+                setBufferOnEncoder(fragmentEncoder, *bufferResource, layoutBinding.internalName, internalLengthName);
             if (isForCompute)
-                setBufferOnEncoder(computeEncoder, *bufferResource, index);
+                setBufferOnEncoder(computeEncoder, *bufferResource, layoutBinding.internalName, internalLengthName);
             boundBuffers.append(bufferResource->buffer.copyRef());
-            break;
-        }
-        case GPUBindingType::Sampler: {
+            return true;
+        };
+
+        auto success = WTF::visit(WTF::makeVisitor([&](GPUBindGroupLayout::UniformBuffer& uniformBuffer) -> bool {
+            return handleBuffer(uniformBuffer.internalLengthName);
+        }, [&](GPUBindGroupLayout::DynamicUniformBuffer& dynamicUniformBuffer) -> bool {
+            return handleBuffer(dynamicUniformBuffer.internalLengthName);
+        }, [&](GPUBindGroupLayout::Sampler&) -> bool {
             auto samplerState = tryGetResourceAsMtlSampler(resourceBinding.resource, functionName);
             if (!samplerState)
-                return nullptr;
+                return false;
             if (isForVertex)
                 setSamplerOnEncoder(vertexEncoder, samplerState, index);
             if (isForFragment)
                 setSamplerOnEncoder(fragmentEncoder, samplerState, index);
             if (isForCompute)
                 setSamplerOnEncoder(computeEncoder, samplerState, index);
-            break;
-        }
-        case GPUBindingType::SampledTexture: {
+            return true;
+        }, [&](GPUBindGroupLayout::SampledTexture&) -> bool {
             auto textureResource = tryGetResourceAsTexture(resourceBinding.resource, functionName);
             if (!textureResource)
-                return nullptr;
+                return false;
             if (isForVertex)
                 setTextureOnEncoder(vertexEncoder, textureResource->platformTexture(), index);
             if (isForFragment)
@@ -231,12 +233,14 @@ RefPtr<GPUBindGroup> GPUBindGroup::tryCreate(const GPUBindGroupDescriptor& descr
             if (isForCompute)
                 setTextureOnEncoder(computeEncoder, textureResource->platformTexture(), index);
             boundTextures.append(textureResource.releaseNonNull());
-            break;
-        }
-        default:
-            LOG(WebGPU, "%s: Resource type not yet implemented.", functionName);
+            return true;
+        }, [&](GPUBindGroupLayout::StorageBuffer& storageBuffer) -> bool {
+            return handleBuffer(storageBuffer.internalLengthName);
+        }, [&](GPUBindGroupLayout::DynamicStorageBuffer& dynamicStorageBuffer) -> bool {
+            return handleBuffer(dynamicStorageBuffer.internalLengthName);
+        }), layoutBinding.internalBindingDetails);
+        if (!success)
             return nullptr;
-        }
     }
     
     return adoptRef(new GPUBindGroup(WTFMove(vertexArgsBuffer), WTFMove(fragmentArgsBuffer), WTFMove(computeArgsBuffer), WTFMove(boundBuffers), WTFMove(boundTextures)));
index 2bb6204..4a9eebb 100644 (file)
@@ -110,20 +110,21 @@ static OptionSet<WHLSL::ShaderStage> convertShaderStageFlags(GPUShaderStageFlags
     return result;
 }
 
-static Optional<WHLSL::BindingType> convertBindingType(GPUBindingType type)
+static Optional<WHLSL::Binding::BindingDetails> convertBindingType(GPUBindGroupLayout::InternalBindingDetails internalBindingDetails)
 {
-    switch (type) {
-    case GPUBindingType::UniformBuffer:
-        return WHLSL::BindingType::UniformBuffer;
-    case GPUBindingType::Sampler:
-        return WHLSL::BindingType::Sampler;
-    case GPUBindingType::SampledTexture:
-        return WHLSL::BindingType::Texture;
-    case GPUBindingType::StorageBuffer:
-        return WHLSL::BindingType::StorageBuffer;
-    default:
+    return WTF::visit(WTF::makeVisitor([&](GPUBindGroupLayout::UniformBuffer uniformBuffer) -> Optional<WHLSL::Binding::BindingDetails> {
+        return { WHLSL::UniformBufferBinding { uniformBuffer.internalLengthName } };
+    }, [&](GPUBindGroupLayout::DynamicUniformBuffer) -> Optional<WHLSL::Binding::BindingDetails> {
         return WTF::nullopt;
-    }
+    }, [&](GPUBindGroupLayout::Sampler) -> Optional<WHLSL::Binding::BindingDetails> {
+        return { WHLSL::SamplerBinding { } };
+    }, [&](GPUBindGroupLayout::SampledTexture) -> Optional<WHLSL::Binding::BindingDetails> {
+        return { WHLSL::TextureBinding { } };
+    }, [&](GPUBindGroupLayout::StorageBuffer storageBuffer) -> Optional<WHLSL::Binding::BindingDetails> {
+        return { WHLSL::StorageBufferBinding { storageBuffer.internalLengthName } };
+    }, [&](GPUBindGroupLayout::DynamicStorageBuffer) -> Optional<WHLSL::Binding::BindingDetails> {
+        return WTF::nullopt;
+    }), internalBindingDetails);
 }
 
 static Optional<WHLSL::TextureFormat> convertTextureFormat(GPUTextureFormat format)
@@ -377,16 +378,17 @@ static Optional<WHLSL::Layout> convertLayout(const GPUPipelineLayout& layout)
         WHLSL::BindGroup bindGroup;
         bindGroup.name = static_cast<unsigned>(i);
         for (const auto& keyValuePair : bindGroupLayout->bindingsMap()) {
-            const auto& gpuBindGroupLayoutBinding = keyValuePair.value;
+            const auto& bindingDetails = keyValuePair.value;
             WHLSL::Binding binding;
-            binding.visibility = convertShaderStageFlags(gpuBindGroupLayoutBinding.visibility);
-            if (auto bindingType = convertBindingType(gpuBindGroupLayoutBinding.type))
-                binding.bindingType = *bindingType;
+            binding.visibility = convertShaderStageFlags(bindingDetails.externalBinding.visibility);
+            if (auto bindingType = convertBindingType(bindingDetails.internalBindingDetails))
+                binding.binding = *bindingType;
             else
                 return WTF::nullopt;
-            if (gpuBindGroupLayoutBinding.binding > std::numeric_limits<unsigned>::max())
+            if (bindingDetails.externalBinding.binding > std::numeric_limits<unsigned>::max())
                 return WTF::nullopt;
-            binding.name = static_cast<unsigned>(gpuBindGroupLayoutBinding.binding);
+            binding.externalName = bindingDetails.externalBinding.binding;
+            binding.internalName = bindingDetails.internalName;
             bindGroup.bindings.append(WTFMove(binding));
         }
         result.append(WTFMove(bindGroup));