mesa: Track the current vertex/element array buffers for glthread.
[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 struct marshal_cmd_Flush
35 {
36 struct marshal_cmd_base cmd_base;
37 };
38
39
40 void
41 _mesa_unmarshal_Flush(struct gl_context *ctx,
42 const struct marshal_cmd_Flush *cmd)
43 {
44 CALL_Flush(ctx->CurrentServerDispatch, ());
45 }
46
47
48 void GLAPIENTRY
49 _mesa_marshal_Flush(void)
50 {
51 GET_CURRENT_CONTEXT(ctx);
52 struct marshal_cmd_Flush *cmd =
53 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_Flush,
54 sizeof(struct marshal_cmd_Flush));
55 (void) cmd;
56 _mesa_post_marshal_hook(ctx);
57
58 /* Flush() needs to be handled specially. In addition to telling the
59 * background thread to flush, we need to ensure that our own buffer is
60 * submitted to the background thread so that it will complete in a finite
61 * amount of time.
62 */
63 _mesa_glthread_flush_batch(ctx);
64 }
65
66
67 struct marshal_cmd_ShaderSource
68 {
69 struct marshal_cmd_base cmd_base;
70 GLuint shader;
71 GLsizei count;
72 /* Followed by GLint length[count], then the contents of all strings,
73 * concatenated.
74 */
75 };
76
77
78 void
79 _mesa_unmarshal_ShaderSource(struct gl_context *ctx,
80 const struct marshal_cmd_ShaderSource *cmd)
81 {
82 const GLint *cmd_length = (const GLint *) (cmd + 1);
83 const GLchar *cmd_strings = (const GLchar *) (cmd_length + cmd->count);
84 /* TODO: how to deal with malloc failure? */
85 const GLchar * *string = malloc(cmd->count * sizeof(const GLchar *));
86 int i;
87
88 for (i = 0; i < cmd->count; ++i) {
89 string[i] = cmd_strings;
90 cmd_strings += cmd_length[i];
91 }
92 CALL_ShaderSource(ctx->CurrentServerDispatch,
93 (cmd->shader, cmd->count, string, cmd_length));
94 free(string);
95 }
96
97
98 static size_t
99 measure_ShaderSource_strings(GLsizei count, const GLchar * const *string,
100 const GLint *length_in, GLint *length_out)
101 {
102 int i;
103 size_t total_string_length = 0;
104
105 for (i = 0; i < count; ++i) {
106 if (length_in == NULL || length_in[i] < 0) {
107 if (string[i])
108 length_out[i] = strlen(string[i]);
109 } else {
110 length_out[i] = length_in[i];
111 }
112 total_string_length += length_out[i];
113 }
114 return total_string_length;
115 }
116
117
118 void GLAPIENTRY
119 _mesa_marshal_ShaderSource(GLuint shader, GLsizei count,
120 const GLchar * const *string, const GLint *length)
121 {
122 /* TODO: how to report an error if count < 0? */
123
124 GET_CURRENT_CONTEXT(ctx);
125 /* TODO: how to deal with malloc failure? */
126 const size_t fixed_cmd_size = sizeof(struct marshal_cmd_ShaderSource);
127 STATIC_ASSERT(sizeof(struct marshal_cmd_ShaderSource) % sizeof(GLint) == 0);
128 size_t length_size = count * sizeof(GLint);
129 GLint *length_tmp = malloc(length_size);
130 size_t total_string_length =
131 measure_ShaderSource_strings(count, string, length, length_tmp);
132 size_t total_cmd_size = fixed_cmd_size + length_size + total_string_length;
133
134 if (total_cmd_size <= MARSHAL_MAX_CMD_SIZE) {
135 struct marshal_cmd_ShaderSource *cmd =
136 _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_ShaderSource,
137 total_cmd_size);
138 GLint *cmd_length = (GLint *) (cmd + 1);
139 GLchar *cmd_strings = (GLchar *) (cmd_length + count);
140 int i;
141
142 cmd->shader = shader;
143 cmd->count = count;
144 memcpy(cmd_length, length_tmp, length_size);
145 for (i = 0; i < count; ++i) {
146 memcpy(cmd_strings, string[i], cmd_length[i]);
147 cmd_strings += cmd_length[i];
148 }
149 _mesa_post_marshal_hook(ctx);
150 } else {
151 _mesa_glthread_finish(ctx);
152 CALL_ShaderSource(ctx->CurrentServerDispatch,
153 (shader, count, string, length_tmp));
154 }
155 free(length_tmp);
156 }
157
158
159 /* BindBufferBase: marshalled asynchronously */
160 struct marshal_cmd_BindBufferBase
161 {
162 struct marshal_cmd_base cmd_base;
163 GLenum target;
164 GLuint index;
165 GLuint buffer;
166 };
167 static inline void
168 _mesa_unmarshal_BindBufferBase(struct gl_context *ctx, const struct marshal_cmd_BindBufferBase *cmd)
169 {
170 const GLenum target = cmd->target;
171 const GLuint index = cmd->index;
172 const GLuint buffer = cmd->buffer;
173 CALL_BindBufferBase(ctx->CurrentServerDispatch, (target, index, buffer));
174 }
175
176 /** Tracks the current bindings for the vertex array and index array buffers.
177 *
178 * This is part of what we need to enable glthread on compat-GL contexts that
179 * happen to use VBOs, without also supporting the full tracking of VBO vs
180 * user vertex array bindings per attribute on each vertex array for
181 * determining what to upload at draw call time.
182 *
183 * Note that GL core makes it so that a buffer binding with an invalid handle
184 * in the "buffer" parameter will throw an error, and then a
185 * glVertexAttribPointer() that followsmight not end up pointing at a VBO.
186 * However, in GL core the draw call would throw an error as well, so we don't
187 * really care if our tracking is wrong for this case -- we never need to
188 * marshal user data for draw calls, and the unmarshal will just generate an
189 * error or not as appropriate.
190 *
191 * For compatibility GL, we do need to accurately know whether the draw call
192 * on the unmarshal side will dereference a user pointer or load data from a
193 * VBO per vertex. That would make it seem like we need to track whether a
194 * "buffer" is valid, so that we can know when an error will be generated
195 * instead of updating the binding. However, compat GL has the ridiculous
196 * feature that if you pass a bad name, it just gens a buffer object for you,
197 * so we escape without having to know if things are valid or not.
198 */
199 static void
200 track_vbo_binding(struct gl_context *ctx, GLenum target, GLuint buffer)
201 {
202 struct glthread_state *glthread = ctx->GLThread;
203
204 switch (target) {
205 case GL_ARRAY_BUFFER:
206 glthread->vertex_array_is_vbo = (buffer != 0);
207 break;
208 case GL_ELEMENT_ARRAY_BUFFER:
209 /* The current element array buffer binding is actually tracked in the
210 * vertex array object instead of the context, so this would need to
211 * change on vertex array object updates.
212 */
213 glthread->element_array_is_vbo = (buffer != 0);
214 break;
215 }
216 }
217
218
219 struct marshal_cmd_BindBuffer
220 {
221 struct marshal_cmd_base cmd_base;
222 GLenum target;
223 GLuint buffer;
224 };
225
226 /**
227 * This is just like the code-generated glBindBuffer() support, except that we
228 * call track_vbo_binding().
229 */
230 void
231 _mesa_unmarshal_BindBuffer(struct gl_context *ctx,
232 const struct marshal_cmd_BindBuffer *cmd)
233 {
234 const GLenum target = cmd->target;
235 const GLuint buffer = cmd->buffer;
236 CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
237 }
238 void GLAPIENTRY
239 _mesa_marshal_BindBuffer(GLenum target, GLuint buffer)
240 {
241 GET_CURRENT_CONTEXT(ctx);
242 size_t cmd_size = sizeof(struct marshal_cmd_BindBuffer);
243 struct marshal_cmd_BindBuffer *cmd;
244 debug_print_marshal("BindBuffer");
245
246 track_vbo_binding(ctx, target, buffer);
247
248 if (cmd_size <= MARSHAL_MAX_CMD_SIZE) {
249 cmd = _mesa_glthread_allocate_command(ctx, DISPATCH_CMD_BindBuffer,
250 cmd_size);
251 cmd->target = target;
252 cmd->buffer = buffer;
253 _mesa_post_marshal_hook(ctx);
254 } else {
255 _mesa_glthread_finish(ctx);
256 CALL_BindBuffer(ctx->CurrentServerDispatch, (target, buffer));
257 }
258 }
259