Merge branch 'master' into blender2.8
[blender.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         /* buffer size (stores number of integers, for actual size multiply by sizeof integer)*/
63         unsigned int bufsize;
64         /* mode of operation */
65         char mode;
66         unsigned int index;
67         int oldhits;
68 } GPUQueryState;
69
70 static GPUQueryState g_query_state = {0};
71
72 /**
73  * initialize and provide buffer for results
74  */
75 void GPU_select_begin(unsigned int *buffer, unsigned int bufsize, rctf *input, char mode, int oldhits)
76 {
77         g_query_state.select_is_active = true;
78         g_query_state.query_issued = false;
79         g_query_state.active_query = 0;
80         g_query_state.use_gpu_select = GPU_select_query_check_active();
81         g_query_state.num_of_queries = 0;
82         g_query_state.bufsize = bufsize;
83         g_query_state.buffer = buffer;
84         g_query_state.mode = mode;
85         g_query_state.index = 0;
86         g_query_state.oldhits = oldhits;
87
88         if (!g_query_state.use_gpu_select) {
89                 glSelectBuffer(bufsize, (GLuint *)buffer);
90                 glRenderMode(GL_SELECT);
91                 glInitNames();
92                 glPushName(-1);
93         }
94         else {
95                 float viewport[4];
96
97                 g_query_state.num_of_queries = ALLOC_QUERIES;
98
99                 g_query_state.queries = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.queries), "gpu selection queries");
100                 g_query_state.id = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.id), "gpu selection ids");
101                 glGenQueries(g_query_state.num_of_queries, g_query_state.queries);
102
103                 glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_VIEWPORT_BIT);
104                 /* disable writing to the framebuffer */
105                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
106
107                 /* In order to save some fill rate we minimize the viewport using rect.
108                  * We need to get the region of the scissor so that our geometry doesn't
109                  * get rejected before the depth test. Should probably cull rect against
110                  * scissor for viewport but this is a rare case I think */
111                 glGetFloatv(GL_SCISSOR_BOX, viewport);
112                 if (!input || input->xmin == input->xmax) {
113                         glViewport(viewport[0], viewport[1], 24, 24);
114                 }
115                 else {
116                         glViewport(viewport[0], viewport[1], (int)(input->xmax - input->xmin), (int)(input->ymax - input->ymin));
117                 }
118
119                 /* occlusion queries operates on fragments that pass tests and since we are interested on all
120                  * objects in the view frustum independently of their order, we need to disable the depth test */
121                 if (mode == GPU_SELECT_ALL) {
122                         glDisable(GL_DEPTH_TEST);
123                         glDepthMask(GL_FALSE);
124                 }
125                 else if (mode == GPU_SELECT_NEAREST_FIRST_PASS) {
126                         glClear(GL_DEPTH_BUFFER_BIT);
127                         glEnable(GL_DEPTH_TEST);
128                         glDepthMask(GL_TRUE);
129                         glDepthFunc(GL_LEQUAL);
130                 }
131                 else if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
132                         glEnable(GL_DEPTH_TEST);
133                         glDepthMask(GL_FALSE);
134                         glDepthFunc(GL_EQUAL);
135                 }
136         }
137 }
138
139 /**
140  * loads a new selection id and ends previous query, if any. In second pass of selection it also returns
141  * if id has been hit on the first pass already.
142  * Thus we can skip drawing un-hit objects.
143  *
144  * \warning We rely on the order of object rendering on passes to be the same for this to work.
145  */
146 bool GPU_select_load_id(unsigned int id)
147 {
148         /* if no selection mode active, ignore */
149         if (!g_query_state.select_is_active)
150                 return true;
151
152         if (!g_query_state.use_gpu_select) {
153                 glLoadName(id);
154         }
155         else {
156                 if (g_query_state.query_issued) {
157                         glEndQuery(GL_SAMPLES_PASSED);
158                 }
159                 /* if required, allocate extra queries */
160                 if (g_query_state.active_query == g_query_state.num_of_queries) {
161                         g_query_state.num_of_queries += ALLOC_QUERIES;
162                         g_query_state.queries = MEM_reallocN(g_query_state.queries, g_query_state.num_of_queries * sizeof(*g_query_state.queries));
163                         g_query_state.id = MEM_reallocN(g_query_state.id, g_query_state.num_of_queries * sizeof(*g_query_state.id));
164                         glGenQueries(ALLOC_QUERIES, &g_query_state.queries[g_query_state.active_query]);
165                 }
166
167                 glBeginQuery(GL_SAMPLES_PASSED, g_query_state.queries[g_query_state.active_query]);
168                 g_query_state.id[g_query_state.active_query] = id;
169                 g_query_state.active_query++;
170                 g_query_state.query_issued = true;
171
172                 if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS && g_query_state.index < g_query_state.oldhits) {
173                         if (g_query_state.buffer[g_query_state.index * 4 + 3] == id) {
174                                 g_query_state.index++;
175                                 return true;
176                         }
177                         else {
178                                 return false;
179                         }
180                 }
181         }
182
183         return true;
184 }
185
186 /**
187  * Cleanup and flush selection results to buffer.
188  * Return number of hits and hits in buffer.
189  * if \a dopass is true, we will do a second pass with occlusion queries to get the closest hit.
190  */
191 unsigned int GPU_select_end(void)
192 {
193         unsigned int hits = 0;
194         if (!g_query_state.use_gpu_select) {
195                 glPopName();
196                 hits = glRenderMode(GL_RENDER);
197         }
198         else {
199                 int i;
200
201                 if (g_query_state.query_issued) {
202                         glEndQuery(GL_SAMPLES_PASSED);
203                 }
204
205                 for (i = 0; i < g_query_state.active_query; i++) {
206                         unsigned int result;
207                         glGetQueryObjectuiv(g_query_state.queries[i], GL_QUERY_RESULT, &result);
208                         if (result > 0) {
209                                 if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) {
210                                         int maxhits = g_query_state.bufsize / 4;
211
212                                         if (hits < maxhits) {
213                                                 g_query_state.buffer[hits * 4] = 1;
214                                                 g_query_state.buffer[hits * 4 + 1] = 0xFFFF;
215                                                 g_query_state.buffer[hits * 4 + 2] = 0xFFFF;
216                                                 g_query_state.buffer[hits * 4 + 3] = g_query_state.id[i];
217
218                                                 hits++;
219                                         }
220                                         else {
221                                                 hits = -1;
222                                                 break;
223                                         }
224                                 }
225                                 else {
226                                         int j;
227                                         /* search in buffer and make selected object first */
228                                         for (j = 0; j < g_query_state.oldhits; j++) {
229                                                 if (g_query_state.buffer[j * 4 + 3] == g_query_state.id[i]) {
230                                                         g_query_state.buffer[j * 4 + 1] = 0;
231                                                         g_query_state.buffer[j * 4 + 2] = 0;
232                                                 }
233                                         }
234                                         break;
235                                 }
236                         }
237                 }
238
239                 glDeleteQueries(g_query_state.num_of_queries, g_query_state.queries);
240                 MEM_freeN(g_query_state.queries);
241                 MEM_freeN(g_query_state.id);
242                 glPopAttrib();
243                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
244         }
245
246         g_query_state.select_is_active = false;
247
248         return hits;
249 }
250
251 /**
252  * has user activated?
253  */
254 bool GPU_select_query_check_active(void)
255 {
256         return ((U.gpu_select_method == USER_SELECT_USE_OCCLUSION_QUERY) ||
257                 ((U.gpu_select_method == USER_SELECT_AUTO) &&
258                  (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY) ||
259                   /* unsupported by nouveau, gallium 0.4, see: T47940 */
260                   GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE))));
261
262 }