diff options
Diffstat (limited to 'src/gfxstream/host/vulkan/emulated_textures/shaders/AstcNew.comp')
-rw-r--r-- | src/gfxstream/host/vulkan/emulated_textures/shaders/AstcNew.comp | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/src/gfxstream/host/vulkan/emulated_textures/shaders/AstcNew.comp b/src/gfxstream/host/vulkan/emulated_textures/shaders/AstcNew.comp new file mode 100644 index 00000000000..7f0b2a29fa8 --- /dev/null +++ b/src/gfxstream/host/vulkan/emulated_textures/shaders/AstcNew.comp @@ -0,0 +1,643 @@ +// Compute shader to perform ASTC decoding. +// +// Usage: +// #include "AstcDecompressor.glsl" +// +// main() { +// uvec4 astcBlock = ... // read an ASTC block +// astcDecoderInitialize(astcBlock, blockSize); +// uvec2 posInBlock = uvec2(0, 0); // which texel we want to decode in the block +// uvec4 texel = astcDecodeTexel(pos); +// } +// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Please refer for the ASTC spec for all the details: +// https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_texture_compression_astc_hdr.txt +// +// +// Quick reminder of an ASTC block layout +// -------------------------------------- +// +// Each ASTC block is 128 bits. From top to bottom (from bit 127 to bit 0), we have: +// 1. weight data (24 - 96 bits). Starts at bit 127 and grows down. (So it needs to be reversed) +// 2. extra CEM data. 0 bits if only 1 partition OR if CEM selector value in bits [24:23] is 00, +// otherwise 2, 5 or 8 bits for 2, 3, or 4 partitions respectively. +// 3. color component selector (CCS) - 2 bits if dual plane is active, otherwise 0 bits. +// 4. Color endpoint data - variable length +// 5. CEM 4 bits if single partition, else 6 bits. +// 6. partition seed 10 bits (13-22) - only if more than 1 partition +// 7. partition count 2 bits (11-12) +// 8. block mode 11 bits ( 0-10) +// +// Optimization ideas +// ------------------ +// +// 1. Use a uniform buffer instead of static arrays to load the tables in AstcLookupTables.glsl +// 2. Investigate using SSBO or sampled image instead of storage image for the input. +// 3. Make decodeTrit() / decodeQuint() return a pair of values, since we always need at least 2 +// 4. Look into which queue we use to run the shader, some GPUs may have a separate compute queue. +// 5. Use a `shared` variable to share the block data and common block config, once we change the +// local group size to match the block size. +// +// Missing features +// ---------------- +// +// 1. Make sure we cover all the cases where we should return the error color? See section C.2.24 +// Illegal Encodings for the full list. +// 2. Add support for 3D slices. +// 3. HDR support? Probably not worth it. + +#include "AstcLookupTables.glsl" + +const uvec4 kErrorColor = uvec4(255, 0, 255, 255); // beautiful magenta, as per the spec + +// Global variables //////////////////////////////////////////////////////////////////////////////// + +uvec4 astcBlock; // Full ASTC block data +uvec2 blockSize; // Size of the ASTC block +bool decodeError; // True if there's an error in the block. +bool voidExtent; // True if void-extent block (all pixels are the same color) +bool dualPlane; // True for dual plane blocks (a block with 2 sets of weights) +uvec2 weightGridSize; // Width and height of the weight grid. Always <= blockSize. +uint numWeights; // Number of weights +uvec3 weightEncoding; // Number of trits (x), quints (y) and bits (z) to encode the weights. +uint weightDataSize; // Size of the weight data in bits +uint numPartitions; // Number of partitions (1-4) +uint partitionSeed; // Determines which partition pattern we use (10 bits) + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// Returns the number of bits needed to encode `numVals` values using a given encoding. +// encoding: number of trits (x), quints (y) and bits (z) used for the encoding. +uint getEncodingSize(uint numVals, uvec3 encoding) { + // See section C.2.22. + uvec2 tqBits = (numVals * encoding.xy * uvec2(8, 7) + uvec2(4, 2)) / uvec2(5, 3); + return numVals * encoding.z + (tqBits.x + tqBits.y); +} + +// This function sets all the global variables above +void astcDecoderInitialize(uvec4 blockData, uvec2 blockSize_) { + astcBlock = blockData; + blockSize = blockSize_; + decodeError = false; + + voidExtent = (astcBlock[3] & 0x1FF) == 0x1FC; + if (voidExtent) return; + + const uint bits01 = bitfieldExtract(astcBlock[3], 0, 2); + const uint bits23 = bitfieldExtract(astcBlock[3], 2, 2); + const uint bit4 = bitfieldExtract(astcBlock[3], 4, 1); + const uint bits56 = bitfieldExtract(astcBlock[3], 5, 2); + const uint bits78 = bitfieldExtract(astcBlock[3], 7, 2); + + uint r; + uint h = bitfieldExtract(astcBlock[3], 9, 1); + dualPlane = bool(bitfieldExtract(astcBlock[3], 10, 1)); + + // Refer to "Table C.2.8 - 2D Block Mode Layout" + if (bits01 == 0) { + r = bits23 << 1 | bit4; + switch (bits78) { + case 0: + weightGridSize = uvec2(12, bits56 + 2); + break; + case 1: + weightGridSize = uvec2(bits56 + 2, 12); + break; + case 2: + weightGridSize = uvec2(bits56 + 6, bitfieldExtract(astcBlock[3], 9, 2) + 6); + dualPlane = false; + h = 0; + break; + case 3: + if (bits56 == 0) { + weightGridSize = uvec2(6, 10); + } else if (bits56 == 1) { + weightGridSize = uvec2(10, 6); + } else { + decodeError = true; + return; + } + } + } else { + r = bits01 << 1 | bit4; + switch (bits23) { + case 0: + weightGridSize = uvec2(bits78 + 4, bits56 + 2); + break; + case 1: + weightGridSize = uvec2(bits78 + 8, bits56 + 2); + break; + case 2: + weightGridSize = uvec2(bits56 + 2, bits78 + 8); + break; + case 3: + if (bits78 >> 1 == 0) { + weightGridSize = uvec2(bits56 + 2, (bits78 & 1) + 6); + } else { + weightGridSize = uvec2((bits78 & 1) + 2, bits56 + 2); + } + } + } + + if (any(greaterThan(weightGridSize, blockSize))) { + decodeError = true; + return; + } + + // weigths + weightEncoding = kWeightEncodings[h << 3 | r]; + numWeights = (weightGridSize.x * weightGridSize.y) << int(dualPlane); + weightDataSize = getEncodingSize(numWeights, weightEncoding); + if (weightDataSize < 24 || weightDataSize > 96 || numWeights > 64) { + decodeError = true; + return; + } + + numPartitions = bitfieldExtract(astcBlock[3], 11, 2) + 1; + if (numPartitions > 1) { + partitionSeed = bitfieldExtract(astcBlock[3], 13, 10); + } + + if (dualPlane && numPartitions == 4) { + decodeError = true; + return; + } +} + +// Extracts a range of bits from a uvec4, treating it as a single 128-bit field. +// offset: index of the first bit to extract (0-127). +// numBits: number of bits to extract. (0-32). If numBits is 0, this returns 0. +// Result is undefined if offset >= 128 or offset + numBits > 128 +uint extractBits(uvec4 data, uint offset, uint numBits) { + if (numBits == 0) return 0; + + const uint i = 3 - offset / 32; + const uint j = 3 - (offset + numBits - 1) / 32; + const uint start = offset & 31; + if (i == j) { + // All the bits to extract are located on the same component of the vector + return bitfieldExtract(data[i], int(start), int(numBits)); + } else { + uint numLowBits = 32 - start; + uint lowBits = bitfieldExtract(data[i], int(start), int(numLowBits)); + uint highBits = bitfieldExtract(data[j], 0, int(numBits - numLowBits)); + return (highBits << numLowBits) | lowBits; + } +} + +// Returns the CEM, a number between 0 and 15 that determines how the endpoints are encoded. +// Also sets a couple of output parameters: +// - startOfExtraCem: bit position of the start of the extra CEM +// - totalEndpoints: number of endpoints in the block, for all partitions. +// - baseEndpointIndex: index of the first endpoint for this partition +// Refer to "Section C.2.11 Color Endpoint Mode" for decoding details +uint decodeCEM(uint partitionIndex, out uint startOfExtraCem, out uint totalEndpoints, + out uint baseEndpointIndex) { + if (numPartitions == 1) { + startOfExtraCem = 128 - weightDataSize; + const uint cem = bitfieldExtract(astcBlock[3], 13, 4); + totalEndpoints = 2 * (cem >> 2) + 2; + baseEndpointIndex = 0; + return cem; + } else { + const uint cemSelector = bitfieldExtract(astcBlock[3], 23, 2); + const uint baseCem = bitfieldExtract(astcBlock[3], 25, 4); + + if (cemSelector == 0) { + // We're in the multi-partition, single CEM case + startOfExtraCem = 128 - weightDataSize; + const uint endpointsPerPartition = 2 * (baseCem >> 2) + 2; + totalEndpoints = endpointsPerPartition * numPartitions; + baseEndpointIndex = endpointsPerPartition * partitionIndex; + return baseCem; + } else { + // Refer to "Figure C.4" for the details of the encoding here. + + // Size in bits of the extra CEM data, which is located right after the weight data. + const uint sizeOfExtraCem = 3 * numPartitions - 4; + startOfExtraCem = 128 - weightDataSize - sizeOfExtraCem; + + // Extract the extra CEM data + const uint extraCem = extractBits(astcBlock, startOfExtraCem, sizeOfExtraCem); + const uint fullCem = extraCem << 4 | baseCem; + + const uint mValue = + bitfieldExtract(fullCem, int(2 * partitionIndex + numPartitions), 2); + const uint cValues = bitfieldExtract(fullCem, 0, int(numPartitions)); + + // TODO(gregschlom): investigate whether a couple of small lookup tables would be more + // efficient here. + totalEndpoints = 2 * (cemSelector * numPartitions + bitCount(cValues)); + baseEndpointIndex = 2 * (cemSelector * partitionIndex + + bitCount(bitfieldExtract(cValues, 0, int(partitionIndex)))); + uint baseClass = cemSelector - 1 + bitfieldExtract(cValues, int(partitionIndex), 1); + return baseClass << 2 | mValue; + } + } +} + +// Decodes a single trit within a block of 5. +// offset: bit offset where the block of trits starts, within the 128 bits of data +// numBits: how many bits are used to encode the LSB (0-6) +// i: index of the trit within the block (0-4) +// See section "C.2.12 Integer Sequence Encoding" +uint decodeTrit(uvec4 data, uint offset, uint numBits, uint i) { + const int inumBits = int(numBits); + + // In the largest encoding possible (1 trit + 6 bits), the block is 38 bits long (5 * 6 + 8). + // Since this wouldn't fit in 32 bits, we extract the low bits for the trit index 0 separately, + // this way we only need at most 4 * 6 + 8 = 32 bits, which fits perfectly. + const uint block = extractBits(data, offset + numBits, 4 * numBits + 8); + + // Extract the 8 bits that encode the pack of 5 trits + // TODO(gregschlom): Optimization idea: if numbits == 0, then packedTrits = block. Worth doing? + const uint packedTrits = bitfieldExtract(block, 0, 2) | + bitfieldExtract(block, 1 * inumBits + 2, 2) << 2 | + bitfieldExtract(block, 2 * inumBits + 4, 1) << 4 | + bitfieldExtract(block, 3 * inumBits + 5, 2) << 5 | + bitfieldExtract(block, 4 * inumBits + 7, 1) << 7; + + // Extract the LSB + uint lowBits; + if (i == 0) { + lowBits = extractBits(data, offset, numBits); + } else { + const int j = int(i) - 1; + const ivec4 deltas = {2, 4, 5, 7}; + lowBits = bitfieldExtract(block, j * inumBits + deltas[j], inumBits); + } + + const uint decoded = kTritEncodings[packedTrits]; + return bitfieldExtract(decoded, 2 * int(i), 2) << numBits | lowBits; +} + +// Decodes a single quint within a block of 3. +// offset: bit offset where the block of quint starts, within the 128 bits of data +// numBits: how many bits are used to encode the LSB (0-5) +// i: index of the quint within the block (0-2) +// See section "C.2.12 Integer Sequence Encoding" +uint decodeQuint(uvec4 data, uint offset, uint numBits, uint i) { + const int inumBits = int(numBits); + + // Note that we don't have the same size issue as trits (see above), since the largest encoding + // here is 1 quint and 5 bits, which is 3 * 5 + 7 = 22 bits long + const uint block = extractBits(data, offset, 3 * numBits + 7); + + // Extract the 7 bits that encode the pack of 3 quints + const uint packedQuints = bitfieldExtract(block, inumBits, 3) | + bitfieldExtract(block, 2 * inumBits + 3, 2) << 3 | + bitfieldExtract(block, 3 * inumBits + 5, 2) << 5; + + // Extract the LSB + const ivec3 deltas = {0, 3, 5}; + const uint lowBits = bitfieldExtract(block, int(i) * inumBits + deltas[i], inumBits); + + const uint decoded = kQuintEncodings[packedQuints]; + return bitfieldExtract(decoded, 3 * int(i), 3) << numBits | lowBits; +} + +uint decode1Weight(uvec4 weightData, uvec3 encoding, uint numWeights, uint index) { + if (index >= numWeights) return 0; + + uint numBits = encoding.z; + + if (encoding.x == 1) { + // 1 trit + uint offset = (index / 5) * (5 * numBits + 8); + uint w = decodeTrit(weightData, offset, numBits, index % 5); + return kUnquantTritWeightMap[3 * ((1 << numBits) - 1) + w]; + } else if (encoding.y == 1) { + // 1 quint + uint offset = (index / 3) * (3 * numBits + 7); + uint w = decodeQuint(weightData, offset, numBits, index % 3); + return kUnquantQuintWeightMap[5 * ((1 << numBits) - 1) + w]; + } else { + // only bits, no trits or quints. We can have between 1 and 6 bits. + uint offset = index * numBits; + uint w = extractBits(weightData, offset, numBits); + + // The first number in the table is the multiplication factor: 63 / (2^numBits - 1) + // The second number is a shift factor to adjust when the previous result isn't an integer. + const uvec2 kUnquantTable[] = {{63, 8}, {21, 8}, {9, 8}, {4, 2}, {2, 4}, {1, 8}}; + const uvec2 unquant = kUnquantTable[numBits - 1]; + w = w * unquant.x | w >> unquant.y; + if (w > 32) w += 1; + return w; + } +} + +uint interpolateWeights(uvec4 weightData, uvec3 encoding, uint numWeights, uint index, + uint gridWidth, uint stride, uint offset, uvec2 fractionalPart) { + uvec4 weightIndices = stride * (uvec4(index) + uvec4(0, 1, gridWidth, gridWidth + 1)) + offset; + + // TODO(gregschlom): Optimization idea: instead of always decoding 4 weights, we could decode + // just what we need depending on whether fractionalPart.x and fractionalPart.y are 0 + uvec4 weights = uvec4(decode1Weight(weightData, encoding, numWeights, weightIndices[0]), + decode1Weight(weightData, encoding, numWeights, weightIndices[1]), + decode1Weight(weightData, encoding, numWeights, weightIndices[2]), + decode1Weight(weightData, encoding, numWeights, weightIndices[3])); + + uint w11 = (fractionalPart.x * fractionalPart.y + 8) >> 4; + uvec4 factors = uvec4(16 - fractionalPart.x - fractionalPart.y + w11, // w00 + fractionalPart.x - w11, // w01 + fractionalPart.y - w11, // w10 + w11); // w11 + + return uint(dot(weights, factors) + 8) >> 4; // this is what the spec calls "effective weight" +} + +uvec2 decodeWeights(uvec4 weightData, const uvec2 posInBlock) { + // Refer to "C.2.18 Weight Infill to interpolate between 4 grid points" + + // TODO(gregschlom): The spec says: "since the block dimensions are constrained, these are + // easily looked up in a table." - Is it worth doing? + uvec2 scaleFactor = (1024 + blockSize / 2) / (blockSize - 1); + + uvec2 homogeneousCoords = posInBlock * scaleFactor; + uvec2 gridCoords = (homogeneousCoords * (weightGridSize - 1) + 32) >> 6; + uvec2 integralPart = gridCoords >> 4; + uvec2 fractionalPart = gridCoords & 0xf; + + uint gridWidth = weightGridSize.x; + uint v0 = integralPart.y * gridWidth + integralPart.x; + + uvec2 weights = uvec2(0); + weights.x = interpolateWeights(weightData, weightEncoding, numWeights, v0, gridWidth, + 1 << int(dualPlane), 0, fractionalPart); + if (dualPlane) { + weights.y = interpolateWeights(weightData, weightEncoding, numWeights, v0, gridWidth, 2, 1, + fractionalPart); + } + return weights; +} + +uint hash52(uint p) { + p ^= p >> 15; + p -= p << 17; + p += p << 7; + p += p << 4; + p ^= p >> 5; + p += p << 16; + p ^= p >> 7; + p ^= p >> 3; + p ^= p << 6; + p ^= p >> 17; + return p; +} + +uint selectPartition(uint seed, uvec2 pos, uint numPartitions) { + if (numPartitions == 1) { + return 0; + } + if (blockSize.x * blockSize.y < 31) { + pos <<= 1; + } + seed = 1024 * numPartitions + (seed - 1024); + uint rnum = hash52(seed); + // TODO(gregschlom): micro-optimization: could repetedly halve the bits to extract them in 6 + // calls to bitfieldExtract instead of 8. + uvec4 seedA = uvec4(bitfieldExtract(rnum, 0, 4), bitfieldExtract(rnum, 4, 4), + bitfieldExtract(rnum, 8, 4), bitfieldExtract(rnum, 12, 4)); + uvec4 seedB = uvec4(bitfieldExtract(rnum, 16, 4), bitfieldExtract(rnum, 20, 4), + bitfieldExtract(rnum, 24, 4), bitfieldExtract(rnum, 28, 4)); + + seedA = seedA * seedA; + seedB = seedB * seedB; + + uvec2 shifts1 = uvec2((seed & 2) != 0 ? 4 : 5, numPartitions == 3 ? 6 : 5); + uvec4 shifts2 = (seed & 1) != 0 ? shifts1.xyxy : shifts1.yxyx; + + seedA >>= shifts2; + seedB >>= shifts2; + + // Note: this could be implemented as matrix multiplication, but we'd have to use floats and I'm + // not sure if the values are always small enough to stay accurate. + uvec4 result = + uvec4(dot(seedA.xy, pos), dot(seedA.zw, pos), dot(seedB.xy, pos), dot(seedB.zw, pos)) + + (uvec4(rnum) >> uvec4(14, 10, 6, 2)); + + result &= uvec4(0x3F); + + if (numPartitions == 2) { + result.zw = uvec2(0); + } else if (numPartitions == 3) { + result.w = 0; + } + + // Return the index of the largest component in `result` + if (all(greaterThanEqual(uvec3(result.x), result.yzw))) { + return 0; + } else if (all(greaterThanEqual(uvec2(result.y), result.zw))) { + return 1; + } else if (result.z >= result.w) { + return 2; + } else { + return 3; + } +} + +uvec3 getEndpointEncoding(uint availableEndpointBits, uint numEndpoints, out uint actualSize) { + // This implements the algorithm described in section "C.2.22 Data Size Determination" + // TODO(gregschlom): This could be implemented with a lookup table instead. Or we could use a + // binary search but not sure if worth it due to the extra cost of branching. + for (uint i = 0; i < kColorEncodings.length(); ++i) { + uvec3 encoding = kColorEncodings[i]; + actualSize = getEncodingSize(numEndpoints, encoding); + if (actualSize <= availableEndpointBits) { + return encoding; + } + } + return uvec3(0); // this should never happen +} + +ivec4 blueContract(ivec4 v) { return ivec4((v.r + v.b) >> 1, (v.g + v.b) >> 1, v.ba); } + +int sum(ivec3 v) { return v.x + v.y + v.z; } + +void bitTransferSigned(inout ivec4 a, inout ivec4 b) { + b >>= 1; + b |= a & 0x80; + a >>= 1; + a &= 0x3f; + // This is equivalent to: "if ((a & 0x20) != 0) a -= 0x40;" in the spec. It treats "a" as a + // 6-bit signed integer, converting it from (0, 63) to (-32, 31) + a = bitfieldExtract(a, 0, 6); +} + +// Decodes the endpoints and writes them to ep0 and ep1. +// vA: even-numbered values in the spec (ie: v0, v2, v4 and v6) +// vB: odd-numbered values in the spec (ie: v1, v3, v5 and v7) +// mode: the CEM (color endpoint mode) +// Note: HDR modes are not supported. +void decodeEndpoints(ivec4 vA, ivec4 vB, uint mode, out uvec4 ep0, out uvec4 ep1) { + switch (mode) { + case 0: // LDR luminance only, direct + ep0 = uvec4(vA.xxx, 255); + ep1 = uvec4(vB.xxx, 255); + return; + + case 1: { // LDR luminance only, base + offset + const int l0 = (vA.x >> 2) | (vB.x & 0xC0); + const int l1 = min(l0 + (vB.x & 0x3F), 255); + ep0 = uvec4(uvec3(l0), 255); + ep1 = uvec4(uvec3(l1), 255); + return; + } + + case 4: // LDR luminance + alpha, direct + ep0 = vA.xxxy; + ep1 = vB.xxxy; + return; + + case 5: // LDR luminance + alpha, base + offset + bitTransferSigned(vB, vA); + ep0 = clamp(vA.xxxy, 0, 255); + ep1 = clamp(vA.xxxy + vB.xxxy, 0, 255); + return; + + case 6: // LDR RGB, base + scale + ep1 = uvec4(vA.x, vB.x, vA.y, 255); + ep0 = uvec4((ep1.rgb * vB.y) >> 8, 255); + return; + + case 10: // LDR RGB, base + scale, plus alphas + ep1 = uvec4(vA.x, vB.x, vA.y, vB.z); + ep0 = uvec4((ep1.rgb * vB.y) >> 8, vA.z); + return; + + case 8: // LDR RGB, direct + vA.a = 255; + vB.a = 255; + case 12: // LDR RGBA, direct + if (sum(vB.rgb) >= sum(vA.rgb)) { + ep0 = vA; + ep1 = vB; + } else { + ep0 = blueContract(vB); + ep1 = blueContract(vA); + } + return; + + case 9: // LDR RGB, base + offset + // We will end up with vA.a = 255 and vB.a = 0 after calling bitTransferSigned(vB, vA) + vA.a = 255; + vB.a = -128; + case 13: // LDR RGBA, base + offset + bitTransferSigned(vB, vA); + if (sum(vB.rgb) >= 0) { + ep0 = clamp(vA, 0, 255); + ep1 = clamp(vA + vB, 0, 255); + } else { + ep0 = clamp(blueContract(vA + vB), 0, 255); + ep1 = clamp(blueContract(vA), 0, 255); + } + return; + + default: + // Unimplemented color encoding. (HDR) + ep0 = uvec4(0); + ep1 = uvec4(0); + } +} + +uint decode1Endpoint(uvec4 data, uint startOffset, uint index, uvec3 encoding) { + uint numBits = encoding.z; + + if (encoding.x == 1) { + // 1 trit + uint offset = (index / 5) * (5 * numBits + 8) + startOffset; + uint ep = decodeTrit(data, offset, numBits, index % 5); + return kUnquantTritColorMap[3 * ((1 << numBits) - 1) + ep]; + } else if (encoding.y == 1) { + // 1 quint + uint offset = (index / 3) * (3 * numBits + 7) + startOffset; + uint ep = decodeQuint(data, offset, numBits, index % 3); + return kUnquantQuintColorMap[5 * ((1 << numBits) - 1) + ep]; + } else { + // only bits, no trits or quints. We can have between 1 and 8 bits. + uint offset = index * numBits + startOffset; + uint w = extractBits(data, offset, numBits); + // The first number in the table is the multiplication factor. 255 / (2^numBits - 1) + // The second number is a shift factor to adjust when the previous result isn't an integer. + const uvec2 kUnquantTable[] = {{255, 8}, {85, 8}, {36, 1}, {17, 8}, + {8, 2}, {4, 4}, {2, 6}, {1, 8}}; + const uvec2 unquant = kUnquantTable[numBits - 1]; + return w * unquant.x | w >> unquant.y; + } +} + +// Creates a 128-bit mask with the lower n bits set to 1 +uvec4 buildBitmask(uint bits) { + ivec4 numBits = int(bits) - ivec4(96, 64, 32, 0); + uvec4 mask = (uvec4(1) << clamp(numBits, ivec4(0), ivec4(31))) - 1; + return mix(mask, uvec4(0xffffffffu), greaterThanEqual(uvec4(bits), uvec4(128, 96, 64, 32))); +} + +// Main function to decode the texel at a given position in the block +uvec4 astcDecodeTexel(const uvec2 posInBlock) { + if (decodeError) { + return kErrorColor; + } + + if (voidExtent) { + return uvec4(bitfieldExtract(astcBlock[1], 8, 8), bitfieldExtract(astcBlock[1], 24, 8), + bitfieldExtract(astcBlock[0], 8, 8), bitfieldExtract(astcBlock[0], 24, 8)); + } + + const uvec4 weightData = bitfieldReverse(astcBlock.wzyx) & buildBitmask(weightDataSize); + const uvec2 weights = decodeWeights(weightData, posInBlock); + + const uint partitionIndex = selectPartition(partitionSeed, posInBlock, numPartitions); + + uint startOfExtraCem = 0; + uint totalEndpoints = 0; + uint baseEndpointIndex = 0; + uint cem = decodeCEM(partitionIndex, startOfExtraCem, totalEndpoints, baseEndpointIndex); + + // Per spec, we must return the error color if we require more than 18 color endpoints + if (totalEndpoints > 18) { + return kErrorColor; + } + + const uint endpointsStart = (numPartitions == 1) ? 17 : 29; + const uint endpointsEnd = -2 * int(dualPlane) + startOfExtraCem; + const uint availableEndpointBits = endpointsEnd - endpointsStart; + // TODO(gregschlom): Do we need this: if (availableEndpointBits >= 128) return kErrorColor; + + uint actualEndpointBits; + const uvec3 endpointEncoding = + getEndpointEncoding(availableEndpointBits, totalEndpoints, actualEndpointBits); + // TODO(gregschlom): Do we need this: if (endpointEncoding == uvec3(0)) return kErrorColor; + + // Number of endpoints pairs in this partition. (Between 1 and 4) + // This is the n field from "Table C.2.17 - Color Endpoint Modes" divided by two + const uint numEndpointPairs = (cem >> 2) + 1; + + ivec4 vA = ivec4(0); // holds what the spec calls v0, v2, v4 and v6 + ivec4 vB = ivec4(0); // holds what the spec calls v1, v3, v5 and v7 + + uvec4 epData = astcBlock & buildBitmask(endpointsStart + actualEndpointBits); + + for (uint i = 0; i < numEndpointPairs; ++i) { + const uint epIdx = 2 * i + baseEndpointIndex; + vA[i] = int(decode1Endpoint(epData, endpointsStart, epIdx, endpointEncoding)); + vB[i] = int(decode1Endpoint(epData, endpointsStart, epIdx + 1, endpointEncoding)); + } + + uvec4 ep0, ep1; + decodeEndpoints(vA, vB, cem, ep0, ep1); + + uvec4 weightsPerChannel = uvec4(weights[0]); + if (dualPlane) { + uint ccs = extractBits(astcBlock, endpointsEnd, 2); + weightsPerChannel[ccs] = weights[1]; + } + + return (ep0 * (64 - weightsPerChannel) + ep1 * weightsPerChannel + 32) >> 6; + + // TODO(gregschlom): Check section "C.2.19 Weight Application" - we're supposed to do something + // else here, depending on whether we're using sRGB or not. Currently we have a difference of up + // to 1 when compared against the reference decoder. Probably not worth trying to fix it though. +} |