mesa/marshal: add custom BufferData/BufferSubData marshalling
[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 "marshal.h"
31 #include "dispatch.h"
32 #include "marshal_generated.h"
33
34 #ifdef HAVE_PTHREAD
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
69 struct marshal_cmd_ShaderSource
70 {
71 struct marshal_cmd_base cmd_base;
72 GLuint shader;
73 GLsizei count;
74 /* Followed by GLint length[count], then the contents of all strings,
75 * concatenated.
76 */
77 };
78
79
80 void
81 _mesa_unmarshal_ShaderSource(struct gl_context *ctx,
82 const struct marshal_cmd_ShaderSource *cmd)
83 {
84 const GLint *cmd_length = (const GLint *) (cmd + 1);
85 const GLchar *cmd_strings = (const GLchar *) (cmd_length + cmd->count);
86 /* TODO: how to deal with malloc failure? */
87 const GLchar * *string = malloc(cmd->count * sizeof(const GLchar *));
88 int i;
89
90 for (i = 0; i < cmd->count; ++i) {
91 string[i] = cmd_strings;
92 cmd_strings += cmd_length[i];
93 }
94 CALL_ShaderSource(ctx->CurrentServerDispatch,
95 (cmd->shader, cmd->count, string, cmd_length));
96 free(string);
97 }
98
99
100 static size_t
101 measure_ShaderSource_strings(GLsizei count, const GLchar * const *string,
102 const GLint *length_in, GLint *length_out)
103 {
104 int i;
105 size_t total_string_length = 0;
106
107 for (i = 0; i < count; ++i) {
108 if (length_in == NULL || length_in[i] < 0) {
109 if (string[i])
110 length_out[i] = strlen(string[i]);
111 } else {
112 length_out[i] = length_in[i];
113 }
114 total_string_length += length_out[i];
115 }
116 return total_string_length;
117 }
118
119
120 void GLAPIENTRY
121 _mesa_marshal_ShaderSource(GLuint shader, GLsizei count,
122 const GLchar * const *string, const GLint *length)
123 {
124 /* TODO: how to report an error if count < 0? */
125
126 GET_CURRENT_CONTEXT(ctx);
127 /* TODO: how to deal with malloc failure? */
128 const size_t fixed_cmd_size = sizeof(struct marshal_cmd_ShaderSource);
129 STATIC_ASSERT(sizeof(struct marshal_cmd_ShaderSource) % sizeof(GLint) == 0);
130 size_t length_size = count * sizeof(GLint);
131 GLint *length_tmp = malloc(length_size);
132 size_t total_string_length =
133 measure_ShaderSource_strings(count, string, length, length_tmp);
134 size_t total_cmd_size = fixed_cmd_size + length_size + total_string_length;
135
136 if (total_cmd_size <= MARSHAL_MAX_CMD_SIZE) {
137 struct marshal_cmd_ShaderSource *cmd =
138 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_ShaderSource,
139 total_cmd_size);
140 GLint *cmd_length = (GLint *) (cmd + 1);
141 GLchar *cmd_strings = (GLchar *) (cmd_length + count);
142 int i;
143
144 cmd->shader = shader;
145 cmd->count = count;
146 memcpy(cmd_length, length_tmp, length_size);
147 for (i = 0; i < count; ++i) {
148 memcpy(cmd_strings, string[i], cmd_length[i]);
149 cmd_strings += cmd_length[i];
150 }
151 _mesa_post_marshal_hook(ctx);
152 } else {
153 _mesa_glthread_finish(ctx);
154 CALL_ShaderSource(ctx->CurrentServerDispatch,
155 (shader, count, string, length_tmp));
156 }
157 free(length_tmp);
158 }
159
160
161 /* BindBufferBase: marshalled asynchronously */
162 struct marshal_cmd_BindBufferBase
163 {
164 struct marshal_cmd_base cmd_base;
165 GLenum target;
166 GLuint index;
167 GLuint buffer;
168 };
169 static inline void
170 _mesa_unmarshal_BindBufferBase(struct gl_context *ctx, const struct marshal_cmd_BindBufferBase *cmd)
171 {
172 const GLenum target = cmd->target;
173 const GLuint index = cmd->index;
174 const GLuint buffer = cmd->buffer;
175 CALL_BindBufferBase(ctx->CurrentServerDispatch, (target, index, buffer));
176 }
177
178 /** Tracks the current bindings for the vertex array and index array buffers.
179 *
180 * This is part of what we need to enable glthread on compat-GL contexts that
181 * happen to use VBOs, without also supporting the full tracking of VBO vs
182 * user vertex array bindings per attribute on each vertex array for
183 * determining what to upload at draw call time.
184 *
185 * Note that GL core makes it so that a buffer binding with an invalid handle
186 * in the "buffer" parameter will throw an error, and then a
187 * glVertexAttribPointer() that followsmight not end up pointing at a VBO.
188 * However, in GL core the draw call would throw an error as well, so we don't
189 * really care if our tracking is wrong for this case -- we never need to
190 * marshal user data for draw calls, and the unmarshal will just generate an
191 * error or not as appropriate.
192 *
193 * For compatibility GL, we do need to accurately know whether the draw call
194 * on the unmarshal side will dereference a user pointer or load data from a
195 * VBO per vertex. That would make it seem like we need to track whether a
196 * "buffer" is valid, so that we can know when an error will be generated
197 * instead of updating the binding. However, compat GL has the ridiculous
198 * feature that if you pass a bad name, it just gens a buffer object for you,
199 * so we escape without having to know if things are valid or not.
200 */
201 static void
202 track_vbo_binding(struct gl_context *ctx, GLenum target, GLuint buffer)
203 {
204 struct glthread_state *glthread = ctx->GLThread;
205
206 switch (target) {
207 case GL_ARRAY_BUFFER:
208 glthread->vertex_array_is_vbo = (buffer != 0);
209 break;
210 case GL_ELEMENT_ARRAY_BUFFER:
211 /* The current element array buffer binding is actually tracked in the
212 * vertex array object instead of the context, so this would need to
213 * change on vertex array object updates.
214 */
215 glthread->element_array_is_vbo = (buffer != 0);
216 break;
217 }
218 }
219
220
221 struct marshal_cmd_BindBuffer
222 {
223 struct marshal_cmd_base cmd_base;
224 GLenum target;
225 GLuint buffer;
226 };
227
228 /**
229 * This is just like the code-generated glBindBuffer() support, except that we
230 * call track_vbo_binding().
231 */
232 void
233 _mesa_unmarshal_BindBuffer(struct gl_context *ctx,
234 const struct marshal_cmd_BindBuffer *cmd)
235 {
236 const GLenum target = cmd->target;
237 const GLuint buffer = cmd->buffer;
238 CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
239 }
240 void GLAPIENTRY
241 _mesa_marshal_BindBuffer(GLenum target, GLuint buffer)
242 {
243 GET_CURRENT_CONTEXT(ctx);
244 size_t cmd_size = sizeof(struct marshal_cmd_BindBuffer);
245 struct marshal_cmd_BindBuffer *cmd;
246 debug_print_marshal("BindBuffer");
247
248 track_vbo_binding(ctx, target, buffer);
249
250 if (cmd_size <= MARSHAL_MAX_CMD_SIZE) {
251 cmd = _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BindBuffer,
252 cmd_size);
253 cmd->target = target;
254 cmd->buffer = buffer;
255 _mesa_post_marshal_hook(ctx);
256 } else {
257 _mesa_glthread_finish(ctx);
258 CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
259 }
260 }
261
262 /* BufferData: marshalled asynchronously */
263 struct marshal_cmd_BufferData
264 {
265 struct marshal_cmd_base cmd_base;
266 GLenum target;
267 GLsizeiptr size;
268 GLenum usage;
269 bool data_null; /* If set, no data follows for "data" */
270 /* Next size bytes are GLvoid data[size] */
271 };
272
273 void
274 _mesa_unmarshal_BufferData(struct gl_context *ctx,
275 const struct marshal_cmd_BufferData *cmd)
276 {
277 const GLenum target = cmd->target;
278 const GLsizeiptr size = cmd->size;
279 const GLenum usage = cmd->usage;
280 const char *variable_data = (const char *) (cmd + 1);
281 const GLvoid *data = (const GLvoid *) variable_data;
282
283 if (cmd->data_null)
284 data = NULL;
285 else
286 variable_data += size;
287
288 CALL_BufferData(ctx->CurrentServerDispatch, (target, size, data, usage));
289 }
290
291 void GLAPIENTRY
292 _mesa_marshal_BufferData(GLenum target, GLsizeiptr size, const GLvoid * data,
293 GLenum usage)
294 {
295 GET_CURRENT_CONTEXT(ctx);
296 size_t cmd_size =
297 sizeof(struct marshal_cmd_BufferData) + (data ? size : 0);
298 debug_print_marshal("BufferData");
299
300 if (unlikely(size < 0)) {
301 _mesa_glthread_finish(ctx);
302 _mesa_error(ctx, GL_INVALID_VALUE, "BufferData(size < 0)");
303 return;
304 }
305
306 if (target != GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD &&
307 cmd_size <= MARSHAL_MAX_CMD_SIZE) {
308 struct marshal_cmd_BufferData *cmd =
309 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferData,
310 cmd_size);
311
312 cmd->target = target;
313 cmd->size = size;
314 cmd->usage = usage;
315 char *variable_data = (char *) (cmd + 1);
316 cmd->data_null = !data;
317 if (!cmd->data_null) {
318 memcpy(variable_data, data, size);
319 variable_data += size;
320 }
321 _mesa_post_marshal_hook(ctx);
322 } else {
323 _mesa_glthread_finish(ctx);
324 CALL_BufferData(ctx->CurrentServerDispatch,
325 (target, size, data, usage));
326 }
327 }
328
329 /* BufferSubData: marshalled asynchronously */
330 struct marshal_cmd_BufferSubData
331 {
332 struct marshal_cmd_base cmd_base;
333 GLenum target;
334 GLintptr offset;
335 GLsizeiptr size;
336 /* Next size bytes are GLvoid data[size] */
337 };
338
339 void
340 _mesa_unmarshal_BufferSubData(struct gl_context *ctx,
341 const struct marshal_cmd_BufferSubData *cmd)
342 {
343 const GLenum target = cmd->target;
344 const GLintptr offset = cmd->offset;
345 const GLsizeiptr size = cmd->size;
346 const char *variable_data = (const char *) (cmd + 1);
347 const GLvoid *data = (const GLvoid *) variable_data;
348
349 variable_data += size;
350 CALL_BufferSubData(ctx->CurrentServerDispatch,
351 (target, offset, size, data));
352 }
353
354 void GLAPIENTRY
355 _mesa_marshal_BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
356 const GLvoid * data)
357 {
358 GET_CURRENT_CONTEXT(ctx);
359 size_t cmd_size = sizeof(struct marshal_cmd_BufferSubData) + size;
360
361 debug_print_marshal("BufferSubData");
362 if (unlikely(size < 0)) {
363 _mesa_glthread_finish(ctx);
364 _mesa_error(ctx, GL_INVALID_VALUE, "BufferSubData(size < 0)");
365 return;
366 }
367
368 if (target != GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD &&
369 cmd_size <= MARSHAL_MAX_CMD_SIZE) {
370 struct marshal_cmd_BufferSubData *cmd =
371 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BufferSubData,
372 cmd_size);
373 cmd->target = target;
374 cmd->offset = offset;
375 cmd->size = size;
376 char *variable_data = (char *) (cmd + 1);
377 memcpy(variable_data, data, size);
378 variable_data += size;
379 _mesa_post_marshal_hook(ctx);
380 } else {
381 _mesa_glthread_finish(ctx);
382 CALL_BufferSubData(ctx->CurrentServerDispatch,
383 (target, offset, size, data));
384 }
385 }
386
387 #endif