Page MenuHome

EEVEE/Workbench: support Curves attributes rendering
ClosedPublic

Authored by Kévin Dietrich (kevindietrich) on May 11 2022, 6:49 AM.

Details

Summary

This adds support to render Curves attributes in Workbench/EEVEE.

Similarly to the hair length attribute, each attribute is stored
in a texture derived from a VBO. As the shading group needs
the textures to be valid upon creation, the attributes are
created and setup during its very creation, instead of doing it lazily
via create_requested. Doing it via create_requested was
also a bit tricky, as contrary to the mesh batch, we cannot
rely on DRW_batch_requested to tell us if attributes need
to be updated or else.

Uniforms are added to the shaders to tell in which scope the
attribute is in (for each point vs. for each curve). The code
generation and shaders had to be modified to pass the uniforms
to each of the attr_load_* functions, but only if the shader is
for curves (HAIR_SHADER is defined). Uniforms are also
declared during code generation as declaring them in the
common_hair_lib is not possible due to their names being
based on the attributes' "safe" names. Using the name is
required to ensure that there is no conflicts between
shaders or between old and new attributes when tweaking
shaders.

Since point attributes need refinement, and since attributes are all
cast to vec4/float4 to account for differences in type conversions
between Blender and OpenGL, the refinement shader for points can be
used as is. The point attributes are stored for each subdivision level
in CurvesEvalFinalCache. Each subdivision level also keeps track of the
attributes already in use so they are properly updated when needed.

Some basic garbage collection was added similar to what is done
for meshes: if the attributes used over time have been different
from the currently used attributes for too long, then the buffers
are freed, ensuring that stale attributesare removed.

Common utilities with the mesh code for handling requested
attributes were moved to a separate file.


Test file :


All types except FLOAT2 were tested, as this is not yet supported by geometry nodes. I would need to check if one of my alembic files has such attributes.

Diff Detail

Repository
rB Blender
Branch
curves_attribute_rendering (branched from master)
Build Status
Buildable 22100
Build 22100: arc lint + arc unit

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
Kévin Dietrich (kevindietrich) requested review of this revision.May 11 2022, 6:49 AM
  • Add support for point domains.
  • Deduplicate code with mesh attributes handling
  • Fix memory leak.

There is still some update issues due to the scope
detection uniforms in the shaders.

Kévin Dietrich (kevindietrich) retitled this revision from EEVEE/Viewport: support Curves attribute rendering [WIP] to EEVEE/Viewport: support Curves attributes rendering [WIP].May 13 2022, 8:48 AM
Kévin Dietrich (kevindietrich) edited the summary of this revision. (Show Details)
Kévin Dietrich (kevindietrich) edited the summary of this revision. (Show Details)
  • Fix missing update when tweaking shaders.
Kévin Dietrich (kevindietrich) retitled this revision from EEVEE/Viewport: support Curves attributes rendering [WIP] to EEVEE/Viewport: support Curves attributes rendering.May 13 2022, 9:08 AM
Kévin Dietrich (kevindietrich) edited the summary of this revision. (Show Details)
source/blender/draw/intern/draw_cache_impl_curves.cc
121–124

Just realized, this would need to be properly defined.

Hans Goudey (HooglyBoogly) requested changes to this revision.May 13 2022, 10:53 AM

Thanks for working on this, it's exciting to see this progressing. I do think this patch is missing a fundamental piece of functionality, and some further changes would be helpful.


This patch doesn't seem to do any evaluation/interpolation of the attributes on control points. You can see that by increasing "Additional Subdivision".
It looks like the evaluated point index is used to lookup in the original point array, which doesn't really work.
I wouldn't recommend using the "Random Value" node in your test file, we can't tell if things are working that way.
If another test file is useful, here is one. The ground truth is visible with the curve to mesh node enabled, and tweaking the "Additional Subdivision" value breaks things.


I see that using void * for render_mutex makes it easier to share with meshes, but that's not a very clean solution IMO.
Another way to do it that would be useful in other situations to is adding a runtime struct for curves:

class CurvesRuntime {
 public:
  mutable std::mutex render_mutex;
};

