diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2022-08-22 18:14:16 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2022-08-22 21:55:09 +0300 |
commit | 46dc57af82e629b854ad4af12ccb3e43ea509b0e (patch) | |
tree | 8745e248d93efcabc822d9b965dc786c6438af7d | |
parent | acfce5c4eb77e225ae1465c7043f5ec724abc314 (diff) |
Add View support with visibility culling
-rw-r--r-- | source/blender/draw/CMakeLists.txt | 3 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_defines.h | 7 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_handle.hh | 15 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager.cc | 15 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager.hh | 30 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_resource.hh | 73 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_shader_shared.h | 12 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_view.cc | 292 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_view.hh | 44 | ||||
-rw-r--r-- | source/blender/draw/intern/shaders/common_intersect_lib.glsl | 68 | ||||
-rw-r--r-- | source/blender/draw/intern/shaders/draw_object_infos_info.hh | 11 | ||||
-rw-r--r-- | source/blender/draw/intern/shaders/draw_view_info.hh | 47 | ||||
-rw-r--r-- | source/blender/draw/intern/shaders/draw_visibility_comp.glsl | 30 |
13 files changed, 548 insertions, 99 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 3aef9f776b6..377baeb8361 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -232,8 +232,9 @@ set(SRC intern/draw_shader.h intern/draw_subdivision.h intern/draw_texture_pool.h - intern/draw_view.h intern/draw_view_data.h + intern/draw_view.cc + intern/draw_view.h intern/mesh_extractors/extract_mesh.hh intern/smaa_textures.h engines/basic/basic_engine.h diff --git a/source/blender/draw/intern/draw_defines.h b/source/blender/draw/intern/draw_defines.h index 9ab57284f8c..a8731dedaf8 100644 --- a/source/blender/draw/intern/draw_defines.h +++ b/source/blender/draw/intern/draw_defines.h @@ -11,8 +11,13 @@ #pragma once +#define DRW_VIEW_UBO_SLOT 0 + #define DRW_OBJ_MAT_SLOT 7 -#define DRW_OBJ_ATTR_SLOT 6 +#define DRW_COMMAND_SLOT 6 +#define DRW_OBJ_INFOS_SLOT 5 +#define DRW_OBJ_ATTR_SLOT 4 #define DRW_FINALIZE_GROUP_SIZE 64 +/* Must be multiple of 32. Set to 64 for optimal scheduling on GCN hardware. */ #define DRW_VISIBILITY_GROUP_SIZE 64
\ No newline at end of file diff --git a/source/blender/draw/intern/draw_handle.hh b/source/blender/draw/intern/draw_handle.hh index 3696a578ea7..48f3391c9ed 100644 --- a/source/blender/draw/intern/draw_handle.hh +++ b/source/blender/draw/intern/draw_handle.hh @@ -18,11 +18,11 @@ * the origin. */ -#include "BKE_duplilist.h" -#include "DNA_object_types.h" - #include "draw_shader_shared.h" +struct Object; +struct DupliObject; + namespace blender::draw { struct ResourceHandle { @@ -47,4 +47,13 @@ struct ResourceHandle { } }; +/* TODO(fclem): Move to somewhere more appropriated after cleaning up the header dependencies. */ +struct ObjectRef { + Object *object; + /** Dupli object that corresponds to the current object. */ + DupliObject *dupli_object; + /** Object that created the dupli-list the current object is part of. */ + Object *dupli_parent; +}; + }; // namespace blender::draw diff --git a/source/blender/draw/intern/draw_manager.cc b/source/blender/draw/intern/draw_manager.cc index 45597ea8e42..b30fae953b8 100644 --- a/source/blender/draw/intern/draw_manager.cc +++ b/source/blender/draw/intern/draw_manager.cc @@ -54,16 +54,21 @@ void Manager::submit(const PassSimple &pass) pass.submit(state); } -#if 0 -void Manager::submit(const PassMain &pass, const View &view) +void Manager::submit(const PassMain &pass, View &view) { - view.compute_visilibity(bounds_buf, resource_len); + view.bind(); - GPU_uniformbuf_bind(view); + view.compute_visibility(bounds_buf, resource_len); + + // GPU_storagebuf_bind(pass.commands_, DRW_COMMAND_SLOT); + GPU_storagebuf_bind(matrix_buf, DRW_OBJ_MAT_SLOT); + GPU_storagebuf_bind(infos_buf, DRW_OBJ_INFOS_SLOT); + // GPU_storagebuf_bind(attribute_buf, DRW_OBJ_ATTR_SLOT); /* TODO */ + + // pass.generate_commands(state); command::RecordingState state; pass.submit(state); } -#endif } // namespace blender::draw diff --git a/source/blender/draw/intern/draw_manager.hh b/source/blender/draw/intern/draw_manager.hh index c5160a81a5c..d84f4de8ca1 100644 --- a/source/blender/draw/intern/draw_manager.hh +++ b/source/blender/draw/intern/draw_manager.hh @@ -11,19 +11,13 @@ #include "BLI_sys_types.h" #include "draw_pass.hh" +#include "draw_resource.hh" +#include "draw_view.hh" #include <string> namespace blender::draw { -struct ObjectRef { - Object *object; - /** Dupli object that corresponds to the current object. */ - DupliObject *dupli_object; - /** Object that created the dupli-list the current object is part of. */ - Object *dupli_parent; -}; - class Manager { using ObjectMatricesBuf = StorageArrayBuffer<ObjectMatrices, 128>; using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>; @@ -59,14 +53,16 @@ class Manager { /** * Populate additional per resource data on demand. */ - void object_attributes(ResourceHandle handle, Object &object, Span<GPUMaterial *> materials); + void extract_object_attributes(ResourceHandle handle, + Object &object, + Span<GPUMaterial *> materials); /** * Submit a pass for drawing. All resource reference will be dereferenced and commands will be * sent to GPU. */ void submit(const PassSimple &pass); - void submit(const PassMain &pass); + void submit(const PassMain &pass, View &view); private: /** @@ -83,21 +79,21 @@ class Manager { inline ResourceHandle Manager::resource_handle(const ObjectRef ref) { bool is_active_object = (ref.dupli_object ? ref.dupli_parent : ref.object) == object_active; - matrix_buf.get_or_resize(resource_len).init(*ref.object); - bounds_buf.get_or_resize(resource_len).init(*ref.object); - infos_buf.get_or_resize(resource_len).init(ref, is_active_object); + matrix_buf.get_or_resize(resource_len).sync(*ref.object); + bounds_buf.get_or_resize(resource_len).sync(*ref.object); + infos_buf.get_or_resize(resource_len).sync(ref, is_active_object); return ResourceHandle(resource_len++, (ref.object->transflag & OB_NEG_SCALE) != 0); } inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix) { - matrix_buf.get_or_resize(resource_len).init(model_matrix); + matrix_buf.get_or_resize(resource_len).sync(model_matrix); return ResourceHandle(resource_len++, false); } -inline void Manager::object_attributes(ResourceHandle handle, - Object &object, - Span<GPUMaterial *> materials) +inline void Manager::extract_object_attributes(ResourceHandle handle, + Object &object, + Span<GPUMaterial *> materials) { /* TODO */ (void)handle; diff --git a/source/blender/draw/intern/draw_resource.hh b/source/blender/draw/intern/draw_resource.hh index 9761c83f964..68e3076d5fa 100644 --- a/source/blender/draw/intern/draw_resource.hh +++ b/source/blender/draw/intern/draw_resource.hh @@ -10,27 +10,34 @@ * Each of them are reference by resource index (#ResourceHandle). */ +#include "BKE_curve.h" #include "BKE_duplilist.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_volume.h" +#include "BLI_hash.h" +#include "DNA_curve_types.h" +#include "DNA_meta_types.h" #include "DNA_object_types.h" +#include "draw_handle.hh" +#include "draw_manager.hh" #include "draw_shader_shared.h" -namespace blender::draw { - /* -------------------------------------------------------------------- */ /** \name ObjectMatrices * \{ */ -void ObjectMatrices::sync(const Object &object) +inline void ObjectMatrices::sync(const Object &object) { - model = object->obmat; - model_inverse = object->imat; + model = object.obmat; + model_inverse = object.imat; } -void ObjectMatrices::sync(const float4x4 &object_mat) +inline void ObjectMatrices::sync(const float4x4 &model_matrix) { - model = object_mat; - model_inverse = object_mat.inverted(); + model = model_matrix; + model_inverse = model_matrix.inverted(); } /** \} */ @@ -39,53 +46,59 @@ void ObjectMatrices::sync(const float4x4 &object_mat) /** \name ObjectInfos * \{ */ -void ObjectInfos::sync(const draw::ObjectRef ref, bool is_active_object) +ENUM_OPERATORS(eObjectInfoFlag, OBJECT_NEGATIVE_SCALE) + +inline void ObjectInfos::sync(const blender::draw::ObjectRef ref, bool is_active_object) { - color = ref.object.color; - index = ref.object.index; - SET_FLAG_FROM_TEST(flag, is_active_object, DRW_OBJECT_ACTIVE); - SET_FLAG_FROM_TEST(flag, ref.object.base_flag & BASE_SELECTED, DRW_OBJECT_SELECTED); - SET_FLAG_FROM_TEST(flag, ref.object.base_flag & BASE_FROM_DUPLI, DRW_OBJECT_FROM_DUPLI); - SET_FLAG_FROM_TEST(flag, ref.object.base_flag & BASE_FROM_SET, DRW_OBJECT_FROM_SET); - SET_FLAG_FROM_TEST(flag, ref.object.transflag & OB_NEG_SCALE, DRW_OBJECT_NEGATIVE_SCALE); - - if (ref.dupli == nullptr) { - /* TODO(fclem): this is rather costly to do at runtime. Maybe we can + color = ref.object->color; + index = ref.object->index; + SET_FLAG_FROM_TEST(flag, is_active_object, eObjectInfoFlag::OBJECT_ACTIVE); + SET_FLAG_FROM_TEST( + flag, ref.object->base_flag & BASE_SELECTED, eObjectInfoFlag::OBJECT_SELECTED); + SET_FLAG_FROM_TEST( + flag, ref.object->base_flag & BASE_FROM_DUPLI, eObjectInfoFlag::OBJECT_FROM_DUPLI); + SET_FLAG_FROM_TEST( + flag, ref.object->base_flag & BASE_FROM_SET, eObjectInfoFlag::OBJECT_FROM_SET); + SET_FLAG_FROM_TEST( + flag, ref.object->transflag & OB_NEG_SCALE, eObjectInfoFlag::OBJECT_NEGATIVE_SCALE); + + if (ref.dupli_object == nullptr) { + /* TODO(fclem): this is rather costly to do at draw time. Maybe we can * put it in ob->runtime and make depsgraph ensure it is up to date. */ - random = BLI_hash_int_2d(BLI_hash_string(ref.object.id.name + 2), 0) * (1.0f / 0xFFFFFFFF); + random = BLI_hash_int_2d(BLI_hash_string(ref.object->id.name + 2), 0) * (1.0f / 0xFFFFFFFF); } else { - random = ref.dupli.random_id * (1.0f / 0xFFFFFFFF); + random = ref.dupli_object->random_id * (1.0f / 0xFFFFFFFF); } /* Default values. Set if needed. */ random = 0.0f; - if (ref.object.data == nullptr) { + if (ref.object->data == nullptr) { orco_add = float3(0.0f); orco_mul = float3(1.0f); return; } - switch (GS(ref.object.data->name)) { + switch (GS(reinterpret_cast<ID *>(ref.object->data)->name)) { case ID_VO: { - BoundBox &bbox = *BKE_volume_boundbox_get(object); + BoundBox &bbox = *BKE_volume_boundbox_get(ref.object); orco_add = (float3(bbox.vec[6]) + float3(bbox.vec[0])) * 0.5f; /* Center. */ orco_mul = float3(bbox.vec[6]) - float3(bbox.vec[0]); /* Size. */ break; } case ID_ME: { - BKE_mesh_texspace_get((Mesh *)ref.object.data, orco_add, orco_mul); + BKE_mesh_texspace_get((Mesh *)ref.object->data, orco_add, orco_mul); break; } case ID_CU_LEGACY: { - Curve &cu = *(Curve *)ref.object.data; + Curve &cu = *(Curve *)ref.object->data; BKE_curve_texspace_ensure(&cu); orco_add = cu.loc; orco_mul = cu.size; break; } case ID_MB: { - MetaBall &mb = *(MetaBall *)ref.object.data; + MetaBall &mb = *(MetaBall *)ref.object->data; orco_add = mb.loc; orco_mul = mb.size; break; @@ -103,9 +116,9 @@ void ObjectInfos::sync(const draw::ObjectRef ref, bool is_active_object) /** \name ObjectBounds * \{ */ -void ObjectBounds::sync(const Object &ob) +inline void ObjectBounds::sync(Object &ob) { - const BoundBox *bbox = BKE_object_boundbox_get(ob); + const BoundBox *bbox = BKE_object_boundbox_get(&ob); if (bbox == nullptr) { bounding_sphere.w = -1.0f; /* Disable test. */ return; @@ -118,5 +131,3 @@ void ObjectBounds::sync(const Object &ob) } /** \} */ - -}; // namespace blender::draw diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index 9ef5601f566..e343098aaf3 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -103,8 +103,8 @@ struct ObjectMatrices { float4x4 model_inverse; #if !defined(GPU_SHADER) && defined(__cplusplus) - void init(const Object &object); - void init(const float4x4 &object_mat); + void sync(const Object &object); + void sync(const float4x4 &model_matrix); #endif }; BLI_STATIC_ASSERT_ALIGN(ObjectMatrices, 16) @@ -138,15 +138,11 @@ struct ObjectInfos { #endif #if !defined(GPU_SHADER) && defined(__cplusplus) - void init(const blender::draw::ObjectRef ref, bool is_active_object); + void sync(const blender::draw::ObjectRef ref, bool is_active_object); #endif }; BLI_STATIC_ASSERT_ALIGN(ObjectInfos, 16) -#define OrcoTexCoFactors (drw_infos[resource_id].orco_mul_bias) -#define ObjectInfo (drw_infos[resource_id].infos) -#define ObjectColor (drw_infos[resource_id].color) - struct ObjectBounds { /** * Uploaded as vertex (0, 4, 3, 1) of the bbox in local space, matching XYZ axis order. @@ -157,7 +153,7 @@ struct ObjectBounds { float4 bounding_sphere; #if !defined(GPU_SHADER) && defined(__cplusplus) - void init(const Object &object); + void sync(Object &ob); #endif }; BLI_STATIC_ASSERT_ALIGN(ObjectBounds, 16) diff --git a/source/blender/draw/intern/draw_view.cc b/source/blender/draw/intern/draw_view.cc index 43147202e71..61e70df23e9 100644 --- a/source/blender/draw/intern/draw_view.cc +++ b/source/blender/draw/intern/draw_view.cc @@ -5,8 +5,300 @@ * \ingroup draw */ +#include "BLI_math_geom.h" +#include "GPU_compute.h" + +#include "draw_shader.h" #include "draw_view.hh" namespace blender::draw { +void View::bind() +{ + update_viewport_size(); + + if (dirty_) { + dirty_ = false; + data_.push_update(); + } + + GPU_uniformbuf_bind(data_, DRW_VIEW_UBO_SLOT); +} + +void View::sync(const float4x4 &view_mat, const float4x4 &win_mat) +{ + data_.viewmat = view_mat; + data_.viewinv = view_mat.inverted(); + data_.winmat = win_mat; + data_.wininv = win_mat.inverted(); + data_.persmat = data_.winmat * data_.viewmat; + data_.persinv = data_.persmat.inverted(); + /* Should not be used anymore. */ + data_.viewcamtexcofac = float4(1.0f, 1.0f, 0.0f, 0.0f); + + update_view_vectors(); + + BoundBox &bound_box = *reinterpret_cast<BoundBox *>(&data_.frustum_corners); + BoundSphere &bound_sphere = *reinterpret_cast<BoundSphere *>(&data_.frustum_bound_sphere); + frustum_boundbox_calc(bound_box); + frustum_culling_planes_calc(); + frustum_culling_sphere_calc(bound_box, bound_sphere); + + dirty_ = true; +} + +void View::frustum_boundbox_calc(BoundBox &bbox) +{ + /* Extract the 8 corners from a Projection Matrix. */ +#if 0 /* Equivalent to this but it has accuracy problems. */ + BKE_boundbox_init_from_minmax(&bbox, float3(-1.0f),float3(1.0f)); + for (int i = 0; i < 8; i++) { + mul_project_m4_v3(data_.wininv.ptr(), bbox.vec[i]); + } +#endif + + float left, right, bottom, top, near, far; + bool is_persp = data_.winmat[3][3] == 0.0f; + + projmat_dimensions(data_.winmat.ptr(), &left, &right, &bottom, &top, &near, &far); + + bbox.vec[0][2] = bbox.vec[3][2] = bbox.vec[7][2] = bbox.vec[4][2] = -near; + bbox.vec[0][0] = bbox.vec[3][0] = left; + bbox.vec[4][0] = bbox.vec[7][0] = right; + bbox.vec[0][1] = bbox.vec[4][1] = bottom; + bbox.vec[7][1] = bbox.vec[3][1] = top; + + /* Get the coordinates of the far plane. */ + if (is_persp) { + float sca_far = far / near; + left *= sca_far; + right *= sca_far; + bottom *= sca_far; + top *= sca_far; + } + + bbox.vec[1][2] = bbox.vec[2][2] = bbox.vec[6][2] = bbox.vec[5][2] = -far; + bbox.vec[1][0] = bbox.vec[2][0] = left; + bbox.vec[6][0] = bbox.vec[5][0] = right; + bbox.vec[1][1] = bbox.vec[5][1] = bottom; + bbox.vec[2][1] = bbox.vec[6][1] = top; + + /* Transform into world space. */ + for (int i = 0; i < 8; i++) { + mul_m4_v3(data_.viewinv.ptr(), bbox.vec[i]); + } +} + +void View::frustum_culling_planes_calc() +{ + planes_from_projmat(data_.persmat.ptr(), + data_.frustum_planes[0], + data_.frustum_planes[5], + data_.frustum_planes[1], + data_.frustum_planes[3], + data_.frustum_planes[4], + data_.frustum_planes[2]); + + /* Normalize. */ + for (int p = 0; p < 6; p++) { + data_.frustum_planes[p].w /= normalize_v3(data_.frustum_planes[p]); + } +} + +void View::frustum_culling_sphere_calc(const BoundBox &bbox, BoundSphere &bsphere) +{ + /* Extract Bounding Sphere */ + if (data_.winmat[3][3] != 0.0f) { + /* Orthographic */ + /* The most extreme points on the near and far plane. (normalized device coords). */ + const float *nearpoint = bbox.vec[0]; + const float *farpoint = bbox.vec[6]; + + /* just use median point */ + mid_v3_v3v3(bsphere.center, farpoint, nearpoint); + bsphere.radius = len_v3v3(bsphere.center, farpoint); + } + else if (data_.winmat[2][0] == 0.0f && data_.winmat[2][1] == 0.0f) { + /* Perspective with symmetrical frustum. */ + + /* We obtain the center and radius of the circumscribed circle of the + * isosceles trapezoid composed by the diagonals of the near and far clipping plane */ + + /* center of each clipping plane */ + float mid_min[3], mid_max[3]; + mid_v3_v3v3(mid_min, bbox.vec[3], bbox.vec[4]); + mid_v3_v3v3(mid_max, bbox.vec[2], bbox.vec[5]); + + /* square length of the diagonals of each clipping plane */ + float a_sq = len_squared_v3v3(bbox.vec[3], bbox.vec[4]); + float b_sq = len_squared_v3v3(bbox.vec[2], bbox.vec[5]); + + /* distance squared between clipping planes */ + float h_sq = len_squared_v3v3(mid_min, mid_max); + + float fac = (4 * h_sq + b_sq - a_sq) / (8 * h_sq); + + /* The goal is to get the smallest sphere, + * not the sphere that passes through each corner */ + CLAMP(fac, 0.0f, 1.0f); + + interp_v3_v3v3(bsphere.center, mid_min, mid_max, fac); + + /* distance from the center to one of the points of the far plane (1, 2, 5, 6) */ + bsphere.radius = len_v3v3(bsphere.center, bbox.vec[1]); + } + else { + /* Perspective with asymmetrical frustum. */ + + /* We put the sphere center on the line that goes from origin + * to the center of the far clipping plane. */ + + /* Detect which of the corner of the far clipping plane is the farthest to the origin */ + float nfar[4]; /* most extreme far point in NDC space */ + float farxy[2]; /* far-point projection onto the near plane */ + float farpoint[3] = {0.0f}; /* most extreme far point in camera coordinate */ + float nearpoint[3]; /* most extreme near point in camera coordinate */ + float farcenter[3] = {0.0f}; /* center of far clipping plane in camera coordinate */ + float F = -1.0f, N; /* square distance of far and near point to origin */ + float f, n; /* distance of far and near point to z axis. f is always > 0 but n can be < 0 */ + float e, s; /* far and near clipping distance (<0) */ + float c; /* slope of center line = distance of far clipping center + * to z axis / far clipping distance. */ + float z; /* projection of sphere center on z axis (<0) */ + + /* Find farthest corner and center of far clip plane. */ + float corner[3] = {1.0f, 1.0f, 1.0f}; /* in clip space */ + for (int i = 0; i < 4; i++) { + float point[3]; + mul_v3_project_m4_v3(point, data_.wininv.ptr(), corner); + float len = len_squared_v3(point); + if (len > F) { + copy_v3_v3(nfar, corner); + copy_v3_v3(farpoint, point); + F = len; + } + add_v3_v3(farcenter, point); + /* rotate by 90 degree to walk through the 4 points of the far clip plane */ + float tmp = corner[0]; + corner[0] = -corner[1]; + corner[1] = tmp; + } + + /* the far center is the average of the far clipping points */ + mul_v3_fl(farcenter, 0.25f); + /* the extreme near point is the opposite point on the near clipping plane */ + copy_v3_fl3(nfar, -nfar[0], -nfar[1], -1.0f); + mul_v3_project_m4_v3(nearpoint, data_.wininv.ptr(), nfar); + /* this is a frustum projection */ + N = len_squared_v3(nearpoint); + e = farpoint[2]; + s = nearpoint[2]; + /* distance to view Z axis */ + f = len_v2(farpoint); + /* get corresponding point on the near plane */ + mul_v2_v2fl(farxy, farpoint, s / e); + /* this formula preserve the sign of n */ + sub_v2_v2(nearpoint, farxy); + n = f * s / e - len_v2(nearpoint); + c = len_v2(farcenter) / e; + /* the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case */ + z = (F - N) / (2.0f * (e - s + c * (f - n))); + + bsphere.center[0] = farcenter[0] * z / e; + bsphere.center[1] = farcenter[1] * z / e; + bsphere.center[2] = z; + + /* For XR, the view matrix may contain a scale factor. Then, transforming only the center + * into world space after calculating the radius will result in incorrect behavior. */ + mul_m4_v3(data_.viewinv.ptr(), bsphere.center); /* Transform to world space. */ + mul_m4_v3(data_.viewinv.ptr(), farpoint); + bsphere.radius = len_v3v3(bsphere.center, farpoint); + } +} + +void View::set_clip_planes(Span<float4> planes) +{ + BLI_assert(planes.size() <= ARRAY_SIZE(data_.clip_planes)); + int i = 0; + for (const auto &plane : planes) { + data_.clip_planes[i++] = plane; + } +} + +void View::update_viewport_size() +{ + float4 viewport; + GPU_viewport_size_get_f(viewport); + float2 viewport_size = float2(viewport.z, viewport.w); + if (assign_if_different(data_.viewport_size, viewport_size)) { + dirty_ = true; + } +} + +void View::update_view_vectors() +{ + bool is_persp = data_.winmat[3][3] == 0.0f; + + /* Near clip distance. */ + data_.viewvecs[0][3] = (is_persp) ? -data_.winmat[3][2] / (data_.winmat[2][2] - 1.0f) : + -(data_.winmat[3][2] + 1.0f) / data_.winmat[2][2]; + + /* Far clip distance. */ + data_.viewvecs[1][3] = (is_persp) ? -data_.winmat[3][2] / (data_.winmat[2][2] + 1.0f) : + -(data_.winmat[3][2] - 1.0f) / data_.winmat[2][2]; + + /* View vectors for the corners of the view frustum. + * Can be used to recreate the world space position easily */ + float3 view_vecs[4] = { + {-1.0f, -1.0f, -1.0f}, + {1.0f, -1.0f, -1.0f}, + {-1.0f, 1.0f, -1.0f}, + {-1.0f, -1.0f, 1.0f}, + }; + + /* Convert the view vectors to view space */ + for (int i = 0; i < 4; i++) { + mul_project_m4_v3(data_.wininv.ptr(), view_vecs[i]); + /* Normalized trick see: + * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ + if (is_persp) { + view_vecs[i].x /= view_vecs[i].z; + view_vecs[i].y /= view_vecs[i].z; + } + } + + /** + * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and + * view_vecs[1] is the vector going from the near-bottom-left corner to + * the far-top-right corner. + * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner + * when Z = 1, and top-left corner if Z = 1. + * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed) + * distance from the near plane to the far clip plane. + */ + copy_v3_v3(data_.viewvecs[0], view_vecs[0]); + + /* we need to store the differences */ + data_.viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0]; + data_.viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1]; + data_.viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2]; +} + +void View::compute_visibility(ObjectBoundsBuf &bounds, uint resource_len) +{ + object_visibility_buf.resize(divide_ceil_u(resource_len, sizeof(uint4))); + + if (do_visibility_ == false) { + uint32_t data = 0xFFFFFFFFu; + GPU_storagebuf_clear(object_visibility_buf, GPU_R32UI, GPU_DATA_UINT, &data); + return; + } + + GPUShader *shader = DRW_shader_draw_visibility_compute_get(); + GPU_shader_bind(shader); + GPU_shader_uniform_1i(shader, "resource_len", resource_len); + GPU_storagebuf_bind(bounds, 0); + GPU_compute_dispatch(shader, divide_ceil_u(resource_len, DRW_VISIBILITY_GROUP_SIZE), 1, 1); +} + } // namespace blender::draw diff --git a/source/blender/draw/intern/draw_view.hh b/source/blender/draw/intern/draw_view.hh index 678c81afdb7..e5d7a834dbf 100644 --- a/source/blender/draw/intern/draw_view.hh +++ b/source/blender/draw/intern/draw_view.hh @@ -13,40 +13,42 @@ namespace blender::draw { +class Manager; + +/* TODO deduplicate. */ +using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>; + class View { - public: - UniformBuffer<ViewInfos> data_; + friend Manager; private: + UniformBuffer<ViewInfos> data_; /** Result of the visibility computation. 1 bit per resource ID. */ StorageArrayBuffer<uint4, 1, true> object_visibility_buf; const char *debug_name; - View *parent = nullptr; - bool do_visibility = true; + bool do_visibility_ = true; + bool dirty_ = true; public: - View(const char *name) : debug_name(name), object_visibility_buf(name){}; + View(const char *name) : object_visibility_buf(name), debug_name(name){}; + + void set_clip_planes(Span<float4> planes); - void sync(); + void sync(const float4x4 &view_mat, const float4x4 &win_mat); private: - void compute_visibility(Manager::ObjectBoundsBuf &bounds, uint resource_len) - { - object_visibility_buf.resize(divide_ceil_u(resource_len, 128)); - - if (do_visibility == false) { - object_visibility_buf.clear(0xFFFFFFFFu); - return; - } - - uint thread_groups = divide_ceil_u(resource_len, DRW_VISIBILITY_GROUP_SIZE); - GPUShader *shader = draw_shader_visibility_get(); - GPU_shader_bind(shader); - GPU_storagebuf_bind(bounds, 0); - GPU_compute_dispatch(shader, thread_groups, 1, 1); - } + /** Called from draw manager. */ + void bind(); + void compute_visibility(ObjectBoundsBuf &bounds, uint resource_len); + + void update_view_vectors(); + void update_viewport_size(); + + void frustum_boundbox_calc(BoundBox &bbox); + void frustum_culling_planes_calc(); + void frustum_culling_sphere_calc(const BoundBox &bbox, BoundSphere &bsphere); }; } // namespace blender::draw diff --git a/source/blender/draw/intern/shaders/common_intersect_lib.glsl b/source/blender/draw/intern/shaders/common_intersect_lib.glsl index 708d361029a..8fa3bd3d373 100644 --- a/source/blender/draw/intern/shaders/common_intersect_lib.glsl +++ b/source/blender/draw/intern/shaders/common_intersect_lib.glsl @@ -70,6 +70,30 @@ IsectBox isect_data_setup(Box shape) return data; } +/* Construct box from 1 corner point + 3 side vectors. */ +IsectBox isect_data_setup(vec3 origin, vec3 side_x, vec3 side_y, vec3 side_z) +{ + IsectBox data; + data.corners[0] = origin; + data.corners[1] = origin + side_x; + data.corners[2] = origin + side_y + side_x; + data.corners[3] = origin + side_y; + data.corners[4] = data.corners[0] + side_z; + data.corners[5] = data.corners[1] + side_z; + data.corners[6] = data.corners[2] + side_z; + data.corners[7] = data.corners[3] + side_z; + + data.planes[0] = isect_plane_setup(data.corners[0], side_y, side_z); + data.planes[1] = isect_plane_setup(data.corners[0], side_x, side_y); + data.planes[2] = isect_plane_setup(data.corners[0], side_z, side_x); + /* Assumes that the box is actually a box! */ + data.planes[3] = vec4(-data.planes[0].xyz, -dot(-data.planes[0].xyz, data.corners[6])); + data.planes[4] = vec4(-data.planes[1].xyz, -dot(-data.planes[1].xyz, data.corners[6])); + data.planes[5] = vec4(-data.planes[2].xyz, -dot(-data.planes[2].xyz, data.corners[6])); + + return data; +} + struct IsectFrustum { vec3 corners[8]; vec4 planes[6]; @@ -194,6 +218,50 @@ bool intersect_view(Box box) return intersects; } +bool intersect_view(IsectBox i_box) +{ + bool intersects = true; + + /* Do Box vertices vs Frustum planes. */ + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(drw_view.frustum_planes[p], vec4(i_box.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_box.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + return intersects; +} + bool intersect_view(Sphere sphere) { bool intersects = true; diff --git a/source/blender/draw/intern/shaders/draw_object_infos_info.hh b/source/blender/draw/intern/shaders/draw_object_infos_info.hh index 8fd55ea351f..de96e86b67a 100644 --- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh +++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh @@ -1,10 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "draw_defines.h" #include "gpu_shader_create_info.hh" GPU_SHADER_CREATE_INFO(draw_object_infos) .typedef_source("draw_shader_shared.h") .define("OBINFO_LIB") + .define("OrcoTexCoFactors", "(drw_infos[resource_id].orco_mul_bias)") + .define("ObjectInfo", "(drw_infos[resource_id].infos)") + .define("ObjectColor", "(drw_infos[resource_id].color)") .uniform_buf(1, "ObjectInfos", "drw_infos[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH); GPU_SHADER_CREATE_INFO(draw_volume_infos) @@ -14,3 +18,10 @@ GPU_SHADER_CREATE_INFO(draw_volume_infos) GPU_SHADER_CREATE_INFO(draw_curves_infos) .typedef_source("draw_shader_shared.h") .uniform_buf(2, "CurvesInfos", "drw_curves", Frequency::BATCH); + +GPU_SHADER_CREATE_INFO(draw_object_infos_new) + .typedef_source("draw_shader_shared.h") + .storage_buf(DRW_OBJ_INFOS_SLOT, Qualifier::READ, "ObjectMatrices", "drw_matrix_buf[]") + .define("drw_ModelMatrixInverse", "drw_matrix_buf[drw_ResourceIndex].model") + .define("drw_ModelMatrix", "drw_matrix_buf[drw_ResourceIndex].model_inverse") + .additional_info("draw_resource_id_new");
\ No newline at end of file diff --git a/source/blender/draw/intern/shaders/draw_view_info.hh b/source/blender/draw/intern/shaders/draw_view_info.hh index 72154b1a1ae..6149812a644 100644 --- a/source/blender/draw/intern/shaders/draw_view_info.hh +++ b/source/blender/draw/intern/shaders/draw_view_info.hh @@ -45,7 +45,7 @@ GPU_SHADER_CREATE_INFO(draw_resource_handle) * \{ */ GPU_SHADER_CREATE_INFO(draw_view) - .uniform_buf(0, "ViewInfos", "drw_view", Frequency::PASS) + .uniform_buf(DRW_VIEW_UBO_SLOT, "ViewInfos", "drw_view", Frequency::PASS) .typedef_source("draw_shader_shared.h"); GPU_SHADER_CREATE_INFO(draw_modelmat) @@ -154,10 +154,49 @@ GPU_SHADER_CREATE_INFO(draw_resource_finalize) .compute_source("draw_resource_finalize_comp.glsl"); GPU_SHADER_CREATE_INFO(draw_visibility_compute) - .local_group_size(128) /* Number of bits in a uvec4. */ + .do_static_compilation(true) + .local_group_size(DRW_VISIBILITY_GROUP_SIZE) .storage_buf(0, Qualifier::READ, "ObjectBounds", "bounds_buf[]") - .storage_buf(1, Qualifier::WRITE, "uint4", "visibility_buf[]") + .storage_buf(1, Qualifier::WRITE, "uint", "visibility_buf[]") .push_constant(Type::UINT, "resource_len") - .compute_source("draw_visibility_comp.glsl"); + .compute_source("draw_visibility_comp.glsl") + .additional_info("draw_view"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Resource ID + * New implementation using gl_DrawID and storage buffers. + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_resource_id_draw_id).define("drw_DrawIndex", "gl_DrawID"); + +/** + * Workaround the lack of gl_DrawID using our own emulation. + */ +GPU_SHADER_CREATE_INFO(draw_resource_id_fallback).vertex_in(15, Type::UINT, "drw_DrawIndex"); + +GPU_SHADER_CREATE_INFO(draw_resource_id_new) + .typedef_source("draw_shader_shared.h") + .storage_buf(DRW_COMMAND_SLOT, Qualifier::READ, "DrawCommand", "drw_command_buf[]") + .define("drw_EngineInstanceCount", "drw_command_buf[drw_DrawIndex].engine_instance_count") + .define("drw_EngineInstanceIndex", "(gl_InstanceID % drw_EngineInstanceCount)") + .define("drw_ObjectInstanceIndex", "(gl_InstanceID / drw_EngineInstanceCount)") + .define("drw_ResourceIndexStart", "drw_command_buf[drw_DrawIndex].resource_id") + .define("drw_ResourceIndex", "(drw_ResourceIndexStart + drw_ObjectInstanceIndex)") + .additional_info("draw_resource_id_draw_id"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Object Resources + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_modelmat_new) + .typedef_source("draw_shader_shared.h") + .storage_buf(DRW_OBJ_MAT_SLOT, Qualifier::READ, "ObjectMatrices", "drw_matrix_buf[]") + .define("drw_ModelMatrixInverse", "drw_matrix_buf[drw_ResourceIndex].drw_modelMatrix") + .define("drw_ModelMatrix", "drw_matrix_buf[drw_ResourceIndex].drw_modelMatrixInverse") + .additional_info("draw_resource_id_new"); /** \} */ diff --git a/source/blender/draw/intern/shaders/draw_visibility_comp.glsl b/source/blender/draw/intern/shaders/draw_visibility_comp.glsl index 885e77af7ed..7d38338fc6e 100644 --- a/source/blender/draw/intern/shaders/draw_visibility_comp.glsl +++ b/source/blender/draw/intern/shaders/draw_visibility_comp.glsl @@ -5,26 +5,40 @@ /* TODO(fclem): This could be augmented by a 2 pass occlusion culling system. */ #pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_intersect_lib.glsl) -shared uint shared_result[4]; +const uint uint_len_per_group = gl_WorkGroupSize.x / 32u; +shared uint shared_result[uint_len_per_group]; void main() { if (gl_LocalInvocationID.x == 0) { - shared_result[0] = 0u; - shared_result[1] = 0u; - shared_result[2] = 0u; - shared_result[3] = 0u; + for (uint i = 0; i < uint_len_per_group; i++) { + shared_result[i] = 0u; + } } barrier(); - uint resource_id = gl_GlobalInvocationID.x; + uint resource_id = min(gl_GlobalInvocationID.x, resource_len - 1u); + + ObjectBounds bounds = bounds_buf[resource_id]; + if (bounds.bounding_sphere.w != -1.0) { + IsectBox box = isect_data_setup(bounds.bounding_corners[0].xyz, + bounds.bounding_corners[1].xyz, + bounds.bounding_corners[2].xyz, + bounds.bounding_corners[3].xyz); + if (intersect_view(box)) { + uint result = 1u << (gl_LocalInvocationID.x % 32u); + atomicOr(shared_result[gl_LocalInvocationID.x / 32u], result); + } + } barrier(); if (gl_LocalInvocationID.x == 0) { - visibility_buf[gl_WorkGroupID.x] = uvec4( - shared_result[0], shared_result[1], shared_result[2], shared_result[3]); + for (uint i = 0; i < uint_len_per_group; i++) { + visibility_buf[gl_WorkGroupID.x * uint_len_per_group + i] = shared_result[i]; + } } }
\ No newline at end of file |