3d5899862817b2d736962053058419727e4c6e73
[blender.git] / source / blender / gpu / intern / gpu_select_sample_query.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_sample_query.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
33 #include <stdlib.h>
34
35 #include "GPU_select.h"
36 #include "GPU_extensions.h"
37 #include "GPU_glew.h"
38  
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_rect.h"
42
43 #include "BLI_utildefines.h"
44
45 #include "gpu_select_private.h"
46
47
48 /* Ad hoc number of queries to allocate to skip doing many glGenQueries */
49 #define ALLOC_QUERIES 200
50
51 typedef struct GPUQueryState {
52         /* Tracks whether a query has been issued so that gpu_load_id can end the previous one */
53         bool query_issued;
54         /* array holding the OpenGL query identifiers */
55         unsigned int *queries;
56         /* array holding the id corresponding to each query */
57         unsigned int *id;
58         /* number of queries in *queries and *id */
59         unsigned int num_of_queries;
60         /* index to the next query to start */
61         unsigned int active_query;
62         /* cache on initialization */
63         unsigned int (*buffer)[4];
64         /* buffer size (stores number of integers, for actual size multiply by sizeof integer)*/
65         unsigned int bufsize;
66         /* mode of operation */
67         char mode;
68         unsigned int index;
69         int oldhits;
70 } GPUQueryState;
71
72 static GPUQueryState g_query_state = {0};
73
74
75 void gpu_select_query_begin(
76         unsigned int (*buffer)[4], unsigned int bufsize,
77         const rcti *input, char mode,
78         int oldhits)
79 {
80         float viewport[4];
81
82         g_query_state.query_issued = false;
83         g_query_state.active_query = 0;
84         g_query_state.num_of_queries = 0;
85         g_query_state.bufsize = bufsize;
86         g_query_state.buffer = buffer;
87         g_query_state.mode = mode;
88         g_query_state.index = 0;
89         g_query_state.oldhits = oldhits;
90
91         g_query_state.num_of_queries = ALLOC_QUERIES;
92
93         g_query_state.queries = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.queries), "gpu selection queries");
94         g_query_state.id = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.id), "gpu selection ids");
95         glGenQueries(g_query_state.num_of_queries, g_query_state.queries);
96
97         glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_VIEWPORT_BIT);
98         /* disable writing to the framebuffer */
99         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
100
101         /* In order to save some fill rate we minimize the viewport using rect.
102          * We need to get the region of the scissor so that our geometry doesn't
103          * get rejected before the depth test. Should probably cull rect against
104          * scissor for viewport but this is a rare case I think */
105         glGetFloatv(GL_SCISSOR_BOX, viewport);
106         glViewport(viewport[0], viewport[1], BLI_rcti_size_x(input), BLI_rcti_size_y(input));
107
108         /* occlusion queries operates on fragments that pass tests and since we are interested on all
109          * objects in the view frustum independently of their order, we need to disable the depth test */
110         if (mode == GPU_SELECT_ALL) {
111                 glDisable(GL_DEPTH_TEST);
112                 glDepthMask(GL_FALSE);
113         }
114         else if (mode == GPU_SELECT_NEAREST_FIRST_PASS) {
115                 glClear(GL_DEPTH_BUFFER_BIT);
116                 glEnable(GL_DEPTH_TEST);
117                 glDepthMask(GL_TRUE);
118                 glDepthFunc(GL_LEQUAL);
119         }
120         else if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
121                 glEnable(GL_DEPTH_TEST);
122                 glDepthMask(GL_FALSE);
123                 glDepthFunc(GL_EQUAL);
124         }
125 }
126
127 bool gpu_select_query_load_id(unsigned int id)
128 {
129         if (g_query_state.query_issued) {
130                 glEndQuery(GL_SAMPLES_PASSED);
131         }
132         /* if required, allocate extra queries */
133         if (g_query_state.active_query == g_query_state.num_of_queries) {
134                 g_query_state.num_of_queries += ALLOC_QUERIES;
135                 g_query_state.queries = MEM_reallocN(g_query_state.queries, g_query_state.num_of_queries * sizeof(*g_query_state.queries));
136                 g_query_state.id = MEM_reallocN(g_query_state.id, g_query_state.num_of_queries * sizeof(*g_query_state.id));
137                 glGenQueries(ALLOC_QUERIES, &g_query_state.queries[g_query_state.active_query]);
138         }
139
140         glBeginQuery(GL_SAMPLES_PASSED, g_query_state.queries[g_query_state.active_query]);
141         g_query_state.id[g_query_state.active_query] = id;
142         g_query_state.active_query++;
143         g_query_state.query_issued = true;
144
145         if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS) {
146                 /* Second pass should never run if first pass fails, can read past 'bufsize' in this case. */
147                 BLI_assert(g_query_state.oldhits != -1);
148                 if (g_query_state.index < g_query_state.oldhits) {
149                         if (g_query_state.buffer[g_query_state.index][3] == id) {
150                                 g_query_state.index++;
151                                 return true;
152                         }
153                         else {
154                                 return false;
155                         }
156                 }
157         }
158
159         return true;
160 }
161
162 unsigned int gpu_select_query_end(void)
163 {
164         int i;
165
166         unsigned int hits = 0;
167         const unsigned int maxhits = g_query_state.bufsize;
168
169         if (g_query_state.query_issued) {
170                 glEndQuery(GL_SAMPLES_PASSED);
171         }
172
173         for (i = 0; i < g_query_state.active_query; i++) {
174                 unsigned int result;
175                 glGetQueryObjectuiv(g_query_state.queries[i], GL_QUERY_RESULT, &result);
176                 if (result > 0) {
177                         if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) {
178
179                                 if (hits < maxhits) {
180                                         g_query_state.buffer[hits][0] = 1;
181                                         g_query_state.buffer[hits][1] = 0xFFFF;
182                                         g_query_state.buffer[hits][2] = 0xFFFF;
183                                         g_query_state.buffer[hits][3] = g_query_state.id[i];
184
185                                         hits++;
186                                 }
187                                 else {
188                                         hits = -1;
189                                         break;
190                                 }
191                         }
192                         else {
193                                 int j;
194                                 /* search in buffer and make selected object first */
195                                 for (j = 0; j < g_query_state.oldhits; j++) {
196                                         if (g_query_state.buffer[j][3] == g_query_state.id[i]) {
197                                                 g_query_state.buffer[j][1] = 0;
198                                                 g_query_state.buffer[j][2] = 0;
199                                         }
200                                 }
201                                 break;
202                         }
203                 }
204         }
205
206         glDeleteQueries(g_query_state.num_of_queries, g_query_state.queries);
207         MEM_freeN(g_query_state.queries);
208         MEM_freeN(g_query_state.id);
209         glPopAttrib();
210         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
211
212         return hits;
213 }