-
Notifications
You must be signed in to change notification settings - Fork 345
Add reference Web GPU Spinning Cube Demo and glMatrix #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
samples/hello-cube.html
Outdated
|
|
||
| const verticesBufferDescriptor = { | ||
| size: vertexArray.byteLength, | ||
| usage: GPUBufferUsage.VERTEX | GPUBufferUsage.TRANSFER_DST |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's kind of unfortunate that setSubData requires TRANSFER_DST and nothing else. On UMA, setSubData might be able to write directly into the buffer (if the stars align correctly).
samples/hello-cube.html
Outdated
| depthCompare: "less" | ||
| }; | ||
|
|
||
| /* Render Pipeline */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd move this comment to above line 150.
| clearColor: { r: 0.5, g: 1.0, b: 1.0, a: 1.0 } | ||
| }; | ||
|
|
||
| // GPUExtent3D |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish we could do something more type-safe here. It seems silly to attach a 3D size to a 2D texture.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, maybe we need GPUSize2D and GPUSize3D (I prefer both of them over "Extent")
samples/hello-cube.html
Outdated
| mipLevelCount: 1, | ||
| sampleCount: 1, | ||
| dimension: "2d", | ||
| format: "d32-float-s8-uint", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At a glance, this looks like a UUID. 🤮
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should at least make them consistent with the other texture formats.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I missed this one when updating the names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, you forgot to include depth and stencil formats when you updated the list of texture formats in the IDL. I'll make a PR in a bit to add them.
samples/hello-cube.html
Outdated
| clearDepth: 1.0 | ||
| }; | ||
|
|
||
| renderPassDescriptor = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of writing into globals, it's probably better style to return a collection of all the variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds very functional indeed.
samples/hello-cube.html
Outdated
| }; | ||
| } | ||
|
|
||
| class MappedBufferBindGroup { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if we're going to add classes as helpers, we could do the same for the transform array matrix. e.g. a class that holds the mappedGroup and exposes the arrayBuffer.
| queue.submit([commandEncoder.finish()]); | ||
|
|
||
| // Ready the current buffer for update after GPU is done with it. | ||
| mappedGroup.buffer.mapWriteAsync().then((arrayBuffer) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The right thing will happen here, but we might not wants apps to rely on the specific timeline that the mapWriteAsync promise resolves. It may be better to explicitly handle the dependency of "the mapping should happen after the command buffer finishes" by using fences. I'm not sure which behavior we want to promote.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this does the right thing, it's probably more readable than barriers.
The thing that I think is a bit weird is that mappedGroups could grow infinitely (if some other part of the code appends to it outside this loop). It's a shame we can't attach the group to the queue or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have this example working with my mapWriteAsync() prototype. I could use a class to control access to the mappedGroups queue, if that's what you mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I understand what you're saying now. Shouldn't mapWriteAsync only resolve after the GPU is done with the buffer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it means that we don't have any pipelining here based on how I understand it. We cannot upload the data for the second frame until the GPU is done with the data for the first. Our example should support double-buffering.
samples/hello-cube.html
Outdated
| mappedGroup.buffer.unmap(); | ||
|
|
||
| const commandEncoder = device.createCommandEncoder(); | ||
| renderPassDescriptor.colorAttachments[0].attachment = swapChain.getTexture().createDefaultTextureView(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've got a single render pass descriptor that every render pass uses. This implicitly requires that beginRenderPass copy the contents of the descriptor so that the next frame doesn't clobber the current frame's execution. This might be okay, but it might not be. If it isn't okay, the group should decide on a mechanism to make it obvious to authors what is going wrong when they write the same code you have here.
samples/hello-cube.html
Outdated
|
|
||
| const vertexSize = 4 * 8; | ||
| const colorOffset = 4 * 4; | ||
| const vertexArray = new Float32Array([ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea whether this giant array is correct. Can't you compute it at runtime?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fairly standard in all spinning cube demos. I don't think it is a big deal. Computing it at runtime would probably be an equally confusing set of for loops.
Maybe the best thing to do would be to name the variables cubeVertices, and provide comments for the 4 * 8 and 4 * 4 values.
| <script src="gl-matrix-min.js"></script> | ||
| </head> | ||
| <body> | ||
| <canvas height=1000 width=1000></canvas> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You set a 600px viewport above, so 1000x1000 would be mostly offscreen.
samples/hello-cube.html
Outdated
|
|
||
| vertex FragmentData vertex_main(float4 position : attribute(0), | ||
| float4 color : attribute(1), | ||
| constant float4x4* modelViewProjectionMatrix : register(b0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the input a pointer to a float4x4?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean as opposed to a reference or copy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As opposed to the more normal HLSL mechanism of defining a cbuffer/ConstantBuffer with your input variables.
|
|
||
| const depthTexture = device.createTexture(depthTextureDescriptor); | ||
|
|
||
| // GPURenderPassDepthStencilAttachmentDescriptor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this comment.
samples/hello-cube.html
Outdated
| this.bindGroup = bindGroup; | ||
| } | ||
| } | ||
| const mappedGroups = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably go at the top.
samples/hello-cube.html
Outdated
| const { buffer, arrayBuffer } = device.createMappedBuffer(transformBufferDescriptor); | ||
| const bindGroup = device.createBindGroup(createTransformBindGroupDescriptor(buffer)); | ||
|
|
||
| mappedGroup = new MappedBufferBindGroup(buffer, arrayBuffer, bindGroup); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since a MappedBufferBindGroup doesn't have anything other than a constructor, it's basically equivalent to an anonymous object. i.e. you could omit the class and just say:
mappedGroup = { buffer, arrayBuffer, bindGroup };
samples/hello-cube.html
Outdated
| init(); | ||
| </script> | ||
| </body> | ||
| </html> No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a newline.
samples/hello-cube.html
Outdated
| } else | ||
| mappedGroup = mappedGroups.shift(); | ||
|
|
||
| updateTransformArray(mappedGroup.arrayBuffer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving myself a note here to fix: updateTransformArray takes a Float32Array, not an ArrayBuffer.
kdashg
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we link to an external source for the library?
https://cdnjs.com/libraries/gl-matrix
|
I've updated the file to match our API changes and removed the library file in favor of an external source. It does not work in current Safari since WHLSL is not the default ingestion format yet. Feel free to make a PR after merging to feature-detect and use SPIR-V shaders, or other API updates I may have missed @kainino0x @austinEng |
* Add reference demo files * Add shading language notice * Addressed basic review issues * Small fixes * Add some clarifying comments * Update to IDL commit e137ea9 * Attempt to get WHLSL shaders working * whoops * Update reference for submission * Delete gl-matrix-min.js
Using npm slightly reduces unnecessary dependencies of the project.
This demo serves as a reference for a Web GPU implementation. It reflects the current API as well as approved pull requests.
Also included is the glMatrix matrix math library by Brandon Jones.