Skip to content

Replace MojoShader with ShaderConductor#7345

Closed
cpt-max wants to merge 32 commits intoMonoGame:developfrom
cpt-max:shaderconductor
Closed

Replace MojoShader with ShaderConductor#7345
cpt-max wants to merge 32 commits intoMonoGame:developfrom
cpt-max:shaderconductor

Conversation

@cpt-max
Copy link
Copy Markdown
Contributor

@cpt-max cpt-max commented Sep 2, 2020

MojoShader is limited to DX9, OpenGL 2 and GLSL 110. ShaderConductor can still target those versions, but it can also target the newest versions of DX, OpenGL and GLSL. This means shader model 4 and 5 are now also available for OpenGL. Here are more details about the supported shader models and shader stages.

Right now ShaderConductor is only used for converting HLSL to GLSL/ESSL, but it can also output DXIL for DX12, SPIR-V for Vulkan, or MSL for Metal, so this can be considered a first step towards supporting those platforms.

Here I list all the things I found so far when migrating shaders from MojoShader to ShaderConductor. Perhaps somebody wants to look into tools to help automate this process?

If you have some projects that target OpenGL, it would be great if you can test this PR, even if it only uses default shaders, as those have also been converted.

MojoShader has not yet been removed, and you can actually mix and match Mojo and Conductor shaders in the same project. To switch a shader back to Mojo you just add the MOJO define to the effect processor in the MGCB editor. This should help isolate problems with this PR. Maybe it makes sense to keep Mojo alive this way until MonoGame 4 is out?

MonoGame.Effect.Compiler now includes three C++ dlls which have been compiled from this ShaderConductor fork, which adds reflection support, plus a few little tweaks.
Those dll's should eventually be moved to the ThirdParty/Dependencies folder.

This PR also contains #7242. The reason for this is that supporting those new shader stages for OpenGL is the main reason I did all of this in the first place, and it safes me a little bit of time this way, so it's probably best to merge that PR first.

Anyone wanting to try out this branch, please use #7533 instead. It fully contains this branch and is kept up to date.

cpt-max and others added 24 commits July 20, 2020 05:26
…arameter updating when uniform buffers are not available.
@cpt-max
Copy link
Copy Markdown
Contributor Author

cpt-max commented Sep 2, 2020

Yeah, I did not test on Mac and the DX unit tests don't work for me, as it always fails to create the DX device. If somebody can give me a hint how to fix that?

@cpt-max
Copy link
Copy Markdown
Contributor Author

cpt-max commented Sep 7, 2020

I set it up on a Mac to fix the failing unit tests. It's all good now.

@cpt-max
Copy link
Copy Markdown
Contributor Author

cpt-max commented Sep 7, 2020

On Mac the OpenGL version is currently always 2.1, which means no SM 4 or 5. It's almost certainly related to this.

@deccer
Copy link
Copy Markdown

deccer commented Sep 21, 2020

Yeah, I did not test on Mac and the DX unit tests don't work for me, as it always fails to create the DX device. If somebody can give me a hint how to fix that?

If the device is created with debug flags (DeviceCreationFlags.Debug), you need a feature installed called Graphics Tools when on windows 10 you can find it usually under -> hit win-key -> type "Manage optional features" -> find/add Graphics Tools in there. Lower versions of windows only needed the DXSDK or latest Windows SDK installed.

@cpt-max
Copy link
Copy Markdown
Contributor Author

cpt-max commented Sep 22, 2020

Thanks for you reply @deccer. I already had the Graphics Tools installed. Looking into it a bit more, the device creation fails because it's trying to create a reference device. If I change it to a regular hardware device, it works.
I'm reading on the internet that this tends to happen on systems with integrated graphics. I have an RX 5700XT, so I shouldn't have this problem. Seems to be a driver problem or something, probably specific to my GPU or system configuration.

@cpt-max
Copy link
Copy Markdown
Contributor Author

cpt-max commented Sep 22, 2020

For people wanting to try this PR, it's best to use PR #7352 instead. It fully contains this PR, but it has a few additional fixes. Maintaining multiple branches takes some extra time, so I'll only update this branch, if I know it's really needed.

@minimalism
Copy link
Copy Markdown
Contributor

