This prevents the use of uninitialized buffer. In addition, use `memset` instead of assigning in a loop.
252 lines
6.5 KiB
C
252 lines
6.5 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 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 <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "GPU_select.h"
|
|
#include "GPU_extensions.h"
|
|
#include "GPU_glew.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_rect.h"
|
|
|
|
#include "DNA_userdef_types.h"
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "gpu_select_private.h"
|
|
|
|
/* Internal algorithm used */
|
|
enum {
|
|
/** 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,
|
|
};
|
|
|
|
typedef struct GPUSelectState {
|
|
/* To ignore selection id calls when not initialized */
|
|
bool select_is_active;
|
|
/* mode of operation */
|
|
char mode;
|
|
/* internal algorithm for selection */
|
|
char algorithm;
|
|
/* allow GPU_select_begin/end without drawing */
|
|
bool use_cache;
|
|
} GPUSelectState;
|
|
|
|
static GPUSelectState g_select_state = {0};
|
|
|
|
/**
|
|
* initialize and provide buffer for results
|
|
*/
|
|
void GPU_select_begin(uint *buffer, uint bufsize, const rcti *input, char 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;
|
|
}
|
|
|
|
switch (g_select_state.algorithm) {
|
|
case ALGO_GL_QUERY:
|
|
{
|
|
g_select_state.use_cache = false;
|
|
gpu_select_query_begin((uint (*)[4])buffer, bufsize / 4, input, mode, oldhits);
|
|
break;
|
|
}
|
|
default: /* ALGO_GL_PICK */
|
|
{
|
|
gpu_select_pick_begin((uint (*)[4])buffer, bufsize / 4, input, mode);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* loads a new selection id and ends previous query, if any. In second pass of selection it also returns
|
|
* if id has been hit on the first pass already.
|
|
* Thus we can skip drawing un-hit objects.
|
|
*
|
|
* \warning We rely on the order of object rendering on passes to be the same for this to work.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cleanup and flush selection results to buffer.
|
|
* Return number of hits and hits in buffer.
|
|
* if \a dopass is true, we will do a second pass with occlusion queries to get the closest hit.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* 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)
|
|
{
|
|
/* validate on GPU_select_begin, clear if not supported */
|
|
BLI_assert(g_select_state.use_cache == false);
|
|
g_select_state.use_cache = true;
|
|
if (g_select_state.algorithm == ALGO_GL_PICK) {
|
|
gpu_select_pick_cache_begin();
|
|
}
|
|
}
|
|
|
|
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) {
|
|
gpu_select_pick_cache_end();
|
|
}
|
|
g_select_state.use_cache = false;
|
|
}
|
|
|
|
bool GPU_select_is_cached(void)
|
|
{
|
|
return g_select_state.use_cache && gpu_select_pick_is_cached();
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* Utilities
|
|
*/
|
|
|
|
/**
|
|
* Helper function, nothing special but avoids doing inline since hit's aren't sorted by depth
|
|
* and purpose of 4x buffer indices isn't so clear.
|
|
*
|
|
* Note that comparing depth as uint is fine.
|
|
*/
|
|
const uint *GPU_select_buffer_near(const uint *buffer, int hits)
|
|
{
|
|
const uint *buffer_near = NULL;
|
|
uint depth_min = (uint) - 1;
|
|
for (int i = 0; i < hits; i++) {
|
|
if (buffer[1] < depth_min) {
|
|
BLI_assert(buffer[3] != -1);
|
|
depth_min = buffer[1];
|
|
buffer_near = buffer;
|
|
}
|
|
buffer += 4;
|
|
}
|
|
return buffer_near;
|
|
}
|
|
|
|
/* Part of the solution copied from `rect_subregion_stride_calc`. */
|
|
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_written = dst_x * dst_y - 1;
|
|
int last_px_id = src_x * (y + dst_y - 1) + (x + dst_x - 1);
|
|
const int skip = src_x - dst_x;
|
|
|
|
memset(&r_buf[last_px_id + 1], 0, (src_x * src_y - (last_px_id + 1)) * sizeof(*r_buf));
|
|
|
|
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));
|
|
}
|