Freigeben über


BC6H-Format

Das BC6H-Format ist ein Texturkomprimierungsformat, das zur Unterstützung von HDR-Farbräumen (High Dynamic Range) in Quelldaten entwickelt wurde.

Informationen zu BC6H/DXGI_FORMAT_BC6H

Das BC6H-Format bietet eine qualitativ hochwertige Komprimierung für Bilder, die drei HDR-Farbkanäle verwenden, mit einem 16-Bit-Wert für jeden Farbkanal des Werts (16:16:16). Es gibt keine Unterstützung für einen Alphakanal.

BC6H wird durch die folgenden DXGI_FORMAT Enumerationswerte angegeben:

  • DXGI_FORMAT_BC6H_TYPELESS.
  • DXGI_FORMAT_BC6H_UF16. Dieses BC6H-Format verwendet kein Zeichenbit in den 16-Bit-Gleitkommafarbkanalwerten.
  • DXGI_FORMAT_BC6H_SF16. Dieses BC6H-Format verwendet ein Zeichenbit in den 16-Bit-Gleitkommafarbkanalwerten.

Anmerkung

Das 16-Bit-Gleitkommaformat für Farbkanäle wird häufig als "halb" Gleitkommaformat bezeichnet. Dieses Format weist das folgende Bitlayout auf:

Format Bitlayout
UF16 (unsigned float) 5 Exponente Bits + 11 Mantissa bits
SF16 (signierter Float-Float) 1 Zeichen bit + 5 exponent bits + 10 mantissa bits

 

 

Das BC6H-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.

BC6H 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, MIP-Karten, Cubemaps und Texturarrays. Alle Bildkacheln müssen dasselbe Format aufweisen.

Einige wichtige Hinweise zum BC6H-Format:

  • BC6H unterstützt die Gleitkomma-Denormalisierung, unterstützt jedoch NICHT INF (Unendlichkeit) und NaN (keine Zahl). Die Ausnahme ist der signierte Modus von BC6H (DXGI_FORMAT_BC6H_SF16), der -INF (negative Unendlichkeit) unterstützt. Beachten Sie, dass diese Unterstützung für -INF lediglich ein Artefakt des Formats selbst ist und nicht speziell von Encodern für dieses Format unterstützt wird. Wenn Encoder auf INF-Eingabedaten (positiv oder negativ) oder NaN-Eingabedaten stoßen, sollten sie diese Daten \ in den maximal zulässigen Nicht-INF-Darstellungswert konvertieren und NaN vor der Komprimierung 0 zuordnen.
  • BC6H unterstützt keinen Alphakanal.
  • Der BC6H-Decoder führt eine Dekomprimierung durch, bevor die Texturfilterung ausgeführt wird.
  • BC6H-Dekomprimierung muss bitgenau sein; d. h. die Hardware muss Ergebnisse zurückgeben, die mit dem in dieser Dokumentation beschriebenen Decoder identisch sind.

BC6H-Implementierung

Ein BC6H-Block besteht aus Modusbits, komprimierten Endpunkten, komprimierten Indizes und einem optionalen Partitionsindex. Dieses Format gibt 14 verschiedene Modi an.

Eine Endpunktfarbe wird als RGB-Triplet gespeichert. BC6H definiert eine Palette von Farben in einer ungefähren Linie über eine Reihe definierter Farbendpunkte. Je nach Modus kann eine Kachel auch in zwei Regionen unterteilt oder als einzelne Region behandelt werden, wobei eine Kachel mit zwei Bereichen über einen separaten Satz von Farbendpunkten für jede Region verfügt. BC6H speichert einen Palettenindex pro Texel.

Im Fall von zwei Regionen gibt es 32 mögliche Partitionen.

Decodieren des BC6H-Formats

Der folgende Pseudocode zeigt die Schritte zum Dekomprimieren des Pixels bei (x,y) mit dem 16 Byte BC6H-Block.

decompress_bc6h(x, y, block)
{
    mode = extract_mode(block);
    endpoints;
    index;
    
    if(mode.type == ONE)
    {
        endpoints = extract_compressed_endpoints(mode, block);
        index = extract_index_ONE(x, y, block);
    }
    else //mode.type == TWO
    {
        partition = extract_partition(block);
        region = get_region(partition, x, y);
        endpoints = extract_compressed_endpoints(mode, region, block);
        index = extract_index_TWO(x, y, partition, block);
    }
    
    unquantize(endpoints);
    color = interpolate(index, endpoints);
    finish_unquantize(color);
}

Die folgende Tabelle enthält die Bitanzahl und werte für jedes der 14 möglichen Formate für BC6H-Blöcke.

