| /* |
| * Copyright © 2014 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #include "glheader.h" |
| #include "bufferobj.h" |
| #include "compute.h" |
| #include "context.h" |
| |
| static bool |
| check_valid_to_compute(struct gl_context *ctx, const char *function) |
| { |
| if (!_mesa_has_compute_shaders(ctx)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "unsupported function (%s) called", |
| function); |
| return false; |
| } |
| |
| /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders: |
| * |
| * "An INVALID_OPERATION error is generated if there is no active program |
| * for the compute shader stage." |
| */ |
| if (ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE] == NULL) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(no active compute shader)", |
| function); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool |
| validate_DispatchCompute(struct gl_context *ctx, const GLuint *num_groups) |
| { |
| if (!check_valid_to_compute(ctx, "glDispatchCompute")) |
| return GL_FALSE; |
| |
| for (int i = 0; i < 3; i++) { |
| /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders: |
| * |
| * "An INVALID_VALUE error is generated if any of num_groups_x, |
| * num_groups_y and num_groups_z are greater than or equal to the |
| * maximum work group count for the corresponding dimension." |
| * |
| * However, the "or equal to" portions appears to be a specification |
| * bug. In all other areas, the specification appears to indicate that |
| * the number of workgroups can match the MAX_COMPUTE_WORK_GROUP_COUNT |
| * value. For example, under DispatchComputeIndirect: |
| * |
| * "If any of num_groups_x, num_groups_y or num_groups_z is greater than |
| * the value of MAX_COMPUTE_WORK_GROUP_COUNT for the corresponding |
| * dimension then the results are undefined." |
| * |
| * Additionally, the OpenGLES 3.1 specification does not contain "or |
| * equal to" as an error condition. |
| */ |
| if (num_groups[i] > ctx->Const.MaxComputeWorkGroupCount[i]) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glDispatchCompute(num_groups_%c)", 'x' + i); |
| return GL_FALSE; |
| } |
| } |
| |
| /* The ARB_compute_variable_group_size spec says: |
| * |
| * "An INVALID_OPERATION error is generated by DispatchCompute if the active |
| * program for the compute shader stage has a variable work group size." |
| */ |
| struct gl_program *prog = ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE]; |
| if (prog->info.cs.local_size_variable) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glDispatchCompute(variable work group size forbidden)"); |
| return GL_FALSE; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| static bool |
| validate_DispatchComputeGroupSizeARB(struct gl_context *ctx, |
| const GLuint *num_groups, |
| const GLuint *group_size) |
| { |
| GLuint total_invocations = 1; |
| |
| if (!check_valid_to_compute(ctx, "glDispatchComputeGroupSizeARB")) |
| return GL_FALSE; |
| |
| /* The ARB_compute_variable_group_size spec says: |
| * |
| * "An INVALID_OPERATION error is generated by |
| * DispatchComputeGroupSizeARB if the active program for the compute |
| * shader stage has a fixed work group size." |
| */ |
| struct gl_program *prog = ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE]; |
| if (!prog->info.cs.local_size_variable) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "glDispatchComputeGroupSizeARB(fixed work group size " |
| "forbidden)"); |
| return GL_FALSE; |
| } |
| |
| for (int i = 0; i < 3; i++) { |
| /* The ARB_compute_variable_group_size spec says: |
| * |
| * "An INVALID_VALUE error is generated if any of num_groups_x, |
| * num_groups_y and num_groups_z are greater than or equal to the |
| * maximum work group count for the corresponding dimension." |
| */ |
| if (num_groups[i] > ctx->Const.MaxComputeWorkGroupCount[i]) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glDispatchComputeGroupSizeARB(num_groups_%c)", 'x' + i); |
| return GL_FALSE; |
| } |
| |
| /* The ARB_compute_variable_group_size spec says: |
| * |
| * "An INVALID_VALUE error is generated by DispatchComputeGroupSizeARB if |
| * any of <group_size_x>, <group_size_y>, or <group_size_z> is less than |
| * or equal to zero or greater than the maximum local work group size |
| * for compute shaders with variable group size |
| * (MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB) in the corresponding |
| * dimension." |
| * |
| * However, the "less than" is a spec bug because they are declared as |
| * unsigned integers. |
| */ |
| if (group_size[i] == 0 || |
| group_size[i] > ctx->Const.MaxComputeVariableGroupSize[i]) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glDispatchComputeGroupSizeARB(group_size_%c)", 'x' + i); |
| return GL_FALSE; |
| } |
| |
| total_invocations *= group_size[i]; |
| } |
| |
| /* The ARB_compute_variable_group_size spec says: |
| * |
| * "An INVALID_VALUE error is generated by DispatchComputeGroupSizeARB if |
| * the product of <group_size_x>, <group_size_y>, and <group_size_z> exceeds |
| * the implementation-dependent maximum local work group invocation count |
| * for compute shaders with variable group size |
| * (MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB)." |
| */ |
| if (total_invocations > ctx->Const.MaxComputeVariableGroupInvocations) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "glDispatchComputeGroupSizeARB(product of local_sizes " |
| "exceeds MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB " |
| "(%d > %d))", total_invocations, |
| ctx->Const.MaxComputeVariableGroupInvocations); |
| return GL_FALSE; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| static bool |
| valid_dispatch_indirect(struct gl_context *ctx, GLintptr indirect) |
| { |
| GLsizei size = 3 * sizeof(GLuint); |
| const uint64_t end = (uint64_t) indirect + size; |
| const char *name = "glDispatchComputeIndirect"; |
| |
| if (!check_valid_to_compute(ctx, name)) |
| return GL_FALSE; |
| |
| /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders: |
| * |
| * "An INVALID_VALUE error is generated if indirect is negative or is not a |
| * multiple of four." |
| */ |
| if (indirect & (sizeof(GLuint) - 1)) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "%s(indirect is not aligned)", name); |
| return GL_FALSE; |
| } |
| |
| if (indirect < 0) { |
| _mesa_error(ctx, GL_INVALID_VALUE, |
| "%s(indirect is less than zero)", name); |
| return GL_FALSE; |
| } |
| |
| /* From the OpenGL 4.3 Core Specification, Chapter 19, Compute Shaders: |
| * |
| * "An INVALID_OPERATION error is generated if no buffer is bound to the |
| * DRAW_INDIRECT_BUFFER binding, or if the command would source data |
| * beyond the end of the buffer object." |
| */ |
| if (!_mesa_is_bufferobj(ctx->DispatchIndirectBuffer)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s: no buffer bound to DISPATCH_INDIRECT_BUFFER", name); |
| return GL_FALSE; |
| } |
| |
| if (_mesa_check_disallowed_mapping(ctx->DispatchIndirectBuffer)) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(DISPATCH_INDIRECT_BUFFER is mapped)", name); |
| return GL_FALSE; |
| } |
| |
| if (ctx->DispatchIndirectBuffer->Size < end) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(DISPATCH_INDIRECT_BUFFER too small)", name); |
| return GL_FALSE; |
| } |
| |
| /* The ARB_compute_variable_group_size spec says: |
| * |
| * "An INVALID_OPERATION error is generated if the active program for the |
| * compute shader stage has a variable work group size." |
| */ |
| struct gl_program *prog = ctx->_Shader->CurrentProgram[MESA_SHADER_COMPUTE]; |
| if (prog->info.cs.local_size_variable) { |
| _mesa_error(ctx, GL_INVALID_OPERATION, |
| "%s(variable work group size forbidden)", name); |
| return GL_FALSE; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| static ALWAYS_INLINE void |
| dispatch_compute(GLuint num_groups_x, GLuint num_groups_y, |
| GLuint num_groups_z, bool no_error) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const GLuint num_groups[3] = { num_groups_x, num_groups_y, num_groups_z }; |
| |
| FLUSH_CURRENT(ctx, 0); |
| |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, "glDispatchCompute(%d, %d, %d)\n", |
| num_groups_x, num_groups_y, num_groups_z); |
| |
| if (!no_error && !validate_DispatchCompute(ctx, num_groups)) |
| return; |
| |
| if (num_groups_x == 0u || num_groups_y == 0u || num_groups_z == 0u) |
| return; |
| |
| ctx->Driver.DispatchCompute(ctx, num_groups); |
| } |
| |
| void GLAPIENTRY |
| _mesa_DispatchCompute_no_error(GLuint num_groups_x, GLuint num_groups_y, |
| GLuint num_groups_z) |
| { |
| dispatch_compute(num_groups_x, num_groups_y, num_groups_z, true); |
| } |
| |
| void GLAPIENTRY |
| _mesa_DispatchCompute(GLuint num_groups_x, |
| GLuint num_groups_y, |
| GLuint num_groups_z) |
| { |
| dispatch_compute(num_groups_x, num_groups_y, num_groups_z, false); |
| } |
| |
| static ALWAYS_INLINE void |
| dispatch_compute_indirect(GLintptr indirect, bool no_error) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| |
| FLUSH_CURRENT(ctx, 0); |
| |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, "glDispatchComputeIndirect(%ld)\n", (long) indirect); |
| |
| if (!no_error && !valid_dispatch_indirect(ctx, indirect)) |
| return; |
| |
| ctx->Driver.DispatchComputeIndirect(ctx, indirect); |
| } |
| |
| extern void GLAPIENTRY |
| _mesa_DispatchComputeIndirect_no_error(GLintptr indirect) |
| { |
| dispatch_compute_indirect(indirect, true); |
| } |
| |
| extern void GLAPIENTRY |
| _mesa_DispatchComputeIndirect(GLintptr indirect) |
| { |
| dispatch_compute_indirect(indirect, false); |
| } |
| |
| 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) |
| { |
| GET_CURRENT_CONTEXT(ctx); |
| const GLuint num_groups[3] = { num_groups_x, num_groups_y, num_groups_z }; |
| const GLuint group_size[3] = { group_size_x, group_size_y, group_size_z }; |
| |
| FLUSH_CURRENT(ctx, 0); |
| |
| if (MESA_VERBOSE & VERBOSE_API) |
| _mesa_debug(ctx, |
| "glDispatchComputeGroupSizeARB(%d, %d, %d, %d, %d, %d)\n", |
| num_groups_x, num_groups_y, num_groups_z, |
| group_size_x, group_size_y, group_size_z); |
| |
| if (!no_error && |
| !validate_DispatchComputeGroupSizeARB(ctx, num_groups, group_size)) |
| return; |
| |
| if (num_groups_x == 0u || num_groups_y == 0u || num_groups_z == 0u) |
| return; |
| |
| ctx->Driver.DispatchComputeGroupSize(ctx, num_groups, group_size); |
| } |
| |
| 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) |
| { |
| dispatch_compute_group_size(num_groups_x, num_groups_y, num_groups_z, |
| group_size_x, group_size_y, group_size_z, |
| true); |
| } |
| |
| 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) |
| { |
| dispatch_compute_group_size(num_groups_x, num_groups_y, num_groups_z, |
| group_size_x, group_size_y, group_size_z, |
| false); |
| } |