Files
test2/source/blender/draw/engines/select/select_instance.hh
Hans Goudey 744f3b2823 Cleanup: Grammar in comments: Fix uses of "own"
"Own" (the adjective) cannot be used on its own. It should be combined
with something like "its own", "our own",  "her own", or "the object's own".
It also isn't used separately to mean something like "separate".

Also, "its own" is correct instead of "it's own" which is a misues of the verb.
2024-03-07 16:23:35 -05:00

256 lines
7.2 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw_engine
*/
#pragma once
#include "BKE_object_types.hh"
#include "DRW_gpu_wrapper.hh"
#include "GPU_select.hh"
#include "../intern/gpu_select_private.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
#include "gpu_shader_create_info.hh"
#include "select_defines.hh"
#include "select_shader_shared.hh"
namespace blender::draw::select {
enum class SelectionType { DISABLED = 0, ENABLED = 1 };
class ID {
private:
uint32_t value;
/* Add type safety to selection ID. Only the select types should provide them. */
ID(uint32_t value) : value(value){};
friend struct SelectBuf;
friend struct SelectMap;
public:
uint32_t get() const
{
return value;
}
};
/**
* Add a dedicated selection id buffer to a pass.
* To be used when not using a #PassMain which can pass the select ID via CustomID.
*/
struct SelectBuf {
const SelectionType selection_type;
StorageVectorBuffer<uint32_t> select_buf = {"select_buf"};
SelectBuf(const SelectionType selection_type) : selection_type(selection_type){};
void select_clear()
{
if (selection_type != SelectionType::DISABLED) {
select_buf.clear();
}
}
void select_append(ID select_id)
{
if (selection_type != SelectionType::DISABLED) {
select_buf.append(select_id.get());
}
}
void select_bind(PassSimple &pass)
{
if (selection_type != SelectionType::DISABLED) {
select_buf.push_update();
pass.bind_ssbo(SELECT_ID_IN, &select_buf);
}
}
};
/**
* Generate selection IDs from objects and keep record of the mapping between them.
* The id's are contiguous so that we can create a destination buffer.
*/
struct SelectMap {
const SelectionType selection_type;
/** Mapping between internal IDs and `object->runtime->select_id`. */
Vector<uint> select_id_map;
#ifndef NDEBUG
/** Debug map containing a copy of the object name. */
Vector<std::string> map_names;
#endif
/** Stores the result of the whole selection drawing. Content depends on selection mode. */
StorageArrayBuffer<uint> select_output_buf = {"select_output_buf"};
/** Dummy buffer. Might be better to remove, but simplify the shader create info patching. */
StorageArrayBuffer<uint, 4, true> dummy_select_buf = {"dummy_select_buf"};
/** Uniform buffer to bind to all passes to pass information about the selection state. */
UniformBuffer<SelectInfoData> info_buf;
/** Will remove the depth test state from any pass drawing objects with select id. */
bool disable_depth_test;
SelectMap(const SelectionType selection_type) : selection_type(selection_type){};
/* TODO(fclem): The sub_object_id id should eventually become some enum or take a sub-object
* reference directly. This would isolate the selection logic to this class. */
[[nodiscard]] const ID select_id(const ObjectRef &ob_ref, uint sub_object_id = 0)
{
if (selection_type == SelectionType::DISABLED) {
return {0};
}
uint object_id = ob_ref.object->runtime->select_id;
uint id = select_id_map.append_and_get_index(object_id | sub_object_id);
#ifndef NDEBUG
map_names.append(ob_ref.object->id.name);
#endif
return {id};
}
/* Load an invalid index that will not write to the output (not selectable). */
[[nodiscard]] const ID select_invalid_id()
{
return {uint32_t(-1)};
}
void begin_sync()
{
if (selection_type == SelectionType::DISABLED) {
return;
}
switch (gpu_select_next_get_mode()) {
case GPU_SELECT_ALL:
info_buf.mode = SelectType::SELECT_ALL;
disable_depth_test = true;
break;
/* Not sure if these 2 NEAREST are mapped to the right algorithm. */
case GPU_SELECT_NEAREST_FIRST_PASS:
case GPU_SELECT_NEAREST_SECOND_PASS:
case GPU_SELECT_PICK_ALL:
info_buf.mode = SelectType::SELECT_PICK_ALL;
info_buf.cursor = int2(gpu_select_next_get_pick_area_center());
disable_depth_test = true;
break;
case GPU_SELECT_PICK_NEAREST:
info_buf.mode = SelectType::SELECT_PICK_NEAREST;
info_buf.cursor = int2(gpu_select_next_get_pick_area_center());
disable_depth_test = true;
break;
}
info_buf.push_update();
select_id_map.clear();
#ifndef NDEBUG
map_names.clear();
#endif
}
/** IMPORTANT: Changes the draw state. Need to be called after the pass's own state_set. */
void select_bind(PassSimple &pass)
{
if (selection_type == SelectionType::DISABLED) {
return;
}
if (disable_depth_test) {
/* TODO: clipping state. */
pass.state_set(DRW_STATE_WRITE_COLOR);
}
pass.bind_ubo(SELECT_DATA, &info_buf);
pass.bind_ssbo(SELECT_ID_OUT, &select_output_buf);
}
/** IMPORTANT: Changes the draw state. Need to be called after the pass's own state_set. */
void select_bind(PassMain &pass)
{
if (selection_type == SelectionType::DISABLED) {
return;
}
pass.use_custom_ids = true;
if (disable_depth_test) {
/* TODO: clipping state. */
pass.state_set(DRW_STATE_WRITE_COLOR);
}
pass.bind_ubo(SELECT_DATA, &info_buf);
/* IMPORTANT: This binds a dummy buffer `in_select_buf` but it is not supposed to be used. */
pass.bind_ssbo(SELECT_ID_IN, &dummy_select_buf);
pass.bind_ssbo(SELECT_ID_OUT, &select_output_buf);
}
void end_sync()
{
if (selection_type == SelectionType::DISABLED) {
return;
}
select_output_buf.resize(ceil_to_multiple_u(select_id_map.size(), 4));
select_output_buf.push_update();
if (info_buf.mode == SelectType::SELECT_ALL) {
/* This mode uses atomicOr and store result as a bitmap. Clear to 0 (no selection). */
GPU_storagebuf_clear(select_output_buf, 0);
}
else {
/* Other modes use atomicMin. Clear to UINT_MAX. */
GPU_storagebuf_clear(select_output_buf, 0xFFFFFFFFu);
}
}
void read_result()
{
if (selection_type == SelectionType::DISABLED) {
return;
}
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
select_output_buf.read();
Vector<GPUSelectResult> hit_results;
/* Convert raw data from GPU to #GPUSelectResult. */
switch (info_buf.mode) {
case SelectType::SELECT_ALL:
for (auto i : IndexRange(select_id_map.size())) {
if (((select_output_buf[i / 32] >> (i % 32)) & 1) != 0) {
GPUSelectResult hit_result{};
hit_result.id = select_id_map[i];
hit_result.depth = 0xFFFF;
hit_results.append(hit_result);
}
}
break;
case SelectType::SELECT_PICK_ALL:
case SelectType::SELECT_PICK_NEAREST:
for (auto i : IndexRange(select_id_map.size())) {
if (select_output_buf[i] != 0xFFFFFFFFu) {
/* NOTE: For `SELECT_PICK_NEAREST`, `select_output_buf` also contains the screen
* distance to cursor in the lowest bits. */
GPUSelectResult hit_result{};
hit_result.id = select_id_map[i];
hit_result.depth = select_output_buf[i];
hit_results.append(hit_result);
}
}
break;
}
gpu_select_next_set_result(hit_results.data(), hit_results.size());
}
};
} // namespace blender::draw::select