| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * ***** BEGIN GPL LICENSE BLOCK ***** | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Contributor(s): Antony Riakiotakis. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * ***** END GPL LICENSE BLOCK ***** | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** \file blender/gpu/intern/gpu_select.c
 | 
					
						
							|  |  |  |  *  \ingroup gpu | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Interface for accessing gpu-related methods for selection. The semantics will be | 
					
						
							|  |  |  |  * similar to glRenderMode(GL_SELECT) since the goal is to maintain compatibility. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include "GPU_select.h"
 | 
					
						
							|  |  |  | #include "GPU_extensions.h"
 | 
					
						
							| 
									
										
										
										
											2014-10-07 15:46:19 -05:00
										 |  |  | #include "GPU_glew.h"
 | 
					
						
							|  |  |  |   | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | #include "MEM_guardedalloc.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "DNA_userdef_types.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-29 19:12:33 +01:00
										 |  |  | #include "BLI_utildefines.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | /* Ad hoc number of queries to allocate to skip doing many glGenQueries */ | 
					
						
							|  |  |  | #define ALLOC_QUERIES 200
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct GPUQueryState { | 
					
						
							|  |  |  | 	/* To ignore selection id calls when not initialized */ | 
					
						
							|  |  |  | 	bool select_is_active; | 
					
						
							|  |  |  | 	/* Tracks whether a query has been issued so that gpu_load_id can end the previous one */ | 
					
						
							|  |  |  | 	bool query_issued; | 
					
						
							|  |  |  | 	/* array holding the OpenGL query identifiers */ | 
					
						
							|  |  |  | 	unsigned int *queries; | 
					
						
							|  |  |  | 	/* array holding the id corresponding to each query */ | 
					
						
							|  |  |  | 	unsigned int *id; | 
					
						
							|  |  |  | 	/* number of queries in *queries and *id */ | 
					
						
							|  |  |  | 	unsigned int num_of_queries; | 
					
						
							|  |  |  | 	/* index to the next query to start */ | 
					
						
							|  |  |  | 	unsigned int active_query; | 
					
						
							|  |  |  | 	/* flag to cache user preference for occlusion based selection */ | 
					
						
							|  |  |  | 	bool use_gpu_select; | 
					
						
							|  |  |  | 	/* cache on initialization */ | 
					
						
							|  |  |  | 	unsigned int *buffer; | 
					
						
							| 
									
										
										
										
											2015-02-24 17:19:07 +01:00
										 |  |  | 	/* buffer size (stores number of integers, for actual size multiply by sizeof integer)*/ | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 	unsigned int bufsize; | 
					
						
							|  |  |  | 	/* mode of operation */ | 
					
						
							|  |  |  | 	char mode; | 
					
						
							|  |  |  | 	unsigned int index; | 
					
						
							|  |  |  | 	int oldhits; | 
					
						
							|  |  |  | } GPUQueryState; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static GPUQueryState g_query_state = {0}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-10 07:12:10 +11:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * initialize and provide buffer for results | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | void GPU_select_begin(unsigned int *buffer, unsigned int bufsize, rctf *input, char mode, int oldhits) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	g_query_state.select_is_active = true; | 
					
						
							|  |  |  | 	g_query_state.query_issued = false; | 
					
						
							|  |  |  | 	g_query_state.active_query = 0; | 
					
						
							|  |  |  | 	g_query_state.use_gpu_select = GPU_select_query_check_active(); | 
					
						
							|  |  |  | 	g_query_state.num_of_queries = 0; | 
					
						
							|  |  |  | 	g_query_state.bufsize = bufsize; | 
					
						
							|  |  |  | 	g_query_state.buffer = buffer; | 
					
						
							|  |  |  | 	g_query_state.mode = mode; | 
					
						
							|  |  |  | 	g_query_state.index = 0; | 
					
						
							|  |  |  | 	g_query_state.oldhits = oldhits; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!g_query_state.use_gpu_select) { | 
					
						
							| 
									
										
										
										
											2015-02-07 04:33:48 +11:00
										 |  |  | 		glSelectBuffer(bufsize, (GLuint *)buffer); | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 		glRenderMode(GL_SELECT); | 
					
						
							|  |  |  | 		glInitNames(); | 
					
						
							|  |  |  | 		glPushName(-1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		float viewport[4]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		g_query_state.num_of_queries = ALLOC_QUERIES; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-07 04:33:48 +11:00
										 |  |  | 		g_query_state.queries = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.queries), "gpu selection queries"); | 
					
						
							|  |  |  | 		g_query_state.id = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.id), "gpu selection ids"); | 
					
						
							| 
									
										
										
										
											2015-11-24 02:20:38 -05:00
										 |  |  | 		glGenQueries(g_query_state.num_of_queries, g_query_state.queries); | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_VIEWPORT_BIT); | 
					
						
							|  |  |  | 		/* disable writing to the framebuffer */ | 
					
						
							|  |  |  | 		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* In order to save some fill rate we minimize the viewport using rect.
 | 
					
						
							|  |  |  | 		 * We need to get the region of the scissor so that our geometry doesn't | 
					
						
							|  |  |  | 		 * get rejected before the depth test. Should probably cull rect against | 
					
						
							|  |  |  | 		 * scissor for viewport but this is a rare case I think */ | 
					
						
							|  |  |  | 		glGetFloatv(GL_SCISSOR_BOX, viewport); | 
					
						
							|  |  |  | 		if (!input || input->xmin == input->xmax) { | 
					
						
							|  |  |  | 			glViewport(viewport[0], viewport[1], 24, 24); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			glViewport(viewport[0], viewport[1], (int)(input->xmax - input->xmin), (int)(input->ymax - input->ymin)); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* occlusion queries operates on fragments that pass tests and since we are interested on all
 | 
					
						
							|  |  |  | 		 * objects in the view frustum independently of their order, we need to disable the depth test */ | 
					
						
							|  |  |  | 		if (mode == GPU_SELECT_ALL) { | 
					
						
							|  |  |  | 			glDisable(GL_DEPTH_TEST); | 
					
						
							|  |  |  | 			glDepthMask(GL_FALSE); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else if (mode == GPU_SELECT_NEAREST_FIRST_PASS) { | 
					
						
							|  |  |  | 			glClear(GL_DEPTH_BUFFER_BIT); | 
					
						
							|  |  |  | 			glEnable(GL_DEPTH_TEST); | 
					
						
							|  |  |  | 			glDepthMask(GL_TRUE); | 
					
						
							|  |  |  | 			glDepthFunc(GL_LEQUAL); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else if (mode == GPU_SELECT_NEAREST_SECOND_PASS) { | 
					
						
							|  |  |  | 			glEnable(GL_DEPTH_TEST); | 
					
						
							| 
									
										
										
										
											2014-09-24 18:02:40 +02:00
										 |  |  | 			glDepthMask(GL_FALSE); | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 			glDepthFunc(GL_EQUAL); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-10 07:12:10 +11:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | bool GPU_select_load_id(unsigned int id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* if no selection mode active, ignore */ | 
					
						
							| 
									
										
										
										
											2015-01-24 01:59:09 +11:00
										 |  |  | 	if (!g_query_state.select_is_active) | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 		return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!g_query_state.use_gpu_select) { | 
					
						
							|  |  |  | 		glLoadName(id); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		if (g_query_state.query_issued) { | 
					
						
							| 
									
										
										
										
											2015-11-24 02:20:38 -05:00
										 |  |  | 			glEndQuery(GL_SAMPLES_PASSED); | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		/* if required, allocate extra queries */ | 
					
						
							|  |  |  | 		if (g_query_state.active_query == g_query_state.num_of_queries) { | 
					
						
							|  |  |  | 			g_query_state.num_of_queries += ALLOC_QUERIES; | 
					
						
							|  |  |  | 			g_query_state.queries = MEM_reallocN(g_query_state.queries, g_query_state.num_of_queries * sizeof(*g_query_state.queries)); | 
					
						
							|  |  |  | 			g_query_state.id = MEM_reallocN(g_query_state.id, g_query_state.num_of_queries * sizeof(*g_query_state.id)); | 
					
						
							| 
									
										
										
										
											2015-11-24 02:20:38 -05:00
										 |  |  | 			glGenQueries(ALLOC_QUERIES, &g_query_state.queries[g_query_state.active_query]); | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-24 02:20:38 -05:00
										 |  |  | 		glBeginQuery(GL_SAMPLES_PASSED, g_query_state.queries[g_query_state.active_query]); | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 		g_query_state.id[g_query_state.active_query] = id; | 
					
						
							|  |  |  | 		g_query_state.active_query++; | 
					
						
							|  |  |  | 		g_query_state.query_issued = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-02 17:45:07 +01:00
										 |  |  | 		if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS && g_query_state.index < g_query_state.oldhits) { | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 			if (g_query_state.buffer[g_query_state.index * 4 + 3] == id) { | 
					
						
							|  |  |  | 				g_query_state.index++; | 
					
						
							|  |  |  | 				return true; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			else { | 
					
						
							|  |  |  | 				return false; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-10 07:12:10 +11:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | unsigned int GPU_select_end(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int hits = 0; | 
					
						
							|  |  |  | 	if (!g_query_state.use_gpu_select) { | 
					
						
							|  |  |  | 		glPopName(); | 
					
						
							|  |  |  | 		hits = glRenderMode(GL_RENDER); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (g_query_state.query_issued) { | 
					
						
							| 
									
										
										
										
											2015-11-24 02:20:38 -05:00
										 |  |  | 			glEndQuery(GL_SAMPLES_PASSED); | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (i = 0; i < g_query_state.active_query; i++) { | 
					
						
							|  |  |  | 			unsigned int result; | 
					
						
							| 
									
										
										
										
											2015-11-24 02:20:38 -05:00
										 |  |  | 			glGetQueryObjectuiv(g_query_state.queries[i], GL_QUERY_RESULT, &result); | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 			if (result > 0) { | 
					
						
							|  |  |  | 				if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) { | 
					
						
							| 
									
										
										
										
											2015-02-24 17:19:07 +01:00
										 |  |  | 					int maxhits = g_query_state.bufsize / 4; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (hits < maxhits) { | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 						g_query_state.buffer[hits * 4] = 1; | 
					
						
							|  |  |  | 						g_query_state.buffer[hits * 4 + 1] = 0xFFFF; | 
					
						
							|  |  |  | 						g_query_state.buffer[hits * 4 + 2] = 0xFFFF; | 
					
						
							|  |  |  | 						g_query_state.buffer[hits * 4 + 3] = g_query_state.id[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						hits++; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					else { | 
					
						
							|  |  |  | 						hits = -1; | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				else { | 
					
						
							|  |  |  | 					int j; | 
					
						
							|  |  |  | 					/* search in buffer and make selected object first */ | 
					
						
							|  |  |  | 					for (j = 0; j < g_query_state.oldhits; j++) { | 
					
						
							|  |  |  | 						if (g_query_state.buffer[j * 4 + 3] == g_query_state.id[i]) { | 
					
						
							|  |  |  | 							g_query_state.buffer[j * 4 + 1] = 0; | 
					
						
							|  |  |  | 							g_query_state.buffer[j * 4 + 2] = 0; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-24 02:20:38 -05:00
										 |  |  | 		glDeleteQueries(g_query_state.num_of_queries, g_query_state.queries); | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | 		MEM_freeN(g_query_state.queries); | 
					
						
							|  |  |  | 		MEM_freeN(g_query_state.id); | 
					
						
							|  |  |  | 		glPopAttrib(); | 
					
						
							|  |  |  | 		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	g_query_state.select_is_active = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return hits; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-10 07:12:10 +11:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * has user activated? | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | bool GPU_select_query_check_active(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-11-24 02:20:38 -05:00
										 |  |  | 	return ((U.gpu_select_method == USER_SELECT_USE_OCCLUSION_QUERY) || | 
					
						
							| 
									
										
										
										
											2016-01-10 07:12:10 +11:00
										 |  |  | 	        ((U.gpu_select_method == USER_SELECT_AUTO) && | 
					
						
							| 
									
										
										
										
											2016-03-31 00:55:48 +11:00
										 |  |  | 	         (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY) || | 
					
						
							|  |  |  | 	          /* unsupported by nouveau, gallium 0.4, see: T47940 */ | 
					
						
							|  |  |  | 	          GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)))); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-23 15:24:07 +02:00
										 |  |  | } |