This is awesome! I took a stab at migrating my fairly shader-heavy project, and it works well. Migrating takes a bit of effort though.
I noticed allocating a shader off the mainthread (e.g. calling contentManager.ReadAsset<Effect>(pathToShader, null)) gives GL error 1282 (InvalidOperation). IMHO this might be a good change but previously it was wrapped with a Threading.BlockOnUIThread(() => call.

I also noticed some peculiarities with ShaderConductor:

  • ShaderConductor.Compile sometimes attempts to access protected memory instead of outputting errors. This happens in some of my bigger shaders, maybe the error/warning list is too big and overflows something. I have not seen it fail to compile a valid shader yet.
  • Shaders with "discard;" give the error "GL_EXT_demote_to_helper_invocation is only supported in Vulkan GLSL."
  • As you mentioned, COLOR0 needs to be SV_TARGET0, but also vertex shaders need to output a SV_POSITION0 and the pixel shader needs to receive it. It compiles either way but the results are terribly broken in the former case. DEPTH to SV_DEPTH also.
  • Semantics in general seem to be a bit pickier. You can no longer omit some semantics from the VS/PS parameter list. In MojoShader it was possible to pass a matrix like float4x4 instanceTransform : TEXCOORD1, float otherValue : TEXCOORD5 and this would work, but to get this to work in ShaderConductor I needed to explicitly pass each float4 row individually as TEXCOORD1, 2, 3 and 4.

@cpt-max
Copy link
Copy Markdown
Contributor Author

cpt-max commented Sep 26, 2020

Thanks for your feedback @minimalism, very much appreciated.

Migrating takes a bit of effort though.

Yeah, a bit of a monkey job. Some automation would be nice.

I noticed allocating a shader off the mainthread (e.g. calling contentManager.ReadAsset<Effect>(pathToShader, null)) gives GL error 1282 (InvalidOperation). IMHO this might be a good change but previously it was wrapped with a Threading.BlockOnUIThread(() => call.

Can you tell me which BlockOnUIThread is not called anymore, that used to be called. I don't think I removed/circumvented any of those. Which GL call is failing exactly?

ShaderConductor.Compile sometimes attempts to access protected memory instead of outputting errors. This happens in some of my bigger shaders, maybe the error/warning list is too big and overflows something. I have not seen it fail to compile a valid shader yet.

That's almost certainly a bug in DirectXShaderCompiler or SPIRV-Cross. If you can send me a crashing shader, I should be able to find out where the crash is happening, and open an issue in the respective repository. Both projects are quite active. I reported a crashing bug before and it was fixed a few weeks later.

Shaders with "discard;" give the error "GL_EXT_demote_to_helper_invocation is only supported in Vulkan GLSL."

I'll see if I can find out more about that in my next ShaderConductor debugging session.

As you mentioned, COLOR0 needs to be SV_TARGET0, but also vertex shaders need to output a SV_POSITION0 and the pixel shader needs to receive it. It compiles either way but the results are terribly broken in the former case. DEPTH to SV_DEPTH also.

Thanks, will add those to the migration document.

Semantics in general seem to be a bit pickier. You can no longer omit some semantics from the VS/PS parameter list. In MojoShader it was possible to pass a matrix like float4x4 instanceTransform : TEXCOORD1, float otherValue : TEXCOORD5 and this would work, but to get this to work in ShaderConductor I needed to explicitly pass each float4 row individually as TEXCOORD1, 2, 3 and 4.

Alright, sounds like something for DirectXShaderCompiler. If the HLSL->DXIL compilation can handle omitting those semantics (not sure if it does) than the HLSL->SPIRV should also handle it.

@minimalism
Copy link
Copy Markdown
Contributor

minimalism commented Sep 27, 2020

Can you tell me which BlockOnUIThread is not called anymore, that used to be called. I don't think I removed/circumvented any of those. Which GL call is failing exactly?

The GL calls in PlatformInitialize (and PlatformClear) in ConstantBuffer.OpenGL.cs. On my device, when calling these methods from a background thread GL.GenBuffers will always return -1 and CheckGLError will always throw 1282.
On closer inspection there wasn't a BlockOnUIThread call previously because there weren't any OpenGL calls in these methods.

That's almost certainly a bug in DirectXShaderCompiler or SPIRV-Cross. If you can send me a crashing shader, I should be able to find out where the crash is happening, and open an issue in the respective repository. Both projects are quite active. I reported a crashing bug before and it was fixed a few weeks later.

Sure, this digitalrune shader reproduces the error for me: https://gist.github.com/minimalism/d27e1b20274f0aa646235a9d061401ea
Building this shader on my device causes a System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt at ShaderConductor.Compile.
That shader has an error at ln 184. Replacing that line with the one below it causes the compilation to succeed.

PS Note that this shader also exhibits one of the issues I mentioned above, where the VS outputs { TEXCOORD, SV_POSITION } but the PS parameter list only accepts { TEXCOORD }. This worked in Mojoshader. It compiles fine in ShaderConductor but doesn't seem to render anything to screen.

Hope this is useful :)

@cpt-max
Copy link
Copy Markdown
Contributor Author

cpt-max commented Sep 28, 2020

The GL calls in PlatformInitialize (and PlatformClear) in ConstantBuffer.OpenGL.cs. On my device, when calling these methods from a background thread GL.GenBuffers will always return -1 and CheckGLError will always throw 1282.

That makes sense, for every ConstantBuffer a uniform buffer object is created. MojoShader didn't need those. I'll just wrap a BlockOnUIThread around these calls then.

@cpt-max
Copy link
Copy Markdown
Contributor Author

cpt-max commented Sep 30, 2020

I updated #7352
Creating shaders on a background thread sould work now (didn't test it).
Discarding of pixels was fixed with an update of DirectXShaderCompiler.
I also simplified your crashing shader and opened an issue for it.

PS Note that this shader also exhibits one of the issues I mentioned above, where the VS outputs { TEXCOORD, SV_POSITION } but the PS parameter list only accepts { TEXCOORD }

You only mentioned that matrix parameters have to be passed row by row. Do you mean that MojoShader accepted SV_POSITION as a pixel shader parameter, but ShaderConductor doesn't?

@minimalism
Copy link
Copy Markdown
Contributor

minimalism commented Oct 2, 2020

Nice! I see they've already merged a fix for the error message error, that will save some frustration for future users no doubt. I'll test the shader on background thread thing even though it's questionable if this practice should really be supported...

You only mentioned that matrix parameters have to be passed row by row. Do you mean that MojoShader accepted SV_POSITION as a pixel shader parameter, but ShaderConductor doesn't?

This situation is what I tried to describe when I said that the pixel shaders needs to receive the SV_POSITION semantic, whereas in MojoShader it was sufficient to just return it from the VS.
Building the same shader with ShaderConductor then inspecting it in NSight revealed that the shader never succeeds in shading any pixels, and always tries to draw off-screen somewhere. When built with MojoShader it would shade the pixels at the position returned by the VS. I don't know which behavior is correct. The shader was originally written by the DigitalRune guys who may have relied on some HLSL/DX9 specific behavior.
To get the shader to behave the same way in ShaderConductor as it did in MojoShader I had to add the SV_POSITION parameter to the pixel shader parameterlist,
I can only speculate as to the reasons, maybe ShaderConductor does some treeshaking and reaches the conclusion that since the pixel shader doesn't receive the SV_POSITION semantic, it can be pruned from the VS.

@cpt-max
Copy link
Copy Markdown
Contributor Author

cpt-max commented Oct 12, 2020

Nice! I see they've already merged a fix for the error message error, that will save some frustration for future users no doubt.

The PR is now updated and contains this fix.

I'll test the shader on background thread thing even though it's questionable if this practice should really be supported...

I'm pretty sure content loading from multiple threads was already supported in XNA, so MonoGame should do it too.

the pixel shaders needs to receive the SV_POSITION semantic, whereas in MojoShader it was sufficient to just return it from the VS.

I just tested this with a very simple vertex and pixel shader. I don't have that problem. It makes no difference if SV_POSITION is part of the pixel shader parameters or not. Maybe it only shows in more complex shaders, or it has been fixed in the new version.

In MojoShader it was possible to pass a matrix like float4x4 instanceTransform : TEXCOORD1, float otherValue : TEXCOORD5 and this would work, but to get this to work in ShaderConductor I needed to explicitly pass each float4 row individually as TEXCOORD1, 2, 3 and 4.

This too I can't reproduce. It works fine for me. Could the update have fixed this too?

@MrScautHD
Copy link
Copy Markdown

This Pr looks amazing don't give it up!

@cpt-max
Copy link
Copy Markdown
Contributor Author

cpt-max commented May 12, 2024

Closing this PR, as it's contained in #7533

@cpt-max cpt-max closed this May 12, 2024
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