mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-25 21:21:14 +00:00
973 lines
23 KiB
C
973 lines
23 KiB
C
/*
|
|
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"
|
|
|
|
static void
|
|
glsl_layout_invalid_A (specifier_t spec, const expr_t *qual_name)
|
|
{
|
|
error (qual_name, "not allowed for vulkan");
|
|
}
|
|
|
|
static void
|
|
glsl_layout_invalid_E (specifier_t spec, const expr_t *qual_name,
|
|
const expr_t *val)
|
|
{
|
|
error (qual_name, "not allowed for vulkan");
|
|
}
|
|
|
|
static void
|
|
set_attribute (attribute_t **attributes, const char *name, const expr_t *val)
|
|
{
|
|
for (auto a = *attributes; a; a = a->next) {
|
|
if (strcmp (a->name, name) == 0) {
|
|
a->params = val;
|
|
return;
|
|
}
|
|
}
|
|
auto attr = new_attribute (name, val);
|
|
attr->next = *attributes;
|
|
*attributes = attr;
|
|
}
|
|
|
|
static void
|
|
glsl_layout_packing (specifier_t spec, const expr_t *qual_name)
|
|
{
|
|
}
|
|
|
|
static void
|
|
glsl_layout_location (specifier_t spec, const expr_t *qual_name,
|
|
const expr_t *val)
|
|
{
|
|
const char *name = expr_string (qual_name);
|
|
set_attribute (&spec.sym->attributes, name, val);
|
|
}
|
|
|
|
static void
|
|
glsl_layout_geom_in_primitive (specifier_t spec, const expr_t *qual_name)
|
|
{
|
|
}
|
|
|
|
static void
|
|
glsl_layout_constant_id (specifier_t spec, const expr_t *qual_name,
|
|
const expr_t *val)
|
|
{
|
|
if (spec.sym->sy_type == sy_const) {
|
|
auto expr = new_value_expr (spec.sym->value, false);
|
|
spec.sym->sy_type = sy_expr;
|
|
spec.sym->expr = expr;
|
|
}
|
|
if (spec.sym->sy_type == sy_expr && spec.sym->expr->type == ex_value) {
|
|
auto value = new_value ();
|
|
*value = *spec.sym->expr->value;
|
|
value->is_constexpr = true;
|
|
spec.sym->expr = new_value_expr (value, false);
|
|
} else {
|
|
error (spec.sym->expr,
|
|
"specialization constant must be a scalar literal");
|
|
return;
|
|
}
|
|
spec.sym->is_constexpr = true;
|
|
const char *name = expr_string (qual_name);
|
|
set_attribute (&spec.sym->attributes, name, val);
|
|
}
|
|
|
|
static void
|
|
glsl_layout_binding (specifier_t spec, const expr_t *qual_name,
|
|
const expr_t *val)
|
|
{
|
|
const char *name = expr_string (qual_name);
|
|
set_attribute (&spec.sym->attributes, name, val);
|
|
}
|
|
|
|
static void
|
|
glsl_layout_set (specifier_t spec, const expr_t *qual_name,
|
|
const expr_t *val)
|
|
{
|
|
const char *name = expr_string (qual_name);
|
|
set_attribute (&spec.sym->attributes, name, val);
|
|
}
|
|
|
|
static void
|
|
glsl_layout_set_property (specifier_t spec, const expr_t *qual_name,
|
|
const expr_t *val)
|
|
{
|
|
auto interface = glsl_iftype_from_sc (spec.storage);
|
|
notice (qual_name, "%s %s %s", glsl_interface_names[interface],
|
|
expr_string (qual_name), get_value_string (val->value));
|
|
}
|
|
|
|
static void
|
|
glsl_layout_geom_out_primitive (specifier_t spec, const expr_t *qual_name)
|
|
{
|
|
}
|
|
|
|
static void
|
|
glsl_layout_geom_out_max_vertices (specifier_t spec, const expr_t *qual_name,
|
|
const expr_t *val)
|
|
{
|
|
}
|
|
|
|
static void
|
|
glsl_layout_format (specifier_t spec, const expr_t *qual_name)
|
|
{
|
|
}
|
|
|
|
static void
|
|
glsl_layout_push_constant (specifier_t spec, const expr_t *qual_name)
|
|
{
|
|
}
|
|
|
|
static void
|
|
glsl_layout_input_attachment_index (specifier_t spec, const expr_t *qual_name,
|
|
const expr_t *val)
|
|
{
|
|
}
|
|
|
|
static void
|
|
glsl_layout_execution_mode (specifier_t spec, const expr_t *qual_name)
|
|
{
|
|
}
|
|
|
|
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;
|
|
void (*apply) (specifier_t spec, const expr_t *qual_name);
|
|
void (*apply_expr) (specifier_t spec, const expr_t *qual_name,
|
|
const expr_t *val);
|
|
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(glsl_layout_invalid_A),
|
|
.obj_mask = D(qual)|D(block),
|
|
.if_mask = I(uniform)|I(buffer),
|
|
},
|
|
{ .name = "packed",
|
|
.apply = A(glsl_layout_invalid_A),
|
|
.obj_mask = D(qual)|D(block),
|
|
.if_mask = I(uniform)|I(buffer),
|
|
},
|
|
{ .name = "std140",
|
|
.apply = A(glsl_layout_packing),
|
|
.obj_mask = D(qual)|D(block),
|
|
.if_mask = I(uniform)|I(buffer),
|
|
},
|
|
{ .name = "std430",
|
|
.apply = A(glsl_layout_packing),
|
|
.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(glsl_layout_binding),
|
|
.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(glsl_layout_set),
|
|
.obj_mask = D(var)|D(block),
|
|
.var_type = V(opaque),
|
|
.if_mask = I(uniform)|I(buffer),
|
|
},
|
|
{ .name = "push_constant",
|
|
.apply = A(glsl_layout_push_constant),
|
|
.obj_mask = D(block),
|
|
.var_type = V(any),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "input_attachment_index",
|
|
.apply = E(glsl_layout_input_attachment_index),
|
|
.obj_mask = D(var),
|
|
.var_type = V(subpass),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "location",
|
|
.apply = E(glsl_layout_invalid_E),
|
|
.obj_mask = D(var),
|
|
.var_type = V(any),
|
|
.if_mask = I(uniform)|I(buffer),
|
|
},
|
|
|
|
{ .name = "location",
|
|
.apply = E(glsl_layout_location),
|
|
.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(glsl_layout_geom_in_primitive),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(in)|I(out),
|
|
.stage_filter = C { "geometry", nullptr },
|
|
},
|
|
{ .name = "lines",
|
|
.apply = A(glsl_layout_geom_in_primitive),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(in),
|
|
.stage_filter = C { "geometry", nullptr },
|
|
},
|
|
{ .name = "lines_adjacency",
|
|
.apply = A(glsl_layout_geom_in_primitive),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(in),
|
|
.stage_filter = C { "geometry", nullptr },
|
|
},
|
|
{ .name = "triangles",
|
|
.apply = A(glsl_layout_geom_in_primitive),
|
|
.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(glsl_layout_execution_mode),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(in),
|
|
.stage_filter = C { "fragment", nullptr },
|
|
},
|
|
|
|
{ .name = "local_size_x",
|
|
.apply = E(glsl_layout_set_property),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(in),
|
|
.stage_filter = C { "compute", nullptr },
|
|
},
|
|
{ .name = "local_size_y",
|
|
.apply = E(glsl_layout_set_property),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(in),
|
|
.stage_filter = C { "compute", nullptr },
|
|
},
|
|
{ .name = "local_size_z",
|
|
.apply = E(glsl_layout_set_property),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(in),
|
|
.stage_filter = C { "compute", nullptr },
|
|
},
|
|
{ .name = "local_size_x_id",
|
|
.apply = E(glsl_layout_set_property),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(in),
|
|
.stage_filter = C { "compute", nullptr },
|
|
},
|
|
{ .name = "local_size_y_id",
|
|
.apply = E(glsl_layout_set_property),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(in),
|
|
.stage_filter = C { "compute", nullptr },
|
|
},
|
|
{ .name = "local_size_z_id",
|
|
.apply = E(glsl_layout_set_property),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(in),
|
|
.stage_filter = C { "compute", 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 },
|
|
},
|
|
|
|
{ .name = "points",
|
|
.apply = A(glsl_layout_geom_out_primitive),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(out),
|
|
.stage_filter = C { "geometry", nullptr },
|
|
},
|
|
{ .name = "line_strip",
|
|
.apply = A(glsl_layout_geom_out_primitive),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(out),
|
|
.stage_filter = C { "geometry", nullptr },
|
|
},
|
|
{ .name = "triangle_strip",
|
|
.apply = A(glsl_layout_geom_out_primitive),
|
|
.obj_mask = D(qual),
|
|
.if_mask = I(out),
|
|
.stage_filter = C { "geometry", nullptr },
|
|
},
|
|
{ .name = "max_vertices",
|
|
.apply = E(glsl_layout_geom_out_max_vertices),
|
|
.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(glsl_layout_constant_id),
|
|
.obj_mask = D(var),
|
|
.var_type = V(scalar),
|
|
},
|
|
|
|
{ .name = "rgba32f",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba16f",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg32f",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg16f",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r11f_g11f_b10f",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r32f",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r16f",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba16",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgb10_a2",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba8",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg16",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg8",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r16",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r8",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba16_snorm",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba8_snorm",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg16_snorm",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg8_snorm",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r16_snorm",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r8_snorm",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba32i",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba16i",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba8i",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg32i",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg16i",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg8i",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r32i",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r16i",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r8i",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba32ui",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba16ui",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgb10_a2ui",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rgba8ui",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg32ui",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg16ui",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "rg8ui",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r32ui",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r16ui",
|
|
.apply = A(glsl_layout_format),
|
|
.obj_mask = D(var),
|
|
.var_type = V(image),
|
|
.if_mask = I(uniform),
|
|
},
|
|
{ .name = "r8ui",
|
|
.apply = A(glsl_layout_format),
|
|
.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 __attribute__((pure))
|
|
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 (sym->type) {
|
|
auto type = sym->type;
|
|
if (is_reference (type)) {
|
|
type = dereference_type (type);
|
|
}
|
|
if (is_array (type)) {
|
|
type = dereference_type (type);
|
|
}
|
|
//FIXME is_handle works only for glsl as there are no user handles
|
|
if (is_handle (type)) {
|
|
var_type = var_opaque;
|
|
// images are opaque types, but certain qualifiers support
|
|
// only images and not other opaque types, but qualifiers that
|
|
// support opaque types in general also support images
|
|
glsl_image_t *image = nullptr;
|
|
//FIXME nicer type check (and remove glsl)
|
|
if (type->handle.type == &type_glsl_image) {
|
|
image = &glsl_imageset.a[type->handle.extra];
|
|
}
|
|
if (qual->var_type != var_opaque && image) {
|
|
if (image->dim == glid_subpassdata) {
|
|
var_type = var_subpass;
|
|
} else {
|
|
var_type = var_image;
|
|
}
|
|
}
|
|
} else if (spec.is_const && is_scalar (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_block;
|
|
}
|
|
} else {
|
|
obj_mask = decl_qual;
|
|
}
|
|
if (!(qual->obj_mask & obj_mask)) {
|
|
return false;
|
|
}
|
|
if (obj_mask == decl_var
|
|
&& qual->var_type != var_any
|
|
&& qual->var_type != var_type) {
|
|
return false;
|
|
}
|
|
if (qual->if_mask != if_mask && !(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;
|
|
|
|
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 (spec, qualifier->expr.e1, val);
|
|
}
|
|
return;
|
|
} else if (qual->apply) {
|
|
if (val) {
|
|
error (qualifier, "%s does not take a value", key.name);
|
|
} else {
|
|
qual->apply (spec, qualifier->expr.e1);
|
|
}
|
|
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);
|
|
}
|
|
}
|