Merge branch 'blender2.7'
[blender.git] / source / blender / gpu / intern / gpu_context.cpp
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) 2016 by Mike Erwin.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup gpu
22  *
23  * Manage GL vertex array IDs in a thread-safe way
24  * Use these instead of glGenBuffers & its friends
25  * - alloc must be called from a thread that is bound
26  *   to the context that will be used for drawing with
27  *   this vao.
28  * - free can be called from any thread
29  */
30
31 #include "BLI_assert.h"
32 #include "BLI_utildefines.h"
33
34 #include "GPU_context.h"
35 #include "GPU_framebuffer.h"
36
37 #include "gpu_batch_private.h"
38 #include "gpu_context_private.h"
39
40 #include <vector>
41 #include <string.h>
42 #include <pthread.h>
43 #include <mutex>
44 #include <unordered_set>
45
46 #if TRUST_NO_ONE
47 #if 0
48 extern "C" {
49 extern int BLI_thread_is_main(void); /* Blender-specific function */
50 }
51
52 static bool thread_is_main() {
53         /* "main" here means the GL context's thread */
54         return BLI_thread_is_main();
55 }
56 #endif
57 #endif
58
59 static std::vector<GLuint> orphaned_buffer_ids;
60 static std::vector<GLuint> orphaned_texture_ids;
61
62 static std::mutex orphans_mutex;
63
64 struct GPUContext {
65         GLuint default_vao;
66         GPUFrameBuffer *current_fbo;
67         std::unordered_set<GPUBatch *> batches; /* Batches that have VAOs from this context */
68 #ifdef DEBUG
69         std::unordered_set<GPUFrameBuffer *> framebuffers; /* Framebuffers that have FBO from this context */
70 #endif
71         std::vector<GLuint> orphaned_vertarray_ids;
72         std::vector<GLuint> orphaned_framebuffer_ids;
73         std::mutex orphans_mutex; /* todo: try spinlock instead */
74 #if TRUST_NO_ONE
75         pthread_t thread; /* Thread on which this context is active. */
76         bool thread_is_used;
77 #endif
78
79         GPUContext() {
80 #if TRUST_NO_ONE
81                 thread_is_used = false;
82 #endif
83                 current_fbo = 0;
84         }
85 };
86
87 #if defined(_MSC_VER) && (_MSC_VER == 1800)
88 #define thread_local __declspec(thread)
89 thread_local GPUContext *active_ctx = NULL;
90 #else
91 static thread_local GPUContext *active_ctx = NULL;
92 #endif
93
94 static void orphans_add(GPUContext *ctx, std::vector<GLuint> *orphan_list, GLuint id)
95 {
96         std::mutex *mutex = (ctx) ? &ctx->orphans_mutex : &orphans_mutex;
97
98         mutex->lock();
99         orphan_list->emplace_back(id);
100         mutex->unlock();
101 }
102
103 static void orphans_clear(GPUContext *ctx)
104 {
105         BLI_assert(ctx); /* need at least an active context */
106         BLI_assert(pthread_equal(pthread_self(), ctx->thread)); /* context has been activated by another thread! */
107
108         ctx->orphans_mutex.lock();
109         if (!ctx->orphaned_vertarray_ids.empty()) {
110                 uint orphan_len = (uint)ctx->orphaned_vertarray_ids.size();
111                 glDeleteVertexArrays(orphan_len, ctx->orphaned_vertarray_ids.data());
112                 ctx->orphaned_vertarray_ids.clear();
113         }
114         if (!ctx->orphaned_framebuffer_ids.empty()) {
115                 uint orphan_len = (uint)ctx->orphaned_framebuffer_ids.size();
116                 glDeleteFramebuffers(orphan_len, ctx->orphaned_framebuffer_ids.data());
117                 ctx->orphaned_framebuffer_ids.clear();
118         }
119
120         ctx->orphans_mutex.unlock();
121
122         orphans_mutex.lock();
123         if (!orphaned_buffer_ids.empty()) {
124                 uint orphan_len = (uint)orphaned_buffer_ids.size();
125                 glDeleteBuffers(orphan_len, orphaned_buffer_ids.data());
126                 orphaned_buffer_ids.clear();
127         }
128         if (!orphaned_texture_ids.empty()) {
129                 uint orphan_len = (uint)orphaned_texture_ids.size();
130                 glDeleteTextures(orphan_len, orphaned_texture_ids.data());
131                 orphaned_texture_ids.clear();
132         }
133         orphans_mutex.unlock();
134 }
135
136 GPUContext *GPU_context_create(void)
137 {
138         /* BLI_assert(thread_is_main()); */
139         GPUContext *ctx = new GPUContext;
140         glGenVertexArrays(1, &ctx->default_vao);
141         GPU_context_active_set(ctx);
142         return ctx;
143 }
144
145 /* to be called after GPU_context_active_set(ctx_to_destroy) */
146 void GPU_context_discard(GPUContext *ctx)
147 {
148         /* Make sure no other thread has locked it. */
149         BLI_assert(ctx == active_ctx);
150         BLI_assert(pthread_equal(pthread_self(), ctx->thread));
151         BLI_assert(ctx->orphaned_vertarray_ids.empty());
152 #ifdef DEBUG
153         /* For now don't allow GPUFrameBuffers to be reuse in another ctx. */
154         BLI_assert(ctx->framebuffers.empty());
155 #endif
156         /* delete remaining vaos */
157         while (!ctx->batches.empty()) {
158                 /* this removes the array entry */
159                 GPU_batch_vao_cache_clear(*ctx->batches.begin());
160         }
161         glDeleteVertexArrays(1, &ctx->default_vao);
162         delete ctx;
163         active_ctx = NULL;
164 }
165
166 /* ctx can be NULL */
167 void GPU_context_active_set(GPUContext *ctx)
168 {
169 #if TRUST_NO_ONE
170         if (active_ctx) {
171                 active_ctx->thread_is_used = false;
172         }
173         /* Make sure no other context is already bound to this thread. */
174         if (ctx) {
175                 /* Make sure no other thread has locked it. */
176                 assert(ctx->thread_is_used == false);
177                 ctx->thread = pthread_self();
178                 ctx->thread_is_used = true;
179         }
180 #endif
181         if (ctx) {
182                 orphans_clear(ctx);
183         }
184         active_ctx = ctx;
185 }
186
187 GPUContext *GPU_context_active_get(void)
188 {
189         return active_ctx;
190 }
191
192 GLuint GPU_vao_default(void)
193 {
194         BLI_assert(active_ctx); /* need at least an active context */
195         BLI_assert(pthread_equal(pthread_self(), active_ctx->thread)); /* context has been activated by another thread! */
196         return active_ctx->default_vao;
197 }
198
199 GLuint GPU_vao_alloc(void)
200 {
201         GLuint new_vao_id = 0;
202         orphans_clear(active_ctx);
203         glGenVertexArrays(1, &new_vao_id);
204         return new_vao_id;
205 }
206
207 GLuint GPU_fbo_alloc(void)
208 {
209         GLuint new_fbo_id = 0;
210         orphans_clear(active_ctx);
211         glGenFramebuffers(1, &new_fbo_id);
212         return new_fbo_id;
213 }
214
215 GLuint GPU_buf_alloc(void)
216 {
217         GLuint new_buffer_id = 0;
218         orphans_clear(active_ctx);
219         glGenBuffers(1, &new_buffer_id);
220         return new_buffer_id;
221 }
222
223 GLuint GPU_tex_alloc(void)
224 {
225         GLuint new_texture_id = 0;
226         orphans_clear(active_ctx);
227         glGenTextures(1, &new_texture_id);
228         return new_texture_id;
229 }
230
231 void GPU_vao_free(GLuint vao_id, GPUContext *ctx)
232 {
233         BLI_assert(ctx);
234         if (ctx == active_ctx) {
235                 glDeleteVertexArrays(1, &vao_id);
236         }
237         else {
238                 orphans_add(ctx, &ctx->orphaned_vertarray_ids, vao_id);
239         }
240 }
241
242 void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx)
243 {
244         BLI_assert(ctx);
245         if (ctx == active_ctx) {
246                 glDeleteFramebuffers(1, &fbo_id);
247         }
248         else {
249                 orphans_add(ctx, &ctx->orphaned_framebuffer_ids, fbo_id);
250         }
251 }
252
253 void GPU_buf_free(GLuint buf_id)
254 {
255         if (active_ctx) {
256                 glDeleteBuffers(1, &buf_id);
257         }
258         else {
259                 orphans_add(NULL, &orphaned_buffer_ids, buf_id);
260         }
261 }
262
263 void GPU_tex_free(GLuint tex_id)
264 {
265         if (active_ctx) {
266                 glDeleteTextures(1, &tex_id);
267         }
268         else {
269                 orphans_add(NULL, &orphaned_texture_ids, tex_id);
270         }
271 }
272
273 /* GPUBatch & GPUFrameBuffer contains respectively VAO & FBO indices
274  * which are not shared across contexts. So we need to keep track of
275  * ownership. */
276
277 void gpu_context_add_batch(GPUContext *ctx, GPUBatch *batch)
278 {
279         BLI_assert(ctx);
280         ctx->orphans_mutex.lock();
281         ctx->batches.emplace(batch);
282         ctx->orphans_mutex.unlock();
283 }
284
285 void gpu_context_remove_batch(GPUContext *ctx, GPUBatch *batch)
286 {
287         BLI_assert(ctx);
288         ctx->orphans_mutex.lock();
289         ctx->batches.erase(batch);
290         ctx->orphans_mutex.unlock();
291 }
292
293 void gpu_context_add_framebuffer(GPUContext *ctx, GPUFrameBuffer *fb)
294 {
295 #ifdef DEBUG
296         BLI_assert(ctx);
297         ctx->orphans_mutex.lock();
298         ctx->framebuffers.emplace(fb);
299         ctx->orphans_mutex.unlock();
300 #else
301         UNUSED_VARS(ctx, fb);
302 #endif
303 }
304
305 void gpu_context_remove_framebuffer(GPUContext *ctx, GPUFrameBuffer *fb)
306 {
307 #ifdef DEBUG
308         BLI_assert(ctx);
309         ctx->orphans_mutex.lock();
310         ctx->framebuffers.erase(fb);
311         ctx->orphans_mutex.unlock();
312 #else
313         UNUSED_VARS(ctx, fb);
314 #endif
315 }
316
317 void gpu_context_active_framebuffer_set(GPUContext *ctx, GPUFrameBuffer *fb)
318 {
319         ctx->current_fbo = fb;
320 }
321
322 GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx)
323 {
324         return ctx->current_fbo;
325 }