Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gpu_select.c « intern « gpu « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: ac33c5d5ca8e9bc1edc449daa9a950f3781200cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/* SPDX-License-Identifier: GPL-2.0-or-later
 * Copyright 2014 Blender Foundation. All rights reserved. */

/** \file
 * \ingroup gpu
 *
 * Interface for accessing GPU-related methods for selection. The semantics are
 * similar to `glRenderMode(GL_SELECT)` from older OpenGL versions.
 */
#include <stdlib.h>
#include <string.h>

#include "GPU_select.h"

#include "MEM_guardedalloc.h"

#include "BLI_rect.h"

#include "DNA_userdef_types.h"

#include "BLI_utildefines.h"

#include "gpu_select_private.h"

/* -------------------------------------------------------------------- */
/** \name Internal Types
 * \{ */

/* Internal algorithm used */
typedef enum eGPUSelectAlgo {
  /** glBegin/EndQuery(GL_SAMPLES_PASSED... ), `gpu_select_query.c`
   * Only sets 4th component (ID) correctly. */
  ALGO_GL_QUERY = 1,
  /** Read depth buffer for every drawing pass and extract depths, `gpu_select_pick.c`
   * Only sets 4th component (ID) correctly. */
  ALGO_GL_PICK = 2,
} eGPUSelectAlgo;

typedef struct GPUSelectState {
  /* To ignore selection id calls when not initialized */
  bool select_is_active;
  /* mode of operation */
  eGPUSelectMode mode;
  /* internal algorithm for selection */
  eGPUSelectAlgo algorithm;
  /* allow GPU_select_begin/end without drawing */
  bool use_cache;
  /**
   * 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;
} GPUSelectState;

static GPUSelectState g_select_state = {0};

/** \} */

/* -------------------------------------------------------------------- */
/** \name Public API
 * \{ */

void GPU_select_begin(GPUSelectResult *buffer,
                      const uint buffer_len,
                      const rcti *input,
                      eGPUSelectMode mode,
                      int oldhits)
{
  if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
    /* In the case hits was '-1',
     * don't start the second pass since it's not going to give useful results.
     * As well as buffer overflow in 'gpu_select_query_load_id'. */
    BLI_assert(oldhits != -1);
  }

  g_select_state.select_is_active = true;
  g_select_state.mode = mode;

  if (ELEM(g_select_state.mode, GPU_SELECT_PICK_ALL, GPU_SELECT_PICK_NEAREST)) {
    g_select_state.algorithm = ALGO_GL_PICK;
  }
  else {
    g_select_state.algorithm = ALGO_GL_QUERY;
  }

  /* 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) {
      case ALGO_GL_QUERY: {
        g_select_state.use_cache = false;
        break;
      }
      default: {
        g_select_state.use_cache = true;
        gpu_select_pick_cache_begin();
        break;
      }
    }
  }

  switch (g_select_state.algorithm) {
    case ALGO_GL_QUERY: {
      gpu_select_query_begin(buffer, buffer_len, input, mode, oldhits);
      break;
    }
    default: /* ALGO_GL_PICK */
    {
      gpu_select_pick_begin(buffer, buffer_len, input, mode);
      break;
    }
  }
}

bool GPU_select_load_id(uint id)
{
  /* if no selection mode active, ignore */
  if (!g_select_state.select_is_active) {
    return true;
  }

  switch (g_select_state.algorithm) {
    case ALGO_GL_QUERY: {
      return gpu_select_query_load_id(id);
    }
    default: /* ALGO_GL_PICK */
    {
      return gpu_select_pick_load_id(id, false);
    }
  }
}

uint GPU_select_end(void)
{
  uint hits = 0;

  switch (g_select_state.algorithm) {
    case ALGO_GL_QUERY: {
      hits = gpu_select_query_end();
      break;
    }
    default: /* ALGO_GL_PICK */
    {
      hits = gpu_select_pick_end();
      break;
    }
  }

  g_select_state.select_is_active = false;

  return hits;
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name Caching
 *
 * Support multiple begin/end's as long as they are within the initial region.
 * Currently only used by #ALGO_GL_PICK.
 * \{ */

void GPU_select_cache_begin(void)
{
  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;
}

void GPU_select_cache_load_id(void)
{
  BLI_assert(g_select_state.use_cache == true);
  if (g_select_state.algorithm == ALGO_GL_PICK) {
    gpu_select_pick_cache_load_id();
  }
}

void GPU_select_cache_end(void)
{
  if (g_select_state.algorithm == ALGO_GL_PICK) {
    BLI_assert(g_select_state.use_cache == true);
    gpu_select_pick_cache_end();
  }
  g_select_state.use_cache = false;
  /* Paranoid assignment, should already be false. */
  g_select_state.use_cache_needs_init = false;
}

bool GPU_select_is_cached(void)
{
  return g_select_state.use_cache && gpu_select_pick_is_cached();
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name Utilities
 * \{ */

const GPUSelectResult *GPU_select_buffer_near(const GPUSelectResult *buffer, int hits)
{
  const GPUSelectResult *buffer_near = NULL;
  uint depth_min = (uint)-1;
  for (int i = 0; i < hits; i++) {
    if (buffer->depth < depth_min) {
      BLI_assert(buffer->id != -1);
      depth_min = buffer->depth;
      buffer_near = buffer;
    }
    buffer++;
  }
  return buffer_near;
}

uint GPU_select_buffer_remove_by_id(GPUSelectResult *buffer, int hits, uint select_id)
{
  GPUSelectResult *buffer_src = buffer;
  GPUSelectResult *buffer_dst = buffer;
  int hits_final = 0;
  for (int i = 0; i < hits; i++) {
    if (buffer_src->id != select_id) {
      if (buffer_dst != buffer_src) {
        memcpy(buffer_dst, buffer_src, sizeof(GPUSelectResult));
      }
      buffer_dst++;
      hits_final += 1;
    }
    buffer_src++;
  }
  return hits_final;
}

void GPU_select_buffer_stride_realign(const rcti *src, const rcti *dst, uint *r_buf)
{
  const int x = dst->xmin - src->xmin;
  const int y = dst->ymin - src->ymin;

  BLI_assert(src->xmin <= dst->xmin && src->ymin <= dst->ymin && src->xmax >= dst->xmax &&
             src->ymax >= dst->ymax);
  BLI_assert(x >= 0 && y >= 0);

  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);

  int last_px_id = src_x * (y + dst_y - 1) + (x + dst_x - 1);
  memset(&r_buf[last_px_id + 1], 0, (src_x * src_y - (last_px_id + 1)) * sizeof(*r_buf));

  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;

  while (true) {
    for (int i = dst_x; i--;) {
      r_buf[last_px_id--] = r_buf[last_px_written--];
    }
    if (last_px_written < 0) {
      break;
    }
    last_px_id -= skip;
    memset(&r_buf[last_px_id + 1], 0, skip * sizeof(*r_buf));
  }
  memset(r_buf, 0, (last_px_id + 1) * sizeof(*r_buf));
}

/** \} */