diff options
-rw-r--r-- | source/blender/draw/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_lightcache.c | 2 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_motion_blur.c | 4 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_render.c | 4 | ||||
-rw-r--r-- | source/blender/draw/engines/workbench/workbench_render.c | 4 | ||||
-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 |
15 files changed, 514 insertions, 147 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index b196d56fae2..8c54c923476 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -82,6 +82,7 @@ set(SRC intern/draw_cache_impl_volume.c intern/draw_color_management.cc intern/draw_common.c + intern/draw_curves.cc intern/draw_debug.c intern/draw_fluid.c intern/draw_hair.c @@ -195,6 +196,7 @@ set(SRC intern/draw_color_management.h intern/draw_common.h intern/draw_common_shader_shared.h + intern/draw_curves_private.h intern/draw_debug.h intern/draw_hair_private.h intern/draw_instance_data.h diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index f18e4db876b..4f562dd9804 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -965,7 +965,7 @@ static void eevee_lightbake_cache_create(EEVEE_Data *vedata, EEVEE_LightBake *lb txl->color = NULL; DRW_render_instance_buffer_finish(); - DRW_hair_update(); + DRW_curves_update(); } static void eevee_lightbake_copy_irradiance(EEVEE_LightBake *lbake, LightCache *lcache) diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index e3342508a14..2e0937dbe49 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -440,9 +440,9 @@ void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata) DRW_render_instance_buffer_finish(); /* Need to be called after #DRW_render_instance_buffer_finish() */ - /* Also we weed to have a correct FBO bound for #DRW_hair_update. */ + /* Also we weed to have a correct FBO bound for #DRW_curves_update. */ GPU_framebuffer_bind(vedata->fbl->main_fb); - DRW_hair_update(); + DRW_curves_update(); DRW_cache_restart(); } diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index 47e2b95f367..bef19c589c2 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -538,9 +538,9 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl DRW_render_instance_buffer_finish(); /* Need to be called after DRW_render_instance_buffer_finish() */ - /* Also we weed to have a correct FBO bound for DRW_hair_update */ + /* Also we weed to have a correct FBO bound for DRW_curves_update */ GPU_framebuffer_bind(fbl->main_fb); - DRW_hair_update(); + DRW_curves_update(); /* Sort transparents before the loop. */ DRW_pass_sort_shgroup_z(psl->transparent_pass); diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index 72de3fe298a..1279682e899 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -170,9 +170,9 @@ void workbench_render(void *ved, RenderEngine *engine, RenderLayer *render_layer DRW_render_instance_buffer_finish(); - /* Also we weed to have a correct FBO bound for #DRW_hair_update */ + /* Also we weed to have a correct FBO bound for #DRW_curves_update */ GPU_framebuffer_bind(dfbl->default_fb); - DRW_hair_update(); + DRW_curves_update(); GPU_framebuffer_bind(dfbl->default_fb); GPU_framebuffer_clear_depth(dfbl->default_fb, 1.0f); 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 |