Modus Partitionsindizes Trennwand Farbendpunkte Modusbits
1 46 Bits 5 Bits 75 Bits (10.555, 10.555, 10.555) 2 Bits (00)
2 46 Bits 5 Bits 75 Bits (7666, 7666, 7666) 2 Bits (01)
3 46 Bits 5 Bits 72 Bits (11.555, 11.444, 11.444) 5 Bits (00010)
4 46 Bits 5 Bits 72 Bits (11.444, 11.555, 11.444) 5 Bits (00110)
5 46 Bits 5 Bits 72 Bits (11.444, 11.444, 11.555) 5 Bits (01010)
6 46 Bits 5 Bits 72 Bits (9555, 9555, 9555) 5 Bits (01110)
7 46 Bits 5 Bits 72 Bits (8666, 8555, 8555) 5 Bits (10010)
8 46 Bits 5 Bits 72 Bits (8555, 8666, 8555) 5 Bits (10110)
9 46 Bits 5 Bits 72 Bits (8555, 8555, 8666) 5 Bits (11010)
10 46 Bits 5 Bits 72 Bits (6666, 6666, 6666) 5 Bits (11110)
11 63 Bits 0 Bits 60 Bits (10.10, 10.10, 10.10) 5 Bits (00011)
12 63 Bits 0 Bits 60 Bits (11.9, 11.9, 11.9) 5 Bits (00111)
13 63 Bits 0 Bits 60 Bits (12.8, 12.8, 12.8) 5 Bits (01011)
14 63 Bits 0 Bits 60 Bits (16.4, 16.4, 16.4) 5 Bits (01111)

 

Jedes Format in dieser Tabelle kann durch die Modusbits eindeutig identifiziert werden. Die ersten zehn Modi werden für Kacheln mit zwei Regionen verwendet, und das Bitfeld des Modus kann entweder zwei oder fünf Bit lang sein. Diese Blöcke verfügen auch über Felder für die komprimierten Farbendpunkte (72 oder 75 Bit), die Partition (5 Bits) und die Partitionsindizes (46 Bit).

Für die komprimierten Farbendpunkte beachten die Werte in der vorherigen Tabelle die Genauigkeit der gespeicherten RGB-Endpunkte und die Anzahl der Bits, die für jeden Farbwert verwendet werden. Beispielsweise gibt Modus 3 eine Farbendpunktgenauigkeitsebene von 11 und die Anzahl der Bits an, die zum Speichern der Deltawerte der transformierten Endpunkte für die Farben Rot, Blau und Grün (5, 4 und 4) verwendet werden. Modus 10 verwendet keine Deltakomprimierung und speichert stattdessen alle vier Farbendpunkte explizit.

Die letzten vier Blockmodi werden für Kacheln mit einem Bereich verwendet, wobei das Modusfeld 5 Bit beträgt. Diese Blöcke verfügen über Felder für die Endpunkte (60 Bit) und die komprimierten Indizes (63 Bit). Modus 11 (wie Modus 10) verwendet keine Deltakomprimierung und speichert stattdessen beide Farbendpunkte explizit.

Modi 10011, 10111, 11011 und 1111 (nicht angezeigt) sind reserviert. Verwenden Sie diese nicht in Ihrem Encoder. Wenn die Hardware Blöcke mit einem dieser Modi übergeben wird, muss der resultierende dekomprimierte Block alle Nullen in allen Kanälen mit Ausnahme des Alphakanals enthalten.

Für BC6H muss der Alphakanal unabhängig vom Modus immer 1,0 zurückgeben.

BC6H-Partitionssatz

Es gibt 32 mögliche Partitionssätze für eine Kachel mit zwei Regionen, die in der folgenden Tabelle definiert sind. Jeder 4x4-Block stellt eine einzelne Form dar.

Tabelle der bc6h-Partitionssätze

In dieser Tabelle mit Partitionssätzen ist der fett formatierte und unterstrichene Eintrag der Speicherort des Fixupindex für Teilmenge 1 (der mit einem weniger Bit angegeben wird). Der Fixupindex für Teilmenge 0 ist immer Index 0, da die Partitionierung immer so angeordnet ist, dass Index 0 immer in Teilmenge 0 ist. Die Partitionsreihenfolge geht von oben links nach unten rechts, von links nach rechts und dann von oben nach unten.

BC6H-komprimiertes Endpunktformat

Bitfelder für komprimierte Bc6h-Endpunktformate

Diese Tabelle enthält die Bitfelder für die komprimierten Endpunkte als Funktion des Endpunktformats, wobei jede Spalte eine Codierung und jede Zeile angibt, die ein Bitfeld angibt. Bei diesem Ansatz werden 82 Bit für Kacheln mit zwei Regionen und 65 Bit für Kacheln in einem Bereich verwendet. Als Beispiel sind die ersten 5 Bits für die Codierung aus einer Region [16 4] oben (insbesondere die spalte rechts) Bits m[4:0], die nächsten 10 Bits rw[9:0] usw. mit den letzten 6 Bits, die bw[10:15] enthalten.

