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
This commit is contained in:
Kévin Dietrich 2022-05-24 05:02:57 +02:00
parent 64a5a7ade1
commit cd968a3273
Notes: blender-bot 2023-02-14 09:48:25 +01:00
Referenced by issue #99129, Viewport/Eevee: hair_info_length regression test not working on AMD GPUs
Referenced by issue #97536, Eevee do not respect the new curve object attribute
Referenced by issue #96455, Render engine support for curves data-block
21 changed files with 737 additions and 202 deletions

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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:
/**

View File

@ -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;
}
/** \} */

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);
/** \} */

View File

@ -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;
}

View File

@ -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) {

View File

@ -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);

View File

@ -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,
GPUVertBuf *vbo,
GPUTexture *tex,
const int subdiv,
const int final_points_len)
{
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, 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 = 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, 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) {
GPUShader *tf_shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM);
if (final_points_len == 0) {
return;
}
#ifdef USE_TRANSFORM_FEEDBACK
DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(
tf_shader, g_tf_pass, cache->final[subdiv].proc_buf);
#else
DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
drw_curves_cache_update_transform_feedback(
cache, cache->final[subdiv].proc_buf, cache->point_tex, subdiv, final_points_len);
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);
#endif
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_shgrp_attach_resources(tf_shgrp, cache, subdiv);
DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len);
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);

View File

@ -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

View File

@ -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);

View File

@ -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 ------------ */

View File

@ -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)

View File

@ -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;

View File

@ -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);