mesa/glthread: add custom marshalling for ClearBufferfv()
[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 #ifdef HAVE_PTHREAD
37
38 struct marshal_cmd_Flush
39 {
40 struct marshal_cmd_base cmd_base;
41 };
42
43
44 void
45 _mesa_unmarshal_Flush(struct gl_context *ctx,
46 const struct marshal_cmd_Flush *cmd)
47 {
48 CALL_Flush(ctx->CurrentServerDispatch, ());
49 }
50
51
52 void GLAPIENTRY
53 _mesa_marshal_Flush(void)
54 {
55 GET_CURRENT_CONTEXT(ctx);
56 struct marshal_cmd_Flush *cmd =
57 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_Flush,
58 sizeof(struct marshal_cmd_Flush));
59 (void) cmd;
60 _mesa_post_marshal_hook(ctx);
61
62 /* Flush() needs to be handled specially. In addition to telling the
63 * background thread to flush, we need to ensure that our own buffer is
64 * submitted to the background thread so that it will complete in a finite
65 * amount of time.
66 */
67 _mesa_glthread_flush_batch(ctx);
68 }
69
70
71 struct marshal_cmd_ShaderSource
72 {
73 struct marshal_cmd_base cmd_base;
74 GLuint shader;
75 GLsizei count;
76 /* Followed by GLint length[count], then the contents of all strings,
77 * concatenated.
78 */
79 };
80
81
82 void
83 _mesa_unmarshal_ShaderSource(struct gl_context *ctx,
84 const struct marshal_cmd_ShaderSource *cmd)
85 {
86 const GLint *cmd_length = (const GLint *) (cmd + 1);
87 const GLchar *cmd_strings = (const GLchar *) (cmd_length + cmd->count);
88 /* TODO: how to deal with malloc failure? */
89 const GLchar * *string = malloc(cmd->count * sizeof(const GLchar *));
90 int i;
91
92 for (i = 0; i < cmd->count; ++i) {
93 string[i] = cmd_strings;
94 cmd_strings += cmd_length[i];
95 }
96 CALL_ShaderSource(ctx->CurrentServerDispatch,
97 (cmd->shader, cmd->count, string, cmd_length));
98 free(string);
99 }
100
101
102 static size_t
103 measure_ShaderSource_strings(GLsizei count, const GLchar * const *string,
104 const GLint *length_in, GLint *length_out)
105 {
106 int i;
107 size_t total_string_length = 0;
108
109 for (i = 0; i < count; ++i) {
110 if (length_in == NULL || length_in[i] < 0) {
111 if (string[i])
112 length_out[i] = strlen(string[i]);
113 } else {
114 length_out[i] = length_in[i];
115 }
116 total_string_length += length_out[i];
117 }
118 return total_string_length;
119 }
120
121
122 void GLAPIENTRY
123 _mesa_marshal_ShaderSource(GLuint shader, GLsizei count,
124 const GLchar * const *string, const GLint *length)
125 {
126 /* TODO: how to report an error if count < 0? */
127
128 GET_CURRENT_CONTEXT(ctx);
129 /* TODO: how to deal with malloc failure? */
130 const size_t fixed_cmd_size = sizeof(struct marshal_cmd_ShaderSource);
131 STATIC_ASSERT(sizeof(struct marshal_cmd_ShaderSource) % sizeof(GLint) == 0);
132 size_t length_size = count * sizeof(GLint);
133 GLint *length_tmp = malloc(length_size);
134 size_t total_string_length =
135 measure_ShaderSource_strings(count, string, length, length_tmp);
136 size_t total_cmd_size = fixed_cmd_size + length_size + total_string_length;
137
138 if (total_cmd_size <= MARSHAL_MAX_CMD_SIZE) {
139 struct marshal_cmd_ShaderSource *cmd =
140 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_ShaderSource,
141 total_cmd_size);
142 GLint *cmd_length = (GLint *) (cmd + 1);
143 GLchar *cmd_strings = (GLchar *) (cmd_length + count);
144 int i;
145
146 cmd->shader = shader;
147 cmd->count = count;
148 memcpy(cmd_length, length_tmp, length_size);
149 for (i = 0; i < count; ++i) {
150 memcpy(cmd_strings, string[i], cmd_length[i]);
151 cmd_strings += cmd_length[i];
152 }
153 _mesa_post_marshal_hook(ctx);
154 } else {
155 _mesa_glthread_finish(ctx);
156 CALL_ShaderSource(ctx->CurrentServerDispatch,
157 (shader, count, string, length_tmp));
158 }
159 free(length_tmp);
160 }
161
162
163 /* BindBufferBase: marshalled asynchronously */
164 struct marshal_cmd_BindBufferBase
165 {
166 struct marshal_cmd_base cmd_base;
167 GLenum target;
168 GLuint index;
169 GLuint buffer;
170 };
171 static inline void
172 _mesa_unmarshal_BindBufferBase(struct gl_context *ctx, const struct marshal_cmd_BindBufferBase *cmd)
173 {
174 const GLenum target = cmd->target;
175 const GLuint index = cmd->index;
176 const GLuint buffer = cmd->buffer;
177 CALL_BindBufferBase(ctx->CurrentServerDispatch, (target, index, buffer));
178 }
179
180 /** Tracks the current bindings for the vertex array and index array buffers.
181 *
182 * This is part of what we need to enable glthread on compat-GL contexts that
183 * happen to use VBOs, without also supporting the full tracking of VBO vs
184 * user vertex array bindings per attribute on each vertex array for
185 * determining what to upload at draw call time.
186 *
187 * Note that GL core makes it so that a buffer binding with an invalid handle
188 * in the "buffer" parameter will throw an error, and then a
189 * glVertexAttribPointer() that followsmight not end up pointing at a VBO.
190 * However, in GL core the draw call would throw an error as well, so we don't
191 * really care if our tracking is wrong for this case -- we never need to
192 * marshal user data for draw calls, and the unmarshal will just generate an
193 * error or not as appropriate.
194 *
195 * For compatibility GL, we do need to accurately know whether the draw call
196 * on the unmarshal side will dereference a user pointer or load data from a
197 * VBO per vertex. That would make it seem like we need to track whether a
198 * "buffer" is valid, so that we can know when an error will be generated
199 * instead of updating the binding. However, compat GL has the ridiculous
200 * feature that if you pass a bad name, it just gens a buffer object for you,
201 * so we escape without having to know if things are valid or not.
202 */
203 static void
204 track_vbo_binding(struct gl_context *ctx, GLenum target, GLuint buffer)
205 {
206 struct glthread_state *glthread = ctx->GLThread;
207
208 switch (target) {
209 case GL_ARRAY_BUFFER:
210 glthread->vertex_array_is_vbo = (buffer != 0);
211 break;
212 case GL_ELEMENT_ARRAY_BUFFER:
213 /* The current element array buffer binding is actually tracked in the
214 * vertex array object instead of the context, so this would need to
215 * change on vertex array object updates.
216 */
217 glthread->element_array_is_vbo = (buffer != 0);
218 break;
219 }
220 }
221
222
223 struct marshal_cmd_BindBuffer
224 {
225 struct marshal_cmd_base cmd_base;
226 GLenum target;
227 GLuint buffer;
228 };
229
230 /**
231 * This is just like the code-generated glBindBuffer() support, except that we
232 * call track_vbo_binding().
233 */
234 void
235 _mesa_unmarshal_BindBuffer(struct gl_context *ctx,
236 const struct marshal_cmd_BindBuffer *cmd)
237 {
238 const GLenum target = cmd->target;
239 const GLuint buffer = cmd->buffer;
240 CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
241 }
242 void GLAPIENTRY
243 _mesa_marshal_BindBuffer(GLenum target, GLuint buffer)
244 {
245 GET_CURRENT_CONTEXT(ctx);
246 size_t cmd_size = sizeof(struct marshal_cmd_BindBuffer);
247 struct marshal_cmd_BindBuffer *cmd;
248 debug_print_marshal("BindBuffer");
249
250 track_vbo_binding(ctx, target, buffer);
251
252 if (cmd_size <= MARSHAL_MAX_CMD_SIZE) {
253 cmd = _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BindBuffer,
254 cmd_size);
255 cmd->target = target;
256 cmd->buffer = buffer;
257 _mesa_post_marshal_hook(ctx);
258 } else {
259 _mesa_glthread_finish(ctx);
260 CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
261 }
262 }
263
264 /* BufferData: marshalled asynchronously */
265 struct marshal_cmd_BufferData
266 {
267 struct marshal_cmd_base cmd_base;
268 GLenum target;
269 GLsizeiptr size;
270 GLenum usage;
271 bool data_null; /* If set, no data follows for "data" */
272 /* Next size bytes are GLvoid data[size] */
273 };
274
275 void
276 _mesa_unmarshal_BufferData(struct gl_context *ctx,
277 const struct marshal_cmd_BufferData *cmd)
278 {
279 const GLenum target = cmd->target;
280 const GLsizeiptr size = cmd->size;
281 const GLenum usage = cmd->usage;
282 const char *variable_data = (const char *) (cmd + 1);
283 const GLvoid *data = (const GLvoid *) variable_data;
284
285 if (cmd->data_null)
286 data = NULL;
287 else
288 variable_data += size;
289
290 CALL_BufferData(ctx->CurrentServerDispatch, (target, size, data, usage));
291 }
292
293 void GLAPIENTRY
294 _mesa_marshal_BufferData(GLenum target, GLsizeiptr size, const GLvoid * data,
295 GLenum usage)
296 {
297 GET_CURRENT_CONTEXT(ctx);
298 size_t cmd_size =
299 sizeof(struct marshal_cmd_BufferData) + (data ? size : 0);
300 debug_print_marshal("BufferData");
301
302 if (unlikely(size < 0)) {
303 _mesa_glthread_finish(ctx);
304 _mesa_error(ctx, GL_INVALID_VALUE, "BufferData(size < 0)");
305 return;
306 }
307
308 if (target != GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD &&
309 cmd_size <= MARSHAL_MAX_CMD_SIZE) {
310 struct marshal_cmd_BufferData *cmd =
311 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferData,
312 cmd_size);
313
314 cmd->target = target;
315 cmd->size = size;
316 cmd->usage = usage;
317 char *variable_data = (char *) (cmd + 1);
318 cmd->data_null = !data;
319 if (!cmd->data_null) {
320 memcpy(variable_data, data, size);
321 variable_data += size;
322 }
323 _mesa_post_marshal_hook(ctx);
324 } else {
325 _mesa_glthread_finish(ctx);
326 CALL_BufferData(ctx->CurrentServerDispatch,
327 (target, size, data, usage));
328 }
329 }
330
331 /* BufferSubData: marshalled asynchronously */
332 struct marshal_cmd_BufferSubData
333 {
334 struct marshal_cmd_base cmd_base;
335 GLenum target;
336 GLintptr offset;
337 GLsizeiptr size;
338 /* Next size bytes are GLvoid data[size] */
339 };
340
341 void
342 _mesa_unmarshal_BufferSubData(struct gl_context *ctx,
343 const struct marshal_cmd_BufferSubData *cmd)
344 {
345 const GLenum target = cmd->target;
346 const GLintptr offset = cmd->offset;
347 const GLsizeiptr size = cmd->size;
348 const char *variable_data = (const char *) (cmd + 1);
349 const GLvoid *data = (const GLvoid *) variable_data;
350
351 variable_data += size;
352 CALL_BufferSubData(ctx->CurrentServerDispatch,
353 (target, offset, size, data));
354 }
355
356 void GLAPIENTRY
357 _mesa_marshal_BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
358 const GLvoid * data)
359 {
360 GET_CURRENT_CONTEXT(ctx);
361 size_t cmd_size = sizeof(struct marshal_cmd_BufferSubData) + size;
362
363 debug_print_marshal("BufferSubData");
364 if (unlikely(size < 0)) {
365 _mesa_glthread_finish(ctx);
366 _mesa_error(ctx, GL_INVALID_VALUE, "BufferSubData(size < 0)");
367 return;
368 }
369
370 if (target != GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD &&
371 cmd_size <= MARSHAL_MAX_CMD_SIZE) {
372 struct marshal_cmd_BufferSubData *cmd =
373 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferSubData,
374 cmd_size);
375 cmd->target = target;
376 cmd->offset = offset;
377 cmd->size = size;
378 char *variable_data = (char *) (cmd + 1);
379 memcpy(variable_data, data, size);
380 variable_data += size;
381 _mesa_post_marshal_hook(ctx);
382 } else {
383 _mesa_glthread_finish(ctx);
384 CALL_BufferSubData(ctx->CurrentServerDispatch,
385 (target, offset, size, data));
386 }
387 }
388
389 /* ClearBufferfv: marshalled asynchronously */
390 struct marshal_cmd_ClearBufferfv
391 {
392 struct marshal_cmd_base cmd_base;
393 GLenum buffer;
394 GLint drawbuffer;
395 };
396
397 void
398 _mesa_unmarshal_ClearBufferfv(struct gl_context *ctx,
399 const struct marshal_cmd_ClearBufferfv *cmd)
400 {
401 const GLenum buffer = cmd->buffer;
402 const GLint drawbuffer = cmd->drawbuffer;
403 const char *variable_data = (const char *) (cmd + 1);
404 const GLfloat *value = (const GLfloat *) variable_data;
405
406 CALL_ClearBufferfv(ctx->CurrentServerDispatch,
407 (buffer, drawbuffer, value));
408 }
409
410 void GLAPIENTRY
411 _mesa_marshal_ClearBufferfv(GLenum buffer, GLint drawbuffer,
412 const GLfloat *value)
413 {
414 GET_CURRENT_CONTEXT(ctx);
415 debug_print_marshal("ClearBufferfv");
416
417 size_t size;
418 switch (buffer) {
419 case GL_DEPTH:
420 size = sizeof(GLfloat);
421 break;
422 case GL_COLOR:
423 size = sizeof(GLfloat) * 4;
424 break;
425 default:
426 _mesa_glthread_finish(ctx);
427
428 /* Page 498 of the PDF, section '17.4.3.1 Clearing Individual Buffers'
429 * of the OpenGL 4.5 spec states:
430 *
431 * "An INVALID_ENUM error is generated by ClearBufferfv and
432 * ClearNamedFramebufferfv if buffer is not COLOR or DEPTH."
433 */
434 _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferfv(buffer=%s)",
435 _mesa_enum_to_string(buffer));
436 return;
437 }
438
439 size_t cmd_size = sizeof(struct marshal_cmd_ClearBufferfv) + size;
440 if (cmd_size <= MARSHAL_MAX_CMD_SIZE) {
441 struct marshal_cmd_ClearBufferfv *cmd =
442 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_ClearBufferfv,
443 cmd_size);
444 cmd->buffer = buffer;
445 cmd->drawbuffer = drawbuffer;
446 GLfloat *variable_data = (GLfloat *) (cmd + 1);
447 if (buffer == GL_COLOR)
448 COPY_4V(variable_data, value);
449 else
450 *variable_data = *value;
451
452 _mesa_post_marshal_hook(ctx);
453 } else {
454 debug_print_sync("ClearBufferfv");
455 _mesa_glthread_finish(ctx);
456 CALL_ClearBufferfv(ctx->CurrentServerDispatch,
457 (buffer, drawbuffer, value));
458 }
459 }
460
461 #endif