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
Custom vertex format support for ParticleFX components #7866
Conversation
(defn vertex-overlay | ||
"Use a vertex layout together with an existing ByteBuffer. Returns a vertex buffer suitable | ||
for the `use-with` function. | ||
|
||
This will assume the ByteBuffer is an integer multiple of the vertex size." | ||
[layout ^ByteBuffer buffer] | ||
(assert layout) | ||
(assert (= 0 (mod (.limit buffer) (:vertex-size layout)))) | ||
(let [^ByteBuffer buffer (.duplicate buffer) | ||
limit (.limit buffer) | ||
count (mod limit (:vertex-size layout)) | ||
buffer-starts (buffer-starts limit layout) | ||
slices (b/slice buffer (map min (repeat limit) buffer-starts))] | ||
(->PersistentVertexBuffer layout count buffer slices (AtomicLong. count) not-allowed not-allowed))) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No used anywhere now
uint32_t buffer_size = dmParticle::GetVertexBufferSize(ctx->m_MaxParticleCount, dmParticle::PARTICLE_GO); | ||
// position : 3 | ||
// color : 4 | ||
// texcoord0 : 2 | ||
// page_index : 1 | ||
const uint32_t default_vx_size = sizeof(float) * (3 + 4 + 2 + 1); | ||
const uint32_t buffer_size = ctx->m_MaxParticleCount * 6 * default_vx_size; | ||
world->m_VertexBufferData.SetCapacity(ctx->m_MaxParticleCount * 6 * default_vx_size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have any particle "formats" now, the size of the vx buffer is only dependent on the vertex size.
obj = bld.stlib(features = obj.features + ['skip_asan'], | ||
includes = obj.includes, | ||
source = bld.path.ant_glob(['graphics_proto.cpp']) + bld.path.parent.ant_glob('proto/graphics/*'), | ||
protoc_includes = obj.protoc_includes, | ||
use = obj.use, | ||
target = 'graphics_proto_noasan') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Particle now depends on graphics being built first, since we use data structures from that library when producing particles.
engine/particle/src/particle.cpp
Outdated
case dmGraphics::VertexAttribute::SEMANTIC_TYPE_COLOR: | ||
{ | ||
if (info.m_NameHash == VERTEX_STREAM_COLOR) | ||
{ | ||
memcpy(write_ptr, &color, info.m_ValueByteSize); | ||
} | ||
else | ||
{ | ||
memcpy(write_ptr, info.m_ValuePtr, info.m_ValueByteSize); | ||
} | ||
} break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As previously mentioned, we need to know if the stream has the name "color" or not, if that is the case we output out the particle color and otherwise we put the binary value from the actual attribute.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this true for the other cases?
If the user set a POSITION or TEXCOORD attribute with name "extra", it will get the predefined position/uv values.
Or isn't is allowed, and should that a build error?
Also, do we want to look at the type?
E.g. if the user really wants to use a single uint32_t instead of float4 for color for bandwidth.
Later, I also feel this "write logic" should be standardized/centralized somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's true I suppose. The problem I ran into was that the "color" semantic is the only one that has any value in the editor since we decorate the property with a color widget. So the way we had originally setup the semantic types means that I can't decorate an attribute with a color widget, but maybe that's fine and should be a separate "edit type" value or something.. But yes, if we are to conform these vertex writing functions into a shared location I think we should produce the built-in data based on the semantic types.
Also, do we want to look at the type?
E.g. if the user really wants to use a single uint32_t instead of float4 for color for bandwidth.
Do you mean with the built-in semantic types? Because the data produced from the content pipeline is narrowed or expanded to the correct type, but the built-in types are not. For the built-in types we need to do data conversion between the floats (it's all float streams I think?) into whatever format. I think we should do it, but I'd rather try to get this PR in first and then take a stab at merging the vertex writing function in a separate PR.
Vector3 p0_local; | ||
Vector3 p1_local; | ||
Vector3 p2_local; | ||
Vector3 p3_local; | ||
|
||
if (use_local_position) | ||
{ | ||
p0_local = -x - y; | ||
p1_local = -x + y; | ||
p2_local = x - y; | ||
p3_local = x + y; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These will only be used if there is a stream with position_local semantic being used.
const char* config_key = MAX_PARTICLE_COUNT_KEY; | ||
|
||
if (format == PARTICLE_GUI) | ||
config_key = "gui.max_particle_count"; | ||
|
||
dmLogWarning("Maximum number of particles (%d) exceeded, particles will not be rendered. Change \"%s\" in the config file.", context->m_MaxParticleCount, config_key); | ||
res = GENERATE_VERTEX_DATA_MAX_PARTICLES_EXCEEDED; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each component has to deal with the error by themselves, since we don't know the source of what's being generated.
if (ix >= context->m_AttributeDataPtrs.Capacity()) | ||
{ | ||
void* new_ptr = malloc(ATTRIBUTE_WRAPPER_SIZE); | ||
context->m_AttributeDataPtrs.OffsetCapacity(1); | ||
context->m_AttributeDataPtrs.Push(new_ptr); | ||
return new_ptr; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to retain the same pointers between every simulation step, which other expanding array solutions will not guarantee, and I don't think we want to preallocate a large array for this since the scratch buffer approach is ONLY for the editor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found some minor niggles on the editor side, but they shouldn't be a lot of effort to fix!
context (:context pfx-sim) | ||
vbuf (vtx/wrap-vertex-buffer vertex-description :static raw-vbuf) | ||
all-raw-vbufs (vset (:raw-vbufs pfx-sim) emitter-index raw-vbuf)] | ||
(swap! pfx-sim-atom assoc :raw-vbufs all-raw-vbufs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit annoyed that we have to do it like this because the vertex-description
is not available until we have a GL context to compile the shaders with.
Perhaps in the future we could get the information from the output of the SPIR-V compiler and prepare the buffers before rendering?
Oh well. Some day...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it makes things more complicated for sure... But hm, I'll add a note about this for now and it will have to be a headache for the future I guess 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mainly the assert in the editor only C++ code needs attention.
Also some questions that may warrant some clarifications.
@@ -1060,7 +1081,7 @@ namespace dmGameSystem | |||
dmGui::NodeType node_type = dmGui::GetNodeType(scene, first_node); | |||
assert(node_type == dmGui::NODE_TYPE_PARTICLEFX); | |||
|
|||
uint32_t vb_max_size = dmParticle::GetMaxVertexBufferSize(gui_world->m_ParticleContext, dmParticle::PARTICLE_GUI) - gui_world->m_RenderedParticlesSize; | |||
uint32_t vb_max_size = dmParticle::GetMaxVertexBufferSize(gui_world->m_ParticleContext, sizeof(ParticleGuiVertex)) - gui_world->m_RenderedParticlesSize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what the best way is to define the particle budget now.
Previously it was bytes, but now that we can support multiple materials with different vertex formats, it might be relevant to think of #particles.
🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The usage of the sizeof(ParticleGuiVertex) seems a little strange now. Perhaps add a comment or two to show that the size is an estimate, and that the code is works ok?
E.g. the vertex count relies on that size
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what the best way is to define the particle budget now.
Previously it was bytes, but now that we can support multiple materials with different vertex formats, it might be relevant to think of #particles.
🤔
What budget are you referring to here? Both the GUI and the particle settings specify particle count (and components)
The usage of the sizeof(ParticleGuiVertex) seems a little strange now. Perhaps add a comment or two to show that the size is an estimate, and that the code is works ok?
E.g. the vertex count relies on that size
But for GUI the size isn't an estimate, it is what is allocated regardless of what material is used 🤔 this is because we don't support any custom vertex formats in GUI right now, so we don't use the configuration from the material.
|
||
uint32_t ro_vertex_count = vb_end - vb_begin; | ||
vertex_buffer.SetSize(vb_end - vertex_buffer.Begin()); | ||
vertex_buffer.SetSize(vb_size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a new pattern in the engine, but I think as communications go, we could make it clearer that between the SetCapacity() and SetSize() the array size is 0 and it's not really ok to write to the memory.
E.g. this could be changed to:
SetCapacity(max_size);
SetSize(max_size);
new_size = ...do work;
SetSize(new_size);
It's not a big deal in this case, but I recall having to change other places where that logic becomes problematic.
engine/particle/src/particle.cpp
Outdated
case dmGraphics::VertexAttribute::SEMANTIC_TYPE_COLOR: | ||
{ | ||
if (info.m_NameHash == VERTEX_STREAM_COLOR) | ||
{ | ||
memcpy(write_ptr, &color, info.m_ValueByteSize); | ||
} | ||
else | ||
{ | ||
memcpy(write_ptr, info.m_ValuePtr, info.m_ValueByteSize); | ||
} | ||
} break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this true for the other cases?
If the user set a POSITION or TEXCOORD attribute with name "extra", it will get the predefined position/uv values.
Or isn't is allowed, and should that a build error?
Also, do we want to look at the type?
E.g. if the user really wants to use a single uint32_t instead of float4 for color for bandwidth.
Later, I also feel this "write logic" should be standardized/centralized somehow.
Particle FX nodes can now utilise the custom vertex formats previously added to sprites. This enables you to pass custom data from the editor into shader attributes when rendering particles.
Fixes #7818
PR checklist