mesa: implement mesh shader draw calls

Reviewed-by: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36976>
This commit is contained in:
Qiang Yu 2025-03-05 20:28:56 +08:00
parent e8a817f196
commit 39d2bf5e68
2 changed files with 257 additions and 0 deletions

View file

@ -6,19 +6,185 @@
#include "mtypes.h"
#include "api_exec_decl.h"
#include "context.h"
#include "state.h"
#include "bufferobj.h"
#include "state_tracker/st_draw.h"
#include "pipe/p_context.h"
#include "pipe/p_screen.h"
static bool
check_mesh_shader_present(struct gl_context *ctx, const char *function)
{
if (!_mesa_has_EXT_mesh_shader(ctx)) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"unsupported function (%s) called",
function);
return false;
}
if (ctx->_Shader->CurrentProgram[MESA_SHADER_MESH] == NULL) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"%s(no active mesh shader)",
function);
return false;
}
return true;
}
static void
draw_mesh_tasks(struct gl_context *ctx, const struct pipe_grid_info *info)
{
FLUSH_FOR_DRAW(ctx);
if (ctx->NewState)
_mesa_update_state(ctx);
ST_PIPELINE_MESH_STATE_MASK(mask);
st_prepare_draw(ctx, mask);
ctx->pipe->draw_mesh_tasks(ctx->pipe, info);
if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) {
_mesa_flush(ctx);
}
}
static bool
validate_draw_mesh_tasks(struct gl_context *ctx, const struct pipe_grid_info *info)
{
if (!check_mesh_shader_present(ctx, "glDrawMeshTasksEXT"))
return false;
const struct pipe_mesh_caps *caps = &ctx->screen->caps.mesh;
const unsigned *max_work_group_count;
unsigned max_work_group_total_count;
if (ctx->_Shader->CurrentProgram[MESA_SHADER_TASK]) {
max_work_group_count = caps->max_task_work_group_count;
max_work_group_total_count = caps->max_task_work_group_total_count;
} else {
max_work_group_count = caps->max_mesh_work_group_count;
max_work_group_total_count = caps->max_mesh_work_group_total_count;
}
for (int i = 0; i < 3; i++) {
if (info->grid[i] > max_work_group_count[i]) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glDrawMeshTasksEXT(num_groups_%c)", 'x' + i);
return false;
}
}
if (info->grid[0] * info->grid[1] * info->grid[2] > max_work_group_total_count) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glDrawMeshTasksEXT exceeds total work group count");
return false;
}
return true;
}
void GLAPIENTRY
_mesa_DrawMeshTasksEXT(GLuint num_groups_x,
GLuint num_groups_y,
GLuint num_groups_z)
{
GET_CURRENT_CONTEXT(ctx);
struct pipe_grid_info info = {
.grid = {num_groups_x, num_groups_y, num_groups_z},
.draw_count = 1,
};
if (!_mesa_is_no_error_enabled(ctx) && !validate_draw_mesh_tasks(ctx, &info))
return;
draw_mesh_tasks(ctx, &info);
}
static bool
validate_draw_mesh_tasks_indirect(struct gl_context *ctx, GLintptr indirect,
GLsizei drawcount, GLsizei stride,
const char *name)
{
if (!check_mesh_shader_present(ctx, name))
return false;
if (indirect & (sizeof(GLuint) - 1)) {
_mesa_error(ctx, GL_INVALID_VALUE,
"%s(indirect is not aligned)", name);
return false;
}
if (indirect < 0) {
_mesa_error(ctx, GL_INVALID_VALUE,
"%s(indirect is less than zero)", name);
return false;
}
if (!ctx->DrawIndirectBuffer) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"%s: no buffer bound to DRAW_INDIRECT_BUFFER", name);
return false;
}
if (_mesa_check_disallowed_mapping(ctx->DrawIndirectBuffer)) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"%s(DRAW_INDIRECT_BUFFER is mapped)", name);
return false;
}
if (stride & 3) {
_mesa_error(ctx, GL_INVALID_VALUE,
"%s(stride is not aligned)", name);
return false;
}
if (stride < 3 * sizeof(GLuint)) {
_mesa_error(ctx, GL_INVALID_VALUE,
"%s(stride is less then DrawMeshTasksIndirectCommandEXT)", name);
return false;
}
if (drawcount <= 0) {
_mesa_error(ctx, GL_INVALID_VALUE,
"%s(drawcount is not positive)", name);
return false;
}
GLsizei size = stride * drawcount;
const uint64_t end = (uint64_t) indirect + size;
if (ctx->DrawIndirectBuffer->Size < end) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"%s(DRAW_INDIRECT_BUFFER too small)", name);
return false;
}
return true;
}
void GLAPIENTRY
_mesa_DrawMeshTasksIndirectEXT(GLintptr indirect)
{
GET_CURRENT_CONTEXT(ctx);
if (!_mesa_is_no_error_enabled(ctx) &&
!validate_draw_mesh_tasks_indirect(
ctx, indirect, 1, 3 * sizeof(GLuint), "glDrawMeshTasksIndirectEXT"))
return;
struct pipe_grid_info info = {
.indirect = ctx->DrawIndirectBuffer->buffer,
.indirect_offset = indirect,
.draw_count = 1,
};
draw_mesh_tasks(ctx, &info);
}
void GLAPIENTRY
@ -26,7 +192,72 @@ _mesa_MultiDrawMeshTasksIndirectEXT(GLintptr indirect,
GLsizei drawcount,
GLsizei stride)
{
GET_CURRENT_CONTEXT(ctx);
/* If <stride> is zero, the array elements are treated as tightly packed. */
if (stride == 0)
stride = 3 * sizeof(GLuint);
if (!_mesa_is_no_error_enabled(ctx) &&
!validate_draw_mesh_tasks_indirect(
ctx, indirect, drawcount, stride, "glMultiDrawMeshTasksIndirectEXT"))
return;
struct pipe_grid_info info = {
.indirect = ctx->DrawIndirectBuffer->buffer,
.indirect_offset = indirect,
.indirect_stride = stride,
.draw_count = drawcount,
};
draw_mesh_tasks(ctx, &info);
}
static bool
validate_multi_draw_mesh_tasks_indirect_count(struct gl_context *ctx,
GLintptr indirect,
GLintptr drawcount,
GLsizei maxdrawcount,
GLsizei stride)
{
const char *name = "glMultiDrawMeshTasksIndirectCountEXT";
if (!validate_draw_mesh_tasks_indirect(ctx, indirect, maxdrawcount, stride, name))
return false;
if (drawcount & (sizeof(GLuint) - 1)) {
_mesa_error(ctx, GL_INVALID_VALUE,
"%s(drawcount is not aligned)", name);
return false;
}
if (drawcount < 0) {
_mesa_error(ctx, GL_INVALID_VALUE,
"%s(drawcount is less than zero)", name);
return false;
}
if (!ctx->ParameterBuffer) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"%s: no buffer bound to PARAMETER_BUFFER", name);
return false;
}
if (_mesa_check_disallowed_mapping(ctx->ParameterBuffer)) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"%s(PARAMETER_BUFFER is mapped)", name);
return false;
}
const uint64_t end = (uint64_t) drawcount + sizeof(GLsizei);
if (ctx->ParameterBuffer->Size < end) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"%s(PARAMETER_BUFFER too small)", name);
return false;
}
return true;
}
void GLAPIENTRY
@ -35,5 +266,25 @@ _mesa_MultiDrawMeshTasksIndirectCountEXT(GLintptr indirect,
GLsizei maxdrawcount,
GLsizei stride)
{
GET_CURRENT_CONTEXT(ctx);
/* If <stride> is zero, the array elements are treated as tightly packed. */
if (stride == 0)
stride = 3 * sizeof(GLuint);
if (!_mesa_is_no_error_enabled(ctx) &&
!validate_multi_draw_mesh_tasks_indirect_count(ctx, indirect, drawcount,
maxdrawcount, stride))
return;
struct pipe_grid_info info = {
.indirect = ctx->DrawIndirectBuffer->buffer,
.indirect_offset = indirect,
.indirect_stride = stride,
.indirect_draw_count = ctx->ParameterBuffer->buffer,
.indirect_draw_count_offset = drawcount,
.draw_count = maxdrawcount,
};
draw_mesh_tasks(ctx, &info);
}

View file

@ -166,6 +166,12 @@ typedef BITSET_DECLARE(st_state_bitset, ST_NUM_ATOMS);
ST_SHADER_STATE_MASK(bitset, CS); \
ST_SET_STATE(bitset, ST_NEW_FB_STATE);
#define ST_PIPELINE_MESH_STATE_MASK(bitset) \
ST_PIPELINE_RENDER_STATE_MASK(bitset); \
BITSET_CLEAR(bitset, ST_NEW_VERTEX_ARRAYS); \
BITSET_CLEAR(bitset, ST_NEW_TESS_STATE); \
BITSET_CLEAR(bitset, ST_NEW_PIXEL_TRANSFER);
#ifdef __cplusplus
}
#endif