diff options
author | Hans Goudey <h.goudey@me.com> | 2022-04-22 18:43:48 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-04-22 18:44:01 +0300 |
commit | 5b87862ddca2cb1b9351a5e250e1743b7245f38b (patch) | |
tree | 53d6e591709d50bcf77ac444f2cb10c12c1874e9 /source/blender/draw/intern | |
parent | 8f05a547d539feb4a8da35ee15239fa46ff38083 (diff) |
Curves: Further split of curves draw code from particles
Extends the changes started in f31c3f8114616bb to completely separate
much of the DRW curves code from the particle hair drawing. In the short
term this increases duplication, but the idea is to simplify development
by making it easier to do larger changes to the new code, and the new
system will replace the particle hair at some point.
After this, only the shaders themselves are shared.
Differential Revision: https://developer.blender.org/D14699
Diffstat (limited to 'source/blender/draw/intern')
-rw-r--r-- | source/blender/draw/intern/draw_cache_impl_curves.cc | 67 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache_impl_particles.c | 2 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_common.h | 19 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_curves.cc | 324 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_curves_private.h | 77 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_hair.c | 97 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_hair_private.h | 10 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager.c | 20 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_shader.c | 24 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_shader.h | 5 |
10 files changed, 505 insertions, 140 deletions
diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index b084a5e5945..f2e747cb276 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -29,9 +29,11 @@ #include "GPU_material.h" #include "GPU_texture.h" +#include "DRW_render.h" + #include "draw_cache_impl.h" /* own include */ #include "draw_cache_inline.h" -#include "draw_hair_private.h" /* own include */ +#include "draw_curves_private.h" /* own include */ using blender::float3; using blender::IndexRange; @@ -41,7 +43,7 @@ using blender::Span; /* Curves GPUBatch Cache */ struct CurvesBatchCache { - ParticleHairCache hair; + CurvesEvalCache curves_cache; GPUBatch *edit_points; @@ -70,6 +72,33 @@ static void curves_batch_cache_init(Curves &curves) cache->is_dirty = false; } +static void curves_batch_cache_clear_data(CurvesEvalCache &curves_cache) +{ + /* TODO: more granular update tagging. */ + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_point_buf); + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_length_buf); + DRW_TEXTURE_FREE_SAFE(curves_cache.point_tex); + DRW_TEXTURE_FREE_SAFE(curves_cache.length_tex); + + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_strand_buf); + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_strand_seg_buf); + DRW_TEXTURE_FREE_SAFE(curves_cache.strand_tex); + DRW_TEXTURE_FREE_SAFE(curves_cache.strand_seg_tex); + + for (int i = 0; i < MAX_HAIR_SUBDIV; i++) { + GPU_VERTBUF_DISCARD_SAFE(curves_cache.final[i].proc_buf); + DRW_TEXTURE_FREE_SAFE(curves_cache.final[i].proc_tex); + for (int j = 0; j < MAX_THICKRES; j++) { + GPU_BATCH_DISCARD_SAFE(curves_cache.final[i].proc_hairs[j]); + } + } + + /* "Normal" legacy hairs */ + GPU_BATCH_DISCARD_SAFE(curves_cache.hairs); + GPU_VERTBUF_DISCARD_SAFE(curves_cache.pos); + GPU_INDEXBUF_DISCARD_SAFE(curves_cache.indices); +} + static void curves_batch_cache_clear(Curves &curves) { CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves.batch_cache); @@ -77,7 +106,8 @@ static void curves_batch_cache_clear(Curves &curves) return; } - particle_batch_cache_clear_hair(&cache->hair); + curves_batch_cache_clear_data(cache->curves_cache); + GPU_BATCH_DISCARD_SAFE(cache->edit_points); } @@ -116,7 +146,7 @@ void DRW_curves_batch_cache_free(Curves *curves) MEM_SAFE_FREE(curves->batch_cache); } -static void ensure_seg_pt_count(const Curves &curves, ParticleHairCache &curves_cache) +static void ensure_seg_pt_count(const Curves &curves, CurvesEvalCache &curves_cache) { if ((curves_cache.pos != nullptr && curves_cache.indices != nullptr) || (curves_cache.proc_point_buf != nullptr)) { @@ -169,7 +199,7 @@ static void curves_batch_cache_fill_segments_proc_pos(const Curves &curves_id, } static void curves_batch_cache_ensure_procedural_pos(Curves &curves, - ParticleHairCache &cache, + CurvesEvalCache &cache, GPUMaterial *gpu_material) { if (cache.proc_point_buf == nullptr || DRW_vbo_requested(cache.proc_point_buf)) { @@ -229,7 +259,7 @@ static void curves_batch_cache_fill_strands_data(const Curves &curves_id, } static void curves_batch_cache_ensure_procedural_strand_data(Curves &curves, - ParticleHairCache &cache) + CurvesEvalCache &cache) { GPUVertBufRaw data_step, seg_step; @@ -259,7 +289,7 @@ static void curves_batch_cache_ensure_procedural_strand_data(Curves &curves, cache.proc_strand_seg_buf); } -static void curves_batch_cache_ensure_procedural_final_points(ParticleHairCache &cache, int subdiv) +static void curves_batch_cache_ensure_procedural_final_points(CurvesEvalCache &cache, int subdiv) { /* Same format as point_tex. */ GPUVertFormat format = {0}; @@ -296,7 +326,7 @@ static void curves_batch_cache_fill_segments_indices(const Curves &curves, } static void curves_batch_cache_ensure_procedural_indices(Curves &curves, - ParticleHairCache &cache, + CurvesEvalCache &cache, const int thickness_res, const int subdiv) { @@ -330,7 +360,7 @@ static void curves_batch_cache_ensure_procedural_indices(Curves &curves, } bool curves_ensure_procedural_data(Object *object, - ParticleHairCache **r_hair_cache, + CurvesEvalCache **r_hair_cache, GPUMaterial *gpu_material, const int subdiv, const int thickness_res) @@ -339,30 +369,31 @@ bool curves_ensure_procedural_data(Object *object, Curves &curves = *static_cast<Curves *>(object->data); CurvesBatchCache &cache = curves_batch_cache_get(curves); - *r_hair_cache = &cache.hair; + *r_hair_cache = &cache.curves_cache; const int steps = 3; /* TODO: don't hard-code? */ (*r_hair_cache)->final[subdiv].strands_res = 1 << (steps + subdiv); /* Refreshed on combing and simulation. */ if ((*r_hair_cache)->proc_point_buf == nullptr) { - ensure_seg_pt_count(curves, cache.hair); - curves_batch_cache_ensure_procedural_pos(curves, cache.hair, gpu_material); + ensure_seg_pt_count(curves, cache.curves_cache); + curves_batch_cache_ensure_procedural_pos(curves, cache.curves_cache, gpu_material); need_ft_update = true; } /* Refreshed if active layer or custom data changes. */ if ((*r_hair_cache)->strand_tex == nullptr) { - curves_batch_cache_ensure_procedural_strand_data(curves, cache.hair); + curves_batch_cache_ensure_procedural_strand_data(curves, cache.curves_cache); } /* Refreshed only on subdiv count change. */ if ((*r_hair_cache)->final[subdiv].proc_buf == nullptr) { - curves_batch_cache_ensure_procedural_final_points(cache.hair, subdiv); + curves_batch_cache_ensure_procedural_final_points(cache.curves_cache, subdiv); need_ft_update = true; } if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == nullptr) { - curves_batch_cache_ensure_procedural_indices(curves, cache.hair, thickness_res, subdiv); + curves_batch_cache_ensure_procedural_indices( + curves, cache.curves_cache, thickness_res, subdiv); } return need_ft_update; @@ -385,10 +416,10 @@ void DRW_curves_batch_cache_create_requested(const Object *ob) CurvesBatchCache &cache = curves_batch_cache_get(*curves); if (DRW_batch_requested(cache.edit_points, GPU_PRIM_POINTS)) { - DRW_vbo_request(cache.edit_points, &cache.hair.proc_point_buf); + DRW_vbo_request(cache.edit_points, &cache.curves_cache.proc_point_buf); } - if (DRW_vbo_requested(cache.hair.proc_point_buf)) { - curves_batch_cache_ensure_procedural_pos(*curves, cache.hair, nullptr); + if (DRW_vbo_requested(cache.curves_cache.proc_point_buf)) { + curves_batch_cache_ensure_procedural_pos(*curves, cache.curves_cache, nullptr); } } diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index 545ffb16e9d..0f1ab967ca5 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -164,7 +164,7 @@ static void particle_batch_cache_clear_point(ParticlePointCache *point_cache) GPU_VERTBUF_DISCARD_SAFE(point_cache->pos); } -void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache) +static void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache) { /* TODO: more granular update tagging. */ GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf); diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index aa421158305..779ac43178c 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -56,16 +56,12 @@ struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object, struct DRWShadingGroup *shgrp, struct GPUMaterial *gpu_material); -struct DRWShadingGroup *DRW_shgroup_curves_create_sub(struct Object *object, - struct DRWShadingGroup *shgrp, - struct GPUMaterial *gpu_material); /** * \note Only valid after #DRW_hair_update(). */ struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md); -struct GPUVertBuf *DRW_curves_pos_buffer_get(struct Object *object); void DRW_hair_duplimat_get(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md, @@ -75,6 +71,21 @@ void DRW_hair_init(void); void DRW_hair_update(void); void DRW_hair_free(void); +/* draw_curves.cc */ + +/** + * \note Only valid after #DRW_curves_update(). + */ +struct GPUVertBuf *DRW_curves_pos_buffer_get(struct Object *object); + +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_update(void); +void DRW_curves_free(void); + /* draw_volume.cc */ /** diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc new file mode 100644 index 00000000000..139efdf4f8c --- /dev/null +++ b/source/blender/draw/intern/draw_curves.cc @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2017 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup draw + * + * \brief Contains procedural GPU hair drawing methods. + */ + +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" + +#include "DNA_customdata_types.h" + +#include "GPU_batch.h" +#include "GPU_capabilities.h" +#include "GPU_compute.h" +#include "GPU_material.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_vertex_buffer.h" + +#include "DRW_render.h" + +#include "draw_hair_private.h" +#include "draw_shader.h" + +#ifndef __APPLE__ +# define USE_TRANSFORM_FEEDBACK +# define USE_COMPUTE_SHADERS +#endif + +BLI_INLINE eParticleRefineShaderType drw_curves_shader_type_get() +{ +#ifdef USE_COMPUTE_SHADERS + if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) { + return PART_REFINE_SHADER_COMPUTE; + } +#endif +#ifdef USE_TRANSFORM_FEEDBACK + return PART_REFINE_SHADER_TRANSFORM_FEEDBACK; +#endif + return PART_REFINE_SHADER_TRANSFORM_FEEDBACK_WORKAROUND; +} + +#ifndef USE_TRANSFORM_FEEDBACK +typedef struct CurvesEvalCall { + struct CurvesEvalCall *next; + GPUVertBuf *vbo; + DRWShadingGroup *shgrp; + uint vert_len; +} CurvesEvalCall; + +static CurvesEvalCall *g_tf_calls = nullptr; +static int g_tf_id_offset; +static int g_tf_target_width; +static int g_tf_target_height; +#endif + +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 */ + +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) +{ + /* Initialize legacy hair too, to avoid verbosity in callers. */ + DRW_hair_init(); + +#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) + g_tf_pass = DRW_pass_create("Update Curves Pass", (DRWState)0); +#else + g_tf_pass = DRW_pass_create("Update Curves Pass", DRW_STATE_WRITE_COLOR); +#endif + + if (g_dummy_vbo == nullptr) { + /* initialize vertex format */ + GPUVertFormat format = {0}; + uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + g_dummy_vbo = GPU_vertbuf_create_with_format(&format); + + const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPU_vertbuf_data_alloc(g_dummy_vbo, 1); + GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert); + /* Create vbo immediately to bind to texture buffer. */ + GPU_vertbuf_use(g_dummy_vbo); + + g_dummy_texture = GPU_texture_create_from_vertbuf("hair_dummy_attr", g_dummy_vbo); + } +} + +static void drw_curves_cache_shgrp_attach_resources(DRWShadingGroup *shgrp, + CurvesEvalCache *cache, + const int subdiv) +{ + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_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 = 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; + } + } +} + +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); + +#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); + + CurvesEvalCall *pr_call = MEM_mallocN(sizeof(*pr_call), __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 + + drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); + DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); + } +} + +static CurvesEvalCache *drw_curves_cache_get(Object *object, + GPUMaterial *gpu_material, + int subdiv, + int thickness_res) +{ + CurvesEvalCache *cache; + bool update = curves_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res); + + if (update) { + if (drw_curves_shader_type_get() == PART_REFINE_SHADER_COMPUTE) { + drw_curves_cache_update_compute(cache, subdiv); + } + else { + drw_curves_cache_update_transform_feedback(cache, subdiv); + } + } + return cache; +} + +GPUVertBuf *DRW_curves_pos_buffer_get(Object *object) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + + int subdiv = scene->r.hair_subdiv; + int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; + + CurvesEvalCache *cache = drw_curves_cache_get(object, nullptr, subdiv, thickness_res); + + return cache->final[subdiv].proc_buf; +} + +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; + + int subdiv = scene->r.hair_subdiv; + int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; + + CurvesEvalCache *curves_cache = drw_curves_cache_get( + object, gpu_material, subdiv, thickness_res); + + DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent); + + /* Fix issue with certain driver not drawing anything if there is no texture bound to + * "ac", "au", "u" or "c". */ + DRW_shgroup_uniform_texture(shgrp, "u", g_dummy_texture); + DRW_shgroup_uniform_texture(shgrp, "au", g_dummy_texture); + DRW_shgroup_uniform_texture(shgrp, "c", g_dummy_texture); + DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); + + /* TODO: Generalize radius implementation for curves data type. */ + float hair_rad_shape = 1.0f; + float hair_rad_root = 0.005f; + float hair_rad_tip = 0.0f; + bool hair_close_tip = true; + + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", curves_cache->final[subdiv].proc_tex); + if (curves_cache->length_tex) { + DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex); + } + 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); + DRW_shgroup_uniform_mat4_copy(shgrp, "hairDupliMatrix", object->obmat); + DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root); + DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip); + DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip); + /* TODO(fclem): Until we have a better way to cull the curves and render with orco, bypass + * culling test. */ + GPUBatch *geom = curves_cache->final[subdiv].proc_hairs[thickness_res - 1]; + DRW_shgroup_call_no_cull(shgrp, geom, object); + + return shgrp; +} + +void DRW_curves_update() +{ + /* Update legacy hair too, to avoid verbosity in callers. */ + DRW_hair_update(); + +#ifndef USE_TRANSFORM_FEEDBACK + /** + * Workaround to transform feedback not working on mac. + * On some system it crashes (see T58489) and on some other it renders garbage (see T60171). + * + * So instead of using transform feedback we render to a texture, + * read back the result to system memory and re-upload as VBO data. + * It is really not ideal performance wise, but it is the simplest + * and the most local workaround that still uses the power of the GPU. + */ + + if (g_tf_calls == nullptr) { + return; + } + + /* Search ideal buffer size. */ + uint max_size = 0; + for (CurvesEvalCall *pr_call = g_tf_calls; pr_call; pr_call = pr_call->next) { + max_size = max_ii(max_size, pr_call->vert_len); + } + + /* Create target Texture / Frame-buffer */ + /* Don't use max size as it can be really heavy and fail. + * Do chunks of maximum 2048 * 2048 hair points. */ + int width = 2048; + int height = min_ii(width, 1 + max_size / width); + GPUTexture *tex = DRW_texture_pool_query_2d( + width, height, GPU_RGBA32F, (void *)DRW_curves_update); + g_tf_target_height = height; + g_tf_target_width = width; + + GPUFrameBuffer *fb = nullptr; + GPU_framebuffer_ensure_config(&fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(tex), + }); + + float *data = MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer"); + + GPU_framebuffer_bind(fb); + while (g_tf_calls != nullptr) { + CurvesEvalCall *pr_call = g_tf_calls; + g_tf_calls = g_tf_calls->next; + + g_tf_id_offset = 0; + while (pr_call->vert_len > 0) { + int max_read_px_len = min_ii(width * height, pr_call->vert_len); + + DRW_draw_pass_subset(g_tf_pass, pr_call->shgrp, pr_call->shgrp); + /* Readback result to main memory. */ + GPU_framebuffer_read_color(fb, 0, 0, width, height, 4, 0, GPU_DATA_FLOAT, data); + /* Upload back to VBO. */ + GPU_vertbuf_use(pr_call->vbo); + GPU_vertbuf_update_sub(pr_call->vbo, + sizeof(float[4]) * g_tf_id_offset, + sizeof(float[4]) * max_read_px_len, + data); + + g_tf_id_offset += max_read_px_len; + pr_call->vert_len -= max_read_px_len; + } + + MEM_freeN(pr_call); + } + + MEM_freeN(data); + GPU_framebuffer_free(fb); +#else + /* Just render the pass when using compute shaders or transform feedback. */ + DRW_draw_pass(g_tf_pass); + if (drw_curves_shader_type_get() == PART_REFINE_SHADER_COMPUTE) { + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + } +#endif +} + +void DRW_curves_free() +{ + DRW_hair_free(); + + GPU_VERTBUF_DISCARD_SAFE(g_dummy_vbo); + DRW_TEXTURE_FREE_SAFE(g_dummy_texture); +} diff --git a/source/blender/draw/intern/draw_curves_private.h b/source/blender/draw/intern/draw_curves_private.h new file mode 100644 index 00000000000..f92e051daca --- /dev/null +++ b/source/blender/draw/intern/draw_curves_private.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2017 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup draw + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_THICKRES 2 /* see eHairType */ +#define MAX_HAIR_SUBDIV 4 /* see hair_subdiv rna */ + +typedef enum CurvesEvalShader { + CURVES_EVAL_CATMULL_ROM = 0, + CURVES_EVAL_BEZIER = 1, +} CurvesEvalShader; +#define CURVES_EVAL_SHADER_NUM 3 + +struct GPUVertBuf; +struct GPUIndexBuf; +struct GPUBatch; +struct GPUTexture; + +typedef struct CurvesEvalFinalCache { + /* Output of the subdivision stage: vertex buff sized to subdiv level. */ + GPUVertBuf *proc_buf; + GPUTexture *proc_tex; + + /* Just contains a huge index buffer used to draw the final hair. */ + GPUBatch *proc_hairs[MAX_THICKRES]; + + int strands_res; /* points per hair, at least 2 */ +} CurvesEvalFinalCache; + +typedef struct CurvesEvalCache { + GPUVertBuf *pos; + GPUIndexBuf *indices; + GPUBatch *hairs; + + /* Hair Procedural display: Interpolation is done on the GPU. */ + GPUVertBuf *proc_point_buf; /* Input control points */ + GPUTexture *point_tex; + + /** Infos of control points strands (segment count and base index) */ + GPUVertBuf *proc_strand_buf; + GPUTexture *strand_tex; + + /* Hair Length */ + GPUVertBuf *proc_length_buf; + GPUTexture *length_tex; + + GPUVertBuf *proc_strand_seg_buf; + GPUTexture *strand_seg_tex; + + CurvesEvalFinalCache final[MAX_HAIR_SUBDIV]; + + int strands_len; + int elems_len; + int point_len; +} CurvesEvalCache; + +/** + * Ensure all textures and buffers needed for GPU accelerated drawing. + */ +bool curves_ensure_procedural_data(struct Object *object, + struct CurvesEvalCache **r_hair_cache, + struct GPUMaterial *gpu_material, + int subdiv, + int thickness_res); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index aac6f7e58c5..8351452769d 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -179,25 +179,6 @@ static ParticleHairCache *drw_hair_particle_cache_get(Object *object, return cache; } -static ParticleHairCache *drw_curves_cache_get(Object *object, - GPUMaterial *gpu_material, - int subdiv, - int thickness_res) -{ - ParticleHairCache *cache; - bool update = curves_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res); - - if (update) { - if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) { - drw_hair_particle_cache_update_compute(cache, subdiv); - } - else { - drw_hair_particle_cache_update_transform_feedback(cache, subdiv); - } - } - return cache; -} - GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, ModifierData *md) { const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -212,19 +193,6 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi return cache->final[subdiv].proc_buf; } -GPUVertBuf *DRW_curves_pos_buffer_get(Object *object) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - - int subdiv = scene->r.hair_subdiv; - int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; - - ParticleHairCache *cache = drw_curves_cache_get(object, NULL, subdiv, thickness_res); - - return cache->final[subdiv].proc_buf; -} - void DRW_hair_duplimat_get(Object *object, ParticleSystem *UNUSED(psys), ModifierData *UNUSED(md), @@ -323,71 +291,6 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, return shgrp; } -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; - - int subdiv = scene->r.hair_subdiv; - int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; - - ParticleHairCache *curves_cache = drw_curves_cache_get( - object, gpu_material, subdiv, thickness_res); - - DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent); - - /* TODO: optimize this. Only bind the ones GPUMaterial needs. */ - for (int i = 0; i < curves_cache->num_uv_layers; i++) { - for (int n = 0; n < MAX_LAYER_NAME_CT && curves_cache->uv_layer_names[i][n][0] != '\0'; n++) { - DRW_shgroup_uniform_texture( - shgrp, curves_cache->uv_layer_names[i][n], curves_cache->uv_tex[i]); - } - } - for (int i = 0; i < curves_cache->num_col_layers; i++) { - for (int n = 0; n < MAX_LAYER_NAME_CT && curves_cache->col_layer_names[i][n][0] != '\0'; n++) { - DRW_shgroup_uniform_texture( - shgrp, curves_cache->col_layer_names[i][n], curves_cache->col_tex[i]); - } - } - - /* Fix issue with certain driver not drawing anything if there is no texture bound to - * "ac", "au", "u" or "c". */ - if (curves_cache->num_uv_layers == 0) { - DRW_shgroup_uniform_texture(shgrp, "u", g_dummy_texture); - DRW_shgroup_uniform_texture(shgrp, "au", g_dummy_texture); - } - if (curves_cache->num_col_layers == 0) { - DRW_shgroup_uniform_texture(shgrp, "c", g_dummy_texture); - DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); - } - - /* TODO: Generalize radius implementation for curves data type. */ - float hair_rad_shape = 1.0f; - float hair_rad_root = 0.005f; - float hair_rad_tip = 0.0f; - bool hair_close_tip = true; - - DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", curves_cache->final[subdiv].proc_tex); - if (curves_cache->length_tex) { - DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex); - } - 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); - DRW_shgroup_uniform_mat4_copy(shgrp, "hairDupliMatrix", object->obmat); - DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root); - DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip); - DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip); - /* TODO(fclem): Until we have a better way to cull the curves and render with orco, bypass - * culling test. */ - GPUBatch *geom = curves_cache->final[subdiv].proc_hairs[thickness_res - 1]; - DRW_shgroup_call_no_cull(shgrp, geom, object); - - return shgrp; -} - void DRW_hair_update(void) { #ifndef USE_TRANSFORM_FEEDBACK diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h index 58e8609106b..c605a4c1587 100644 --- a/source/blender/draw/intern/draw_hair_private.h +++ b/source/blender/draw/intern/draw_hair_private.h @@ -75,7 +75,6 @@ typedef struct ParticleHairCache { int point_len; } ParticleHairCache; -void particle_batch_cache_clear_hair(struct ParticleHairCache *hair_cache); /** * Ensure all textures and buffers needed for GPU accelerated drawing. @@ -88,15 +87,6 @@ bool particles_ensure_procedural_data(struct Object *object, int subdiv, int thickness_res); -/** - * Ensure all textures and buffers needed for GPU accelerated drawing. - */ -bool curves_ensure_procedural_data(struct Object *object, - struct ParticleHairCache **r_hair_cache, - struct GPUMaterial *gpu_material, - int subdiv, - int thickness_res); - #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index e0dfc29b966..adf9d4a13df 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1650,7 +1650,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_globals_update(); drw_debug_init(); - DRW_hair_init(); + DRW_curves_init(); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -1707,7 +1707,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, GPU_framebuffer_bind(DST.default_framebuffer); GPU_framebuffer_clear_depth_stencil(DST.default_framebuffer, 1.0f, 0xFF); - DRW_hair_update(); + DRW_curves_update(); DRW_draw_callbacks_pre_scene(); @@ -2022,7 +2022,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_hair_init(); + DRW_curves_init(); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2079,7 +2079,7 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, drw_manager_init(&DST, NULL, NULL); - DRW_hair_init(); + DRW_curves_init(); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2114,7 +2114,7 @@ void DRW_cache_restart(void) DST.buffer_finish_called = false; - DRW_hair_init(); + DRW_curves_init(); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); } @@ -2433,7 +2433,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, /* Init engines */ drw_engines_init(); - DRW_hair_init(); + DRW_curves_init(); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2512,7 +2512,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, DRW_state_reset(); DRW_draw_callbacks_pre_scene(); - DRW_hair_update(); + DRW_curves_update(); /* Only 1-2 passes. */ while (true) { @@ -2607,7 +2607,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, /* Init engines */ drw_engines_init(); - DRW_hair_init(); + DRW_curves_init(); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2642,7 +2642,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, /* Start Drawing */ DRW_state_reset(); - DRW_hair_update(); + DRW_curves_update(); drw_engines_draw_scene(); @@ -3033,7 +3033,7 @@ void DRW_engines_free(void) GPU_FRAMEBUFFER_FREE_SAFE(g_select_buffer.framebuffer_depth_only); DRW_shaders_free(); - DRW_hair_free(); + DRW_curves_free(); DRW_volume_free(); DRW_shape_cache_free(); DRW_stats_free(); diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.c index 53da300c106..487a09d313d 100644 --- a/source/blender/draw/intern/draw_shader.c +++ b/source/blender/draw/intern/draw_shader.c @@ -92,6 +92,30 @@ GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, return e_data.hair_refine_sh[refinement]; } +GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineShaderType sh_type) +{ + /* TODO: Implement curves evaluation types (Bezier and Catmull Rom). */ + if (e_data.hair_refine_sh[type] == NULL) { + GPUShader *sh = NULL; + switch (sh_type) { + case PART_REFINE_SHADER_COMPUTE: + sh = hair_refine_shader_compute_create(type); + break; + case PART_REFINE_SHADER_TRANSFORM_FEEDBACK: + sh = hair_refine_shader_transform_feedback_create(type); + break; + case PART_REFINE_SHADER_TRANSFORM_FEEDBACK_WORKAROUND: + sh = hair_refine_shader_transform_feedback_workaround_create(type); + break; + default: + BLI_assert_msg(0, "Incorrect shader type"); + } + e_data.hair_refine_sh[type] = sh; + } + + return e_data.hair_refine_sh[type]; +} + /** \} */ void DRW_shaders_free(void) diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h index 65b9cafc1d9..650e78c9362 100644 --- a/source/blender/draw/intern/draw_shader.h +++ b/source/blender/draw/intern/draw_shader.h @@ -7,6 +7,7 @@ #pragma once +#include "draw_curves_private.h" #include "draw_hair_private.h" #ifdef __cplusplus @@ -25,6 +26,10 @@ typedef enum eParticleRefineShaderType { struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, eParticleRefineShaderType sh_type); + +struct GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, + eParticleRefineShaderType sh_type); + void DRW_shaders_free(void); #ifdef __cplusplus |