Fix build errors
[blender.git] / source / blender / gpu / intern / gpu_select.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2014 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup gpu
22  *
23  * Interface for accessing gpu-related methods for selection. The semantics are
24  * similar to glRenderMode(GL_SELECT) from older OpenGL versions.
25  */
26 #include <string.h>
27 #include <stdlib.h>
28
29 #include "GPU_select.h"
30 #include "GPU_extensions.h"
31 #include "GPU_glew.h"
32
33 #include "MEM_guardedalloc.h"
34
35 #include "BLI_rect.h"
36
37 #include "DNA_userdef_types.h"
38
39 #include "BLI_utildefines.h"
40
41 #include "gpu_select_private.h"
42
43 /* Internal algorithm used */
44 enum {
45         /** glBegin/EndQuery(GL_SAMPLES_PASSED... ), `gpu_select_query.c`
46          * Only sets 4th component (ID) correctly. */
47         ALGO_GL_QUERY = 1,
48         /** Read depth buffer for every drawing pass and extract depths, `gpu_select_pick.c`
49          * Only sets 4th component (ID) correctly. */
50         ALGO_GL_PICK = 2,
51 };
52
53 typedef struct GPUSelectState {
54         /* To ignore selection id calls when not initialized */
55         bool select_is_active;
56         /* mode of operation */
57         char mode;
58         /* internal algorithm for selection */
59         char algorithm;
60         /* allow GPU_select_begin/end without drawing */
61         bool use_cache;
62 } GPUSelectState;
63
64 static GPUSelectState g_select_state = {0};
65
66 /**
67  * initialize and provide buffer for results
68  */
69 void GPU_select_begin(uint *buffer, uint bufsize, const rcti *input, char mode, int oldhits)
70 {
71         if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
72                 /* In the case hits was '-1', don't start the second pass since it's not going to give useful results.
73                  * As well as buffer overflow in 'gpu_select_query_load_id'. */
74                 BLI_assert(oldhits != -1);
75         }
76
77         g_select_state.select_is_active = true;
78         g_select_state.mode = mode;
79
80         if (ELEM(g_select_state.mode, GPU_SELECT_PICK_ALL, GPU_SELECT_PICK_NEAREST)) {
81                 g_select_state.algorithm = ALGO_GL_PICK;
82         }
83         else {
84                 g_select_state.algorithm = ALGO_GL_QUERY;
85         }
86
87         switch (g_select_state.algorithm) {
88                 case ALGO_GL_QUERY:
89                 {
90                         g_select_state.use_cache = false;
91                         gpu_select_query_begin((uint (*)[4])buffer, bufsize / 4, input, mode, oldhits);
92                         break;
93                 }
94                 default:  /* ALGO_GL_PICK */
95                 {
96                         gpu_select_pick_begin((uint (*)[4])buffer, bufsize / 4, input, mode);
97                         break;
98                 }
99         }
100 }
101
102 /**
103  * loads a new selection id and ends previous query, if any. In second pass of selection it also returns
104  * if id has been hit on the first pass already.
105  * Thus we can skip drawing un-hit objects.
106  *
107  * \warning We rely on the order of object rendering on passes to be the same for this to work.
108  */
109 bool GPU_select_load_id(uint id)
110 {
111         /* if no selection mode active, ignore */
112         if (!g_select_state.select_is_active)
113                 return true;
114
115         switch (g_select_state.algorithm) {
116                 case ALGO_GL_QUERY:
117                 {
118                         return gpu_select_query_load_id(id);
119                 }
120                 default:  /* ALGO_GL_PICK */
121                 {
122                         return gpu_select_pick_load_id(id);
123                 }
124         }
125 }
126
127 /**
128  * Cleanup and flush selection results to buffer.
129  * Return number of hits and hits in buffer.
130  * if \a dopass is true, we will do a second pass with occlusion queries to get the closest hit.
131  */
132 uint GPU_select_end(void)
133 {
134         uint hits = 0;
135
136         switch (g_select_state.algorithm) {
137                 case ALGO_GL_QUERY:
138                 {
139                         hits = gpu_select_query_end();
140                         break;
141                 }
142                 default:  /* ALGO_GL_PICK */
143                 {
144                         hits = gpu_select_pick_end();
145                         break;
146                 }
147         }
148
149         g_select_state.select_is_active = false;
150
151         return hits;
152 }
153
154 /* ----------------------------------------------------------------------------
155  * Caching
156  *
157  * Support multiple begin/end's as long as they are within the initial region.
158  * Currently only used by ALGO_GL_PICK.
159  */
160
161 void GPU_select_cache_begin(void)
162 {
163         /* validate on GPU_select_begin, clear if not supported */
164         BLI_assert(g_select_state.use_cache == false);
165         g_select_state.use_cache = true;
166         if (g_select_state.algorithm == ALGO_GL_PICK) {
167                 gpu_select_pick_cache_begin();
168         }
169 }
170
171 void GPU_select_cache_load_id(void)
172 {
173         BLI_assert(g_select_state.use_cache == true);
174         if (g_select_state.algorithm == ALGO_GL_PICK) {
175                 gpu_select_pick_cache_load_id();
176         }
177 }
178
179 void GPU_select_cache_end(void)
180 {
181         if (g_select_state.algorithm == ALGO_GL_PICK) {
182                 gpu_select_pick_cache_end();
183         }
184         g_select_state.use_cache = false;
185 }
186
187 bool GPU_select_is_cached(void)
188 {
189         return g_select_state.use_cache && gpu_select_pick_is_cached();
190 }
191
192
193 /* ----------------------------------------------------------------------------
194  * Utilities
195  */
196
197 /**
198  * Helper function, nothing special but avoids doing inline since hit's aren't sorted by depth
199  * and purpose of 4x buffer indices isn't so clear.
200  *
201  * Note that comparing depth as uint is fine.
202  */
203 const uint *GPU_select_buffer_near(const uint *buffer, int hits)
204 {
205         const uint *buffer_near = NULL;
206         uint depth_min = (uint) - 1;
207         for (int i = 0; i < hits; i++) {
208                 if (buffer[1] < depth_min) {
209                         BLI_assert(buffer[3] != -1);
210                         depth_min = buffer[1];
211                         buffer_near = buffer;
212                 }
213                 buffer += 4;
214         }
215         return buffer_near;
216 }
217
218 /* Part of the solution copied from `rect_subregion_stride_calc`. */
219 void GPU_select_buffer_stride_realign(
220         const rcti *src, const rcti *dst, uint *r_buf)
221 {
222         const int src_x = BLI_rcti_size_x(src);
223         // const int src_y = BLI_rcti_size_y(src);
224         int dst_x = BLI_rcti_size_x(dst);
225         int dst_y = BLI_rcti_size_y(dst);
226         int x = dst->xmin - src->xmin;
227         int y = dst->ymin - src->ymin;
228
229         BLI_assert(src->xmin <= dst->xmin && src->ymin <= dst->ymin &&
230                    src->xmax >= dst->xmax && src->ymax >= dst->ymax);
231         BLI_assert(x >= 0 && y >= 0);
232
233         int last_px_written = dst_x * dst_y - 1;
234         int last_px_id = src_x * (y + dst_y - 1) + (x + dst_x - 1);
235
236         int skip = src_x - dst_x;
237         while (dst_y--) {
238                 int i;
239                 for (i = dst_x; i--;) {
240                         r_buf[last_px_id--] = r_buf[last_px_written--];
241                 }
242                 if (last_px_written < 0) {
243                         break;
244                 }
245                 for (i = skip; i--;) {
246                         r_buf[last_px_id--] = 0u;
247                 }
248         }
249         memset(r_buf, 0, (last_px_id + 1) * sizeof(*r_buf));
250 }