mesa: precompute draw time prim validation during state changes

This moves the body of _mesa_valid_prim_mode into new function
_mesa_update_valid_to_render_state, which is called when the affected
states are changed and sets new variable gl_context::ValidPrimMask,
which determines errors reported by draw calls.

_mesa_valid_prim_mode only has to check ValidPrimMask and choose
between GL_INVALID_ENUM and GL_INVALID_OPERATION depending on whether
the primitive would be allowed by the GL version and extensions
(GL_INVALID_OPERATION) or not (GL_INVALID_ENUM).

Reviewed-by: Zoltán Böszörményi <zboszor@gmail.com>
Reviewed-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/8798>
This commit is contained in:
Marek Olšák 2021-01-16 09:36:45 -05:00 committed by Marge Bot
parent f6913fb366
commit 4be5fc2916
9 changed files with 203 additions and 177 deletions

View file

@ -351,20 +351,121 @@ check_valid_to_render(struct gl_context *ctx, const char *function)
}
/**
* Is 'mode' a valid value for glBegin(), glDrawArrays(), glDrawElements(),
* etc? Also, do additional checking related to transformation feedback.
* Note: this function cannot be called during glNewList(GL_COMPILE) because
* this code depends on current transform feedback state.
* Also, do additional checking related to tessellation shaders.
* Compute the bitmask of allowed primitive types (ValidPrimMask) depending
* on shaders and current states. This is used by draw validation.
*
* If some combinations of shaders and states are invalid, ValidPrimMask is
* set to 0, which will always set GL_INVALID_OPERATION in draw calls
* except for invalid enums, which will set GL_INVALID_ENUM, minimizing
* the number of gl_context variables that have to be read by draw calls.
*/
GLboolean
_mesa_valid_prim_mode(struct gl_context *ctx, GLenum mode, const char *name)
void
_mesa_update_valid_to_render_state(struct gl_context *ctx)
{
bool valid_enum = _mesa_is_valid_prim_mode(ctx, mode);
struct gl_pipeline_object *shader = ctx->_Shader;
unsigned mask = ctx->SupportedPrimMask;
if (!valid_enum) {
_mesa_error(ctx, GL_INVALID_ENUM, "%s(mode=%x)", name, mode);
return GL_FALSE;
if (_mesa_is_no_error_enabled(ctx)) {
ctx->ValidPrimMask = mask;
return;
}
/* Start with an empty mask and set this to the trimmed mask at the end. */
ctx->ValidPrimMask = 0;
/* From GL_INTEL_conservative_rasterization spec:
*
* The conservative rasterization option applies only to polygons with
* PolygonMode state set to FILL. Draw requests for polygons with different
* PolygonMode setting or for other primitive types (points/lines) generate
* INVALID_OPERATION error.
*/
if (ctx->IntelConservativeRasterization) {
if (ctx->Polygon.FrontMode != GL_FILL ||
ctx->Polygon.BackMode != GL_FILL) {
return;
} else {
mask &= (1 << GL_TRIANGLES) |
(1 << GL_TRIANGLE_STRIP) |
(1 << GL_TRIANGLE_FAN) |
(1 << GL_QUADS) |
(1 << GL_QUAD_STRIP) |
(1 << GL_POLYGON) |
(1 << GL_TRIANGLES_ADJACENCY) |
(1 << GL_TRIANGLE_STRIP_ADJACENCY);
}
}
/* From the GL_EXT_transform_feedback spec:
*
* "The error INVALID_OPERATION is generated if Begin, or any command
* that performs an explicit Begin, is called when:
*
* * a geometry shader is not active and <mode> does not match the
* allowed begin modes for the current transform feedback state as
* given by table X.1.
*
* * a geometry shader is active and the output primitive type of the
* geometry shader does not match the allowed begin modes for the
* current transform feedback state as given by table X.1.
*
*/
if (_mesa_is_xfb_active_and_unpaused(ctx)) {
if(shader->CurrentProgram[MESA_SHADER_GEOMETRY]) {
switch (shader->CurrentProgram[MESA_SHADER_GEOMETRY]->
info.gs.output_primitive) {
case GL_POINTS:
if (ctx->TransformFeedback.Mode != GL_POINTS)
mask = 0;
break;
case GL_LINE_STRIP:
if (ctx->TransformFeedback.Mode != GL_LINES)
mask = 0;
break;
case GL_TRIANGLE_STRIP:
if (ctx->TransformFeedback.Mode != GL_TRIANGLES)
mask = 0;
break;
default:
mask = 0;
}
}
else if (shader->CurrentProgram[MESA_SHADER_TESS_EVAL]) {
struct gl_program *tes =
shader->CurrentProgram[MESA_SHADER_TESS_EVAL];
if (tes->info.tess.point_mode) {
if (ctx->TransformFeedback.Mode != GL_POINTS)
mask = 0;
} else if (tes->info.tess.primitive_mode == GL_ISOLINES) {
if (ctx->TransformFeedback.Mode != GL_LINES)
mask = 0;
} else {
if (ctx->TransformFeedback.Mode != GL_TRIANGLES)
mask = 0;
}
}
else {
switch (ctx->TransformFeedback.Mode) {
case GL_POINTS:
mask &= 1 << GL_POINTS;
break;
case GL_LINES:
mask &= (1 << GL_LINES) |
(1 << GL_LINE_LOOP) |
(1 << GL_LINE_STRIP);
break;
case GL_TRIANGLES:
/* TODO: This doesn't look right, but it matches the original code. */
mask &= ~((1 << GL_POINTS) |
(1 << GL_LINES) |
(1 << GL_LINE_LOOP) |
(1 << GL_LINE_STRIP));
break;
}
}
if (!mask)
return;
}
/* From the OpenGL 4.5 specification, section 11.3.1:
@ -395,62 +496,51 @@ _mesa_valid_prim_mode(struct gl_context *ctx, GLenum mode, const char *name)
* the draw primitive mode, the tessellation evaluation shader primitive
* mode should be used for the checking.
*/
if (ctx->_Shader->CurrentProgram[MESA_SHADER_GEOMETRY]) {
if (shader->CurrentProgram[MESA_SHADER_GEOMETRY]) {
const GLenum geom_mode =
ctx->_Shader->CurrentProgram[MESA_SHADER_GEOMETRY]->
shader->CurrentProgram[MESA_SHADER_GEOMETRY]->
info.gs.input_primitive;
struct gl_program *tes =
ctx->_Shader->CurrentProgram[MESA_SHADER_TESS_EVAL];
GLenum mode_before_gs = mode;
shader->CurrentProgram[MESA_SHADER_TESS_EVAL];
if (tes) {
bool valid;
if (tes->info.tess.point_mode)
mode_before_gs = GL_POINTS;
valid = geom_mode == GL_POINTS;
else if (tes->info.tess.primitive_mode == GL_ISOLINES)
mode_before_gs = GL_LINES;
valid = geom_mode == GL_LINES;
else
/* the GL_QUADS mode generates triangles too */
mode_before_gs = GL_TRIANGLES;
}
valid = geom_mode == GL_TRIANGLES;
switch (mode_before_gs) {
case GL_POINTS:
valid_enum = (geom_mode == GL_POINTS);
break;
case GL_LINES:
case GL_LINE_LOOP:
case GL_LINE_STRIP:
valid_enum = (geom_mode == GL_LINES);
break;
case GL_TRIANGLES:
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
valid_enum = (geom_mode == GL_TRIANGLES);
break;
case GL_QUADS:
case GL_QUAD_STRIP:
case GL_POLYGON:
valid_enum = false;
break;
case GL_LINES_ADJACENCY:
case GL_LINE_STRIP_ADJACENCY:
valid_enum = (geom_mode == GL_LINES_ADJACENCY);
break;
case GL_TRIANGLES_ADJACENCY:
case GL_TRIANGLE_STRIP_ADJACENCY:
valid_enum = (geom_mode == GL_TRIANGLES_ADJACENCY);
break;
default:
valid_enum = false;
break;
}
if (!valid_enum) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"%s(mode=%s vs geometry shader input %s)",
name,
_mesa_lookup_prim_by_nr(mode_before_gs),
_mesa_lookup_prim_by_nr(geom_mode));
return GL_FALSE;
/* TES and GS use incompatible primitive types. Discard all draws. */
if (!valid)
return;
} else {
switch (geom_mode) {
case GL_POINTS:
mask &= 1 << GL_POINTS;
break;
case GL_LINES:
mask &= (1 << GL_LINES) |
(1 << GL_LINE_LOOP) |
(1 << GL_LINE_STRIP);
break;
case GL_TRIANGLES:
mask &= (1 << GL_TRIANGLES) |
(1 << GL_TRIANGLE_STRIP) |
(1 << GL_TRIANGLE_FAN);
break;
case GL_LINES_ADJACENCY:
mask &= (1 << GL_LINES_ADJACENCY) |
(1 << GL_LINE_STRIP_ADJACENCY);
break;
case GL_TRIANGLES_ADJACENCY:
mask &= (1 << GL_TRIANGLES_ADJACENCY) |
(1 << GL_TRIANGLE_STRIP_ADJACENCY);
break;
}
}
}
@ -468,133 +558,40 @@ _mesa_valid_prim_mode(struct gl_context *ctx, GLenum mode, const char *name)
* PATCHES."
*
*/
if (ctx->_Shader->CurrentProgram[MESA_SHADER_TESS_EVAL] ||
ctx->_Shader->CurrentProgram[MESA_SHADER_TESS_CTRL]) {
if (mode != GL_PATCHES) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"only GL_PATCHES valid with tessellation");
return GL_FALSE;
}
if (shader->CurrentProgram[MESA_SHADER_TESS_EVAL] ||
shader->CurrentProgram[MESA_SHADER_TESS_CTRL]) {
mask &= 1 << GL_PATCHES;
}
else {
if (mode == GL_PATCHES) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"GL_PATCHES only valid with tessellation");
return GL_FALSE;
}
mask &= ~(1 << GL_PATCHES);
}
/* From the GL_EXT_transform_feedback spec:
*
* "The error INVALID_OPERATION is generated if Begin, or any command
* that performs an explicit Begin, is called when:
*
* * a geometry shader is not active and <mode> does not match the
* allowed begin modes for the current transform feedback state as
* given by table X.1.
*
* * a geometry shader is active and the output primitive type of the
* geometry shader does not match the allowed begin modes for the
* current transform feedback state as given by table X.1.
*
*/
if (_mesa_is_xfb_active_and_unpaused(ctx)) {
GLboolean pass = GL_TRUE;
ctx->ValidPrimMask = mask;
}
if(ctx->_Shader->CurrentProgram[MESA_SHADER_GEOMETRY]) {
switch (ctx->_Shader->CurrentProgram[MESA_SHADER_GEOMETRY]->
info.gs.output_primitive) {
case GL_POINTS:
pass = ctx->TransformFeedback.Mode == GL_POINTS;
break;
case GL_LINE_STRIP:
pass = ctx->TransformFeedback.Mode == GL_LINES;
break;
case GL_TRIANGLE_STRIP:
pass = ctx->TransformFeedback.Mode == GL_TRIANGLES;
break;
default:
pass = GL_FALSE;
}
}
else if (ctx->_Shader->CurrentProgram[MESA_SHADER_TESS_EVAL]) {
struct gl_program *tes =
ctx->_Shader->CurrentProgram[MESA_SHADER_TESS_EVAL];
if (tes->info.tess.point_mode)
pass = ctx->TransformFeedback.Mode == GL_POINTS;
else if (tes->info.tess.primitive_mode == GL_ISOLINES)
pass = ctx->TransformFeedback.Mode == GL_LINES;
else
pass = ctx->TransformFeedback.Mode == GL_TRIANGLES;
}
else {
switch (mode) {
case GL_POINTS:
pass = ctx->TransformFeedback.Mode == GL_POINTS;
break;
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
pass = ctx->TransformFeedback.Mode == GL_LINES;
break;
default:
pass = ctx->TransformFeedback.Mode == GL_TRIANGLES;
break;
}
}
if (!pass) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"%s(mode=%s vs transform feedback %s)",
name,
_mesa_lookup_prim_by_nr(mode),
_mesa_lookup_prim_by_nr(ctx->TransformFeedback.Mode));
return GL_FALSE;
}
/**
* Is 'mode' a valid value for glBegin(), glDrawArrays(), glDrawElements(),
* etc? Also, do additional checking related to transformation feedback.
* Note: this function cannot be called during glNewList(GL_COMPILE) because
* this code depends on current transform feedback state.
* Also, do additional checking related to tessellation shaders.
*/
GLboolean
_mesa_valid_prim_mode(struct gl_context *ctx, GLenum mode, const char *name)
{
/* All primitive type enums are less than 32, so we can use the shift. */
if (mode >= 32 || !((1u << mode) & ctx->ValidPrimMask)) {
/* If the primitive type is not in SupportedPrimMask, set GL_INVALID_ENUM,
* else set GL_INVALID_OPERATION.
*/
_mesa_error(ctx,
mode >= 32 || !((1u << mode) & ctx->SupportedPrimMask) ?
GL_INVALID_ENUM : GL_INVALID_OPERATION,
"%s(mode=%x)", name, mode);
return false;
}
/* From GL_INTEL_conservative_rasterization spec:
*
* The conservative rasterization option applies only to polygons with
* PolygonMode state set to FILL. Draw requests for polygons with different
* PolygonMode setting or for other primitive types (points/lines) generate
* INVALID_OPERATION error.
*/
if (ctx->IntelConservativeRasterization) {
GLboolean pass = GL_TRUE;
switch (mode) {
case GL_POINTS:
case GL_LINES:
case GL_LINE_LOOP:
case GL_LINE_STRIP:
case GL_LINES_ADJACENCY:
case GL_LINE_STRIP_ADJACENCY:
pass = GL_FALSE;
break;
case GL_TRIANGLES:
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_QUADS:
case GL_QUAD_STRIP:
case GL_POLYGON:
case GL_TRIANGLES_ADJACENCY:
case GL_TRIANGLE_STRIP_ADJACENCY:
if (ctx->Polygon.FrontMode != GL_FILL ||
ctx->Polygon.BackMode != GL_FILL)
pass = GL_FALSE;
break;
default:
pass = GL_FALSE;
}
if (!pass) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"mode=%s invalid with GL_INTEL_conservative_rasterization",
_mesa_lookup_prim_by_nr(mode));
return GL_FALSE;
}
}
return GL_TRUE;
return true;
}
/**

View file

@ -127,6 +127,9 @@ _mesa_validate_MultiDrawElementsIndirectCount(struct gl_context *ctx,
GLsizei maxdrawcount,
GLsizei stride);
extern void
_mesa_update_valid_to_render_state(struct gl_context *ctx);
/**
* Is 'mode' a valid value for glBegin(), glDrawArrays(), glDrawElements(),
* etc? The set of legal values depends on whether geometry shaders/programs

View file

@ -34,6 +34,7 @@
#include "clip.h"
#include "context.h"
#include "debug_output.h"
#include "draw_validate.h"
#include "enable.h"
#include "errors.h"
#include "light.h"
@ -607,6 +608,7 @@ _mesa_set_enable(struct gl_context *ctx, GLenum cap, GLboolean state)
ctx->NewDriverState |=
ctx->DriverFlags.NewIntelConservativeRasterization;
ctx->IntelConservativeRasterization = state;
_mesa_update_valid_to_render_state(ctx);
break;
case GL_CONSERVATIVE_RASTERIZATION_NV:
if (!_mesa_has_NV_conservative_raster(ctx))

View file

@ -5227,6 +5227,13 @@ struct gl_context
*/
GLbitfield SupportedPrimMask;
/**
* Bitmask of valid primitive types depending on current states (such as
* shaders). This is 0 if the current states should result in
* GL_INVALID_OPERATION in draw calls.
*/
GLbitfield ValidPrimMask;
/** \name The various 4x4 matrix stacks */
/*@{*/
struct gl_matrix_stack ModelviewMatrixStack;

