Skip to content

Bind point renaming#351

Closed
litherum wants to merge 1 commit intogpuweb:masterfrom
litherum:binding
Closed

Bind point renaming#351
litherum wants to merge 1 commit intogpuweb:masterfrom
litherum:binding

Conversation

@litherum
Copy link
Contributor

@litherum litherum commented Jul 1, 2019

Due to the resolution that the WebGPU API's binding model should not be modified to match the shading language's binding model, and due to the fact that backwards compatibility with existing HLSL code is important, this patch adds an "adapter" to bridge between the two binding models. This adapter exists in the Javascript API, because we're trying to minimize the number of places that existing shaders have to be updated.

The proposal is fairly simple: provide a mapping of strings (of the form register(u2, space1)) to the two-level binding model used in the API. This mapping is provided in the GPUShaderModule so compilation can be done as early as possible and doesn't have to be deferred until pipeline creation time.

This new map is optional, so authors which already have a source file which adheres to WebGPU's binding model don't need to supply anything. If this field is not present, it behaves as if the map has 0 fields in it. For every field that is present, compilation is expected to behave as if the WHLSL semantic is replaced with the associated value. For every semantic in the source shader, if a mapping is not present for it, it behaves as if the binding comes from the digit after the type character (the 2 in register(u2, space1)) and the bindGroup comes from the space (the 1 in register(u2, space1)).

