mesa/marshal: extract ClearBuffer helpers
[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 #include "marshal_generated.h"
35
36 struct marshal_cmd_Flush
37 {
38 struct marshal_cmd_base cmd_base;
39 };
40
41
42 void
43 _mesa_unmarshal_Flush(struct gl_context *ctx,
44 const struct marshal_cmd_Flush *cmd)
45 {
46 CALL_Flush(ctx->CurrentServerDispatch, ());
47 }
48
49
50 void GLAPIENTRY
51 _mesa_marshal_Flush(void)
52 {
53 GET_CURRENT_CONTEXT(ctx);
54 struct marshal_cmd_Flush *cmd =
55 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_Flush,
56 sizeof(struct marshal_cmd_Flush));
57 (void) cmd;
58 _mesa_post_marshal_hook(ctx);
59
60 /* Flush() needs to be handled specially. In addition to telling the
61 * background thread to flush, we need to ensure that our own buffer is
62 * submitted to the background thread so that it will complete in a finite
63 * amount of time.
64 */
65 _mesa_glthread_flush_batch(ctx);
66 }
67
68 /* Enable: marshalled asynchronously */
69 struct marshal_cmd_Enable
70 {
71 struct marshal_cmd_base cmd_base;
72 GLenum cap;
73 };
74
75 void
76 _mesa_unmarshal_Enable(struct gl_context *ctx,
77 const struct marshal_cmd_Enable *cmd)
78 {
79 const GLenum cap = cmd->cap;
80 CALL_Enable(ctx->CurrentServerDispatch, (cap));
81 }
82
83 void GLAPIENTRY
84 _mesa_marshal_Enable(GLenum cap)
85 {
86 GET_CURRENT_CONTEXT(ctx);
87 struct marshal_cmd_Enable *cmd;
88 debug_print_marshal("Enable");
89
90 if (cap == GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) {
91 _mesa_glthread_finish(ctx);
92 _mesa_glthread_restore_dispatch(ctx);
93 } else {
94 cmd = _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_Enable,
95 sizeof(*cmd));
96 cmd->cap = cap;
97 _mesa_post_marshal_hook(ctx);
98 return;
99 }
100
101 _mesa_glthread_finish(ctx);
102 debug_print_sync_fallback("Enable");
103 CALL_Enable(ctx->CurrentServerDispatch, (cap));
104 }
105
106 struct marshal_cmd_ShaderSource
107 {
108 struct marshal_cmd_base cmd_base;
109 GLuint shader;
110 GLsizei count;
111 /* Followed by GLint length[count], then the contents of all strings,
112 * concatenated.
113 */
114 };
115
116
117 void
118 _mesa_unmarshal_ShaderSource(struct gl_context *ctx,
119 const struct marshal_cmd_ShaderSource *cmd)
120 {
121 const GLint *cmd_length = (const GLint *) (cmd + 1);
122 const GLchar *cmd_strings = (const GLchar *) (cmd_length + cmd->count);
123 /* TODO: how to deal with malloc failure? */
124 const GLchar * *string = malloc(cmd->count * sizeof(const GLchar *));
125 int i;
126
127 for (i = 0; i < cmd->count; ++i) {
128 string[i] = cmd_strings;
129 cmd_strings += cmd_length[i];
130 }
131 CALL_ShaderSource(ctx->CurrentServerDispatch,
132 (cmd->shader, cmd->count, string, cmd_length));
133 free(string);
134 }
135
136
137 static size_t
138 measure_ShaderSource_strings(GLsizei count, const GLchar * const *string,
139 const GLint *length_in, GLint *length_out)
140 {
141 int i;
142 size_t total_string_length = 0;
143
144 for (i = 0; i < count; ++i) {
145 if (length_in == NULL || length_in[i] < 0) {
146 if (string[i])
147 length_out[i] = strlen(string[i]);
148 } else {
149 length_out[i] = length_in[i];
150 }
151 total_string_length += length_out[i];
152 }
153 return total_string_length;
154 }
155
156
157 void GLAPIENTRY
158 _mesa_marshal_ShaderSource(GLuint shader, GLsizei count,
159 const GLchar * const *string, const GLint *length)
160 {
161 /* TODO: how to report an error if count < 0? */
162
163 GET_CURRENT_CONTEXT(ctx);
164 /* TODO: how to deal with malloc failure? */
165 const size_t fixed_cmd_size = sizeof(struct marshal_cmd_ShaderSource);
166 STATIC_ASSERT(sizeof(struct marshal_cmd_ShaderSource) % sizeof(GLint) == 0);
167 size_t length_size = count * sizeof(GLint);
168 GLint *length_tmp = malloc(length_size);
169 size_t total_string_length =
170 measure_ShaderSource_strings(count, string, length, length_tmp);
171 size_t total_cmd_size = fixed_cmd_size + length_size + total_string_length;
172
173 if (total_cmd_size <= MARSHAL_MAX_CMD_SIZE) {
174 struct marshal_cmd_ShaderSource *cmd =
175 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_ShaderSource,
176 total_cmd_size);
177 GLint *cmd_length = (GLint *) (cmd + 1);
178 GLchar *cmd_strings = (GLchar *) (cmd_length + count);
179 int i;
180
181 cmd->shader = shader;
182 cmd->count = count;
183 memcpy(cmd_length, length_tmp, length_size);
184 for (i = 0; i < count; ++i) {
185 memcpy(cmd_strings, string[i], cmd_length[i]);
186 cmd_strings += cmd_length[i];
187 }
188 _mesa_post_marshal_hook(ctx);
189 } else {
190 _mesa_glthread_finish(ctx);
191 CALL_ShaderSource(ctx->CurrentServerDispatch,
192 (shader, count, string, length_tmp));
193 }
194 free(length_tmp);
195 }
196
197
198 /* BindBufferBase: marshalled asynchronously */
199 struct marshal_cmd_BindBufferBase
200 {
201 struct marshal_cmd_base cmd_base;
202 GLenum target;
203 GLuint index;
204 GLuint buffer;
205 };
206
207 /** Tracks the current bindings for the vertex array and index array buffers.
208 *
209 * This is part of what we need to enable glthread on compat-GL contexts that
210 * happen to use VBOs, without also supporting the full tracking of VBO vs
211 * user vertex array bindings per attribute on each vertex array for
212 * determining what to upload at draw call time.
213 *
214 * Note that GL core makes it so that a buffer binding with an invalid handle
215 * in the "buffer" parameter will throw an error, and then a
216 * glVertexAttribPointer() that followsmight not end up pointing at a VBO.
217 * However, in GL core the draw call would throw an error as well, so we don't
218 * really care if our tracking is wrong for this case -- we never need to
219 * marshal user data for draw calls, and the unmarshal will just generate an
220 * error or not as appropriate.
221 *
222 * For compatibility GL, we do need to accurately know whether the draw call
223 * on the unmarshal side will dereference a user pointer or load data from a
224 * VBO per vertex. That would make it seem like we need to track whether a
225 * "buffer" is valid, so that we can know when an error will be generated
226 * instead of updating the binding. However, compat GL has the ridiculous
227 * feature that if you pass a bad name, it just gens a buffer object for you,
228 * so we escape without having to know if things are valid or not.
229 */
230 static void
231 track_vbo_binding(struct gl_context *ctx, GLenum target, GLuint buffer)
232 {
233 struct glthread_state *glthread = ctx->GLThread;
234
235 switch (target) {
236 case GL_ARRAY_BUFFER:
237 glthread->vertex_array_is_vbo = (buffer != 0);
238 break;
239 case GL_ELEMENT_ARRAY_BUFFER:
240 /* The current element array buffer binding is actually tracked in the
241 * vertex array object instead of the context, so this would need to
242 * change on vertex array object updates.
243 */
244 glthread->element_array_is_vbo = (buffer != 0);
245 break;
246 }
247 }
248
249
250 struct marshal_cmd_BindBuffer
251 {
252 struct marshal_cmd_base cmd_base;
253 GLenum target;
254 GLuint buffer;
255 };
256
257 /**
258 * This is just like the code-generated glBindBuffer() support, except that we
259 * call track_vbo_binding().
260 */
261 void
262 _mesa_unmarshal_BindBuffer(struct gl_context *ctx,
263 const struct marshal_cmd_BindBuffer *cmd)
264 {
265 const GLenum target = cmd->target;
266 const GLuint buffer = cmd->buffer;
267 CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
268 }
269 void GLAPIENTRY
270 _mesa_marshal_BindBuffer(GLenum target, GLuint buffer)
271 {
272 GET_CURRENT_CONTEXT(ctx);
273 size_t cmd_size = sizeof(struct marshal_cmd_BindBuffer);
274 struct marshal_cmd_BindBuffer *cmd;
275 debug_print_marshal("BindBuffer");
276
277 track_vbo_binding(ctx, target, buffer);
278
279 if (cmd_size <= MARSHAL_MAX_CMD_SIZE) {
280 cmd = _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BindBuffer,
281 cmd_size);
282 cmd->target = target;
283 cmd->buffer = buffer;
284 _mesa_post_marshal_hook(ctx);
285 } else {
286 _mesa_glthread_finish(ctx);
287 CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
288 }
289 }
290
291 /* BufferData: marshalled asynchronously */
292 struct marshal_cmd_BufferData
293 {
294 struct marshal_cmd_base cmd_base;
295 GLenum target;
296 GLsizeiptr size;
297 GLenum usage;
298 bool data_null; /* If set, no data follows for "data" */
299 /* Next size bytes are GLubyte data[size] */
300 };
301
302 void
303 _mesa_unmarshal_BufferData(struct gl_context *ctx,
304 const struct marshal_cmd_BufferData *cmd)
305 {
306 const GLenum target = cmd->target;
307 const GLsizeiptr size = cmd->size;
308 const GLenum usage = cmd->usage;
309 const void *data;
310
311 if (cmd->data_null)
312 data = NULL;
313 else
314 data = (const void *) (cmd + 1);
315
316 CALL_BufferData(ctx->CurrentServerDispatch, (target, size, data, usage));
317 }
318
319 void GLAPIENTRY
320 _mesa_marshal_BufferData(GLenum target, GLsizeiptr size, const GLvoid * data,
321 GLenum usage)
322 {
323 GET_CURRENT_CONTEXT(ctx);
324 size_t cmd_size =
325 sizeof(struct marshal_cmd_BufferData) + (data ? size : 0);
326 debug_print_marshal("BufferData");
327
328 if (unlikely(size < 0)) {
329 _mesa_glthread_finish(ctx);
330 _mesa_error(ctx, GL_INVALID_VALUE, "BufferData(size < 0)");
331 return;
332 }
333
334 if (target != GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD &&
335 cmd_size <= MARSHAL_MAX_CMD_SIZE) {
336 struct marshal_cmd_BufferData *cmd =
337 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferData,
338 cmd_size);
339
340 cmd->target = target;
341 cmd->size = size;
342 cmd->usage = usage;
343 cmd->data_null = !data;
344 if (data) {
345 char *variable_data = (char *) (cmd + 1);
346 memcpy(variable_data, data, size);
347 }
348 _mesa_post_marshal_hook(ctx);
349 } else {
350 _mesa_glthread_finish(ctx);
351 CALL_BufferData(ctx->CurrentServerDispatch,
352 (target, size, data, usage));
353 }
354 }
355
356 /* BufferSubData: marshalled asynchronously */
357 struct marshal_cmd_BufferSubData
358 {
359 struct marshal_cmd_base cmd_base;
360 GLenum target;
361 GLintptr offset;
362 GLsizeiptr size;
363 /* Next size bytes are GLubyte data[size] */
364 };
365
366 void
367 _mesa_unmarshal_BufferSubData(struct gl_context *ctx,
368 const struct marshal_cmd_BufferSubData *cmd)
369 {
370 const GLenum target = cmd->target;
371 const GLintptr offset = cmd->offset;
372 const GLsizeiptr size = cmd->size;
373 const void *data = (const void *) (cmd + 1);
374
375 CALL_BufferSubData(ctx->CurrentServerDispatch,
376 (target, offset, size, data));
377 }
378
379 void GLAPIENTRY
380 _mesa_marshal_BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
381 const GLvoid * data)
382 {
383 GET_CURRENT_CONTEXT(ctx);
384 size_t cmd_size = sizeof(struct marshal_cmd_BufferSubData) + size;
385
386 debug_print_marshal("BufferSubData");
387 if (unlikely(size < 0)) {
388 _mesa_glthread_finish(ctx);
389 _mesa_error(ctx, GL_INVALID_VALUE, "BufferSubData(size < 0)");
390 return;
391 }
392
393 if (target != GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD &&
394 cmd_size <= MARSHAL_MAX_CMD_SIZE) {
395 struct marshal_cmd_BufferSubData *cmd =
396 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferSubData,
397 cmd_size);
398 cmd->target = target;
399 cmd->offset = offset;
400 cmd->size = size;
401 char *variable_data = (char *) (cmd + 1);
402 memcpy(variable_data, data, size);
403 _mesa_post_marshal_hook(ctx);
404 } else {
405 _mesa_glthread_finish(ctx);
406 CALL_BufferSubData(ctx->CurrentServerDispatch,
407 (target, offset, size, data));
408 }
409 }
410
411 /* NamedBufferData: marshalled asynchronously */
412 struct marshal_cmd_NamedBufferData
413 {
414 struct marshal_cmd_base cmd_base;
415 GLuint name;
416 GLsizei size;
417 GLenum usage;
418 /* Next size bytes are GLubyte data[size] */
419 };
420
421 void
422 _mesa_unmarshal_NamedBufferData(struct gl_context *ctx,
423 const struct marshal_cmd_NamedBufferData *cmd)
424 {
425 const GLuint name = cmd->name;
426 const GLsizei size = cmd->size;
427 const GLenum usage = cmd->usage;
428 const void *data = (const void *) (cmd + 1);
429
430 CALL_NamedBufferData(ctx->CurrentServerDispatch,
431 (name, size, data, usage));
432 }
433
434 void GLAPIENTRY
435 _mesa_marshal_NamedBufferData(GLuint buffer, GLsizeiptr size,
436 const GLvoid * data, GLenum usage)
437 {
438 GET_CURRENT_CONTEXT(ctx);
439 size_t cmd_size = sizeof(struct marshal_cmd_NamedBufferData) + size;
440
441 debug_print_marshal("NamedBufferData");
442 if (unlikely(size < 0)) {
443 _mesa_glthread_finish(ctx);
444 _mesa_error(ctx, GL_INVALID_VALUE, "NamedBufferData(size < 0)");
445 return;
446 }
447
448 if (buffer > 0 && cmd_size <= MARSHAL_MAX_CMD_SIZE) {
449 struct marshal_cmd_NamedBufferData *cmd =
450 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_NamedBufferData,
451 cmd_size);
452 cmd->name = buffer;
453 cmd->size = size;
454 cmd->usage = usage;
455 char *variable_data = (char *) (cmd + 1);
456 memcpy(variable_data, data, size);
457 _mesa_post_marshal_hook(ctx);
458 } else {
459 _mesa_glthread_finish(ctx);
460 CALL_NamedBufferData(ctx->CurrentServerDispatch,
461 (buffer, size, data, usage));
462 }
463 }
464
465 /* NamedBufferSubData: marshalled asynchronously */
466 struct marshal_cmd_NamedBufferSubData
467 {
468 struct marshal_cmd_base cmd_base;
469 GLuint name;
470 GLintptr offset;
471 GLsizei size;
472 /* Next size bytes are GLubyte data[size] */
473 };
474
475 void
476 _mesa_unmarshal_NamedBufferSubData(struct gl_context *ctx,
477 const struct marshal_cmd_NamedBufferSubData *cmd)
478 {
479 const GLuint name = cmd->name;
480 const GLintptr offset = cmd->offset;
481 const GLsizei size = cmd->size;
482 const void *data = (const void *) (cmd + 1);
483
484 CALL_NamedBufferSubData(ctx->CurrentServerDispatch,
485 (name, offset, size, data));
486 }
487
488 void GLAPIENTRY
489 _mesa_marshal_NamedBufferSubData(GLuint buffer, GLintptr offset,
490 GLsizeiptr size, const GLvoid * data)
491 {
492 GET_CURRENT_CONTEXT(ctx);
493 size_t cmd_size = sizeof(struct marshal_cmd_NamedBufferSubData) + size;
494
495 debug_print_marshal("NamedBufferSubData");
496 if (unlikely(size < 0)) {
497 _mesa_glthread_finish(ctx);
498 _mesa_error(ctx, GL_INVALID_VALUE, "NamedBufferSubData(size < 0)");
499 return;
500 }
501
502 if (buffer > 0 && cmd_size <= MARSHAL_MAX_CMD_SIZE) {
503 struct marshal_cmd_NamedBufferSubData *cmd =
504 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_NamedBufferSubData,
505 cmd_size);
506 cmd->name = buffer;
507 cmd->offset = offset;
508 cmd->size = size;
509 char *variable_data = (char *) (cmd + 1);
510 memcpy(variable_data, data, size);
511 _mesa_post_marshal_hook(ctx);
512 } else {
513 _mesa_glthread_finish(ctx);
514 CALL_NamedBufferSubData(ctx->CurrentServerDispatch,
515 (buffer, offset, size, data));
516 }
517 }
518
519 /* ClearBufferfv: marshalled asynchronously */
520 struct marshal_cmd_ClearBuffer
521 {
522 struct marshal_cmd_base cmd_base;
523 GLenum buffer;
524 GLint drawbuffer;
525 };
526
527 void
528 _mesa_unmarshal_ClearBufferfv(struct gl_context *ctx,
529 const struct marshal_cmd_ClearBuffer *cmd)
530 {
531 const GLenum buffer = cmd->buffer;
532 const GLint drawbuffer = cmd->drawbuffer;
533 const char *variable_data = (const char *) (cmd + 1);
534 const GLfloat *value = (const GLfloat *) variable_data;
535
536 CALL_ClearBufferfv(ctx->CurrentServerDispatch,
537 (buffer, drawbuffer, value));
538 }
539
540 static inline size_t buffer_to_size(GLenum buffer)
541 {
542 switch (buffer) {
543 case GL_COLOR:
544 return 4;
545 case GL_DEPTH_STENCIL:
546 return 2;
547 case GL_STENCIL:
548 case GL_DEPTH:
549 return 1;
550 default:
551 return 0;
552 }
553 }
554
555 static inline bool clear_buffer_add_command(struct gl_context *ctx, uint16_t id,
556 GLenum buffer, GLint drawbuffer,
557 const GLuint *value, size_t size)
558 {
559 size_t cmd_size = sizeof(struct marshal_cmd_ClearBuffer) + 4 * size;
560 if (cmd_size <= MARSHAL_MAX_CMD_SIZE) {
561 struct marshal_cmd_ClearBuffer *cmd =
562 _mesa_glthread_allocate_command(ctx, id,
563 cmd_size);
564 cmd->buffer = buffer;
565 cmd->drawbuffer = drawbuffer;
566 GLuint *variable_data = (GLuint *) (cmd + 1);
567 if (size == 4)
568 COPY_4V(variable_data, value);
569 else if (size == 2)
570 COPY_2V(variable_data, value);
571 else
572 *variable_data = *value;
573
574 _mesa_post_marshal_hook(ctx);
575 return true;
576 }
577
578 return false;
579 }
580
581 void GLAPIENTRY
582 _mesa_marshal_ClearBufferfv(GLenum buffer, GLint drawbuffer,
583 const GLfloat *value)
584 {
585 GET_CURRENT_CONTEXT(ctx);
586 debug_print_marshal("ClearBufferfv");
587
588 if (!(buffer == GL_DEPTH || buffer == GL_COLOR)) {
589 _mesa_glthread_finish(ctx);
590
591 /* Page 498 of the PDF, section '17.4.3.1 Clearing Individual Buffers'
592 * of the OpenGL 4.5 spec states:
593 *
594 * "An INVALID_ENUM error is generated by ClearBufferfv and
595 * ClearNamedFramebufferfv if buffer is not COLOR or DEPTH."
596 */
597 _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferfv(buffer=%s)",
598 _mesa_enum_to_string(buffer));
599 }
600
601 size_t size = buffer_to_size(buffer);
602 if (!clear_buffer_add_command(ctx, DISPATCH_CMD_ClearBufferfv, buffer,
603 drawbuffer, (GLuint *)value, size)) {
604 debug_print_sync("ClearBufferfv");
605 _mesa_glthread_finish(ctx);
606 CALL_ClearBufferfv(ctx->CurrentServerDispatch,
607 (buffer, drawbuffer, value));
608 }
609 }