-
Notifications
You must be signed in to change notification settings - Fork 558
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
MSL: RWBuffer<uint> is translated into a texture2d and buffer of atomic_int even though SPIRV-Cross can tell the original type #1362
Comments
This is a typed buffer, and not a raw buffer. MSL does not support typed buffers, and thus is emulated as a texture2D. In SPIR-V a typed buffer is expressed an image with buffer dimension, so this is all expected. There is an option which enables native texture buffer support (minspec is Metal 2.1): https://github.com/KhronosGroup/SPIRV-Cross/blob/master/spirv_msl.hpp#L311 However, even with that, MSL does not support atomics on typed buffers/images, and thus we get the weirdness you see here. Another contributor added the atomic side buffer as a workaround for this problem, but it's not pretty. I'm not sure what I can do, MSL is making it needlessly difficult to implement this as you'd expect. The only viable workaround would be to detect this special case and rewrite a texture buffer to raw buffer and deduce that the type is u32/i32 from that. |
Ok, I see another avenue here. The OpTypeImage format is declared as R32UI by DXC, that helps a lot. That might be enough to make this work. However, this can only possibly work for texel buffers, not RWTexture, just in case you were expecting that to work as well. |
Actually, that won't work either ... Texture buffers in MSL are declared with |
Here's an equivalent:
With --msl-native-texture-buffer I get:
|
From my perspective it's not clear why use a texture resource to emulate the buffer in the first place (and this might be lack of experience with Metal on my part), is it just that it's simpler to translate the SPIR-V that way? For more context, a snippet like this, which uses both atomic and regular writes:
When passed through our existing MSL backend (which does HLSL -> [FXC]-> DXBC -> [HLSLcc] -> MSL, so there's no SPIR-V in the middle) produces the following MSL:
And I'm not clear why couldn't SPIRV-Cross produce the same/similar (at least in the fact that is would use [[buffer ()]] and not [[texture()]]), given that it can tell that the resource was originally a buffer. We'd highly prefer if we didn't have to change our resource types depending on what compiler backend we're using as shaders from both might need be used together. |
Because again, it's a typed buffer. That means that pixel conversion is applied to the buffer's contents on every load and store. In Vulkan, you would create a |
Either HLSLCC or FXC must be applying some very special workaround here, because that MSL code rewrites the fact that the buffer is typed, and I don't see how that would work in a more interesting case. I'm open to adding support for a similar workaround in SPIRV-Cross, but it would have to be opt-in. E.g. something like, typed buffer with R32UI/I format declared will be treated as a plain buffer, but that's a very narrow workaround and not a general purpose solution. |
Opt-in flag to get that behavior would work just fine for our (Unity) use case. |
As @aras-p said, having an opt-in would be great for us.
You might have already had this in mind, but to be explicit about it - we would need regular Buffer<> types to produce [[buffer()]] bindings as well (not just RWBuffer<> types) |
All E.g. for a This workaround can only work iff:
There is also the question of alignment. Texel buffers can support very tight alignment, but buffer objects often don't. Does Metal guarantee support for 4-byte aligned buffer descriptors? |
I think all of this is actually for a fairly narrow case of: original HLSL source has a Yes that's a very "unorthodox" use case, and arguably the original HLSL source should be rewritten to use structured buffer or byte address buffer. But as it is right now, whoever wrote original HLSL ("anyone can write it!" is an issue we have at Unity :)), would be "oh this works everywhere, except on Metal, Metal must be broken!". Or even more likely, not even test on Metal, only to find out their end users or someone entirely else find that it's broken on Metal. Yes that's a very "non generic" use case, and would be kind of a gross hack. But a flag to SPIRV-Cross that would essentially allow saying "if you find buffer or rwbuffer with 4-byte integer type that only ever does atomic operations on it, please output that as a buffer and not a texture" would probably cover this specific case. |
|
Not exactly, our current backend has the limitation that it tries to produce output from DXBC which essentially has very little type information when compared to SPIR-V from what I've seen. This results in minimal type information being left in the output MSL, the buffer is assumed to be packed in memory as it would be in DirectX and casts are performed on every access
So for a
it would still generate a buffer of
|
So that code translates |
Yes, I just noticed that myself, so as @aras-p said we're most likely only meaningfully using |
So in that case, a workaround would look something like:
It is still unclear to me why you would need to emit pointer types for Buffer though. There are several mechanisms you could use in Metal to implement this "correctly". The only real problem case I can see where this workaround would have made sense is RWBuffer with atomics, since Metal has no reasonable way to express this without ugly hackery. |
Yeah that sounds like it would work for our case. But all the discussion above has good points, our own particular (very occasional) usage of |
That would be much appreciated. Working around this in SPIRV-Cross is a last resort and I really don't want to do it, especially if it starts to affect all typed buffers like this. At that point it becomes working around app bugs instead of workaround Metal limitations. A SPIR-V -> SPIR-V transformation is more appropriate if possible, but the best fix is to fix the invalid shader code of course. I'll mark the issue as rejected for the time being. Can be re-evaluated later. |
*Replace (RW)Buffer resources as these no longer work as before - HLSLcc would generate buffer bindings, whereas DXC+SPIRV-Cross outputs textures (see KhronosGroup/SPIRV-Cross#1362 for details) *Removed explicit register locations in compute shaders as these do not have any practival meaning AFAIK (only C# graphics API references direct bindings) *Replace legacy 'COLOR' PS output semantic with 'SV_Target' in cloud shader
…DXC MSL backend. *HLSLcc outputs MSL buffer bindings for HLSL's (RW)Buffer type, whereas DXC+SPIRV-Cross outputs texture bindings (see KhronosGroup/SPIRV-Cross#1362 for details)
* Replace (RW)Buffer usages with (RW)StructuredBuffer to work with our DXC MSL backend. *HLSLcc outputs MSL buffer bindings for HLSL's (RW)Buffer type, whereas DXC+SPIRV-Cross outputs texture bindings (see KhronosGroup/SPIRV-Cross#1362 for details) * Avoid using DXC for tessellation shaders when targeting Metal * Add sufficient DXC requirements for SubsurfaceScattering.compute * Add tmeporary DXC bug workaround for using 'min' in array length declarations * Fix DXC error about missing return values when _DETAIL is undefined. * Avoid write to global constant to fix DXC compilation error. * Replace Buffer<> with StructuredBuffer<> to work around DXC MSL limitation * Add missed Buffer<> to StructuredBuffer<> conversion * Make sure workaround for 'min()' in array declarations is only used for DXC * Disable DXC for URP particle shaders which rely on UBO writes. * Fix usage of raw VFACE semantic which won't link for DXC's MSL backend * Add link for relevant DXC bug next to min() in array size declaration workaround * Remove '#pragma require wavebasic' from SSR shader used for DXC testing * Undo (RW)Buffer to (RW)StructuredBuffer changes in shaders. * Formatting Co-authored-by: lukas.taparauskas <lukas.taparauskas@unity3d.com> Co-authored-by: Sebastien Lagarde <sebastien@unity3d.com>
We're using DXC to turn the following HLSL:
Into the following SPIR-V
and then we use SPIRV-Cross to generate MSL:
However, it creates a redundant texture2d and also changes the name of the buffer resource by appending
_atomic
at the end. Both of these could be avoided by doing something similar as done for RWStructuredBuffer, which would produce a regular MSL buffer of uint (not atomic_int) and would only cast to atomic where necessary (seen here). If I use SPIRV-Cross to generate HLSL instead of MSL it will produce the RWBuffer as in the original source:so it seems to have enough context to know that the texture won't be needed. This issue does not seem to be specific to DXC as I get the same output with glslang as seen here. Also to the point about context - using SPIRV-Cross reflection on the compiled MSL reports this resource as a
SPVC_RESOURCE_TYPE_STORAGE_IMAGE
and a dimensionality of bufferThe issue seems vaguely related to #950.
However I don't agree with the recommendation that instead of a RWBuffer we should use a RWStructuredBuffer or RWByteAddressBuffer since they produce the desired MSL buffer. This is not an option for us at Unity as our users can write their own shaders and thus the issue can't be fixed at the HLSL level.
The text was updated successfully, but these errors were encountered: