Format BC6H
Le format BC6H est un format de compression de texture conçu pour prendre en charge les espaces de couleurs de plage haute dynamique (HDR) dans les données sources.
- À propos de BC6H/DXGI_FORMAT_BC6H
- Implémentation BC6H
- Décodage du format BC6H
- Format de point de terminaison compressé BC6H
- Signer l’extension pour les valeurs de point de terminaison
- Transformer l’inversion pour les valeurs de point de terminaison
- Non-quantisation des points de terminaison de couleur
- Rubriques connexes
À propos de BC6H/DXGI_FORMAT_BC6H
Le format BC6H fournit une compression de haute qualité pour les images qui utilisent trois canaux de couleur HDR, avec une valeur 16 bits pour chaque canal de couleur de la valeur (16 :16 :16). Il n’existe aucune prise en charge d’un canal alpha.
BC6H est spécifié par les valeurs d’énumération DXGI_FORMAT suivantes :
- DXGI_FORMAT_BC6H_TYPELESS.
- DXGI_FORMAT_BC6H_UF16. Ce format BC6H n’utilise pas de bit de signe dans les valeurs des canaux de couleur à virgule flottante 16 bits.
- DXGI_FORMAT_BC6H_SF16. Ce format BC6H utilise un bit de signe dans les valeurs de canal de couleur à virgule flottante 16 bits.
Remarque
Le format à virgule flottante 16 bits pour les canaux de couleur est souvent appelé format à virgule flottante « demi ». Ce format a la disposition de bits suivante :
Format | Disposition du bit |
---|---|
UF16 (float non signé) | 5 bits exposants + 11 bits de mantissa |
SF16 (flottant signé) | 1 bits de signe + 5 bits exposants + 10 bits mantissa |
Le format BC6H peut être utilisé pour les ressources de texture Texture2D (y compris les tableaux), Texture3D ou TextureCube (y compris les tableaux). De même, ce format s’applique aux surfaces MIP-map associées à ces ressources.
BC6H utilise une taille de bloc fixe de 16 octets (128 bits) et une taille de vignette fixe de 4 x 4 texels. Comme pour les formats BC précédents, les images de texture supérieures à la taille de vignette prise en charge (4x4) sont compressées à l’aide de plusieurs blocs. Cette identité d’adressage s’applique également aux images tridimensionnelles, aux mappages MIP, aux cubemaps et aux tableaux de texture. Toutes les vignettes d’image doivent être du même format.
Quelques remarques importantes sur le format BC6H :
- BC6H prend en charge la dénormalisation à virgule flottante, mais ne prend pas en charge INF (infini) et NaN (pas un nombre). L’exception est le mode signé de BC6H (DXGI_FORMAT_BC6H_SF16), qui prend en charge -INF (infini négatif). Notez que cette prise en charge de -INF est simplement un artefact du format lui-même et n’est pas spécifiquement prise en charge par les encodeurs pour ce format. En règle générale, lorsque les encodeurs rencontrent des données d’entrée INF (positives ou négatives) ou NaN, ils doivent \ convertir ces données en valeur de représentation non INF autorisée maximale et mapper NaN à 0 avant la compression.
- BC6H ne prend pas en charge un canal alpha.
- Le décodeur BC6H effectue la décompression avant d’effectuer le filtrage de texture.
- La décompression BC6H doit être un peu précise ; autrement dit, le matériel doit retourner des résultats identiques au décodeur décrit dans cette documentation.
Implémentation BC6H
Un bloc BC6H se compose de bits de mode, de points de terminaison compressés, d’index compressés et d’un index de partition facultatif. Ce format spécifie 14 modes différents.
Une couleur de point de terminaison est stockée sous forme de triplet RVB. BC6H définit une palette de couleurs sur une ligne approximative sur un certain nombre de points de terminaison de couleur définis. En outre, selon le mode, une vignette peut être divisée en deux régions ou traitée comme une seule région, où une vignette à deux régions a un ensemble distinct de points de terminaison de couleur pour chaque région. BC6H stocke un index de palette par texel.
Dans le cas de deux régions, il existe 32 partitions possibles.
Décodage du format BC6H
Le pseudocode ci-dessous montre les étapes permettant de décompresser le pixel à (x,y) en fonction du bloc BC6H de 16 octets.
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);
}
Le tableau suivant contient le nombre de bits et les valeurs pour chacun des 14 formats possibles pour les blocs BC6H.
Mode | Index de partition | Partition | Points de terminaison de couleur | Bits de mode |
---|---|---|---|---|
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) |
Chaque format de ce tableau peut être identifié de manière unique par les bits de mode. Les dix premiers modes sont utilisés pour les vignettes à deux régions, et le champ bit du mode peut être long de deux ou cinq bits. Ces blocs ont également des champs pour les points de terminaison de couleur compressés (72 ou 75 bits), la partition (5 bits) et les index de partition (46 bits).
Pour les points de terminaison de couleur compressés, les valeurs du tableau précédent notent la précision des points de terminaison RVB stockés et le nombre de bits utilisés pour chaque valeur de couleur. Par exemple, le mode 3 spécifie un niveau de précision de point de terminaison de couleur de 11 et le nombre de bits utilisés pour stocker les valeurs delta des points de terminaison transformés pour les couleurs rouges, bleues et vertes (5, 4 et 4 respectivement). Le mode 10 n’utilise pas la compression delta et stocke les quatre points de terminaison de couleur explicitement.
Les quatre derniers modes de bloc sont utilisés pour les vignettes d’une région, où le champ de mode est de 5 bits. Ces blocs ont des champs pour les points de terminaison (60 bits) et les index compressés (63 bits). Le mode 11 (comme le mode 10) n’utilise pas la compression delta et stocke les deux points de terminaison de couleur explicitement.
Les modes 10011, 10111, 11011 et 11111 (non affichés) sont réservés. N’utilisez pas ces éléments dans votre encodeur. Si le matériel est passé à des blocs avec l’un de ces modes spécifiés, le bloc compressé résultant doit contenir tous les zéros dans tous les canaux, à l’exception du canal alpha.
Pour BC6H, le canal alpha doit toujours retourner 1,0, quel que soit le mode.
Jeu de partitions BC6H
Il existe 32 ensembles de partitions possibles pour une vignette à deux régions et qui sont définis dans le tableau ci-dessous. Chaque bloc 4x4 représente une forme unique.
Dans cette table des jeux de partitions, l’entrée en gras et souligné est l’emplacement de l’index de correction pour le sous-ensemble 1 (qui est spécifié avec un bit inférieur). L’index de correction pour le sous-ensemble 0 est toujours index 0, car le partitionnement est toujours organisé de sorte que l’index 0 soit toujours dans le sous-ensemble 0. L’ordre de partition passe du haut à gauche au bas à droite, en se déplaçant de gauche à droite, puis de haut en bas.
Format de point de terminaison compressé BC6H
Ce tableau présente les champs de bits des points de terminaison compressés comme fonction du format de point de terminaison, avec chaque colonne spécifiant un encodage et chaque ligne spécifiant un champ de bits. Cette approche prend jusqu’à 82 bits pour les vignettes à deux régions et 65 bits pour les vignettes d’une région. Par exemple, les 5 premiers bits de l’encodage one-region [16 4] ci-dessus (plus précisément la colonne la plus à droite) sont des bits m[4 :0], les 10 bits suivants sont des bits rw[9 :0], et ainsi de suite avec les 6 derniers bits contenant bw[10 :15].
Les noms de champs du tableau ci-dessus sont définis comme suit :
Champ | Variable |
---|---|
m | mode |
d | index de forme |
rw | endpt[0].A[0] |
rx | endpt[0].B[0] |
rie | 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] |
by | endpt[1].A[2] |
bz | endpt[1].B[2] |
Endpt[i], où i est soit 0 ou 1, fait référence au 0e ou au 1er ensemble de points de terminaison respectivement.
Signer l’extension pour les valeurs de point de terminaison
Pour les vignettes à deux régions, il existe quatre valeurs de point de terminaison qui peuvent être étendues. Endpt[0].A est signé uniquement si le format est un format signé ; les autres points de terminaison sont signés uniquement si le point de terminaison a été transformé ou si le format est un format signé. Le code ci-dessous illustre l’algorithme permettant d’étendre le signe des valeurs de point de terminaison à deux régions.
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]);
}
}
}
Pour les vignettes d’une région, le comportement est le même, uniquement avec endpt[1] supprimé.
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]);
}
}
Transformer l’inversion pour les valeurs de point de terminaison
Pour les vignettes à deux régions, la transformation applique l’inverse de l’encodage de différence, en ajoutant la valeur de base à endpt[0]. À trois autres entrées pour un total de 9 opérations d’ajout. Dans l’image ci-dessous, la valeur de base est représentée sous la forme « A0 » et a la plus haute précision à virgule flottante. « A1 », « B0 » et « B1 » sont tous des deltas calculés à partir de la valeur d’ancre, et ces valeurs delta sont représentées avec une précision inférieure. (A0 correspond à endpt[0].A, B0 correspond à endpt[0].B, A1 correspond à endpt[1].A et B1 correspond à endpt[1].B).
Pour les vignettes d’une région, il n’existe qu’un décalage delta, et par conséquent, 3 opérations d’ajout.
Le décompresseur doit s’assurer que les résultats de la transformation inverse ne dépasseront pas la précision de endpt[0].a. Dans le cas d’un dépassement de capacité, les valeurs résultant de la transformation inverse doivent être encapsulées dans le même nombre de bits. Si la précision d’A0 est « p », l’algorithme de transformation est :
B0 = (B0 + A0) & ((1 << p) - 1)
Pour les formats signés, les résultats du calcul delta doivent également être étendus. Si l’opération d’extension de signe considère l’extension des deux signes, où 0 est positif et 1 est négatif, l’extension de signe 0 prend soin de la pince ci-dessus. De façon équivalente, après la pince ci-dessus, seule une valeur de 1 (négative) doit être étendue.
Non-quantisation des points de terminaison de couleur
Étant donné les points de terminaison non compressés, l’étape suivante consiste à effectuer une non-quantisation initiale des points de terminaison de couleur. Cela implique trois étapes :
- Une non-quantisation des palettes de couleurs
- Interpolation des palettes
- Finalisation de la non-quantisation
Le fait de séparer le processus de non-quantisation en deux parties (non-quantisation de la palette de couleurs avant l’interpolation et l’interpolation finale après l’interpolation) réduit le nombre d’opérations de multiplication requises par rapport à un processus de non-quantisation complet avant l’interpolation de palette.
Le code ci-dessous illustre le processus de récupération des estimations des valeurs de couleur 16 bits d’origine, puis l’utilisation des valeurs de pondération fournies pour ajouter 6 valeurs de couleur supplémentaires à la palette. La même opération est effectuée sur chaque canal.
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);
}
L’exemple de code suivant illustre le processus d’interpolation, avec les observations suivantes :
- Étant donné que la plage complète de valeurs de couleur pour la fonction unquantize (ci-dessous) est comprise entre -32768 et 65535, l’interpolateur est implémenté à l’aide d’une arithmétique signée 17 bits.
- Après l’interpolation, les valeurs sont passées à la fonction finish_unquantize (décrite dans le troisième exemple de cette section), qui applique la mise à l’échelle finale.
- Tous les décompresseurs matériels sont nécessaires pour retourner des résultats bit précis avec ces fonctions.
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 est appelée après l’interpolation de palette. La fonction unquantize reporte la mise à l’échelle de 31/32 pour signé, 31/64 pour non signé. Ce comportement est nécessaire pour obtenir la valeur finale en demi-plage valide (-0x7BFF ~ 0x7BFF) une fois l’interpolation de palette terminée afin de réduire le nombre de multiplications nécessaires. finish_unquantize applique la mise à l’échelle finale et retourne une valeur courte non signée qui est réinterprétée en demie.
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);
}
}
Rubriques connexes