Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorKévin Dietrich <kevin.dietrich@mailoo.org>2022-05-24 06:02:57 +0300
committerKévin Dietrich <kevin.dietrich@mailoo.org>2022-05-24 06:02:57 +0300
commitcd968a32739587a5baacdd278a670d44570cbd8a (patch)
tree33ef22c30e0352fdc2ea073847bcc1342ebc3dfc /source
parent64a5a7ade1e98234247a4140c182423ebc616d26 (diff)
EEVEE: support Curves attributes rendering
This adds support to render Curves attributes in EEVEE. 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 which we cannot rely on anyway as contrary to the mesh batch, we do cannot really tell if attributes need to be updated or else via some `DRW_batch_requested`. 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 is 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. This adds `CurvesInfos` to the shader creation info, which stores the scope in which the attributes are defined. Scopes are stored as booleans, in an array indexed by attribute loading order which is also the order in which the attributes were added to the material. A mapping is necessary between the indices used for the scoping, and the ones used in the Curves cache, as this may contain stale attributes which have not been garbage collected yet. Common utilities with the mesh code for handling requested attributes were moved to a separate file. Differential Revision: https://developer.blender.org/D14916
Diffstat (limited to 'source')
-rw-r--r--source/blender/draw/CMakeLists.txt2
-rw-r--r--source/blender/draw/engines/eevee/eevee_shaders_extra.cc4
-rw-r--r--source/blender/draw/engines/eevee/shaders/shadow_vert.glsl24
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_vert.glsl24
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.cc1
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl25
-rw-r--r--source/blender/draw/intern/draw_attributes.cc112
-rw-r--r--source/blender/draw/intern/draw_attributes.h60
-rw-r--r--source/blender/draw/intern/draw_cache.c3
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h15
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h1
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curves.cc226
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c151
-rw-r--r--source/blender/draw/intern/draw_common.h4
-rw-r--r--source/blender/draw/intern/draw_curves.cc218
-rw-r--r--source/blender/draw/intern/draw_curves_private.h29
-rw-r--r--source/blender/draw/intern/draw_manager.c13
-rw-r--r--source/blender/draw/intern/draw_manager.h2
-rw-r--r--source/blender/draw/intern/draw_shader_shared.h14
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc5
-rw-r--r--source/blender/draw/intern/shaders/draw_object_infos_info.hh4
21 files changed, 736 insertions, 201 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 3e90c2cb707..3381dbadbab 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -69,6 +69,7 @@ set(SRC
intern/mesh_extractors/extract_mesh_vbo_uv.cc
intern/mesh_extractors/extract_mesh_vbo_vcol.cc
intern/mesh_extractors/extract_mesh_vbo_weights.cc
+ intern/draw_attributes.cc
intern/draw_cache_impl_curve.cc
intern/draw_cache_impl_curves.cc
intern/draw_cache_impl_displist.c
@@ -198,6 +199,7 @@ set(SRC
DRW_select_buffer.h
intern/DRW_gpu_wrapper.hh
intern/DRW_render.h
+ intern/draw_attributes.h
intern/draw_cache.h
intern/draw_cache_extract.h
intern/draw_cache_impl.h
diff --git a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc
index 05577944140..216a15de2b9 100644
--- a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc
+++ b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc
@@ -118,6 +118,10 @@ void eevee_shader_material_create_info_amend(GPUMaterial *gpumat,
info.vertex_inputs_.clear();
}
+ if (is_hair) {
+ info.additional_info("draw_curves_infos");
+ }
+
if (!is_volume) {
info.define("EEVEE_GENERATED_INTERFACE");
info.vertex_out(*stage_interface);
diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
index 5295a05b965..9e136426e22 100644
--- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
@@ -65,6 +65,22 @@ vec3 attr_load_orco(samplerBuffer cd_buf)
}
# endif
+/* Per attribute scope follows loading order. */
+int g_curves_attr_id = 0;
+
+/* Return the index to use for looking up the attribute value in the sampler
+ * based on the attribute scope (point or spline). */
+int curves_attribute_element_id()
+{
+ int id = hairStrandID;
+ if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) {
+ id = hair_get_base_id();
+ }
+
+ g_curves_attr_id += 1;
+ return id;
+}
+
vec4 attr_load_tangent(samplerBuffer cd_buf)
{
/* Not supported. */
@@ -73,22 +89,22 @@ vec4 attr_load_tangent(samplerBuffer cd_buf)
vec4 attr_load_vec4(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, hairStrandID).rgba;
+ return texelFetch(cd_buf, curves_attribute_element_id()).rgba;
}
vec3 attr_load_vec3(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, hairStrandID).rgb;
+ return texelFetch(cd_buf, curves_attribute_element_id()).rgb;
}
vec2 attr_load_vec2(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, hairStrandID).rg;
+ return texelFetch(cd_buf, curves_attribute_element_id()).rg;
}
float attr_load_float(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, hairStrandID).r;
+ return texelFetch(cd_buf, curves_attribute_element_id()).r;
}
#else
diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
index 04f38978076..013ba7e9312 100644
--- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
@@ -72,6 +72,22 @@ vec3 attr_load_orco(samplerBuffer cd_buf)
}
# endif
+/* Per attribute scope follows loading order. */
+int g_curves_attr_id = 0;
+
+/* Return the index to use for looking up the attribute value in the sampler
+ * based on the attribute scope (point or spline). */
+int curves_attribute_element_id()
+{
+ int id = hairStrandID;
+ if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) {
+ id = hair_get_base_id();
+ }
+
+ g_curves_attr_id += 1;
+ return id;
+}
+
vec4 attr_load_tangent(samplerBuffer cd_buf)
{
return vec4(hairTangent, 1.0);
@@ -79,22 +95,22 @@ vec4 attr_load_tangent(samplerBuffer cd_buf)
vec4 attr_load_vec4(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, hairStrandID).rgba;
+ return texelFetch(cd_buf, curves_attribute_element_id()).rgba;
}
vec3 attr_load_vec3(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, hairStrandID).rgb;
+ return texelFetch(cd_buf, curves_attribute_element_id()).rgb;
}
vec2 attr_load_vec2(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, hairStrandID).rg;
+ return texelFetch(cd_buf, curves_attribute_element_id()).rg;
}
float attr_load_float(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, hairStrandID).r;
+ return texelFetch(cd_buf, curves_attribute_element_id()).r;
}
#else
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc
index 009eb54864c..09aa97e49e9 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc
@@ -161,6 +161,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
}
}
info.vertex_inputs_.clear();
+ info.additional_info("draw_curves_infos");
break;
case MAT_GEOM_WORLD:
/**
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl
index 3c5acf62e30..ea9d98c5060 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl
@@ -112,6 +112,7 @@ float attr_load_float(float attr)
/** \name Curve
*
* Curve objects loads attributes from buffers through sampler buffers.
+ * Per attribute scope follows loading order.
* \{ */
# ifdef OBINFO_LIB
@@ -122,6 +123,22 @@ vec3 attr_load_orco(vec4 orco)
return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
}
# endif
+
+int g_curves_attr_id = 0;
+
+/* Return the index to use for looking up the attribute value in the sampler
+ * based on the attribute scope (point or spline). */
+int curves_attribute_element_id()
+{
+ int id = interp.curves_strand_id;
+ if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) {
+ id = hair_get_base_id();
+ }
+
+ g_curves_attr_id += 1;
+ return id;
+}
+
vec4 attr_load_tangent(samplerBuffer cd_buf)
{
/* Not supported for the moment. */
@@ -137,19 +154,19 @@ vec4 attr_load_color(samplerBuffer cd_buf)
}
vec4 attr_load_vec4(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, interp.curves_strand_id).rgba;
+ return texelFetch(cd_buf, curves_attribute_element_id()).rgba;
}
vec3 attr_load_vec3(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, interp.curves_strand_id).rgb;
+ return texelFetch(cd_buf, curves_attribute_element_id()).rgb;
}
vec2 attr_load_vec2(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, interp.curves_strand_id).rg;
+ return texelFetch(cd_buf, curves_attribute_element_id()).rg;
}
float attr_load_float(samplerBuffer cd_buf)
{
- return texelFetch(cd_buf, interp.curves_strand_id).r;
+ return texelFetch(cd_buf, curves_attribute_element_id()).r;
}
/** \} */
diff --git a/source/blender/draw/intern/draw_attributes.cc b/source/blender/draw/intern/draw_attributes.cc
new file mode 100644
index 00000000000..714f1dbb3d1
--- /dev/null
+++ b/source/blender/draw/intern/draw_attributes.cc
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+#include "draw_attributes.h"
+
+/* Return true if the given DRW_AttributeRequest is already in the requests. */
+static bool drw_attributes_has_request(const DRW_Attributes *requests, DRW_AttributeRequest req)
+{
+ for (int i = 0; i < requests->num_requests; i++) {
+ const DRW_AttributeRequest src_req = requests->requests[i];
+ if (src_req.domain != req.domain) {
+ continue;
+ }
+ if (src_req.layer_index != req.layer_index) {
+ continue;
+ }
+ if (src_req.cd_type != req.cd_type) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+}
+
+static void drw_attributes_merge_requests(const DRW_Attributes *src_requests,
+ DRW_Attributes *dst_requests)
+{
+ for (int i = 0; i < src_requests->num_requests; i++) {
+ if (dst_requests->num_requests == GPU_MAX_ATTR) {
+ return;
+ }
+
+ if (drw_attributes_has_request(dst_requests, src_requests->requests[i])) {
+ continue;
+ }
+
+ dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i];
+ dst_requests->num_requests += 1;
+ }
+}
+
+void drw_attributes_clear(DRW_Attributes *attributes)
+{
+ memset(attributes, 0, sizeof(DRW_Attributes));
+}
+
+void drw_attributes_merge(DRW_Attributes *dst,
+ const DRW_Attributes *src,
+ ThreadMutex *render_mutex)
+{
+ BLI_mutex_lock(render_mutex);
+ drw_attributes_merge_requests(src, dst);
+ BLI_mutex_unlock(render_mutex);
+}
+
+bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b)
+{
+ for (int i = 0; i < b->num_requests; i++) {
+ if (!drw_attributes_has_request(a, b->requests[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs,
+ CustomDataType type,
+ int layer,
+ AttributeDomain domain)
+{
+ if (attrs->num_requests >= GPU_MAX_ATTR) {
+ return nullptr;
+ }
+
+ DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests];
+ req->cd_type = type;
+ req->layer_index = layer;
+ req->domain = domain;
+ attrs->num_requests += 1;
+ return req;
+}
+
+bool drw_custom_data_match_attribute(const CustomData *custom_data,
+ const char *name,
+ int *r_layer_index,
+ int *r_type)
+{
+ const int possible_attribute_types[7] = {
+ CD_PROP_BOOL,
+ CD_PROP_INT8,
+ CD_PROP_INT32,
+ CD_PROP_FLOAT,
+ CD_PROP_FLOAT2,
+ CD_PROP_FLOAT3,
+ CD_PROP_COLOR,
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) {
+ const int attr_type = possible_attribute_types[i];
+ int layer_index = CustomData_get_named_layer(custom_data, attr_type, name);
+ if (layer_index == -1) {
+ continue;
+ }
+
+ *r_layer_index = layer_index;
+ *r_type = attr_type;
+ return true;
+ }
+
+ return false;
+}
diff --git a/source/blender/draw/intern/draw_attributes.h b/source/blender/draw/intern/draw_attributes.h
new file mode 100644
index 00000000000..192ffa43337
--- /dev/null
+++ b/source/blender/draw/intern/draw_attributes.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Utilities for rendering attributes.
+ */
+
+#pragma once
+
+#include "DNA_customdata_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute.h"
+
+#include "BLI_sys_types.h"
+#include "BLI_threads.h"
+
+#include "GPU_shader.h"
+#include "GPU_vertex_format.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DRW_AttributeRequest {
+ CustomDataType cd_type;
+ int layer_index;
+ AttributeDomain domain;
+ char attribute_name[64];
+} DRW_AttributeRequest;
+
+typedef struct DRW_Attributes {
+ DRW_AttributeRequest requests[GPU_MAX_ATTR];
+ int num_requests;
+} DRW_Attributes;
+
+void drw_attributes_clear(DRW_Attributes *attributes);
+
+void drw_attributes_merge(DRW_Attributes *dst,
+ const DRW_Attributes *src,
+ ThreadMutex *render_mutex);
+
+/* Return true if all requests in b are in a. */
+bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b);
+
+DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs,
+ CustomDataType type,
+ int layer,
+ AttributeDomain domain);
+
+bool drw_custom_data_match_attribute(const CustomData *custom_data,
+ const char *name,
+ int *r_layer_index,
+ int *r_type);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index 67700a4274f..fb074cc728e 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -3400,6 +3400,9 @@ void DRW_batch_cache_free_old(Object *ob, int ctime)
case OB_MESH:
DRW_mesh_batch_cache_free_old((Mesh *)ob->data, ctime);
break;
+ case OB_CURVES:
+ DRW_curves_batch_cache_free_old((Curves *)ob->data, ctime);
+ break;
/* TODO: all cases. */
default:
break;
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index cb6006e303a..ce3ad9923da 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -20,6 +20,8 @@ struct TaskGraph;
#include "GPU_index_buffer.h"
#include "GPU_vertex_buffer.h"
+#include "draw_attributes.h"
+
/* Vertex Group Selection and display options */
typedef struct DRW_MeshWeightState {
int defgroup_active;
@@ -67,17 +69,6 @@ typedef enum eMRIterType {
} eMRIterType;
ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT)
-typedef struct DRW_AttributeRequest {
- CustomDataType cd_type;
- int layer_index;
- AttributeDomain domain;
-} DRW_AttributeRequest;
-
-typedef struct DRW_MeshAttributes {
- DRW_AttributeRequest requests[GPU_MAX_ATTR];
- int num_requests;
-} DRW_MeshAttributes;
-
typedef enum eMRDataType {
MR_DATA_NONE = 0,
MR_DATA_POLY_NOR = 1 << 1,
@@ -294,7 +285,7 @@ typedef struct MeshBatchCache {
DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time;
- DRW_MeshAttributes attr_used, attr_needed, attr_used_over_time;
+ DRW_Attributes attr_used, attr_needed, attr_used_over_time;
int lastmatch;
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index f877c94208f..0755d5967d5 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -83,6 +83,7 @@ void DRW_batch_cache_free_old(struct Object *ob, int ctime);
* \note For now this only free the shading batches / VBO if any cd layers is not needed anymore.
*/
void DRW_mesh_batch_cache_free_old(struct Mesh *me, int ctime);
+void DRW_curves_batch_cache_free_old(struct Curves *curves, int ctime);
/** \} */
diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc
index 1896df7c650..f9cf0021fcd 100644
--- a/source/blender/draw/intern/draw_cache_impl_curves.cc
+++ b/source/blender/draw/intern/draw_cache_impl_curves.cc
@@ -24,6 +24,7 @@
#include "DNA_scene_types.h"
#include "BKE_curves.hh"
+#include "BKE_geometry_set.hh"
#include "GPU_batch.h"
#include "GPU_material.h"
@@ -31,10 +32,13 @@
#include "DRW_render.h"
+#include "draw_attributes.h"
#include "draw_cache_impl.h" /* own include */
#include "draw_cache_inline.h"
#include "draw_curves_private.h" /* own include */
+#include "draw_shader.h"
+using blender::ColorGeometry4f;
using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
@@ -50,6 +54,10 @@ struct CurvesBatchCache {
/* To determine if cache is invalid. */
bool is_dirty;
+
+ /** Needed when updating material data (e.g. attributes) as the same curves might be used for
+ * multiple objects with different materials. */
+ ThreadMutex render_mutex;
};
static bool curves_batch_cache_valid(const Curves &curves)
@@ -64,6 +72,7 @@ static void curves_batch_cache_init(Curves &curves)
if (!cache) {
cache = MEM_cnew<CurvesBatchCache>(__func__);
+ BLI_mutex_init(&cache->render_mutex);
curves.batch_cache = cache;
}
else {
@@ -73,6 +82,23 @@ static void curves_batch_cache_init(Curves &curves)
cache->is_dirty = false;
}
+static void curves_discard_attributes(CurvesEvalCache &curves_cache)
+{
+ for (int i = 0; i < GPU_MAX_ATTR; i++) {
+ GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_attributes_buf[i]);
+ DRW_TEXTURE_FREE_SAFE(curves_cache.proc_attributes_tex[i]);
+ }
+
+ for (int i = 0; i < MAX_HAIR_SUBDIV; i++) {
+ for (int j = 0; j < GPU_MAX_ATTR; j++) {
+ GPU_VERTBUF_DISCARD_SAFE(curves_cache.final[i].attributes_buf[j]);
+ DRW_TEXTURE_FREE_SAFE(curves_cache.final[i].attributes_tex[j]);
+ }
+
+ drw_attributes_clear(&curves_cache.final[i].attr_used);
+ }
+}
+
static void curves_batch_cache_clear_data(CurvesEvalCache &curves_cache)
{
/* TODO: more granular update tagging. */
@@ -93,6 +119,8 @@ static void curves_batch_cache_clear_data(CurvesEvalCache &curves_cache)
GPU_BATCH_DISCARD_SAFE(curves_cache.final[i].proc_hairs[j]);
}
}
+
+ curves_discard_attributes(curves_cache);
}
static void curves_batch_cache_clear(Curves &curves)
@@ -139,9 +167,39 @@ void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode)
void DRW_curves_batch_cache_free(Curves *curves)
{
curves_batch_cache_clear(*curves);
+ CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache);
+ BLI_mutex_end(&cache->render_mutex);
MEM_SAFE_FREE(curves->batch_cache);
}
+void DRW_curves_batch_cache_free_old(Curves *curves, int ctime)
+{
+ CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache);
+ if (cache == nullptr) {
+ return;
+ }
+
+ bool do_discard = false;
+
+ for (int i = 0; i < MAX_HAIR_SUBDIV; i++) {
+ CurvesEvalFinalCache &final_cache = cache->curves_cache.final[i];
+
+ if (drw_attributes_overlap(&final_cache.attr_used_over_time, &final_cache.attr_used)) {
+ final_cache.last_attr_matching_time = ctime;
+ }
+
+ if (ctime - final_cache.last_attr_matching_time > U.vbotimeout) {
+ do_discard = true;
+ }
+
+ drw_attributes_clear(&final_cache.attr_used_over_time);
+ }
+
+ if (do_discard) {
+ curves_discard_attributes(cache->curves_cache);
+ }
+}
+
static void ensure_seg_pt_count(const Curves &curves, CurvesEvalCache &curves_cache)
{
if (curves_cache.proc_point_buf != nullptr) {
@@ -242,6 +300,88 @@ static void curves_batch_cache_ensure_procedural_pos(Curves &curves,
}
}
+void drw_curves_get_attribute_sampler_name(const char *layer_name, char r_sampler_name[32])
+{
+ char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+ /* Attributes use auto-name. */
+ BLI_snprintf(r_sampler_name, 32, "a%s", attr_safe_name);
+}
+
+static void curves_batch_cache_ensure_procedural_final_attr(
+ CurvesEvalCache &cache, GPUVertFormat *format, int subdiv, int index, const char *name)
+{
+ CurvesEvalFinalCache &final_cache = cache.final[subdiv];
+ final_cache.attributes_buf[index] = GPU_vertbuf_create_with_format_ex(format,
+ GPU_USAGE_DEVICE_ONLY);
+
+ /* Create a destination buffer for the transform feedback. Sized appropriately */
+ /* Those are points! not line segments. */
+ GPU_vertbuf_data_alloc(final_cache.attributes_buf[index],
+ final_cache.strands_res * cache.strands_len);
+
+ /* Create vbo immediately to bind to texture buffer. */
+ GPU_vertbuf_use(final_cache.attributes_buf[index]);
+
+ final_cache.attributes_tex[index] = GPU_texture_create_from_vertbuf(
+ name, final_cache.attributes_buf[index]);
+}
+
+static void curves_batch_ensure_attribute(const Curves &curves,
+ CurvesEvalCache &cache,
+ const DRW_AttributeRequest &request,
+ int subdiv,
+ int index)
+{
+ GPU_VERTBUF_DISCARD_SAFE(cache.proc_attributes_buf[index]);
+ DRW_TEXTURE_FREE_SAFE(cache.proc_attributes_tex[index]);
+
+ char sampler_name[32];
+ drw_curves_get_attribute_sampler_name(request.attribute_name, sampler_name);
+
+ GPUVertFormat format = {0};
+ GPU_vertformat_deinterleave(&format);
+ /* All attributes use vec4, see comment below. */
+ GPU_vertformat_attr_add(&format, sampler_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+
+ cache.proc_attributes_buf[index] = GPU_vertbuf_create_with_format(&format);
+ GPUVertBuf *attr_vbo = cache.proc_attributes_buf[index];
+
+ GPU_vertbuf_data_alloc(attr_vbo,
+ request.domain == ATTR_DOMAIN_POINT ? curves.geometry.point_num :
+ curves.geometry.curve_num);
+
+ 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 state swizzle to map the attribute correctly as for volume attributes, so we
+ * can control the conversion ourselves. */
+ blender::VArray<ColorGeometry4f> attribute = component.attribute_get_for_read<ColorGeometry4f>(
+ request.attribute_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);
+
+ GPU_vertbuf_use(attr_vbo);
+ cache.proc_attributes_tex[index] = GPU_texture_create_from_vertbuf(sampler_name, attr_vbo);
+
+ /* Existing final data may have been for a different attribute (with a different name or domain),
+ * free the data. */
+ GPU_VERTBUF_DISCARD_SAFE(cache.final[subdiv].attributes_buf[index]);
+ DRW_TEXTURE_FREE_SAFE(cache.final[subdiv].attributes_tex[index]);
+
+ /* Ensure final data for points. */
+ if (request.domain == ATTR_DOMAIN_POINT) {
+ curves_batch_cache_ensure_procedural_final_attr(cache, &format, subdiv, index, sampler_name);
+ }
+}
+
static void curves_batch_cache_fill_strands_data(const Curves &curves_id,
GPUVertBufRaw &data_step,
GPUVertBufRaw &seg_step)
@@ -358,6 +498,88 @@ static void curves_batch_cache_ensure_procedural_indices(Curves &curves,
prim_type, vbo, GPU_indexbuf_build(&elb), GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
}
+static bool curves_ensure_attributes(const Curves &curves,
+ CurvesBatchCache &cache,
+ GPUMaterial *gpu_material,
+ int subdiv)
+{
+ ThreadMutex *render_mutex = &cache.render_mutex;
+ const CustomData *cd_curve = &curves.geometry.curve_data;
+ const CustomData *cd_point = &curves.geometry.point_data;
+
+ DRW_Attributes attrs_needed;
+ drw_attributes_clear(&attrs_needed);
+ ListBase gpu_attrs = GPU_material_attributes(gpu_material);
+ LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
+ const char *name = gpu_attr->name;
+ int type = gpu_attr->type;
+ int layer = -1;
+ AttributeDomain domain;
+
+ if (drw_custom_data_match_attribute(cd_curve, name, &layer, &type)) {
+ domain = ATTR_DOMAIN_CURVE;
+ }
+ else if (drw_custom_data_match_attribute(cd_point, name, &layer, &type)) {
+ domain = ATTR_DOMAIN_POINT;
+ }
+ else {
+ continue;
+ }
+
+ switch (type) {
+ default:
+ break;
+ case CD_PROP_BOOL:
+ case CD_PROP_INT8:
+ case CD_PROP_INT32:
+ case CD_PROP_FLOAT:
+ case CD_PROP_FLOAT2:
+ case CD_PROP_FLOAT3:
+ case CD_PROP_COLOR: {
+ if (layer != -1) {
+ DRW_AttributeRequest *req = drw_attributes_add_request(
+ &attrs_needed, (CustomDataType)type, layer, domain);
+ if (req) {
+ BLI_strncpy(req->attribute_name, name, sizeof(req->attribute_name));
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ CurvesEvalFinalCache &final_cache = cache.curves_cache.final[subdiv];
+
+ const bool attr_overlap = drw_attributes_overlap(&final_cache.attr_used, &attrs_needed);
+ if (attr_overlap == false) {
+ /* Some new attributes have been added, free all and start over. */
+ for (int i = 0; i < GPU_MAX_ATTR; i++) {
+ GPU_VERTBUF_DISCARD_SAFE(cache.curves_cache.proc_attributes_buf[i]);
+ DRW_TEXTURE_FREE_SAFE(cache.curves_cache.proc_attributes_tex[i]);
+ }
+ drw_attributes_merge(&final_cache.attr_used, &attrs_needed, render_mutex);
+ }
+ drw_attributes_merge(&final_cache.attr_used_over_time, &attrs_needed, render_mutex);
+
+ bool need_tf_update = false;
+
+ for (int i = 0; i < final_cache.attr_used.num_requests; i++) {
+ const DRW_AttributeRequest &request = final_cache.attr_used.requests[i];
+
+ if (cache.curves_cache.proc_attributes_buf[i] != nullptr) {
+ continue;
+ }
+
+ if (request.domain == ATTR_DOMAIN_POINT) {
+ need_tf_update = true;
+ }
+
+ curves_batch_ensure_attribute(curves, cache.curves_cache, request, subdiv, i);
+ }
+
+ return need_tf_update;
+}
+
bool curves_ensure_procedural_data(Object *object,
CurvesEvalCache **r_hair_cache,
GPUMaterial *gpu_material,
@@ -395,6 +617,10 @@ bool curves_ensure_procedural_data(Object *object,
curves, cache.curves_cache, thickness_res, subdiv);
}
+ if (gpu_material) {
+ need_ft_update |= curves_ensure_attributes(curves, cache, gpu_material, subdiv);
+ }
+
return need_ft_update;
}
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 7fdeaf34965..a6ab2176d16 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -281,91 +281,6 @@ static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *c
cd_used->edit_uv = 1;
}
-/** \name DRW_MeshAttributes
- *
- * Utilities for handling requested attributes.
- * \{ */
-
-/* Return true if the given DRW_AttributeRequest is already in the requests. */
-static bool has_request(const DRW_MeshAttributes *requests, DRW_AttributeRequest req)
-{
- for (int i = 0; i < requests->num_requests; i++) {
- const DRW_AttributeRequest src_req = requests->requests[i];
- if (src_req.domain != req.domain) {
- continue;
- }
- if (src_req.layer_index != req.layer_index) {
- continue;
- }
- if (src_req.cd_type != req.cd_type) {
- continue;
- }
- return true;
- }
- return false;
-}
-
-static void mesh_attrs_merge_requests(const DRW_MeshAttributes *src_requests,
- DRW_MeshAttributes *dst_requests)
-{
- for (int i = 0; i < src_requests->num_requests; i++) {
- if (dst_requests->num_requests == GPU_MAX_ATTR) {
- return;
- }
-
- if (has_request(dst_requests, src_requests->requests[i])) {
- continue;
- }
-
- dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i];
- dst_requests->num_requests += 1;
- }
-}
-
-static void drw_mesh_attributes_clear(DRW_MeshAttributes *attributes)
-{
- memset(attributes, 0, sizeof(DRW_MeshAttributes));
-}
-
-static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst,
- const DRW_MeshAttributes *src,
- ThreadMutex *mesh_render_mutex)
-{
- BLI_mutex_lock(mesh_render_mutex);
- mesh_attrs_merge_requests(src, dst);
- BLI_mutex_unlock(mesh_render_mutex);
-}
-
-/* Return true if all requests in b are in a. */
-static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b)
-{
- for (int i = 0; i < b->num_requests; i++) {
- if (!has_request(a, b->requests[i])) {
- return false;
- }
- }
-
- return true;
-}
-
-static void drw_mesh_attributes_add_request(DRW_MeshAttributes *attrs,
- CustomDataType type,
- int layer,
- AttributeDomain domain)
-{
- if (attrs->num_requests >= GPU_MAX_ATTR) {
- return;
- }
-
- DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests];
- req->cd_type = type;
- req->layer_index = layer;
- req->domain = domain;
- attrs->num_requests += 1;
-}
-
-/** \} */
-
BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me)
{
switch ((eMeshWrapperType)me->runtime.wrapper_type) {
@@ -475,36 +390,6 @@ static void mesh_cd_calc_active_mloopcol_layer(const Object *object,
}
}
-static bool custom_data_match_attribute(const CustomData *custom_data,
- const char *name,
- int *r_layer_index,
- int *r_type)
-{
- const int possible_attribute_types[7] = {
- CD_PROP_BOOL,
- CD_PROP_INT8,
- CD_PROP_INT32,
- CD_PROP_FLOAT,
- CD_PROP_FLOAT2,
- CD_PROP_FLOAT3,
- CD_PROP_COLOR,
- };
-
- for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) {
- const int attr_type = possible_attribute_types[i];
- int layer_index = CustomData_get_named_layer(custom_data, attr_type, name);
- if (layer_index == -1) {
- continue;
- }
-
- *r_layer_index = layer_index;
- *r_type = attr_type;
- return true;
- }
-
- return false;
-}
-
static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query,
const CustomData *cd_vdata,
const CustomData *cd_ldata,
@@ -556,7 +441,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
const Mesh *me,
struct GPUMaterial **gpumat_array,
int gpumat_array_len,
- DRW_MeshAttributes *attributes)
+ DRW_Attributes *attributes)
{
const Mesh *me_final = editmesh_final_or_this(object, me);
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
@@ -636,16 +521,16 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
if (layer == -1) {
/* Try to match a generic attribute, we use the first attribute domain with a
* matching name. */
- if (custom_data_match_attribute(cd_vdata, name, &layer, &type)) {
+ if (drw_custom_data_match_attribute(cd_vdata, name, &layer, &type)) {
domain = ATTR_DOMAIN_POINT;
}
- else if (custom_data_match_attribute(cd_ldata, name, &layer, &type)) {
+ else if (drw_custom_data_match_attribute(cd_ldata, name, &layer, &type)) {
domain = ATTR_DOMAIN_CORNER;
}
- else if (custom_data_match_attribute(cd_pdata, name, &layer, &type)) {
+ else if (drw_custom_data_match_attribute(cd_pdata, name, &layer, &type)) {
domain = ATTR_DOMAIN_FACE;
}
- else if (custom_data_match_attribute(cd_edata, name, &layer, &type)) {
+ else if (drw_custom_data_match_attribute(cd_edata, name, &layer, &type)) {
domain = ATTR_DOMAIN_EDGE;
}
else {
@@ -718,7 +603,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
}
if (layer != -1 && domain != ATTR_DOMAIN_NUM) {
- drw_mesh_attributes_add_request(attributes, type, layer, domain);
+ drw_attributes_add_request(attributes, type, layer, domain);
}
break;
}
@@ -729,7 +614,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
case CD_PROP_FLOAT:
case CD_PROP_FLOAT2: {
if (layer != -1 && domain != ATTR_DOMAIN_NUM) {
- drw_mesh_attributes_add_request(attributes, type, layer, domain);
+ drw_attributes_add_request(attributes, type, layer, domain);
}
break;
}
@@ -1317,8 +1202,8 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object,
uint gpumat_array_len)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- DRW_MeshAttributes attrs_needed;
- drw_mesh_attributes_clear(&attrs_needed);
+ DRW_Attributes attrs_needed;
+ drw_attributes_clear(&attrs_needed);
DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(
object, me, gpumat_array, gpumat_array_len, &attrs_needed);
@@ -1326,7 +1211,7 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object,
mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex;
- drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex);
+ drw_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex);
mesh_batch_cache_request_surface_batches(cache);
return cache->surface_per_mat;
}
@@ -1596,7 +1481,7 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime)
cache->lastmatch = ctime;
}
- if (drw_mesh_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) {
+ if (drw_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) {
cache->lastmatch = ctime;
}
@@ -1605,12 +1490,12 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime)
}
mesh_cd_layers_type_clear(&cache->cd_used_over_time);
- drw_mesh_attributes_clear(&cache->attr_used_over_time);
+ drw_attributes_clear(&cache->attr_used_over_time);
}
static void drw_add_attributes_vbo(GPUBatch *batch,
MeshBufferList *mbuflist,
- DRW_MeshAttributes *attr_used)
+ DRW_Attributes *attr_used)
{
for (int i = 0; i < attr_used->num_requests; i++) {
DRW_vbo_request(batch, &mbuflist->vbo.attr[i]);
@@ -1721,7 +1606,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
/* TODO(fclem): We could be a bit smarter here and only do it per
* material. */
bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed);
- bool attr_overlap = drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed);
+ bool attr_overlap = drw_attributes_overlap(&cache->attr_used, &cache->attr_needed);
if (cd_overlap == false || attr_overlap == false) {
FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) {
@@ -1741,7 +1626,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) {
GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol);
}
- if (!drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed)) {
+ if (!drw_attributes_overlap(&cache->attr_used, &cache->attr_needed)) {
for (int i = 0; i < GPU_MAX_ATTR; i++) {
GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]);
}
@@ -1756,13 +1641,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
cache->batch_ready &= ~(MBC_SURFACE);
mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed);
- drw_mesh_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex);
+ drw_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex);
}
mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed);
mesh_cd_layers_type_clear(&cache->cd_needed);
- drw_mesh_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex);
- drw_mesh_attributes_clear(&cache->attr_needed);
+ drw_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex);
+ drw_attributes_clear(&cache->attr_needed);
}
if (batch_requested & MBC_EDITUV) {
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index 84e79cd8be9..b6b0c94f4bf 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -13,6 +13,7 @@
extern "C" {
#endif
+struct CurvesUniformBufPool;
struct DRWShadingGroup;
struct FluidModifierData;
struct GPUMaterial;
@@ -82,7 +83,8 @@ struct DRWShadingGroup *DRW_shgroup_curves_create_sub(struct Object *object,
struct DRWShadingGroup *shgrp,
struct GPUMaterial *gpu_material);
-void DRW_curves_init(void);
+void DRW_curves_init(struct DRWData *drw_data);
+void DRW_curves_ubos_pool_free(struct CurvesUniformBufPool *pool);
void DRW_curves_update(void);
void DRW_curves_free(void);
diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc
index 2edf596ac63..d90c63b680e 100644
--- a/source/blender/draw/intern/draw_curves.cc
+++ b/source/blender/draw/intern/draw_curves.cc
@@ -10,6 +10,7 @@
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
+#include "DNA_curves_types.h"
#include "DNA_customdata_types.h"
#include "GPU_batch.h"
@@ -20,9 +21,13 @@
#include "GPU_texture.h"
#include "GPU_vertex_buffer.h"
+#include "DRW_gpu_wrapper.hh"
#include "DRW_render.h"
+#include "draw_cache_impl.h"
+#include "draw_curves_private.h"
#include "draw_hair_private.h"
+#include "draw_manager.h"
#include "draw_shader.h"
#ifndef __APPLE__
@@ -61,16 +66,43 @@ static GPUVertBuf *g_dummy_vbo = nullptr;
static GPUTexture *g_dummy_texture = nullptr;
static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in the future */
+using CurvesInfosBuf = blender::draw::UniformBuffer<CurvesInfos>;
+
+struct CurvesUniformBufPool {
+ blender::Vector<std::unique_ptr<CurvesInfosBuf>> ubos;
+ int used = 0;
+
+ void reset()
+ {
+ used = 0;
+ }
+
+ CurvesInfosBuf &alloc()
+ {
+ if (used >= ubos.size()) {
+ ubos.append(std::make_unique<CurvesInfosBuf>());
+ return *ubos.last().get();
+ }
+ return *ubos[used++].get();
+ }
+};
+
static GPUShader *curves_eval_shader_get(CurvesEvalShader type)
{
return DRW_shader_curves_refine_get(type, drw_curves_shader_type_get());
}
-void DRW_curves_init(void)
+void DRW_curves_init(DRWData *drw_data)
{
/* Initialize legacy hair too, to avoid verbosity in callers. */
DRW_hair_init();
+ if (drw_data->curves_ubos == nullptr) {
+ drw_data->curves_ubos = MEM_new<CurvesUniformBufPool>("CurvesUniformBufPool");
+ }
+ CurvesUniformBufPool *pool = drw_data->curves_ubos;
+ pool->reset();
+
#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS)
g_tf_pass = DRW_pass_create("Update Curves Pass", (DRWState)0);
#else
@@ -94,63 +126,120 @@ void DRW_curves_init(void)
}
}
+void DRW_curves_ubos_pool_free(CurvesUniformBufPool *pool)
+{
+ MEM_delete(pool);
+}
+
static void drw_curves_cache_shgrp_attach_resources(DRWShadingGroup *shgrp,
CurvesEvalCache *cache,
+ GPUTexture *tex,
const int subdiv)
{
- DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_tex);
+ DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", tex);
DRW_shgroup_uniform_texture(shgrp, "hairStrandBuffer", cache->strand_tex);
DRW_shgroup_uniform_texture(shgrp, "hairStrandSegBuffer", cache->strand_seg_tex);
DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1);
}
+static void drw_curves_cache_update_compute(CurvesEvalCache *cache,
+ const int subdiv,
+ const int strands_len,
+ GPUVertBuf *buffer,
+ GPUTexture *tex)
+{
+ GPUShader *shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM);
+ DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass);
+ drw_curves_cache_shgrp_attach_resources(shgrp, cache, tex, subdiv);
+ DRW_shgroup_vertex_buffer(shgrp, "posTime", buffer);
+
+ const int max_strands_per_call = GPU_max_work_group_count(0);
+ int strands_start = 0;
+ while (strands_start < strands_len) {
+ int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call);
+ DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp);
+ DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start);
+ DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1);
+ strands_start += batch_strands_len;
+ }
+}
+
static void drw_curves_cache_update_compute(CurvesEvalCache *cache, const int subdiv)
{
const int strands_len = cache->strands_len;
const int final_points_len = cache->final[subdiv].strands_res * strands_len;
- if (final_points_len > 0) {
- GPUShader *shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM);
- DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass);
- drw_curves_cache_shgrp_attach_resources(shgrp, cache, subdiv);
- DRW_shgroup_vertex_buffer(shgrp, "posTime", cache->final[subdiv].proc_buf);
-
- const int max_strands_per_call = GPU_max_work_group_count(0);
- int strands_start = 0;
- while (strands_start < strands_len) {
- int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call);
- DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp);
- DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start);
- DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1);
- strands_start += batch_strands_len;
+ if (final_points_len == 0) {
+ return;
+ }
+
+ drw_curves_cache_update_compute(
+ cache, subdiv, strands_len, cache->final[subdiv].proc_buf, cache->point_tex);
+
+ const DRW_Attributes &attrs = cache->final[subdiv].attr_used;
+ for (int i = 0; i < attrs.num_requests; i++) {
+ /* Only refine point attributes. */
+ if (attrs.requests[i].domain == ATTR_DOMAIN_CURVE) {
+ continue;
}
+
+ drw_curves_cache_update_compute(cache,
+ subdiv,
+ strands_len,
+ cache->final[subdiv].attributes_buf[i],
+ cache->proc_attributes_tex[i]);
}
}
-static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, const int subdiv)
+static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache,
+ GPUVertBuf *vbo,
+ GPUTexture *tex,
+ const int subdiv,
+ const int final_points_len)
{
- const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len;
- if (final_points_len > 0) {
- GPUShader *tf_shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM);
+ GPUShader *tf_shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM);
#ifdef USE_TRANSFORM_FEEDBACK
- DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(
- tf_shader, g_tf_pass, cache->final[subdiv].proc_buf);
+ DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass, vbo);
#else
- DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
-
- CurvesEvalCall *pr_call = MEM_new<CurvesEvalCall>(__func__);
- pr_call->next = g_tf_calls;
- pr_call->vbo = cache->final[subdiv].proc_buf;
- pr_call->shgrp = tf_shgrp;
- pr_call->vert_len = final_points_len;
- g_tf_calls = pr_call;
- DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1);
- DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1);
- DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1);
+ DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
+
+ CurvesEvalCall *pr_call = MEM_new<CurvesEvalCall>(__func__);
+ pr_call->next = g_tf_calls;
+ pr_call->vbo = vbo;
+ pr_call->shgrp = tf_shgrp;
+ pr_call->vert_len = final_points_len;
+ g_tf_calls = pr_call;
+ DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1);
+ DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1);
+ DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1);
#endif
- drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv);
- DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len);
+ drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, tex, subdiv);
+ DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len);
+}
+
+static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, const int subdiv)
+{
+ const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len;
+ if (final_points_len == 0) {
+ return;
+ }
+
+ drw_curves_cache_update_transform_feedback(
+ cache, cache->final[subdiv].proc_buf, cache->point_tex, subdiv, final_points_len);
+
+ const DRW_Attributes &attrs = cache->final[subdiv].attr_used;
+ for (int i = 0; i < attrs.num_requests; i++) {
+ /* Only refine point attributes. */
+ if (attrs.requests[i].domain == ATTR_DOMAIN_CURVE) {
+ continue;
+ }
+
+ drw_curves_cache_update_transform_feedback(cache,
+ cache->final[subdiv].attributes_buf[i],
+ cache->proc_attributes_tex[i],
+ subdiv,
+ final_points_len);
}
}
@@ -186,12 +275,34 @@ GPUVertBuf *DRW_curves_pos_buffer_get(Object *object)
return cache->final[subdiv].proc_buf;
}
+static int attribute_index_in_material(GPUMaterial *gpu_material, const char *name)
+{
+ if (!gpu_material) {
+ return -1;
+ }
+
+ int index = 0;
+
+ ListBase gpu_attrs = GPU_material_attributes(gpu_material);
+ LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
+ if (STREQ(gpu_attr->name, name)) {
+ return index;
+ }
+
+ index++;
+ }
+
+ return -1;
+}
+
DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object,
DRWShadingGroup *shgrp_parent,
GPUMaterial *gpu_material)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
+ CurvesUniformBufPool *pool = DST.vmempool->curves_ubos;
+ CurvesInfosBuf &curves_infos = pool->alloc();
int subdiv = scene->r.hair_subdiv;
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
@@ -218,6 +329,43 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object,
if (curves_cache->length_tex) {
DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex);
}
+
+ const DRW_Attributes &attrs = curves_cache->final[subdiv].attr_used;
+ for (int i = 0; i < attrs.num_requests; i++) {
+ const DRW_AttributeRequest &request = attrs.requests[i];
+
+ char sampler_name[32];
+ drw_curves_get_attribute_sampler_name(request.attribute_name, sampler_name);
+
+ if (request.domain == ATTR_DOMAIN_CURVE) {
+ if (!curves_cache->proc_attributes_tex[i]) {
+ continue;
+ }
+
+ DRW_shgroup_uniform_texture(shgrp, sampler_name, curves_cache->proc_attributes_tex[i]);
+ }
+ else {
+ if (!curves_cache->final[subdiv].attributes_tex[i]) {
+ continue;
+ }
+ DRW_shgroup_uniform_texture(
+ shgrp, sampler_name, curves_cache->final[subdiv].attributes_tex[i]);
+ }
+
+ /* Some attributes may not be used in the shader anymore and were not garbage collected yet, so
+ * we need to find the right index for this attribute as uniforms defining the scope of the
+ * attributes are based on attribute loading order, which is itself based on the material's
+ * attributes. */
+ const int index = attribute_index_in_material(gpu_material, request.attribute_name);
+ if (index != -1) {
+ curves_infos.is_point_attribute[index] = request.domain == ATTR_DOMAIN_POINT;
+ }
+ }
+
+ curves_infos.push_update();
+
+ DRW_shgroup_uniform_block(shgrp, "drw_curves", curves_infos);
+
DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &curves_cache->final[subdiv].strands_res, 1);
DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape);
diff --git a/source/blender/draw/intern/draw_curves_private.h b/source/blender/draw/intern/draw_curves_private.h
index 76d5f15319d..ed4dd50dfbe 100644
--- a/source/blender/draw/intern/draw_curves_private.h
+++ b/source/blender/draw/intern/draw_curves_private.h
@@ -7,6 +7,11 @@
#pragma once
+#include "BKE_attribute.h"
+#include "GPU_shader.h"
+
+#include "draw_attributes.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -35,6 +40,23 @@ typedef struct CurvesEvalFinalCache {
/* Points per curve, at least 2. */
int strands_res;
+
+ /* Attributes currently being or about to be drawn. */
+ DRW_Attributes attr_used;
+
+ /* Attributes which were used at some point. This is used for garbage collection, to remove
+ * attributes which are not used in shaders anymore due to user edits. */
+ DRW_Attributes attr_used_over_time;
+
+ /* Last time, in seconds, the `attr_used` and `attr_used_over_time` were exactly the same.
+ * If the delta between this time and the current scene time is greater than the timeout set in
+ * user preferences (`U.vbotimeout`) then garbage collection is performed. */
+ int last_attr_matching_time;
+
+ /* Output of the subdivision stage: vertex buffers sized to subdiv level. This is only attributes
+ * on point domain. */
+ GPUVertBuf *attributes_buf[GPU_MAX_ATTR];
+ GPUTexture *attributes_tex[GPU_MAX_ATTR];
} CurvesEvalFinalCache;
/* Curves procedural display: Evaluation is done on the GPU. */
@@ -56,6 +78,11 @@ typedef struct CurvesEvalCache {
CurvesEvalFinalCache final[MAX_HAIR_SUBDIV];
+ /* For point attributes, which need subdivision, these are the input data.
+ * For spline attributes, which need not subdivision, these are the final data. */
+ GPUVertBuf *proc_attributes_buf[GPU_MAX_ATTR];
+ GPUTexture *proc_attributes_tex[GPU_MAX_ATTR];
+
int strands_len;
int elems_len;
int point_len;
@@ -70,6 +97,8 @@ bool curves_ensure_procedural_data(struct Object *object,
int subdiv,
int thickness_res);
+void drw_curves_get_attribute_sampler_name(const char *layer_name, char r_sampler_name[32]);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index b3c4e21639d..1372b843442 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -479,6 +479,7 @@ void DRW_viewport_data_free(DRWData *drw_data)
MEM_freeN(drw_data->obinfos_ubo);
}
DRW_volume_ubos_pool_free(drw_data->volume_grids_ubos);
+ DRW_curves_ubos_pool_free(drw_data->curves_ubos);
MEM_freeN(drw_data);
}
@@ -1650,7 +1651,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
DRW_globals_update();
drw_debug_init();
- DRW_curves_init();
+ DRW_curves_init(DST.vmempool);
DRW_volume_init(DST.vmempool);
DRW_smoke_init(DST.vmempool);
@@ -2022,7 +2023,7 @@ void DRW_render_object_iter(
void (*callback)(void *vedata, Object *ob, RenderEngine *engine, struct Depsgraph *depsgraph))
{
const DRWContextState *draw_ctx = DRW_context_state_get();
- DRW_curves_init();
+ DRW_curves_init(DST.vmempool);
DRW_volume_init(DST.vmempool);
DRW_smoke_init(DST.vmempool);
@@ -2079,7 +2080,7 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type,
drw_manager_init(&DST, NULL, NULL);
- DRW_curves_init();
+ DRW_curves_init(DST.vmempool);
DRW_volume_init(DST.vmempool);
DRW_smoke_init(DST.vmempool);
@@ -2114,7 +2115,7 @@ void DRW_cache_restart(void)
DST.buffer_finish_called = false;
- DRW_curves_init();
+ DRW_curves_init(DST.vmempool);
DRW_volume_init(DST.vmempool);
DRW_smoke_init(DST.vmempool);
}
@@ -2433,7 +2434,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
/* Init engines */
drw_engines_init();
- DRW_curves_init();
+ DRW_curves_init(DST.vmempool);
DRW_volume_init(DST.vmempool);
DRW_smoke_init(DST.vmempool);
@@ -2607,7 +2608,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph,
/* Init engines */
drw_engines_init();
- DRW_curves_init();
+ DRW_curves_init(DST.vmempool);
DRW_volume_init(DST.vmempool);
DRW_smoke_init(DST.vmempool);
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index aa0c472be04..6d384c599d8 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -538,6 +538,8 @@ typedef struct DRWData {
struct DRWTexturePool *texture_pool;
/** Per stereo view data. Contains engine data and default framebuffers. */
struct DRWViewData *view_data[2];
+ /** Per draw-call curves object data. */
+ struct CurvesUniformBufPool *curves_ubos;
} DRWData;
/* ------------- DRAW MANAGER ------------ */
diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h
index db128fffde7..94c0c53dab7 100644
--- a/source/blender/draw/intern/draw_shader_shared.h
+++ b/source/blender/draw/intern/draw_shader_shared.h
@@ -1,12 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef GPU_SHADER
+# include "GPU_shader.h"
# include "GPU_shader_shared_utils.h"
typedef struct ViewInfos ViewInfos;
typedef struct ObjectMatrices ObjectMatrices;
typedef struct ObjectInfos ObjectInfos;
typedef struct VolumeInfos VolumeInfos;
+typedef struct CurvesInfos CurvesInfos;
#endif
#define DRW_SHADER_SHARED_H
@@ -16,6 +18,10 @@ typedef struct VolumeInfos VolumeInfos;
/* Define the maximum number of grid we allow in a volume UBO. */
#define DRW_GRID_PER_VOLUME_MAX 16
+/* Define the maximum number of attribute we allow in a curves UBO.
+ * This should be kept in sync with `GPU_ATTR_MAX` */
+#define DRW_ATTRIBUTE_PER_CURVES_MAX 15
+
struct ViewInfos {
/* View matrices */
float4x4 persmat;
@@ -79,6 +85,14 @@ struct VolumeInfos {
};
BLI_STATIC_ASSERT_ALIGN(VolumeInfos, 16)
+struct CurvesInfos {
+ /* Per attribute scope, follows loading order.
+ * NOTE: uint as bool in GLSL is 4 bytes. */
+ uint is_point_attribute[DRW_ATTRIBUTE_PER_CURVES_MAX];
+ int _pad;
+};
+BLI_STATIC_ASSERT_ALIGN(CurvesInfos, 16)
+
#define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors)
#define ObjectInfo (drw_infos[resource_id].drw_Infos)
#define ObjectColor (drw_infos[resource_id].drw_ObjectColor)
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
index d275b672366..e08aa1da420 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
@@ -14,6 +14,7 @@
#include "BKE_attribute.h"
+#include "draw_attributes.h"
#include "draw_subdivision.h"
#include "extract_mesh.h"
@@ -284,7 +285,7 @@ static void extract_attr_init(const MeshRenderData *mr,
void *UNUSED(tls_data),
int index)
{
- const DRW_MeshAttributes *attrs_used = &cache->attr_used;
+ const DRW_Attributes *attrs_used = &cache->attr_used;
const DRW_AttributeRequest &request = attrs_used->requests[index];
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
@@ -337,7 +338,7 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache,
void *UNUSED(tls_data),
int index)
{
- const DRW_MeshAttributes *attrs_used = &cache->attr_used;
+ const DRW_Attributes *attrs_used = &cache->attr_used;
const DRW_AttributeRequest &request = attrs_used->requests[index];
Mesh *coarse_mesh = subdiv_cache->mesh;
diff --git a/source/blender/draw/intern/shaders/draw_object_infos_info.hh b/source/blender/draw/intern/shaders/draw_object_infos_info.hh
index c74a043ec97..8fd55ea351f 100644
--- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh
+++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh
@@ -10,3 +10,7 @@ GPU_SHADER_CREATE_INFO(draw_object_infos)
GPU_SHADER_CREATE_INFO(draw_volume_infos)
.typedef_source("draw_shader_shared.h")
.uniform_buf(2, "VolumeInfos", "drw_volume", Frequency::BATCH);
+
+GPU_SHADER_CREATE_INFO(draw_curves_infos)
+ .typedef_source("draw_shader_shared.h")
+ .uniform_buf(2, "CurvesInfos", "drw_curves", Frequency::BATCH);