1 /*
2  * Copyright © 2014 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
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "glheader.h"
25 #include "bufferobj.h"
26 #include "compute.h"
27 #include "context.h"
28 
29 static bool
check_valid_to_compute(struct gl_context * ctx,const char * function)30 check_valid_to_compute(struct gl_context *ctx, const char *function)
31 {
32    if (!_mesa_has_compute_shaders(ctx)) {
33       _mesa_error(ctx, GL_INVALID_OPERATION,
34                   "unsupported function (%s) called",
35                   function);
36       return false;
37    }
38 
39    /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders:
40     *
41     * "An INVALID_OPERATION error is generated if there is no active program
42     *  for the compute shader stage."
43     */
44    if (ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE] == NULL) {
45       _mesa_error(ctx, GL_INVALID_OPERATION,
46                   "%s(no active compute shader)",
47                   function);
48       return false;
49    }
50 
51    return true;
52 }
53 
54 static bool
validate_DispatchCompute(struct gl_context * ctx,const GLuint * num_groups)55 validate_DispatchCompute(struct gl_context *ctx, const GLuint *num_groups)
56 {
57    if (!check_valid_to_compute(ctx, "glDispatchCompute"))
58       return GL_FALSE;
59 
60    for (int i = 0; i < 3; i++) {
61       /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders:
62        *
63        * "An INVALID_VALUE error is generated if any of num_groups_x,
64        *  num_groups_y and num_groups_z are greater than or equal to the
65        *  maximum work group count for the corresponding dimension."
66        *
67        * However, the "or equal to" portions appears to be a specification
68        * bug. In all other areas, the specification appears to indicate that
69        * the number of workgroups can match the MAX_COMPUTE_WORK_GROUP_COUNT
70        * value. For example, under DispatchComputeIndirect:
71        *
72        * "If any of num_groups_x, num_groups_y or num_groups_z is greater than
73        *  the value of MAX_COMPUTE_WORK_GROUP_COUNT for the corresponding
74        *  dimension then the results are undefined."
75        *
76        * Additionally, the OpenGLES 3.1 specification does not contain "or
77        * equal to" as an error condition.
78        */
79       if (num_groups[i] > ctx->Const.MaxComputeWorkGroupCount[i]) {
80          _mesa_error(ctx, GL_INVALID_VALUE,
81                      "glDispatchCompute(num_groups_%c)", 'x' + i);
82          return GL_FALSE;
83       }
84    }
85 
86    /* The ARB_compute_variable_group_size spec says:
87     *
88     * "An INVALID_OPERATION error is generated by DispatchCompute if the active
89     *  program for the compute shader stage has a variable work group size."
90     */
91    struct gl_program *prog = ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE];
92    if (prog->info.cs.local_size_variable) {
93       _mesa_error(ctx, GL_INVALID_OPERATION,
94                   "glDispatchCompute(variable work group size forbidden)");
95       return GL_FALSE;
96    }
97 
98    return GL_TRUE;
99 }
100 
101 static bool
validate_DispatchComputeGroupSizeARB(struct gl_context * ctx,const GLuint * num_groups,const GLuint * group_size)102 validate_DispatchComputeGroupSizeARB(struct gl_context *ctx,
103                                      const GLuint *num_groups,
104                                      const GLuint *group_size)
105 {
106    GLuint total_invocations = 1;
107 
108    if (!check_valid_to_compute(ctx, "glDispatchComputeGroupSizeARB"))
109       return GL_FALSE;
110 
111    /* The ARB_compute_variable_group_size spec says:
112     *
113     * "An INVALID_OPERATION error is generated by
114     *  DispatchComputeGroupSizeARB if the active program for the compute
115     *  shader stage has a fixed work group size."
116     */
117    struct gl_program *prog = ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE];
118    if (!prog->info.cs.local_size_variable) {
119       _mesa_error(ctx, GL_INVALID_OPERATION,
120                   "glDispatchComputeGroupSizeARB(fixed work group size "
121                   "forbidden)");
122       return GL_FALSE;
123    }
124 
125    for (int i = 0; i < 3; i++) {
126       /* The ARB_compute_variable_group_size spec says:
127        *
128        * "An INVALID_VALUE error is generated if any of num_groups_x,
129        *  num_groups_y and num_groups_z are greater than or equal to the
130        *  maximum work group count for the corresponding dimension."
131        */
132       if (num_groups[i] > ctx->Const.MaxComputeWorkGroupCount[i]) {
133          _mesa_error(ctx, GL_INVALID_VALUE,
134                      "glDispatchComputeGroupSizeARB(num_groups_%c)", 'x' + i);
135          return GL_FALSE;
136       }
137 
138       /* The ARB_compute_variable_group_size spec says:
139        *
140        * "An INVALID_VALUE error is generated by DispatchComputeGroupSizeARB if
141        *  any of <group_size_x>, <group_size_y>, or <group_size_z> is less than
142        *  or equal to zero or greater than the maximum local work group size
143        *  for compute shaders with variable group size
144        *  (MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB) in the corresponding
145        *  dimension."
146        *
147        * However, the "less than" is a spec bug because they are declared as
148        * unsigned integers.
149        */
150       if (group_size[i] == 0 ||
151           group_size[i] > ctx->Const.MaxComputeVariableGroupSize[i]) {
152          _mesa_error(ctx, GL_INVALID_VALUE,
153                      "glDispatchComputeGroupSizeARB(group_size_%c)", 'x' + i);
154          return GL_FALSE;
155       }
156 
157       total_invocations *= group_size[i];
158    }
159 
160    /* The ARB_compute_variable_group_size spec says:
161     *
162     * "An INVALID_VALUE error is generated by DispatchComputeGroupSizeARB if
163     *  the product of <group_size_x>, <group_size_y>, and <group_size_z> exceeds
164     *  the implementation-dependent maximum local work group invocation count
165     *  for compute shaders with variable group size
166     *  (MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB)."
167     */
168    if (total_invocations > ctx->Const.MaxComputeVariableGroupInvocations) {
169       _mesa_error(ctx, GL_INVALID_VALUE,
170                   "glDispatchComputeGroupSizeARB(product of local_sizes "
171                   "exceeds MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB "
172                   "(%d > %d))", total_invocations,
173                   ctx->Const.MaxComputeVariableGroupInvocations);
174       return GL_FALSE;
175    }
176 
177    return GL_TRUE;
178 }
179 
180 static bool
valid_dispatch_indirect(struct gl_context * ctx,GLintptr indirect)181 valid_dispatch_indirect(struct gl_context *ctx,  GLintptr indirect)
182 {
183    GLsizei size = 3 * sizeof(GLuint);
184    const uint64_t end = (uint64_t) indirect + size;
185    const char *name = "glDispatchComputeIndirect";
186 
187    if (!check_valid_to_compute(ctx, name))
188       return GL_FALSE;
189 
190    /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders:
191     *
192     * "An INVALID_VALUE error is generated if indirect is negative or is not a
193     *  multiple of four."
194     */
195    if (indirect & (sizeof(GLuint) - 1)) {
196       _mesa_error(ctx, GL_INVALID_VALUE,
197                   "%s(indirect is not aligned)", name);
198       return GL_FALSE;
199    }
200 
201    if (indirect < 0) {
202       _mesa_error(ctx, GL_INVALID_VALUE,
203                   "%s(indirect is less than zero)", name);
204       return GL_FALSE;
205    }
206 
207    /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders:
208     *
209     * "An INVALID_OPERATION error is generated if no buffer is bound to the
210     *  DRAW_INDIRECT_BUFFER binding, or if the command would source data
211     *  beyond the end of the buffer object."
212     */
213    if (!_mesa_is_bufferobj(ctx->DispatchIndirectBuffer)) {
214       _mesa_error(ctx, GL_INVALID_OPERATION,
215                   "%s: no buffer bound to DISPATCH_INDIRECT_BUFFER", name);
216       return GL_FALSE;
217    }
218 
219    if (_mesa_check_disallowed_mapping(ctx->DispatchIndirectBuffer)) {
220       _mesa_error(ctx, GL_INVALID_OPERATION,
221                   "%s(DISPATCH_INDIRECT_BUFFER is mapped)", name);
222       return GL_FALSE;
223    }
224 
225    if (ctx->DispatchIndirectBuffer->Size < end) {
226       _mesa_error(ctx, GL_INVALID_OPERATION,
227                   "%s(DISPATCH_INDIRECT_BUFFER too small)", name);
228       return GL_FALSE;
229    }
230 
231    /* The ARB_compute_variable_group_size spec says:
232     *
233     * "An INVALID_OPERATION error is generated if the active program for the
234     *  compute shader stage has a variable work group size."
235     */
236    struct gl_program *prog = ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE];
237    if (prog->info.cs.local_size_variable) {
238       _mesa_error(ctx, GL_INVALID_OPERATION,
239                   "%s(variable work group size forbidden)", name);
240       return GL_FALSE;
241    }
242 
243    return GL_TRUE;
244 }
245 
246 static ALWAYS_INLINE void
dispatch_compute(GLuint num_groups_x,GLuint num_groups_y,GLuint num_groups_z,bool no_error)247 dispatch_compute(GLuint num_groups_x, GLuint num_groups_y,
248                  GLuint num_groups_z, bool no_error)
249 {
250    GET_CURRENT_CONTEXT(ctx);
251    const GLuint num_groups[3] = { num_groups_x, num_groups_y, num_groups_z };
252 
253    FLUSH_CURRENT(ctx, 0);
254 
255    if (MESA_VERBOSE & VERBOSE_API)
256       _mesa_debug(ctx, "glDispatchCompute(%d, %d, %d)\n",
257                   num_groups_x, num_groups_y, num_groups_z);
258 
259    if (!no_error && !validate_DispatchCompute(ctx, num_groups))
260       return;
261 
262    if (num_groups_x == 0u || num_groups_y == 0u || num_groups_z == 0u)
263        return;
264 
265    ctx->Driver.DispatchCompute(ctx, num_groups);
266 }
267 
268 void GLAPIENTRY
_mesa_DispatchCompute_no_error(GLuint num_groups_x,GLuint num_groups_y,GLuint num_groups_z)269 _mesa_DispatchCompute_no_error(GLuint num_groups_x, GLuint num_groups_y,
270                                GLuint num_groups_z)
271 {
272    dispatch_compute(num_groups_x, num_groups_y, num_groups_z, true);
273 }
274 
275 void GLAPIENTRY
_mesa_DispatchCompute(GLuint num_groups_x,GLuint num_groups_y,GLuint num_groups_z)276 _mesa_DispatchCompute(GLuint num_groups_x,
277                       GLuint num_groups_y,
278                       GLuint num_groups_z)
279 {
280    dispatch_compute(num_groups_x, num_groups_y, num_groups_z, false);
281 }
282 
283 static ALWAYS_INLINE void
dispatch_compute_indirect(GLintptr indirect,bool no_error)284 dispatch_compute_indirect(GLintptr indirect, bool no_error)
285 {
286    GET_CURRENT_CONTEXT(ctx);
287 
288    FLUSH_CURRENT(ctx, 0);
289 
290    if (MESA_VERBOSE & VERBOSE_API)
291       _mesa_debug(ctx, "glDispatchComputeIndirect(%ld)\n", (long) indirect);
292 
293    if (!no_error && !valid_dispatch_indirect(ctx, indirect))
294       return;
295 
296    ctx->Driver.DispatchComputeIndirect(ctx, indirect);
297 }
298 
299 extern void GLAPIENTRY
_mesa_DispatchComputeIndirect_no_error(GLintptr indirect)300 _mesa_DispatchComputeIndirect_no_error(GLintptr indirect)
301 {
302    dispatch_compute_indirect(indirect, true);
303 }
304 
305 extern void GLAPIENTRY
_mesa_DispatchComputeIndirect(GLintptr indirect)306 _mesa_DispatchComputeIndirect(GLintptr indirect)
307 {
308    dispatch_compute_indirect(indirect, false);
309 }
310 
311 static ALWAYS_INLINE void
dispatch_compute_group_size(GLuint num_groups_x,GLuint num_groups_y,GLuint num_groups_z,GLuint group_size_x,GLuint group_size_y,GLuint group_size_z,bool no_error)312 dispatch_compute_group_size(GLuint num_groups_x, GLuint num_groups_y,
313                             GLuint num_groups_z, GLuint group_size_x,
314                             GLuint group_size_y, GLuint group_size_z,
315                             bool no_error)
316 {
317    GET_CURRENT_CONTEXT(ctx);
318    const GLuint num_groups[3] = { num_groups_x, num_groups_y, num_groups_z };
319    const GLuint group_size[3] = { group_size_x, group_size_y, group_size_z };
320 
321    FLUSH_CURRENT(ctx, 0);
322 
323    if (MESA_VERBOSE & VERBOSE_API)
324       _mesa_debug(ctx,
325                   "glDispatchComputeGroupSizeARB(%d, %d, %d, %d, %d, %d)\n",
326                   num_groups_x, num_groups_y, num_groups_z,
327                   group_size_x, group_size_y, group_size_z);
328 
329    if (!no_error &&
330        !validate_DispatchComputeGroupSizeARB(ctx, num_groups, group_size))
331       return;
332 
333    if (num_groups_x == 0u || num_groups_y == 0u || num_groups_z == 0u)
334        return;
335 
336    ctx->Driver.DispatchComputeGroupSize(ctx, num_groups, group_size);
337 }
338 
339 void GLAPIENTRY
_mesa_DispatchComputeGroupSizeARB_no_error(GLuint num_groups_x,GLuint num_groups_y,GLuint num_groups_z,GLuint group_size_x,GLuint group_size_y,GLuint group_size_z)340 _mesa_DispatchComputeGroupSizeARB_no_error(GLuint num_groups_x,
341                                            GLuint num_groups_y,
342                                            GLuint num_groups_z,
343                                            GLuint group_size_x,
344                                            GLuint group_size_y,
345                                            GLuint group_size_z)
346 {
347    dispatch_compute_group_size(num_groups_x, num_groups_y, num_groups_z,
348                                group_size_x, group_size_y, group_size_z,
349                                true);
350 }
351 
352 void GLAPIENTRY
_mesa_DispatchComputeGroupSizeARB(GLuint num_groups_x,GLuint num_groups_y,GLuint num_groups_z,GLuint group_size_x,GLuint group_size_y,GLuint group_size_z)353 _mesa_DispatchComputeGroupSizeARB(GLuint num_groups_x, GLuint num_groups_y,
354                                   GLuint num_groups_z, GLuint group_size_x,
355                                   GLuint group_size_y, GLuint group_size_z)
356 {
357    dispatch_compute_group_size(num_groups_x, num_groups_y, num_groups_z,
358                                group_size_x, group_size_y, group_size_z,
359                                false);
360 }
361