That can be stored directly in DNA with the same method as CurvesGeometryRuntimeHandle.
We could add any runtime data to that struct that doesn't fit well in CurvesGeometryRuntime.
I think using std::mutex is preferable, especially because it is used in existing curves code.

For thread safety, the lock could just be outside of drw_attributes_merge.


I also added a comment to the attribute reading and conversion code; I think it can be much simpler.

source/blender/draw/intern/draw_attributes.c
4

It makes sense to add new files here as C++ files IMO.

source/blender/draw/intern/draw_attributes.h
32

Adding curves specific fields here suggests to me that the abstraction is premature. Can we store this elsewhere or avoid the abstraction in this case?

73–77
78

Usually type names use pascal case: AttributeTypeConvertor
Might make sense to add GPU or DRW at the front though, since this has a very specific purpose.

source/blender/draw/intern/draw_cache_impl_curves.cc
45–50

This section is empty

264–395

I see why you did it this way, but most of this attribute retrieval strikes me as a solved problem that can be written at a much higher level without a performance cost. I would suggest keeping the actual attribute name around and using it to retrieve the data from CurveComponent with the specified type.

Some variant of the following code will retrieve the attribute and convert it to the color type, using the existing attribute API. In the future the CurveComponent won't be necessarily because the same attribute API will be accessible directly on bke::CurvesGeometry

CurveComponent component;
component.replace(const_cast<Curves *>(&curves), GeometryOwnershipType::ReadOnly);

...

/* TODO(@kevindietrich): float4 is used for scalar attributes as the implicit conversion done
 * by OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following
 * the Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a
 * similar texture as for volume attribute, so we can control the conversion ourselves. */
blender::VArray<ColorGeometry4f> attribute = component.attribute_get_for_read<ColorGeometry4f>(
    request.sampler_name, request.domain, {0.0f, 0.0f, 0.0f, 1.0f});

MutableSpan<ColorGeometry4f> vbo_span{
    static_cast<ColorGeometry4f *>(GPU_vertbuf_get_data(attr_vbo)),
    component.attribute_domain_num(request.domain)};

attribute.materialize(vbo_span);
source/blender/draw/intern/draw_curves_private.h
67

Some comments on these fields would help distinguish them, it's not obvious without further investigation.

source/blender/makesdna/DNA_curves_types.h
157

A comment should explain the actual reasoning for the mutex, rather than "some thread-safety". I guess it's something like "The same curves might be used for multiple objects with different materials" or something? I'm not sure though, because I thought the draw manager's object iteration was single threaded. batch_cache would have the same problem.

This revision now requires changes to proceed.May 13 2022, 10:53 AM
source/blender/draw/intern/draw_attributes.h
55–59

Also, these is_curves arguments on functions used for meshes also suggest to me that this isn't a proper abstraction, especially if they're added from the start.

Kévin Dietrich (kevindietrich) marked 9 inline comments as done.May 20 2022, 7:28 AM

I am not too sure about using std::mutex (unless it is unanimously requested). The reason being that I don't like that synchronization primitives being used all over the place, even if it is simple. This is less error prone to encapsulate the locking/unlocking inside of drw_attributes_merge.

source/blender/makesdna/DNA_curves_types.h
157

Multi-threading is indeed not implemented currently. This is added because there is one for meshes as well. But even the mesh one is not useful currently, as there is no multi-threading. (The addition of the mesh mutex was requested by the module owner back then, so I am also preemptively adding one here.)

Kévin Dietrich (kevindietrich) marked an inline comment as done.

Address review comments.

Actually implement point attribute refinement. Forgetting about was
a bit embarrassing, my apologies. Since attributes are all cast to
vec4/float4 to account for differences in type conversions between
Blender and OpenGL, the refinement shader for point can be used as
is.

The point attributes are stored for each subdivision level in
CurvesEvalFinalCache. This also now contains attr_used so
attributes are properly updated when changing the subdivision level.