Die Feldnamen in der obigen Tabelle sind wie folgt definiert:

Feld Variable
m Modus
d Shape-Index
Rw endpt[0]. A[0]
Rx endpt[0]. B[0]
Ry endpt[1]. A[0]
rz endpt[1]. B[0]
Gw endpt[0]. A[1]
gx endpt[0]. B[1]
Gy endpt[1]. A[1]
gz endpt[1]. B[1]
Bw endpt[0]. A[2]
Bx endpt[0]. B[2]
bis endpt[1]. A[2]
Bz endpt[1]. B[2]

 

Endpt[i], wobei i entweder 0 oder 1 ist, bezieht sich auf den 0. oder 1. Satz von Endpunkten.

Sign-Erweiterung für Endpunktwerte

Für Kacheln mit zwei Regionen gibt es vier Endpunktwerte, die erweitert werden können. Endpt[0]. A ist nur signiert, wenn das Format ein signiertes Format ist; die anderen Endpunkte werden nur signiert, wenn der Endpunkt transformiert wurde oder das Format ein signiertes Format ist. Der folgende Code veranschaulicht den Algorithmus zum Erweitern des Zeichens von Endpunktwerten in zwei Regionen.

static void sign_extend_two_region(Pattern &p, IntEndpts endpts[NREGIONS_TWO])
{
    for (int i=0; i<NCHANNELS; ++i)
    {
      if (BC6H::FORMAT == SIGNED_F16)
        endpts[0].A[i] = SIGN_EXTEND(endpts[0].A[i], p.chan[i].prec);
      if (p.transformed || BC6H::FORMAT == SIGNED_F16)
      {
        endpts[0].B[i] = SIGN_EXTEND(endpts[0].B[i], p.chan[i].delta[0]);
        endpts[1].A[i] = SIGN_EXTEND(endpts[1].A[i], p.chan[i].delta[1]);
        endpts[1].B[i] = SIGN_EXTEND(endpts[1].B[i], p.chan[i].delta[2]);
      }
    }
}

Bei Kacheln mit einem Bereich ist das Verhalten identisch, nur bei entfernten Endpt[1].

static void sign_extend_one_region(Pattern &p, IntEndpts endpts[NREGIONS_ONE])
{
    for (int i=0; i<NCHANNELS; ++i)
    {
    if (BC6H::FORMAT == SIGNED_F16)
        endpts[0].A[i] = SIGN_EXTEND(endpts[0].A[i], p.chan[i].prec);
    if (p.transformed || BC6H::FORMAT == SIGNED_F16) 
        endpts[0].B[i] = SIGN_EXTEND(endpts[0].B[i], p.chan[i].delta[0]);
    }
}

Transformieren von Inversion für Endpunktwerte

Bei Kacheln mit zwei Regionen wendet die Transformation die Umkehrung der Differenzcodierung an, wobei der Basiswert am Endpt[0] hinzugefügt wird. A bis zu den drei anderen Einträgen für insgesamt 9 Add-Vorgänge. In der abbildung unten wird der Basiswert als "A0" dargestellt und hat die höchste Gleitkommagenauigkeit. "A1", "B0" und "B1" sind alle Deltas, die aus dem Ankerwert berechnet werden, und diese Deltawerte werden mit niedrigerer Genauigkeit dargestellt. (A0 entspricht Endpt[0]. A, B0 entspricht Endpt[0]. B, A1 entspricht Endpt[1]. A und B1 entspricht Endpt[1].B.)

Berechnung von Transformationsendpunktwerten

Für Kacheln mit einer Region gibt es nur einen Delta-Offset und daher nur 3 Add-Vorgänge.

Die Dekomprimierung muss sicherstellen, dass die Ergebnisse der Umkehrtransformation nicht die Genauigkeit von endpt[0].a überlaufen. Bei einem Überlauf müssen die Werte, die sich aus der Umgekehrten Transformation ergeben, innerhalb derselben Anzahl von Bits umbrochen werden. Wenn die Genauigkeit von A0 "p" Bits ist, lautet der Transformationsalgorithmus:

B0 = (B0 + A0) & ((1 << p) - 1)

Bei signierten Formaten müssen auch die Ergebnisse der Delta-Berechnung erweitert werden. Wenn der Sign-Erweiterungsvorgang die Erweiterung beider Zeichen berücksichtigt, wobei 0 positiv und 1 negativ ist, kümmert sich die Sign-Erweiterung von 0 um die Klammer oben. Entsprechend muss nach der obigen Klammer nur ein Wert von 1 (negativ) zeichengeschützt werden.

Entquantisierung von Farbendpunkten

