inteja
September 13, 2022, 8:01pm
1
@bghgary @Cedric I’m having trouble with instancedBuffers in Babylon React Native, specifically changing UVs per instance, which results in unpredictable changing of vertex position rather than UVs, but no error.
I found this open issue:
opened 10:50AM - 21 May 21 UTC
Hello! I'm trying to color mesh instances on `BabylonReactNative` scene using th… is example: https://playground.babylonjs.com/#YPABS1
But when i move code that works in playground to `BabylonReactNative` scene all instances keeps appearing black.
Is there any specific `BabylonReactNative` issues with `instancedBuffers` ?
But there’s also a mention of some work having been done on this in the merged PR:
BabylonJS:master
← CedricGuillemet:thinInstances
opened 09:30AM - 01 Feb 22 UTC
This PR needs https://github.com/BabylonJS/Babylon.js/pull/11900 that's in beta.… 6. package.json has been updated to reflect this change. VT need also this PR : https://github.com/BabylonJS/Babylon.js/pull/11917 this PR will be updated to reflect the change (NPM update) but it won't have any impact on Native code.
It will be difficult to fully understand changes without reading this comment.
# Context:
bgfx instancing is quite limited: only 5 vec4 attributes can be used for instancing (world matrix + color basically). These attributes named i_data0 .. 4 and D3D semantic texcoord3..7 are bound with the instance buffer data when available.
No more than 5 vec4 special streams! see https://github.com/bkaradzic/bgfx/issues/2008#issuecomment-578819111
Most of this work would have been unnecessary if it was possible to set `instance divisor` to any stream. Like OpenGL/WebGL allows (and I guess, any other graphic API). In bjs, any stream can have a divisor==1.
More over, streams for instancing are stored in a different buffer (with a lifetime of 1 frame). Where bjs treats any stream (instance or vertex buffer) the same.
The change is divided in 2 parts:
- make difference in recorded vertex streams so Native Engine knows if it must be stored as an instance buffer or a regular vertex buffer.
- Find and modify vertex attributes in shaders so bgfx can bind them as instanced buffer. This work is dependant on the rendering API.
# Vertex Buffer vs Instance Buffer
Now, instance divisor is forwarded to native when recording.
If the divisor is 1, there are pushed into the `m_vertexBufferInstanceRecords` map. A divisor == 0 is a regular vertex stream. When binding a vertex array for rendering, these buffer arrays are merged together into a bgfx instance buffer. This instance buffer lives for 1 frame (no destruction/storage). All the instance streams must be grouped inside this instance buffer in this pattern:
[i_dataA[0], i_dataB[0], i_dataC[0]....
i_dataA[1], i_dataB[1], i_dataC[1]...]
## Direct3D
Vec4 datas are stored in reverse order of semantics: https://github.com/bkaradzic/bgfx/blob/4581f14cd481bad1e0d6292f0dd0a6e298c2ee18/src/renderer_d3d11.cpp#L2701
Vertex buffer is stored in ascending order in `VertexArray` because `m_vertexBufferInstanceRecords` is `std::map<bgfx::Attrib::Enum, InstanceVertexBufferRecord>` so vertex buffer iteration is in reverse order in `VertexArray::SetVertexBuffers` as pointer to bgfx instance array is increased linearly. Same for OpenGL.
## Metal and OpenGL
Order of vec4 is the same as the location. No reverse iterator here.
# Shader attribute definition
## BabylonJS
Before https://github.com/BabylonJS/Babylon.js/pull/11900 there was 1 attribute named `color`. it either contained Mesh Vertex colors OR instance Color. This was simple: depending on divisor and bound vertex buffer, the same shader code use the color to render the mesh. It was not possible to have Mesh vertex color AND Instance color at the same time but it was not a need. But as native shaders need name to properly bind the attribute, name needs to be different and `instanceColor` has been added.
## Direct3D
Quite simple: in `VertexVaryingInTraverserD3D`, add new attribute names from BabylonJS, bind them as expected semantic for bgfx and rename them as well. As bgfx binds with semantics, the new `instanceColor` has the appropriate semantic.
## OpenGL
Binding is based on name, hence the renaming (i_data0...i_data5). As long as i_dataX and location ares in increasing order, it's fine. First instance attribute, first served.
## Metal
Binding based on attribute location. The difference here is bgfx is binding the last streams (a mix between OGL and D3D). It means, if for some reason (order of attributes in bjs shaders) attributes appear like this to the shader:
- location 0: instanceColor
- location 1: position
- location 2: world0
- location 3: world1
- location 4: world2
- location 5: world3
It needs to be remapped to :
- location 0: position
- location 1: instanceColor
- location 2: world0
- location 3: world1
- location 4: world2
- location 5: world3
That's why there are 2 passes. 1st one for stream, second for instanced ones.
## Other technical possibilities
It's always possible to have a different technical path but with time allowed, this is the best I could do.
Other possibilities:
- modify bgfx to allow instance divisor per stream. It would be in sync with webGL. This tiny bit of work cover a change in API contract and also reimplementing instances for a few platforms (D3D11, D3D12, Metal, OpenGL, Vulkan) while keeping the backward compatibility. This also implies discussing the work with Branimir before.
- discard HW instancing and add loop on instance instead. As many `submit` call as mesh to render. Instance attributes need to be replaced by uniform. This means some work in Shader Traversers and also some questions: if a vertex buffer is used with a divisor ==1, how is it known when compiling shader that 1 (or more) particular attribute must be replaced by a uniform? Then, what's the performance will be? Especially on mobile.
# Tests
All platforms tested OK!
Related : https://github.com/BabylonJS/BabylonReactNative/issues/232 #992
So I’m a bit confused about the status.
Here’s some PG examples of the types of instancedBuffers
usage I’m experiencing trouble with:
bghgary
September 13, 2022, 8:29pm
2
I’ll let @Cedric take this one.
1 Like
Cedric
September 14, 2022, 8:38am
3
Instancing with BabylonNative/BabylonReactNative is very limited because of constraints on our rendering back end.
Basically, you can only instanciate the world matrix and the color. That’s a total of 5 vec4.
For now, it’s not possible to instanciate uv or anything else.
If you need different UV per instance, I would try to store an UV offset in the color. Like an index in an atlas texture.
So, with this technic, you will have to write your vertex shader accordingly.
2 Likes
inteja
September 14, 2022, 9:34am
4
Thanks @Cedric
So just use a vec4 and instancedBuffer of stride length 4? Name can be anything, so I could call it uvOffset4?
Something like the following?
mesh.registerInstancedBuffer("uvOffset4", 4);
// Dynamically set uOffset and vOffset somewhere ...
instance.instancedBuffers.uvOffset4 = new Vector4(uOffset, vOffset, 0, 0);
Then in the shader, something like:
attribute vec4 uvOffset4;
vec2 uvOffset = vec2(uvOffset4[0], uvOffset4[1]);
vMainUV1 = uvUpdated + uvOffset;
Is that right?
Cedric
September 14, 2022, 9:45am
5
No, create a buffer named color
, just like if you want to instanciate colors.
Then, in the shader, get the color vec4 value and use it to store something else than colors.
inteja
September 14, 2022, 9:47am
6
Ok, I thought maybe the name doesn’t matter, but all good, I will use color.
Thanks again @Cedric !
inteja
September 14, 2022, 9:49am
7
Will just update that code snippet in case someone else stumbles on it.
mesh.registerInstancedBuffer("color", 4);
// Dynamically set uOffset and vOffset somewhere ...
instance.instancedBuffers.color = new Color4(uOffset, vOffset, 0, 0);
Then in the shader, something like:
attribute vec4 color;
vec2 uvOffset = vec2(color[0], color[1]);
vMainUV1 = uvUpdated + uvOffset;
Better?
Cedric
September 14, 2022, 9:58am
8
yes! and it leaves you 2 floats for more instancing infos
1 Like
inteja
September 14, 2022, 11:02am
9
Hi @Cedric , I’ve tried this in Babylon React Native but am now getting a shader compilation error:
BJS - [20:53:19]: Error: Exception in HostFunction: ERROR: 0:158: 'color' : redefinition
I am creating this shader as a material plugin for StandardMaterial
.
Then I tried commenting out attribute vec4 color;
and got the warning WARNING: Fail to create vertex buffer. Number of vertex buffers higher than max count or too many instanced streams.
Do I need to apply this workaround just with a simple shader, not a material plugin of a StandardMaterial
?
Cedric
September 14, 2022, 12:20pm
10
Do you have other buffers set as instance buffers?
can you share access to your PG code?
inteja
September 14, 2022, 7:31pm
11
No that color
instanced buffer is the only one I’ve set in the entire codebase.
Here’s a PG that replicates the issue, so I’ve done something wrong …
Blake
September 14, 2022, 8:11pm
12
Could try setting useVertexColors to false on the mesh so that the standard material doesn’t use the color attribute.
1 Like
inteja
September 14, 2022, 11:41pm
14
Hmmm, sorry me again @Cedric , but I just can’t seem to get this working in Babylon React Native. Here’s an adjusted PG showing exactly what I’m trying to do in the way it’s implemented in Babylon React Native (as per all the suggestions above). This code works fine in the playground, but in my Babylon React Native codebase the geometry is messed up and just unlit black but no errors or warnings.
Cedric
September 15, 2022, 11:45am
15
I’m taking a closer look.
Cedric
September 15, 2022, 1:02pm
16
I did it a bit differently:
I removed https://playground.babylonjs.com/#3BU7P2
and changed slightly CUSTOM_VERTEX_MAIN_END
Then, I used thinInstanceSetBuffer
And I got it working with native.
3 Likes
inteja
September 15, 2022, 7:03pm
17
Thanks for looking into this @Cedric
So I need to use thin instances rather than instances?