2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2014-07-23 15:24:07 +02:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup gpu
|
2014-07-23 15:24:07 +02:00
|
|
|
*
|
2021-10-03 12:06:06 +11:00
|
|
|
* Interface for accessing GPU-related methods for selection. The semantics are
|
|
|
|
|
* similar to `glRenderMode(GL_SELECT)` from older OpenGL versions.
|
2014-07-23 15:24:07 +02:00
|
|
|
*/
|
2023-07-28 09:38:07 +10:00
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
2017-03-09 05:17:55 +11:00
|
|
|
|
2023-12-01 03:39:03 +01:00
|
|
|
#include "GPU_select.hh"
|
2017-03-09 05:17:55 +11:00
|
|
|
|
2019-03-28 15:59:51 -03:00
|
|
|
#include "BLI_rect.h"
|
|
|
|
|
|
2014-11-29 19:12:33 +01:00
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
|
|
2024-03-23 01:24:18 +01:00
|
|
|
#include "gpu_select_private.hh"
|
2017-03-09 05:17:55 +11:00
|
|
|
|
2021-10-05 11:10:25 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Internal Types
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2017-03-09 05:17:55 +11:00
|
|
|
/* Internal algorithm used */
|
2023-07-27 14:16:58 +02:00
|
|
|
enum eGPUSelectAlgo {
|
2023-07-31 11:50:54 +10:00
|
|
|
/**
|
|
|
|
|
* `glBegin/EndQuery(GL_SAMPLES_PASSED... )`, `gpu_select_query.c`
|
|
|
|
|
* Only sets 4th component (ID) correctly.
|
|
|
|
|
*/
|
2025-02-05 11:19:13 +11:00
|
|
|
ALGO_SAMPLE_QUERY = 1,
|
2023-07-31 11:50:54 +10:00
|
|
|
/**
|
|
|
|
|
* Read depth buffer for every drawing pass and extract depths, `gpu_select_pick.cc`
|
|
|
|
|
* Only sets 4th component (ID) correctly.
|
|
|
|
|
*/
|
2025-02-05 11:19:13 +11:00
|
|
|
ALGO_DEPTH_PICK = 2,
|
2023-05-23 15:00:38 +02:00
|
|
|
/** Use Select-Next draw engine. */
|
|
|
|
|
ALGO_SELECT_NEXT = 3,
|
2023-07-27 14:16:58 +02:00
|
|
|
};
|
2017-03-09 05:17:55 +11:00
|
|
|
|
2023-07-27 14:16:58 +02:00
|
|
|
struct GPUSelectState {
|
2014-07-23 15:24:07 +02:00
|
|
|
/* To ignore selection id calls when not initialized */
|
|
|
|
|
bool select_is_active;
|
|
|
|
|
/* mode of operation */
|
2022-01-31 13:01:27 +11:00
|
|
|
eGPUSelectMode mode;
|
2017-03-09 05:17:55 +11:00
|
|
|
/* internal algorithm for selection */
|
2022-01-31 13:01:27 +11:00
|
|
|
eGPUSelectAlgo algorithm;
|
2017-03-09 05:17:55 +11:00
|
|
|
/* allow GPU_select_begin/end without drawing */
|
|
|
|
|
bool use_cache;
|
2022-01-26 17:10:34 +11:00
|
|
|
/**
|
|
|
|
|
* Signifies that #GPU_select_cache_begin has been called,
|
|
|
|
|
* future calls to #GPU_select_begin should initialize the cache.
|
|
|
|
|
*
|
|
|
|
|
* \note #GPU_select_cache_begin could perform initialization but doesn't as it's inconvenient
|
|
|
|
|
* for callers making the cache begin/end calls outside lower level selection logic
|
|
|
|
|
* where the `mode` to pass to #GPU_select_begin yet isn't known.
|
|
|
|
|
*/
|
|
|
|
|
bool use_cache_needs_init;
|
2023-07-27 14:16:58 +02:00
|
|
|
};
|
2014-07-23 15:24:07 +02:00
|
|
|
|
2023-07-28 09:38:07 +10:00
|
|
|
static GPUSelectState g_select_state = {false};
|
2014-07-23 15:24:07 +02:00
|
|
|
|
2021-10-05 11:10:25 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Public API
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2023-11-30 21:27:15 +01:00
|
|
|
static void gpu_select_begin_ex(GPUSelectBuffer *buffer,
|
2023-05-23 15:00:38 +02:00
|
|
|
const rcti *input,
|
|
|
|
|
eGPUSelectMode mode,
|
|
|
|
|
int oldhits,
|
|
|
|
|
bool use_select_next)
|
2014-07-23 15:24:07 +02:00
|
|
|
{
|
2017-06-14 17:03:49 +10:00
|
|
|
if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
|
2019-04-22 01:45:43 +10:00
|
|
|
/* In the case hits was '-1',
|
|
|
|
|
* don't start the second pass since it's not going to give useful results.
|
2017-06-14 17:03:49 +10:00
|
|
|
* As well as buffer overflow in 'gpu_select_query_load_id'. */
|
|
|
|
|
BLI_assert(oldhits != -1);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-09 05:17:55 +11:00
|
|
|
g_select_state.select_is_active = true;
|
|
|
|
|
g_select_state.mode = mode;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-23 15:00:38 +02:00
|
|
|
if (use_select_next) {
|
|
|
|
|
g_select_state.algorithm = ALGO_SELECT_NEXT;
|
|
|
|
|
}
|
|
|
|
|
else if (ELEM(g_select_state.mode, GPU_SELECT_PICK_ALL, GPU_SELECT_PICK_NEAREST)) {
|
2025-02-05 11:19:13 +11:00
|
|
|
g_select_state.algorithm = ALGO_DEPTH_PICK;
|
2017-03-09 05:17:55 +11:00
|
|
|
}
|
2014-07-23 15:24:07 +02:00
|
|
|
else {
|
2025-02-05 11:19:13 +11:00
|
|
|
g_select_state.algorithm = ALGO_SAMPLE_QUERY;
|
2017-03-09 05:17:55 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-01-26 17:10:34 +11:00
|
|
|
/* This function is called when cache has already been initialized,
|
|
|
|
|
* so only manipulate cache values when cache is pending. */
|
|
|
|
|
if (g_select_state.use_cache_needs_init) {
|
|
|
|
|
g_select_state.use_cache_needs_init = false;
|
|
|
|
|
|
|
|
|
|
switch (g_select_state.algorithm) {
|
2023-05-23 15:00:38 +02:00
|
|
|
case ALGO_SELECT_NEXT:
|
2025-02-05 11:19:13 +11:00
|
|
|
case ALGO_SAMPLE_QUERY: {
|
2022-01-26 17:10:34 +11:00
|
|
|
g_select_state.use_cache = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
g_select_state.use_cache = true;
|
|
|
|
|
gpu_select_pick_cache_begin();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-09 05:17:55 +11:00
|
|
|
switch (g_select_state.algorithm) {
|
2023-05-23 15:00:38 +02:00
|
|
|
case ALGO_SELECT_NEXT: {
|
2023-11-30 21:27:15 +01:00
|
|
|
gpu_select_next_begin(buffer, input, mode);
|
2023-05-23 15:00:38 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2025-02-05 11:19:13 +11:00
|
|
|
case ALGO_SAMPLE_QUERY: {
|
2023-11-30 21:27:15 +01:00
|
|
|
gpu_select_query_begin(buffer, input, mode, oldhits);
|
2017-03-09 05:17:55 +11:00
|
|
|
break;
|
2014-07-23 15:24:07 +02:00
|
|
|
}
|
2025-02-05 11:19:13 +11:00
|
|
|
default: /* ALGO_DEPTH_PICK */
|
2017-03-09 05:17:55 +11:00
|
|
|
{
|
2023-11-30 21:27:15 +01:00
|
|
|
gpu_select_pick_begin(buffer, input, mode);
|
2017-03-09 05:17:55 +11:00
|
|
|
break;
|
2014-07-23 15:24:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 21:27:15 +01:00
|
|
|
void GPU_select_begin_next(GPUSelectBuffer *buffer,
|
2023-05-23 15:00:38 +02:00
|
|
|
const rcti *input,
|
|
|
|
|
eGPUSelectMode mode,
|
|
|
|
|
int oldhits)
|
|
|
|
|
{
|
2024-12-02 19:47:55 +01:00
|
|
|
gpu_select_begin_ex(buffer, input, mode, oldhits, true);
|
2023-05-23 15:00:38 +02:00
|
|
|
}
|
|
|
|
|
|
2023-11-30 21:27:15 +01:00
|
|
|
void GPU_select_begin(GPUSelectBuffer *buffer, const rcti *input, eGPUSelectMode mode, int oldhits)
|
2023-05-23 15:00:38 +02:00
|
|
|
{
|
2023-11-30 21:27:15 +01:00
|
|
|
gpu_select_begin_ex(buffer, input, mode, oldhits, false);
|
2023-05-23 15:00:38 +02:00
|
|
|
}
|
|
|
|
|
|
2018-02-28 11:37:39 +11:00
|
|
|
bool GPU_select_load_id(uint id)
|
2014-07-23 15:24:07 +02:00
|
|
|
{
|
|
|
|
|
/* if no selection mode active, ignore */
|
2019-04-22 09:32:37 +10:00
|
|
|
if (!g_select_state.select_is_active) {
|
2014-07-23 15:24:07 +02:00
|
|
|
return true;
|
2019-04-22 09:32:37 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-09 05:17:55 +11:00
|
|
|
switch (g_select_state.algorithm) {
|
2023-05-23 15:00:38 +02:00
|
|
|
case ALGO_SELECT_NEXT:
|
|
|
|
|
/* This shouldn't use this pipeline. */
|
|
|
|
|
BLI_assert_unreachable();
|
2023-07-28 09:38:07 +10:00
|
|
|
return false;
|
2023-05-23 15:00:38 +02:00
|
|
|
|
2025-02-05 11:19:13 +11:00
|
|
|
case ALGO_SAMPLE_QUERY: {
|
2017-03-09 05:17:55 +11:00
|
|
|
return gpu_select_query_load_id(id);
|
2014-07-23 15:24:07 +02:00
|
|
|
}
|
2025-02-05 11:19:13 +11:00
|
|
|
default: /* ALGO_DEPTH_PICK */
|
2017-03-09 05:17:55 +11:00
|
|
|
{
|
2019-04-27 20:37:11 +02:00
|
|
|
return gpu_select_pick_load_id(id, false);
|
2014-07-23 15:24:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-28 09:38:07 +10:00
|
|
|
uint GPU_select_end()
|
2014-07-23 15:24:07 +02:00
|
|
|
{
|
2018-02-28 11:37:39 +11:00
|
|
|
uint hits = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-09 05:17:55 +11:00
|
|
|
switch (g_select_state.algorithm) {
|
2023-05-23 15:00:38 +02:00
|
|
|
case ALGO_SELECT_NEXT: {
|
|
|
|
|
hits = gpu_select_next_end();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-02-05 11:19:13 +11:00
|
|
|
case ALGO_SAMPLE_QUERY: {
|
2017-03-09 05:17:55 +11:00
|
|
|
hits = gpu_select_query_end();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-02-05 11:19:13 +11:00
|
|
|
default: /* ALGO_DEPTH_PICK */
|
2017-03-09 05:17:55 +11:00
|
|
|
{
|
|
|
|
|
hits = gpu_select_pick_end();
|
|
|
|
|
break;
|
2014-07-23 15:24:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-09 05:17:55 +11:00
|
|
|
g_select_state.select_is_active = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-07-23 15:24:07 +02:00
|
|
|
return hits;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-05 11:10:25 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Caching
|
2017-03-09 05:17:55 +11:00
|
|
|
*
|
|
|
|
|
* Support multiple begin/end's as long as they are within the initial region.
|
2025-02-05 11:19:13 +11:00
|
|
|
* Currently only used by #ALGO_DEPTH_PICK.
|
2021-10-05 11:10:25 +11:00
|
|
|
* \{ */
|
2017-03-09 05:17:55 +11:00
|
|
|
|
2023-07-28 09:38:07 +10:00
|
|
|
void GPU_select_cache_begin()
|
2017-03-09 05:17:55 +11:00
|
|
|
{
|
2022-01-26 17:10:34 +11:00
|
|
|
BLI_assert(g_select_state.select_is_active == false);
|
|
|
|
|
/* Ensure #GPU_select_cache_end is always called. */
|
|
|
|
|
BLI_assert(g_select_state.use_cache_needs_init == false);
|
|
|
|
|
|
|
|
|
|
/* Signal that cache should be used, instead of calling the algorithms cache-begin function.
|
|
|
|
|
* This is more convenient as the exact method of selection may not be known by the caller. */
|
|
|
|
|
g_select_state.use_cache_needs_init = true;
|
2017-03-09 05:17:55 +11:00
|
|
|
}
|
|
|
|
|
|
2023-07-28 09:38:07 +10:00
|
|
|
void GPU_select_cache_load_id()
|
2017-03-09 05:17:55 +11:00
|
|
|
{
|
|
|
|
|
BLI_assert(g_select_state.use_cache == true);
|
2025-02-05 11:19:13 +11:00
|
|
|
if (g_select_state.algorithm == ALGO_DEPTH_PICK) {
|
2017-03-09 05:17:55 +11:00
|
|
|
gpu_select_pick_cache_load_id();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-28 09:38:07 +10:00
|
|
|
void GPU_select_cache_end()
|
2017-03-09 05:17:55 +11:00
|
|
|
{
|
2025-02-05 11:19:13 +11:00
|
|
|
if (g_select_state.algorithm == ALGO_DEPTH_PICK) {
|
2022-01-26 17:10:34 +11:00
|
|
|
BLI_assert(g_select_state.use_cache == true);
|
2017-03-09 05:17:55 +11:00
|
|
|
gpu_select_pick_cache_end();
|
|
|
|
|
}
|
|
|
|
|
g_select_state.use_cache = false;
|
2022-01-26 17:10:34 +11:00
|
|
|
/* Paranoid assignment, should already be false. */
|
|
|
|
|
g_select_state.use_cache_needs_init = false;
|
2017-03-09 05:17:55 +11:00
|
|
|
}
|
|
|
|
|
|
2023-07-28 09:38:07 +10:00
|
|
|
bool GPU_select_is_cached()
|
2017-03-09 05:17:55 +11:00
|
|
|
{
|
|
|
|
|
return g_select_state.use_cache && gpu_select_pick_is_cached();
|
|
|
|
|
}
|
2017-07-19 20:12:24 +10:00
|
|
|
|
2021-10-05 11:10:25 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Utilities
|
|
|
|
|
* \{ */
|
2017-07-19 20:12:24 +10:00
|
|
|
|
2023-11-30 21:27:15 +01:00
|
|
|
const GPUSelectResult *GPU_select_buffer_near(const blender::Span<GPUSelectResult> hit_results)
|
2017-07-19 20:12:24 +10:00
|
|
|
{
|
2023-12-01 09:45:02 +11:00
|
|
|
const GPUSelectResult *hit_result_near = nullptr;
|
2023-07-28 09:38:07 +10:00
|
|
|
uint depth_min = uint(-1);
|
2023-12-01 09:45:02 +11:00
|
|
|
for (const GPUSelectResult &hit_result : hit_results) {
|
|
|
|
|
if (hit_result.depth < depth_min) {
|
|
|
|
|
BLI_assert(hit_result.id != -1);
|
|
|
|
|
depth_min = hit_result.depth;
|
|
|
|
|
hit_result_near = &hit_result;
|
2017-07-19 20:12:24 +10:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-01 09:45:02 +11:00
|
|
|
return hit_result_near;
|
2017-07-26 02:34:14 +10:00
|
|
|
}
|
2019-03-28 15:59:51 -03:00
|
|
|
|
2023-11-30 21:27:15 +01:00
|
|
|
uint GPU_select_buffer_remove_by_id(blender::MutableSpan<GPUSelectResult> hit_results,
|
|
|
|
|
uint select_id)
|
2020-06-10 17:50:11 +10:00
|
|
|
{
|
2023-11-30 21:27:15 +01:00
|
|
|
uint index_src = 0;
|
|
|
|
|
uint index_dst = 0;
|
|
|
|
|
uint hits_final = 0;
|
2023-12-01 09:45:02 +11:00
|
|
|
for (const GPUSelectResult &hit_result : hit_results) {
|
|
|
|
|
if (hit_result.id != select_id) {
|
2023-11-30 21:27:15 +01:00
|
|
|
if (index_dst != index_src) {
|
2023-12-01 09:45:02 +11:00
|
|
|
hit_results[index_dst] = hit_result;
|
2020-06-10 17:50:11 +10:00
|
|
|
}
|
2023-11-30 21:27:15 +01:00
|
|
|
index_dst++;
|
|
|
|
|
hits_final++;
|
2020-06-10 17:50:11 +10:00
|
|
|
}
|
2023-11-30 21:27:15 +01:00
|
|
|
index_src++;
|
2020-06-10 17:50:11 +10:00
|
|
|
}
|
|
|
|
|
return hits_final;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-28 15:59:51 -03:00
|
|
|
void GPU_select_buffer_stride_realign(const rcti *src, const rcti *dst, uint *r_buf)
|
|
|
|
|
{
|
2019-03-31 15:58:29 -03:00
|
|
|
const int x = dst->xmin - src->xmin;
|
|
|
|
|
const int y = dst->ymin - src->ymin;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-28 15:59:51 -03:00
|
|
|
BLI_assert(src->xmin <= dst->xmin && src->ymin <= dst->ymin && src->xmax >= dst->xmax &&
|
|
|
|
|
src->ymax >= dst->ymax);
|
|
|
|
|
BLI_assert(x >= 0 && y >= 0);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-31 15:58:29 -03:00
|
|
|
const int src_x = BLI_rcti_size_x(src);
|
|
|
|
|
const int src_y = BLI_rcti_size_y(src);
|
|
|
|
|
const int dst_x = BLI_rcti_size_x(dst);
|
|
|
|
|
const int dst_y = BLI_rcti_size_y(dst);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-28 15:59:51 -03:00
|
|
|
int last_px_id = src_x * (y + dst_y - 1) + (x + dst_x - 1);
|
2019-03-31 15:58:29 -03:00
|
|
|
memset(&r_buf[last_px_id + 1], 0, (src_x * src_y - (last_px_id + 1)) * sizeof(*r_buf));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-05-21 20:56:27 -03:00
|
|
|
if (last_px_id < 0) {
|
|
|
|
|
/* Nothing to write. */
|
|
|
|
|
BLI_assert(last_px_id == -1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int last_px_written = dst_x * dst_y - 1;
|
|
|
|
|
const int skip = src_x - dst_x;
|
|
|
|
|
|
2019-03-31 15:58:29 -03:00
|
|
|
while (true) {
|
|
|
|
|
for (int i = dst_x; i--;) {
|
2019-03-28 15:59:51 -03:00
|
|
|
r_buf[last_px_id--] = r_buf[last_px_written--];
|
|
|
|
|
}
|
|
|
|
|
if (last_px_written < 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-03-31 15:58:29 -03:00
|
|
|
last_px_id -= skip;
|
|
|
|
|
memset(&r_buf[last_px_id + 1], 0, skip * sizeof(*r_buf));
|
2019-03-28 15:59:51 -03:00
|
|
|
}
|
|
|
|
|
memset(r_buf, 0, (last_px_id + 1) * sizeof(*r_buf));
|
|
|
|
|
}
|
2021-10-05 11:10:25 +11:00
|
|
|
|
|
|
|
|
/** \} */
|