[qfcc] Do basic error checking on layout qualifiers

I'm not sure I got all the checks right, but bsp_gbuf.geom passes the
validity (but failes due to not having implemented the application of
the qualifiers).
This commit is contained in:
Bill Currie 2024-09-10 19:03:13 +09:00
parent 5d7a8127c2
commit 7742450e03
5 changed files with 841 additions and 125 deletions

View file

@ -31,6 +31,7 @@
typedef struct specifier_s specifier_t;
typedef struct attribute_s attribute_t;
typedef struct expr_s expr_t;
typedef struct ex_list_s ex_list_t;
typedef struct type_s type_t;
typedef struct symbol_s symbol_t;
typedef struct symtab_s symtab_t;
@ -91,11 +92,13 @@ void glsl_declare_block (specifier_t spec, symbol_t *block_sym,
symbol_t *instance_name);
glsl_block_t *glsl_get_block (const char *name, glsl_interface_t interface);
symtab_t *glsl_optimize_attributes (attribute_t *attributes);
void glsl_apply_attributes (symtab_t *attributes, specifier_t spec);
void glsl_parse_declaration (specifier_t spec,
symbol_t *sym, const type_t *array,
const expr_t *init, symtab_t *symtab);
void glsl_declare_field (specifier_t spec, symtab_t *symtab);
void glsl_layout (const ex_list_t *qualifiers, specifier_t spec);
bool glsl_on_include (const char *name);
void glsl_include (int behavior, void *scanner);

View file

@ -46,6 +46,7 @@ qfcc_SOURCES = \
tools/qfcc/source/glsl-block.c \
tools/qfcc/source/glsl-builtins.c \
tools/qfcc/source/glsl-declaration.c \
tools/qfcc/source/glsl-layout.c \
tools/qfcc/source/glsl-parse.y \
tools/qfcc/source/glsl-sub_comp.c \
tools/qfcc/source/glsl-sub_frag.c \

View file