View file

@ -34,6 +34,7 @@
#include <stdbool.h>
#include "main/glheader.h"
#include "main/context.h"
#include "main/draw_validate.h"
#include "main/enums.h"
#include "main/hash.h"
#include "main/mtypes.h"
@ -537,6 +538,7 @@ _mesa_bind_pipeline(struct gl_context *ctx,
_mesa_update_vertex_processing_mode(ctx);
_mesa_update_allow_draw_out_of_order(ctx);
_mesa_update_primitive_id_is_unused(ctx);
_mesa_update_valid_to_render_state(ctx);
}
}

View file

@ -31,6 +31,7 @@
#include "glheader.h"
#include "context.h"
#include "draw_validate.h"
#include "image.h"
#include "enums.h"
#include "pack.h"
@ -222,6 +223,9 @@ polygon_mode(struct gl_context *ctx, GLenum face, GLenum mode, bool no_error)
if (ctx->Driver.PolygonMode)
ctx->Driver.PolygonMode(ctx, face, mode);
if (ctx->Extensions.INTEL_conservative_rasterization)
_mesa_update_valid_to_render_state(ctx);
}

View file

@ -38,6 +38,7 @@
#include "main/glheader.h"
#include "main/context.h"
#include "draw_validate.h"
#include "main/enums.h"
#include "main/glspirv.h"
#include "main/hash.h"
@ -2582,6 +2583,7 @@ _mesa_use_program(struct gl_context *ctx, gl_shader_stage stage,
_mesa_reference_program(ctx, target, prog);
_mesa_update_allow_draw_out_of_order(ctx);
_mesa_update_primitive_id_is_unused(ctx);
_mesa_update_valid_to_render_state(ctx);
if (stage == MESA_SHADER_VERTEX)
_mesa_update_vertex_processing_mode(ctx);
return;

View file

@ -33,6 +33,7 @@
#include "buffers.h"
#include "context.h"
#include "draw_validate.h"
#include "hash.h"
#include "macros.h"
#include "mtypes.h"
@ -483,6 +484,7 @@ begin_transform_feedback(struct gl_context *ctx, GLenum mode, bool no_error)
assert(ctx->Driver.BeginTransformFeedback);
ctx->Driver.BeginTransformFeedback(ctx, mode, obj);
_mesa_update_valid_to_render_state(ctx);
}
@ -516,6 +518,7 @@ end_transform_feedback(struct gl_context *ctx,
ctx->TransformFeedback.CurrentObject->Active = GL_FALSE;
ctx->TransformFeedback.CurrentObject->Paused = GL_FALSE;
ctx->TransformFeedback.CurrentObject->EndedAnytime = GL_TRUE;
_mesa_update_valid_to_render_state(ctx);
}
@ -1256,6 +1259,7 @@ pause_transform_feedback(struct gl_context *ctx,
ctx->Driver.PauseTransformFeedback(ctx, obj);
obj->Paused = GL_TRUE;
_mesa_update_valid_to_render_state(ctx);
}
@ -1300,6 +1304,7 @@ resume_transform_feedback(struct gl_context *ctx,
assert(ctx->Driver.ResumeTransformFeedback);
ctx->Driver.ResumeTransformFeedback(ctx, obj);
_mesa_update_valid_to_render_state(ctx);
}

View file

@ -25,6 +25,7 @@
#include <stdio.h>
#include "context.h"
#include "draw_validate.h"
#include "util/os_misc.h"
#include "util/simple_mtx.h"
@ -702,6 +703,9 @@ done:
if (_mesa_has_tessellation(ctx))
ctx->SupportedPrimMask |= 1 << GL_PATCHES;
/* First time initialization. */
_mesa_update_valid_to_render_state(ctx);
}