Some basic garbage collection was added similar to what is done
for meshes: if attr_used_over_time has different from attr_used
for too long, then the attributes buffers are freed, ensure that
stale attributes (not used by shaders anymore) are removed. This
is not thouroughly tested: even by setting the timeout to 1 second
and tweaking the shader I could not triggering the collection.
The implementation matches the one for Meshes, so I think it will
do (hopefully).

Fix indices used to lookup point attributes to take STRIP rendering
into account.

Kévin Dietrich (kevindietrich) retitled this revision from EEVEE/Viewport: support Curves attributes rendering to EEVEE/Workbench: support Curves attributes rendering.May 20 2022, 2:26 PM
Kévin Dietrich (kevindietrich) edited the summary of this revision. (Show Details)
Clément Foucault (fclem) requested changes to this revision.May 20 2022, 11:56 PM

I see you changed some things in eevee_next but not the ShaderCreateInfo for the resource binding.
You should be able to test whatever you are doing in EEVEE-Next unless I broke something with the commits from Wednesday.

I am not completely sure how this adds any functionality to Workbench. Is Workbench capable to display custom attributes already?

Also would be nice to turn the example file into rendering tests for both EEVEE, Cycles and Workbench.

source/blender/draw/engines/eevee/eevee_shaders_extra.cc
86

Add the uniform here using info.push_constant(). Ideally, you should group all uniforms inside a uniform buffer like volume object do. See draw_volume_infos for more info.

132

Define them using the shaderCreateInfo instead of using ifdef and glsl strings. See my other comment.

source/blender/gpu/intern/gpu_codegen.cc
341–343

Remove that define from here. No need to provide a boolean to the load function. Look at how volume rendering does that.

I agree, it is a bit convoluted and hidden from first sight but I would like to keep it as implementation details of the renderer than of then codegen.

This revision now requires changes to proceed.May 20 2022, 11:56 PM

Add CurvesInfos which holds an array of booleans to determine the scope
of an attribute. This array is indexed based on the attribute loading order.
Since some attributes may not be used in the material (e.g. disconnected
attribute node), we cannot use the same indexing as DRW_Attributes, so a
mapping to the index inside of the material attribute list is done.

The change in eevee_shader_material_create_info_amend might be merged with
some other condition, but I am not really understanding part of the control
flow. It does not seem to work if I put it in the
is_hair && !info.vertex_out_interfaces_.is_empty() block.

For EEVEE Next, I could not do much testing. I added the same changes to the
shader creation info as for EEVEE, however, the code is quite broken.

First, rendering the Curves object is using the particles system and its
related code; that I can deal with in a separate patch if no one else is
already looking into it. However, shader compilation fails, as definitions
for attr_load_temperature_post and attr_load_color_post are missing in
the fragment shader (these are used from gpu_shader_material_attribute.glsl).
I tried looking around but I am not too sure if they have to be defined
manually somehow, or if eevee_attributes_lib.glsl needs to be included
somewhere.

For Workbench, it's indeed not working, I mechanically renamed modified
"viewport" to "Workbench" when last updating the patch description for some
reason.

For the tests, I think that can be handled as a separate patch, as it would
also deal with Cycles. I guess the tests should include all attribute types
and domains so that scalars are also checked when swizzling is implemented
(could be a good thing to also add some tests for meshes, if not done already).

It does not seem to work if I put it in the is_hair && !info.vertex_out_interfaces_.is_empty() block.

This is because drw_curves uniform buffer object is still required because it's used by the definition of curves_id() even if the function is not actually used. So, no problem with adding the UBO definition even if not used.
For the record, info.vertex_out_interfaces_.is_empty() is to test if there is any attribute needed by the shader nodetree.

For EEVEE Next, I could not do much testing. I added the same changes to the shader creation info as for EEVEE, however, the code is quite broken.

I will continue developpement on my side after this patch is merged.

For the tests, I think that can be handled as a separate patch, as it would also deal with Cycles.

I agree.

I think there is nothing much to add. This patch can be merged in this state.

source/blender/draw/engines/eevee/eevee_shaders_extra.cc
123–126

I am not sure this is needed. For volume it is required in order to compute volume "orco" for grid attributes loading.