@ -42,126 +42,6 @@
#include "tools/qfcc/include/type.h"
#include "tools/qfcc/include/value.h"
#if 0
typedef struct layout_qual_s {
const char *name;
bool (*valid) (symbol_t *sym, glsl_interface_t interface);
bool (*apply) ();
bool (*apply_expr) ();
} layout_qual_t;
// qual var block member iface
static layout_qual_t layout_qualifiers[] = {
{"shared", /* q _ b _ uniform/buffer */, ___, nullptr},
{"packed", /* q _ b _ uniform/buffer */, ___, nullptr},
{"std140", /* q _ b _ uniform/buffer */, ___, nullptr},
{"std430", /* q _ b _ uniform/buffer */, ___, nullptr},
{"row_major", /* q _ b m uniform/buffer */, ___, nullptr},
{"column_major", /* q _ b m uniform/buffer */, ___, nullptr},
{"binding", /* _ o b _ uniform/buffer */, nullptr, ___},
{"offset", /* _ a _ m uniform/buffer */, nullptr, ___},
{"align", /* _ o b _ uniform/buffer */, nullptr, ___},
{"set", /* _ o b _ uniform/buffer */, nullptr, ___},
{"push_constant", /* _ _ b _ uniform(vulkan) */, ___, nullptr},
{"input_attachment_index", /* _ s _ _ uniform(vulkan) */, nullptr, ___},
{"location", /* _ v _ _ uniform/buffer/subroutine */, nullptr, ___},
{"location", /* _ v b m all in/out except compute */, nullptr, ___},
{"component", /* _ v _ m all in/out except compute */, nullptr, ___},
{"index", /* _ v _ _ fragment out/subroutine */, nullptr, ___},
{"triangles", /* q _ _ _ tese in */, ___, nullptr},
{"quads", /* q _ _ _ tese in */, ___, nullptr},
{"isolines", /* q _ _ _ tese in */, ___, nullptr},
{"equal_spacing", /* q _ _ _ tese in */, ___, nullptr},
{"fractional_even_spacing", /* q _ _ _ tese in */, ___, nullptr},
{"fractional_odd_spacing", /* q _ _ _ tese in */, ___, nullptr},
{"cw", /* q _ _ _ tese in */, ___, nullptr},
{"ccw", /* q _ _ _ tese in */, ___, nullptr},
{"point_mode", /* q _ _ _ tese in */, ___, nullptr},
{"points", /* q _ _ _ geom in/out */, ___, nullptr},
//? [ points ] // q _ _ _ geom in
{"lines", /* q _ _ _ geom in */, ___, nullptr},
{"lines_adjacency", /* q _ _ _ geom in */, ___, nullptr},
{"triangles", /* q _ _ _ geom in */, ___, nullptr},
{"triangles_adjacency", /* q _ _ _ geom in */, ___, nullptr},
{"invocations", /* q _ _ _ geom in */, nullptr, ___},
{"origin_upper_left", /* _ f _ _ frag in */, ___, nullptr},
{"pixel_center_integer", /* _ f _ _ frag in */, ___, nullptr},
{"early_fragment_tests", /* q _ _ _ frag in */, ___, nullptr},
{"local_size_x", /* q _ _ _ comp in */, nullptr, ___},
{"local_size_y", /* q _ _ _ comp in */, nullptr, ___},
{"local_size_z", /* q _ _ _ comp in */, nullptr, ___},
{"local_size_x_id", /* q _ _ _ comp in */, nullptr, ___},
{"local_size_y_id", /* q _ _ _ comp in */, nullptr, ___},
{"local_size_z_id", /* q _ _ _ comp in */, nullptr, ___},
{"xfb_buffer", /* q v b m vert/tess/geom out */, nullptr, ___},
{"xfb_stride", /* q v b m vert/tess/geom out */, nullptr, ___},
{"xfb_offset", /* _ v b m vert/tess/geom out */, nullptr, ___},
{"vertices", /* q _ _ _ tesc out */, nullptr, ___},
//? [ points ] // q _ _ _ geom out
{"line_strip", /* q _ _ _ geom out */, ___, nullptr},
{"triangle_strip", /* q _ _ _ geom out */, ___, nullptr},
{"max_vertices", /* q _ _ _ geom out */, nullptr, ___},
{"stream", /* q v b m geom out */, nullptr, ___},
{"depth_any", /* _ v _ _ frag out */, ___, nullptr},
{"depth_greater", /* _ v _ _ frag out */, ___, nullptr},
{"depth_less", /* _ v _ _ frag out */, ___, nullptr},
{"depth_unchanged", /* _ v _ _ frag out */, ___, nullptr},
{"constant_id", /* _ s _ _ const */, nullptr, ___},
{"rgba32f", /* _ i _ _ uniform */, ___, nullptr},
{"rgba16f", /* _ i _ _ uniform */, ___, nullptr},
{"rg32f", /* _ i _ _ uniform */, ___, nullptr},
{"rg16f", /* _ i _ _ uniform */, ___, nullptr},
{"r11f_g11f_b10f", /* _ i _ _ uniform */, ___, nullptr},
{"r32f", /* _ i _ _ uniform */, ___, nullptr},
{"r16f", /* _ i _ _ uniform */, ___, nullptr},
{"rgba16", /* _ i _ _ uniform */, ___, nullptr},
{"rgb10_a2", /* _ i _ _ uniform */, ___, nullptr},
{"rgba8", /* _ i _ _ uniform */, ___, nullptr},
{"rg16", /* _ i _ _ uniform */, ___, nullptr},
{"rg8", /* _ i _ _ uniform */, ___, nullptr},
{"r16", /* _ i _ _ uniform */, ___, nullptr},
{"r8", /* _ i _ _ uniform */, ___, nullptr},
{"rgba16_snorm", /* _ i _ _ uniform */, ___, nullptr},
{"rgba8_snorm", /* _ i _ _ uniform */, ___, nullptr},
{"rg16_snorm", /* _ i _ _ uniform */, ___, nullptr},
{"rg8_snorm", /* _ i _ _ uniform */, ___, nullptr},
{"r16_snorm", /* _ i _ _ uniform */, ___, nullptr},
{"r8_snorm", /* _ i _ _ uniform */, ___, nullptr},
{"rgba32i", /* _ i _ _ uniform */, ___, nullptr},
{"rgba16i", /* _ i _ _ uniform */, ___, nullptr},
{"rgba8i", /* _ i _ _ uniform */, ___, nullptr},
{"rg32i", /* _ i _ _ uniform */, ___, nullptr},
{"rg16i", /* _ i _ _ uniform */, ___, nullptr},
{"rg8i", /* _ i _ _ uniform */, ___, nullptr},
{"r32i", /* _ i _ _ uniform */, ___, nullptr},
{"r16i", /* _ i _ _ uniform */, ___, nullptr},
{"r8i", /* _ i _ _ uniform */, ___, nullptr},
{"rgba32ui", /* _ i _ _ uniform */, ___, nullptr},
{"rgba16ui", /* _ i _ _ uniform */, ___, nullptr},
{"rgb10_a2ui", /* _ i _ _ uniform */, ___, nullptr},
{"rgba8ui", /* _ i _ _ uniform */, ___, nullptr},
{"rg32ui", /* _ i _ _ uniform */, ___, nullptr},
{"rg16ui", /* _ i _ _ uniform */, ___, nullptr},
{"rg8ui", /* _ i _ _ uniform */, ___, nullptr},
{"r32ui", /* _ i _ _ uniform */, ___, nullptr},
{"r16ui", /* _ i _ _ uniform */, ___, nullptr},
{"r8ui", /* _ i _ _ uniform */, ___, nullptr},
};
#endif
const char *glsl_interface_names[glsl_num_interfaces] = {
"in",
"out",
@ -197,6 +77,7 @@ glsl_optimize_attributes (attribute_t *attributes)
}
symtab_addsymbol (attrtab, attrsym);
}
#if 0
notice (0, "%s", attr->name);
if (!attr->params) continue;
for (auto p = attr->params->list.head; p; p = p->next) {
@ -208,6 +89,20 @@ glsl_optimize_attributes (attribute_t *attributes)
notice (0, " %s", get_value_string (p->expr->value));
}
}
#endif
}
return attrtab;
}
void
glsl_apply_attributes (symtab_t *attributes, specifier_t spec)
{
for (auto attr = attributes->symbols; attr; attr = attr->next) {
if (strcmp (attr->name, "layout") == 0) {
if (attr->sy_type != sy_list) {
internal_error (0, "bogus layout qualifier");
}
glsl_layout (&attr->list, spec);
}
}
}

