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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/draw/CMakeLists.txt2
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c4
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c4
-rw-r--r--source/blender/draw/engines/workbench/workbench_render.c4
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curves.cc67
-rw-r--r--source/blender/draw/intern/draw_cache_impl_particles.c2
-rw-r--r--source/blender/draw/intern/draw_common.h19
-rw-r--r--source/blender/draw/intern/draw_curves.cc324
-rw-r--r--source/blender/draw/intern/draw_curves_private.h77
-rw-r--r--source/blender/draw/intern/draw_hair.c97
-rw-r--r--source/blender/draw/intern/draw_hair_private.h10
-rw-r--r--source/blender/draw/intern/draw_manager.c20
-rw-r--r--source/blender/draw/intern/draw_shader.c24
-rw-r--r--source/blender/draw/intern/draw_shader.h5
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