Blender V5.0
select_instance.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#pragma once
10
11#include "BKE_object_types.hh"
12
13#include "DRW_gpu_wrapper.hh"
14
15#include "GPU_select.hh"
16
17#include "../intern/gpu_select_private.hh"
18
19#include "draw_manager.hh"
20#include "draw_pass.hh"
21
23
24#include "select_defines.hh"
26
27namespace blender::draw::select {
28
29// #define DEBUG_PRINT
30
31enum class SelectionType { DISABLED = 0, ENABLED = 1 };
32
33class ID {
34 private:
35 uint32_t value;
36
37 /* Add type safety to selection ID. Only the select types should provide them. */
38 ID(uint32_t value) : value(value) {};
39
40 friend struct SelectBuf;
41 friend struct SelectMap;
42
43 public:
44 uint32_t get() const
45 {
46 return value;
47 }
48};
49
54struct SelectBuf {
56
58
60
62 {
64 select_buf.clear();
65 }
66 }
67
68 void select_append(ID select_id)
69 {
71 select_buf.append(select_id.get());
72 }
73 }
74
76 {
78 select_buf.push_update();
80 }
81 }
82};
83
88struct SelectMap {
90
95#ifndef NDEBUG
98#endif
107
109
110 /* TODO(fclem): The sub_object_id id should eventually become some enum or take a sub-object
111 * reference directly. This would isolate the selection logic to this class. */
112 [[nodiscard]] const ID select_id(const ObjectRef &ob_ref, uint sub_object_id = 0)
113 {
115 return {0};
116 }
117
118 if (sub_object_id == uint(-1)) {
119 /* WORKAROUND: Armature code set the sub_object_id to -1 when individual bones are not
120 * selectable (i.e. in object mode). */
121 sub_object_id = 0;
122 }
123
124 uint object_id = ob_ref.object->runtime->select_id;
125 uint id = select_id_map.append_and_get_index(object_id | sub_object_id);
126 in_front_map.append(ob_ref.object->dtx & OB_DRAW_IN_FRONT);
127
128#ifdef DEBUG_PRINT
129 /* Print mapping from object name, select id and the mapping to internal select id.
130 * If something is wrong at this stage, it indicates an error in the caller code. */
131 printf("%s : %u | %u = %u -> %u\n",
132 ob_ref.object->id.name,
133 object_id,
134 sub_object_id,
135 object_id | sub_object_id,
136 id);
137#endif
138
139#ifndef NDEBUG
140 map_names.append(ob_ref.object->id.name);
141#endif
142 return {id};
143 }
144
145 /* TODO: refactor this method to select::ID::invalid(). */
146 /* Load an invalid index that will not write to the output (not selectable). */
147 [[nodiscard]] static const ID select_invalid_id()
148 {
149 return {uint32_t(-1)};
150 }
151
153 {
155 return;
156 }
157
158 this->clipping_plane_count = clipping_plane_count;
159
160 select_id_map.clear();
161 in_front_map.clear();
162#ifndef NDEBUG
163 map_names.clear();
164#endif
165 }
166
178
181 {
183 return;
184 }
185
186 pass.use_custom_ids = true;
189 /* IMPORTANT: This binds a dummy buffer `in_select_buf` but it is not supposed to be used. */
192 }
193
194 /* TODO: Deduplicate. */
197 {
199 return;
200 }
201
202 pass.use_custom_ids = true;
205 /* IMPORTANT: This binds a dummy buffer `in_select_buf` but it is not supposed to be used. */
208 }
209
210 void end_sync()
211 {
213 return;
214 }
215
216 BLI_assert(select_id_map.size() == in_front_map.size());
217
219 select_output_buf.push_update();
220 }
221
222 void pre_draw()
223 {
225 return;
226 }
227
228 switch (gpu_select_next_get_mode()) {
229 /* Should not be used anymore for viewport selection. */
234 break;
235 case GPU_SELECT_ALL:
237 info_buf.cursor = int2(0);
238 /* This mode uses atomicOr and store result as a bitmap. Clear to 0 (no selection). */
240 break;
244 /* Mode uses atomicMin. Clear to UINT_MAX. */
246 break;
250 /* Mode uses atomicMin. Clear to UINT_MAX. */
252 break;
253 }
254 info_buf.push_update();
255 }
256
258 {
260 return;
261 }
262
264 /* This flush call should not be required. Still, on non-unified memory architecture
265 * Apple devices this is needed for the result to be host visible.
266 * This is likely to be a bug in the GPU backend.
267 * So it should eventually be transformed into a backend
268 * workaround instead of being fixed in user code. */
269 select_output_buf.async_flush_to_host();
270 select_output_buf.read();
271
272 Vector<GPUSelectResult> hit_results;
273
274 /* Convert raw data from GPU to #GPUSelectResult. */
275 switch (info_buf.mode) {
277 for (auto i : IndexRange(select_id_map.size())) {
278 if (((select_output_buf[i / 32] >> (i % 32)) & 1) != 0) {
279 GPUSelectResult hit_result{};
280 hit_result.id = select_id_map[i];
281 hit_result.depth = 0xFFFF;
282 hit_results.append(hit_result);
283 }
284 }
285 break;
286
288 for (auto i : IndexRange(select_id_map.size())) {
289 if (select_output_buf[i] != 0xFFFFFFFFu) {
290 GPUSelectResult hit_result{};
291 hit_result.id = select_id_map[i];
292 hit_result.depth = select_output_buf[i];
293 if (in_front_map[i]) {
294 /* Divide "In Front" objects depth so they go first. */
295 /* TODO(Miguel Pozo): This reproduces the previous engine behavior, but it breaks
296 * with code using depth for position reconstruction. Should we improve this? */
297 float offset_depth = *reinterpret_cast<float *>(&hit_result.depth) / 100.0f;
298 hit_result.depth = *reinterpret_cast<uint32_t *>(&offset_depth);
299 }
300 hit_results.append(hit_result);
301 }
302 }
303 break;
304
306 for (auto i : IndexRange(select_id_map.size())) {
307 if (select_output_buf[i] != 0xFFFFFFFFu) {
308 /* NOTE: For `SELECT_PICK_NEAREST`, `select_output_buf` also contains the screen
309 * distance to cursor in the lowest bits. */
310 GPUSelectResult hit_result{};
311 hit_result.id = select_id_map[i];
312 hit_result.depth = select_output_buf[i];
313 if (in_front_map[i]) {
314 /* Divide "In Front" objects depth so they go first. */
315 const uint32_t depth_mask = 0x00FFFFFFu << 8u;
316 uint32_t offset_depth = ((hit_result.depth & depth_mask) >> 8u) / 100;
317 hit_result.depth &= ~depth_mask;
318 hit_result.depth |= offset_depth << 8u;
319 }
320 if (hit_results.is_empty() || hit_result.depth < hit_results[0].depth) {
321 hit_results = {hit_result};
322 }
323 }
324 }
325 break;
326 }
327#ifdef DEBUG_PRINT
328 for (auto &hit : hit_results) {
329 /* Print hit results right out of the GPU selection buffer.
330 * If something is wrong at this stage, it indicates an error in the selection shaders. */
331 printf(" hit: %u: depth %u\n", hit.id, hit.depth);
332 }
333#endif
334
335 gpu_select_next_set_result(hit_results.data(), hit_results.size());
336 }
337};
338
339} // namespace blender::draw::select
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE uint ceil_to_multiple_u(uint a, uint b)
MINLINE uint max_uu(uint a, uint b)
unsigned int uint
struct ID ID
@ OB_DRAW_IN_FRONT
@ GPU_SELECT_NEAREST_SECOND_PASS
Definition GPU_select.hh:23
@ GPU_SELECT_INVALID
Definition GPU_select.hh:19
@ GPU_SELECT_NEAREST_FIRST_PASS
Definition GPU_select.hh:22
@ GPU_SELECT_PICK_ALL
Definition GPU_select.hh:25
@ GPU_SELECT_ALL
Definition GPU_select.hh:20
@ GPU_SELECT_PICK_NEAREST
Definition GPU_select.hh:26
@ GPU_BARRIER_BUFFER_UPDATE
Definition GPU_state.hh:56
void GPU_memory_barrier(GPUBarrier barrier)
Definition gpu_state.cc:326
void GPU_storagebuf_clear(blender::gpu::StorageBuf *ssbo, uint32_t clear_value)
int64_t size() const
void append(const T &value)
bool is_empty() const
void state_set(DRWState state, int clip_plane_count=0)
void bind_ubo(const char *name, gpu::UniformBuf *buffer)
void bind_ssbo(const char *name, gpu::StorageBuf *buffer)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:499
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
void gpu_select_next_set_result(GPUSelectResult *hit_buf, uint hit_len)
int gpu_select_next_get_pick_area_center()
GPUSelectMode gpu_select_next_get_mode()
#define printf(...)
detail::Pass< command::DrawCommandBuf > PassSimple
detail::Pass< command::DrawMultiBuf > PassMain
VecBase< int32_t, 2 > int2
#define SELECT_DATA
#define SELECT_ID_IN
#define SELECT_ID_OUT
unsigned int id
Definition GPU_select.hh:34
unsigned int depth
Definition GPU_select.hh:42
char name[258]
Definition DNA_ID.h:432
ObjectRuntimeHandle * runtime
StorageVectorBuffer< uint32_t > select_buf
void select_bind(PassSimple::Sub &pass)
SelectBuf(const SelectionType selection_type)
void select_bind(PassMain &pass, PassMain::Sub &sub)
UniformBuffer< SelectInfoData > info_buf
void select_bind(PassSimple &pass)
void begin_sync(int clipping_plane_count)
StorageArrayBuffer< uint > select_output_buf
StorageArrayBuffer< uint, 4, true > dummy_select_buf
const ID select_id(const ObjectRef &ob_ref, uint sub_object_id=0)
SelectMap(const SelectionType selection_type)
i
Definition text_draw.cc:230