View file

@ -49,7 +49,7 @@ glsl_parse_declaration (specifier_t spec, symbol_t *sym, const type_t *array,
spec.type = append_type (array, spec.type);
spec.type = find_type (spec.type);
}
/*auto attributes =*/ glsl_optimize_attributes (spec.attributes);
auto attributes = glsl_optimize_attributes (spec.attributes);
if (sym && sym->sy_type == sy_expr) {
auto id_list = sym->expr;
if (id_list->type != ex_list) {
@ -60,19 +60,22 @@ glsl_parse_declaration (specifier_t spec, symbol_t *sym, const type_t *array,
internal_error (id_list, "not a symbol");
}
spec.sym = id->expr->symbol;
declare_symbol (spec, init, symtab);
spec.sym = declare_symbol (spec, init, symtab);
glsl_apply_attributes (attributes, spec);
}
} else {
spec.sym = sym;
if (spec.sym) {
declare_symbol (spec, init, symtab);
spec.sym = declare_symbol (spec, init, symtab);
}
glsl_apply_attributes (attributes, spec);
}
}
void
glsl_declare_field (specifier_t spec, symtab_t *symtab)
{
/*auto attributes =*/ glsl_optimize_attributes (spec.attributes);
declare_field (spec, symtab);
auto attributes = glsl_optimize_attributes (spec.attributes);
spec.sym = declare_field (spec, symtab);
glsl_apply_attributes (attributes, spec);
}

View file

