Freigeben über


BC7-Format

Das BC7-Format ist ein Texturkomprimierungsformat, das für die hochwertige Komprimierung von RGB- und RGBA-Daten verwendet wird.

Informationen zu den Blockmodi des BC7-Formats finden Sie unter Referenz zum BC7-Formatmodus.

Informationen zu 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 -Texturressourcen (einschließlich Arrays), Texture3D oder TextureCube (einschließlich Arrays) verwendet werden. In ähnlicher Weise 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 Texels. Wie bei früheren BC-Formaten werden Texturbilder, die größer als die unterstützte Kachelgröße (4x4) sind, mithilfe mehrerer Blöcke komprimiert. Diese Adressidentität gilt auch für dreidimensionale Bilder und MIP-Karten, Cubemaps und Texturarrays. Alle Bildkacheln müssen das gleiche Format aufweisen.

BC7 komprimiert sowohl dreikanalige (RGB) als auch vierkanalige (RGBA)-Fixed-Point-Datenbilder. In der Regel sind Quelldaten 8 Bit pro Farbkomponente (Kanal), obwohl das Format Quelldaten mit höheren Bits pro Farbkomponente codieren kann. Alle Bildkacheln müssen das gleiche Format aufweisen.

Der BC7-Decoder führt eine Dekomprimierung durch, bevor die Texturfilterung angewendet wird.

BC7-Dekomprimierungshardware muss bitgenau sein. Das heißt, die Hardware muss Ergebnisse zurückgeben, die mit den vom Decoder zurückgegebenen Ergebnissen identisch sind, die in diesem Dokument beschrieben werden.

BC7-Implementierung

Eine BC7-Implementierung kann einen von 8 Modi angeben, wobei der Modus im kleinsten Bit des 16-Byte-Blocks (128 Bit) angegeben wird. Der Modus wird durch 0 oder mehr Bits codiert, gefolgt von einer 1.

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 in einigen Blockmodi die Endpunktdarstellung in einer Form codiert, die - wiederum für die Zwecke dieser Dokumentation - als "RGBP" bezeichnet werden soll, 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.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. In ähnlicher Weise wird für Quelldaten mit einem Alphakanal der Endpunkt als RGBA 6.6.6.6 interpretiert, wenn die Darstellung für das Format "RGBAP 5.5.5.5.1" lautet. Je nach Blockmodus können Sie das freigegebene, am wenigsten signifikante Bit für beide Endpunkte einer Teilmenge einzeln (2 P-Bits 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 verfügen die Endpunkte über eine reine RGB-Darstellung, und die Alphakomponente wird für alle Texel in den Quelldaten als 1.0 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 neben den Farbkomponentenwerten interpoliert.

Für BC7-Blöcke mit separaten Farb- und Alphakomponenten besteht ein Block aus Modusbits, Rotationsbits, komprimierten Endpunkten, komprimierten Indizes und einem optionalen Indexauswahlbit. Diese Blöcke verfügen über einen effektiven RGB-Vektor [R, G, B] und einen skalaren Alphakanal [A], der separat codiert ist.

In der folgenden Tabelle sind die Komponenten der einzelnen Blocktypen aufgeführt.

BC7-Block enthält... Modusbits Drehbits Indexauswahlbit Partitionsbits komprimierte Endpunkte P-Bit komprimierte Indizes
Nur Farbkomponenten Erforderlich Erforderlich Erforderlich optional Erforderlich
Farbe + Alpha kombiniert Erforderlich optional Erforderlich optional Erforderlich
Farbe und Alpha getrennt Erforderlich Erforderlich optional Erforderlich Erforderlich

 

BC7 definiert eine Farbpalette auf 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, korrigiert der Encoder den Zustand eines Bits der komprimierten Indexdaten für diese Teilmenge. Dazu wählt er eine Endpunktreihenfolge aus, die es dem Index für den angegebenen "Fix-Up"-Index ermöglicht, sein wichtigstes Bit auf 0 festzulegen, und das dann verworfen werden kann, wodurch ein Bit pro Teilmenge gespeichert wird. Bei Blockmodi mit nur einer einzelnen Teilmenge ist der Fix-Up-Index immer Index 0.

Decodieren des BC7-Formats

Der folgende Pseudocode beschreibt die Schritte zum Dekomprimieren des Pixels bei (x,y) unter Berücksichtigung 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 von Endpunktfarben- und Alphakomponenten für jede Teilmenge in einem 16-Byte-BC7-Block.

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: Lassen Sie "c" die zu generierende Komponente sein; "e0" die Komponente des Endpunkts 0 der Teilmenge sein; und lassen Sie "e1" die 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: einen für den Vektorkanal und einen für den skalaren Kanal. Für Modus 4 sind diese Indizes unterschiedlich breit (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, jedoch mit umgekehrtem Verhalten 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;
}

Texturblockkomprimierung in Direct3D 11