Hello Everyone,
I’m making my own hybrid Renderer and i would like to get an idea on how the HRV2 is passing the material property override to the shader.
Any explanation or link to an example/doc will be highly appreciated.
Thanks!
Hello Everyone,
I’m making my own hybrid Renderer and i would like to get an idea on how the HRV2 is passing the material property override to the shader.
Any explanation or link to an example/doc will be highly appreciated.
Thanks!
May I ask why?
The hybrid Renderer v2 is very unstable:
Plus is not supporting some features our game require:
I suspect the issues you are encountering are happening at the BatchRendererGroup layer, so I won’t go into how that works (you can find a description at the bottom of HR’s docs page). The other piece is that they use a struct containing 128 instances of DynamicComponentTypeHandle which are used for fetching the instanced properties which then get uploaded to compute buffers dictated by the BatchRendererGroup API.
Yeah the Hybrid Renderer V2 has a “GPU ECS” storage buffer which we update each frame with changed override data.
When drawing we have an index and meta data buffer so that we can fetch out the separate properties (indices are stored in the batch renderer group and passed on from there when drawing).
We are working towards addressing all of these points. Hybrid V2 is fully built on top of the SRP batcher, lightmaps should now be supported as well in the latest release. Terrain is still to be road mapped and we cannot guarantee GLES 3.1 support as it is now, but it’s still under discussion.
Anything else you have listed above sounds like bugs we would really like repro cases for, irregardless of if you decide to go for your own implementation or not. I’m especially interested in the perf drop you seem to have encountered in the URP shaders.
I’m very interested in how we can add our own custom properties to our custom rendering solutions.
My chunk renderer looks like this:
{
var entities = chunkMeshes.ToEntityArray(Allocator.TempJob);
var positionData = chunkMeshes.ToComponentDataArray<ChunkPosition>(Allocator.TempJob);
for (var i = 0; i < entities.Length; i++)
{
float3 position = positionData*.GetVoxelPosition(voxelDimensions).ToFloat3();*
*var positionFloat4x4 = float4x4.TRS(position, identityRotation, scale);*
*Matrix4x4 positionMatrix = positionFloat4x4;*
_var entity = entities*;*_
_*var renderMesh = EntityManager.GetSharedComponentData<ChunkMeshLink>(entity);*_
_*Graphics.DrawMesh(renderMesh.mesh, positionMatrix, renderMesh.material, 0);*_
_*}*_
_*positionData.Dispose();*_
_*entities.Dispose();*_
_*}```*_
_*By having a simpler system to render our entities, we have more control and can optimize data more anyway. There are alot more benefits then using the solution that fits all scenarios. I am just wondering how we can pass in data into the shaders, based on each mesh (per mesh) colors. I could easily grab the data with an EntityQuery, i'm just wondering how we can pass it into the Graphics utility.*_
_*I believe originally, the timing was around 8-12 ms per frame for the normal rendering systems, whereas the custom one for chunks went down to less then 1ms. (not exact numbers, just from memory, there was a significant speedup though)*_
Hi DreamingImLatios, I didn’t fully understand what you wrote, but it did something good. Somehow your comment triggered my brain neurons to finally solve the problem. I also double checked the unity docs on Graphics DrawMesh functions.
If anyone has any ideas to optimize it, please let me know haha. Could really use something like ‘GetSharedComponentDataFromEntity’ from unitys side. Early ECS had a query to get a list of shared components, but wasn’t added with newer ECS packages.
This was the solution for inserting custom properties into a custom render system.
public class MinivoxRenderSystem : SystemBase
{
private EntityQuery chunkMeshes;
private MaterialPropertyBlock materialPropertyBlock;
private UnityEngine.Color setColor;
protected override void OnCreate()
{
chunkMeshes = GetEntityQuery(ComponentType.ReadOnly<RenderChunkMesh>(), ComponentType.ReadOnly<MinivoxChunkRender>(),
ComponentType.ReadOnly<MaterialBaseColor>());
materialPropertyBlock = new MaterialPropertyBlock();
}
protected override void OnUpdate()
{
//var entities = chunkMeshes.ToEntityArrayAsync(Allocator.TempJob, out var jobHandle);
//Dependency = JobHandle.CombineDependencies(Dependency, jobHandle);
var entities = chunkMeshes.ToEntityArray(Allocator.TempJob);
var positions = GetComponentDataFromEntity<MinivoxChunkRender>(true);
var colors = GetComponentDataFromEntity<MaterialBaseColor>(true);
var scale = new float3(1,1,1);
var identityRotation = quaternion.identity;
for (var i = 0; i < entities.Length; i++)
{
var e = entities[i];
var position = positions[e].position;
var positionFloat4x4 = float4x4.TRS(position, identityRotation, scale);
Matrix4x4 positionMatrix = positionFloat4x4;
var entity = entities[i];
var renderChunkMesh = EntityManager.GetSharedComponentData<RenderChunkMesh>(entity);
var color = colors[e].Value;
setColor.r = color.x;
setColor.g = color.y;
setColor.b = color.z;
setColor.a = color.w;
materialPropertyBlock.SetColor("_BaseColor", setColor);
Graphics.DrawMesh(renderChunkMesh.mesh, positionMatrix, renderChunkMesh.material, 0, null, 0, materialPropertyBlock);
// Graphics.DrawMesh(renderChunkMesh.mesh, positionMatrix, renderChunkMesh.material, 0);
}
entities.Dispose();
}
}
There are ways to optimize…try to follow this thread
I did check the thread, but I wasn’t able to see any useful information. They mentioned Graphics.DrawMeshInstancedProcedural but I am using unique meshes, so that does not apply to me I believe.
Oh and something about copying directly to the compute buffer… sounded useful? Would that replace MaterialBlocks I wonder… Just not sure if that was what you meant?
Sorry, I did not read very carefully, if you strictly have unique meshes, much of the thread I copied does not apply, apologies.
You can use drawmeshinstancedindirect and use compute buffers instead of mpb. As usual dreaming’s advice is good…
I’m not familiar enough with the performance characteristics between DrawMesh and MaterialPropertyBlock, nor do I know enough about your target platforms, so I would have to see actual profiling characteristics before I can fully recommend trying swapping MaterialPropertyBlock with a compute buffer if you aren’t familiar enough with the technique.
But a different question, is there a reason you are not using Entities.ForEach in your most recent code snippet?
Ahh there was no good reason… I do remember grabbing this code from the earliest ECS days and haven’t looked at it enough since. I knew something was wrong. I apologize in advance for a long post.
As for the compute buffers, it should be quicker then using the material property blocks. I’m just a bit unfamiliar with them. I just checked the documentation:
https://docs.unity3d.com/ScriptReference/ComputeBuffer.SetData.html
I think this function is the best to use with ECS:
public void SetData(NativeArray data);
Not sure how to implement it when it comes to using it, but I found a tutorial that will surely help:
https://catlikecoding.com/unity/tutorials/basics/compute-shaders/
I was able to get the timing down for 250 Unique Grass Models to 0.8ms with setting the material property block, and 0.5ms without the material base being set (commented out). I am sure if I implement the ComputeBuffer using the NativeArray, while updating the NativeArray in a parallel job, and only recreating the NativeArray when the size changes. From experience, it should become close to 0.01ms for that amount of data being moved to parallel systems.
The catlike tutorial also went over how to set up compute shaders to work with shadergraph by using a custom function node. Seems a little complicated, but essentially we are just declaring data into the GPU using the ComputeBuffer class, and then using the Shader to read from it. So this lets us do a number of awesome things. I could essentially move any of my game logic to the GPU by setting up the data properly! a whole new world This will help me with my other roadblock of not being able to implement vertex skinning deformations. https://discussions.unity.com/t/824591
Using DrawMeshProcedural or DrawMeshIndirect should do wonders for my custom particle systems though. I am already eyeballing them results. The Sprite Renderer project linked was very impressive at showcasing the technology.
// todo: use attribute to make this work in editor
[UpdateInGroup(typeof(PresentationSystemGroup))]
public class MinivoxRenderSystem : SystemBase
{
private UnityEngine.MaterialPropertyBlock materialPropertyBlock;
private UnityEngine.Color setColor;
protected override void OnCreate()
{
materialPropertyBlock = new UnityEngine.MaterialPropertyBlock();
}
protected override void OnUpdate()
{
Entities.ForEach((Entity e, in RenderChunkMesh renderChunkMesh, in MinivoxChunkRender minivoxChunkRender, in MaterialBaseColor materialBaseColor) =>
{
var color = materialBaseColor.Value;
setColor.r = color.x;
setColor.g = color.y;
setColor.b = color.z;
setColor.a = color.w;
materialPropertyBlock.SetColor("_BaseColor", setColor);
UnityEngine.Graphics.DrawMesh(renderChunkMesh.mesh, minivoxChunkRender.positionMatrix, renderChunkMesh.material, 0, null, 0, materialPropertyBlock);
}).WithoutBurst().Run();
}
}
(I’m also not sure how we can get this to draw in editor when pausing the game, after selecting a GameObject it disappears, I was hoping there was an attribute we can add to the system, tried many so far)
Nope (it’s useful but not the best)
Begin\EndWrite (if used properly according to rules) it’s a fastest and best possible approach in Unity for writing into Compute Buffers. Depends on the hardware it always will be faster or equal (in very rare cases) to SetData but never slower.
What is the correct way to write data to Compute buffers using Begin \ EndWrite?
the ComputeBuffer.BeginWrite api returns a NativeArray, does that mean we can write to it in Bursted jobs?
if so, what is the best approach to access multiple ComputeBuffers NativeArrays in parallel jobs?
Thanks!
Snippet from our instanced indirect buffer handling. You could I guess complicate this by doing the copy in a job but really it’s just a memcpy under the hood so quite cheap. And if you needed to optimize buffer handling you would likely aggregate or use some other approach based on context. And for reasons other then trying to optimize out a memcpy.
NativeArray<IndirectInstanceData> bufferData = SourceBuffer.BeginWrite<IndirectInstanceData>(0, instanceCount);
NativeArray<IndirectInstanceData>.Copy(Matrices, 0, bufferData, 0, instanceCount);
SourceBuffer.EndWrite<IndirectInstanceData>(instanceCount);
I wanted to do it in parallel because I thought setting data from CPU to GPU was very slow. also in my case this process doesn’t happen every frame only when the map regions are Enabled / Disabled, but I am copying it to a high number of compute buffers (~ 100).
Is there a limit on the number of compute buffers used?
I’m in the same situation, dealing with thousands of possible updates. Having it all in a parallel job would be the best! I will probably try to work on this on the weekend, hopefully can get it to work such a way! Even in my above code, I wonder if anyone knows a way (a unity/systematic way) to check if the component was updated? That would optimize it a bit anyway.
I wonder if begin/endwrite is faster then unitys mesh functions for setting mesh vertex/uv/tri/normal data ![]()
Yes, you can execute your system when one of the concerned CD version has changed.
I’m sorry I can’t help you with this I’m not using procedural mesh rendering. GL ![]()