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
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.
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
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?
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
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
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.
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:
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 positionWS : TEXCOORD2;
half3 normalWS : TEXCOORD3;
float4 camFwdAndDistToCam : TEXCOORD4;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
In the vertex shader I start the code with the following snippet:
Varyings GrassVertex(Attributes input)
{
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
And the pixel shader I start with:
half4 GrassFragment(Varyings input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
With this I’ve made it work on URP. I hope this helps you with your problem.
Best regards,
Eduardo
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
Btw, it’s better to use the CommandBuffer.DrawMeshInstanced, rather than Graphics.DrawMeshInstanced inside a render pass
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
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
@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.
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:smile: