Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
268 lines
7.4 KiB
C++
268 lines
7.4 KiB
C++
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup draw
|
|
*/
|
|
|
|
#include "BKE_global.h"
|
|
#include "GPU_compute.h"
|
|
|
|
#include "draw_debug.hh"
|
|
#include "draw_defines.h"
|
|
#include "draw_manager.h"
|
|
#include "draw_manager.hh"
|
|
#include "draw_pass.hh"
|
|
#include "draw_shader.h"
|
|
|
|
namespace blender::draw {
|
|
|
|
Manager::~Manager()
|
|
{
|
|
for (GPUTexture *texture : acquired_textures) {
|
|
/* Decrease refcount and free if 0. */
|
|
GPU_texture_free(texture);
|
|
}
|
|
}
|
|
|
|
void Manager::begin_sync()
|
|
{
|
|
matrix_buf.swap();
|
|
bounds_buf.swap();
|
|
infos_buf.swap();
|
|
|
|
/* TODO: This means the reference is kept until further redraw or manager tear-down. Instead,
|
|
* they should be released after each draw loop. But for now, mimics old DRW behavior. */
|
|
for (GPUTexture *texture : acquired_textures) {
|
|
/* Decrease refcount and free if 0. */
|
|
GPU_texture_free(texture);
|
|
}
|
|
|
|
acquired_textures.clear();
|
|
layer_attributes.clear();
|
|
|
|
#ifdef DEBUG
|
|
/* Detect uninitialized data. */
|
|
memset(matrix_buf.current().data(),
|
|
0xF0,
|
|
matrix_buf.current().size() * sizeof(*matrix_buf.current().data()));
|
|
memset(bounds_buf.current().data(),
|
|
0xF0,
|
|
matrix_buf.current().size() * sizeof(*bounds_buf.current().data()));
|
|
memset(infos_buf.current().data(),
|
|
0xF0,
|
|
matrix_buf.current().size() * sizeof(*infos_buf.current().data()));
|
|
#endif
|
|
resource_len_ = 0;
|
|
attribute_len_ = 0;
|
|
/* TODO(fclem): Resize buffers if too big, but with an hysteresis threshold. */
|
|
|
|
object_active = DST.draw_ctx.obact;
|
|
|
|
/* Init the 0 resource. */
|
|
resource_handle(float4x4::identity());
|
|
}
|
|
|
|
void Manager::sync_layer_attributes()
|
|
{
|
|
/* Sort the attribute IDs - the shaders use binary search. */
|
|
Vector<uint32_t> id_list;
|
|
|
|
id_list.reserve(layer_attributes.size());
|
|
|
|
for (uint32_t id : layer_attributes.keys()) {
|
|
id_list.append(id);
|
|
}
|
|
|
|
std::sort(id_list.begin(), id_list.end());
|
|
|
|
/* Look up the attributes. */
|
|
int count = 0, size = layer_attributes_buf.end() - layer_attributes_buf.begin();
|
|
|
|
for (uint32_t id : id_list) {
|
|
if (layer_attributes_buf[count].sync(
|
|
DST.draw_ctx.scene, DST.draw_ctx.view_layer, layer_attributes.lookup(id)))
|
|
{
|
|
/* Check if the buffer is full. */
|
|
if (++count == size) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
layer_attributes_buf[0].buffer_length = count;
|
|
}
|
|
|
|
void Manager::end_sync()
|
|
{
|
|
GPU_debug_group_begin("Manager.end_sync");
|
|
|
|
sync_layer_attributes();
|
|
|
|
matrix_buf.current().push_update();
|
|
bounds_buf.current().push_update();
|
|
infos_buf.current().push_update();
|
|
attributes_buf.push_update();
|
|
layer_attributes_buf.push_update();
|
|
attributes_buf_legacy.push_update();
|
|
|
|
/* Useful for debugging the following resource finalize. But will trigger the drawing of the GPU
|
|
* debug draw/print buffers for every frame. Not nice for performance. */
|
|
// debug_bind();
|
|
|
|
/* Dispatch compute to finalize the resources on GPU. Save a bit of CPU time. */
|
|
uint thread_groups = divide_ceil_u(resource_len_, DRW_FINALIZE_GROUP_SIZE);
|
|
GPUShader *shader = DRW_shader_draw_resource_finalize_get();
|
|
GPU_shader_bind(shader);
|
|
GPU_shader_uniform_1i(shader, "resource_len", resource_len_);
|
|
GPU_storagebuf_bind(matrix_buf.current(), GPU_shader_get_ssbo_binding(shader, "matrix_buf"));
|
|
GPU_storagebuf_bind(bounds_buf.current(), GPU_shader_get_ssbo_binding(shader, "bounds_buf"));
|
|
GPU_storagebuf_bind(infos_buf.current(), GPU_shader_get_ssbo_binding(shader, "infos_buf"));
|
|
GPU_compute_dispatch(shader, thread_groups, 1, 1);
|
|
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
|
|
|
|
GPU_debug_group_end();
|
|
}
|
|
|
|
void Manager::debug_bind()
|
|
{
|
|
#ifdef DEBUG
|
|
if (DST.debug == nullptr) {
|
|
return;
|
|
}
|
|
GPU_storagebuf_bind(drw_debug_gpu_draw_buf_get(), DRW_DEBUG_DRAW_SLOT);
|
|
GPU_storagebuf_bind(drw_debug_gpu_print_buf_get(), DRW_DEBUG_PRINT_SLOT);
|
|
# ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER
|
|
/* Add a barrier to allow multiple shader writing to the same buffer. */
|
|
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
void Manager::resource_bind()
|
|
{
|
|
GPU_storagebuf_bind(matrix_buf.current(), DRW_OBJ_MAT_SLOT);
|
|
GPU_storagebuf_bind(infos_buf.current(), DRW_OBJ_INFOS_SLOT);
|
|
GPU_storagebuf_bind(attributes_buf, DRW_OBJ_ATTR_SLOT);
|
|
GPU_uniformbuf_bind(layer_attributes_buf, DRW_LAYER_ATTR_UBO_SLOT);
|
|
/* 2 is the hardcoded location of the uniform attr UBO. */
|
|
/* TODO(@fclem): Remove this workaround. */
|
|
GPU_uniformbuf_bind(attributes_buf_legacy, 2);
|
|
}
|
|
|
|
void Manager::submit(PassSimple &pass, View &view)
|
|
{
|
|
view.bind();
|
|
|
|
debug_bind();
|
|
|
|
command::RecordingState state;
|
|
state.inverted_view = view.is_inverted();
|
|
|
|
pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_, pass.sub_passes_);
|
|
|
|
resource_bind();
|
|
|
|
pass.submit(state);
|
|
|
|
state.cleanup();
|
|
}
|
|
|
|
void Manager::submit(PassMain &pass, View &view)
|
|
{
|
|
view.bind();
|
|
|
|
debug_bind();
|
|
|
|
bool freeze_culling = (U.experimental.use_viewport_debug && DST.draw_ctx.v3d &&
|
|
(DST.draw_ctx.v3d->debug_flag & V3D_DEBUG_FREEZE_CULLING) != 0);
|
|
|
|
view.compute_visibility(bounds_buf.current(), resource_len_, freeze_culling);
|
|
|
|
command::RecordingState state;
|
|
state.inverted_view = view.is_inverted();
|
|
|
|
pass.draw_commands_buf_.bind(state,
|
|
pass.headers_,
|
|
pass.commands_,
|
|
view.get_visibility_buffer(),
|
|
view.visibility_word_per_draw(),
|
|
view.view_len_,
|
|
pass.use_custom_ids);
|
|
|
|
resource_bind();
|
|
|
|
pass.submit(state);
|
|
|
|
state.cleanup();
|
|
}
|
|
|
|
void Manager::submit(PassSortable &pass, View &view)
|
|
{
|
|
pass.sort();
|
|
|
|
this->submit(static_cast<PassMain &>(pass), view);
|
|
}
|
|
|
|
void Manager::submit(PassSimple &pass)
|
|
{
|
|
debug_bind();
|
|
|
|
command::RecordingState state;
|
|
|
|
pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_, pass.sub_passes_);
|
|
|
|
resource_bind();
|
|
|
|
pass.submit(state);
|
|
|
|
state.cleanup();
|
|
}
|
|
|
|
Manager::SubmitDebugOutput Manager::submit_debug(PassSimple &pass, View &view)
|
|
{
|
|
submit(pass, view);
|
|
|
|
pass.draw_commands_buf_.resource_id_buf_.read();
|
|
|
|
Manager::SubmitDebugOutput output;
|
|
output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(),
|
|
pass.draw_commands_buf_.resource_id_count_};
|
|
/* There is no visibility data for PassSimple. */
|
|
output.visibility = {(uint *)view.get_visibility_buffer().data(), 0};
|
|
return output;
|
|
}
|
|
|
|
Manager::SubmitDebugOutput Manager::submit_debug(PassMain &pass, View &view)
|
|
{
|
|
submit(pass, view);
|
|
|
|
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
|
|
|
|
pass.draw_commands_buf_.resource_id_buf_.read();
|
|
view.get_visibility_buffer().read();
|
|
|
|
Manager::SubmitDebugOutput output;
|
|
output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(),
|
|
pass.draw_commands_buf_.resource_id_count_};
|
|
output.visibility = {(uint *)view.get_visibility_buffer().data(),
|
|
divide_ceil_u(resource_len_, 32)};
|
|
return output;
|
|
}
|
|
|
|
Manager::DataDebugOutput Manager::data_debug()
|
|
{
|
|
matrix_buf.current().read();
|
|
bounds_buf.current().read();
|
|
infos_buf.current().read();
|
|
|
|
Manager::DataDebugOutput output;
|
|
output.matrices = {matrix_buf.current().data(), resource_len_};
|
|
output.bounds = {bounds_buf.current().data(), resource_len_};
|
|
output.infos = {infos_buf.current().data(), resource_len_};
|
|
return output;
|
|
}
|
|
|
|
} // namespace blender::draw
|