If honoring this mapping would cause duplicate binding points, the program is in error (we don't try again without the mapping; the mapping, when specified, is required to be applied). If two parameters in the source program (before the mapping) have duplicate semantics, the program is in error.

SPIR-V would just ignore this new field.

This renaming allows for mapping a parameter from one bind group to another. This is so that a WHLSL program with all its parameters in the same space can spread those parameters out across multiple bind groups.

Also considered: Instead of the keys being strings, they could be a dictionary containing 3 fields: binding, bind group, and type. However, this has less flexibility for further binding model advances in the future, doesn't map well to WebIDL, and is less obvious to a WHLSL author which variable maps to which entry in the map.

Usage:

const overriddenBindPoints = {"register(u2, space1)": {binding: 3, bindGroup: 4}};
const computeStage = device.createShaderModule(code, overriddenBindPoints);
const pipelineDescriptor = {computeStage, ...};
const computePipeline = device.createComputePipeline(pipelineDescriptor);

Preview | Diff

@damyanp
Copy link

damyanp commented Jul 1, 2019

I believe that the SPIR-V backend to dxc has had to solve a similar problem:

https://github.com/Microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst#hlsl-register-and-vulkan-binding

Was the approach here considered?

@kvark
Copy link
Contributor

kvark commented Jul 2, 2019

@damyanp the approach you linked to requires changing the source (by adding the "[[vk::binding(X[, Y])]]" attributes), which is something we are trying to avoid in order to preserve compatibility with existing code bases.

@litherum thank you for writing this up!

This mapping is provided in the GPUShaderModule so compilation can be done as early as possible and doesn't have to be deferred until pipeline creation time.

I think some of the backends (Dx12, Metal-without-argument-buffers) would still require us to defer the shader creation to the pipeline creation time, since this is where the layout is known. But having the API shaped in a way that at least allows an implementation to do this work ahead of time is good.

const overriddenBindPoints = {"register(u2, space1)": {binding: 3, bindGroup: 4}};

If the "space" qualifier is already provided, wouldn't we want to just use it directly? In what case would an extra mapping be needed? My understanding was that we only want to do this to support shaders that were written for DX11 resource model to begin with. Did you consider just mapping "u2", "t0", etc as a key instead of the whole "register(x, y)"?

@damyanp
Copy link

damyanp commented Jul 2, 2019

@kvark - dxc has several strategies, one of which involves changing the source code. There's also a bunch of command line parameters that can be passed to dxc to control the policy of how the registers are remapped. My thinking is that anyone who already has shaders that they're compiling with dxc but using with vulkan will have figured out what parameters they need to pass to dxc in order to configure a set of registers that work with that style of binding model.

Can you help me understand why DX12 would need to understand the layout in order to compile the shader? In DX12 the shader can be compiled to bytecode without knowing the layout. When the bytecode is passed to the driver while creating the pipeline state object the layout is required. Or am I missing something?

@kainino0x
Copy link
Contributor

BTW, we've already talked about what DXC does for both D3D12 and for Vulkan in #188 and #192.

@kainino0x
Copy link
Contributor

Offhand, I think you (@damyanp) are right that this mapping could be at pipeline creation instead of shader module creation. (In the world, of course, where WHLSL is what's used to create a shader module.)

@kvark
Copy link
Contributor

kvark commented Jul 2, 2019

@damyanp command line options is not a choice we could consider here, since no command line is involved. And the fact that the users already know the mapping they need is one of the reasons we went with Vulkan-like binding model to begin with. This PR is making a concrete proposal on how the users would communicate this information (that they already know) to us.

Can you help me understand why DX12 would need to understand the layout in order to compile the shader?

Problem originates from the limitation of DX12 that samplers and non-samplers can live in the same descriptor heap. Before calling SPIRV-Cross we (in gfx-rs) are setting up the resource overrides accordingly, and this is where the pipeline layout is needed. That is to say, it's been a while since I looked in depth at that logic piece, so we might find a way to improve that.

@kainino0x
Copy link
Contributor

They could be arguments in a dictionary instead of actual command line args. But the actual semantics of those command line arguments (assuming you're talking about -fvk-s-shift & co) is very compat-focused, and doesn't seem like a reasonable first-class API.

@damyanp
Copy link

damyanp commented Jul 2, 2019

I would argue that requiring developers to manually list out a mapping for all shader registers is also very compat focused. The whole point of this overrides API is to provide a way to adapt a shader written for one binding model to work with a different binding model. We would also expect that any shader specifically written for WebGPU would not need this modification applied.

My expectation here is that the implementation for this would effectively modify the source code that's passed to the WHLSL compiler, and could easily be implemented outside of WebGPU.

Given this, I'm unsure that this should be a first-class API.

@kainino0x
Copy link
Contributor

If duplicate entries appear in the sequence, the first one wins.

nit: Would make more sense to me to just make it invalid. Also, that semantic or requirement should be in the spec (as comment or prose)

@litherum
Copy link
Contributor Author

litherum commented Jul 2, 2019

We would also expect that any shader specifically written for WebGPU would not need this modification applied.

Correct; this entire API is optional.

could easily be implemented outside of WebGPU

Not without a parser written in JS/WASM, which places an undue burden on web developers.

@damyanp
Copy link

damyanp commented Jul 2, 2019

Not without a parser written in JS/WASM, which places an undue burden on web developers.

Well, that only has to be written once :) This translation could happen even earlier on in the process - before the shader is even loaded into the browser, for example.

I would like to see WebGPU and WHLSL agree on the binding model. If we want to provide paths to help port non-WHLSL shaders to WHLSL then that's great, but I don't think that this conversion belongs in WebGPU.

@kainino0x
Copy link
Contributor

This is basically the crux of what I wanted to say in the meeting (but didn't have time). While HLSL compatibility is important to some users of the API, having incompatibilities only in bindings seems acceptable to me - it's a tiny volume of changes for an existing engine, especially given they have to also rewrite all of the non-shader code.

For those that really want to run totally unmodified, they're going to have to use a translation layer anyway, and that translation layer would be able to do this extra work.

@litherum
Copy link
Contributor Author

litherum commented Jul 8, 2019

I would like to see WebGPU and WHLSL agree on the binding model.

I, too, would like to see WebGPU and WHLSL agree on the binding model. However, I believe that ship has sailed in #188.

@damyanp
Copy link

damyanp commented Jul 8, 2019

I'd like to continue seeing if we can find a way to have WHLSL feel like a more natural shading language for WebGPU.

On another note though, I'm wondering how we'd expect someone who has a load of HLSL shaders that they compile with dxc to spir-v using the various -fvt-*-shift compiler options generate the appropriate mapping tables to work with this API? It looks like they'd have to build up a mapping table listing every possible remapping. Can we improve on this?

@grorg
Copy link
Contributor

grorg commented Jul 8, 2019

Discussed at 8 July 2019 teleconference

@kainino0x
Copy link
Contributor

Rebased over #365 (and squashed, sorry)


<script type=idl>
dictionary GPUShaderSemantic {
required DOMString type; // "u", "s", "t", "b"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could be an enum


dictionary GPUShaderModuleDescriptor : GPUObjectDescriptorBase {
required GPUShaderCode code;
sequence<GPUOverriddenBindPoint>? overriddenBindPoints = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should probably be

sequence<GPUOverriddenBindPoint> overriddenBindPoints = [];

Note: While the choice of shader language is undecided,
`GPUShaderModuleDescriptor` will temporarily accept both text and binary input.

#### Bind Point Renaming #### {#bind-point-renaming}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: Added this header for you when I did the rebase

@grorg
Copy link
Contributor

grorg commented Jul 15, 2019

This was discussed at the 15 July 2019 teleconference.

@kainino0x
Copy link
Contributor

I think we all agreed to close this and make the changes in WHLSL. Closing!

@kainino0x kainino0x closed this Jul 15, 2019
ben-clayton pushed a commit to ben-clayton/gpuweb that referenced this pull request Sep 6, 2022
* Port the renderPass resolve tests from GLSL to WGSL

* Update src/webgpu/api/operation/render_pass/resolve.spec.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants