blob: cbd166b1095cab6bb31ad356658aa73b3534c1c2 [file] [log] [blame]
/*
* 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);
}