@ -0,0 +1,814 @@
/*
glsl-layout.c
GLSL layout attribute handling
Copyright (C) 2024 Bill Currie <bill@taniwha.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <strings.h>
#include "QF/alloc.h"
#include "QF/hash.h"
#include "QF/heapsort.h"
#include "QF/va.h"
#include "tools/qfcc/include/attribute.h"
#include "tools/qfcc/include/def.h"
#include "tools/qfcc/include/diagnostic.h"
#include "tools/qfcc/include/glsl-lang.h"
#include "tools/qfcc/include/shared.h"
#include "tools/qfcc/include/strpool.h"
#include "tools/qfcc/include/symtab.h"
#include "tools/qfcc/include/type.h"
#include "tools/qfcc/include/value.h"
typedef enum {
decl_var = 1 << 0,
decl_qual = 1 << 1,
decl_block = 1 << 2,
decl_member = 1 << 3,
} glsl_obj_t;
typedef enum {
var_any,
var_opaque,
var_atomic,
var_subpass,
var_scalar,
var_image,
var_gl_FragCoord,
var_gl_FragDepth,
} glsl_var_t;
typedef struct layout_qual_s {
const char *name;
bool (*apply) ();
bool (*apply_expr) ();
unsigned obj_mask;
glsl_var_t var_type;
unsigned if_mask;
const char **stage_filter;
} layout_qual_t;
#define A(a) a, nullptr
#define E(e) nullptr, e
#define D(d) (decl_##d)
#define D_all D(qual)|D(var)|D(block)|D(member)
#define V(v) (var_##v)
#define I(i) (1 << (glsl_##i))
#define C (const char *[])
static bool sorted_layout_qualifiers;
static layout_qual_t layout_qualifiers[] = {
{ .name = "shared",
.apply = A(nullptr),
.obj_mask = D(qual)|D(block),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "packed",
.apply = A(nullptr),
.obj_mask = D(qual)|D(block),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "std140",
.apply = A(nullptr),
.obj_mask = D(qual)|D(block),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "std430",
.apply = A(nullptr),
.obj_mask = D(qual)|D(block),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "row_major",
.apply = A(nullptr),
.obj_mask = D(qual)|D(block)|D(member),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "column_major",
.apply = A(nullptr),
.obj_mask = D(qual)|D(block)|D(member),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "binding",
.apply = E(nullptr),
.obj_mask = D(var)|D(block),
.var_type = V(opaque),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "offset",
.apply = E(nullptr),
.obj_mask = D(var)|D(member),
.var_type = V(opaque),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "align",
.apply = E(nullptr),
.obj_mask = D(var)|D(block),
.var_type = V(opaque),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "set",
.apply = E(nullptr),
.obj_mask = D(var)|D(block),
.var_type = V(opaque),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "push_constant",
.apply = A(nullptr),
.obj_mask = D(block),
.var_type = V(any),
.if_mask = I(uniform),
},
{ .name = "input_attachment_index",
.apply = E(nullptr),
.obj_mask = D(var),
.var_type = V(subpass),
.if_mask = I(uniform),
},
{ .name = "location",
.apply = E(nullptr),
.obj_mask = D(var),
.var_type = V(any),
.if_mask = I(uniform)|I(buffer),
},
{ .name = "location",
.apply = E(nullptr),
.obj_mask = D(var)|D(block)|D(member),
.var_type = V(any),
.if_mask = I(in)|I(out),
.stage_filter = C { "!compute", nullptr },
},
{ .name = "component",
.apply = E(nullptr),
.obj_mask = D(var)|D(member),
.var_type = V(any),
.if_mask = I(in)|I(out),
.stage_filter = C { "!compute", nullptr },
},
{ .name = "index",
.apply = E(nullptr),
.obj_mask = D(var),
.var_type = V(any),
.if_mask = I(out),
.stage_filter = C { "fragment", nullptr },
},
{ .name = "triangles",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "tessellation evaluation", nullptr },
},
{ .name = "quads",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "tessellation evaluation", nullptr },
},
{ .name = "isolines",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "tessellation evaluation", nullptr },
},
{ .name = "equal_spacing",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "tessellation evaluation", nullptr },
},
{ .name = "fractional_even_spacing",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "tessellation evaluation", nullptr },
},
{ .name = "fractional_odd_spacing",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "tessellation evaluation", nullptr },
},
{ .name = "cw",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "tessellation evaluation", nullptr },
},
{ .name = "ccw",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "tessellation evaluation", nullptr },
},
{ .name = "point_mode",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "tessellation evaluation", nullptr },
},
{ .name = "points",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in)|I(out),
.stage_filter = C { "geometry", nullptr },
},
//? [ points ] // q _ _ _ geom in
{ .name = "lines",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "geometry", nullptr },
},
{ .name = "lines_adjacency",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "geometry", nullptr },
},
{ .name = "triangles",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "geometry", nullptr },
},
{ .name = "triangles_adjacency",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "geometry", nullptr },
},
{ .name = "invocations",
.apply = E(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "geometry", nullptr },
},
{ .name = "origin_upper_left",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "fragment", nullptr },
},
{ .name = "pixel_center_integer",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "fragment", nullptr },
},
{ .name = "early_fragment_tests",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "fragment", nullptr },
},
{ .name = "local_size_x",
.apply = E(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "comp", nullptr },
},
{ .name = "local_size_y",
.apply = E(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "comp", nullptr },
},
{ .name = "local_size_z",
.apply = E(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "comp", nullptr },
},
{ .name = "local_size_x_id",
.apply = E(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "comp", nullptr },
},
{ .name = "local_size_y_id",
.apply = E(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "comp", nullptr },
},
{ .name = "local_size_z_id",
.apply = E(nullptr),
.obj_mask = D(qual),
.if_mask = I(in),
.stage_filter = C { "comp", nullptr },
},
{ .name = "xfb_buffer",
.apply = E(nullptr),
.obj_mask = D_all,
.var_type = V(any),
.if_mask = I(out),
.stage_filter = C {
"vertex",
"tessellation control",
"tessellation evaluation",
"geometry",
nullptr
},
},
{ .name = "xfb_stride",
.apply = E(nullptr),
.obj_mask = D_all,
.var_type = V(any),
.if_mask = I(out),
.stage_filter = C {
"vertex",
"tessellation control",
"tessellation evaluation",
"geometry",
nullptr
},
},
{ .name = "xfb_offset",
.apply = E(nullptr),
.obj_mask = D_all,
.var_type = V(any),
.if_mask = I(out),
.stage_filter = C {
"vertex",
"tessellation control",
"tessellation evaluation",
"geometry",
nullptr
},
},
{ .name = "vertices",
.apply = E(nullptr),
.obj_mask = D(qual),
.if_mask = I(out),
.stage_filter = C { "tessellation control", nullptr },
},
//? [ points ] // q _ _ _ geom out
{ .name = "line_strip",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(out),
.stage_filter = C { "geometry", nullptr },
},
{ .name = "triangle_strip",
.apply = A(nullptr),
.obj_mask = D(qual),
.if_mask = I(out),
.stage_filter = C { "geometry", nullptr },
},
{ .name = "max_vertices",
.apply = E(nullptr),
.obj_mask = D(qual),
.if_mask = I(out),
.stage_filter = C { "geometry", nullptr },
},
{ .name = "stream",
.apply = E(nullptr),
.obj_mask = D_all,
.var_type = V(any),
.if_mask = I(out),
.stage_filter = C { "geometry", nullptr },
},
{ .name = "depth_any",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(any),
.if_mask = I(out),
.stage_filter = C { "fragment", nullptr },
},
{ .name = "depth_greater",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(any),
.if_mask = I(out),
.stage_filter = C { "fragment", nullptr },
},
{ .name = "depth_less",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(any),
.if_mask = I(out),
.stage_filter = C { "fragment", nullptr },
},
{ .name = "depth_unchanged",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(any),
.if_mask = I(out),
.stage_filter = C { "fragment", nullptr },
},
{ .name = "constant_id",
.apply = E(nullptr),
.obj_mask = D(var),
.var_type = V(scalar),
},
{ .name = "rgba32f",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba16f",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg32f",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg16f",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r11f_g11f_b10f",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r32f",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r16f",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba16",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgb10_a2",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba8",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg16",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg8",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r16",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r8",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba16_snorm",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba8_snorm",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg16_snorm",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg8_snorm",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r16_snorm",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r8_snorm",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba32i",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba16i",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba8i",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg32i",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg16i",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg8i",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r32i",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r16i",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r8i",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba32ui",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba16ui",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgb10_a2ui",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rgba8ui",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg32ui",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg16ui",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "rg8ui",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r32ui",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r16ui",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
{ .name = "r8ui",
.apply = A(nullptr),
.obj_mask = D(var),
.var_type = V(image),
.if_mask = I(uniform),
},
};
#undef A
#undef E
#undef D
#undef D_all
#undef V
#undef I
#undef C
#define num_quals (sizeof (layout_qualifiers) / sizeof (layout_qualifiers[0]))
#define layout_qual_sz num_quals, sizeof (layout_qual_t)
static int
layout_qual_cmp (const void *_a, const void *_b)
{
auto a = (const layout_qual_t *) _a;
auto b = (const layout_qual_t *) _b;
return strcasecmp (a->name, b->name);
}
static bool
layout_check_qualifier (const layout_qual_t *qual, specifier_t spec)
{
unsigned obj_mask = 0;
glsl_var_t var_type = var_any;
unsigned if_mask = 0;
auto interface = glsl_iftype_from_sc (spec.storage);
if (interface < glsl_num_interfaces) {
if_mask = 1 << interface;
}
if (spec.sym) {
auto sym = spec.sym;
if (is_handle (sym->type)) {
var_type = var_opaque;
} else if (is_scalar (sym->type)) {
var_type = var_scalar;
} else if (strcmp (sym->name, "gl_FragCoord") == 0) {
var_type = var_gl_FragCoord;
} else if (strcmp (sym->name, "gl_FragDepth") == 0) {
var_type = var_gl_FragDepth;
}
obj_mask = decl_var;
if (sym->table->parent && sym->table->parent->type == stab_struct) {
obj_mask = decl_member;
}
} else {
obj_mask = decl_qual;
}
if (!(qual->obj_mask & obj_mask)) {
return false;
}
if (qual->var_type != var_type) {
return false;
}
if (!(qual->if_mask & if_mask)) {
return false;
}
if (qual->stage_filter) {
bool ok = false;
for (auto sf = qual->stage_filter; *sf; sf++) {
if (**sf == '!') {
if (strcmp (*sf + 1, glsl_sublang.name) == 0) {
return false;
}
ok = true;
} else {
if (strcmp (*sf, glsl_sublang.name) == 0) {
return true;
}
}
}
return ok;
}
return true;
}
static void
layout_apply_qualifier (const expr_t *qualifier, specifier_t spec)
{
layout_qual_t key = {
.name = qualifier->type == ex_expr
? expr_string (qualifier->expr.e1)
: expr_string (qualifier)
};
auto val = qualifier->type == ex_expr
? qualifier->expr.e2
: nullptr;
notice (qualifier, "%s %p", key.name, spec.sym);
const layout_qual_t *qual;
qual = bsearch (&key, layout_qualifiers, layout_qual_sz, layout_qual_cmp);
if (!qual) {
error (0, "invalid layout qualifier: %s", key.name);
return;
}
// there may be multiple entries with the same qualifier name, so find
// the first one
while (qual > layout_qualifiers && layout_qual_cmp (&key, qual - 1) == 0) {
qual--;
}
// check all matching entries
while (qual - layout_qualifiers < (ptrdiff_t) num_quals
&& layout_qual_cmp (&key, qual) == 0) {
if (layout_check_qualifier (qual, spec)) {
if (qual->apply_expr) {
if (!val) {
error (qualifier, "%s requires a value", key.name);
} else {
qual->apply_expr ();
}
return;
} else if (qual->apply) {
if (!val) {
error (qualifier, "%s does not take a value", key.name);
} else {
qual->apply ();
}
return;
} else {
error (0, "unsupported layout qualifier: %s", key.name);
return;
}
}
qual++;
}
error (0, "invalid layout qualifier: %s", key.name);
return;
}
void
glsl_layout (const ex_list_t *qualifiers, specifier_t spec)
{
if (!sorted_layout_qualifiers) {
heapsort (layout_qualifiers, layout_qual_sz, layout_qual_cmp);
}
for (auto q = qualifiers->head; q; q = q->next) {
layout_apply_qualifier (q->expr, spec);
}
}