Commit 775a9325 authored by Niklas Haas's avatar Niklas Haas
Browse files

colorspace: add support for Dolby Vision

This covers Profiles 5 and 8.X. After playing around with the
implementation quite a lot, I eventually settled on the current state,
which requires no LUTs or state and is implemented entirely in pure
GLSL. This is slightly slower than a pure 1D LUT approach, but has the
benefit of not needing to add an extra `pl_shader_obj` to the API at
all, and thus being transparently backwards compatible.

The decoding metadata is assumed to be passed in via a new struct
`pl_dovi_metadata`, which contains both the colorspace matrices
(especially important for profiles 8.X) and reshaping coefficients.

Closes #113
parent 51559564
......@@ -35,6 +35,7 @@ project('libplacebo', ['c', 'cpp'],
'182': 'add pl_vulkan_get, pl_opengl_get, pl_d3d11_get',
'183': 'relax pl_shared_mem.size > 0 requirement',
'184': 'add pl_map_avframe/pl_unmap_avframe, deprecate pl_upload_avframe',
'185': 'add PL_COLOR_SYSTEM_DOLBYVISION and reshaping',
}.keys().length(),
# Fix version
0)
......
......@@ -33,6 +33,7 @@ bool pl_color_system_is_ycbcr_like(enum pl_color_system sys)
case PL_COLOR_SYSTEM_BT_2020_C:
case PL_COLOR_SYSTEM_BT_2100_PQ:
case PL_COLOR_SYSTEM_BT_2100_HLG:
case PL_COLOR_SYSTEM_DOLBYVISION:
case PL_COLOR_SYSTEM_YCGCO:
return true;
case PL_COLOR_SYSTEM_COUNT: break;
......@@ -55,6 +56,7 @@ bool pl_color_system_is_linear(enum pl_color_system sys)
case PL_COLOR_SYSTEM_BT_2020_C:
case PL_COLOR_SYSTEM_BT_2100_PQ:
case PL_COLOR_SYSTEM_BT_2100_HLG:
case PL_COLOR_SYSTEM_DOLBYVISION:
case PL_COLOR_SYSTEM_XYZ:
return false;
case PL_COLOR_SYSTEM_COUNT: break;
......@@ -115,6 +117,7 @@ bool pl_color_repr_equal(const struct pl_color_repr *c1,
return c1->sys == c2->sys &&
c1->levels == c2->levels &&
c1->alpha == c2->alpha &&
c1->dovi == c2->dovi &&
pl_bit_encoding_equal(&c1->bits, &c2->bits);
}
......@@ -134,12 +137,16 @@ void pl_color_repr_merge(struct pl_color_repr *orig, const struct pl_color_repr
.sys = PL_DEF(orig->sys, new->sys),
.levels = PL_DEF(orig->levels, new->levels),
.alpha = PL_DEF(orig->alpha, new->alpha),
.dovi = PL_DEF(orig->dovi, new->dovi),
.bits = pl_bit_encoding_merge(&orig->bits, &new->bits),
};
}
enum pl_color_levels pl_color_levels_guess(const struct pl_color_repr *repr)
{
if (repr->sys == PL_COLOR_SYSTEM_DOLBYVISION)
return PL_COLOR_LEVELS_FULL;
if (repr->levels)
return repr->levels;
......@@ -1024,7 +1031,7 @@ struct pl_transform3x3 pl_color_repr_decode(struct pl_color_repr *repr,
m = (struct pl_matrix3x3) {{
{0, 0, 1},
{1, 0, 0},
{0, 1, 0}
{0, 1, 0},
}};
break;
case PL_COLOR_SYSTEM_BT_2100_PQ: {
......@@ -1049,6 +1056,9 @@ struct pl_transform3x3 pl_color_repr_decode(struct pl_color_repr *repr,
}};
break;
}
case PL_COLOR_SYSTEM_DOLBYVISION:
m = repr->dovi->nonlinear;
break;
case PL_COLOR_SYSTEM_YCGCO:
m = (struct pl_matrix3x3) {{
{1, -1, 1},
......@@ -1126,7 +1136,14 @@ struct pl_transform3x3 pl_color_repr_decode(struct pl_color_repr *repr,
double mul[3] = { ymul, ymul, ymul };
double black[3] = { ymin, ymin, ymin };
if (pl_color_system_is_ycbcr_like(repr->sys)) {
if (repr->sys == PL_COLOR_SYSTEM_DOLBYVISION) {
// The RPU matrix already includes levels normalization, but in this
// case we also have to respect the signalled color offsets
for (int i = 0; i < 3; i++) {
mul[i] = 1.0;
black[i] = repr->dovi->nonlinear_offset[i] * scale;
}
} else if (pl_color_system_is_ycbcr_like(repr->sys)) {
mul[1] = mul[2] = cmul;
black[1] = black[2] = cmid;
}
......
......@@ -37,6 +37,7 @@ enum pl_color_system {
PL_COLOR_SYSTEM_BT_2020_C, // ITU-R Rec. BT.2020 (constant luminance)
PL_COLOR_SYSTEM_BT_2100_PQ, // ITU-R Rec. BT.2100 ICtCp PQ variant
PL_COLOR_SYSTEM_BT_2100_HLG, // ITU-R Rec. BT.2100 ICtCp HLG variant
PL_COLOR_SYSTEM_DOLBYVISION, // Dolby Vision (see pl_dovi_metadata)
PL_COLOR_SYSTEM_YCGCO, // YCgCo (derived from RGB)
// Other color systems:
PL_COLOR_SYSTEM_RGB, // Red, Green and Blue
......@@ -120,6 +121,26 @@ struct pl_bit_encoding {
bool pl_bit_encoding_equal(const struct pl_bit_encoding *b1,
const struct pl_bit_encoding *b2);
// Parsed metadata from the Dolby Vision RPU
struct pl_dovi_metadata {
// Colorspace transformation metadata
float nonlinear_offset[3]; // input offset ("ycc_to_rgb_offset")
struct pl_matrix3x3 nonlinear; // before PQ, also called "ycc_to_rgb"
struct pl_matrix3x3 linear; // after PQ, also called "rgb_to_lms"
// Reshape data, grouped by component
struct pl_reshape_data {
uint8_t num_pivots;
float pivots[9]; // normalized to [0.0, 1.0] based on BL bit depth
uint8_t method[8]; // 0 = polynomial, 1 = MMR
// Note: these must be normalized (divide by coefficient_log2_denom)
float poly_coeffs[8][3]; // x^0, x^1, x^2, unused must be 0
uint8_t mmr_order[8]; // 1, 2 or 3
float mmr_constant[8];
float mmr_coeffs[8][3 /* order */][7];
} comp[3];
};
// Struct describing the underlying color system and representation. This
// information is needed to convert an encoded color to a normalized RGB triple
// in the range 0-1.
......@@ -128,6 +149,14 @@ struct pl_color_repr {
enum pl_color_levels levels;
enum pl_alpha_mode alpha;
struct pl_bit_encoding bits; // or {0} if unknown
// Metadata for PL_COLOR_SYSTEM_DOLBYVISION. Note that, for the sake of
// efficiency, this is treated purely as an opaque reference - functions
// like pl_color_repr_equal will merely do a pointer equality test.
//
// The only functions that actually dereference it in any way are
// pl_color_repr_decode, pl_shader_decode_color and pl_render_image(_mix).
const struct pl_dovi_metadata *dovi;
};
// Some common color representations. It's worth pointing out that all of these
......
......@@ -32,6 +32,10 @@ PL_API_BEGIN
void pl_shader_set_alpha(pl_shader sh, struct pl_color_repr *repr,
enum pl_alpha_mode mode);
// Colorspace reshaping for PL_COLOR_SYSTEM_DOLBYVISION. Note that this is done
// automatically by `pl_shader_decode_color` for PL_COLOR_SYSTEM_DOLBYVISION.
void pl_shader_dovi_reshape(pl_shader sh, const struct pl_dovi_metadata *data);
// Decode the color into normalized RGB, given a specified color_repr. This
// also takes care of additional pre- and post-conversions requires for the
// "special" color systems (XYZ, BT.2020-C, etc.). If `params` is left as NULL,
......
......@@ -60,6 +60,7 @@ static inline enum Dav1dMatrixCoefficients pl_system_to_dav1d(enum pl_color_syst
case PL_COLOR_SYSTEM_BT_2020_C: return DAV1D_MC_BT2020_CL;
case PL_COLOR_SYSTEM_BT_2100_PQ: return DAV1D_MC_ICTCP;
case PL_COLOR_SYSTEM_BT_2100_HLG: return DAV1D_MC_ICTCP;
case PL_COLOR_SYSTEM_DOLBYVISION: return DAV1D_MC_UNKNOWN; // missing
case PL_COLOR_SYSTEM_YCGCO: return DAV1D_MC_SMPTE_YCGCO;
case PL_COLOR_SYSTEM_RGB: return DAV1D_MC_IDENTITY;
case PL_COLOR_SYSTEM_XYZ: return DAV1D_MC_IDENTITY;
......
......@@ -84,6 +84,7 @@ static inline enum AVColorSpace pl_system_to_av(enum pl_color_system sys)
case PL_COLOR_SYSTEM_BT_2020_C: return AVCOL_SPC_BT2020_CL;
case PL_COLOR_SYSTEM_BT_2100_PQ: return AVCOL_SPC_ICTCP;
case PL_COLOR_SYSTEM_BT_2100_HLG: return AVCOL_SPC_ICTCP;
case PL_COLOR_SYSTEM_DOLBYVISION: return AVCOL_SPC_UNSPECIFIED; // missing
case PL_COLOR_SYSTEM_YCGCO: return AVCOL_SPC_YCGCO;
case PL_COLOR_SYSTEM_RGB: return AVCOL_SPC_RGB;
case PL_COLOR_SYSTEM_XYZ: return AVCOL_SPC_UNSPECIFIED; // handled differently
......
......@@ -2251,6 +2251,7 @@ static inline enum plane_type detect_plane_type(const struct pl_plane *plane,
case PL_COLOR_SYSTEM_BT_2020_C:
case PL_COLOR_SYSTEM_BT_2100_PQ:
case PL_COLOR_SYSTEM_BT_2100_HLG:
case PL_COLOR_SYSTEM_DOLBYVISION:
case PL_COLOR_SYSTEM_YCGCO:
case PL_COLOR_SYSTEM_COUNT:
break;
......
......@@ -172,6 +172,7 @@ enum pl_shader_obj_type {
PL_SHADER_OBJ_LUT,
PL_SHADER_OBJ_AV1_GRAIN,
PL_SHADER_OBJ_FILM_GRAIN,
PL_SHADER_OBJ_RESHAPE,
};
struct pl_shader_obj {
......
......@@ -33,6 +33,212 @@ void pl_shader_set_alpha(pl_shader sh, struct pl_color_repr *repr,
}
}
static inline void reshape_mmr(pl_shader sh, ident_t mmr, bool single,
int min_order, int max_order)
{
if (sh_glsl(sh).version < 130) {
SH_FAIL(sh, "MMR reshaping requires GLSL 130+");
return;
}
if (single) {
GLSL("const uint mmr_idx = 0u; \n");
} else {
GLSL("uint mmr_idx = uint(coeffs.y); \n");
}
assert(min_order <= max_order);
if (min_order < max_order)
GLSL("uint order = uint(coeffs.w); \n");
GLSL("vec4 sigX; \n"
"s = coeffs.x; \n"
"sigX.xyz = sig.xxy * sig.yzz; \n"
"sigX.w = sigX.x * sig.z; \n"
"s += dot(%s[mmr_idx + 0].xyz, sig); \n"
"s += dot(%s[mmr_idx + 1], sigX); \n",
mmr, mmr);
if (max_order >= 2) {
if (min_order < 2)
GLSL("if (order >= 2) { \n");
GLSL("vec3 sig2 = sig * sig; \n"
"vec4 sigX2 = sigX * sigX; \n"
"s += dot(%s[mmr_idx + 2].xyz, sig2); \n"
"s += dot(%s[mmr_idx + 3], sigX2); \n",
mmr, mmr);
if (max_order == 3) {
if (min_order < 3)
GLSL("if (order >= 3 { \n");
GLSL("s += dot(%s[mmr_idx + 4].xyz, sig2 * sig); \n"
"s += dot(%s[mmr_idx + 5], sigX2 * sigX); \n",
mmr, mmr);
if (min_order < 3)
GLSL("} \n");
}
if (min_order < 2)
GLSL("} \n");
}
}
static inline void reshape_poly(pl_shader sh)
{
GLSL("s = (coeffs.z * s + coeffs.y) * s + coeffs.x; \n");
}
void pl_shader_dovi_reshape(pl_shader sh, const struct pl_dovi_metadata *data)
{
if (!data || !sh_require(sh, PL_SHADER_SIG_COLOR, 0, 0))
return;
sh_describe(sh, "reshaping");
GLSL("// pl_shader_reshape \n"
"{ \n"
"vec3 sig; \n"
"vec4 coeffs; \n"
"float s; \n"
"sig = clamp(color.rgb, 0.0, 1.0); \n");
float coeffs_data[8][4];
float mmr_packed_data[8*6][4];
for (int c = 0; c < 3; c++) {
const struct pl_reshape_data *comp = &data->comp[c];
if (!comp->num_pivots)
continue;
pl_assert(comp->num_pivots >= 2 && comp->num_pivots <= 9);
// Prepare coefficients for GPU
bool has_poly = false, has_mmr = false, mmr_single = true;
int mmr_idx = 0, min_order = 3, max_order = 1;
for (int i = 0; i < comp->num_pivots - 1; i++) {
switch (comp->method[i]) {
case 0: // polynomial
has_poly = true;
coeffs_data[i][3] = 0.0; // order=0 signals polynomial
for (int k = 0; k < 3; k++)
coeffs_data[i][k] = comp->poly_coeffs[i][k];
break;
case 1:
min_order = PL_MIN(min_order, comp->mmr_order[i]);
max_order = PL_MAX(max_order, comp->mmr_order[i]);
mmr_single = !has_mmr;
has_mmr = true;
coeffs_data[i][3] = (float) comp->mmr_order[i];
coeffs_data[i][0] = comp->mmr_constant[i];
coeffs_data[i][1] = (float) mmr_idx;
for (int j = 0; j < comp->mmr_order[i]; j++) {
// store weights per order as two packed vec4s
float *mmr = &mmr_packed_data[mmr_idx][0];
mmr[0] = comp->mmr_coeffs[i][j][0];
mmr[1] = comp->mmr_coeffs[i][j][1];
mmr[2] = comp->mmr_coeffs[i][j][2];
mmr[3] = 0.0; // unused
mmr[4] = comp->mmr_coeffs[i][j][3];
mmr[5] = comp->mmr_coeffs[i][j][4];
mmr[6] = comp->mmr_coeffs[i][j][5];
mmr[7] = comp->mmr_coeffs[i][j][6];
mmr_idx += 2;
}
break;
default:
pl_unreachable();
}
}
// Don't mark these as dynamic, because they don't typically change
// _that_ frequently, and we would prefer to avoid putting them in PCs
ident_t pivots = NULL;
if (comp->num_pivots > 2) {
pivots = sh_var(sh, (struct pl_shader_var) {
.data = &comp->pivots[1], // skip lower bound
.var = {
.name = "pivots",
.type = PL_VAR_FLOAT,
.dim_v = 1,
.dim_m = 1,
.dim_a = comp->num_pivots - 2, // skip lower/upper bounds
},
});
}
ident_t coeffs = sh_var(sh, (struct pl_shader_var) {
.data = coeffs_data,
.var = {
.name = "coeffs",
.type = PL_VAR_FLOAT,
.dim_v = 4,
.dim_m = 1,
.dim_a = comp->num_pivots - 1,
},
});
ident_t mmr = NULL;
if (has_mmr) {
mmr = sh_var(sh, (struct pl_shader_var) {
.data = mmr_packed_data,
.var = {
.name = "mmr",
.type = PL_VAR_FLOAT,
.dim_v = 4,
.dim_m = 1,
.dim_a = mmr_idx,
},
});
}
// Select the right coefficient based on the pivot points
if (comp->num_pivots > 2) {
GLSL("coeffs = %s[%d]; \n", coeffs, comp->num_pivots - 2);
} else {
GLSL("coeffs = %s; \n", coeffs);
}
GLSL("s = sig[%d]; \n", c);
for (int idx = comp->num_pivots - 3; idx >= 0; idx--) {
if (comp->num_pivots > 3) {
GLSL("coeffs = mix(coeffs, %s[%d], bvec4(s < %s[%d])); \n",
coeffs, idx, pivots, idx);
} else {
GLSL("coeffs = mix(coeffs, %s[%d], bvec4(s < %s)); \n",
coeffs, idx, pivots);
}
}
if (has_mmr && has_poly) {
GLSL("if (coeffs.w == 0.0) { \n");
reshape_poly(sh);
GLSL("} else { \n");
reshape_mmr(sh, mmr, mmr_single, min_order, max_order);
GLSL("} \n");
} else if (has_poly) {
reshape_poly(sh);
} else {
assert(has_mmr);
GLSL("{ \n");
reshape_mmr(sh, mmr, mmr_single, min_order, max_order);
GLSL("} \n");
}
// Hard-code these as constants because they're exceptionally unlikely
// to change from frame to frame (if they do, shoot the sample author)
ident_t lo = SH_FLOAT(comp->pivots[0]);
ident_t hi = SH_FLOAT(comp->pivots[comp->num_pivots - 1]);
GLSL("color[%d] = clamp(s, %s, %s); \n", c, lo, hi);
}
GLSL("}\n");
}
void pl_shader_decode_color(pl_shader sh, struct pl_color_repr *repr,
const struct pl_color_adjustment *params)
{
......@@ -52,6 +258,12 @@ void pl_shader_decode_color(pl_shader sh, struct pl_color_repr *repr,
GLSL("color.rgb = pow(vec3(%s) * color.rgb, vec3(2.6));\n", scale);
}
if (repr->sys == PL_COLOR_SYSTEM_DOLBYVISION) {
ident_t scale = SH_FLOAT(pl_color_repr_normalize(repr));
GLSL("color.rgb *= vec3(%s); \n", scale);
pl_shader_dovi_reshape(sh, repr->dovi);
}
enum pl_color_system orig_sys = repr->sys;
struct pl_transform3x3 tr = pl_color_repr_decode(repr, params);
......@@ -135,6 +347,27 @@ void pl_shader_decode_color(pl_shader sh, struct pl_color_repr *repr,
break;
}
case PL_COLOR_SYSTEM_DOLBYVISION: {
// Dolby Vision always outputs BT.2020-referred HPE LMS, so hard-code
// the inverse LMS->RGB matrix corresponding to this color space.
struct pl_matrix3x3 dovi_lms2rgb = {{
{ 3.06441879, -2.16597676, 0.10155818},
{-0.65612108, 1.78554118, -0.12943749},
{ 0.01736321, -0.04725154, 1.03004253},
}};
pl_matrix3x3_mul(&dovi_lms2rgb, &repr->dovi->linear);
ident_t mat = sh_var(sh, (struct pl_shader_var) {
.var = pl_var_mat3("lms2rgb"),
.data = PL_TRANSPOSE_3X3(dovi_lms2rgb.m),
});
pl_shader_linearize(sh, pl_color_space_hdr10);
GLSL("color.rgb = %s * color.rgb; \n", mat);
pl_shader_delinearize(sh, pl_color_space_hdr10);
break;
}
case PL_COLOR_SYSTEM_UNKNOWN:
case PL_COLOR_SYSTEM_RGB:
case PL_COLOR_SYSTEM_XYZ:
......@@ -216,6 +449,10 @@ void pl_shader_encode_color(pl_shader sh, const struct pl_color_repr *repr)
break;
}
case PL_COLOR_SYSTEM_DOLBYVISION:
SH_FAIL(sh, "Cannot un-apply dolbyvision yet (no inverse reshaping)!");
return;
case PL_COLOR_SYSTEM_UNKNOWN:
case PL_COLOR_SYSTEM_RGB:
case PL_COLOR_SYSTEM_XYZ:
......
......@@ -51,7 +51,6 @@ static inline enum pl_channel channel_map(int i, const struct pl_film_grain_para
case PL_COLOR_SYSTEM_UNKNOWN:
case PL_COLOR_SYSTEM_RGB:
return map_rgb[comp];
case PL_COLOR_SYSTEM_XYZ:
return map_xyz[comp];
......@@ -62,6 +61,7 @@ static inline enum pl_channel channel_map(int i, const struct pl_film_grain_para
case PL_COLOR_SYSTEM_BT_2020_C:
case PL_COLOR_SYSTEM_BT_2100_PQ:
case PL_COLOR_SYSTEM_BT_2100_HLG:
case PL_COLOR_SYSTEM_DOLBYVISION:
case PL_COLOR_SYSTEM_YCGCO:
return comp;
......
......@@ -286,6 +286,44 @@ static void bench_h274_grain(pl_shader sh, pl_shader_obj *state, pl_tex src)
pl_shader_film_grain(sh, state, &params);
}
static void bench_reshape_poly(pl_shader sh, pl_shader_obj *state, pl_tex src)
{
pl_shader_sample_direct(sh, pl_sample_src( .tex = src ));
pl_shader_dovi_reshape(sh, &(struct pl_dovi_metadata) { .comp = {
{
.num_pivots = 8,
.pivots = {0.0, 0.00488758553, 0.0420332365, 0.177908108,
0.428152502, 0.678396881, 0.92864126, 1.0},
.method = {0, 0, 0, 0, 0, 0, 0},
.poly_coeffs = {
{0.00290930271, 2.30019712, 50.1446037},
{0.00725257397, 1.88119054, -4.49443769},
{0.0150123835, 1.61106598, -1.64833081},
{0.0498571396, 1.2059114, -0.430627108},
{0.0878019333, 1.01845241, -0.19669354},
{0.120447636, 0.920134187, -0.122338772},
{2.12430835, -3.30913281, 2.10893941},
},
}, {
.num_pivots = 2,
.pivots = {0.0, 1.0},
.method = {0},
.poly_coeffs = {{-0.397901177, 1.85908031, 0}},
}, {
.num_pivots = 2,
.pivots = {0.0, 1.0},
.method = {0},
.poly_coeffs = {{-0.399355531, 1.85591626, 0}},
},
}});
}
static void bench_reshape_mmr(pl_shader sh, pl_shader_obj *state, pl_tex src)
{
pl_shader_sample_direct(sh, pl_sample_src( .tex = src ));
pl_shader_dovi_reshape(sh, &dovi_meta); // this includes MMR
}
static float data[TEX_SIZE * TEX_SIZE * 4 + 8192];
static void bench_download(pl_gpu gpu, pl_tex tex)
......@@ -374,6 +412,8 @@ int main()
benchmark(vk->gpu, "av1_grain", BENCH_SH(bench_av1_grain));
benchmark(vk->gpu, "av1_grain_lap", BENCH_SH(bench_av1_grain_lap));
benchmark(vk->gpu, "h274_grain", BENCH_SH(bench_h274_grain));
benchmark(vk->gpu, "reshape_poly", BENCH_SH(bench_reshape_poly));
benchmark(vk->gpu, "reshape_mmr", BENCH_SH(bench_reshape_mmr));
pl_vulkan_destroy(&vk);
pl_log_destroy(&log);
......
......@@ -464,6 +464,8 @@ static void pl_shader_tests(pl_gpu gpu)
}
for (enum pl_color_system sys = 0; sys < PL_COLOR_SYSTEM_COUNT; sys++) {
if (sys == PL_COLOR_SYSTEM_DOLBYVISION)
continue; // requires metadata
sh = pl_dispatch_begin(dp);
pl_shader_sample_nearest(sh, pl_sample_src( .tex = src ));
pl_shader_encode_color(sh, &(struct pl_color_repr) { .sys = sys });
......@@ -685,6 +687,22 @@ static void pl_shader_tests(pl_gpu gpu)
.target = fbo,
}));
// Test dolbyvision
if (gpu->glsl.version >= 130) {
struct pl_color_repr repr = {
.sys = PL_COLOR_SYSTEM_DOLBYVISION,
.dovi = &dovi_meta,
};
sh = pl_dispatch_begin(dp);
pl_shader_sample_direct(sh, pl_sample_src( .tex = src ));
pl_shader_decode_color(sh, &repr, NULL);
REQUIRE(pl_dispatch_finish(dp, &(struct pl_dispatch_params) {
.shader = &sh,
.target = fbo,
}));
}
pl_dispatch_destroy(&dp);
pl_tex_destroy(gpu, &src);
pl_tex_destroy(gpu, &fbo);
......
......@@ -129,3 +129,49 @@ static const struct pl_h274_grain_data h274_grain_data = {
.intensity_interval_upper_bound = {&h274_upper_bound},
.comp_model_value = {&h274_values},
};
static const struct pl_dovi_metadata dovi_meta = {
.nonlinear = {{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}},
.linear = {{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}},
.comp = {
{
.num_pivots = 9,
.pivots = {0.0615835786, 0.129032254, 0.353861183,
0.604105592, 0.854349971, 0.890518069,
0.906158328, 0.913978517, 0.92082113},
.method = {0, 0, 0, 0, 0, 0, 0, 0},
.poly_coeffs = {
{-0.0488376617, 1.99335372, -2.41716385},
{-0.0141925812, 1.61829138, -1.53397191},
{ 0.157061458, 0.63640213, -0.11302495},
{0.25272119, 0.246226311, 0.27281332},
{0.951621532, -1.35507894, 1.18898678},
{6.41251612, -13.6188488, 8.07336903},
{13.467535, -29.1869125, 16.6612244},
{28.2321472, -61.8516273, 34.7264938}
},
}, {
.num_pivots = 2,
.pivots = {0.0, 1.0},
.method = {1},
.mmr_order = {3},
.mmr_constant = {-0.500733018},
.mmr_coeffs = {{
{1.08411026, 3.80807829, 0.0881733894, -3.23097038, -0.409078479, -1.31310081, 2.71297002},
{-0.241833091, -3.57880807, -0.108109117, 3.13198471, 0.869203091, 1.96561158, -9.30871677},
{-0.177356839, 1.48970401, 0.0908923149, -0.510447979, -0.687603354, -0.934977889, 12.3544884},
}},
}, {
.num_pivots = 2,
.pivots = {0.0, 1.0},
.method = {1},
.mmr_order = {3},
.mmr_constant = {-1.23833287},
.mmr_coeffs = {{
{3.52909589, 0.383154511, 5.50820637, -1.02094889, -6.36386824, 0.194121242, 0.64683497},
{-2.57899785, -0.626081586, -6.05729723, 2.29143763, 9.14653015, -0.0507702827, -4.17724133},
{0.705404401, 0.341412306, 2.98387456, -1.71712542, -4.91501331, 0.1465137, 6.38665438},
}},
},
},
};
Supports Markdown
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