Search Unity

Graphics.DrawMeshInstanced not working on URP?

Discussion in 'Universal Render Pipeline' started by Barkley, Jan 12, 2020.

  1. Barkley

    Barkley

    Joined:
    Dec 1, 2012
    Posts:
    76
    Hi,
    I used to have a system to render grass (in a special way) where we used Graphics.DrawMeshInstanced to optimize its rendering. When I ported to URP this system is not working anymore.
    I'm calling the Graphics.DrawMeshInstanced method in the Update(). I'm not sure if this is not working because it's not supported in URP anymore or because maybe I need to do something special in the shader or anywhere for it to work (I've already marked the materials 'Support GPU Instancing' checkbox).
    I'm considering adding a render pass to do this, but I'd rather avoid it (to avoid re-doing it). Still, if it's going to be more efficient to do it like that I'd be happy to do so.
    Any information on this subject will be most welcome.
    Best regards,
    Eduardo
     
  2. phil_lira

    phil_lira

    Unity Technologies

    Joined:
    Dec 17, 2014
    Posts:
    584
    SRP doesn't support any rendering code in Updates that target the camera target because the camera target is not setup at this point.

    I'd say to inject a render pass. If you use the template we provide at Create menu this should be as simple as copying the code you have to the execute method of the renderer feature script then add the feature to the renderer.
     
  3. Barkley

    Barkley

    Joined:
    Dec 1, 2012
    Posts:
    76
    Thanks for the info! It makes a lot of sense. I've already created a few RenderPasses for other stuff so it shouldn't take me long to get that working. I just wanted to be sure that was the right way to go.

    BTW, I would suggest (and I understand this will be low on your list of priorities unless it's pretty easy which I guess it isn't) that you show some sort of warning when a Graphics.DrawXXX method is called from the wrong point. Just like you show warnings when you try to access thread unsafe functions from worker threads.

    Cheers,
    Eduardo
     
  4. phil_lira

    phil_lira

    Unity Technologies

    Joined:
    Dec 17, 2014
    Posts:
    584
    Upon further inspection, it seems my above statement is incorrect. DrawXXNow is that one that doesn't work. DrawXX should work as it defers rendering to a later step.

    Could you submit a bug about this?
     
    Barkley and MadeFromPolygons like this.
  5. Barkley

    Barkley

    Joined:
    Dec 1, 2012
    Posts:
    76
    Hi Phil,

    Thanks for the info! I can report a bug, but my project is huge (almost 10GB of data) and I'm not currently in a position to be able to start moving things to a new project to get the minimum viable project that reproduces the bug. Is there a way we could go around this? (I would be happy to send a USB drive with the project in it if necessary).

    I'm also experiencing another bug whereby every now and then graphics get corrupted and textures start to switch randomly to other textures in materials. It fixes closing the editor and restarting it, but it's pretty annoying (and it didn't happen before URP). So the same project could serve for 2 bugs :)

    Cheers,
    Eduardo
     
  6. Barkley

    Barkley

    Joined:
    Dec 1, 2012
    Posts:
    76
    Hi Phil,

    I finally found out where the problem was: the shader. My bad for not testing that first. I did implement the render pass and then had some issues with the shadow casting (I don't know how to do shadow casting for geometry rendered through a render pass).

    In order for instancing to work on URP you need to add the instancing macros to your shader AND (and that's what's not so obvious) the output stereo macros too (both in the pixel and vertex shaders).

    If you don't have that then only the first instance of the object is rendered (I believe).

    Thanks!
    Eduardo
     
  7. MorgonKaffe

    MorgonKaffe

    Joined:
    Apr 9, 2018
    Posts:
    5
    If you have time, could you please explain what you mean with output stereo macros? I'm also trying to render stuff through DrawMeshInstanced, but it only renders one instance of the object for me. This thread is the closest thing I've found to a solution to my problem. I'm not good at shaders, so any help would be appreciated.
     
  8. Barkley

    Barkley

    Joined:
    Dec 1, 2012
    Posts:
    76
    Hi,

    I'll try to elaborate. In the Attributes (the structure you use as an input for the vertex shader) you have to include the following macro:

    UNITY_VERTEX_INPUT_INSTANCE_ID

    In the Varyings (the structure you use as an output of the vertex shader and input for the pixel shader) you have to include the following macros:

    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO

    Both macros are part of the struct declaration and should go without semicolon at the end. An example:

    Code (CSharp):
    1.            
    2.             struct Attributes
    3.             {
    4.                 float4 positionOS        : POSITION;
    5.                 float3 normalOS            : NORMAL;
    6.                 float4 tangentOS        : TANGENT;
    7.                 float2 uv                : TEXCOORD0;
    8.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    9.             };
    10.  
    11.             struct Varyings
    12.             {
    13.                 float4 positionCS               : SV_POSITION;
    14.  
    15.                 float2 uv                        : TEXCOORD0;
    16.                 float3 positionWS                : TEXCOORD2;
    17.                 half3 normalWS                    : TEXCOORD3;
    18.  
    19.                 float4 camFwdAndDistToCam        : TEXCOORD4;
    20.  
    21.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    22.                 UNITY_VERTEX_OUTPUT_STEREO
    23.             };
    24.  
    In the vertex shader I start the code with the following snippet:

    Code (CSharp):
    1.  
    2.             Varyings GrassVertex(Attributes input)
    3.             {
    4.                 Varyings output = (Varyings)0;
    5.  
    6.                 UNITY_SETUP_INSTANCE_ID(input);
    7.                 UNITY_TRANSFER_INSTANCE_ID(input, output);
    8.                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
    9.  
    And the pixel shader I start with:

    Code (CSharp):
    1.  
    2.             half4 GrassFragment(Varyings input) : SV_Target
    3.             {
    4.                 UNITY_SETUP_INSTANCE_ID(input);
    5.                 UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
    6.  
    With this I've made it work on URP. I hope this helps you with your problem.

    Best regards,
    Eduardo
     
    jolyne_zwx likes this.
  9. MorgonKaffe

    MorgonKaffe

    Joined:
    Apr 9, 2018
    Posts:
    5
    Thanks for the response Eduardo. It was really clear and made me understand this much better. Sadly I still couldn't get it to work right now. There doesn't seem to be any varying struct declared in this shader, I can't even find a pixel shader in it. The vertex shader however has VertexOutputDeferred as output. I guess I'll try to rewrite it from scratch, it's a 2 year old shader that should be used to render lots of animated character, but I can only get it to render a single, not animated character. Shading is hard ^^

    Best regards,
    Oscar
     
  10. liiir1985

    liiir1985

    Joined:
    Jul 30, 2014
    Posts:
    147
    Btw, it's better to use the CommandBuffer.DrawMeshInstanced, rather than Graphics.DrawMeshInstanced inside a render pass
     
  11. Barkley

    Barkley

    Joined:
    Dec 1, 2012
    Posts:
    76
    I understand that using CommandBuffer.DrawMeshInstanced will only render it in the pass you're currently rendering while using Graphics.DrawMeshInstanced will render it in every pass the shader needs (ie shadows, etc). So I suppose it's best to use the one that better fits your needs (if you only need the meshes in one pass then use CommandBuffer, otherwise it may be easier to use Graphics).

    Maybe I'm wrong with this though. It would be good if someone could confirm.

    Cheers,
    Eduardo
     
  12. liiir1985

    liiir1985

    Joined:
    Jul 30, 2014
    Posts:
    147
    Graphics.DrawMeshInstanced will make the draw instantly hence blocks the main thread and waits the draw operation to be completed, which is bad for the performance, CommandBuffer.xxxx on the other hand will queue the draw operation and do it in the render thread or graphic jobs, so it costs no time on main thread. Using CommandBuffer in a render pass is as simple as Graphics API by simply replace the word "Graphics" to the commandbuffer instance you can get from the pass context
     
  13. Barkley

    Barkley

    Joined:
    Dec 1, 2012
    Posts:
    76
    @liiir1985 I don't think that's correct based on the comment from @phil_lira above that says "DrawXXNow is that one that doesn't work. DrawXX should work as it defers rendering to a later step." (I've highlighted the important part)

    It would be good if someone from Unity could confirm what's the actual behavior.
     
    LapidistCubed likes this.
  14. MUGIK

    MUGIK

    Joined:
    Jul 2, 2015
    Posts:
    481
    Also if you are writing a custom shader with multiple pipelines support,
    don't forget to move SubShader that contains "RenderPipeline" = "UniversalPipeline" to the top!

    Spent several hours on this stupid thing:D
     
    WillNode, dongch007 and SunnySunshine like this.