glsl: validate global out xfb_stride qualifiers and set stride on empty buffers

Here we use the built-in validation in
ast_layout_expression::process_qualifier_constant() to check for mismatching
global out strides on buffers in a single shader.

From the ARB_enhanced_layouts spec:

   "While *xfb_stride* can be declared multiple times for the same buffer,
   it is a compile-time or link-time error to have different values
   specified for the stride for the same buffer."

For intrastage validation a new helper link_xfb_stride_layout_qualifiers()
is created. We also take this opportunity to make sure stride is at least
a multiple of 4, we will validate doubles at a later stage.

From the ARB_enhanced_layouts spec:

   "If the buffer is capturing any double-typed outputs, the stride must
   be a multiple of 8, otherwise it must be a multiple of 4, or a
   compile-time or link-time error results."

Finally we update store_tfeedback_info() to apply the strides to
LinkedTransformFeedback and update the buffers bitmask to mark any global
buffers with a stride as active. For example a shader with:

layout (xfb_buffer = 0, xfb_offset = 0)  out vec4 gs_fs;
layout (xfb_buffer = 1, xfb_stride = 64) out;

Is expected to have a buffer bound to both 0 and 1.

From the ARB_enhanced_layouts spec:

   "A binding point requires a bound buffer object if and only if its
   associated stride in the program object used for transform feedback
   primitive capture is non-zero."

Reviewed-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
Timothy Arceri
2016-03-11 11:57:52 +11:00
parent cf039a309a
commit 8120e869b1
4 changed files with 94 additions and 0 deletions

View File

@@ -1617,6 +1617,17 @@ set_shader_inout_layout(struct gl_shader *shader,
assert(!state->fs_early_fragment_tests);
}
for (unsigned i = 0; i < MAX_FEEDBACK_BUFFERS; i++) {
if (state->out_qualifier->out_xfb_stride[i]) {
unsigned xfb_stride;
if (state->out_qualifier->out_xfb_stride[i]->
process_qualifier_constant(state, "xfb_stride", &xfb_stride,
true)) {
shader->TransformFeedback.BufferStride[i] = xfb_stride;
}
}
}
switch (shader->Stage) {
case MESA_SHADER_TESS_CTRL:
shader->TessCtrl.VerticesOut = 0;

View File

@@ -930,6 +930,17 @@ store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog,
unsigned buffer =
num_tfeedback_decls ? tfeedback_decls[0].get_buffer() : 0;
/* Apply any xfb_stride global qualifiers */
if (has_xfb_qualifiers) {
for (unsigned j = 0; j < MAX_FEEDBACK_BUFFERS; j++) {
if (prog->TransformFeedback.BufferStride[j]) {
buffers |= 1 << j;
prog->LinkedTransformFeedback.Buffers[j].Stride =
prog->TransformFeedback.BufferStride[j] / 4;
}
}
}
for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
if (tfeedback_decls[i].is_next_buffer_separator()) {
num_buffers++;

View File

@@ -1584,6 +1584,69 @@ private:
hash_table *unnamed_interfaces;
};
/**
* Check for conflicting xfb_stride default qualifiers and store buffer stride
* for later use.
*/
static void
link_xfb_stride_layout_qualifiers(struct gl_context *ctx,
struct gl_shader_program *prog,
struct gl_shader *linked_shader,
struct gl_shader **shader_list,
unsigned num_shaders)
{
for (unsigned i = 0; i < MAX_FEEDBACK_BUFFERS; i++) {
linked_shader->TransformFeedback.BufferStride[i] = 0;
}
for (unsigned i = 0; i < num_shaders; i++) {
struct gl_shader *shader = shader_list[i];
for (unsigned j = 0; j < MAX_FEEDBACK_BUFFERS; j++) {
if (shader->TransformFeedback.BufferStride[j]) {
if (linked_shader->TransformFeedback.BufferStride[j] != 0 &&
shader->TransformFeedback.BufferStride[j] != 0 &&
linked_shader->TransformFeedback.BufferStride[j] !=
shader->TransformFeedback.BufferStride[j]) {
linker_error(prog,
"intrastage shaders defined with conflicting "
"xfb_stride for buffer %d (%d and %d)\n", j,
linked_shader->TransformFeedback.BufferStride[j],
shader->TransformFeedback.BufferStride[j]);
return;
}
if (shader->TransformFeedback.BufferStride[j])
linked_shader->TransformFeedback.BufferStride[j] =
shader->TransformFeedback.BufferStride[j];
}
}
}
for (unsigned j = 0; j < MAX_FEEDBACK_BUFFERS; j++) {
if (linked_shader->TransformFeedback.BufferStride[j]) {
prog->TransformFeedback.BufferStride[j] =
linked_shader->TransformFeedback.BufferStride[j];
/* We will validate doubles at a later stage */
if (prog->TransformFeedback.BufferStride[j] % 4) {
linker_error(prog, "invalid qualifier xfb_stride=%d must be a "
"multiple of 4 or if its applied to a type that is "
"or contains a double a multiple of 8.",
prog->TransformFeedback.BufferStride[j]);
return;
}
if (prog->TransformFeedback.BufferStride[j] / 4 >
ctx->Const.MaxTransformFeedbackInterleavedComponents) {
linker_error(prog,
"The MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS "
"limit has been exceeded.");
return;
}
}
}
}
/**
* Performs the cross-validation of tessellation control shader vertices and
@@ -2101,6 +2164,8 @@ link_intrastage_shaders(void *mem_ctx,
link_tes_in_layout_qualifiers(prog, linked, shader_list, num_shaders);
link_gs_inout_layout_qualifiers(prog, linked, shader_list, num_shaders);
link_cs_input_layout_qualifiers(prog, linked, shader_list, num_shaders);
link_xfb_stride_layout_qualifiers(ctx, prog, linked, shader_list,
num_shaders);
populate_symbol_table(linked);

View File

@@ -2337,6 +2337,11 @@ struct gl_shader
bool origin_upper_left;
bool pixel_center_integer;
struct {
/** Global xfb_stride out qualifier if any */
GLuint BufferStride[MAX_FEEDBACK_BUFFERS];
} TransformFeedback;
/**
* Tessellation Control shader state from layout qualifiers.
*/
@@ -2674,6 +2679,8 @@ struct gl_shader_program
*/
struct {
GLenum BufferMode;
/** Global xfb_stride out qualifier if any */
GLuint BufferStride[MAX_FEEDBACK_BUFFERS];
GLuint NumVarying;
GLchar **VaryingNames; /**< Array [NumVarying] of char * */
} TransformFeedback;