BC7-Format
Das BC7-Format ist ein Texturkomprimierungsformat, das für die qualitativ hochwertige Komprimierung von RGB- und RGBA-Daten verwendet wird.
Informationen zu den Blockmodi des BC7-Formats finden Sie unter BC7 Format Mode Reference.
Über BC7/DXGI_FORMAT_BC7
BC7 wird durch die folgenden DXGI_FORMAT Enumerationswerte angegeben:
- DXGI_FORMAT_BC7_TYPELESS.
- DXGI_FORMAT_BC7_UNORM.
- DXGI_FORMAT_BC7_UNORM_SRGB.
Das BC7-Format kann für Texture2D- (einschließlich Arrays), Texture3D- oder TextureCube-Texturressourcen (einschließlich Arrays) verwendet werden. Ebenso gilt dieses Format für alle MIP-Kartenoberflächen, die diesen Ressourcen zugeordnet sind.
BC7 verwendet eine feste Blockgröße von 16 Bytes (128 Bit) und eine feste Kachelgröße von 4x4 Texeln. Wie bei vorherigen BC-Formaten werden Texturbilder, die größer als die unterstützte Kachelgröße (4x4) sind, mithilfe mehrerer Blöcke komprimiert. Diese Adressierungsidentität gilt auch für dreidimensionale Bilder und MIP-Karten, Cubemaps und Texturarrays. Alle Bildkacheln müssen dasselbe Format aufweisen.
BC7 komprimiert sowohl Drei-Kanal-(RGB)- als auch RGBA-Festpunktdatenbilder. Quelldaten sind in der Regel 8 Bit pro Farbkomponente (Kanal), obwohl das Format Quelldaten mit höheren Bits pro Farbkomponente codieren kann. Alle Bildkacheln müssen dasselbe Format aufweisen.
Der BC7-Decoder führt die Dekomprimierung aus, bevor die Texturfilterung angewendet wird.
BC7-Dekomprimierungshardware muss bitgenau sein; d. h., die Hardware muss Ergebnisse zurückgeben, die mit den Ergebnissen identisch sind, die vom in diesem Dokument beschriebenen Decoder zurückgegeben werden.
BC7-Implementierung
Eine BC7-Implementierung kann einen von 8 Modi angeben, wobei der Modus im geringsten signifikanten Bit des 16-Byte-Blocks (128 Bit) angegeben ist. Der Modus wird mit null oder mehr Bits mit dem Wert 0, gefolgt von einer 1, codiert.
Ein BC7-Block kann mehrere Endpunktpaare enthalten. Für die Zwecke dieser Dokumentation kann der Satz von Indizes, die einem Endpunktpaar entsprechen, als "Teilmenge" bezeichnet werden. Außerdem wird die Endpunktdarstellung in einigen Blockmodi in einer Form codiert, die – für die Zwecke dieser Dokumentation – erneut als "RGBP" bezeichnet wird, wobei das "P"-Bit ein freigegebenes, am wenigsten signifikantes Bit für die Farbkomponenten des Endpunkts darstellt. Wenn die Endpunktdarstellung für das Format beispielsweise "RGB 5.5.5.5.1" lautet, wird der Endpunkt als RGB 6.6.6-Wert interpretiert, wobei der Zustand des P-Bits das am wenigsten signifikante Bit jeder Komponente definiert. Ebenso wird der Endpunkt für Quelldaten mit einem Alphakanal interpretiert, wenn die Darstellung für das Format "RGBAP 5.5.5.5.1" lautet, als RGBA 6.6.6.6.6 interpretiert wird. Je nach Blockmodus können Sie das freigegebene am wenigsten signifikante Bit für beide Endpunkte einer Teilmenge einzeln (2 P-Bit pro Teilmenge) oder für Endpunkte einer Teilmenge (1 P-Bit pro Teilmenge) angeben.
Für BC7-Blöcke, die die Alphakomponente nicht explizit codieren, besteht ein BC7-Block aus Modusbits, Partitionsbits, komprimierten Endpunkten, komprimierten Indizes und einem optionalen P-Bit. In diesen Blöcken weisen die Endpunkte eine rgb-only-Darstellung auf, und die Alphakomponente wird als 1,0 für alle Texel in den Quelldaten decodiert.
Für BC7-Blöcke mit kombinierten Farb- und Alphakomponenten besteht ein Block aus Modusbits, komprimierten Endpunkten, komprimierten Indizes und optionalen Partitionsbits und einem P-Bit. In diesen Blöcken werden die Endpunktfarben im RGBA-Format ausgedrückt, und Alphakomponentenwerte werden zusammen mit den Farbkomponentenwerten interpoliert.
Für BC7-Blöcke mit separaten Farb- und Alphakomponenten besteht ein Block aus Modusbits, Drehungsbits, komprimierten Endpunkten, komprimierten Indizes und einem optionalen Indexmarkiererbit. Diese Blöcke weisen einen effektiven RGB-Vektor [R, G, B] und einen skalaren Alphakanal [A] separat codiert auf.
In der folgenden Tabelle sind die Komponenten der einzelnen Blocktypen aufgeführt.
BC7-Block enthält... | Modusbits | Drehungsbits | Indexauswahlbit | Partitionsbits | komprimierte Endpunkte | P-Bit | komprimierte Indizes |
---|---|---|---|---|---|---|---|
Nur Farbkomponenten | Erforderlich | N/A | N/A | Erforderlich | Erforderlich | wahlfrei | Erforderlich |
Farbe + Alpha kombiniert | Erforderlich | N/A | N/A | wahlfrei | Erforderlich | wahlfrei | Erforderlich |
Farbe und Alpha getrennt | Erforderlich | Erforderlich | wahlfrei | N/A | Erforderlich | N/A | Erforderlich |
BC7 definiert eine Palette von Farben in einer ungefähren Linie zwischen zwei Endpunkten. Der Moduswert bestimmt die Anzahl der interpolierenden Endpunktpaare pro Block. BC7 speichert einen Palettenindex pro Texel.
Für jede Teilmenge von Indizes, die einem Endpunktpaar entsprechen, behebt der Encoder den Status eines Bits der komprimierten Indexdaten für diese Teilmenge. Dazu wählen Sie eine Endpunktreihenfolge aus, mit der der Index für den angegebenen "fix-up"-Index das wichtigste Bit auf 0 festlegen kann und das dann verworfen werden kann und ein Bit pro Teilmenge gespeichert werden kann. Bei Blockmodi mit nur einer einzelnen Teilmenge ist der Fixupindex immer Index 0.
Decodieren des BC7-Formats
Der folgende Pseudocode beschreibt die Schritte zum Dekomprimieren des Pixels bei (x,y) aufgrund des 16 Byte BC7-Blocks.
decompress_bc7(x, y, block)
{
mode = extract_mode(block);
//decode partition data from explicit partition bits
subset_index = 0;
num_subsets = 1;
if (mode.type == 0 OR == 1 OR == 2 OR == 3 OR == 7)
{
num_subsets = get_num_subsets(mode.type);
partition_set_id = extract_partition_set_id(mode, block);
subset_index = get_partition_index(num_subsets, partition_set_id, x, y);
}
//extract raw, compressed endpoint bits
UINT8 endpoint_array[2 * num_subsets][4] = extract_endpoints(mode, block);
//decode endpoint color and alpha for each subset
fully_decode_endpoints(endpoint_array, mode, block);
//endpoints are now complete.
UINT8 endpoint_start[4] = endpoint_array[2 * subset_index];
UINT8 endpoint_end[4] = endpoint_array[2 * subset_index + 1];
//Determine the palette index for this pixel
alpha_index = get_alpha_index(block, mode, x, y);
alpha_bitcount = get_alpha_bitcount(block, mode);
color_index = get_color_index(block, mode, x, y);
color_bitcount = get_color_bitcount(block, mode);
//determine output
UINT8 output[4];
output.rgb = interpolate(endpoint_start.rgb, endpoint_end.rgb, color_index, color_bitcount);
output.a = interpolate(endpoint_start.a, endpoint_end.a, alpha_index, alpha_bitcount);
if (mode.type == 4 OR == 5)
{
//Decode the 2 color rotation bits as follows:
// 00 – Block format is Scalar(A) Vector(RGB) - no swapping
// 01 – Block format is Scalar(R) Vector(AGB) - swap A and R
// 10 – Block format is Scalar(G) Vector(RAB) - swap A and G
// 11 - Block format is Scalar(B) Vector(RGA) - swap A and B
rotation = extract_rot_bits(mode, block);
output = swap_channels(output, rotation);
}
}
Der folgende Pseudocode beschreibt die Schritte zum vollständigen Decodieren der Endpunktfarbe und Alphakomponenten für jede Teilmenge, die einen 16-Byte-BC7-Block enthält.
fully_decode_endpoints(endpoint_array, mode, block)
{
//first handle modes that have P-bits
if (mode.type == 0 OR == 1 OR == 3 OR == 6 OR == 7)
{
for each endpoint i
{
//component-wise left-shift
endpoint_array[i].rgba = endpoint_array[i].rgba << 1;
}
//if P-bit is shared
if (mode.type == 1)
{
pbit_zero = extract_pbit_zero(mode, block);
pbit_one = extract_pbit_one(mode, block);
//rgb component-wise insert pbits
endpoint_array[0].rgb |= pbit_zero;
endpoint_array[1].rgb |= pbit_zero;
endpoint_array[2].rgb |= pbit_one;
endpoint_array[3].rgb |= pbit_one;
}
else //unique P-bit per endpoint
{
pbit_array = extract_pbit_array(mode, block);
for each endpoint i
{
endpoint_array[i].rgba |= pbit_array[i];
}
}
}
for each endpoint i
{
// Color_component_precision & alpha_component_precision includes pbit
// left shift endpoint components so that their MSB lies in bit 7
endpoint_array[i].rgb = endpoint_array[i].rgb << (8 - color_component_precision(mode));
endpoint_array[i].a = endpoint_array[i].a << (8 - alpha_component_precision(mode));
// Replicate each component's MSB into the LSBs revealed by the left-shift operation above
endpoint_array[i].rgb = endpoint_array[i].rgb | (endpoint_array[i].rgb >> color_component_precision(mode));
endpoint_array[i].a = endpoint_array[i].a | (endpoint_array[i].a >> alpha_component_precision(mode));
}
//If this mode does not explicitly define the alpha component
//set alpha equal to 1.0
if (mode.type == 0 OR == 1 OR == 2 OR == 3)
{
for each endpoint i
{
endpoint_array[i].a = 255; //i.e. alpha = 1.0f
}
}
}
Um jede interpolierte Komponente für jede Teilmenge zu generieren, verwenden Sie den folgenden Algorithmus: "c" als Komponente zu generieren; "e0" soll die Komponente von Endpunkt 0 der Teilmenge sein; und lassen Sie "e1" diese Komponente von Endpunkt 1 der Teilmenge sein.
UINT16 aWeights2[] = {0, 21, 43, 64};
UINT16 aWeights3[] = {0, 9, 18, 27, 37, 46, 55, 64};
UINT16 aWeights4[] = {0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64};
UINT8 interpolate(UINT8 e0, UINT8 e1, UINT8 index, UINT8 indexprecision)
{
if(indexprecision == 2)
return (UINT8) (((64 - aWeights2[index])*UINT16(e0) + aWeights2[index]*UINT16(e1) + 32) >> 6);
else if(indexprecision == 3)
return (UINT8) (((64 - aWeights3[index])*UINT16(e0) + aWeights3[index]*UINT16(e1) + 32) >> 6);
else // indexprecision == 4
return (UINT8) (((64 - aWeights4[index])*UINT16(e0) + aWeights4[index]*UINT16(e1) + 32) >> 6);
}
Der folgende Pseudocode veranschaulicht, wie Indizes und Bitanzahlen für Farb- und Alphakomponenten extrahiert werden. Blöcke mit separater Farbe und Alpha verfügen auch über zwei Sätze von Indexdaten: eine für den Vektorkanal und eine für den skalaren Kanal. Bei Modus 4 bestehen diese Indizes aus unterschiedlichen Breiten (2 oder 3 Bit), und es gibt einen Ein-Bit-Selektor, der angibt, ob die Vektor- oder Skalardaten die 3-Bit-Indizes verwenden. (Das Extrahieren der Alphabitanzahl ähnelt dem Extrahieren der Farbbitanzahl, aber mit umgekehrten Verhaltensweisen basierend auf dem idxMode Bit.)
bitcount get_color_bitcount(block, mode)
{
if (mode.type == 0 OR == 1)
return 3;
if (mode.type == 2 OR == 3 OR == 5 OR == 7)
return 2;
if (mode.type == 6)
return 4;
//The only remaining case is Mode 4 with 1-bit index selector
idxMode = extract_idxMode(block);
if (idxMode == 0)
return 2;
else
return 3;
}
Verwandte Themen