mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-12 06:39:26 +00:00
[vulkan] Plumbing ASTC decoding capabilities for HDR
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -30,6 +33,7 @@ UNIFORM(3) uint block_size;
|
||||
UNIFORM(4) uint x_shift;
|
||||
UNIFORM(5) uint block_height;
|
||||
UNIFORM(6) uint block_height_mask;
|
||||
UNIFORM(7) uint is_srgb;
|
||||
END_PUSH_CONSTANTS
|
||||
|
||||
struct EncodingData {
|
||||
@@ -40,7 +44,11 @@ layout(binding = BINDING_INPUT_BUFFER, std430) readonly restrict buffer InputBuf
|
||||
uvec4 astc_data[];
|
||||
};
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(binding = BINDING_OUTPUT_IMAGE, rgba16f) uniform writeonly restrict image2DArray dest_image;
|
||||
#else
|
||||
layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly restrict image2DArray dest_image;
|
||||
#endif
|
||||
|
||||
const uint GOB_SIZE_X_SHIFT = 6;
|
||||
const uint GOB_SIZE_Y_SHIFT = 3;
|
||||
@@ -130,6 +138,16 @@ uvec4 ReplicateByteTo16(uvec4 value) {
|
||||
return value * 0x101;
|
||||
}
|
||||
|
||||
// sRGB EOTF (decode: encoded value -> linear). Only used on the Vulkan path, where the
|
||||
// destination image is a linear float format with no format-level sRGB tag of its own.
|
||||
float SRGBToLinear(float c) {
|
||||
return (c <= 0.04045) ? (c / 12.92) : pow((c + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
|
||||
vec3 SRGBToLinear(vec3 c) {
|
||||
return vec3(SRGBToLinear(c.x), SRGBToLinear(c.y), SRGBToLinear(c.z));
|
||||
}
|
||||
|
||||
uint ReplicateBitTo7(uint value) {
|
||||
return value * 127;
|
||||
}
|
||||
@@ -589,6 +607,163 @@ ivec4 BlueContract(int a, int r, int g, int b) {
|
||||
return ivec4(a, (r + b) >> 1, (g + b) >> 1, b);
|
||||
}
|
||||
|
||||
bool IsHDRColorEndpointMode(uint cem) {
|
||||
return cem == 2u || cem == 3u || cem == 7u || cem == 11u || cem == 14u || cem == 15u;
|
||||
}
|
||||
|
||||
// Sign-extends the low nbits of value (a 2's complement field packed into the bottom of an
|
||||
// otherwise-unsigned integer), per C.2.15's HDR endpoint bitfield unpacking.
|
||||
int SignExtend(int value, uint nbits) {
|
||||
int sign_bit = 1 << (nbits - 1u);
|
||||
return (value ^ sign_bit) - sign_bit;
|
||||
}
|
||||
|
||||
// HDR Endpoint Mode 7 (C.2.15): base RGB + scale factor.
|
||||
void DecodeHDREndpointMode7(uint v0, uint v1, uint v2, uint v3, out ivec3 e0, out ivec3 e1) {
|
||||
uint modeval = ((v0 & 0xC0u) >> 6u) | ((v1 & 0x80u) >> 5u) | ((v2 & 0x80u) >> 4u);
|
||||
|
||||
uint majcomp;
|
||||
uint mode;
|
||||
if ((modeval & 0xCu) != 0xCu) {
|
||||
majcomp = modeval >> 2u;
|
||||
mode = modeval & 3u;
|
||||
} else if (modeval != 0xFu) {
|
||||
majcomp = modeval & 3u;
|
||||
mode = 4u;
|
||||
} else {
|
||||
majcomp = 0u;
|
||||
mode = 5u;
|
||||
}
|
||||
|
||||
int red = int(v0 & 0x3Fu);
|
||||
int green = int(v1 & 0x1Fu);
|
||||
int blue = int(v2 & 0x1Fu);
|
||||
int scale = int(v3 & 0x1Fu);
|
||||
|
||||
uint x0 = (v1 >> 6u) & 1u;
|
||||
uint x1 = (v1 >> 5u) & 1u;
|
||||
uint x2 = (v2 >> 6u) & 1u;
|
||||
uint x3 = (v2 >> 5u) & 1u;
|
||||
uint x4 = (v3 >> 7u) & 1u;
|
||||
uint x5 = (v3 >> 6u) & 1u;
|
||||
uint x6 = (v3 >> 5u) & 1u;
|
||||
|
||||
uint ohm = 1u << mode;
|
||||
if ((ohm & 0x30u) != 0u) green |= int(x0 << 6u);
|
||||
if ((ohm & 0x3Au) != 0u) green |= int(x1 << 5u);
|
||||
if ((ohm & 0x30u) != 0u) blue |= int(x2 << 6u);
|
||||
if ((ohm & 0x3Au) != 0u) blue |= int(x3 << 5u);
|
||||
if ((ohm & 0x3Du) != 0u) scale |= int(x6 << 5u);
|
||||
if ((ohm & 0x2Du) != 0u) scale |= int(x5 << 6u);
|
||||
if ((ohm & 0x04u) != 0u) scale |= int(x4 << 7u);
|
||||
if ((ohm & 0x3Bu) != 0u) red |= int(x4 << 6u);
|
||||
if ((ohm & 0x04u) != 0u) red |= int(x3 << 6u);
|
||||
if ((ohm & 0x10u) != 0u) red |= int(x5 << 7u);
|
||||
if ((ohm & 0x0Fu) != 0u) red |= int(x2 << 7u);
|
||||
if ((ohm & 0x05u) != 0u) red |= int(x1 << 8u);
|
||||
if ((ohm & 0x0Au) != 0u) red |= int(x0 << 8u);
|
||||
if ((ohm & 0x05u) != 0u) red |= int(x0 << 9u);
|
||||
if ((ohm & 0x02u) != 0u) red |= int(x6 << 9u);
|
||||
if ((ohm & 0x01u) != 0u) red |= int(x3 << 10u);
|
||||
if ((ohm & 0x02u) != 0u) red |= int(x5 << 10u);
|
||||
|
||||
int shamts[6] = int[](1, 1, 2, 3, 4, 5);
|
||||
int shamt = shamts[mode];
|
||||
red <<= shamt;
|
||||
green <<= shamt;
|
||||
blue <<= shamt;
|
||||
scale <<= shamt;
|
||||
|
||||
if (mode != 5u) {
|
||||
green = red - green;
|
||||
blue = red - blue;
|
||||
}
|
||||
|
||||
if (majcomp == 1u) {
|
||||
int t = red; red = green; green = t;
|
||||
}
|
||||
if (majcomp == 2u) {
|
||||
int t = red; red = blue; blue = t;
|
||||
}
|
||||
|
||||
e1 = ivec3(clamp(red, 0, 0xFFF), clamp(green, 0, 0xFFF), clamp(blue, 0, 0xFFF));
|
||||
e0 = ivec3(clamp(red - scale, 0, 0xFFF), clamp(green - scale, 0, 0xFFF),
|
||||
clamp(blue - scale, 0, 0xFFF));
|
||||
}
|
||||
|
||||
// HDR Endpoint Mode 11 (C.2.15): direct RGB pair. Shared by modes 11, 14 and 15, which all
|
||||
// decode their RGB the same way and only differ in how alpha is filled in.
|
||||
void DecodeHDREndpointMode11(uint v0, uint v1, uint v2, uint v3, uint v4, uint v5, out ivec3 e0,
|
||||
out ivec3 e1) {
|
||||
uint majcomp = ((v4 & 0x80u) >> 7u) | ((v5 & 0x80u) >> 6u);
|
||||
if (majcomp == 3u) {
|
||||
e0 = ivec3(int(v0 << 4u), int(v2 << 4u), int((v4 & 0x7Fu) << 5u));
|
||||
e1 = ivec3(int(v1 << 4u), int(v3 << 4u), int((v5 & 0x7Fu) << 5u));
|
||||
return;
|
||||
}
|
||||
|
||||
uint mode = ((v1 & 0x80u) >> 7u) | ((v2 & 0x80u) >> 6u) | ((v3 & 0x80u) >> 5u);
|
||||
int va = int(v0 | ((v1 & 0x40u) << 2u));
|
||||
int vb0 = int(v2 & 0x3Fu);
|
||||
int vb1 = int(v3 & 0x3Fu);
|
||||
int vc = int(v1 & 0x3Fu);
|
||||
int vd0 = int(v4 & 0x7Fu);
|
||||
int vd1 = int(v5 & 0x7Fu);
|
||||
|
||||
int dbitstab[8] = int[](7, 6, 7, 6, 5, 6, 5, 6);
|
||||
vd0 = SignExtend(vd0, uint(dbitstab[mode]));
|
||||
vd1 = SignExtend(vd1, uint(dbitstab[mode]));
|
||||
|
||||
uint x0 = (v2 >> 6u) & 1u;
|
||||
uint x1 = (v3 >> 6u) & 1u;
|
||||
uint x2 = (v4 >> 6u) & 1u;
|
||||
uint x3 = (v5 >> 6u) & 1u;
|
||||
uint x4 = (v4 >> 5u) & 1u;
|
||||
uint x5 = (v5 >> 5u) & 1u;
|
||||
|
||||
uint ohm = 1u << mode;
|
||||
if ((ohm & 0xA4u) != 0u) va |= int(x0 << 9u);
|
||||
if ((ohm & 0x08u) != 0u) va |= int(x2 << 9u);
|
||||
if ((ohm & 0x50u) != 0u) va |= int(x4 << 9u);
|
||||
if ((ohm & 0x50u) != 0u) va |= int(x5 << 10u);
|
||||
if ((ohm & 0xA0u) != 0u) va |= int(x1 << 10u);
|
||||
if ((ohm & 0xC0u) != 0u) va |= int(x2 << 11u);
|
||||
if ((ohm & 0x04u) != 0u) vc |= int(x1 << 6u);
|
||||
if ((ohm & 0xE8u) != 0u) vc |= int(x3 << 6u);
|
||||
if ((ohm & 0x20u) != 0u) vc |= int(x2 << 7u);
|
||||
if ((ohm & 0x5Bu) != 0u) vb0 |= int(x0 << 6u);
|
||||
if ((ohm & 0x5Bu) != 0u) vb1 |= int(x1 << 6u);
|
||||
if ((ohm & 0x12u) != 0u) vb0 |= int(x2 << 7u);
|
||||
if ((ohm & 0x12u) != 0u) vb1 |= int(x3 << 7u);
|
||||
|
||||
int shamt = (int(mode) >> 1) ^ 3;
|
||||
va <<= shamt;
|
||||
vb0 <<= shamt;
|
||||
vb1 <<= shamt;
|
||||
vc <<= shamt;
|
||||
vd0 <<= shamt;
|
||||
vd1 <<= shamt;
|
||||
|
||||
int r1 = clamp(va, 0, 0xFFF);
|
||||
int g1 = clamp(va - vb0, 0, 0xFFF);
|
||||
int b1 = clamp(va - vb1, 0, 0xFFF);
|
||||
int r0 = clamp(va - vc, 0, 0xFFF);
|
||||
int g0 = clamp(va - vb0 - vc - vd0, 0, 0xFFF);
|
||||
int b0 = clamp(va - vb1 - vc - vd1, 0, 0xFFF);
|
||||
|
||||
if (majcomp == 1u) {
|
||||
int t;
|
||||
t = r0; r0 = g0; g0 = t;
|
||||
t = r1; r1 = g1; g1 = t;
|
||||
} else if (majcomp == 2u) {
|
||||
int t;
|
||||
t = r0; r0 = b0; b0 = t;
|
||||
t = r1; r1 = b1; b1 = t;
|
||||
}
|
||||
e0 = ivec3(r0, g0, b0);
|
||||
e1 = ivec3(r1, g1, b1);
|
||||
}
|
||||
|
||||
void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, uint color_values[32],
|
||||
inout uint colvals_index) {
|
||||
#define READ_UINT_VALUES(N) \
|
||||
@@ -715,8 +890,88 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
READ_UINT_VALUES(2)
|
||||
uint y0, y1;
|
||||
if (V[0].y >= V[0].x) {
|
||||
y0 = V[0].x << 4u;
|
||||
y1 = V[0].y << 4u;
|
||||
} else {
|
||||
y0 = (V[0].y << 4u) + 8u;
|
||||
y1 = (V[0].x << 4u) - 8u;
|
||||
}
|
||||
ep1 = uvec4(0x780u, y0, y0, y0);
|
||||
ep2 = uvec4(0x780u, y1, y1, y1);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
READ_UINT_VALUES(2)
|
||||
uint y0, d;
|
||||
if ((V[0].x & 0x80u) != 0u) {
|
||||
y0 = ((V[0].y & 0xE0u) << 4u) | ((V[0].x & 0x7Fu) << 2u);
|
||||
d = (V[0].y & 0x1Fu) << 2u;
|
||||
} else {
|
||||
y0 = ((V[0].y & 0xF0u) << 4u) | ((V[0].x & 0x7Fu) << 1u);
|
||||
d = (V[0].y & 0x0Fu) << 1u;
|
||||
}
|
||||
const uint y1 = min(y0 + d, 0xFFFu);
|
||||
ep1 = uvec4(0x780u, y0, y0, y0);
|
||||
ep2 = uvec4(0x780u, y1, y1, y1);
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
READ_UINT_VALUES(4)
|
||||
ivec3 e0, e1;
|
||||
DecodeHDREndpointMode7(V[0].x, V[0].y, V[0].z, V[0].w, e0, e1);
|
||||
ep1 = uvec4(0x780u, uint(e0.x), uint(e0.y), uint(e0.z));
|
||||
ep2 = uvec4(0x780u, uint(e1.x), uint(e1.y), uint(e1.z));
|
||||
break;
|
||||
}
|
||||
case 11: {
|
||||
READ_UINT_VALUES(6)
|
||||
ivec3 e0, e1;
|
||||
DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1);
|
||||
ep1 = uvec4(0x780u, uint(e0.x), uint(e0.y), uint(e0.z));
|
||||
ep2 = uvec4(0x780u, uint(e1.x), uint(e1.y), uint(e1.z));
|
||||
break;
|
||||
}
|
||||
case 14: {
|
||||
READ_UINT_VALUES(8)
|
||||
ivec3 e0, e1;
|
||||
DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1);
|
||||
// Only HDR mode with LDR (8-bit UNORM)-interpreted alpha; left as-is (0-255).
|
||||
ep1 = uvec4(V[1].z, uint(e0.x), uint(e0.y), uint(e0.z));
|
||||
ep2 = uvec4(V[1].w, uint(e1.x), uint(e1.y), uint(e1.z));
|
||||
break;
|
||||
}
|
||||
case 15: {
|
||||
READ_UINT_VALUES(8)
|
||||
ivec3 e0, e1;
|
||||
DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1);
|
||||
const uint mode = ((V[1].z >> 7u) & 1u) | ((V[1].w >> 6u) & 2u);
|
||||
int a6 = int(V[1].z & 0x7Fu);
|
||||
int a7 = int(V[1].w & 0x7Fu);
|
||||
int alpha0, alpha1;
|
||||
if (mode == 3u) {
|
||||
alpha0 = a6 << 5;
|
||||
alpha1 = a7 << 5;
|
||||
} else {
|
||||
a6 |= (a7 << int(mode + 1u)) & 0x780;
|
||||
a7 &= int(0x3Fu >> mode);
|
||||
a7 ^= int(0x20u >> mode);
|
||||
a7 -= int(0x20u >> mode);
|
||||
a6 <<= int(4u - mode);
|
||||
a7 <<= int(4u - mode);
|
||||
a7 += a6;
|
||||
alpha0 = a6;
|
||||
alpha1 = clamp(a7, 0, 0xFFF);
|
||||
}
|
||||
ep1 = uvec4(uint(alpha0), uint(e0.x), uint(e0.y), uint(e0.z));
|
||||
ep2 = uvec4(uint(alpha1), uint(e1.x), uint(e1.y), uint(e1.z));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// HDR mode, or more likely a bug computing the color_endpoint_mode
|
||||
// Not a valid CEM at all (all 16 values are now handled above).
|
||||
ep1 = uvec4(0xFF, 0xFF, 0, 0);
|
||||
ep2 = uvec4(0xFF, 0xFF, 0, 0);
|
||||
break;
|
||||
@@ -1112,13 +1367,59 @@ void DecompressBlock(ivec3 coord) {
|
||||
if (num_partitions > 1) {
|
||||
local_partition = Select2DPartition(partition_index, i, j, num_partitions);
|
||||
}
|
||||
const uvec4 C0 = ReplicateByteTo16(endpoints0[local_partition]);
|
||||
const uvec4 C1 = ReplicateByteTo16(endpoints1[local_partition]);
|
||||
const uint local_cem = color_endpoint_mode[local_partition];
|
||||
const uvec4 weight_vec = GetUnquantizedWeightVector(j, i, size_params, plane_index, dual_plane);
|
||||
const vec4 Cf =
|
||||
vec4((C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64);
|
||||
const vec4 p = (Cf / 65535.0f);
|
||||
|
||||
vec4 p;
|
||||
if (IsHDRColorEndpointMode(local_cem)) {
|
||||
// Endpoints are raw 12-bit pseudo-logarithmic values; shift left 4 bits to
|
||||
// become 16-bit before interpolating, per C.2.19.
|
||||
const uvec4 C0 = endpoints0[local_partition] << 4u;
|
||||
const uvec4 C1 = endpoints1[local_partition] << 4u;
|
||||
const uvec4 C = (C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64u;
|
||||
const uvec4 E = (C & uvec4(0xF800u)) >> 11u;
|
||||
const uvec4 M = C & uvec4(0x7FFu);
|
||||
const uvec4 Mt_lo = 3u * M;
|
||||
const uvec4 Mt_mid = 4u * M - 512u;
|
||||
const uvec4 Mt_hi = 5u * M - 2048u;
|
||||
const uvec4 Mt =
|
||||
mix(Mt_lo, mix(Mt_mid, Mt_hi, greaterThanEqual(M, uvec4(1536u))),
|
||||
greaterThanEqual(M, uvec4(512u)));
|
||||
const uvec4 Cf = (E << 10u) + (Mt >> 3u);
|
||||
// +Inf/NaN clamps to the largest finite FP16 value (0x7BFF).
|
||||
const uvec4 half_bits = mix(Cf, uvec4(0x7BFFu), greaterThanEqual(Cf, uvec4(0x7C00u)));
|
||||
p = vec4(unpackHalf2x16(half_bits.x).x, unpackHalf2x16(half_bits.y).x,
|
||||
unpackHalf2x16(half_bits.z).x, unpackHalf2x16(half_bits.w).x);
|
||||
|
||||
// Mode 14 keeps an LDR (8-bit UNORM)-interpreted alpha; component 0 here (A,
|
||||
// see the ep1/ep2 layout used throughout this file).
|
||||
if (local_cem == 14u) {
|
||||
const uint a0 = ReplicateByteTo16(uvec4(endpoints0[local_partition].x)).x;
|
||||
const uint a1 = ReplicateByteTo16(uvec4(endpoints1[local_partition].x)).x;
|
||||
const uint Ca = (a0 * (64u - weight_vec.x) + a1 * weight_vec.x + 32u) / 64u;
|
||||
p.x = float(Ca) / 65535.0f;
|
||||
}
|
||||
} else {
|
||||
const uvec4 C0 = ReplicateByteTo16(endpoints0[local_partition]);
|
||||
const uvec4 C1 = ReplicateByteTo16(endpoints1[local_partition]);
|
||||
const vec4 Cf =
|
||||
vec4((C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64);
|
||||
p = Cf / 65535.0f;
|
||||
#ifdef VULKAN
|
||||
// Destination is always linear RGBA16F on this path; apply the sRGB curve
|
||||
// ourselves since there's no sRGB-tagged format left to do it automatically.
|
||||
// p is (A,R,G,B); alpha (p.x) is never gamma-encoded.
|
||||
if (is_srgb != 0u) {
|
||||
p.yzw = SRGBToLinear(p.yzw);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef VULKAN
|
||||
imageStore(dest_image, coord + ivec3(i, j, 0), p.gbar);
|
||||
#else
|
||||
imageStore(dest_image, coord + ivec3(i, j, 0), clamp(p, 0.0f, 1.0f).gbar);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user