Files
test2/source/blender/draw/intern/draw_debug.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

296 lines
9.1 KiB
C++
Raw Normal View History

/* 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.hh"
#include "BLI_math_bits.h"
#include "BLI_math_matrix.h"
#include "BLI_math_matrix.hh"
#include "GPU_batch.hh"
#include "GPU_debug.hh"
#include "draw_context_private.hh"
#include "draw_debug.hh"
2024-01-05 11:16:57 -05:00
#include "draw_shader.hh"
#include "draw_shader_shared.hh"
namespace blender::draw {
/* -------------------------------------------------------------------- */
/** \name Init and state
* \{ */
void DebugDraw::reset()
{
for (int i = 0; i < 2; i++) {
vertex_len_.store(0);
if (cpu_draw_buf_.current() == nullptr) {
cpu_draw_buf_.current() = MEM_new<DebugDrawBuf>("DebugDrawBuf-CPU", "DebugDrawBuf-CPU");
gpu_draw_buf_.current() = MEM_new<DebugDrawBuf>("DebugDrawBuf-GPU", "DebugDrawBuf-GPU");
}
cpu_draw_buf_.current()->command.vertex_len = 0;
cpu_draw_buf_.current()->command.vertex_first = 0;
cpu_draw_buf_.current()->command.instance_len = 1;
cpu_draw_buf_.current()->command.instance_first_array = 0;
gpu_draw_buf_.current()->command.vertex_len = 0;
gpu_draw_buf_.current()->command.vertex_first = 0;
gpu_draw_buf_.current()->command.instance_len = 1;
gpu_draw_buf_.current()->command.instance_first_array = 0;
gpu_draw_buf_.current()->push_update();
cpu_draw_buf_.swap();
gpu_draw_buf_.swap();
}
gpu_draw_buf_used = false;
}
GPUStorageBuf *DebugDraw::gpu_draw_buf_get()
{
#ifdef WITH_DRAW_DEBUG
gpu_draw_buf_used = true;
return *gpu_draw_buf_.current();
#else
return nullptr;
#endif
}
void DebugDraw::clear_gpu_data()
{
for (int i = 0; i < 2; i++) {
MEM_SAFE_DELETE(cpu_draw_buf_.current());
MEM_SAFE_DELETE(gpu_draw_buf_.current());
cpu_draw_buf_.swap();
gpu_draw_buf_.swap();
}
}
void drw_debug_clear()
{
DebugDraw::get().reset();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Draw functions
* \{ */
void drw_debug_line(const float3 v1, const float3 v2, const float4 color, const uint lifetime)
{
DebugDraw &dd = DebugDraw::get();
dd.draw_line(v1, v2, debug_color_pack(color), lifetime);
}
void drw_debug_polygon(Span<float3> face_verts, const float4 color, const uint lifetime)
{
BLI_assert(!face_verts.is_empty());
DebugDraw &dd = DebugDraw::get();
uint col = debug_color_pack(color);
float3 v0 = face_verts.last();
for (auto vert : face_verts) {
float3 v1 = vert;
dd.draw_line(v0, v1, col, lifetime);
v0 = v1;
}
}
void drw_debug_bbox(const BoundBox &bbox, const float4 color, const uint lifetime)
{
DebugDraw &dd = DebugDraw::get();
uint col = debug_color_pack(color);
dd.draw_line(bbox.vec[0], bbox.vec[1], col, lifetime);
dd.draw_line(bbox.vec[1], bbox.vec[2], col, lifetime);
dd.draw_line(bbox.vec[2], bbox.vec[3], col, lifetime);
dd.draw_line(bbox.vec[3], bbox.vec[0], col, lifetime);
dd.draw_line(bbox.vec[4], bbox.vec[5], col, lifetime);
dd.draw_line(bbox.vec[5], bbox.vec[6], col, lifetime);
dd.draw_line(bbox.vec[6], bbox.vec[7], col, lifetime);
dd.draw_line(bbox.vec[7], bbox.vec[4], col, lifetime);
dd.draw_line(bbox.vec[0], bbox.vec[4], col, lifetime);
dd.draw_line(bbox.vec[1], bbox.vec[5], col, lifetime);
dd.draw_line(bbox.vec[2], bbox.vec[6], col, lifetime);
dd.draw_line(bbox.vec[3], bbox.vec[7], col, lifetime);
}
static Vector<float3> precompute_sphere_points(int circle_resolution)
{
Vector<float3> result;
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);
const float point[3] = {cosf(angle), sinf(angle), 0.0f};
result.append(float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3]));
}
}
}
return result;
}
void drw_debug_sphere(const float3 center, float radius, const float4 color, const uint lifetime)
{
/** Precomputed shapes verts. */
static Vector<float3> sphere_verts = precompute_sphere_points(16);
DebugDraw &dd = DebugDraw::get();
uint col = debug_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;
dd.draw_line(v0, v1, col, lifetime);
}
}
void drw_debug_point(const float3 pos, float rad, const float4 col, const uint lifetime)
{
static Vector<float3> point_verts = precompute_sphere_points(4);
DebugDraw &dd = DebugDraw::get();
uint color = debug_color_pack(col);
for (auto i : IndexRange(point_verts.size() / 2)) {
float3 v0 = point_verts[i * 2] * rad + pos;
float3 v1 = point_verts[i * 2 + 1] * rad + pos;
dd.draw_line(v0, v1, color, lifetime);
}
}
void drw_debug_matrix(const float4x4 &m4, const uint lifetime)
{
float3 v0 = math::transform_point(m4, float3(0.0f, 0.0f, 0.0f));
float3 v1 = math::transform_point(m4, float3(1.0f, 0.0f, 0.0f));
float3 v2 = math::transform_point(m4, float3(0.0f, 1.0f, 0.0f));
float3 v3 = math::transform_point(m4, float3(0.0f, 0.0f, 1.0f));
DebugDraw &dd = DebugDraw::get();
dd.draw_line(v0, v1, debug_color_pack(float4(1.0f, 0.0f, 0.0f, 1.0f)), lifetime);
dd.draw_line(v0, v2, debug_color_pack(float4(0.0f, 1.0f, 0.0f, 1.0f)), lifetime);
dd.draw_line(v0, v3, debug_color_pack(float4(0.0f, 0.0f, 1.0f, 1.0f)), lifetime);
}
void drw_debug_matrix_as_bbox(const float4x4 &mat, const float4 color, const uint lifetime)
{
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]);
}
drw_debug_bbox(bb, color, lifetime);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Internals
*
* \{ */
void DebugDraw::draw_line(float3 v1, float3 v2, uint color, const uint lifetime)
{
DebugDrawBuf &buf = *cpu_draw_buf_.current();
uint index = vertex_len_.fetch_add(2);
if (index + 2 < DRW_DEBUG_DRAW_VERT_MAX) {
buf.verts[index / 2] = debug_line_make(float_as_uint(v1.x),
float_as_uint(v1.y),
float_as_uint(v1.z),
float_as_uint(v2.x),
float_as_uint(v2.y),
float_as_uint(v2.z),
color,
lifetime);
DRWManager: New implementation. This is a new implementation of the draw manager using modern rendering practices and GPU driven culling. This only ports features that are not considered deprecated or to be removed. The old DRW API is kept working along side this new one, and does not interfeer with it. However this needed some more hacking inside the draw_view_lib.glsl. At least the create info are well separated. The reviewer might start by looking at `draw_pass_test.cc` to see the API in usage. Important files are `draw_pass.hh`, `draw_command.hh`, `draw_command_shared.hh`. In a nutshell (for a developper used to old DRW API): - `DRWShadingGroups` are replaced by `Pass<T>::Sub`. - Contrary to DRWShadingGroups, all commands recorded inside a pass or sub-pass (even binds / push_constant / uniforms) will be executed in order. - All memory is managed per object (except for Sub-Pass which are managed by their parent pass) and not from draw manager pools. So passes "can" potentially be recorded once and submitted multiple time (but this is not really encouraged for now). The only implicit link is between resource lifetime and `ResourceHandles` - Sub passes can be any level deep. - IMPORTANT: All state propagate from sub pass to subpass. There is no state stack concept anymore. Ensure the correct render state is set before drawing anything using `Pass::state_set()`. - The drawcalls now needs a `ResourceHandle` instead of an `Object *`. This is to remove any implicit dependency between `Pass` and `Manager`. This was a huge problem in old implementation since the manager did not know what to pull from the object. Now it is explicitly requested by the engine. - The pases need to be submitted to a `draw::Manager` instance which can be retrieved using `DRW_manager_get()` (for now). Internally: - All object data are stored in contiguous storage buffers. Removing a lot of complexity in the pass submission. - Draw calls are sorted and visibility tested on GPU. Making more modern culling and better instancing usage possible in the future. - Unit Tests have been added for regression testing and avoid most API breakage. - `draw::View` now contains culling data for all objects in the scene allowing caching for multiple views. - Bounding box and sphere final setup is moved to GPU. - Some global resources locations have been hardcoded to reduce complexity. What is missing: - ~~Workaround for lack of gl_BaseInstanceARB.~~ Done - ~~Object Uniform Attributes.~~ Done (Not in this patch) - Workaround for hardware supporting a maximum of 8 SSBO. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D15817
2022-09-02 18:30:48 +02:00
buf.command.vertex_len += 2;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Display
* \{ */
void DebugDraw::display_lines(View &view)
{
const bool cpu_draw_buf_used = vertex_len_.load() != 0;
if (!cpu_draw_buf_used && !gpu_draw_buf_used) {
return;
}
command::StateSet::set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
gpu::Batch *batch = GPU_batch_procedural_lines_get();
GPUShader *shader = DRW_shader_debug_draw_display_get();
GPU_batch_set_shader(batch, shader);
GPU_shader_uniform_mat4(shader, "persmat", view.persmat().ptr());
GPU_shader_uniform_2f(shader, "size_viewport", viewport_size[2], viewport_size[3]);
if (gpu_draw_buf_used) {
gpu::DebugScope debug_scope("GPU");
/* Reset buffer. */
gpu_draw_buf_.next()->command.vertex_len = 0;
gpu_draw_buf_.next()->push_update();
GPU_storagebuf_bind(*gpu_draw_buf_.current(), DRW_DEBUG_DRAW_SLOT);
GPU_storagebuf_bind(*gpu_draw_buf_.next(), DRW_DEBUG_DRAW_FEEDBACK_SLOT);
GPU_batch_draw_indirect(batch, *gpu_draw_buf_.current(), 0);
GPU_storagebuf_unbind(*gpu_draw_buf_.current());
GPU_storagebuf_unbind(*gpu_draw_buf_.next());
}
{
gpu::DebugScope debug_scope("CPU");
/* We might have race condition here (a writer thread might still be outputting vertices).
* But that is ok. At worse, we will be missing some vertex data and show 1 corrupted line. */
cpu_draw_buf_.current()->command.vertex_len = vertex_len_.load();
cpu_draw_buf_.current()->push_update();
/* Reset buffer. */
cpu_draw_buf_.next()->command.vertex_len = 0;
cpu_draw_buf_.next()->push_update();
GPU_storagebuf_bind(*cpu_draw_buf_.current(), DRW_DEBUG_DRAW_SLOT);
GPU_storagebuf_bind(*cpu_draw_buf_.next(), DRW_DEBUG_DRAW_FEEDBACK_SLOT);
GPU_batch_draw_indirect(batch, *cpu_draw_buf_.current(), 0);
GPU_storagebuf_unbind(*cpu_draw_buf_.current());
GPU_storagebuf_unbind(*cpu_draw_buf_.next());
/* Read result of lifetime management. */
cpu_draw_buf_.next()->read();
vertex_len_.store(min_ii(DRW_DEBUG_DRAW_VERT_MAX, cpu_draw_buf_.next()->command.vertex_len));
}
gpu_draw_buf_.swap();
cpu_draw_buf_.swap();
}
void DebugDraw::display_to_view(View &view)
{
/* Display only on the main thread. Avoid concurrent usage of the resource. */
BLI_assert(BLI_thread_is_main());
GPU_debug_group_begin("DebugDraw");
display_lines(view);
GPU_debug_group_end();
}
/** \} */
} // namespace blender::draw