Blender V5.0
gpu_select_pick.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2017 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include <cfloat>
12#include <cstdlib>
13#include <cstring>
14
15#include "GPU_debug.hh"
16#include "GPU_framebuffer.hh"
17#include "GPU_select.hh"
18#include "GPU_state.hh"
19
20#include "MEM_guardedalloc.h"
21
22#include "BLI_listbase.h"
23#include "BLI_rect.h"
24#include "BLI_utildefines.h"
25
26#include "gpu_select_private.hh"
27
28#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
29
30// #define DEBUG_PRINT
31
32/* Alloc number for depths */
33#define ALLOC_DEPTHS 200
34
35/* Z-depth of cleared depth buffer */
36#define DEPTH_MAX 0xffffffff
37
38/* -------------------------------------------------------------------- */
41
53
55using depth_t = uint;
56
62static void rect_subregion_stride_calc(const rcti *src, const rcti *dst, SubRectStride *r_sub)
63{
64 const int src_x = BLI_rcti_size_x(src);
65 // const int src_y = BLI_rcti_size_y(src);
66 const int dst_x = BLI_rcti_size_x(dst);
67 const int dst_y = BLI_rcti_size_y(dst);
68 const int x = dst->xmin - src->xmin;
69 const int y = dst->ymin - src->ymin;
70
71 BLI_assert(src->xmin <= dst->xmin && src->ymin <= dst->ymin && src->xmax >= dst->xmax &&
72 src->ymax >= dst->ymax);
73 BLI_assert(x >= 0 && y >= 0);
74
75 r_sub->start = uint((src_x * y) + x);
76 r_sub->span = uint(dst_x);
77 r_sub->span_len = uint(dst_y);
78 r_sub->skip = uint(src_x - dst_x);
79}
80
85BLI_INLINE bool depth_is_filled(const depth_t *prev, const depth_t *curr)
86{
87 return (*prev != *curr) && (*curr != DEPTH_MAX);
88}
89
91
92/* -------------------------------------------------------------------- */
98
105
107{
108 DepthBufCache *rect = static_cast<DepthBufCache *>(
109 MEM_mallocN(sizeof(DepthBufCache) + sizeof(depth_t) * rect_len, __func__));
110 rect->id = SELECT_ID_NONE;
111 return rect;
112}
113
114static bool depth_buf_rect_depth_any(const DepthBufCache *rect_depth, uint rect_len)
115{
116 const depth_t *curr = rect_depth->buf;
117 for (uint i = 0; i < rect_len; i++, curr++) {
118 if (*curr != DEPTH_MAX) {
119 return true;
120 }
121 }
122 return false;
123}
124
125static bool depth_buf_subrect_depth_any(const DepthBufCache *rect_depth,
126 const SubRectStride *sub_rect)
127{
128 const depth_t *curr = rect_depth->buf + sub_rect->start;
129 for (uint i = 0; i < sub_rect->span_len; i++) {
130 const depth_t *curr_end = curr + sub_rect->span;
131 for (; curr < curr_end; curr++, curr++) {
132 if (*curr != DEPTH_MAX) {
133 return true;
134 }
135 }
136 curr += sub_rect->skip;
137 }
138 return false;
139}
140
142 const DepthBufCache *rect_curr,
143 uint rect_len)
144{
145#if 0
146 return memcmp(rect_depth_a->buf, rect_depth_b->buf, rect_len * sizeof(depth_t)) != 0;
147#else
148 const depth_t *prev = rect_prev->buf;
149 const depth_t *curr = rect_curr->buf;
150 for (uint i = 0; i < rect_len; i++, curr++, prev++) {
151 if (depth_is_filled(prev, curr)) {
152 return true;
153 }
154 }
155 return false;
156#endif
157}
158
163 const DepthBufCache *rect_dst,
164 const SubRectStride *sub_rect)
165{
166 /* Same as #depth_buf_rect_depth_any_filled but different rectangle sizes. */
167 const depth_t *prev = rect_src->buf + sub_rect->start;
168 const depth_t *curr = rect_dst->buf + sub_rect->start;
169 for (uint i = 0; i < sub_rect->span_len; i++) {
170 const depth_t *curr_end = curr + sub_rect->span;
171 for (; curr < curr_end; prev++, curr++) {
172 if (depth_is_filled(prev, curr)) {
173 return true;
174 }
175 }
176 prev += sub_rect->skip;
177 curr += sub_rect->skip;
178 }
179 return false;
180}
181
183
184/* -------------------------------------------------------------------- */
189
194
195static int depth_id_cmp(const void *v1, const void *v2)
196{
197 const DepthID *d1 = static_cast<const DepthID *>(v1), *d2 = static_cast<const DepthID *>(v2);
198 if (d1->id < d2->id) {
199 return -1;
200 }
201 if (d1->id > d2->id) {
202 return 1;
203 }
204
205 return 0;
206}
207
208static int depth_cmp(const void *v1, const void *v2)
209{
210 const DepthID *d1 = static_cast<const DepthID *>(v1), *d2 = static_cast<const DepthID *>(v2);
211 if (d1->depth < d2->depth) {
212 return -1;
213 }
214 if (d1->depth > d2->depth) {
215 return 1;
216 }
217
218 return 0;
219}
220
222
223/* -------------------------------------------------------------------- */
226
293
295
297{
299
300#ifdef DEBUG_PRINT
301 printf("%s: mode=%d, use_cache=%d, is_cache=%d\n",
302 __func__,
303 int(mode),
304 ps->use_cache,
305 ps->is_cached);
306#endif
307
308 GPU_debug_group_begin("Selection Pick");
309
310 ps->buffer = buffer;
311 ps->mode = mode;
312
313 const uint rect_len = uint(BLI_rcti_size_x(input) * BLI_rcti_size_y(input));
314 ps->dst.clip_rect = *input;
315 ps->dst.rect_len = rect_len;
316
317 /* Avoids unnecessary GPU operations when cache is available and they are unnecessary. */
318 if (ps->is_cached == false) {
322
323 /* Disable writing to the frame-buffer. */
324 GPU_color_mask(false, false, false, false);
325
326 GPU_depth_mask(true);
327 /* Always use #GPU_DEPTH_LESS_EQUAL even though #GPU_SELECT_PICK_ALL always clears the buffer.
328 * This is because individual objects themselves might have sections that overlap and we need
329 * these to have the correct distance information. */
331
332 float viewport[4];
333 GPU_viewport_size_get_f(viewport);
334
335 ps->src.clip_rect = *input;
336 ps->src.rect_len = rect_len;
337
338 ps->gpu.clip_readpixels[0] = int(viewport[0]);
339 ps->gpu.clip_readpixels[1] = int(viewport[1]);
342
344
345 /* It's possible we don't want to clear depth buffer,
346 * so existing elements are masked by current z-buffer. */
347 GPU_clear_depth(1.0f);
348
349 /* scratch buffer (read new values here) */
350 ps->gpu.rect_depth_test = depth_buf_malloc(rect_len);
351 ps->gpu.rect_depth = depth_buf_malloc(rect_len);
352
353 /* Set initial 'far' value. */
354 for (uint i = 0; i < rect_len; i++) {
355 ps->gpu.rect_depth->buf[i] = DEPTH_MAX;
356 }
357
358 ps->gpu.is_init = false;
359 ps->gpu.prev_id = 0;
360 }
361 else {
362 /* Using cache `ps->is_cached == true`. */
363 /* `src.clip_rect` -> `dst.clip_rect`. */
365 BLI_assert(ps->gpu.rect_depth == nullptr);
366 BLI_assert(ps->gpu.rect_depth_test == nullptr);
367 }
368
369 if (mode == GPU_SELECT_PICK_ALL) {
370 ps->all.hits = static_cast<DepthID *>(
371 MEM_mallocN(sizeof(*ps->all.hits) * ALLOC_DEPTHS, __func__));
372 ps->all.hits_len = 0;
374 }
375 else {
376 /* Set to 0xff for #SELECT_ID_NONE. */
378 memset(ps->nearest.rect_id, 0xff, sizeof(uint) * ps->dst.rect_len);
379 }
380}
381
386static void gpu_select_load_id_pass_all(const DepthBufCache *rect_curr)
387{
389 const uint id = rect_curr->id;
390 /* find the best depth for this pass and store in 'all.hits' */
391 depth_t depth_best = DEPTH_MAX;
392
393#define EVAL_TEST() \
394 if (depth_best > *curr) { \
395 depth_best = *curr; \
396 } \
397 ((void)0)
398
399 if (ps->is_cached == false) {
400 const depth_t *curr = rect_curr->buf;
401 BLI_assert(ps->src.rect_len == ps->dst.rect_len);
402 const uint rect_len = ps->src.rect_len;
403 for (uint i = 0; i < rect_len; i++, curr++) {
404 EVAL_TEST();
405 }
406 }
407 else {
408 /* Same as above but different rectangle sizes. */
409 const depth_t *curr = rect_curr->buf + ps->cache.sub_rect.start;
410 for (uint i = 0; i < ps->cache.sub_rect.span_len; i++) {
411 const depth_t *curr_end = curr + ps->cache.sub_rect.span;
412 for (; curr < curr_end; curr++) {
413 EVAL_TEST();
414 }
415 curr += ps->cache.sub_rect.skip;
416 }
417 }
418
419#undef EVAL_TEST
420
421 /* Ensure enough space. */
422 if (UNLIKELY(ps->all.hits_len == ps->all.hits_len_alloc)) {
424 ps->all.hits = static_cast<DepthID *>(
425 MEM_reallocN(ps->all.hits, ps->all.hits_len_alloc * sizeof(*ps->all.hits)));
426 }
427 DepthID *d = &ps->all.hits[ps->all.hits_len++];
428 d->id = id;
429 d->depth = depth_best;
430}
431
433 const DepthBufCache *rect_curr)
434{
436 const uint id = rect_curr->id;
437 /* Keep track each pixels ID in `nearest.rect_id`. */
438 if (id != SELECT_ID_NONE) {
439 uint *id_ptr = ps->nearest.rect_id;
440
441/* Check against DEPTH_MAX because XRAY will clear the buffer,
442 * so previously set values will become unset.
443 * In this case just leave those id's left as-is. */
444#define EVAL_TEST() \
445 if (depth_is_filled(prev, curr)) { \
446 *id_ptr = id; \
447 } \
448 ((void)0)
449
450 if (ps->is_cached == false) {
451 const depth_t *prev = rect_prev->buf;
452 const depth_t *curr = rect_curr->buf;
453 BLI_assert(ps->src.rect_len == ps->dst.rect_len);
454 const uint rect_len = ps->src.rect_len;
455 for (uint i = 0; i < rect_len; i++, curr++, prev++, id_ptr++) {
456 EVAL_TEST();
457 }
458 }
459 else {
460 /* same as above but different rect sizes */
461 const depth_t *prev = rect_prev->buf + ps->cache.sub_rect.start;
462 const depth_t *curr = rect_curr->buf + ps->cache.sub_rect.start;
463 for (uint i = 0; i < ps->cache.sub_rect.span_len; i++) {
464 const depth_t *curr_end = curr + ps->cache.sub_rect.span;
465 for (; curr < curr_end; prev++, curr++, id_ptr++) {
466 EVAL_TEST();
467 }
468 prev += ps->cache.sub_rect.skip;
469 curr += ps->cache.sub_rect.skip;
470 }
471 }
472
473#undef EVAL_TEST
474 }
475}
476
478{
480
481 if (ps->gpu.is_init) {
482 if (id == ps->gpu.prev_id && !end) {
483 /* No need to read if we are still drawing for the same id since
484 * all these depths will be merged / de-duplicated in the end. */
485 return true;
486 }
487
488 const uint rect_len = ps->src.rect_len;
492 /* Perform initial check since most cases the array remains unchanged. */
493
494 bool do_pass = false;
495 if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
496 if (depth_buf_rect_depth_any(ps->gpu.rect_depth_test, rect_len)) {
497 ps->gpu.rect_depth_test->id = ps->gpu.prev_id;
499 do_pass = true;
500 }
501 }
502 else {
504 ps->gpu.rect_depth_test->id = ps->gpu.prev_id;
506 do_pass = true;
507 }
508 }
509
510 if (do_pass) {
511 /* Store depth in cache */
512 if (ps->use_cache) {
515 }
516
517 std::swap(ps->gpu.rect_depth, ps->gpu.rect_depth_test);
518
519 if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
520 /* (fclem) This is to be on the safe side. I don't know if this is required. */
521 bool prev_depth_mask = GPU_depth_mask_get();
522 /* we want new depths every time */
523 GPU_depth_mask(true);
524 GPU_clear_depth(1.0f);
525
526 GPU_depth_mask(prev_depth_mask);
527 }
528 }
529 }
530
531 ps->gpu.is_init = true;
532 ps->gpu.prev_id = id;
533
534 return true;
535}
536
538{
540
541#ifdef DEBUG_PRINT
542 printf("%s\n", __func__);
543#endif
544
545 if (ps->is_cached == false) {
546 if (ps->gpu.is_init) {
547 /* force finishing last pass */
549 }
553 }
554
556
557 /* Assign but never free directly since it may be in cache. */
558 DepthBufCache *rect_depth_final;
559
560 /* Store depth in cache */
561 if (ps->use_cache && !ps->is_cached) {
563 ps->gpu.rect_depth = nullptr;
564 rect_depth_final = static_cast<DepthBufCache *>(ps->cache.bufs.last);
565 }
566 else if (ps->is_cached) {
567 rect_depth_final = static_cast<DepthBufCache *>(ps->cache.bufs.last);
568 }
569 else {
570 /* Common case, no cache. */
571 rect_depth_final = ps->gpu.rect_depth;
572 }
573
574 DepthID *depth_data;
575 uint depth_data_len = 0;
576
577 if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
578 depth_data = ps->all.hits;
579 depth_data_len = ps->all.hits_len;
580 /* Move ownership. */
581 ps->all.hits = nullptr;
582 ps->all.hits_len = 0;
583 ps->all.hits_len_alloc = 0;
584 }
585 else {
586 /* #GPU_SELECT_PICK_NEAREST */
587
588 /* Over allocate (unlikely we have as many depths as pixels). */
589 uint depth_data_len_first_pass = 0;
590 depth_data = static_cast<DepthID *>(
591 MEM_mallocN(ps->dst.rect_len * sizeof(*depth_data), __func__));
592
593 /* Partially de-duplicating copy,
594 * when contiguous ID's are found - update their closest depth.
595 * This isn't essential but means there is less data to sort. */
596
597#define EVAL_TEST(i_src, i_dst) \
598 { \
599 const uint id = ps->nearest.rect_id[i_dst]; \
600 if (id != SELECT_ID_NONE) { \
601 const depth_t depth = rect_depth_final->buf[i_src]; \
602 if (depth_last == nullptr || depth_last->id != id) { \
603 DepthID *d = &depth_data[depth_data_len_first_pass++]; \
604 d->id = id; \
605 d->depth = depth; \
606 } \
607 else if (depth_last->depth > depth) { \
608 depth_last->depth = depth; \
609 } \
610 } \
611 } \
612 ((void)0)
613
614 {
615 DepthID *depth_last = nullptr;
616 if (ps->is_cached == false) {
617 for (uint i = 0; i < ps->src.rect_len; i++) {
618 EVAL_TEST(i, i);
619 }
620 }
621 else {
622 /* Same as above but different rectangle sizes. */
623 uint i_src = ps->cache.sub_rect.start, i_dst = 0;
624 for (uint j = 0; j < ps->cache.sub_rect.span_len; j++) {
625 const uint i_src_end = i_src + ps->cache.sub_rect.span;
626 for (; i_src < i_src_end; i_src++, i_dst++) {
627 EVAL_TEST(i_src, i_dst);
628 }
629 i_src += ps->cache.sub_rect.skip;
630 }
631 }
632 }
633
634#undef EVAL_TEST
635
636 qsort(depth_data, depth_data_len_first_pass, sizeof(DepthID), depth_id_cmp);
637
638 /* Sort by ID's then keep the best depth for each ID. */
639 depth_data_len = 0;
640 {
641 DepthID *depth_last = nullptr;
642 for (uint i = 0; i < depth_data_len_first_pass; i++) {
643 if (depth_last == nullptr || depth_last->id != depth_data[i].id) {
644 depth_last = &depth_data[depth_data_len++];
645 *depth_last = depth_data[i];
646 }
647 else if (depth_last->depth > depth_data[i].depth) {
648 depth_last->depth = depth_data[i].depth;
649 }
650 }
651 }
652 }
653
654 /* Finally sort each unique (id, depth) pair by depth
655 * so the final hit-list is sorted by depth (nearest first). */
656 uint hits = 0;
657
658 /* Leave sorting up to the caller. */
659 qsort(depth_data, depth_data_len, sizeof(DepthID), depth_cmp);
660
661 g_pick_state.buffer->storage.reserve(g_pick_state.buffer->storage.size() + depth_data_len);
662 for (uint i = 0; i < depth_data_len; i++) {
663#ifdef DEBUG_PRINT
664 printf(" hit: %u: depth %u\n", depth_data[i].id, depth_data[i].depth);
665#endif
666 GPUSelectResult hit_result{};
667 hit_result.id = depth_data[i].id;
668 hit_result.depth = depth_data[i].depth;
669 g_pick_state.buffer->storage.append_unchecked(hit_result);
670 hits++;
671 }
672
673 MEM_freeN(depth_data);
674
677
678 if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
679 /* 'hits' already freed as 'depth_data' */
680 }
681 else {
683 ps->nearest.rect_id = nullptr;
684 }
685
686 if (ps->use_cache) {
687 ps->is_cached = true;
688 }
689
690 return hits;
691}
692
694
695/* -------------------------------------------------------------------- */
700
702{
703 BLI_assert(g_pick_state.use_cache == false);
704#ifdef DEBUG_PRINT
705 printf("%s\n", __func__);
706#endif
707 g_pick_state.use_cache = true;
708 g_pick_state.is_cached = false;
709}
710
712{
713#ifdef DEBUG_PRINT
714 printf("%s: with %d buffers\n", __func__, BLI_listbase_count(&g_pick_state.cache.bufs));
715#endif
716 g_pick_state.use_cache = false;
717 g_pick_state.is_cached = false;
718
719 BLI_freelistN(&g_pick_state.cache.bufs);
720}
721
723{
724 return g_pick_state.is_cached;
725}
726
728{
729 BLI_assert(g_pick_state.is_cached == true);
731#ifdef DEBUG_PRINT
732 printf("%s (building depth from cache)\n", __func__);
733#endif
734 LISTBASE_FOREACH (DepthBufCache *, rect_depth, &ps->cache.bufs) {
735 if (rect_depth->next != nullptr) {
736 /* We know the buffers differ, but this sub-region may not.
737 * Double check before adding an id-pass. */
738 if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
739 if (depth_buf_subrect_depth_any(rect_depth->next, &ps->cache.sub_rect)) {
740 gpu_select_load_id_pass_all(rect_depth->next);
741 }
742 }
743 else {
744 if (depth_buf_subrect_depth_any_filled(rect_depth, rect_depth->next, &ps->cache.sub_rect))
745 {
746 gpu_select_load_id_pass_nearest(rect_depth, rect_depth->next);
747 }
748 }
749 }
750 }
751}
752
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
unsigned int uint
#define UNPACK4(a)
#define UNLIKELY(x)
void GPU_debug_group_end()
Definition gpu_debug.cc:33
void GPU_debug_group_begin(const char *name)
Definition gpu_debug.cc:22
void GPU_clear_depth(float depth)
void GPU_framebuffer_read_depth(blender::gpu::FrameBuffer *fb, int x, int y, int width, int height, eGPUDataFormat data_format, void *r_data)
blender::gpu::FrameBuffer * GPU_framebuffer_active_get()
GPUSelectMode
Definition GPU_select.hh:18
@ GPU_SELECT_PICK_ALL
Definition GPU_select.hh:25
GPUDepthTest
Definition GPU_state.hh:110
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:114
void GPU_depth_mask(bool depth)
Definition gpu_state.cc:110
void GPU_depth_test(GPUDepthTest test)
Definition gpu_state.cc:68
void GPU_color_mask(bool r, bool g, bool b, bool a)
Definition gpu_state.cc:98
GPUDepthTest GPU_depth_test_get()
Definition gpu_state.cc:244
GPUWriteMask
Definition GPU_state.hh:16
void GPU_viewport(int x, int y, int width, int height)
Definition gpu_state.cc:199
void GPU_write_mask(GPUWriteMask mask)
Definition gpu_state.cc:93
GPUWriteMask GPU_write_mask_get()
Definition gpu_state.cc:232
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:273
bool GPU_depth_mask_get()
Definition gpu_state.cc:287
void GPU_scissor_get(int coords[4])
Definition gpu_state.cc:268
@ GPU_DATA_UINT
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
bool gpu_select_pick_load_id(uint id, bool end)
void gpu_select_pick_begin(GPUSelectBuffer *buffer, const rcti *input, GPUSelectMode mode)
#define DEPTH_MAX
static DepthBufCache * depth_buf_malloc(uint rect_len)
static bool depth_buf_subrect_depth_any(const DepthBufCache *rect_depth, const SubRectStride *sub_rect)
#define ALLOC_DEPTHS
static int depth_cmp(const void *v1, const void *v2)
uint gpu_select_pick_end()
void gpu_select_pick_cache_begin()
BLI_INLINE bool depth_is_filled(const depth_t *prev, const depth_t *curr)
static bool depth_buf_subrect_depth_any_filled(const DepthBufCache *rect_src, const DepthBufCache *rect_dst, const SubRectStride *sub_rect)
static void rect_subregion_stride_calc(const rcti *src, const rcti *dst, SubRectStride *r_sub)
#define EVAL_TEST()
bool gpu_select_pick_is_cached()
uint depth_t
static int depth_id_cmp(const void *v1, const void *v2)
void gpu_select_pick_cache_load_id()
static GPUPickState g_pick_state
static void gpu_select_load_id_pass_all(const DepthBufCache *rect_curr)
static bool depth_buf_rect_depth_any_filled(const DepthBufCache *rect_prev, const DepthBufCache *rect_curr, uint rect_len)
static void gpu_select_load_id_pass_nearest(const DepthBufCache *rect_prev, const DepthBufCache *rect_curr)
void gpu_select_pick_cache_end()
static bool depth_buf_rect_depth_any(const DepthBufCache *rect_depth, uint rect_len)
#define SELECT_ID_NONE
#define input
#define printf(...)
bool all(VecOp< bool, D >) RET
BLI_INLINE float fb(float length, float L)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
DepthBufCache * next
DepthBufCache * prev
depth_t depth
GPUDepthTest depth_test
GPUSelectBuffer * buffer
DepthBufCache * rect_depth
struct GPUPickState::@165256152023044360216152151233240056157011130047 dst
GPUSelectMode mode
struct GPUPickState::@277056311340227302307104132302102101063115033051 cache
SubRectStride sub_rect
struct GPUPickState::@165256152023044360216152151233240056157011130047 src
struct GPUPickState::@360224015057162344327242036171332311344074073365::@313131235225103262053006161310024255225003066302 all
struct GPUPickState::@306061261262327106340214205126063074306041315030 gpu
struct GPUPickState::@360224015057162344327242036171332311344074073365::@165357267111104347001116107022334107201367020210 nearest
GPUWriteMask write_mask
DepthBufCache * rect_depth_test
unsigned int id
Definition GPU_select.hh:34
unsigned int depth
Definition GPU_select.hh:42
void * last
int ymin
int ymax
int xmin
int xmax
i
Definition text_draw.cc:230