glthread: add custom marshalling for glNamedBuffer(Sub)DataEXT
[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 /** Tracks the current bindings for the vertex array and index array buffers.
140 *
141 * This is part of what we need to enable glthread on compat-GL contexts that
142 * happen to use VBOs, without also supporting the full tracking of VBO vs
143 * user vertex array bindings per attribute on each vertex array for
144 * determining what to upload at draw call time.
145 *
146 * Note that GL core makes it so that a buffer binding with an invalid handle
147 * in the "buffer" parameter will throw an error, and then a
148 * glVertexAttribPointer() that followsmight not end up pointing at a VBO.
149 * However, in GL core the draw call would throw an error as well, so we don't
150 * really care if our tracking is wrong for this case -- we never need to
151 * marshal user data for draw calls, and the unmarshal will just generate an
152 * error or not as appropriate.
153 *
154 * For compatibility GL, we do need to accurately know whether the draw call
155 * on the unmarshal side will dereference a user pointer or load data from a
156 * VBO per vertex. That would make it seem like we need to track whether a
157 * "buffer" is valid, so that we can know when an error will be generated
158 * instead of updating the binding. However, compat GL has the ridiculous
159 * feature that if you pass a bad name, it just gens a buffer object for you,
160 * so we escape without having to know if things are valid or not.
161 */
162 void
163 _mesa_glthread_BindBuffer(struct gl_context *ctx, GLenum target, GLuint buffer)
164 {
165 struct glthread_state *glthread = ctx->GLThread;
166
167 switch (target) {
168 case GL_ARRAY_BUFFER:
169 glthread->vertex_array_is_vbo = (buffer != 0);
170 break;
171 case GL_ELEMENT_ARRAY_BUFFER:
172 /* The current element array buffer binding is actually tracked in the
173 * vertex array object instead of the context, so this would need to
174 * change on vertex array object updates.
175 */
176 glthread->CurrentVAO->IndexBufferIsUserPointer = buffer != 0;
177 break;
178 case GL_DRAW_INDIRECT_BUFFER:
179 glthread->draw_indirect_buffer_is_vbo = buffer != 0;
180 break;
181 }
182 }
183
184
185 /* BufferData: marshalled asynchronously */
186 struct marshal_cmd_BufferData
187 {
188 struct marshal_cmd_base cmd_base;
189 GLuint target_or_name;
190 GLsizeiptr size;
191 GLenum usage;
192 const GLvoid *data_external_mem;
193 bool data_null; /* If set, no data follows for "data" */
194 bool named;
195 bool ext_dsa;
196 /* Next size bytes are GLubyte data[size] */
197 };
198
199 void
200 _mesa_unmarshal_BufferData(struct gl_context *ctx,
201 const struct marshal_cmd_BufferData *cmd)
202 {
203 const GLuint target_or_name = cmd->target_or_name;
204 const GLsizei size = cmd->size;
205 const GLenum usage = cmd->usage;
206 const void *data;
207
208 if (cmd->data_null)
209 data = NULL;
210 else if (!cmd->named && target_or_name == GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD)
211 data = cmd->data_external_mem;
212 else
213 data = (const void *) (cmd + 1);
214
215 if (cmd->ext_dsa) {
216 CALL_NamedBufferDataEXT(ctx->CurrentServerDispatch,
217 (target_or_name, size, data, usage));
218 } else if (cmd->named) {
219 CALL_NamedBufferData(ctx->CurrentServerDispatch,
220 (target_or_name, size, data, usage));
221 } else {
222 CALL_BufferData(ctx->CurrentServerDispatch,
223 (target_or_name, size, data, usage));
224 }
225 }
226
227 void
228 _mesa_unmarshal_NamedBufferData(struct gl_context *ctx,
229 const struct marshal_cmd_BufferData *cmd)
230 {
231 unreachable("never used - all BufferData variants use DISPATCH_CMD_BufferData");
232 }
233
234 void
235 _mesa_unmarshal_NamedBufferDataEXT(struct gl_context *ctx,
236 const struct marshal_cmd_BufferData *cmd)
237 {
238 unreachable("never used - all BufferData variants use DISPATCH_CMD_BufferData");
239 }
240
241 static void
242 _mesa_marshal_BufferData_merged(GLuint target_or_name, GLsizeiptr size,
243 const GLvoid *data, GLenum usage, bool named,
244 bool ext_dsa, const char *func)
245 {
246 GET_CURRENT_CONTEXT(ctx);
247 bool external_mem = !named &&
248 target_or_name == GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD;
249 bool copy_data = data && !external_mem;
250 int cmd_size = sizeof(struct marshal_cmd_BufferData) + (copy_data ? size : 0);
251 debug_print_marshal("BufferData");
252
253 if (unlikely(size < 0 || size > INT_MAX || cmd_size < 0 ||
254 cmd_size > MARSHAL_MAX_CMD_SIZE ||
255 (named && target_or_name == 0))) {
256 _mesa_glthread_finish_before(ctx, func);
257 if (named) {
258 CALL_NamedBufferData(ctx->CurrentServerDispatch,
259 (target_or_name, size, data, usage));
260 } else {
261 CALL_BufferData(ctx->CurrentServerDispatch,
262 (target_or_name, size, data, usage));
263 }
264 return;
265 }
266
267 struct marshal_cmd_BufferData *cmd =
268 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferData,
269 cmd_size);
270
271 cmd->target_or_name = target_or_name;
272 cmd->size = size;
273 cmd->usage = usage;
274 cmd->data_null = !data;
275 cmd->named = named;
276 cmd->ext_dsa = ext_dsa;
277 cmd->data_external_mem = data;
278
279 if (copy_data) {
280 char *variable_data = (char *) (cmd + 1);
281 memcpy(variable_data, data, size);
282 }
283 _mesa_post_marshal_hook(ctx);
284 }
285
286 void GLAPIENTRY
287 _mesa_marshal_BufferData(GLenum target, GLsizeiptr size, const GLvoid * data,
288 GLenum usage)
289 {
290 _mesa_marshal_BufferData_merged(target, size, data, usage, false, false,
291 "BufferData");
292 }
293
294 void GLAPIENTRY
295 _mesa_marshal_NamedBufferData(GLuint buffer, GLsizeiptr size,
296 const GLvoid * data, GLenum usage)
297 {
298 _mesa_marshal_BufferData_merged(buffer, size, data, usage, true, false,
299 "NamedBufferData");
300 }
301
302 void GLAPIENTRY
303 _mesa_marshal_NamedBufferDataEXT(GLuint buffer, GLsizeiptr size,
304 const GLvoid *data, GLenum usage)
305 {
306 _mesa_marshal_BufferData_merged(buffer, size, data, usage, true, true,
307 "NamedBufferDataEXT");
308 }
309
310
311 /* BufferSubData: marshalled asynchronously */
312 struct marshal_cmd_BufferSubData
313 {
314 struct marshal_cmd_base cmd_base;
315 GLenum target_or_name;
316 GLintptr offset;
317 GLsizeiptr size;
318 bool named;
319 bool ext_dsa;
320 /* Next size bytes are GLubyte data[size] */
321 };
322
323 void
324 _mesa_unmarshal_BufferSubData(struct gl_context *ctx,
325 const struct marshal_cmd_BufferSubData *cmd)
326 {
327 const GLenum target_or_name = cmd->target_or_name;
328 const GLintptr offset = cmd->offset;
329 const GLsizeiptr size = cmd->size;
330 const void *data = (const void *) (cmd + 1);
331
332 if (cmd->ext_dsa) {
333 CALL_NamedBufferSubDataEXT(ctx->CurrentServerDispatch,
334 (target_or_name, offset, size, data));
335 } else if (cmd->named) {
336 CALL_NamedBufferSubData(ctx->CurrentServerDispatch,
337 (target_or_name, offset, size, data));
338 } else {
339 CALL_BufferSubData(ctx->CurrentServerDispatch,
340 (target_or_name, offset, size, data));
341 }
342 }
343
344 void
345 _mesa_unmarshal_NamedBufferSubData(struct gl_context *ctx,
346 const struct marshal_cmd_BufferSubData *cmd)
347 {
348 unreachable("never used - all BufferSubData variants use DISPATCH_CMD_BufferSubData");
349 }
350
351 void
352 _mesa_unmarshal_NamedBufferSubDataEXT(struct gl_context *ctx,
353 const struct marshal_cmd_BufferSubData *cmd)
354 {
355 unreachable("never used - all BufferSubData variants use DISPATCH_CMD_BufferSubData");
356 }
357
358 static void
359 _mesa_marshal_BufferSubData_merged(GLuint target_or_name, GLintptr offset,
360 GLsizeiptr size, const GLvoid *data,
361 bool named, bool ext_dsa, const char *func)
362 {
363 GET_CURRENT_CONTEXT(ctx);
364 size_t cmd_size = sizeof(struct marshal_cmd_BufferSubData) + size;
365 debug_print_marshal(func);
366
367 if (unlikely(size < 0 || size > INT_MAX || cmd_size < 0 ||
368 cmd_size > MARSHAL_MAX_CMD_SIZE || !data ||
369 (named && target_or_name == 0))) {
370 _mesa_glthread_finish_before(ctx, func);
371 if (named) {
372 CALL_NamedBufferSubData(ctx->CurrentServerDispatch,
373 (target_or_name, offset, size, data));
374 } else {
375 CALL_BufferSubData(ctx->CurrentServerDispatch,
376 (target_or_name, offset, size, data));
377 }
378 return;
379 }
380
381 struct marshal_cmd_BufferSubData *cmd =
382 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferSubData,
383 cmd_size);
384 cmd->target_or_name = target_or_name;
385 cmd->offset = offset;
386 cmd->size = size;
387 cmd->named = named;
388 cmd->ext_dsa = ext_dsa;
389
390 char *variable_data = (char *) (cmd + 1);
391 memcpy(variable_data, data, size);
392 _mesa_post_marshal_hook(ctx);
393 }
394
395 void GLAPIENTRY
396 _mesa_marshal_BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
397 const GLvoid * data)
398 {
399 _mesa_marshal_BufferSubData_merged(target, offset, size, data, false,
400 false, "BufferSubData");
401 }
402
403 void GLAPIENTRY
404 _mesa_marshal_NamedBufferSubData(GLuint buffer, GLintptr offset,
405 GLsizeiptr size, const GLvoid * data)
406 {
407 _mesa_marshal_BufferSubData_merged(buffer, offset, size, data, true,
408 false, "NamedBufferSubData");
409 }
410
411 void GLAPIENTRY
412 _mesa_marshal_NamedBufferSubDataEXT(GLuint buffer, GLintptr offset,
413 GLsizeiptr size, const GLvoid * data)
414 {
415 _mesa_marshal_BufferSubData_merged(buffer, offset, size, data, true,
416 true, "NamedBufferSubDataEXT");
417 }