51721426bca0b730a61d18e5c0866a4ba11beadd
[blender-staging.git] / source / blender / gpu / intern / gpu_select.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2014 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Antony Riakiotakis.
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/gpu/intern/gpu_select.c
27  *  \ingroup gpu
28  *
29  * Interface for accessing gpu-related methods for selection. The semantics will be
30  * similar to glRenderMode(GL_SELECT) since the goal is to maintain compatibility.
31  */
32 #include "GPU_select.h"
33 #include "GPU_extensions.h"
34 #include "GPU_glew.h"
35  
36 #include "MEM_guardedalloc.h"
37
38 #include "DNA_userdef_types.h"
39
40 #include "BLI_utildefines.h"
41
42 /* Ad hoc number of queries to allocate to skip doing many glGenQueries */
43 #define ALLOC_QUERIES 200
44
45 typedef struct GPUQueryState {
46         /* To ignore selection id calls when not initialized */
47         bool select_is_active;
48         /* Tracks whether a query has been issued so that gpu_load_id can end the previous one */
49         bool query_issued;
50         /* array holding the OpenGL query identifiers */
51         unsigned int *queries;
52         /* array holding the id corresponding to each query */
53         unsigned int *id;
54         /* number of queries in *queries and *id */
55         unsigned int num_of_queries;
56         /* index to the next query to start */
57         unsigned int active_query;
58         /* flag to cache user preference for occlusion based selection */
59         bool use_gpu_select;
60         /* cache on initialization */
61         unsigned int *buffer;
62         unsigned int bufsize;
63         /* mode of operation */
64         char mode;
65         unsigned int index;
66         int oldhits;
67 } GPUQueryState;
68
69 static GPUQueryState g_query_state = {0};
70
71 void GPU_select_begin(unsigned int *buffer, unsigned int bufsize, rctf *input, char mode, int oldhits)
72 {
73         g_query_state.select_is_active = true;
74         g_query_state.query_issued = false;
75         g_query_state.active_query = 0;
76         g_query_state.use_gpu_select = GPU_select_query_check_active();
77         g_query_state.num_of_queries = 0;
78         g_query_state.bufsize = bufsize;
79         g_query_state.buffer = buffer;
80         g_query_state.mode = mode;
81         g_query_state.index = 0;
82         g_query_state.oldhits = oldhits;
83
84         if (!g_query_state.use_gpu_select) {
85                 glSelectBuffer( bufsize, (GLuint *)buffer);
86                 glRenderMode(GL_SELECT);
87                 glInitNames();
88                 glPushName(-1);
89         }
90         else {
91                 float viewport[4];
92
93                 g_query_state.num_of_queries = ALLOC_QUERIES;
94
95                 g_query_state.queries = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.queries) , "gpu selection queries");
96                 g_query_state.id = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.id) , "gpu selection ids");
97                 glGenQueriesARB(g_query_state.num_of_queries, g_query_state.queries);
98
99                 glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_VIEWPORT_BIT);
100                 /* disable writing to the framebuffer */
101                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
102
103                 /* In order to save some fill rate we minimize the viewport using rect.
104                  * We need to get the region of the scissor so that our geometry doesn't
105                  * get rejected before the depth test. Should probably cull rect against
106                  * scissor for viewport but this is a rare case I think */
107                 glGetFloatv(GL_SCISSOR_BOX, viewport);
108                 if (!input || input->xmin == input->xmax) {
109                         glViewport(viewport[0], viewport[1], 24, 24);
110                 }
111                 else {
112                         glViewport(viewport[0], viewport[1], (int)(input->xmax - input->xmin), (int)(input->ymax - input->ymin));
113                 }
114
115                 /* occlusion queries operates on fragments that pass tests and since we are interested on all
116                  * objects in the view frustum independently of their order, we need to disable the depth test */
117                 if (mode == GPU_SELECT_ALL) {
118                         glDisable(GL_DEPTH_TEST);
119                         glDepthMask(GL_FALSE);
120                 }
121                 else if (mode == GPU_SELECT_NEAREST_FIRST_PASS) {
122                         glClear(GL_DEPTH_BUFFER_BIT);
123                         glEnable(GL_DEPTH_TEST);
124                         glDepthMask(GL_TRUE);
125                         glDepthFunc(GL_LEQUAL);
126                 }
127                 else if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
128                         glEnable(GL_DEPTH_TEST);
129                         glDepthMask(GL_FALSE);
130                         glDepthFunc(GL_EQUAL);
131                 }
132         }
133 }
134
135 bool GPU_select_load_id(unsigned int id)
136 {
137         /* if no selection mode active, ignore */
138         if (!g_query_state.select_is_active)
139                 return true;
140
141         if (!g_query_state.use_gpu_select) {
142                 glLoadName(id);
143         }
144         else {
145                 if (g_query_state.query_issued) {
146                         glEndQueryARB(GL_SAMPLES_PASSED_ARB);
147                 }
148                 /* if required, allocate extra queries */
149                 if (g_query_state.active_query == g_query_state.num_of_queries) {
150                         g_query_state.num_of_queries += ALLOC_QUERIES;
151                         g_query_state.queries = MEM_reallocN(g_query_state.queries, g_query_state.num_of_queries * sizeof(*g_query_state.queries));
152                         g_query_state.id = MEM_reallocN(g_query_state.id, g_query_state.num_of_queries * sizeof(*g_query_state.id));
153                         glGenQueriesARB(ALLOC_QUERIES, &g_query_state.queries[g_query_state.active_query]);
154                 }
155
156                 glBeginQueryARB(GL_SAMPLES_PASSED_ARB, g_query_state.queries[g_query_state.active_query]);
157                 g_query_state.id[g_query_state.active_query] = id;
158                 g_query_state.active_query++;
159                 g_query_state.query_issued = true;
160
161                 if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS && g_query_state.index < g_query_state.oldhits) {
162                         if (g_query_state.buffer[g_query_state.index * 4 + 3] == id) {
163                                 g_query_state.index++;
164                                 return true;
165                         }
166                         else {
167                                 return false;
168                         }
169                 }
170         }
171
172         return true;
173 }
174
175 unsigned int GPU_select_end(void)
176 {
177         unsigned int hits = 0;
178         if (!g_query_state.use_gpu_select) {
179                 glPopName();
180                 hits = glRenderMode(GL_RENDER);
181         }
182         else {
183                 int i;
184
185                 if (g_query_state.query_issued) {
186                         glEndQueryARB(GL_SAMPLES_PASSED_ARB);
187                 }
188
189                 for (i = 0; i < g_query_state.active_query; i++) {
190                         unsigned int result;
191                         glGetQueryObjectuivARB(g_query_state.queries[i], GL_QUERY_RESULT_ARB, &result);
192                         if (result > 0) {
193                                 if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) {
194                                         if (hits < g_query_state.bufsize) {
195                                                 g_query_state.buffer[hits * 4] = 1;
196                                                 g_query_state.buffer[hits * 4 + 1] = 0xFFFF;
197                                                 g_query_state.buffer[hits * 4 + 2] = 0xFFFF;
198                                                 g_query_state.buffer[hits * 4 + 3] = g_query_state.id[i];
199
200                                                 hits++;
201                                         }
202                                         else {
203                                                 hits = -1;
204                                                 break;
205                                         }
206                                 }
207                                 else {
208                                         int j;
209                                         /* search in buffer and make selected object first */
210                                         for (j = 0; j < g_query_state.oldhits; j++) {
211                                                 if (g_query_state.buffer[j * 4 + 3] == g_query_state.id[i]) {
212                                                         g_query_state.buffer[j * 4 + 1] = 0;
213                                                         g_query_state.buffer[j * 4 + 2] = 0;
214                                                 }
215                                         }
216                                         break;
217                                 }
218                         }
219                 }
220
221                 glDeleteQueriesARB(g_query_state.num_of_queries, g_query_state.queries);
222                 MEM_freeN(g_query_state.queries);
223                 MEM_freeN(g_query_state.id);
224                 glPopAttrib();
225                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
226         }
227
228         g_query_state.select_is_active = false;
229
230         return hits;
231 }
232
233
234 bool GPU_select_query_check_support(void)
235 {
236         return GLEW_ARB_occlusion_query;
237 }
238
239
240 bool GPU_select_query_check_active(void)
241 {
242         return GLEW_ARB_occlusion_query &&
243                ((U.gpu_select_method == USER_SELECT_USE_OCCLUSION_QUERY) ||
244                 ((U.gpu_select_method == USER_SELECT_AUTO) && GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)));
245 }