Cleanup: comments (long lines) in gpu
[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',
73      * don't start the second pass since it's not going to give useful results.
74      * As well as buffer overflow in 'gpu_select_query_load_id'. */
75     BLI_assert(oldhits != -1);
76   }
77
78   g_select_state.select_is_active = true;
79   g_select_state.mode = mode;
80
81   if (ELEM(g_select_state.mode, GPU_SELECT_PICK_ALL, GPU_SELECT_PICK_NEAREST)) {
82     g_select_state.algorithm = ALGO_GL_PICK;
83   }
84   else {
85     g_select_state.algorithm = ALGO_GL_QUERY;
86   }
87
88   switch (g_select_state.algorithm) {
89     case ALGO_GL_QUERY: {
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.
104  * In second pass of selection it also returns
105  * if id has been hit on the first pass already.
106  * Thus we can skip drawing un-hit objects.
107  *
108  * \warning We rely on the order of object rendering on passes to be the same for this to work.
109  */
110 bool GPU_select_load_id(uint id)
111 {
112   /* if no selection mode active, ignore */
113   if (!g_select_state.select_is_active)
114     return true;
115
116   switch (g_select_state.algorithm) {
117     case ALGO_GL_QUERY: {
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       hits = gpu_select_query_end();
139       break;
140     }
141     default: /* ALGO_GL_PICK */
142     {
143       hits = gpu_select_pick_end();
144       break;
145     }
146   }
147
148   g_select_state.select_is_active = false;
149
150   return hits;
151 }
152
153 /* ----------------------------------------------------------------------------
154  * Caching
155  *
156  * Support multiple begin/end's as long as they are within the initial region.
157  * Currently only used by ALGO_GL_PICK.
158  */
159
160 void GPU_select_cache_begin(void)
161 {
162   /* validate on GPU_select_begin, clear if not supported */
163   BLI_assert(g_select_state.use_cache == false);
164   g_select_state.use_cache = true;
165   if (g_select_state.algorithm == ALGO_GL_PICK) {
166     gpu_select_pick_cache_begin();
167   }
168 }
169
170 void GPU_select_cache_load_id(void)
171 {
172   BLI_assert(g_select_state.use_cache == true);
173   if (g_select_state.algorithm == ALGO_GL_PICK) {
174     gpu_select_pick_cache_load_id();
175   }
176 }
177
178 void GPU_select_cache_end(void)
179 {
180   if (g_select_state.algorithm == ALGO_GL_PICK) {
181     gpu_select_pick_cache_end();
182   }
183   g_select_state.use_cache = false;
184 }
185
186 bool GPU_select_is_cached(void)
187 {
188   return g_select_state.use_cache && gpu_select_pick_is_cached();
189 }
190
191 /* ----------------------------------------------------------------------------
192  * Utilities
193  */
194
195 /**
196  * Helper function, nothing special but avoids doing inline since hit's aren't sorted by depth
197  * and purpose of 4x buffer indices isn't so clear.
198  *
199  * Note that comparing depth as uint is fine.
200  */
201 const uint *GPU_select_buffer_near(const uint *buffer, int hits)
202 {
203   const uint *buffer_near = NULL;
204   uint depth_min = (uint)-1;
205   for (int i = 0; i < hits; i++) {
206     if (buffer[1] < depth_min) {
207       BLI_assert(buffer[3] != -1);
208       depth_min = buffer[1];
209       buffer_near = buffer;
210     }
211     buffer += 4;
212   }
213   return buffer_near;
214 }
215
216 /* Part of the solution copied from `rect_subregion_stride_calc`. */
217 void GPU_select_buffer_stride_realign(const rcti *src, const rcti *dst, uint *r_buf)
218 {
219   const int x = dst->xmin - src->xmin;
220   const int y = dst->ymin - src->ymin;
221
222   BLI_assert(src->xmin <= dst->xmin && src->ymin <= dst->ymin && src->xmax >= dst->xmax &&
223              src->ymax >= dst->ymax);
224   BLI_assert(x >= 0 && y >= 0);
225
226   const int src_x = BLI_rcti_size_x(src);
227   const int src_y = BLI_rcti_size_y(src);
228   const int dst_x = BLI_rcti_size_x(dst);
229   const int dst_y = BLI_rcti_size_y(dst);
230
231   int last_px_written = dst_x * dst_y - 1;
232   int last_px_id = src_x * (y + dst_y - 1) + (x + dst_x - 1);
233   const int skip = src_x - dst_x;
234
235   memset(&r_buf[last_px_id + 1], 0, (src_x * src_y - (last_px_id + 1)) * sizeof(*r_buf));
236
237   while (true) {
238     for (int i = dst_x; i--;) {
239       r_buf[last_px_id--] = r_buf[last_px_written--];
240     }
241     if (last_px_written < 0) {
242       break;
243     }
244     last_px_id -= skip;
245     memset(&r_buf[last_px_id + 1], 0, skip * sizeof(*r_buf));
246   }
247   memset(r_buf, 0, (last_px_id + 1) * sizeof(*r_buf));
248 }