Source/WebCore:
[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 positionBufferIndex = 0;
14 const texCoordsBufferIndex = 1;
15 const positionAttributeNum = 0;
16 const texCoordsAttributeNum = 1;
17 const bindGroupIndex = 0;
18 const textureBindingNum = 0;
19 const samplerBindingNum = 1;
20
21 const shaderCode = `
22 #include <metal_stdlib>
23     
24 using namespace metal;
25
26 struct VertexIn
27 {
28     float4 position [[attribute(${positionAttributeNum})]];
29     float2 texCoords [[attribute(${texCoordsAttributeNum})]];
30 };
31
32 struct VertexOut
33 {
34     float4 position [[position]];
35     float2 texCoords;
36 };
37
38 vertex VertexOut vertex_main(VertexIn vertexIn [[stage_in]])
39 {
40     VertexOut vOut;
41     vOut.position = vertexIn.position;
42     vOut.texCoords = vertexIn.texCoords;
43     return vOut;
44 }
45
46 struct TextureSampler
47 {
48     texture2d<float> t [[id(${textureBindingNum})]];
49     sampler s [[id(${samplerBindingNum})]];
50 };
51
52 fragment float4 fragment_main(VertexOut v [[stage_in]], const device TextureSampler& args [[buffer(${bindGroupIndex})]])
53 {
54     return args.t.sample(args.s, v.texCoords);
55 }
56 `
57
58 function createInputStateDescriptor() {
59     return {
60         indexFormat: "uint32",
61         attributes: [{
62             shaderLocation: positionAttributeNum,
63             inputSlot: positionBufferIndex,
64             offset: 0,
65             format: "float4"
66         }, {
67             shaderLocation: texCoordsAttributeNum,
68             inputSlot: texCoordsBufferIndex,
69             offset: 0,
70             format: "float2"
71         }],
72         inputs: [{
73             inputSlot: positionBufferIndex,
74             stride: 4 * 4,
75             stepMode: "vertex"
76         }, {
77             inputSlot: texCoordsBufferIndex,
78             stride: 4 * 2,
79             stepMode: "vertex"
80         }]
81     }
82 }
83
84 async function test() {
85     const device = await getBasicDevice();
86     const canvas = document.querySelector("canvas");
87     const swapChain = createBasicSwapChain(canvas, device);
88     // FIXME: Replace with non-MSL shaders.
89     const shaderModule = device.createShaderModule({ code: shaderCode });
90
91     const positionArray = new Float32Array([
92         // float4 xyzw
93         -1, 1, 0, 1,
94         -1, -1, 0, 1,
95         1, 1, 0, 1, 
96         1, -1, 0, 1
97     ]);
98     const positionBuffer = device.createBuffer({ size: positionArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
99     positionBuffer.setSubData(0, positionArray.buffer);
100
101     const texCoordsArray = new Float32Array([
102         // float2 texCoords
103         0, 0,
104         0, 1,
105         1, 0,
106         1, 1
107     ]);
108     const textureCoordBuffer = device.createBuffer({ size: texCoordsArray.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST });
109     textureCoordBuffer.setSubData(0, texCoordsArray.buffer);
110
111     const inputStateDescriptor = createInputStateDescriptor();
112
113     // Load texture image
114     const image = new Image();
115     const imageLoadPromise = new Promise(resolve => { 
116         image.onload = () => resolve(); 
117         image.src = "resources/blue-checkered.png";
118     });
119     await Promise.resolve(imageLoadPromise);
120
121     // Convert image to data and fill GPUBuffer
122     const canvas2d = document.createElement("canvas");
123     canvas2d.width = image.width;
124     canvas2d.height = image.height;
125     const context2d = canvas2d.getContext("2d");
126     context2d.drawImage(image, 0, 0);
127     const imageData = context2d.getImageData(0, 0, image.width, image.height);
128
129     const textureBufferDescriptor = {
130         size: imageData.data.length,
131         usage: GPUBufferUsage.TRANSFER_SRC | GPUBufferUsage.TRANSFER_DST
132     };
133     const textureBuffer = device.createBuffer(textureBufferDescriptor);
134     textureBuffer.setSubData(0, imageData.data.buffer);
135
136     // Create GPUTexture
137     const textureSize = {
138         width: image.width,
139         height: image.height,
140         depth: 1
141     };
142
143     const textureDescriptor = {
144         size: { width: image.width, height: image.height, depth: 1 },
145         arrayLayerCount: 1,
146         mipLevelCount: 1,
147         sampleCount: 1,
148         dimension: "2d",
149         format: "rgba8unorm",
150         usage: GPUTextureUsage.TRANSFER_DST | GPUTextureUsage.SAMPLED
151     };
152     const texture = device.createTexture(textureDescriptor);
153
154     // Bind texture and a sampler to pipeline
155     const textureLayoutBinding = { 
156         binding: textureBindingNum, 
157         visibility: GPUShaderStageBit.FRAGMENT, 
158         type: "sampled-texture" 
159     };
160     const samplerLayoutBinding = {
161         binding: samplerBindingNum,
162         visibility: GPUShaderStageBit.FRAGMENT,
163         type: "sampler"
164     };
165
166     const bindGroupLayoutDescriptor = {
167         bindings: [textureLayoutBinding, samplerLayoutBinding]
168     };
169     bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
170     const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] });
171
172     const textureBinding = { 
173         binding: textureBindingNum, 
174         resource: texture.createDefaultView() 
175     };
176     const samplerBinding = {
177         binding: samplerBindingNum,
178         resource: device.createSampler({ minFilter: "nearest", magFilter: "nearest" })
179     };
180
181     const bindGroupDescriptor = {
182         layout: bindGroupLayout,
183         bindings: [textureBinding, samplerBinding]
184     };
185     const bindGroup = device.createBindGroup(bindGroupDescriptor);
186
187     // Pipeline and render
188     const pipeline = createBasicPipeline(shaderModule, device, null, pipelineLayout, inputStateDescriptor);
189     const commandEncoder = device.createCommandEncoder();
190
191     const bufferCopyView = {
192         buffer: textureBuffer,
193         offset: 0,
194         rowPitch: image.width * 4,
195         imageHeight: 0
196     };
197     const textureCopyView = {
198         texture: texture,
199         mipLevel: 0,
200         arrayLayer: 0,
201         origin: { x: 0, y: 0, z: 0 }
202     };
203     commandEncoder.copyBufferToTexture(bufferCopyView, textureCopyView, textureSize);
204     const passEncoder = beginBasicRenderPass(swapChain, commandEncoder);
205     passEncoder.setPipeline(pipeline);
206     passEncoder.setBindGroup(bindGroupIndex, bindGroup);
207     passEncoder.setVertexBuffers(positionBufferIndex, [positionBuffer, textureCoordBuffer], [0, 0]);
208     passEncoder.draw(4, 1, 0, 0);
209     passEncoder.endPass();
210
211     const queue = device.getQueue();
212     queue.submit([commandEncoder.finish()]);
213     positionBuffer.destroy();
214     textureCoordBuffer.destroy();
215     texture.destroy();
216
217     if (window.testRunner)
218         testRunner.notifyDone();
219 }
220
221 test();
222 </script>