Search Unity

Question How can I get access to the native pointer for a shader (vert, frag)?

Discussion in 'General Graphics' started by joshuacwilde, Aug 28, 2022.

  1. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
  2. georgerh

    georgerh

    Joined:
    Feb 28, 2020
    Posts:
    72
    The pointer of the current shader is passed in kUnityRenderingExtEventBeforeDrawCall

    Code (CSharp):
    1. typedef struct UnityRenderingExtBeforeDrawCallParams
    2. {
    3.     void*   vertexShader;                           // bound vertex shader (platform dependent)
    4.     void*   fragmentShader;                         // bound fragment shader (platform dependent)
    5.     void*   geometryShader;                         // bound geometry shader (platform dependent)
    6.     void*   hullShader;                             // bound hull shader (platform dependent)
    7.     void*   domainShader;                           // bound domain shader (platform dependent)
    8.     int     eyeIndex;                               // the index of the current stereo "eye" being currently rendered.
    9. } UnityRenderingExtBeforeDrawCallParams;
    For example, for DX11, you'd cast it like this:
    static_cast<ID3D11VertexShader*>(params->vertexShader)

    So you have to make a draw call with every shader variant you care about.

    Haven't found an offical way to get the current compute shader, though. Maybe you can query the current compute shader with ID3D11DeviceContext::CSGetShader

    Update: Just for reference, there is also a shader compiler API, which I just found out, but it's D3D11 only.
     
    Last edited: Aug 29, 2022
    joshuacwilde likes this.
  3. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    ah ok interesting! This is very helpful! I think I can hack something together then here. I must admit I didn't think this would be possible, but it gives me hope seeing this.
     
  4. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,022
    This is only for DX11.
    I don't think you can do what you're describing.
    Maybe you could try calling some C# API for rendering from your plugin.
     
  5. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Dang. :(

    What I would like to do is insert draw calls into unity drawing using my existing shaders. Since we have all custom shaders and lighting, the unity draw commands are just pure overhead at this point. I would like to avoid them for a lot of our scene geometry.
     
  6. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,022
    Are you perf bound by this overhead?
    There's quite some caching involved on multiple levels, so it shouldn't be too expensive.
     
  7. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Yeah, I mean we handle all the culling, and lighting using our own methods, so at the end of the frame we just issue a bunch of drawmeshinstanced calls (tried with both the graphics version, and the command buffer version, but the performance is pretty similar either way). We are using multi threaded rendering, but even in the avg case, that puts us at about 2ms (on a newer flagship device) just for those graphics calls on the main thread. I would expect this to be around 0.6 ms given the render thread should be the one actually executing the calls, and the main thread should only have to handle copying the matrices into some GPU buffer (which I estimate to be around 0.6 ms based on some other tests I did). This is on GLES btw.

    Maybe I can grab the precompiled shaders some how at build time and just save the ones I will use at runtime for this optimized code path. Is there a way to do that do you know?

    I am really grasping at straws here, because the potential for a 2 ms (1.4 if I can do our own draw calls, and the other 0.6 moving buffer copying to a separate thread) perf gain is a pretty big deal and we really need it rn.
     
    Last edited: Aug 29, 2022
  8. georgerh

    georgerh

    Joined:
    Feb 28, 2020
    Posts:
    72
    On GLES, you can probably call
    Code (CSharp):
    1. GLint id;
    2. glGetIntegerv(GL_CURRENT_PROGRAM,&id);
    in kUnityRenderingExtEventBeforeDrawCall, unless the entire event is not triggered. Resources seem to be bound before the event.
     
  9. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Hmm, yeah I did a quick test, and it doesn't seem to be called, which seems like that is what @aleksandrk is saying.

    Also just found this : https://forum.unity.com/threads/unityrenderingextevent-not-being-called.1058963/
     
  10. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,022
    No, I don't think there's anything even close to this.

    Right, but they also need to communicate with each other - the main thread gathers the data needed for rendering and then sends it to the render thread, which issues the gfx api calls.
     
  11. joelv

    joelv

    Unity Technologies

    Joined:
    Mar 20, 2015
    Posts:
    203
    Not exactly what you are looking for, but...
    In Unity 2022.1 we introduced some new API that allows you to submit draws a bit more efficiently. I wrote about it here in an introductory post and we will be landing some improvements to this interface going forward.

    It's not perfect and there is still a bit of main thread cost around but we are seeing great improvements with regards to CPU performance with this. One of the reasons this interface was written originally was that DrawMeshInstanced had too much overhead.

    If you have the time please give it a go and let us know what you think.
     
    Sluggy likes this.
  12. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Yeah BRG looks cool. I tried to get the demos working but it didn't seem to work, I'll post in the other thread about it.
     
  13. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Turned out that Unity 2022 is still pretty buggy, and BRG still seems to have some issues on top of that. It was too risky to move to that, especially since we still have an issue with upgrading to 2021 as mentioned here : https://forum.unity.com/threads/how-can-i-get-master-texture-limit-logs-to-stop-spamming.1307874/

    I ended up being able to get the shaders using the hidden API "ShaderUtil.OpenCompiledShader". So now as part of my build process I get those shader variants, then save the source code so it can be later accessed and compiled for use natively.

    It took a bit of effort, but I am now able to use raw OpenGL ES calls to render my static scene geometry. This saves around 1.5 ms on the main thread, which is a massive savings for a mobile game. And that was savings on top of the savings I already got from doing all my own rendering manually instead of using GameObjects.

    It took a few weeks (more than I had hoped, but not bad). And for the first week or so, I was mostly running into Unity graphics bugs, like GetBufferPtr() not working on UBOs until calling SetData(), or the Unity CBUFFER_START macro just straight up not even working at all (cbuffer works, but you can't use the Unity macro for it). There was one other Unity bug I ran into, but don't recall atm.

    But now I have a system that is a lot faster than built in Unity, and I would encourage anyone that has lower level rendering experience to go for it (it isn't hard to learn if you don't know it yet), because it wasn't too difficult, and the performance benefits are totally worth it.
     
    c0d3_m0nk3y likes this.
  14. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,022
    You may want to use the CompileVariant public API instead.

    I'd say it's more in the "undocumented behaviour" area.

    You can force enable it by adding a
    #pragma enable_cbuffer
    .
     
    c0d3_m0nk3y and joshuacwilde like this.
  15. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Ah! This would have been very helpful to know. Thanks. I was parsing the shader and collecting the constant buffers manually. I'll switch over to this. Also, I see enable_cbuffer in the docs now. I missed that before.

    I was using ShaderUtil.OpenCompiledShader and it forces it to open the currently set code editor, so at one point I had probably over 1000 editors open. My RAM shot past 100 GB a couple times. I had to make a dummy app that the editor switches to while calling OpenCompiledShader(). But the new mentioned API will make things a lot easier.
     
  16. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Does the CompileVariant API use shader stripping? ShaderUtil.OpenCompiledShader is nice because it uses shader stripping and no changes there were needed.
     
  17. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,022
    No, it compiles a single variant at a time. You give it the keywords you want to compile with.
     
    joshuacwilde likes this.
  18. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    But how would i go about extending rendering? Isnt this what plugins are about.

    I'm interested in exposing dx12's ExecuteIndirect, and i'm struggling to setup the correct pipeline state.

    Graphics.Render/Draw Indirect doesnt provide everything ExecuteIndirect can do, hence the desire for a native plugin.
     
  19. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    What about opposite? Create shader in native plugin and use it in regular Unity C# code?

    Purpose is to use platform specific shader semantics that unity dont expose to us.