Skip to content
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

discard vs OpKill #34

Open
kg opened this issue Aug 29, 2020 · 2 comments
Open

discard vs OpKill #34

kg opened this issue Aug 29, 2020 · 2 comments
Labels
bug Something isn't working

Comments

@kg
Copy link
Contributor

kg commented Aug 29, 2020

discard in HLSL explicitly is specified as not terminating execution (https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/discard--sm4---asm-). In GLSL, it is apparently unspecified, and while NVIDIA continues execution AMD does not. In comparison, SPIR-V says that OpKill terminates execution of the fragment shader and it must be the end of a block.

The result of this is that right now if you have a SM3 shader that does 'discard' without immediately returning, mojoshader will produce a buggy or outright broken SPIRV shader that may cause a crash if validation is enabled or you run under renderdoc.

One easy way to do this by accident is to use 'clip' or 'discard' inside of a function. If the shader main() calls the function and the function discards, there's no way for the function performing the discard to 'return' out of the caller, and producing the required behavior (kill-then-return) might not be possible to do reliably at all unless you can be certain fxc is going to flatten everything and not put anything after the discard operation.

I don't know if this should be "fixed", and it's kind of weird semantically to have discard not terminate execution in SM3. But it's observable in SM4 and it is a "real" thing on hardware since other fragments in the execution group may not have discarded, so maybe this does matter. In my case I was able to find the problem shader pretty easily but it took some digging to figure out discard was the issue because the validation error I happened to get was something different entirely:

  • My shader did some texture fetches
  • Then it conditionally performed discard based on the fetch results
  • Then it initialized the output registers

In hlsl and glsl, this shader is valid and does not contain UB, since execution continues after the discard and the output regs are initialized. In SPIRV the output regs are not initialized since the discard killed execution and then you get a validation failure due to the uninitialized outputs.

@krolli
Copy link
Contributor

krolli commented Sep 12, 2020

As mentioned on discord, one possible solution would be to use SPV_EXT_demote_to_helper_invocation if supported. It would also require checking support and enabling VK_EXT_shader_demote_to_helper_invocation in Vulkan. I'm not sure whether there is similar extension for OpenGL, but since there is always GLSL as an option, it probably isn't that big a deal.

@flibitijibibo
Copy link
Collaborator

The OpenGL extension is simply GL_EXT_demote_to_helper_invocation. The extension is available on all drivers made after ~summer 2019, so it would be a reasonable extension to add if someone wants to implement it in MojoShader and FNA3D.

@flibitijibibo flibitijibibo added the bug Something isn't working label Jul 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants