Commit ec6e5260 authored by Niklas Haas's avatar Niklas Haas

shaders: refactor variable definitions

Group the variable state up into its own struct, which also includes
metadata like whether or not the variable is dynamic.

Instead of hard-coding static names for all variables, generate fresh
identifiers and pass them around. (In theory, we could get away with
just the `_N` variable names, but I want to keep around the
human-friendly variable names for debugging purposes; so I went with the
`_name_N` approach)

Also change the ra_var interpretation back from column major to row
major, to match the struct color_matrix/color_transform. This is
probably more convenient for everybody involved, even though it makes
`dim_m` sort of weird. The new helper functions should hopefully make
that less painful anyway.

As an aside, fixes a bug where the wrong luma coefficients were used for
the inverse OOTF. Annoyingly, this makes it harder to detect when
src_luma/dst_luma are required, so just always load the uniforms again.
(It's not like it matters)
parent 082956d3
......@@ -265,6 +265,15 @@ struct ra_var {
// variable is not legal.
const char *ra_var_glsl_type_name(struct ra_var var);
// Helper functions for constructing the most common ra_vars.
struct ra_var ra_var_float(const char *name);
struct ra_var ra_var_vec2(const char *name);
struct ra_var ra_var_vec3(const char *name);
struct ra_var ra_var_vec4(const char *name);
struct ra_var ra_var_mat2(const char *name);
struct ra_var ra_var_mat3(const char *name);
struct ra_var ra_var_mat4(const char *name);
// Represents the layout requirements of an input variable
struct ra_var_layout {
size_t align; // the alignment requirements (always a power of two)
......@@ -275,8 +284,7 @@ struct ra_var_layout {
// Returns the host layout of an input variable as required for a
// tightly-packed, byte-aligned C data type.
//
// NOTE: matrices are assumed to be column major, since this matches typical
// graphics APIs.
// NOTE: matrices are assumed to be row major, similar to pl_color_matrix.
struct ra_var_layout ra_var_host_layout(struct ra_var var);
// Type of a shader input descriptor.
......
......@@ -21,6 +21,18 @@
#include "colorspace.h"
#include "ra.h"
// Represents a bound shared variable / descriptor
struct pl_shader_var {
struct ra_var var; // the underlying variable description
const void *data; // the raw data (interpretation as with ra_var_update)
bool dynamic; // if true, the value is expected to change frequently
};
struct pl_shader_desc {
struct ra_desc desc; // the underlying descriptor description
const void *binding; // the object being bound (as for ra_desc_update)
};
// Represents a shader fragment. This is not a complete shader, but a
// collection of shader text together with description of the input required to
// satisfy it.
......@@ -43,17 +55,12 @@ struct pl_shader {
// size requirements for this shader pass.
size_t compute_shmem;
// A set of input variables needed to satisfy this shader fragment, together
// with their underlying raw data (as required for ra_var_update).
struct ra_var *variables;
const void **variable_data;
// A set of input variables needed by this shader fragment.
struct pl_shader_var *variables;
int num_variables;
// A list of input descriptors needed to satisfy this shader fragment,
// together with the underlying objects bound to the corresponding
// descriptor (as required for ra_desc_update).
struct ra_desc *descriptors;
const void **descriptor_bindings;
// A list of input descriptors needed by this shader fragment,
struct pl_shader_desc *descriptors;
int num_descriptors;
};
......
......@@ -85,6 +85,24 @@ const char *ra_var_glsl_type_name(struct ra_var var)
return types[var.type][var.dim_m][var.dim_v];
}
#define RA_VAR_FV(TYPE, M, V) \
struct ra_var ra_var_##TYPE(const char *name) { \
return (struct ra_var) { \
.name = name, \
.type = RA_VAR_FLOAT, \
.dim_m = M, \
.dim_v = V, \
}; \
}
RA_VAR_FV(float, 1, 1)
RA_VAR_FV(vec2, 1, 2)
RA_VAR_FV(vec3, 1, 3)
RA_VAR_FV(vec4, 1, 4)
RA_VAR_FV(mat2, 2, 2)
RA_VAR_FV(mat3, 3, 3)
RA_VAR_FV(mat4, 4, 4)
struct ra_var_layout ra_var_host_layout(struct ra_var var)
{
size_t row_size = ra_var_type_size(var.type) * var.dim_v;
......
......@@ -24,6 +24,7 @@
struct priv {
struct bstr buffer;
bool flexible_work_groups;
int fresh;
};
struct pl_shader *pl_shader_alloc(struct pl_context *ctx,
......@@ -53,68 +54,18 @@ bool pl_shader_is_compute(const struct pl_shader *s)
return ret;
}
// Append a raw `struct ra_var` to the pl_shader (while making a copy of
// the variable name and data).
static void pl_shader_var(struct pl_shader *s, struct ra_var var,
const void *data)
{
size_t size = ra_var_host_layout(var).size;
int idx = s->num_variables++;
TARRAY_GROW(s, s->variables, idx);
TARRAY_GROW(s, s->variable_data, idx);
var.name = talloc_strdup(s, var.name);
s->variables[idx] = var;
s->variable_data[idx] = talloc_memdup(s, data, size);
}
// Helpers for some of the most common variable types
static void pl_shader_var_vec3(struct pl_shader *s, const char *name,
const float f[3])
{
pl_shader_var(s, (struct ra_var) {
.name = name,
.type = RA_VAR_FLOAT,
.dim_v = 3,
.dim_m = 1,
}, f);
}
static void pl_shader_var_mat3(struct pl_shader *s, const char *name,
bool column_major, const float m[3][3])
{
struct ra_var var = {
.name = name,
.type = RA_VAR_FLOAT,
.dim_v = 3,
.dim_m = 3,
};
if (column_major) {
pl_shader_var(s, var, m);
} else {
float tmp[3][3] = {
{ m[0][0], m[1][0], m[2][0] },
{ m[0][1], m[1][1], m[2][1] },
{ m[0][2], m[1][2], m[2][2] },
};
pl_shader_var(s, var, tmp);
}
}
typedef const char * var_t;
// Append a raw `struct ra_desc` to the pl_shader (while making a copy of
// the descriptor name).
static void pl_shader_desc(struct pl_shader *s, struct ra_desc desc,
const void *binding)
// Add a new shader var and return its identifier (string)
static var_t var(struct pl_shader *s, struct pl_shader_var sv)
{
int idx = s->num_descriptors++;
TARRAY_GROW(s, s->descriptors, idx);
TARRAY_GROW(s, s->descriptor_bindings, idx);
struct priv *p = s->priv;
const char *safename = sv.var.name ? sv.var.name : "";
sv.var.name = talloc_asprintf(s, "_%s_%d", safename, p->fresh++);
sv.data = talloc_memdup(s, sv.data, ra_var_host_layout(sv.var).size);
desc.name = talloc_strdup(s, desc.name);
s->descriptors[idx] = desc;
s->descriptor_bindings[idx] = binding;
TARRAY_APPEND(s, s->variables, s->num_variables, sv);
return sv.var.name;
}
static void pl_shader_append(struct pl_shader *s, const char *fmt, ...)
......@@ -318,7 +269,7 @@ void pl_shader_delinearize(struct pl_shader *s, enum pl_color_transfer trc)
// Applies the OOTF / inverse OOTF. `peak` corresponds to the nominal peak
// (needed to scale the functions correctly)
static void pl_shader_ootf(struct pl_shader *s, enum pl_color_light light,
float peak)
float peak, var_t luma)
{
if (!light || light == PL_COLOR_LIGHT_DISPLAY)
return;
......@@ -331,8 +282,8 @@ static void pl_shader_ootf(struct pl_shader *s, enum pl_color_light light,
case PL_COLOR_LIGHT_SCENE_HLG:
// HLG OOTF from BT.2100, assuming a reference display with a
// peak of 1000 cd/m² -> gamma = 1.2
GLSL("color.rgb *= vec3(%f * pow(dot(src_luma, color.rgb), 0.2));\n",
(1000 / PL_COLOR_REF_WHITE) / pow(12, 1.2));
GLSL("color.rgb *= vec3(%f * pow(dot(%s, color.rgb), 0.2));\n",
(1000 / PL_COLOR_REF_WHITE) / pow(12, 1.2), luma);
break;
case PL_COLOR_LIGHT_SCENE_709_1886:
// This OOTF is defined by encoding the result as 709 and then decoding
......@@ -355,7 +306,7 @@ static void pl_shader_ootf(struct pl_shader *s, enum pl_color_light light,
}
static void pl_shader_inverse_ootf(struct pl_shader *s, enum pl_color_light light,
float peak)
float peak, var_t luma)
{
if (!light || light == PL_COLOR_LIGHT_DISPLAY)
return;
......@@ -367,9 +318,9 @@ static void pl_shader_inverse_ootf(struct pl_shader *s, enum pl_color_light ligh
{
case PL_COLOR_LIGHT_SCENE_HLG:
GLSL("color.rgb *= vec3(1.0/%f); \n"
"color.rgb /= vec3(max(1e-6, pow(dot(src_luma, color.rgb), \n"
"color.rgb /= vec3(max(1e-6, pow(dot(%s, color.rgb), \n"
" 0.2/1.2))); \n",
(1000 / PL_COLOR_REF_WHITE) / pow(12, 1.2));
(1000 / PL_COLOR_REF_WHITE) / pow(12, 1.2), luma);
break;
case PL_COLOR_LIGHT_SCENE_709_1886:
GLSL("color.rgb = pow(color.rgb, vec3(1.0/2.4)); \n"
......@@ -395,17 +346,17 @@ const struct pl_color_map_params pl_color_map_recommended_params = {
.peak_detect_frames = 50,
};
static void pl_shader_tone_map(struct pl_shader *s, float ref_peak,
const struct pl_color_map_params *params)
static void pl_shader_tone_map(struct pl_shader *s, float ref_peak, var_t luma,
const struct pl_color_map_params *params)
{
GLSL("// pl_shader_tone_map\n");
// Desaturate the color using a coefficient dependent on the luminance
if (params->tone_mapping_desaturate > 0) {
GLSL("float luma = dot(dst_luma, color.rgb); \n"
GLSL("float luma = dot(%s, color.rgb); \n"
"float overbright = max(luma - %f, 1e-6) / max(luma, 1e-6); \n"
"color.rgb = mix(color.rgb, vec3(luma), overbright); \n",
params->tone_mapping_desaturate);
luma, params->tone_mapping_desaturate);
}
// To prevent discoloration due to out-of-bounds clipping, we need to make
......@@ -510,18 +461,25 @@ void pl_shader_color_map(struct pl_shader *s,
if (!src.sig_peak)
src.sig_peak = pl_color_transfer_nominal_peak(src.transfer);
// Various operations need access to the src_luma and dst_luma respectively,
// so just always make them available
struct pl_color_matrix rgb2xyz;
rgb2xyz = pl_get_rgb2xyz_matrix(pl_raw_primaries_get(src.primaries));
var_t src_luma = var(s, (struct pl_shader_var) {
.var = ra_var_vec3("src_luma"),
.data = rgb2xyz.m[1], // RGB->Y vector
});
rgb2xyz = pl_get_rgb2xyz_matrix(pl_raw_primaries_get(dst.primaries));
var_t dst_luma = var(s, (struct pl_shader_var) {
.var = ra_var_vec3("dst_luma"),
.data = rgb2xyz.m[1], // RGB->Y vector
});
// Compute the highest encodable level
float src_range = pl_color_transfer_nominal_peak(src.transfer),
dst_range = pl_color_transfer_nominal_peak(dst.transfer);
float ref_peak = src.sig_peak / dst_range;
// OOTF and inverse OOTF need access to the src_luma coefficients
struct pl_color_matrix rgb2xyz;
if (src.light != dst.light) {
rgb2xyz = pl_get_rgb2xyz_matrix(pl_raw_primaries_get(src.primaries));
pl_shader_var_vec3(s, "src_luma", rgb2xyz.m[1]);
}
// All operations from here on require linear light as a starting point,
// so we linearize even if src.gamma == dst.gamma when one of the other
// operations needs it
......@@ -538,7 +496,7 @@ void pl_shader_color_map(struct pl_shader *s,
}
if (src.light != dst.light)
pl_shader_ootf(s, src.light, pl_color_transfer_nominal_peak(src.transfer));
pl_shader_ootf(s, src.light, src_range, src_luma);
// Rescale the signal to compensate for differences in the encoding range
// and reference white level. This is necessary because of the 0-1 value
......@@ -552,26 +510,24 @@ void pl_shader_color_map(struct pl_shader *s,
if (src.primaries != dst.primaries) {
struct pl_raw_primaries csp_src = pl_raw_primaries_get(src.primaries),
csp_dst = pl_raw_primaries_get(dst.primaries);
struct pl_color_matrix cms_matrix;
cms_matrix = pl_get_color_mapping_matrix(csp_src, csp_dst, params->intent);
pl_shader_var_mat3(s, "cms_matrix", false, cms_matrix.m);
GLSL("color.rgb = cms_matrix * color.rgb;\n");
struct pl_color_matrix cms_mat;
cms_mat = pl_get_color_mapping_matrix(csp_src, csp_dst, params->intent);
GLSL("color.rgb = %s * color.rgb;\n", var(s, (struct pl_shader_var) {
.var = ra_var_mat3("cms_matrix"),
.data = cms_mat.m,
}));
// Since this can reduce the gamut, figure out by how much
for (int c = 0; c < 3; c++)
ref_peak = fmaxf(ref_peak, cms_matrix.m[c][c]);
ref_peak = fmaxf(ref_peak, cms_mat.m[c][c]);
}
// Tone map to prevent clipping when the source signal peak exceeds the
// encodable range or we've reduced the gamut
if (ref_peak > 1) {
rgb2xyz = pl_get_rgb2xyz_matrix(pl_raw_primaries_get(dst.primaries));
pl_shader_var_vec3(s, "dst_luma", rgb2xyz.m[1]);
pl_shader_tone_map(s, ref_peak, params);
}
if (ref_peak > 1)
pl_shader_tone_map(s, ref_peak, dst_luma, params);
if (src.light != dst.light) {
pl_shader_inverse_ootf(s, dst.light, pl_color_transfer_nominal_peak(dst.transfer));
}
if (src.light != dst.light)
pl_shader_inverse_ootf(s, dst.light, dst_range, dst_luma);
// Warn for remaining out-of-gamut colors is enabled
if (params->gamut_warning) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment