diff options
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); |