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