Files
test/source/blender/draw/intern/draw_debug.cc
Jeroen Bakker 6d91d36161 GPU: Remove GPU_shader_storage_buffer_objects_support
Blender 4.0 requires OpenGL 4.3 which always support SSBO's.
Platforms that don't support enough SSBO bind points will be marked
as unsupported.

Users who start Blender on those platforms will be informed via a
dialog. This PR also updates the `--debug-gpu-force-workarounds`
to match our minimum requirements. Note that some bugs are still
there that should be solved in other PRs:

* Workbench only renders the object using a unit matrix this is because
  there is a bug in the workaround for shader_draw_parameters
* Navigating with middle mouse button is not working. Unsure what the
  cause is, but might be a missing feature check in the OpenGL backend.

Related to #112224

Pull Request: https://projects.blender.org/blender/blender/pulls/112572
2023-09-21 12:55:51 +02:00

715 lines
19 KiB
C++

/* SPDX-FileCopyrightText: 2018 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*
* \brief Simple API to draw debug shapes in the viewport.
*/
#include "BKE_object.h"
#include "BLI_link_utils.h"
#include "BLI_math_matrix.hh"
#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>
#include <sstream>
#if defined(DEBUG) || defined(WITH_DRAW_DEBUG)
# define DRAW_DEBUG
#else
/* Uncomment to forcibly enable debug draw in release mode. */
//#define DRAW_DEBUG
#endif
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.vertex_len = 0;
cpu_print_buf_.command.vertex_first = 0;
cpu_print_buf_.command.instance_len = 1;
cpu_print_buf_.command.instance_first_array = 0;
cpu_draw_buf_.command.vertex_len = 0;
cpu_draw_buf_.command.vertex_first = 0;
cpu_draw_buf_.command.instance_len = 1;
cpu_draw_buf_.command.instance_first_array = 0;
gpu_print_buf_.command.vertex_len = 0;
gpu_print_buf_.command.vertex_first = 0;
gpu_print_buf_.command.instance_len = 1;
gpu_print_buf_.command.instance_first_array = 0;
gpu_print_buf_used = false;
gpu_draw_buf_.command.vertex_len = 0;
gpu_draw_buf_.command.vertex_first = 0;
gpu_draw_buf_.command.instance_len = 1;
gpu_draw_buf_.command.instance_first_array = 0;
gpu_draw_buf_used = false;
print_col_ = 0;
print_row_ = 0;
modelmat_reset();
}
void DebugDraw::modelmat_reset()
{
model_mat_ = float4x4::identity();
}
void DebugDraw::modelmat_set(const float modelmat[4][4])
{
model_mat_ = float4x4_view(modelmat);
}
GPUStorageBuf *DebugDraw::gpu_draw_buf_get()
{
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()
{
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> face_verts, float4 color)
{
BLI_assert(!face_verts.is_empty());
uint col = color_pack(color);
float3 v0 = math::transform_point(model_mat_, face_verts.last());
for (auto vert : face_verts) {
float3 v1 = math::transform_point(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 libraries (`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.vertex_len;
if (index + 2 < DRW_DEBUG_DRAW_VERT_MAX) {
buf.verts[index + 0] = vert_pack(math::transform_point(model_mat_, v1), color);
buf.verts[index + 1] = vert_pack(math::transform_point(model_mat_, v2), color);
buf.command.vertex_len += 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.vert_color = color;
return vert;
}
void DebugDraw::print_newline()
{
print_col_ = 0u;
print_row_ = ++cpu_print_buf_.command.instance_first_array;
}
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.vertex_len++;
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.vertex_len == 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);
GPU_shader_uniform_mat4(shader, "persmat", persmat.ptr());
if (gpu_draw_buf_used) {
GPU_debug_group_begin("GPU");
GPU_storagebuf_bind(gpu_draw_buf_, DRW_DEBUG_DRAW_SLOT);
GPU_batch_draw_indirect(batch, gpu_draw_buf_, 0);
GPU_storagebuf_unbind(gpu_draw_buf_);
GPU_debug_group_end();
}
GPU_debug_group_begin("CPU");
GPU_storagebuf_bind(cpu_draw_buf_, DRW_DEBUG_DRAW_SLOT);
GPU_batch_draw_indirect(batch, cpu_draw_buf_, 0);
GPU_storagebuf_unbind(cpu_draw_buf_);
GPU_debug_group_end();
GPU_debug_group_end();
}
void DebugDraw::display_prints()
{
if (cpu_print_buf_.command.vertex_len == 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);
float f_viewport[4];
GPU_viewport_size_get_f(f_viewport);
GPU_shader_uniform_2fv(shader, "viewport_size", &f_viewport[2]);
if (gpu_print_buf_used) {
GPU_debug_group_begin("GPU");
GPU_storagebuf_bind(gpu_print_buf_, DRW_DEBUG_PRINT_SLOT);
GPU_batch_draw_indirect(batch, gpu_print_buf_, 0);
GPU_storagebuf_unbind(gpu_print_buf_);
GPU_debug_group_end();
}
GPU_debug_group_begin("CPU");
GPU_storagebuf_bind(cpu_print_buf_, DRW_DEBUG_PRINT_SLOT);
GPU_batch_draw_indirect(batch, cpu_print_buf_, 0);
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
/* -------------------------------------------------------------------- */
/** \name DebugDraw Access
* \{ */
blender::draw::DebugDraw *DRW_debug_get()
{
return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name C-API private
* \{ */
void drw_debug_draw()
{
#ifdef DRAW_DEBUG
if (DST.debug == nullptr) {
return;
}
/* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->display_to_view();
#endif
}
/**
* 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 DRAW_DEBUG
/* 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 (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()
{
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_reset();
}
void DRW_debug_modelmat(const float modelmat[4][4])
{
#ifdef DRAW_DEBUG
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_set(modelmat);
#else
UNUSED_VARS(modelmat);
#endif
}
void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4])
{
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])
{
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])
{
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix(float4x4(m));
}
void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4])
{
blender::float4x4 m4(m);
if (invert) {
m4 = blender::math::invert(m4);
}
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix_as_bbox(m4, color);
}
void DRW_debug_bbox(const BoundBox *bbox, const float color[4])
{
#ifdef DRAW_DEBUG
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_bbox(*bbox, color);
#else
UNUSED_VARS(bbox, color);
#endif
}
void DRW_debug_sphere(const float center[3], float radius, const float color[4])
{
reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_sphere(center, radius, color);
}
/** \} */