[Web GPU] GPUTexture and GPUTextureView updates, and related GPUBindGroup updates
[WebKit-https.git] / LayoutTests / webgpu / texture-triangle-strip.html
1 <!DOCTYPE html>
2 <meta charset="utf-8">
3 <title>WebGPU Hello Triangles</title>
4 <meta name="assert" content="WebGPU correctly renders a green canvas.">
5 <link rel="match" href="vertex-buffer-triangle-strip-expected.html">
6 <p>Pass if square canvas below is a 4 by 4 blue/green checkerboard.</p>
7 <canvas width="400" height="400"></canvas>
8 <script src="js/webgpu-functions.js"></script>
9 <script>
10 if (window.testRunner)
11     testRunner.waitUntilDone();
12
13 const shaderCode = `
14 #include <metal_stdlib>
15     
16 using namespace metal;
17
18 struct VertexIn
19 {
20     float4 position [[attribute(0)]];
21     float2 texCoords [[attribute(1)]];
22 };
23
24 struct VertexOut
25 {
26     float4 position [[position]];
27     float2 texCoords;
28 };
29
30 vertex VertexOut vertex_main(VertexIn vertexIn [[stage_in]])
31 {
32     VertexOut vOut;
33     vOut.position = vertexIn.position;
34     vOut.texCoords = vertexIn.texCoords;
35     return vOut;
36 }
37
38 struct Texture
39 {
40     texture2d<float> image [[id(0)]];
41 };
42
43 fragment float4 fragment_main(VertexOut v [[stage_in]], const device Texture& t [[buffer(0)]])
44 {
45     constexpr sampler s(coord::normalized, address::clamp_to_zero, filter::nearest);
46     return t.image.sample(s, v.texCoords);
47 }
48 `
49
50 function createInputStateDescriptor() {
51     return {
52         indexFormat: WebGPUIndexFormat.UINT32,
53         attributes: [{
54             shaderLocation: 0,
55             inputSlot: 0,
56             offset: 0,
57             format: WebGPUVertexFormat.FLOAT_R32_G32_B32_A32
58         }, {
59             shaderLocation: 1,
60             inputSlot: 1,
61             offset: 0,
62             format: WebGPUVertexFormat.FLOAT_R32_G32
63         }],
64         inputs: [{
65             inputSlot: 0,
66             stride: 4 * 4,
67             stepMode: WebGPUInputStepMode.VERTEX
68         }, {
69             inputSlot: 1,
70             stride: 4 * 2,
71             stepMode: WebGPUInputStepMode.VERTEX
72         }]
73     }
74 }
75
76 async function test() {
77     const device = await getBasicDevice();
78     const canvas = document.querySelector("canvas");
79     const context = createBasicContext(canvas, device);
80     // FIXME: Replace with non-MSL shaders.
81     const shaderModule = device.createShaderModule({ code: shaderCode });
82
83     const vertexArray = new Float32Array([
84         // float4 xyzw
85         -1, 1, 0, 1,
86         -1, -1, 0, 1,
87         1, 1, 0, 1, 
88         1, -1, 0, 1
89     ]);
90     const vertexBuffer = device.createBuffer({ size: vertexArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
91     vertexBuffer.setSubData(0, vertexArray.buffer);
92
93     const textureCoordArray = new Float32Array([
94         // float2 texCoords
95         0, 0,
96         0, 1,
97         1, 0,
98         1, 1
99     ]);
100     const textureCoordBuffer = device.createBuffer({ size: textureCoordArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
101     textureCoordBuffer.setSubData(0, textureCoordArray.buffer);
102
103     const inputStateDescriptor = createInputStateDescriptor();
104
105     // Load texture image
106     const image = new Image();
107     const imageLoadPromise = new Promise(resolve => { 
108         image.onload = () => resolve(); 
109         image.src = "resources/blue-checkered.png";
110     });
111     await Promise.resolve(imageLoadPromise);
112
113     // Convert image to data and fill GPUBuffer
114     const canvas2d = document.createElement("canvas");
115     canvas2d.width = image.width;
116     canvas2d.height = image.height;
117     const context2d = canvas2d.getContext("2d");
118     context2d.drawImage(image, 0, 0);
119     const imageData = context2d.getImageData(0, 0, image.width, image.height);
120
121     const textureBufferDescriptor = {
122         size: imageData.data.length,
123         usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.TRANSFER_DST
124     };
125     const textureBuffer = device.createBuffer(textureBufferDescriptor);
126     textureBuffer.setSubData(0, imageData.data.buffer);
127
128     // Create GPUTexture
129     const textureSize = {
130         width: image.width,
131         height: image.height,
132         depth: 1
133     };
134
135     const textureDescriptor = {
136         size: { width: image.width, height: image.height, depth: 1 },
137         arrayLayerCount: 1,
138         mipLevelCount: 1,
139         sampleCount: 1,
140         dimension: "2d",
141         format: "r8g8b8a8-unorm",
142         usage: GPUTextureUsage.TRANSFER_DST | GPUTextureUsage.SAMPLED
143     };
144     const texture = device.createTexture(textureDescriptor);
145
146     // Bind texture to pipeline
147     const textureBindingNum = 0;
148     const bindGroupLayoutDescriptor = {
149         bindings: [{ binding: textureBindingNum, visibility: GPUShaderStageBit.FRAGMENT, type: "sampled-texture" }]
150     };
151     bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
152     const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] });
153
154     const bindGroupDescriptor = {
155         layout: bindGroupLayout,
156         bindings: [{ binding: textureBindingNum, resource: texture.createDefaultTextureView() }]
157     };
158     const bindGroup = device.createBindGroup(bindGroupDescriptor);
159
160     // Pipeline and render
161     const pipeline = createBasicPipeline(shaderModule, device, pipelineLayout, inputStateDescriptor);
162     const commandBuffer = device.createCommandBuffer();
163
164     const bufferCopyView = {
165         buffer: textureBuffer,
166         offset: 0,
167         rowPitch: image.width * 4,
168         imageHeight: 0
169     };
170     const textureCopyView = {
171         texture: texture,
172         mipLevel: 0,
173         arrayLayer: 0,
174         origin: { x: 0, y: 0, z: 0 }
175     };
176     commandBuffer.copyBufferToTexture(bufferCopyView, textureCopyView, textureSize);
177     const passEncoder = beginBasicRenderPass(context, commandBuffer);
178     passEncoder.setPipeline(pipeline);
179     passEncoder.setBindGroup(0, bindGroup);
180     passEncoder.setVertexBuffers(0, [vertexBuffer, textureCoordBuffer], [0, 0]);
181     passEncoder.draw(4, 1, 0, 0);
182     passEncoder.endPass();
183
184     const queue = device.getQueue();
185     queue.submit([commandBuffer]);
186     vertexBuffer.destroy();
187     textureCoordBuffer.destroy();
188     texture.destroy();
189     context.present();
190
191     if (window.testRunner)
192         testRunner.notifyDone();
193 }
194
195 test();
196 </script>