source/blender/draw/intern/shaders/draw_object_infos_info.hh
16 ↗(On Diff #51709)

I think buffer slot can be set to 2. There is no way an drawcall can be a volume and curve at the same time.

Hans Goudey (HooglyBoogly) requested changes to this revision.May 22 2022, 1:59 PM

The results are looking good now in my basic test. I added a few inline comments, mostly simple things.

Sorry to stick on this point, but I'm honestly still not happy with adding render_mutex to Curves at this point, especially if it's not doing anything.
Yes, maybe it makes future multi-threading of the attribute retrieval process from multiple objects using the same data a bit simpler,
but it also conflicts with other more current design goals: using RAII types where possible, keeping runtime data out of base DNA structs,
type safety, and clarity (adding code that has a design and a purpose). I hadn't heard anything about specific plans to multi-thread the resource extraction
for render engines, and it's not clear to me that this would actually be necessary. One implementation might give the render engine
a curves data-block and the information for all of its users at the same time.

source/blender/draw/engines/eevee/shaders/surface_vert.glsl
78–87

I notice the exact same function is added in shadow_vert.glsl. Should it be implemented somewhere else and deduplicated?

I know this isn't my area, but given the rather vague function name, I'd imagine a comment would be helpful (I'm not sure of the purpose of this right now).

source/blender/draw/intern/draw_cache_impl_curves.cc
529–530

This funkyness with ATTR_DOMAIN_NUM seems unnecessary here. There are three cases, point, curve, and "other/invalid". In the third case, continue is used, so the following code will never see an invalid value, and domain doesn't need to have an initialized value at the start of the function.

source/blender/draw/intern/draw_curves.cc
71

The style guide mentions that signed integers should be used for indices.

75–77

Is there a reason to not just use std::unique_ptr?

225
source/blender/draw/intern/draw_curves_private.h
51

Thanks for adding the comments here. I'm sure it will save people lots of time in the future.

You could also add the units of last_attr_matching_time, I assumed it is a frame?

source/blender/draw/intern/draw_manager.h
542 ↗(On Diff #51709)

Could probably just forward declare the type to avoid the need to cast?

source/blender/makesdna/DNA_curves_types.h
157

It feels quite wrong to add functionality and a comment we know is incorrect, even if it's planned to change in the future. Also, I think the whole mesh batch cache creation isn't threadsafe anyway, so it seems a bit early to try to make a single part of the API threadsafe without a clear written for how plan for multi-threaded object processing will be implemented.

If we really have to include this now, the state should be made much clearer with comments.

This revision now requires changes to proceed.May 22 2022, 1:59 PM
Kévin Dietrich (kevindietrich) marked 10 inline comments as done.May 23 2022, 8:55 AM
Kévin Dietrich (kevindietrich) added inline comments.
source/blender/draw/engines/eevee/shaders/surface_vert.glsl
78–87

I can have a more descriptive name. The only place where this could be placed is also shared with EEVEE next which has a different implementation, and may conflict. Although the main difference is accessing the strand ID. I guess the strand ID could be a function parameter, but that makes for a strange API. Also note that the attr_load_* are also duplicated between the surface and shadow shaders. It is a bit of a necessary evil I guess.

source/blender/draw/intern/draw_curves_private.h
51

I added some precision to the comment, it is in seconds.

Kévin Dietrich (kevindietrich) marked an inline comment as done.

Address review comments.

Render mutex is still present, can be removed either now or as a
cleanup pass after merge, to not introduce changes to other unrelated
areas, as discussed in the GPU meeting.

Move render mutex to CurvesBatchCache.

I have a few more small comments, but overall looks good to me. I'm also accepting in the name of Hans, since he is on holidays and his concern has been addressed.

source/blender/blenkernel/intern/curves.cc
25

unnecessary include

source/blender/draw/intern/draw_attributes.cc
56 ↗(On Diff #51745)

Parameters can be const.

source/blender/draw/intern/draw_cache_impl_curves.cc
65

Can be embedded in the struct directly, doesn't have to be a pointer.

This revision is now accepted and ready to land.May 23 2022, 3:19 PM