Angesichts der nicht komprimierten Endpunkte besteht der nächste Schritt darin, eine anfängliche Entquantisierung der Farbendpunkte durchzuführen. Dies umfasst drei Schritte:

  • Eine Entquantisierung der Farbpaletten
  • Interpolation der Paletten
  • Abschluss der Entquantisierung

Durch das Trennen des Unquantisierungsprozesses in zwei Teile (Farbpaletten-Entquantisierung vor der Interpolation und der endgültigen Entquantisierung nach der Interpolation) wird die Anzahl der multiplikationsvorgänge reduziert, die im Vergleich zu einem vollständigen Unquantisierungsprozess vor der Paletteninterpolation erforderlich sind.

Der folgende Code veranschaulicht den Prozess zum Abrufen von Schätzungen der ursprünglichen 16-Bit-Farbwerte und anschließend mithilfe der bereitgestellten Gewichtungswerte, um der Palette 6 zusätzliche Farbwerte hinzuzufügen. Derselbe Vorgang wird für jeden Kanal ausgeführt.

int aWeight3[] = {0, 9, 18, 27, 37, 46, 55, 64};
int aWeight4[] = {0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64};

// c1, c2: endpoints of a component
void generate_palette_unquantized(UINT8 uNumIndices, int c1, int c2, int prec, UINT16 palette[NINDICES])
{
    int* aWeights;
    if(uNumIndices == 8)
        aWeights = aWeight3;
    else  // uNumIndices == 16
        aWeights = aWeight4;

    int a = unquantize(c1, prec); 
    int b = unquantize(c2, prec);

    // interpolate
    for(int i = 0; i < uNumIndices; ++i)
        palette[i] = finish_unquantize((a * (64 - aWeights[i]) + b * aWeights[i] + 32) >> 6);
}

Im nächsten Codebeispiel wird der Interpolationsprozess mit den folgenden Beobachtungen veranschaulicht:

  • Da der vollständige Farbbereich für die nichtquantisieren Funktion (unten) von -32768 bis 65535 liegt, wird der Interpolator mit einer 17-Bit-signierten Arithmetik implementiert.
  • Nach der Interpolation werden die Werte an die finish_unquantize-Funktion übergeben (im dritten Beispiel in diesem Abschnitt beschrieben), wodurch die endgültige Skalierung angewendet wird.
  • Alle Hardwaredekompressoren sind erforderlich, um bitgenaue Ergebnisse mit diesen Funktionen zurückzugeben.
int unquantize(int comp, int uBitsPerComp)
{
    int unq, s = 0;
    switch(BC6H::FORMAT)
    {
    case UNSIGNED_F16:
        if(uBitsPerComp >= 15)
            unq = comp;
        else if(comp == 0)
            unq = 0;
        else if(comp == ((1 << uBitsPerComp) - 1))
            unq = 0xFFFF;
        else
            unq = ((comp << 16) + 0x8000) >> uBitsPerComp;
        break;

    case SIGNED_F16:
        if(uBitsPerComp >= 16)
            unq = comp;
        else
        {
            if(comp < 0)
            {
                s = 1;
                comp = -comp;
            }

            if(comp == 0)
                unq = 0;
            else if(comp >= ((1 << (uBitsPerComp - 1)) - 1))
                unq = 0x7FFF;
            else
                unq = ((comp << 15) + 0x4000) >> (uBitsPerComp-1);

            if(s)
                unq = -unq;
        }
        break;
    }
    return unq;
}

finish_unquantize wird nach der Paletteninterpolation aufgerufen. Die Funktion entquantisieren, verschiebt die Skalierung um 31/32 für signiert, 31/64 für nicht signiert. Dieses Verhalten ist erforderlich, um den endgültigen Wert in gültigen halben Bereich(-0x7BFF ~ 0x7BFF) nach Abschluss der Paletteninterpolation abzurufen, um die Anzahl der erforderlichen Multiplikationen zu verringern. finish_unquantize wendet die endgültige Skalierung an und gibt einen unsignierten kurzen Wert zurück, der in Hälfteneu interpretiert wird.

unsigned short finish_unquantize(int comp)
{
    if(BC6H::FORMAT == UNSIGNED_F16)
    {
        comp = (comp * 31) >> 6;                                         // scale the magnitude by 31/64
        return (unsigned short) comp;
    }
    else // (BC6H::FORMAT == SIGNED_F16)
    {
        comp = (comp < 0) ? -(((-comp) * 31) >> 5) : (comp * 31) >> 5;   // scale the magnitude by 31/32
        int s = 0;
        if(comp < 0)
        {
            s = 0x8000;
            comp = -comp;
        }
        return (unsigned short) (s | comp);
    }
}

Texturblockkomprimierung in Direct3D 11-