glthread: add marshal_call_after and remove custom glFlush and glEnable code
[mesa.git] / src / mesa / main / marshal.c
1 /*
2 * Copyright © 2012 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 /** \file marshal.c
25 *
26 * Custom functions for marshalling GL calls from the main thread to a worker
27 * thread when automatic code generation isn't appropriate.
28 */
29
30 #include "main/enums.h"
31 #include "main/macros.h"
32 #include "marshal.h"
33 #include "dispatch.h"
34
35 static inline void
36 _mesa_post_marshal_hook(struct gl_context *ctx)
37 {
38 /* This can be enabled for debugging whether a failure is a synchronization
39 * problem between the main thread and the worker thread, or a failure in
40 * how we actually marshal.
41 */
42 if (false)
43 _mesa_glthread_finish(ctx);
44 }
45
46
47 struct marshal_cmd_ShaderSource
48 {
49 struct marshal_cmd_base cmd_base;
50 GLuint shader;
51 GLsizei count;
52 /* Followed by GLint length[count], then the contents of all strings,
53 * concatenated.
54 */
55 };
56
57
58 void
59 _mesa_unmarshal_ShaderSource(struct gl_context *ctx,
60 const struct marshal_cmd_ShaderSource *cmd)
61 {
62 const GLint *cmd_length = (const GLint *) (cmd + 1);
63 const GLchar *cmd_strings = (const GLchar *) (cmd_length + cmd->count);
64 /* TODO: how to deal with malloc failure? */
65 const GLchar * *string = malloc(cmd->count * sizeof(const GLchar *));
66 int i;
67
68 for (i = 0; i < cmd->count; ++i) {
69 string[i] = cmd_strings;
70 cmd_strings += cmd_length[i];
71 }
72 CALL_ShaderSource(ctx->CurrentServerDispatch,
73 (cmd->shader, cmd->count, string, cmd_length));
74 free((void *)string);
75 }
76
77
78 static size_t
79 measure_ShaderSource_strings(GLsizei count, const GLchar * const *string,
80 const GLint *length_in, GLint *length_out)
81 {
82 int i;
83 size_t total_string_length = 0;
84
85 for (i = 0; i < count; ++i) {
86 if (length_in == NULL || length_in[i] < 0) {
87 if (string[i])
88 length_out[i] = strlen(string[i]);
89 } else {
90 length_out[i] = length_in[i];
91 }
92 total_string_length += length_out[i];
93 }
94 return total_string_length;
95 }
96
97
98 void GLAPIENTRY
99 _mesa_marshal_ShaderSource(GLuint shader, GLsizei count,
100 const GLchar * const *string, const GLint *length)
101 {
102 /* TODO: how to report an error if count < 0? */
103
104 GET_CURRENT_CONTEXT(ctx);
105 /* TODO: how to deal with malloc failure? */
106 const size_t fixed_cmd_size = sizeof(struct marshal_cmd_ShaderSource);
107 STATIC_ASSERT(sizeof(struct marshal_cmd_ShaderSource) % sizeof(GLint) == 0);
108 size_t length_size = count * sizeof(GLint);
109 GLint *length_tmp = malloc(length_size);
110 size_t total_string_length =
111 measure_ShaderSource_strings(count, string, length, length_tmp);
112 size_t total_cmd_size = fixed_cmd_size + length_size + total_string_length;
113
114 if (total_cmd_size <= MARSHAL_MAX_CMD_SIZE) {
115 struct marshal_cmd_ShaderSource *cmd =
116 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_ShaderSource,
117 total_cmd_size);
118 GLint *cmd_length = (GLint *) (cmd + 1);
119 GLchar *cmd_strings = (GLchar *) (cmd_length + count);
120 int i;
121
122 cmd->shader = shader;
123 cmd->count = count;
124 memcpy(cmd_length, length_tmp, length_size);
125 for (i = 0; i < count; ++i) {
126 memcpy(cmd_strings, string[i], cmd_length[i]);
127 cmd_strings += cmd_length[i];
128 }
129 _mesa_post_marshal_hook(ctx);
130 } else {
131 _mesa_glthread_finish(ctx);
132 CALL_ShaderSource(ctx->CurrentServerDispatch,
133 (shader, count, string, length_tmp));
134 }
135 free(length_tmp);
136 }
137
138
139 /* BindBufferBase: marshalled asynchronously */
140 struct marshal_cmd_BindBufferBase
141 {
142 struct marshal_cmd_base cmd_base;
143 GLenum target;
144 GLuint index;
145 GLuint buffer;
146 };
147
148 /** Tracks the current bindings for the vertex array and index array buffers.
149 *
150 * This is part of what we need to enable glthread on compat-GL contexts that
151 * happen to use VBOs, without also supporting the full tracking of VBO vs
152 * user vertex array bindings per attribute on each vertex array for
153 * determining what to upload at draw call time.
154 *
155 * Note that GL core makes it so that a buffer binding with an invalid handle
156 * in the "buffer" parameter will throw an error, and then a
157 * glVertexAttribPointer() that followsmight not end up pointing at a VBO.
158 * However, in GL core the draw call would throw an error as well, so we don't
159 * really care if our tracking is wrong for this case -- we never need to
160 * marshal user data for draw calls, and the unmarshal will just generate an
161 * error or not as appropriate.
162 *
163 * For compatibility GL, we do need to accurately know whether the draw call
164 * on the unmarshal side will dereference a user pointer or load data from a
165 * VBO per vertex. That would make it seem like we need to track whether a
166 * "buffer" is valid, so that we can know when an error will be generated
167 * instead of updating the binding. However, compat GL has the ridiculous
168 * feature that if you pass a bad name, it just gens a buffer object for you,
169 * so we escape without having to know if things are valid or not.
170 */
171 static void
172 track_vbo_binding(struct gl_context *ctx, GLenum target, GLuint buffer)
173 {
174 struct glthread_state *glthread = ctx->GLThread;
175
176 switch (target) {
177 case GL_ARRAY_BUFFER:
178 glthread->vertex_array_is_vbo = (buffer != 0);
179 break;
180 case GL_ELEMENT_ARRAY_BUFFER:
181 /* The current element array buffer binding is actually tracked in the
182 * vertex array object instead of the context, so this would need to
183 * change on vertex array object updates.
184 */
185 glthread->element_array_is_vbo = (buffer != 0);
186 break;
187 case GL_DRAW_INDIRECT_BUFFER:
188 glthread->draw_indirect_buffer_is_vbo = buffer != 0;
189 break;
190 }
191 }
192
193
194 struct marshal_cmd_BindBuffer
195 {
196 struct marshal_cmd_base cmd_base;
197 GLenum target;
198 GLuint buffer;
199 };
200
201 /**
202 * This is just like the code-generated glBindBuffer() support, except that we
203 * call track_vbo_binding().
204 */
205 void
206 _mesa_unmarshal_BindBuffer(struct gl_context *ctx,
207 const struct marshal_cmd_BindBuffer *cmd)
208 {
209 const GLenum target = cmd->target;
210 const GLuint buffer = cmd->buffer;
211 CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
212 }
213 void GLAPIENTRY
214 _mesa_marshal_BindBuffer(GLenum target, GLuint buffer)
215 {
216 GET_CURRENT_CONTEXT(ctx);
217 size_t cmd_size = sizeof(struct marshal_cmd_BindBuffer);
218 struct marshal_cmd_BindBuffer *cmd;
219 debug_print_marshal("BindBuffer");
220
221 track_vbo_binding(ctx, target, buffer);
222
223 if (cmd_size <= MARSHAL_MAX_CMD_SIZE) {
224 cmd = _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BindBuffer,
225 cmd_size);
226 cmd->target = target;
227 cmd->buffer = buffer;
228 _mesa_post_marshal_hook(ctx);
229 } else {
230 _mesa_glthread_finish(ctx);
231 CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
232 }
233 }
234
235 /* BufferData: marshalled asynchronously */
236 struct marshal_cmd_BufferData
237 {
238 struct marshal_cmd_base cmd_base;
239 GLenum target;
240 GLsizeiptr size;
241 GLenum usage;
242 bool data_null; /* If set, no data follows for "data" */
243 /* Next size bytes are GLubyte data[size] */
244 };
245
246 void
247 _mesa_unmarshal_BufferData(struct gl_context *ctx,
248 const struct marshal_cmd_BufferData *cmd)
249 {
250 const GLenum target = cmd->target;
251 const GLsizeiptr size = cmd->size;
252 const GLenum usage = cmd->usage;
253 const void *data;
254
255 if (cmd->data_null)
256 data = NULL;
257 else
258 data = (const void *) (cmd + 1);
259
260 CALL_BufferData(ctx->CurrentServerDispatch, (target, size, data, usage));
261 }
262
263 void GLAPIENTRY
264 _mesa_marshal_BufferData(GLenum target, GLsizeiptr size, const GLvoid * data,
265 GLenum usage)
266 {
267 GET_CURRENT_CONTEXT(ctx);
268 size_t cmd_size =
269 sizeof(struct marshal_cmd_BufferData) + (data ? size : 0);
270 debug_print_marshal("BufferData");
271
272 if (unlikely(size < 0)) {
273 _mesa_glthread_finish(ctx);
274 _mesa_error(ctx, GL_INVALID_VALUE, "BufferData(size < 0)");
275 return;
276 }
277
278 if (target != GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD &&
279 cmd_size <= MARSHAL_MAX_CMD_SIZE) {
280 struct marshal_cmd_BufferData *cmd =
281 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferData,
282 cmd_size);
283
284 cmd->target = target;
285 cmd->size = size;
286 cmd->usage = usage;
287 cmd->data_null = !data;
288 if (data) {
289 char *variable_data = (char *) (cmd + 1);
290 memcpy(variable_data, data, size);
291 }
292 _mesa_post_marshal_hook(ctx);
293 } else {
294 _mesa_glthread_finish(ctx);
295 CALL_BufferData(ctx->CurrentServerDispatch,
296 (target, size, data, usage));
297 }
298 }
299
300 /* BufferSubData: marshalled asynchronously */
301 struct marshal_cmd_BufferSubData
302 {
303 struct marshal_cmd_base cmd_base;
304 GLenum target;
305 GLintptr offset;
306 GLsizeiptr size;
307 /* Next size bytes are GLubyte data[size] */
308 };
309
310 void
311 _mesa_unmarshal_BufferSubData(struct gl_context *ctx,
312 const struct marshal_cmd_BufferSubData *cmd)
313 {
314 const GLenum target = cmd->target;
315 const GLintptr offset = cmd->offset;
316 const GLsizeiptr size = cmd->size;
317 const void *data = (const void *) (cmd + 1);
318
319 CALL_BufferSubData(ctx->CurrentServerDispatch,
320 (target, offset, size, data));
321 }
322
323 void GLAPIENTRY
324 _mesa_marshal_BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
325 const GLvoid * data)
326 {
327 GET_CURRENT_CONTEXT(ctx);
328 size_t cmd_size = sizeof(struct marshal_cmd_BufferSubData) + size;
329
330 debug_print_marshal("BufferSubData");
331 if (unlikely(size < 0)) {
332 _mesa_glthread_finish(ctx);
333 _mesa_error(ctx, GL_INVALID_VALUE, "BufferSubData(size < 0)");
334 return;
335 }
336
337 if (target != GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD &&
338 cmd_size <= MARSHAL_MAX_CMD_SIZE) {
339 struct marshal_cmd_BufferSubData *cmd =
340 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferSubData,
341 cmd_size);
342 cmd->target = target;
343 cmd->offset = offset;
344 cmd->size = size;
345 char *variable_data = (char *) (cmd + 1);
346 memcpy(variable_data, data, size);
347 _mesa_post_marshal_hook(ctx);
348 } else {
349 _mesa_glthread_finish(ctx);
350 CALL_BufferSubData(ctx->CurrentServerDispatch,
351 (target, offset, size, data));
352 }
353 }
354
355 /* NamedBufferData: marshalled asynchronously */
356 struct marshal_cmd_NamedBufferData
357 {
358 struct marshal_cmd_base cmd_base;
359 GLuint name;
360 GLsizei size;
361 GLenum usage;
362 bool data_null; /* If set, no data follows for "data" */
363 /* Next size bytes are GLubyte data[size] */
364 };
365
366 void
367 _mesa_unmarshal_NamedBufferData(struct gl_context *ctx,
368 const struct marshal_cmd_NamedBufferData *cmd)
369 {
370 const GLuint name = cmd->name;
371 const GLsizei size = cmd->size;
372 const GLenum usage = cmd->usage;
373 const void *data;
374
375 if (cmd->data_null)
376 data = NULL;
377 else
378 data = (const void *) (cmd + 1);
379
380 CALL_NamedBufferData(ctx->CurrentServerDispatch,
381 (name, size, data, usage));
382 }
383
384 void GLAPIENTRY
385 _mesa_marshal_NamedBufferData(GLuint buffer, GLsizeiptr size,
386 const GLvoid * data, GLenum usage)
387 {
388 GET_CURRENT_CONTEXT(ctx);
389 size_t cmd_size = sizeof(struct marshal_cmd_NamedBufferData) + (data ? size : 0);
390
391 debug_print_marshal("NamedBufferData");
392 if (unlikely(size < 0)) {
393 _mesa_glthread_finish(ctx);
394 _mesa_error(ctx, GL_INVALID_VALUE, "NamedBufferData(size < 0)");
395 return;
396 }
397
398 if (buffer > 0 && cmd_size <= MARSHAL_MAX_CMD_SIZE) {
399 struct marshal_cmd_NamedBufferData *cmd =
400 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_NamedBufferData,
401 cmd_size);
402 cmd->name = buffer;
403 cmd->size = size;
404 cmd->usage = usage;
405 cmd->data_null = !data;
406 if (data) {
407 char *variable_data = (char *) (cmd + 1);
408 memcpy(variable_data, data, size);
409 }
410 _mesa_post_marshal_hook(ctx);
411 } else {
412 _mesa_glthread_finish(ctx);
413 CALL_NamedBufferData(ctx->CurrentServerDispatch,
414 (buffer, size, data, usage));
415 }
416 }
417
418 /* NamedBufferSubData: marshalled asynchronously */
419 struct marshal_cmd_NamedBufferSubData
420 {
421 struct marshal_cmd_base cmd_base;
422 GLuint name;
423 GLintptr offset;
424 GLsizei size;
425 /* Next size bytes are GLubyte data[size] */
426 };
427
428 void
429 _mesa_unmarshal_NamedBufferSubData(struct gl_context *ctx,
430 const struct marshal_cmd_NamedBufferSubData *cmd)
431 {
432 const GLuint name = cmd->name;
433 const GLintptr offset = cmd->offset;
434 const GLsizei size = cmd->size;
435 const void *data = (const void *) (cmd + 1);
436
437 CALL_NamedBufferSubData(ctx->CurrentServerDispatch,
438 (name, offset, size, data));
439 }
440
441 void GLAPIENTRY
442 _mesa_marshal_NamedBufferSubData(GLuint buffer, GLintptr offset,
443 GLsizeiptr size, const GLvoid * data)
444 {
445 GET_CURRENT_CONTEXT(ctx);
446 size_t cmd_size = sizeof(struct marshal_cmd_NamedBufferSubData) + size;
447
448 debug_print_marshal("NamedBufferSubData");
449 if (unlikely(size < 0)) {
450 _mesa_glthread_finish(ctx);
451 _mesa_error(ctx, GL_INVALID_VALUE, "NamedBufferSubData(size < 0)");
452 return;
453 }
454
455 if (buffer > 0 && cmd_size <= MARSHAL_MAX_CMD_SIZE) {
456 struct marshal_cmd_NamedBufferSubData *cmd =
457 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_NamedBufferSubData,
458 cmd_size);
459 cmd->name = buffer;
460 cmd->offset = offset;
461 cmd->size = size;
462 char *variable_data = (char *) (cmd + 1);
463 memcpy(variable_data, data, size);
464 _mesa_post_marshal_hook(ctx);
465 } else {
466 _mesa_glthread_finish(ctx);
467 CALL_NamedBufferSubData(ctx->CurrentServerDispatch,
468 (buffer, offset, size, data));
469 }
470 }