From 7742450e03b8b4dab91e6799ab8faecb082781ca Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 10 Sep 2024 19:03:13 +0900 Subject: [PATCH] [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). --- tools/qfcc/include/glsl-lang.h | 3 + tools/qfcc/source/Makemodule.am | 1 + tools/qfcc/source/glsl-attribute.c | 135 +---- tools/qfcc/source/glsl-declaration.c | 13 +- tools/qfcc/source/glsl-layout.c | 814 +++++++++++++++++++++++++++ 5 files changed, 841 insertions(+), 125 deletions(-) create mode 100644 tools/qfcc/source/glsl-layout.c diff --git a/tools/qfcc/include/glsl-lang.h b/tools/qfcc/include/glsl-lang.h index d23d9a8af..f6ec1d4eb 100644 --- a/tools/qfcc/include/glsl-lang.h +++ b/tools/qfcc/include/glsl-lang.h @@ -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); diff --git a/tools/qfcc/source/Makemodule.am b/tools/qfcc/source/Makemodule.am index 627fe83e6..003777b75 100644 --- a/tools/qfcc/source/Makemodule.am +++ b/tools/qfcc/source/Makemodule.am @@ -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 \ diff --git a/tools/qfcc/source/glsl-attribute.c b/tools/qfcc/source/glsl-attribute.c index 642f50641..21ad83de3 100644 --- a/tools/qfcc/source/glsl-attribute.c +++ b/tools/qfcc/source/glsl-attribute.c @@ -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); + } + } +} diff --git a/tools/qfcc/source/glsl-declaration.c b/tools/qfcc/source/glsl-declaration.c index fd7b41e1d..3c6c52ecd 100644 --- a/tools/qfcc/source/glsl-declaration.c +++ b/tools/qfcc/source/glsl-declaration.c @@ -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); } diff --git a/tools/qfcc/source/glsl-layout.c b/tools/qfcc/source/glsl-layout.c new file mode 100644 index 000000000..d6124455b --- /dev/null +++ b/tools/qfcc/source/glsl-layout.c @@ -0,0 +1,814 @@ +/* + glsl-layout.c + + GLSL layout attribute handling + + Copyright (C) 2024 Bill Currie + + 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 + +#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); + } +}