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

Drawing Thousands of Meshes with DrawMeshInstanced / Indirect in Unity #6

Open
Toqozz opened this issue Feb 26, 2021 · 10 comments
Open

Comments

@Toqozz
Copy link
Owner

Toqozz commented Feb 26, 2021

Discuss the blog post here; share your thoughts, improvements, or any issues you run into so that others can find them.

@thecrazy
Copy link

thecrazy commented Apr 30, 2021

I played around with your demo and was trying to use the standard shader but for some reason nothing is drawn unless I use the color variation shader.

On the blog it seems to suggest you were able to do it, any idea what may be causing this?

I'm not rendering anything fancy, just replaced the shader on your material to standard.

@Toqozz
Copy link
Owner Author

Toqozz commented May 1, 2021

Hmmm. My guess is that Unity has updated their shader in a new version and it no longer works with DrawMeshInstanced.
What version of Unity are you using?

I'll have more of a look into it now.

@Toqozz
Copy link
Owner Author

Toqozz commented May 1, 2021

I tested Unity 2021.1.5f1, 2020.3.6f1, and 2019.4.1f1 and it seems to work on all of them.

Note that the standard shader will not work while using DrawMeshInstancedIndirect, only DrawMeshInstanced. With DrawMeshInstancedIndirect, we had to define and fill a custom _Properties buffer, which Unity's standard shader has no knowledge of.

@thecrazy
Copy link

thecrazy commented Jul 1, 2021

Oh ok I will take a look at it again.
Thank you.

@StumpyTheAngry
Copy link

This is an excellent demo and has potentially solved a large headache I've been dealing with for a while. You mention several times in the blog post the idea that properties need to be byte-wise identical - are you able to expand on that a bit. I'm not that familiar with the term and am keen to understand how to avoid any issues.

@Toqozz
Copy link
Owner Author

Toqozz commented Sep 17, 2021

This is an excellent demo and has potentially solved a large headache I've been dealing with for a while. You mention several times in the blog post the idea that properties need to be byte-wise identical - are you able to expand on that a bit. I'm not that familiar with the term and am keen to understand how to avoid any issues.

So the Shader and the C# script are separate things and even run on separate hardware, and so they have no real type information about each other.

Essentially there's a data interpretation that needs to happen. Unity needs to send data from your C# code (system memory) to the GPU (gpu memory) to actually do anything with it on the GPU. The problem is that the GPU doesn't really have any information about what data is being sent, it just gets the raw bytes. So we create an identical structure on the GPU to be able to interpret it correctly -- this is what I mean when I say that the two structures need to be byte-wise identical.

Very simplified example with some comments:

// CSharp structure.
// The size of a Vector4 is 16 bytes -- 4 * 4 byte floats.
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/sizeof
struct MeshProperties {
    Vector4 color;    // Vector4s are 16 bytes -- 4 * 4 byte floats,  This is true or hlsl code too. 
}

// Create a buffer and send the bytes to the GPU here.., probably with `ComputeBuffer`
...
// HLSL structure
// The size of a float4 is also 16 bytes -- 4 * 4 byte floats, so this matches.
// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-scalar
struct MeshProperties {
    float4 color;
};

StructuredBuffer<MeshProperties> _Properties;

// Do something with MeshProperties here...
...

The sanity checks to do are:

  1. Are the sizes of the structs identical?
  2. Is the order of the fields within the structs identical?

If you get 1. wrong, you can get out of bounds memory access, which is never good. If you get 2. wrong, you can get garbage data. For example if you had 2 floats and you mess up the order, you'll obviously get a mismatch.


This is actually a useful concept for a variety of other programming stuff too. When communicating between programming languages the same sort of thing has to happen. You also have to be careful to make sure that the language itself doesn't mess anything up. For example, in Rust they have #[repr(c)], which ensures your Rust struct has the same structure (lol) as your C one.

@benb7760
Copy link

I'm fairly new to this aspect of Unity so forgive if this question is naive;

In the examples utilizing DrawMeshInstancedIndirect, you call SetBuffer on the material. Would this mean that this material can only be used for this particular set of mesh instances?

The scenario I am working towards is as follows: my material is actually a Texture Array, from which my shader selects textures based on each instance (among other things). If I used this approach, would this mean I would need to create a new material for this particular set of DrawMeshInstancedIndirect, and keep another copy for other rendering calls which utilize the texture array?

Could we instead call SetBuffer on a MaterialPropertyBlock and use the overload of DrawMeshInstancedIndirect, providing this MaterialPropertyBlock. Would this have the same effect, while allowing the material to be re-used elsewhere?

My main concern is that the Texture Array I am using will ideally serve quite a few different render calls which can all draw from the same array of textures, and if I need to create a new Material for each set of render calls, I will be increasing the memory considerably, as I am expecting the arrays to be considerable in size.

@Toqozz
Copy link
Owner Author

Toqozz commented Jan 28, 2022

I'm fairly new to this aspect of Unity so forgive if this question is naive;

In the examples utilizing DrawMeshInstancedIndirect, you call SetBuffer on the material. Would this mean that this material can only be used for this particular set of mesh instances?

The scenario I am working towards is as follows: my material is actually a Texture Array, from which my shader selects textures based on each instance (among other things). If I used this approach, would this mean I would need to create a new material for this particular set of DrawMeshInstancedIndirect, and keep another copy for other rendering calls which utilize the texture array?

Could we instead call SetBuffer on a MaterialPropertyBlock and use the overload of DrawMeshInstancedIndirect, providing this MaterialPropertyBlock. Would this have the same effect, while allowing the material to be re-used elsewhere?

My main concern is that the Texture Array I am using will ideally serve quite a few different render calls which can all draw from the same array of textures, and if I need to create a new Material for each set of render calls, I will be increasing the memory considerably, as I am expecting the arrays to be considerable in size.

Sorry for the late reply.

I’m a little unclear on what you’re trying to do. Are you saying that you have a mesh that you want to draw many times, but only some of them using the texture array, and the rest using DrawMeshInstanced, not touching the texture array at all?

You are free to use or ignore whatever data you want in the shader:

// Even ids use texture, odd use properties (don’t actually do this).
if (v.id % 2 == 0) {
    // Use and do texture array stuff.
} else {
    // Use and do properties buffer stuff.
}

If my understanding of what you want is correct, I would just do the simple thing and set the texture array on the material (note: on the material, not the material instance -- this should make it available to any instance, DrawMeshInstanced or otherwise), and then set a material keyword on the instanced materials to decide on which path you want to go through: https://docs.unity3d.com/Manual/shader-variants-and-keywords.html .

@tomorrowevening
Copy link

How could Standard shading lighting be implemented?

@elih1492
Copy link

elih1492 commented Aug 3, 2023

I tried copying the scripts into my own project and nothing was showing up, so I downloaded the project files and it's was working on there so I copied the project exactly (I imported the assets) into my project and still nothing is showing up when I play.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants