diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2022-08-08 20:01:38 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2022-08-09 16:45:46 +0300 |
commit | f6639cc4fc1fd9fbbe1454ce216d6d0c8fe1ebdf (patch) | |
tree | dc4b1924fc1d4fbd085351cc26dab32c3235b448 /source/blender/draw | |
parent | 2e4727e123c1cc652212875074f19f7b4bf46563 (diff) |
DRW: DebugDraw: Port module to C++ and add GPU capabilities
This is a complete rewrite of the draw debug drawing module in C++.
It uses `GPUStorageBuf` to store the data to be drawn and use indirect
drawing. This makes it easier to do a mirror API for GPU shaders.
The C++ API class is exposed through `draw_debug.hh` and should be used
when possible in new code.
However, the debug drawing will not work for platform not yet supporting
`GPUStorageBuf`. Also keep in mind that this module must only be used
in debug build for performance and compatibility reasons.
Diffstat (limited to 'source/blender/draw')
20 files changed, 1935 insertions, 227 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index c34a6daa126..9ecd157bd02 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -83,7 +83,7 @@ set(SRC intern/draw_color_management.cc intern/draw_common.c intern/draw_curves.cc - intern/draw_debug.c + intern/draw_debug.cc intern/draw_fluid.c intern/draw_hair.cc intern/draw_instance_data.c @@ -214,6 +214,7 @@ set(SRC intern/draw_common_shader_shared.h intern/draw_curves_private.h intern/draw_debug.h + intern/draw_debug.hh intern/draw_hair_private.h intern/draw_instance_data.h intern/draw_manager.h @@ -434,20 +435,19 @@ set(GLSL_SRC intern/shaders/common_attribute_lib.glsl intern/shaders/common_colormanagement_lib.glsl + intern/shaders/common_debug_draw_lib.glsl + intern/shaders/common_debug_print_lib.glsl + intern/shaders/common_fullscreen_vert.glsl + intern/shaders/common_fxaa_lib.glsl intern/shaders/common_globals_lib.glsl intern/shaders/common_gpencil_lib.glsl - intern/shaders/common_pointcloud_lib.glsl intern/shaders/common_hair_lib.glsl - intern/shaders/common_hair_refine_vert.glsl intern/shaders/common_hair_refine_comp.glsl - intern/shaders/common_math_lib.glsl + intern/shaders/common_hair_refine_vert.glsl intern/shaders/common_math_geom_lib.glsl - intern/shaders/common_view_clipping_lib.glsl - intern/shaders/common_view_lib.glsl - intern/shaders/common_fxaa_lib.glsl + intern/shaders/common_math_lib.glsl + intern/shaders/common_pointcloud_lib.glsl intern/shaders/common_smaa_lib.glsl - intern/shaders/common_fullscreen_vert.glsl - intern/shaders/common_subdiv_custom_data_interp_comp.glsl intern/shaders/common_subdiv_ibo_lines_comp.glsl intern/shaders/common_subdiv_ibo_tris_comp.glsl @@ -460,6 +460,13 @@ set(GLSL_SRC intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl intern/shaders/common_subdiv_vbo_lnor_comp.glsl intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl + intern/shaders/common_view_clipping_lib.glsl + intern/shaders/common_view_lib.glsl + intern/shaders/draw_debug_draw_display_frag.glsl + intern/shaders/draw_debug_draw_display_vert.glsl + intern/shaders/draw_debug_info.hh + intern/shaders/draw_debug_print_display_frag.glsl + intern/shaders/draw_debug_print_display_vert.glsl intern/draw_common_shader_shared.h intern/draw_shader_shared.h diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c index 536242f67d8..a3ab4cdb830 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c +++ b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c @@ -357,7 +357,7 @@ static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo, mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat); #ifdef DEBUG_CSM - DRW_debug_m4_as_bbox(viewprojmat, dbg_col, true); + DRW_debug_m4_as_bbox(viewprojmat, true, dbg_col); #endif } diff --git a/source/blender/draw/intern/draw_debug.c b/source/blender/draw/intern/draw_debug.c deleted file mode 100644 index b568119627e..00000000000 --- a/source/blender/draw/intern/draw_debug.c +++ /dev/null @@ -1,196 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2018 Blender Foundation. */ - -/** \file - * \ingroup draw - * - * \brief Simple API to draw debug shapes in the viewport. - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_object_types.h" - -#include "BKE_object.h" - -#include "BLI_link_utils.h" - -#include "GPU_immediate.h" -#include "GPU_matrix.h" - -#include "draw_debug.h" -#include "draw_manager.h" - -/* --------- Register --------- */ - -/* Matrix applied to all points before drawing. Could be a stack if needed. */ -static float g_modelmat[4][4]; - -void DRW_debug_modelmat_reset(void) -{ - unit_m4(g_modelmat); -} - -void DRW_debug_modelmat(const float modelmat[4][4]) -{ - copy_m4_m4(g_modelmat, modelmat); -} - -void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]) -{ - DRWDebugLine *line = MEM_mallocN(sizeof(DRWDebugLine), "DRWDebugLine"); - mul_v3_m4v3(line->pos[0], g_modelmat, v1); - mul_v3_m4v3(line->pos[1], g_modelmat, v2); - copy_v4_v4(line->color, color); - BLI_LINKS_PREPEND(DST.debug.lines, line); -} - -void DRW_debug_polygon_v3(const float (*v)[3], const int vert_len, const float color[4]) -{ - BLI_assert(vert_len > 1); - - for (int i = 0; i < vert_len; i++) { - DRW_debug_line_v3v3(v[i], v[(i + 1) % vert_len], color); - } -} - -void DRW_debug_m4(const float m[4][4]) -{ - float v0[3] = {0.0f, 0.0f, 0.0f}; - float v1[3] = {1.0f, 0.0f, 0.0f}; - float v2[3] = {0.0f, 1.0f, 0.0f}; - float v3[3] = {0.0f, 0.0f, 1.0f}; - - mul_m4_v3(m, v0); - mul_m4_v3(m, v1); - mul_m4_v3(m, v2); - mul_m4_v3(m, v3); - - DRW_debug_line_v3v3(v0, v1, (float[4]){1.0f, 0.0f, 0.0f, 1.0f}); - DRW_debug_line_v3v3(v0, v2, (float[4]){0.0f, 1.0f, 0.0f, 1.0f}); - DRW_debug_line_v3v3(v0, v3, (float[4]){0.0f, 0.0f, 1.0f, 1.0f}); -} - -void DRW_debug_bbox(const BoundBox *bbox, const float color[4]) -{ - DRW_debug_line_v3v3(bbox->vec[0], bbox->vec[1], color); - DRW_debug_line_v3v3(bbox->vec[1], bbox->vec[2], color); - DRW_debug_line_v3v3(bbox->vec[2], bbox->vec[3], color); - DRW_debug_line_v3v3(bbox->vec[3], bbox->vec[0], color); - - DRW_debug_line_v3v3(bbox->vec[4], bbox->vec[5], color); - DRW_debug_line_v3v3(bbox->vec[5], bbox->vec[6], color); - DRW_debug_line_v3v3(bbox->vec[6], bbox->vec[7], color); - DRW_debug_line_v3v3(bbox->vec[7], bbox->vec[4], color); - - DRW_debug_line_v3v3(bbox->vec[0], bbox->vec[4], color); - DRW_debug_line_v3v3(bbox->vec[1], bbox->vec[5], color); - DRW_debug_line_v3v3(bbox->vec[2], bbox->vec[6], color); - DRW_debug_line_v3v3(bbox->vec[3], bbox->vec[7], color); -} - -void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], const bool invert) -{ - BoundBox bb; - const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; - float project_matrix[4][4]; - if (invert) { - invert_m4_m4(project_matrix, m); - } - else { - copy_m4_m4(project_matrix, m); - } - - BKE_boundbox_init_from_minmax(&bb, min, max); - for (int i = 0; i < 8; i++) { - mul_project_m4_v3(project_matrix, bb.vec[i]); - } - DRW_debug_bbox(&bb, color); -} - -void DRW_debug_sphere(const float center[3], const float radius, const float color[4]) -{ - float size_mat[4][4]; - DRWDebugSphere *sphere = MEM_mallocN(sizeof(DRWDebugSphere), "DRWDebugSphere"); - /* Bake all transform into a Matrix4 */ - scale_m4_fl(size_mat, radius); - copy_m4_m4(sphere->mat, g_modelmat); - translate_m4(sphere->mat, center[0], center[1], center[2]); - mul_m4_m4m4(sphere->mat, sphere->mat, size_mat); - - copy_v4_v4(sphere->color, color); - BLI_LINKS_PREPEND(DST.debug.spheres, sphere); -} - -/* --------- Render --------- */ - -static void drw_debug_draw_lines(void) -{ - int count = BLI_linklist_count((LinkNode *)DST.debug.lines); - - if (count == 0) { - return; - } - - GPUVertFormat *vert_format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(vert_format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR); - - immBegin(GPU_PRIM_LINES, count * 2); - - while (DST.debug.lines) { - void *next = DST.debug.lines->next; - - immAttr4fv(col, DST.debug.lines->color); - immVertex3fv(pos, DST.debug.lines->pos[0]); - - immAttr4fv(col, DST.debug.lines->color); - immVertex3fv(pos, DST.debug.lines->pos[1]); - - MEM_freeN(DST.debug.lines); - DST.debug.lines = next; - } - immEnd(); - - immUnbindProgram(); -} - -static void drw_debug_draw_spheres(void) -{ - int count = BLI_linklist_count((LinkNode *)DST.debug.spheres); - - if (count == 0) { - return; - } - - float persmat[4][4]; - DRW_view_persmat_get(NULL, persmat, false); - - GPUBatch *empty_sphere = DRW_cache_empty_sphere_get(); - GPU_batch_program_set_builtin(empty_sphere, GPU_SHADER_3D_UNIFORM_COLOR); - while (DST.debug.spheres) { - void *next = DST.debug.spheres->next; - float MVP[4][4]; - - mul_m4_m4m4(MVP, persmat, DST.debug.spheres->mat); - GPU_batch_uniform_mat4(empty_sphere, "ModelViewProjectionMatrix", MVP); - GPU_batch_uniform_4fv(empty_sphere, "color", DST.debug.spheres->color); - GPU_batch_draw(empty_sphere); - - MEM_freeN(DST.debug.spheres); - DST.debug.spheres = next; - } -} - -void drw_debug_draw(void) -{ - drw_debug_draw_lines(); - drw_debug_draw_spheres(); -} - -void drw_debug_init(void) -{ - DRW_debug_modelmat_reset(); -} diff --git a/source/blender/draw/intern/draw_debug.cc b/source/blender/draw/intern/draw_debug.cc new file mode 100644 index 00000000000..4bfdda06609 --- /dev/null +++ b/source/blender/draw/intern/draw_debug.cc @@ -0,0 +1,719 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2018 Blender Foundation. */ + +/** \file + * \ingroup draw + * + * \brief Simple API to draw debug shapes in the viewport. + */ + +#include "BKE_object.h" +#include "BLI_link_utils.h" +#include "GPU_batch.h" +#include "GPU_capabilities.h" +#include "GPU_debug.h" + +#include "draw_debug.h" +#include "draw_debug.hh" +#include "draw_manager.h" +#include "draw_shader.h" +#include "draw_shader_shared.h" + +#include <iomanip> + +namespace blender::draw { + +/* -------------------------------------------------------------------- */ +/** \name Init and state + * \{ */ + +DebugDraw::DebugDraw() +{ + constexpr int circle_resolution = 16; + for (auto axis : IndexRange(3)) { + for (auto edge : IndexRange(circle_resolution)) { + for (auto vert : IndexRange(2)) { + const float angle = (2 * M_PI) * (edge + vert) / float(circle_resolution); + float point[3] = {cosf(angle), sinf(angle), 0.0f}; + sphere_verts_.append( + float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3])); + } + } + } + + constexpr int point_resolution = 4; + for (auto axis : IndexRange(3)) { + for (auto edge : IndexRange(point_resolution)) { + for (auto vert : IndexRange(2)) { + const float angle = (2 * M_PI) * (edge + vert) / float(point_resolution); + float point[3] = {cosf(angle), sinf(angle), 0.0f}; + point_verts_.append( + float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3])); + } + } + } +}; + +void DebugDraw::init() +{ + cpu_print_buf_.command.v_count = 0; + cpu_print_buf_.command.v_first = 0; + cpu_print_buf_.command.i_count = 1; + cpu_print_buf_.command.i_first = 0; + + cpu_draw_buf_.command.v_count = 0; + cpu_draw_buf_.command.v_first = 0; + cpu_draw_buf_.command.i_count = 1; + cpu_draw_buf_.command.i_first = 0; + + gpu_print_buf_.command.v_count = 0; + gpu_print_buf_.command.v_first = 0; + gpu_print_buf_.command.i_count = 1; + gpu_print_buf_.command.i_first = 0; + gpu_print_buf_used = false; + + gpu_draw_buf_.command.v_count = 0; + gpu_draw_buf_.command.v_first = 0; + gpu_draw_buf_.command.i_count = 1; + gpu_draw_buf_.command.i_first = 0; + gpu_draw_buf_used = false; + + modelmat_reset(); +} + +void DebugDraw::modelmat_reset() +{ + model_mat_ = float4x4::identity(); +} + +void DebugDraw::modelmat_set(const float modelmat[4][4]) +{ + model_mat_ = modelmat; +} + +GPUStorageBuf *DebugDraw::gpu_draw_buf_get() +{ + BLI_assert(GPU_shader_storage_buffer_objects_support()); + if (!gpu_draw_buf_used) { + gpu_draw_buf_used = true; + gpu_draw_buf_.push_update(); + } + return gpu_draw_buf_; +} + +GPUStorageBuf *DebugDraw::gpu_print_buf_get() +{ + BLI_assert(GPU_shader_storage_buffer_objects_support()); + if (!gpu_print_buf_used) { + gpu_print_buf_used = true; + gpu_print_buf_.push_update(); + } + return gpu_print_buf_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw functions + * \{ */ + +void DebugDraw::draw_line(float3 v1, float3 v2, float4 color) +{ + draw_line(v1, v2, color_pack(color)); +} + +void DebugDraw::draw_polygon(Span<float3> poly_verts, float4 color) +{ + BLI_assert(!poly_verts.is_empty()); + + uint col = color_pack(color); + float3 v0 = model_mat_ * poly_verts.last(); + for (auto vert : poly_verts) { + float3 v1 = model_mat_ * vert; + draw_line(v0, v1, col); + v0 = v1; + } +} + +void DebugDraw::draw_matrix(const float4x4 m4) +{ + float3 v0 = float3(0.0f, 0.0f, 0.0f); + float3 v1 = float3(1.0f, 0.0f, 0.0f); + float3 v2 = float3(0.0f, 1.0f, 0.0f); + float3 v3 = float3(0.0f, 0.0f, 1.0f); + + mul_project_m4_v3(m4.ptr(), v0); + mul_project_m4_v3(m4.ptr(), v1); + mul_project_m4_v3(m4.ptr(), v2); + mul_project_m4_v3(m4.ptr(), v3); + + draw_line(v0, v1, float4(1.0f, 0.0f, 0.0f, 1.0f)); + draw_line(v0, v2, float4(0.0f, 1.0f, 0.0f, 1.0f)); + draw_line(v0, v3, float4(0.0f, 0.0f, 1.0f, 1.0f)); +} + +void DebugDraw::draw_bbox(const BoundBox &bbox, const float4 color) +{ + uint col = color_pack(color); + draw_line(bbox.vec[0], bbox.vec[1], col); + draw_line(bbox.vec[1], bbox.vec[2], col); + draw_line(bbox.vec[2], bbox.vec[3], col); + draw_line(bbox.vec[3], bbox.vec[0], col); + + draw_line(bbox.vec[4], bbox.vec[5], col); + draw_line(bbox.vec[5], bbox.vec[6], col); + draw_line(bbox.vec[6], bbox.vec[7], col); + draw_line(bbox.vec[7], bbox.vec[4], col); + + draw_line(bbox.vec[0], bbox.vec[4], col); + draw_line(bbox.vec[1], bbox.vec[5], col); + draw_line(bbox.vec[2], bbox.vec[6], col); + draw_line(bbox.vec[3], bbox.vec[7], col); +} + +void DebugDraw::draw_matrix_as_bbox(float4x4 mat, const float4 color) +{ + BoundBox bb; + const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; + BKE_boundbox_init_from_minmax(&bb, min, max); + for (auto i : IndexRange(8)) { + mul_project_m4_v3(mat.ptr(), bb.vec[i]); + } + draw_bbox(bb, color); +} + +void DebugDraw::draw_sphere(const float3 center, float radius, const float4 color) +{ + uint col = color_pack(color); + for (auto i : IndexRange(sphere_verts_.size() / 2)) { + float3 v0 = sphere_verts_[i * 2] * radius + center; + float3 v1 = sphere_verts_[i * 2 + 1] * radius + center; + draw_line(v0, v1, col); + } +} + +void DebugDraw::draw_point(const float3 center, float radius, const float4 color) +{ + uint col = color_pack(color); + for (auto i : IndexRange(point_verts_.size() / 2)) { + float3 v0 = point_verts_[i * 2] * radius + center; + float3 v1 = point_verts_[i * 2 + 1] * radius + center; + draw_line(v0, v1, col); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Print functions + * \{ */ + +template<> void DebugDraw::print_value<uint>(const uint &value) +{ + print_value_uint(value, false, false, true); +} +template<> void DebugDraw::print_value<int>(const int &value) +{ + print_value_uint(uint(abs(value)), false, (value < 0), false); +} +template<> void DebugDraw::print_value<bool>(const bool &value) +{ + print_string(value ? "true " : "false"); +} +template<> void DebugDraw::print_value<float>(const float &val) +{ + std::stringstream ss; + ss << std::setw(12) << std::to_string(val); + print_string(ss.str()); +} +template<> void DebugDraw::print_value<double>(const double &val) +{ + print_value(float(val)); +} + +template<> void DebugDraw::print_value_hex<uint>(const uint &value) +{ + print_value_uint(value, true, false, false); +} +template<> void DebugDraw::print_value_hex<int>(const int &value) +{ + print_value_uint(uint(value), true, false, false); +} +template<> void DebugDraw::print_value_hex<float>(const float &value) +{ + print_value_uint(*reinterpret_cast<const uint *>(&value), true, false, false); +} +template<> void DebugDraw::print_value_hex<double>(const double &val) +{ + print_value_hex(float(val)); +} + +template<> void DebugDraw::print_value_binary<uint>(const uint &value) +{ + print_value_binary(value); +} +template<> void DebugDraw::print_value_binary<int>(const int &value) +{ + print_value_binary(uint(value)); +} +template<> void DebugDraw::print_value_binary<float>(const float &value) +{ + print_value_binary(*reinterpret_cast<const uint *>(&value)); +} +template<> void DebugDraw::print_value_binary<double>(const double &val) +{ + print_value_binary(float(val)); +} + +template<> void DebugDraw::print_value<float2>(const float2 &value) +{ + print_no_endl("float2(", value[0], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<float3>(const float3 &value) +{ + print_no_endl("float3(", value[0], ", ", value[1], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<float4>(const float4 &value) +{ + print_no_endl("float4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +template<> void DebugDraw::print_value<int2>(const int2 &value) +{ + print_no_endl("int2(", value[0], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<int3>(const int3 &value) +{ + print_no_endl("int3(", value[0], ", ", value[1], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<int4>(const int4 &value) +{ + print_no_endl("int4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +template<> void DebugDraw::print_value<uint2>(const uint2 &value) +{ + print_no_endl("uint2(", value[0], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<uint3>(const uint3 &value) +{ + print_no_endl("uint3(", value[0], ", ", value[1], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<uint4>(const uint4 &value) +{ + print_no_endl("uint4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internals + * + * IMPORTANT: All of these are copied from the shader libs (common_debug_draw_lib.glsl & + * common_debug_print_lib.glsl). They need to be kept in sync to write the same data. + * \{ */ + +void DebugDraw::draw_line(float3 v1, float3 v2, uint color) +{ + DebugDrawBuf &buf = cpu_draw_buf_; + uint index = buf.command.v_count; + if (index + 2 < DRW_DEBUG_DRAW_VERT_MAX) { + buf.verts[index + 0] = vert_pack(model_mat_ * v1, color); + buf.verts[index + 1] = vert_pack(model_mat_ * v2, color); + buf.command.v_count += 2; + } +} + +/* Keep in sync with drw_debug_color_pack(). */ +uint DebugDraw::color_pack(float4 color) +{ + color = math::clamp(color, 0.0f, 1.0f); + uint result = 0; + result |= uint(color.x * 255.0f) << 0u; + result |= uint(color.y * 255.0f) << 8u; + result |= uint(color.z * 255.0f) << 16u; + result |= uint(color.w * 255.0f) << 24u; + return result; +} + +DRWDebugVert DebugDraw::vert_pack(float3 pos, uint color) +{ + DRWDebugVert vert; + vert.pos0 = *reinterpret_cast<uint32_t *>(&pos.x); + vert.pos1 = *reinterpret_cast<uint32_t *>(&pos.y); + vert.pos2 = *reinterpret_cast<uint32_t *>(&pos.z); + vert.color = color; + return vert; +} + +void DebugDraw::print_newline() +{ + print_col_ = 0u; + print_row_ = ++cpu_print_buf_.command.i_first; +} + +void DebugDraw::print_string_start(uint len) +{ + /* Break before word. */ + if (print_col_ + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + print_newline(); + } +} + +/* Copied from gpu_shader_dependency. */ +void DebugDraw::print_string(std::string str) +{ + size_t len_before_pad = str.length(); + /* Pad string to uint size to avoid out of bound reads. */ + while (str.length() % 4 != 0) { + str += " "; + } + + print_string_start(len_before_pad); + for (size_t i = 0; i < len_before_pad; i += 4) { + union { + uint8_t chars[4]; + uint32_t word; + }; + + chars[0] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 0); + chars[1] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 1); + chars[2] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 2); + chars[3] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 3); + + if (i + 4 > len_before_pad) { + chars[len_before_pad - i] = '\0'; + } + print_char4(word); + } +} + +/* Keep in sync with shader. */ +void DebugDraw::print_char4(uint data) +{ + /* Convert into char stream. */ + for (; data != 0u; data >>= 8u) { + uint char1 = data & 0xFFu; + /* Check for null terminator. */ + if (char1 == 0x00) { + break; + } + /* NOTE: Do not skip the header manually like in GPU. */ + uint cursor = cpu_print_buf_.command.v_count++; + if (cursor < DRW_DEBUG_PRINT_MAX) { + /* For future usage. (i.e: Color) */ + uint flags = 0u; + uint col = print_col_++; + uint print_header = (flags << 24u) | (print_row_ << 16u) | (col << 8u); + cpu_print_buf_.char_array[cursor] = print_header | char1; + /* Break word. */ + if (print_col_ > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + print_newline(); + } + } + } +} + +void DebugDraw::print_append_char(uint char1, uint &char4) +{ + char4 = (char4 << 8u) | char1; +} + +void DebugDraw::print_append_digit(uint digit, uint &char4) +{ + const uint char_A = 0x41u; + const uint char_0 = 0x30u; + bool is_hexadecimal = digit > 9u; + char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit)); +} + +void DebugDraw::print_append_space(uint &char4) +{ + char4 = (char4 << 8u) | 0x20u; +} + +void DebugDraw::print_value_binary(uint value) +{ + print_string("0b"); + print_string_start(10u * 4u); + uint digits[10] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; + uint digit = 0u; + for (uint i = 0u; i < 32u; i++) { + print_append_digit(((value >> i) & 1u), digits[digit / 4u]); + digit++; + if ((i % 4u) == 3u) { + print_append_space(digits[digit / 4u]); + digit++; + } + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 9; j >= 0; j--) { + print_char4(digits[j]); + } +} + +void DebugDraw::print_value_uint(uint value, + const bool hex, + bool is_negative, + const bool is_unsigned) +{ + print_string_start(3u * 4u); + const uint blank_value = hex ? 0x30303030u : 0x20202020u; + const uint prefix = hex ? 0x78302020u : 0x20202020u; + uint digits[3] = {blank_value, blank_value, prefix}; + const uint base = hex ? 16u : 10u; + uint digit = 0u; + /* Add `u` suffix. */ + if (is_unsigned) { + print_append_char('u', digits[digit / 4u]); + digit++; + } + /* Number's digits. */ + for (; value != 0u || digit == uint(is_unsigned); value /= base) { + print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Add negative sign. */ + if (is_negative) { + print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + print_char4(digits[j]); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Display + * \{ */ + +void DebugDraw::display_lines() +{ + if (cpu_draw_buf_.command.v_count == 0 && gpu_draw_buf_used == false) { + return; + } + GPU_debug_group_begin("Lines"); + cpu_draw_buf_.push_update(); + + float4x4 persmat; + const DRWView *view = DRW_view_get_active(); + DRW_view_persmat_get(view, persmat.ptr(), false); + + drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); + + GPUBatch *batch = drw_cache_procedural_lines_get(); + GPUShader *shader = DRW_shader_debug_draw_display_get(); + GPU_batch_set_shader(batch, shader); + int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS); + GPU_shader_uniform_mat4(shader, "persmat", persmat.ptr()); + + if (gpu_draw_buf_used) { + GPU_debug_group_begin("GPU"); + GPU_storagebuf_bind(gpu_draw_buf_, slot); + GPU_batch_draw_indirect(batch, gpu_draw_buf_); + GPU_storagebuf_unbind(gpu_draw_buf_); + GPU_debug_group_end(); + } + + GPU_debug_group_begin("CPU"); + GPU_storagebuf_bind(cpu_draw_buf_, slot); + GPU_batch_draw_indirect(batch, cpu_draw_buf_); + GPU_storagebuf_unbind(cpu_draw_buf_); + GPU_debug_group_end(); + + GPU_debug_group_end(); +} + +void DebugDraw::display_prints() +{ + if (cpu_print_buf_.command.v_count == 0 && gpu_print_buf_used == false) { + return; + } + GPU_debug_group_begin("Prints"); + cpu_print_buf_.push_update(); + + drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_PROGRAM_POINT_SIZE); + + GPUBatch *batch = drw_cache_procedural_points_get(); + GPUShader *shader = DRW_shader_debug_print_display_get(); + GPU_batch_set_shader(batch, shader); + int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT); + + if (gpu_print_buf_used) { + GPU_debug_group_begin("GPU"); + GPU_storagebuf_bind(gpu_print_buf_, slot); + GPU_batch_draw_indirect(batch, gpu_print_buf_); + GPU_storagebuf_unbind(gpu_print_buf_); + GPU_debug_group_end(); + } + + GPU_debug_group_begin("CPU"); + GPU_storagebuf_bind(cpu_print_buf_, slot); + GPU_batch_draw_indirect(batch, cpu_print_buf_); + GPU_storagebuf_unbind(cpu_print_buf_); + GPU_debug_group_end(); + + GPU_debug_group_end(); +} + +void DebugDraw::display_to_view() +{ + GPU_debug_group_begin("DebugDraw"); + + display_lines(); + /* Print 3D shapes before text to avoid overlaps. */ + display_prints(); + /* Init again so we don't draw the same thing twice. */ + init(); + + GPU_debug_group_end(); +} + +} // namespace blender::draw + +blender::draw::DebugDraw *DRW_debug_get() +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return nullptr; + } + return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C-API private + * \{ */ + +void drw_debug_draw() +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + /* TODO(fclem): Convenience for now. Will have to move to DRWManager. */ + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->display_to_view(); +} + +/** + * NOTE: Init is once per draw manager cycle. + */ +void drw_debug_init() +{ + /* Module should not be used in release builds. */ + /* TODO(fclem): Hide the functions declarations without using ifdefs everywhere. */ +#ifdef DEBUG + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + /* TODO(fclem): Convenience for now. Will have to move to DRWManager. */ + if (DST.debug == nullptr) { + DST.debug = reinterpret_cast<DRWDebugModule *>(new blender::draw::DebugDraw()); + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->init(); +#endif +} + +void drw_debug_module_free(DRWDebugModule *module) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + if (module != nullptr) { + delete reinterpret_cast<blender::draw::DebugDraw *>(module); + } +} + +GPUStorageBuf *drw_debug_gpu_draw_buf_get() +{ + return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_draw_buf_get(); +} + +GPUStorageBuf *drw_debug_gpu_print_buf_get() +{ + return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_print_buf_get(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C-API public + * \{ */ + +void DRW_debug_modelmat_reset() +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_reset(); +} + +void DRW_debug_modelmat(const float modelmat[4][4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_set(modelmat); +} + +void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_line(v1, v2, color); +} + +void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_polygon( + blender::Span<float3>((float3 *)v, vert_len), color); +} + +void DRW_debug_m4(const float m[4][4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix(m); +} + +void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + blender::float4x4 m4 = m; + if (invert) { + m4 = m4.inverted(); + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix_as_bbox(m4, color); +} + +void DRW_debug_bbox(const BoundBox *bbox, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_bbox(*bbox, color); +} + +void DRW_debug_sphere(const float center[3], float radius, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_sphere(center, radius, color); +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_debug.h b/source/blender/draw/intern/draw_debug.h index 333d734edb9..9a56a12242e 100644 --- a/source/blender/draw/intern/draw_debug.h +++ b/source/blender/draw/intern/draw_debug.h @@ -3,21 +3,38 @@ /** \file * \ingroup draw + * + * \brief Simple API to draw debug shapes in the viewport. + * IMPORTANT: This is the legacy API for C. Use draw_debug.hh instead in new C++ code. */ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DRWDebugModule DRWDebugModule; + struct BoundBox; void DRW_debug_modelmat_reset(void); void DRW_debug_modelmat(const float modelmat[4][4]); +/** + * IMPORTANT: For now there is a limit of DRW_DEBUG_DRAW_VERT_MAX that can be drawn + * using all the draw functions. + */ void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]); void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4]); /** * \note g_modelmat is still applied on top. */ void DRW_debug_m4(const float m[4][4]); -void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], bool invert); +void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4]); void DRW_debug_bbox(const BoundBox *bbox, const float color[4]); void DRW_debug_sphere(const float center[3], float radius, const float color[4]); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_debug.hh b/source/blender/draw/intern/draw_debug.hh new file mode 100644 index 00000000000..4a2a367aef0 --- /dev/null +++ b/source/blender/draw/intern/draw_debug.hh @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup draw + * + * \brief Simple API to draw debug shapes and log in the viewport. + * + * Both CPU and GPU implementation are supported and symmetrical (meaning GPU shader can use it + * too, see common_debug_print/draw_lib.glsl). + * + * NOTE: CPU logging will overlap GPU logging on screen as it is drawn after. + */ + +#pragma once + +#include "BLI_math_vec_types.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" +#include "DNA_object_types.h" +#include "DRW_gpu_wrapper.hh" + +namespace blender::draw { + +/* Shortcuts to avoid boilerplate code and match shader API. */ +#define drw_debug_line(...) DRW_debug_get()->draw_line(__VA_ARGS__) +#define drw_debug_polygon(...) DRW_debug_get()->draw_polygon(__VA_ARGS__) +#define drw_debug_bbox(...) DRW_debug_get()->draw_bbox(__VA_ARGS__) +#define drw_debug_sphere(...) DRW_debug_get()->draw_sphere(__VA_ARGS__) +#define drw_debug_point(...) DRW_debug_get()->draw_point(__VA_ARGS__) +#define drw_debug_matrix(...) DRW_debug_get()->draw_matrix(__VA_ARGS__) +#define drw_debug_matrix_as_bbox(...) DRW_debug_get()->draw_matrix_as_bbox(__VA_ARGS__) +#define drw_print(...) DRW_debug_get()->print(__VA_ARGS__) +#define drw_print_hex(...) DRW_debug_get()->print_hex(__VA_ARGS__) +#define drw_print_binary(...) DRW_debug_get()->print_binary(__VA_ARGS__) +#define drw_print_no_endl(...) DRW_debug_get()->print_no_endl(__VA_ARGS__) + +/* Will log variable along with its name, like the shader version of print(). */ +#define drw_print_id(v_) DRW_debug_get()->print(#v_, "= ", v_) +#define drw_print_id_no_endl(v_) DRW_debug_get()->print_no_endl(#v_, "= ", v_) + +class DebugDraw { + private: + using DebugDrawBuf = StorageBuffer<DRWDebugDrawBuffer>; + using DebugPrintBuf = StorageBuffer<DRWDebugPrintBuffer>; + + /** Data buffers containing all verts or chars to draw. */ + DebugDrawBuf cpu_draw_buf_ = {"DebugDrawBuf-CPU"}; + DebugDrawBuf gpu_draw_buf_ = {"DebugDrawBuf-GPU"}; + DebugPrintBuf cpu_print_buf_ = {"DebugPrintBuf-CPU"}; + DebugPrintBuf gpu_print_buf_ = {"DebugPrintBuf-GPU"}; + /** True if the gpu buffer have been requested and may contain data to draw. */ + bool gpu_print_buf_used = false; + bool gpu_draw_buf_used = false; + /** Matrix applied to all points before drawing. Could be a stack if needed. */ + float4x4 model_mat_; + /** Precomputed shapes verts. */ + Vector<float3> sphere_verts_; + Vector<float3> point_verts_; + /** Cursor position for print functionality. */ + uint print_col_ = 0; + uint print_row_ = 0; + + public: + DebugDraw(); + ~DebugDraw(){}; + + /** + * Resets all buffers and reset model matrix state. + * Not to be called by user. + */ + void init(); + + /** + * Resets model matrix state to identity. + */ + void modelmat_reset(); + /** + * Sets model matrix transform to apply to any vertex passed to drawing functions. + */ + void modelmat_set(const float modelmat[4][4]); + + /** + * Drawing functions that will draw wire-frames with the given color. + */ + void draw_line(float3 v1, float3 v2, float4 color = {1, 0, 0, 1}); + void draw_polygon(Span<float3> poly_verts, float4 color = {1, 0, 0, 1}); + void draw_bbox(const BoundBox &bbox, const float4 color = {1, 0, 0, 1}); + void draw_sphere(const float3 center, float radius, const float4 color = {1, 0, 0, 1}); + void draw_point(const float3 center, float radius = 0.01f, const float4 color = {1, 0, 0, 1}); + /** + * Draw a matrix transformation as 3 colored axes. + */ + void draw_matrix(const float4x4 m4); + /** + * Draw a matrix as a 2 units length bounding box, centered on origin. + */ + void draw_matrix_as_bbox(float4x4 mat, const float4 color = {1, 0, 0, 1}); + + /** + * Will draw all debug shapes and text cached up until now to the current view / framebuffer. + * Draw buffers will be emptied and ready for new debug data. + */ + void display_to_view(); + + /** + * Log variable or strings inside the viewport. + * Using a unique non string argument will print the variable name with it. + * Concatenate by using multiple arguments. i.e: `print("Looped ", n, "times.")`. + */ + template<typename... Ts> void print(StringRefNull str, Ts... args) + { + print_no_endl(str, args...); + print_newline(); + } + template<typename T> void print(const T &&value) + { + print_value(value); + print_newline(); + } + template<typename T> void print_hex(const T &&value) + { + print_value_hex(value); + print_newline(); + } + template<typename T> void print_binary(const T &&value) + { + print_value_binary(value); + print_newline(); + } + + /** + * Same as `print()` but does not finish the line. + */ + void print_no_endl(std::string arg) + { + print_string(arg); + } + void print_no_endl(StringRef arg) + { + print_string(arg); + } + void print_no_endl(StringRefNull arg) + { + print_string(arg); + } + void print_no_endl(char const *arg) + { + print_string(StringRefNull(arg)); + } + template<typename T> void print_no_endl(T arg) + { + print_value(arg); + } + template<typename T, typename... Ts> void print_no_endl(T arg, Ts... args) + { + print_no_endl(arg); + print_no_endl(args...); + } + + /** + * Not to be called by user. Should become private. + */ + GPUStorageBuf *gpu_draw_buf_get(); + GPUStorageBuf *gpu_print_buf_get(); + + private: + uint color_pack(float4 color); + DRWDebugVert vert_pack(float3 pos, uint color); + + void draw_line(float3 v1, float3 v2, uint color); + + void print_newline(); + void print_string_start(uint len); + void print_string(std::string str); + void print_char4(uint data); + void print_append_char(uint char1, uint &char4); + void print_append_digit(uint digit, uint &char4); + void print_append_space(uint &char4); + void print_value_binary(uint value); + void print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned); + + template<typename T> void print_value(const T &value); + template<typename T> void print_value_hex(const T &value); + template<typename T> void print_value_binary(const T &value); + + void display_lines(); + void display_prints(); +}; + +} // namespace blender::draw + +/** + * Ease of use function to get the debug module. + * TODO(fclem): Should be removed once DRWManager is no longer global. + * IMPORTANT: Can return nullptr if storage buffer is not supported. + */ +blender::draw::DebugDraw *DRW_debug_get(); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index b2422504825..4688ca6604e 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -3028,6 +3028,8 @@ void DRW_engines_free(void) DRW_stats_free(); DRW_globals_free(); + drw_debug_module_free(DST.debug); + DRW_UBO_FREE_SAFE(G_draw.block_ubo); DRW_UBO_FREE_SAFE(G_draw.view_ubo); DRW_TEXTURE_FREE_SAFE(G_draw.ramp); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 411123f00bf..a29f2fa7507 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -501,20 +501,6 @@ typedef struct DRWCommandSmallChunk { BLI_STATIC_ASSERT_ALIGN(DRWCommandChunk, 16); #endif -/* ------------- DRAW DEBUG ------------ */ - -typedef struct DRWDebugLine { - struct DRWDebugLine *next; /* linked list */ - float pos[2][3]; - float color[4]; -} DRWDebugLine; - -typedef struct DRWDebugSphere { - struct DRWDebugSphere *next; /* linked list */ - float mat[4][4]; - float color[4]; -} DRWDebugSphere; - /* ------------- Memory Pools ------------ */ /* Contains memory pools information */ @@ -656,11 +642,7 @@ typedef struct DRWManager { GPUDrawList *draw_list; - struct { - /* TODO(@fclem): optimize: use chunks. */ - DRWDebugLine *lines; - DRWDebugSphere *spheres; - } debug; + DRWDebugModule *debug; } DRWManager; extern DRWManager DST; /* TODO: get rid of this and allow multi-threaded rendering. */ @@ -675,6 +657,9 @@ void drw_state_set(DRWState state); void drw_debug_draw(void); void drw_debug_init(void); +void drw_debug_module_free(DRWDebugModule *module); +GPUStorageBuf *drw_debug_gpu_draw_buf_get(void); +GPUStorageBuf *drw_debug_gpu_print_buf_get(void); eDRWCommandType command_type_get(const uint64_t *command_type_bits, int index); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 9392efcf92b..2f6090b22c4 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -17,9 +17,14 @@ #include "BKE_pbvh.h" #include "BKE_volume.h" +/* For debug cursor position. */ +#include "WM_api.h" +#include "wm_window.h" + #include "DNA_curve_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" +#include "DNA_screen_types.h" #include "BLI_alloca.h" #include "BLI_hash.h" @@ -39,6 +44,16 @@ #include "intern/gpu_codegen.h" +/** + * IMPORTANT: + * In order to be able to write to the same print buffer sequentially, we add a barrier to allow + * multiple shader calls writting to the same buffer. + * However, this adds explicit synchronisation events which might change the rest of the + * application behavior and hide some bugs. If you know you are using shader debug print in only + * one shader pass, you can comment this out to remove the aforementioned barrier. + */ +#define DISABLE_DEBUG_SHADER_PRINT_BARRIER + /* -------------------------------------------------------------------- */ /** \name Uniform Buffer Object (DRW_uniformbuffer) * \{ */ @@ -647,7 +662,7 @@ static void drw_call_obinfos_init(DRWObjectInfos *ob_infos, Object *ob) drw_call_calc_orco(ob, ob_infos->orcotexfac); /* Random float value. */ uint random = (DST.dupli_source) ? - DST.dupli_source->random_id : + DST.dupli_source->random_id : /* TODO(fclem): this is rather costly to do at runtime. Maybe we can * put it in ob->runtime and make depsgraph ensure it is up to date. */ BLI_hash_int_2d(BLI_hash_string(ob->id.name + 2), 0); @@ -1510,6 +1525,27 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader) shgroup, view_ubo_location, DRW_UNIFORM_BLOCK, G_draw.view_ubo, 0, 0, 1); } +#ifdef DEBUG + int debug_print_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT); + if (debug_print_location != -1) { + GPUStorageBuf *buf = drw_debug_gpu_print_buf_get(); + drw_shgroup_uniform_create_ex( + shgroup, debug_print_location, DRW_UNIFORM_STORAGE_BLOCK, buf, 0, 0, 1); +# ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER + /* Add a barrier to allow multiple shader writting to the same buffer. */ + DRW_shgroup_barrier(shgroup, GPU_BARRIER_SHADER_STORAGE); +# endif + } + + int debug_draw_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS); + if (debug_draw_location != -1) { + GPUStorageBuf *buf = drw_debug_gpu_draw_buf_get(); + drw_shgroup_uniform_create_ex( + shgroup, debug_draw_location, DRW_UNIFORM_STORAGE_BLOCK, buf, 0, 0, 1); + /* NOTE(fclem): No barrier as ordering is not important. */ + } +#endif + /* Not supported. */ BLI_assert(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV) == -1); BLI_assert(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW) == -1); @@ -1986,6 +2022,13 @@ DRWView *DRW_view_create(const float viewmat[4][4], copy_v4_fl4(view->storage.viewcamtexcofac, 1.0f, 1.0f, 0.0f, 0.0f); + if (DST.draw_ctx.evil_C && DST.draw_ctx.region) { + int region_origin[2] = {DST.draw_ctx.region->winrct.xmin, DST.draw_ctx.region->winrct.ymin}; + struct wmWindow *win = CTX_wm_window(DST.draw_ctx.evil_C); + wm_cursor_position_get(win, &view->storage.mouse_pixel[0], &view->storage.mouse_pixel[1]); + sub_v2_v2v2_int(view->storage.mouse_pixel, view->storage.mouse_pixel, region_origin); + } + DRW_view_update(view, viewmat, winmat, culling_viewmat, culling_winmat); return view; diff --git a/source/blender/draw/intern/draw_shader.cc b/source/blender/draw/intern/draw_shader.cc index 001ceb0ae8d..ecb30d54b64 100644 --- a/source/blender/draw/intern/draw_shader.cc +++ b/source/blender/draw/intern/draw_shader.cc @@ -24,6 +24,8 @@ extern "C" char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; static struct { struct GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER]; + struct GPUShader *debug_print_display_sh; + struct GPUShader *debug_draw_display_sh; } e_data = {{nullptr}}; /* -------------------------------------------------------------------- */ @@ -109,6 +111,22 @@ GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineSh return e_data.hair_refine_sh[type]; } +GPUShader *DRW_shader_debug_print_display_get() +{ + if (e_data.debug_print_display_sh == nullptr) { + e_data.debug_print_display_sh = GPU_shader_create_from_info_name("draw_debug_print_display"); + } + return e_data.debug_print_display_sh; +} + +GPUShader *DRW_shader_debug_draw_display_get() +{ + if (e_data.debug_draw_display_sh == nullptr) { + e_data.debug_draw_display_sh = GPU_shader_create_from_info_name("draw_debug_draw_display"); + } + return e_data.debug_draw_display_sh; +} + /** \} */ void DRW_shaders_free() @@ -116,4 +134,6 @@ void DRW_shaders_free() for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) { DRW_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]); } + DRW_SHADER_FREE_SAFE(e_data.debug_print_display_sh); + DRW_SHADER_FREE_SAFE(e_data.debug_draw_display_sh); } diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h index 63d755cc334..dabb4b3327f 100644 --- a/source/blender/draw/intern/draw_shader.h +++ b/source/blender/draw/intern/draw_shader.h @@ -30,6 +30,9 @@ struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, struct GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineShaderType sh_type); +struct GPUShader *DRW_shader_debug_print_display_get(void); +struct GPUShader *DRW_shader_debug_draw_display_get(void); + void DRW_shaders_free(void); #ifdef __cplusplus diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index fbcc78e52f9..0fe4f3fbbff 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -14,6 +14,9 @@ typedef struct CurvesInfos CurvesInfos; typedef struct DrawCommand DrawCommand; typedef struct DrawCommandIndexed DrawCommandIndexed; typedef struct DispatchCommand DispatchCommand; +typedef struct DRWDebugPrintBuffer DRWDebugPrintBuffer; +typedef struct DRWDebugVert DRWDebugVert; +typedef struct DRWDebugDrawBuffer DRWDebugDrawBuffer; #endif #define DRW_SHADER_SHARED_H @@ -48,6 +51,12 @@ struct ViewInfos { /** NOTE: vec3 arrays are padded to vec4. */ float4 frustum_corners[8]; float4 frustum_planes[6]; + + /** For debugging purpose */ + /* Mouse pixel. */ + int2 mouse_pixel; + + int2 _pad0; }; BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) @@ -131,3 +140,59 @@ struct DispatchCommand { uint _pad0; }; BLI_STATIC_ASSERT_ALIGN(DispatchCommand, 16) + +/* -------------------------------------------------------------------- */ +/** \name Debug print + * \{ */ + +/* Take the header (DrawCommand) into account. */ +#define DRW_DEBUG_PRINT_MAX (8 * 1024) - 4 +/* NOTE: Cannot be more than 255 (because of column encoding). */ +#define DRW_DEBUG_PRINT_WORD_WRAP_COLUMN 120u + +/* The debug print buffer is laid-out as the following struct. + * But we use plain array in shader code instead because of driver issues. */ +struct DRWDebugPrintBuffer { + DrawCommand command; + /** Each character is encoded as 3 uchar with char_index, row and column position. */ + uint char_array[DRW_DEBUG_PRINT_MAX]; +}; +BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16) + +/* Use number of char as vertex count. Equivalent to `DRWDebugPrintBuffer.command.v_count`. */ +#define drw_debug_print_cursor drw_debug_print_buf[0] +/* Reuse first instance as row index as we don't use instancing. Equivalent to + * `DRWDebugPrintBuffer.command.i_first`. */ +#define drw_debug_print_row_shared drw_debug_print_buf[3] + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Debug draw shapes + * \{ */ + +struct DRWDebugVert { + /* This is a weird layout, but needed to be able to use DRWDebugVert as + * a DrawCommand and avoid alignment issues. See drw_debug_verts_buf[] definition. */ + uint pos0; + uint pos1; + uint pos2; + uint color; +}; +BLI_STATIC_ASSERT_ALIGN(DRWDebugVert, 16) + +/* Take the header (DrawCommand) into account. */ +#define DRW_DEBUG_DRAW_VERT_MAX (64 * 1024) - 1 + +/* The debug draw buffer is laid-out as the following struct. + * But we use plain array in shader code instead because of driver issues. */ +struct DRWDebugDrawBuffer { + DrawCommand command; + DRWDebugVert verts[DRW_DEBUG_DRAW_VERT_MAX]; +}; +BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16) + +/* Equivalent to `DRWDebugDrawBuffer.command.v_count`. */ +#define drw_debug_draw_v_count drw_debug_verts_buf[0].pos0 + +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl new file mode 100644 index 00000000000..7a79f957462 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl @@ -0,0 +1,216 @@ + +/** + * Debugging drawing library + * + * Quick way to draw debug geometry. All input should be in world space and + * will be rendered in the default view. No additional setup required. + **/ + +/** Global switch option. */ +bool drw_debug_draw_enable = true; +const vec4 drw_debug_default_color = vec4(1.0, 0.0, 0.0, 1.0); + +/* -------------------------------------------------------------------- */ +/** \name Interals + * \{ */ + +uint drw_debug_start_draw(uint v_needed) +{ + uint vertid = atomicAdd(drw_debug_draw_v_count, v_needed); + /* NOTE: Skip the header manually. */ + vertid += 1; + return vertid; +} + +uint drw_debug_color_pack(vec4 color) +{ + color = clamp(color, 0.0, 1.0); + uint result = 0; + result |= uint(color.x * 255.0) << 0u; + result |= uint(color.y * 255.0) << 8u; + result |= uint(color.z * 255.0) << 16u; + result |= uint(color.w * 255.0) << 24u; + return result; +} + +void drw_debug_line(inout uint vertid, vec3 v1, vec3 v2, uint color) +{ + drw_debug_verts_buf[vertid++] = DRWDebugVert( + floatBitsToUint(v1.x), floatBitsToUint(v1.y), floatBitsToUint(v1.z), color); + drw_debug_verts_buf[vertid++] = DRWDebugVert( + floatBitsToUint(v2.x), floatBitsToUint(v2.y), floatBitsToUint(v2.z), color); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name API + * \{ */ + +/** + * Draw a line. + */ +void drw_debug_line(vec3 v1, vec3 v2, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const uint v_needed = 2; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + drw_debug_line(vertid, v1, v2, drw_debug_color_pack(color)); + } +} +void drw_debug_line(vec3 v1, vec3 v2) +{ + drw_debug_line(v1, v2, drw_debug_default_color); +} + +/** + * Draw a quad contour. + */ +void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const uint v_needed = 8; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + uint pcolor = drw_debug_color_pack(color); + drw_debug_line(vertid, v1, v2, pcolor); + drw_debug_line(vertid, v2, v3, pcolor); + drw_debug_line(vertid, v3, v4, pcolor); + drw_debug_line(vertid, v4, v1, pcolor); + } +} +void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4) +{ + drw_debug_quad(v1, v2, v3, v4, drw_debug_default_color); +} + +/** + * Draw a point as octahedron wireframe. + */ +void drw_debug_point(vec3 p, float radius, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + vec3 c = vec3(radius, -radius, 0); + vec3 v1 = p + c.xzz; + vec3 v2 = p + c.zxz; + vec3 v3 = p + c.yzz; + vec3 v4 = p + c.zyz; + vec3 v5 = p + c.zzx; + vec3 v6 = p + c.zzy; + + const uint v_needed = 12 * 2; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + uint pcolor = drw_debug_color_pack(color); + drw_debug_line(vertid, v1, v2, pcolor); + drw_debug_line(vertid, v2, v3, pcolor); + drw_debug_line(vertid, v3, v4, pcolor); + drw_debug_line(vertid, v4, v1, pcolor); + drw_debug_line(vertid, v1, v5, pcolor); + drw_debug_line(vertid, v2, v5, pcolor); + drw_debug_line(vertid, v3, v5, pcolor); + drw_debug_line(vertid, v4, v5, pcolor); + drw_debug_line(vertid, v1, v6, pcolor); + drw_debug_line(vertid, v2, v6, pcolor); + drw_debug_line(vertid, v3, v6, pcolor); + drw_debug_line(vertid, v4, v6, pcolor); + } +} +void drw_debug_point(vec3 p, float radius) +{ + drw_debug_point(p, radius, drw_debug_default_color); +} +void drw_debug_point(vec3 p) +{ + drw_debug_point(p, 0.01); +} + +/** + * Draw a sphere wireframe as 3 axes circle. + */ +void drw_debug_sphere(vec3 p, float radius, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const int circle_resolution = 16; + const uint v_needed = circle_resolution * 2 * 3; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + uint pcolor = drw_debug_color_pack(color); + for (int axis = 0; axis < 3; axis++) { + for (int edge = 0; edge < circle_resolution; edge++) { + float angle1 = (2.0 * 3.141592) * float(edge + 0) / float(circle_resolution); + vec3 p1 = vec3(cos(angle1), sin(angle1), 0.0); + p1 = vec3(p1[(0 + axis) % 3], p1[(1 + axis) % 3], p1[(2 + axis) % 3]); + + float angle2 = (2.0 * 3.141592) * float(edge + 1) / float(circle_resolution); + vec3 p2 = vec3(cos(angle2), sin(angle2), 0.0); + p2 = vec3(p2[(0 + axis) % 3], p2[(1 + axis) % 3], p2[(2 + axis) % 3]); + + drw_debug_line(vertid, p1, p2, pcolor); + } + } + } +} +void drw_debug_sphere(vec3 p, float radius) +{ + drw_debug_sphere(p, radius, drw_debug_default_color); +} + +/** + * Draw a matrix transformation as 3 colored axes. + */ +void drw_debug_matrix(mat4 mat, vec4 color) +{ + vec4 p[4] = vec4[4](vec4(0, 0, 0, 1), vec4(1, 0, 0, 1), vec4(0, 1, 0, 1), vec4(0, 0, 1, 1)); + for (int i = 0; i < 4; i++) { + p[i] = mat * p[i]; + p[i].xyz /= p[i].w; + } + drw_debug_line(p[0].xyz, p[0].xyz, vec4(1, 0, 0, 1)); + drw_debug_line(p[0].xyz, p[1].xyz, vec4(0, 1, 0, 1)); + drw_debug_line(p[0].xyz, p[2].xyz, vec4(0, 0, 1, 1)); +} +void drw_debug_matrix(mat4 mat) +{ + drw_debug_matrix(mat, drw_debug_default_color); +} + +/** + * Draw a matrix as a 2 units length bounding box, centered on origin. + */ +void drw_debug_matrix_as_bbox(mat4 mat, vec4 color) +{ + vec4 p[8] = vec4[8](vec4(-1, -1, -1, 1), + vec4(1, -1, -1, 1), + vec4(1, 1, -1, 1), + vec4(-1, 1, -1, 1), + vec4(-1, -1, 1, 1), + vec4(1, -1, 1, 1), + vec4(1, 1, 1, 1), + vec4(-1, 1, 1, 1)); + for (int i = 0; i < 8; i++) { + p[i] = mat * p[i]; + p[i].xyz /= p[i].w; + } + drw_debug_quad(p[0].xyz, p[1].xyz, p[2].xyz, p[3].xyz, color); + drw_debug_line(p[0].xyz, p[4].xyz, color); + drw_debug_line(p[1].xyz, p[5].xyz, color); + drw_debug_line(p[2].xyz, p[6].xyz, color); + drw_debug_line(p[3].xyz, p[7].xyz, color); + drw_debug_quad(p[4].xyz, p[5].xyz, p[6].xyz, p[7].xyz, color); +} +void drw_debug_matrix_as_bbox(mat4 mat) +{ + drw_debug_matrix_as_bbox(mat, drw_debug_default_color); +} + +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_debug_print_lib.glsl b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl new file mode 100644 index 00000000000..0c7f32bd00d --- /dev/null +++ b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl @@ -0,0 +1,389 @@ + +/** + * Debug print implementation for shaders. + * + * `print()`: + * Log variable or strings inside the viewport. + * Using a unique non string argument will print the variable name with it. + * Concatenate by using multiple arguments. i.e: `print("Looped ", n, "times.")`. + * `drw_print_no_endl()`: + * Same as `print()` but does not finish the line. + * `drw_print_value()`: + * Display only the value of a variable. Does not finish the line. + * `drw_print_value_hex()`: + * Display only the hex representation of a variable. Does not finish the line. + * `drw_print_value_binary()`: Display only the binary representation of a + * variable. Does not finish the line. + * + * IMPORTANT: As it is now, it is not yet thread safe. Only print from one thread. You can use the + * IS_DEBUG_MOUSE_FRAGMENT macro in fragment shader to filter using mouse position or + * IS_FIRST_INVOCATION in compute shaders. + * + * NOTE: Floating point representation might not be very precise (see drw_print_value(float)). + * + * IMPORTANT: Multipler drawcalls can write to the buffer in sequence (if they are from different + * shgroups). However, we add barriers to support this case and it might change the application + * behavior. Uncomment DISABLE_DEBUG_SHADER_drw_print_BARRIER to remove the barriers if that + * happens. But then you are limited to a single invocation output. + * + * IMPORTANT: All of these are copied to the CPU debug libs (draw_debug.cc). They need to be kept + * in sync to write the same data. + */ + +/** Global switch option when you want to silence all prints from all shaders at once. */ +bool drw_debug_print_enable = true; + +/* Set drw_print_col to max value so we will start by creating a new line and get the correct + * threadsafe row. */ +uint drw_print_col = DRW_DEBUG_PRINT_WORD_WRAP_COLUMN; +uint drw_print_row = 0u; + +void drw_print_newline() +{ + if (!drw_debug_print_enable) { + return; + } + drw_print_col = 0u; + drw_print_row = atomicAdd(drw_debug_print_row_shared, 1u) + 1u; +} + +void drw_print_string_start(uint len) +{ + if (!drw_debug_print_enable) { + return; + } + /* Break before word. */ + if (drw_print_col + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + drw_print_newline(); + } +} + +void drw_print_char4(uint data) +{ + if (!drw_debug_print_enable) { + return; + } + /* Convert into char stream. */ + for (; data != 0u; data >>= 8u) { + uint char1 = data & 0xFFu; + /* Check for null terminator. */ + if (char1 == 0x00) { + break; + } + uint cursor = atomicAdd(drw_debug_print_cursor, 1u); + /* NOTE: Skip the header manually. */ + cursor += 4; + if (cursor < DRW_DEBUG_PRINT_MAX) { + /* For future usage. (i.e: Color) */ + uint flags = 0u; + uint col = drw_print_col++; + uint drw_print_header = (flags << 24u) | (drw_print_row << 16u) | (col << 8u); + drw_debug_print_buf[cursor] = drw_print_header | char1; + /* Break word. */ + if (drw_print_col > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + drw_print_newline(); + } + } + } +} + +/** + * NOTE(fclem): Strange behavior emerge when trying to increment the digit + * counter inside the append function. It looks like the compiler does not see + * it is referenced as an index for char4 and thus do not capture the right + * reference. I do not know if this is undefined behavior. As a matter of + * precaution, we implement all the append function separately. This behavior + * was observed on both Mesa & amdgpu-pro. + */ +/* Using ascii char code. Expect char1 to be less or equal to 0xFF. Appends chars to the right. */ +void drw_print_append_char(uint char1, inout uint char4) +{ + char4 = (char4 << 8u) | char1; +} + +void drw_print_append_digit(uint digit, inout uint char4) +{ + const uint char_A = 0x41u; + const uint char_0 = 0x30u; + bool is_hexadecimal = digit > 9u; + char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit)); +} + +void drw_print_append_space(inout uint char4) +{ + char4 = (char4 << 8u) | 0x20u; +} + +void drw_print_value_binary(uint value) +{ + drw_print_no_endl("0b"); + drw_print_string_start(10u * 4u); + uint digits[10] = uint[10](0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u); + uint digit = 0u; + for (uint i = 0u; i < 32u; i++) { + drw_print_append_digit(((value >> i) & 1u), digits[digit / 4u]); + digit++; + if ((i % 4u) == 3u) { + drw_print_append_space(digits[digit / 4u]); + digit++; + } + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 9; j >= 0; j--) { + drw_print_char4(digits[j]); + } +} + +void drw_print_value_binary(int value) +{ + drw_print_value_binary(uint(value)); +} + +void drw_print_value_binary(float value) +{ + drw_print_value_binary(floatBitsToUint(value)); +} + +void drw_print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned) +{ + drw_print_string_start(3u * 4u); + const uint blank_value = hex ? 0x30303030u : 0x20202020u; + const uint prefix = hex ? 0x78302020u : 0x20202020u; + uint digits[3] = uint[3](blank_value, blank_value, prefix); + const uint base = hex ? 16u : 10u; + uint digit = 0u; + /* Add `u` suffix. */ + if (is_unsigned) { + drw_print_append_char('u', digits[digit / 4u]); + digit++; + } + /* Number's digits. */ + for (; value != 0u || digit == uint(is_unsigned); value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Add negative sign. */ + if (is_negative) { + drw_print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + drw_print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + drw_print_char4(digits[j]); + } +} + +void drw_print_value_hex(uint value) +{ + drw_print_value_uint(value, true, false, false); +} + +void drw_print_value_hex(int value) +{ + drw_print_value_uint(uint(value), true, false, false); +} + +void drw_print_value_hex(float value) +{ + drw_print_value_uint(floatBitsToUint(value), true, false, false); +} + +void drw_print_value(uint value) +{ + drw_print_value_uint(value, false, false, true); +} + +void drw_print_value(int value) +{ + drw_print_value_uint(uint(abs(value)), false, (value < 0), false); +} + +void drw_print_value(bool value) +{ + if (value) { + drw_print_no_endl("true "); + } + else { + drw_print_no_endl("false"); + } +} + +/* NOTE(@fclem): This is homebrew and might not be 100% accurate (accuracy has + * not been tested and might dependent on compiler implementation). If unsure, + * use drw_print_value_hex and transcribe the value manually with another tool. */ +void drw_print_value(float val) +{ + /* We pad the string to match normal float values length. */ + if (isnan(val)) { + drw_print_no_endl(" NaN"); + return; + } + if (isinf(val)) { + if (sign(val) < 0.0) { + drw_print_no_endl(" -Inf"); + } + else { + drw_print_no_endl(" Inf"); + } + return; + } + + /* Adjusted for significant digits (6) with sign (1), decimal separator (1) + * and exponent (4). */ + const float significant_digits = 6.0; + drw_print_string_start(3u * 4u); + uint digits[3] = uint[3](0x20202020u, 0x20202020u, 0x20202020u); + + float exponent = floor(log(abs(val)) / log(10.0)); + bool display_exponent = exponent >= (significant_digits) || + exponent <= (-significant_digits + 1.0); + + float int_significant_digits = min(exponent + 1.0, significant_digits); + float dec_significant_digits = max(0.0, significant_digits - int_significant_digits); + /* Power to get to the rounding point. */ + float rounding_power = dec_significant_digits; + + if (val == 0.0 || isinf(exponent)) { + display_exponent = false; + int_significant_digits = dec_significant_digits = 1.0; + } + /* Remap to keep significant numbers count. */ + if (display_exponent) { + int_significant_digits = 1.0; + dec_significant_digits = significant_digits - int_significant_digits; + rounding_power = -exponent + dec_significant_digits; + } + /* Round at the last significant digit. */ + val = round(val * pow(10.0, rounding_power)); + /* Get back to final exponent. */ + val *= pow(10.0, -dec_significant_digits); + + float int_part; + float dec_part = modf(val, int_part); + + dec_part *= pow(10.0, dec_significant_digits); + + const uint base = 10u; + uint digit = 0u; + /* Exponent */ + uint value = uint(abs(exponent)); + if (display_exponent) { + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Exponent sign. */ + uint sign_char = (exponent < 0.0) ? '-' : '+'; + drw_print_append_char(sign_char, digits[digit / 4u]); + digit++; + /* Exponent `e` suffix. */ + drw_print_append_char(0x65u, digits[digit / 4u]); + digit++; + } + /* Decimal part. */ + value = uint(abs(dec_part)); +#if 0 /* We don't do that because it makes unstable values really hard to \ + read. */ + /* Trim trailing zeros. */ + while ((value % base) == 0u) { + value /= base; + if (value == 0u) { + break; + } + } +#endif + if (value != 0u) { + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Point separator. */ + drw_print_append_char('.', digits[digit / 4u]); + digit++; + } + /* Integer part. */ + value = uint(abs(int_part)); + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Negative sign. */ + if (val < 0.0) { + drw_print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + drw_print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + drw_print_char4(digits[j]); + } +} + +void drw_print_value(vec2 value) +{ + drw_print_no_endl("vec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(vec3 value) +{ + drw_print_no_endl("vec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(vec4 value) +{ + drw_print_no_endl("vec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void drw_print_value(ivec2 value) +{ + drw_print_no_endl("ivec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(ivec3 value) +{ + drw_print_no_endl("ivec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(ivec4 value) +{ + drw_print_no_endl("ivec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void drw_print_value(uvec2 value) +{ + drw_print_no_endl("uvec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(uvec3 value) +{ + drw_print_no_endl("uvec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(uvec4 value) +{ + drw_print_no_endl("uvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void drw_print_value(bvec2 value) +{ + drw_print_no_endl("bvec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(bvec3 value) +{ + drw_print_no_endl("bvec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(bvec4 value) +{ + drw_print_no_endl("bvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index 8eecaa46b58..8ab2ef10e4c 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -37,6 +37,9 @@ layout(std140) uniform viewBlock # endif #endif +#define IS_DEBUG_MOUSE_FRAGMENT (ivec2(gl_FragCoord) == drw_view.mouse_pixel) +#define IS_FIRST_INVOCATION (gl_GlobalInvocationID == uvec3(0)) + #define ViewNear (ViewVecs[0].w) #define ViewFar (ViewVecs[1].w) diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl new file mode 100644 index 00000000000..3fc5294b024 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl @@ -0,0 +1,9 @@ + +/** + * Display debug edge list. + **/ + +void main() +{ + out_color = interp.color; +} diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl new file mode 100644 index 00000000000..92c546aa203 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl @@ -0,0 +1,15 @@ + +/** + * Display debug edge list. + **/ + +void main() +{ + /* Skip the first vertex containing header data. */ + DRWDebugVert vert = drw_debug_verts_buf[gl_VertexID + 1]; + vec3 pos = uintBitsToFloat(uvec3(vert.pos0, vert.pos1, vert.pos2)); + vec4 col = vec4((uvec4(vert.color) >> uvec4(0, 8, 16, 24)) & 0xFFu); + + interp.color = col; + gl_Position = persmat * vec4(pos, 1.0); +} diff --git a/source/blender/draw/intern/shaders/draw_debug_info.hh b/source/blender/draw/intern/shaders/draw_debug_info.hh new file mode 100644 index 00000000000..7fbbc858d61 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_info.hh @@ -0,0 +1,51 @@ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Debug print + * + * Allows print() function to have logging support inside shaders. + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_debug_print) + .typedef_source("draw_shader_shared.h") + .storage_buf(7, Qualifier::READ_WRITE, "uint", "drw_debug_print_buf[]"); + +GPU_SHADER_INTERFACE_INFO(draw_debug_print_display_iface, "").flat(Type::UINT, "char_index"); + +GPU_SHADER_CREATE_INFO(draw_debug_print_display) + .do_static_compilation(true) + .typedef_source("draw_shader_shared.h") + .storage_buf(7, Qualifier::READ, "uint", "drw_debug_print_buf[]") + .vertex_out(draw_debug_print_display_iface) + .fragment_out(0, Type::VEC4, "out_color") + .vertex_source("draw_debug_print_display_vert.glsl") + .fragment_source("draw_debug_print_display_frag.glsl") + .additional_info("draw_view"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Debug draw shapes + * + * Allows to draw lines and points just like the DRW_debug module functions. + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_debug_draw) + .typedef_source("draw_shader_shared.h") + .storage_buf(6, Qualifier::READ_WRITE, "DRWDebugVert", "drw_debug_verts_buf[]"); + +GPU_SHADER_INTERFACE_INFO(draw_debug_draw_display_iface, "interp").flat(Type::VEC4, "color"); + +GPU_SHADER_CREATE_INFO(draw_debug_draw_display) + .do_static_compilation(true) + .typedef_source("draw_shader_shared.h") + .storage_buf(6, Qualifier::READ, "DRWDebugVert", "drw_debug_verts_buf[]") + .vertex_out(draw_debug_draw_display_iface) + .fragment_out(0, Type::VEC4, "out_color") + .push_constant(Type::MAT4, "persmat") + .vertex_source("draw_debug_draw_display_vert.glsl") + .fragment_source("draw_debug_draw_display_frag.glsl") + .additional_info("draw_view"); + +/** \} */ diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl new file mode 100644 index 00000000000..fe608816109 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl @@ -0,0 +1,133 @@ + +/** + * Display characters using an ascii table. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +bool char_intersect(uvec2 bitmap_position) +{ + /* Using 8x8 = 64bits = uvec2. */ + uvec2 ascii_bitmap[96] = uvec2[96](uvec2(0x00000000u, 0x00000000u), + uvec2(0x18001800u, 0x183c3c18u), + uvec2(0x00000000u, 0x36360000u), + uvec2(0x7f363600u, 0x36367f36u), + uvec2(0x301f0c00u, 0x0c3e031eu), + uvec2(0x0c666300u, 0x00633318u), + uvec2(0x3b336e00u, 0x1c361c6eu), + uvec2(0x00000000u, 0x06060300u), + uvec2(0x060c1800u, 0x180c0606u), + uvec2(0x180c0600u, 0x060c1818u), + uvec2(0x3c660000u, 0x00663cffu), + uvec2(0x0c0c0000u, 0x000c0c3fu), + uvec2(0x000c0c06u, 0x00000000u), + uvec2(0x00000000u, 0x0000003fu), + uvec2(0x000c0c00u, 0x00000000u), + uvec2(0x06030100u, 0x6030180cu), + uvec2(0x6f673e00u, 0x3e63737bu), + uvec2(0x0c0c3f00u, 0x0c0e0c0cu), + uvec2(0x06333f00u, 0x1e33301cu), + uvec2(0x30331e00u, 0x1e33301cu), + uvec2(0x7f307800u, 0x383c3633u), + uvec2(0x30331e00u, 0x3f031f30u), + uvec2(0x33331e00u, 0x1c06031fu), + uvec2(0x0c0c0c00u, 0x3f333018u), + uvec2(0x33331e00u, 0x1e33331eu), + uvec2(0x30180e00u, 0x1e33333eu), + uvec2(0x000c0c00u, 0x000c0c00u), + uvec2(0x000c0c06u, 0x000c0c00u), + uvec2(0x060c1800u, 0x180c0603u), + uvec2(0x003f0000u, 0x00003f00u), + uvec2(0x180c0600u, 0x060c1830u), + uvec2(0x0c000c00u, 0x1e333018u), + uvec2(0x7b031e00u, 0x3e637b7bu), + uvec2(0x3f333300u, 0x0c1e3333u), + uvec2(0x66663f00u, 0x3f66663eu), + uvec2(0x03663c00u, 0x3c660303u), + uvec2(0x66361f00u, 0x1f366666u), + uvec2(0x16467f00u, 0x7f46161eu), + uvec2(0x16060f00u, 0x7f46161eu), + uvec2(0x73667c00u, 0x3c660303u), + uvec2(0x33333300u, 0x3333333fu), + uvec2(0x0c0c1e00u, 0x1e0c0c0cu), + uvec2(0x33331e00u, 0x78303030u), + uvec2(0x36666700u, 0x6766361eu), + uvec2(0x46667f00u, 0x0f060606u), + uvec2(0x6b636300u, 0x63777f7fu), + uvec2(0x73636300u, 0x63676f7bu), + uvec2(0x63361c00u, 0x1c366363u), + uvec2(0x06060f00u, 0x3f66663eu), + uvec2(0x3b1e3800u, 0x1e333333u), + uvec2(0x36666700u, 0x3f66663eu), + uvec2(0x38331e00u, 0x1e33070eu), + uvec2(0x0c0c1e00u, 0x3f2d0c0cu), + uvec2(0x33333f00u, 0x33333333u), + uvec2(0x331e0c00u, 0x33333333u), + uvec2(0x7f776300u, 0x6363636bu), + uvec2(0x1c366300u, 0x6363361cu), + uvec2(0x0c0c1e00u, 0x3333331eu), + uvec2(0x4c667f00u, 0x7f633118u), + uvec2(0x06061e00u, 0x1e060606u), + uvec2(0x30604000u, 0x03060c18u), + uvec2(0x18181e00u, 0x1e181818u), + uvec2(0x00000000u, 0x081c3663u), + uvec2(0x000000ffu, 0x00000000u), + uvec2(0x00000000u, 0x0c0c1800u), + uvec2(0x3e336e00u, 0x00001e30u), + uvec2(0x66663b00u, 0x0706063eu), + uvec2(0x03331e00u, 0x00001e33u), + uvec2(0x33336e00u, 0x3830303eu), + uvec2(0x3f031e00u, 0x00001e33u), + uvec2(0x06060f00u, 0x1c36060fu), + uvec2(0x333e301fu, 0x00006e33u), + uvec2(0x66666700u, 0x0706366eu), + uvec2(0x0c0c1e00u, 0x0c000e0cu), + uvec2(0x3033331eu, 0x30003030u), + uvec2(0x1e366700u, 0x07066636u), + uvec2(0x0c0c1e00u, 0x0e0c0c0cu), + uvec2(0x7f6b6300u, 0x0000337fu), + uvec2(0x33333300u, 0x00001f33u), + uvec2(0x33331e00u, 0x00001e33u), + uvec2(0x663e060fu, 0x00003b66u), + uvec2(0x333e3078u, 0x00006e33u), + uvec2(0x66060f00u, 0x00003b6eu), + uvec2(0x1e301f00u, 0x00003e03u), + uvec2(0x0c2c1800u, 0x080c3e0cu), + uvec2(0x33336e00u, 0x00003333u), + uvec2(0x331e0c00u, 0x00003333u), + uvec2(0x7f7f3600u, 0x0000636bu), + uvec2(0x1c366300u, 0x00006336u), + uvec2(0x333e301fu, 0x00003333u), + uvec2(0x0c263f00u, 0x00003f19u), + uvec2(0x0c0c3800u, 0x380c0c07u), + uvec2(0x18181800u, 0x18181800u), + uvec2(0x0c0c0700u, 0x070c0c38u), + uvec2(0x00000000u, 0x6e3b0000u), + uvec2(0x00000000u, 0x00000000u)); + + if (!in_range_inclusive(bitmap_position, uvec2(0), uvec2(7))) { + return false; + } + uint char_bits = ascii_bitmap[char_index][bitmap_position.y >> 2u & 1u]; + char_bits = (char_bits >> ((bitmap_position.y & 3u) * 8u + bitmap_position.x)); + return (char_bits & 1u) != 0u; +} + +void main() +{ + uvec2 bitmap_position = uvec2(gl_PointCoord.xy * 8.0); + /* Point coord start from top left corner. But layout is from bottom to top. */ + bitmap_position.y = 7 - bitmap_position.y; + + if (char_intersect(bitmap_position)) { + out_color = vec4(1); + } + else if (char_intersect(bitmap_position + uvec2(0, 1))) { + /* Shadow */ + out_color = vec4(0, 0, 0, 1); + } + else { + /* Transparent Background for ease of read. */ + out_color = vec4(0, 0, 0, 0.2); + } +}
\ No newline at end of file diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl new file mode 100644 index 00000000000..c8fc3815436 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl @@ -0,0 +1,29 @@ + +/** + * Display characters using an ascii table. Outputs one point per character. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + /* Skip first 4 chars containing header data. */ + uint char_data = drw_debug_print_buf[gl_VertexID + 4]; + char_index = (char_data & 0xFFu) - 0x20u; + + /* Discard invalid chars. */ + if (char_index >= 96u) { + gl_Position = vec4(-1); + gl_PointSize = 0.0; + return; + } + uint row = (char_data >> 16u) & 0xFFu; + uint col = (char_data >> 8u) & 0xFFu; + + float char_size = 16.0; + /* Change anchor point to the top left. */ + vec2 pos_on_screen = char_size * vec2(col, row) + char_size * 4; + gl_Position = vec4( + pos_on_screen * drw_view.viewport_size_inverse * vec2(2.0, -2.0) - vec2(1.0, -1.0), 0, 1); + gl_PointSize = char_size; +}
\ No newline at end of file |