Compresión de bloques
La compresión de bloques es una técnica de compresión de textura perdida para reducir el tamaño de textura y la superficie de memoria, lo que proporciona un aumento del rendimiento. Una textura comprimida por bloques puede ser menor que una textura con 32 bits por color.
La compresión de bloques es una técnica de compresión de textura para reducir el tamaño de la textura. En comparación con una textura con 32 bits por color, una textura comprimida por bloque puede ser hasta un 75 % más pequeña. Las aplicaciones suelen ver un aumento del rendimiento al usar la compresión de bloques debido a la superficie de memoria más pequeña.
Aunque la pérdida, la compresión de bloques funciona bien y se recomienda para todas las texturas que se transforman y filtran por la canalización. Las texturas que se asignan directamente a la pantalla (elementos de la interfaz de usuario como iconos y texto) no son buenas opciones para la compresión, ya que los artefactos son más evidentes.
Una textura comprimida por bloques debe crearse como un múltiplo de tamaño 4 en todas las dimensiones y no se puede usar como salida de la canalización.
Funcionamiento de la compresión de bloques
La compresión de bloques es una técnica para reducir la cantidad de memoria necesaria para almacenar datos de color. Al almacenar algunos colores en su tamaño original y otros colores mediante un esquema de codificación, puede reducir drásticamente la cantidad de memoria necesaria para almacenar la imagen. Dado que el hardware descodifica automáticamente los datos comprimidos, no hay ninguna penalización de rendimiento para usar texturas comprimidas.
Para ver cómo funciona la compresión, examine los dos ejemplos siguientes. En el primer ejemplo se describe la cantidad de memoria utilizada al almacenar datos sin comprimir; en el segundo ejemplo se describe la cantidad de memoria usada al almacenar datos comprimidos.
Almacenamiento de datos sin comprimir
La siguiente ilustración representa una textura 4×4 sin comprimir. Supongamos que cada color contiene un único componente de color (rojo por ejemplo) y se almacena en un byte de memoria.
Los datos sin comprimir se distribuyen en memoria secuencialmente y requieren 16 bytes, como se muestra en la ilustración siguiente.
Almacenamiento de datos comprimidos
Ahora que ha visto cuánta memoria usa una imagen sin comprimir, eche un vistazo a la cantidad de memoria que guarda una imagen comprimida. El formato de compresión BC4 almacena 2 colores (1 byte cada uno) y 16 índices de 3 bits (48 bits o 6 bytes) que se usan para interpolar los colores originales en la textura, como se muestra en la ilustración siguiente.
El espacio total necesario para almacenar los datos comprimidos es de 8 bytes, lo que supone un ahorro de memoria del 50 por ciento en el ejemplo sin comprimir. Los ahorros son incluso mayores cuando se usa más de un componente de color.
El ahorro considerable de memoria proporcionado por la compresión de bloques puede provocar un aumento del rendimiento. Este rendimiento conlleva el costo de la calidad de la imagen (debido a la interpolación de color); sin embargo, la menor calidad a menudo no es perceptible.
En la sección siguiente se muestra cómo Direct3D habilita el uso de la compresión de bloques en una aplicación.
Uso de la compresión de bloques
Cree una textura comprimida en bloques como una textura sin comprimir, salvo que especifique un formato comprimido en bloques.
A continuación, cree una vista para enlazar la textura a la canalización, ya que una textura comprimida por bloques solo se puede usar como entrada para una fase del sombreador, quiere crear una vista de recursos del sombreador.
Use una textura comprimida de bloque de la misma manera que usaría una textura sin comprimir. Si la aplicación obtendrá un puntero de memoria para bloquear los datos comprimidos, debe tener en cuenta el relleno de memoria en un mapa mip que hace que el tamaño declarado sea diferente del tamaño real.
Tamaño virtual frente al tamaño físico
Si tiene código de aplicación que usa un puntero de memoria para recorrer la memoria de una textura comprimida en bloques, hay una consideración importante que puede requerir una modificación en el código de la aplicación. Una textura comprimida por bloques debe ser un múltiplo de 4 en todas las dimensiones, ya que los algoritmos de compresión de bloques funcionan en bloques de textura 4x4. Este será un problema para un mapa mip cuyas dimensiones iniciales son divisibles en 4, pero los niveles subdivididos no. En el diagrama siguiente se muestra la diferencia en el área entre el tamaño virtual (declarado) y el tamaño físico (real) de cada nivel de mapa mip.
En el lado izquierdo del diagrama se muestran los tamaños de nivel de mapa mip que se generan para una textura 60×40 sin comprimir. El tamaño de nivel superior se toma de la llamada API que genera la textura; cada nivel posterior es la mitad del tamaño del nivel anterior. Para una textura sin comprimir, no hay ninguna diferencia entre el tamaño virtual (declarado) y el tamaño físico (real).
En el lado derecho del diagrama se muestran los tamaños de nivel de mapa mip que se generan para la misma textura de 60×40 con compresión. Tenga en cuenta que tanto el segundo como el tercer nivel tienen relleno de memoria para hacer que los factores de tamaño de 4 en cada nivel. Esto es necesario para que los algoritmos puedan funcionar en bloques de textura 4×4. Esto es especialmente evidente si considera niveles mipmap menores que 4×4; el tamaño de estos niveles mipmap muy pequeños se redondeará hasta el factor más cercano de 4 cuando se asigne memoria de textura.
El hardware de muestreo usa el tamaño virtual; cuando se muestrea la textura, se omite el relleno de memoria. En el caso de los niveles mipmap menores que 4×4, solo se usarán los cuatro primeros elementos de textura para un mapa de 2×2 y solo un bloque 1×1 usará el primer elemento de textura. Sin embargo, no hay ninguna estructura de API que exponga el tamaño físico (incluido el relleno de memoria).
En resumen, tenga cuidado de usar bloques de memoria alineados al copiar regiones que contienen datos comprimidos por bloques. Para hacerlo en una aplicación que obtiene un puntero de memoria, asegúrese de que el puntero usa el tono de superficie para tener en cuenta el tamaño de memoria física.
Algoritmos de compresión
Las técnicas de compresión de bloques en Direct3D dividen los datos de textura sin comprimir en bloques 4×4, comprimen cada bloque y, a continuación, almacenan los datos. Por este motivo, se espera que las texturas se comprima deben tener dimensiones de textura que sean múltiplos de 4.
En el diagrama anterior se muestra una textura particionada en bloques de elementos de textura. El primer bloque muestra el diseño de los 16 elementos de textura etiquetados a-p, pero cada bloque tiene la misma organización de datos.
Direct3D implementa varios esquemas de compresión, cada uno implementa un equilibrio diferente entre el número de componentes almacenados, el número de bits por componente y la cantidad de memoria consumida. Use esta tabla para ayudar a elegir el formato que mejor funcione con el tipo de datos y la resolución de datos que mejor se adapte a la aplicación.
Datos de origen | Resolución de compresión de datos (en bits) | Elija este formato de compresión. |
---|---|---|
Color y alfa de tres componentes | Color (5:6:5), Alfa (1) o ningún alfa | BC1 |
Color y alfa de tres componentes | Color (5:6:5), Alfa (4) | BC2 |
Color y alfa de tres componentes | Color (5:6:5), Alfa (8) | BC3 |
Color de un componente | Un componente (8) | BC4 |
Color de dos componentes | Dos componentes (8:8) | BC5 |
BC1
Use el primer formato de compresión de bloques (BC1) (ya sea DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT_BC1_UNORM o DXGI_BC1_UNORM_SRGB) para almacenar datos de color de tres componentes mediante un color de 5:6:5 (5 bits rojo, 6 bits verde, 5 bits azul). Esto es cierto incluso si los datos también contienen alfa de 1 bits. Suponiendo que una textura de 4×4 con el formato de datos más grande posible, el formato BC1 reduce la memoria necesaria de 48 bytes (16 colores × 3 componentes/color × 1 byte/componente) a 8 bytes de memoria.
El algoritmo funciona en 4×4 bloques de elementos de textura. En lugar de almacenar 16 colores, el algoritmo guarda dos colores de referencia (color_0 y color_1) y 16 índices de color de 2 bits (bloques a-p), como se muestra en el diagrama siguiente.
Los índices de color (a-p) se usan para buscar los colores originales de una tabla de colores. La tabla de colores contiene 4 colores. Los dos primeros colores, color_0 y color_1, son los colores mínimos y máximos. Los otros dos colores, color_2 y color_3, son colores intermedios calculados con interpolación lineal.
color_2 = 2/3*color_0 + 1/3*color_1
color_3 = 1/3*color_0 + 2/3*color_1
A los cuatro colores se les asignan valores de índice de 2 bits que se guardarán en bloques a-p.
color_0 = 00
color_1 = 01
color_2 = 10
color_3 = 11
Por último, cada uno de los colores de los bloques a-p se compara con los cuatro colores de la tabla de colores y el índice del color más cercano se almacena en los bloques de 2 bits.
Este algoritmo se presta a los datos que también contienen alfa de 1 bits. La única diferencia es que color_3 se establece en 0 (que representa un color transparente) y color_2 es una combinación lineal de color_0 y color_1.
color_2 = 1/2*color_0 + 1/2*color_1;
color_3 = 0;
BC2
Use el formato BC2 (ya sea DXGI_FORMAT_BC2_TYPELESS, DXGI_FORMAT_BC2_UNORM o DXGI_BC2_UNORM_SRGB) para almacenar datos que contengan datos de color y alfa con baja coherencia (use BC3 para datos alfa altamente coherentes). El formato BC2 almacena datos RGB como un color de 5:6:5 (5 bits rojo, 6 bits verde, 5 bits azul) y alfa como un valor independiente de 4 bits. Suponiendo que una textura de 4×4 usando el formato de datos más grande posible, esta técnica de compresión reduce la memoria necesaria de 64 bytes (16 colores × 4 componentes/color × 1 byte/componente) a 16 bytes de memoria.
El formato BC2 almacena colores con el mismo número de bits y diseño de datos que el formato BC1 ; sin embargo, BC2 requiere más de 64 bits de memoria para almacenar los datos alfa, como se muestra en el diagrama siguiente.
BC3
Use el formato BC3 (ya sea DXGI_FORMAT_BC3_TYPELESS, DXGI_FORMAT_BC3_UNORM o DXGI_BC3_UNORM_SRGB) para almacenar datos de color altamente coherentes (use BC2 con datos alfa menos coherentes). El formato BC3 almacena datos de color mediante el color 5:6:5 (5 bits rojo, 6 bits verde, 5 bits azul) y datos alfa mediante un byte. Suponiendo que una textura de 4×4 usando el formato de datos más grande posible, esta técnica de compresión reduce la memoria necesaria de 64 bytes (16 colores × 4 componentes/color × 1 byte/componente) a 16 bytes de memoria.
El formato BC3 almacena colores con el mismo número de bits y diseño de datos que el formato BC1 ; sin embargo, BC3 requiere más de 64 bits de memoria para almacenar los datos alfa. El formato BC3 controla alfa almacenando dos valores de referencia e interpolando entre ellos (de forma similar a cómo BC1 almacena el color RGB).
El algoritmo funciona en 4×4 bloques de elementos de textura. En lugar de almacenar 16 valores alfa, el algoritmo almacena 2 alfas de referencia (alpha_0 y alpha_1) y índices de color de 16 3 bits (alfa a a p), como se muestra en el diagrama siguiente.
El formato BC3 usa los índices alfa (a–p) para buscar los colores originales de una tabla de búsqueda que contiene 8 valores. Los dos primeros valores (alpha_0 y alpha_1) son los valores mínimo y máximo; los otros seis valores intermedios se calculan mediante interpolación lineal.
El algoritmo determina el número de valores alfa interpolados examinando los dos valores alfa de referencia. Si alpha_0 es mayor que alpha_1, BC3 interpola 6 valores alfa; de lo contrario, interpola 4. Cuando BC3 interpola solo 4 valores alfa, establece dos valores alfa adicionales (0 para totalmente transparente y 255 para totalmente opaco). BC3 comprime los valores alfa en el área de textura 4×4 almacenando el código de bits correspondiente a los valores alfa interpolados que coinciden con el alfa original de un elemento de textura determinado.
if( alpha_0 > alpha_1 )
{
// 6 interpolated alpha values.
alpha_2 = 6/7*alpha_0 + 1/7*alpha_1; // bit code 010
alpha_3 = 5/7*alpha_0 + 2/7*alpha_1; // bit code 011
alpha_4 = 4/7*alpha_0 + 3/7*alpha_1; // bit code 100
alpha_5 = 3/7*alpha_0 + 4/7*alpha_1; // bit code 101
alpha_6 = 2/7*alpha_0 + 5/7*alpha_1; // bit code 110
alpha_7 = 1/7*alpha_0 + 6/7*alpha_1; // bit code 111
}
else
{
// 4 interpolated alpha values.
alpha_2 = 4/5*alpha_0 + 1/5*alpha_1; // bit code 010
alpha_3 = 3/5*alpha_0 + 2/5*alpha_1; // bit code 011
alpha_4 = 2/5*alpha_0 + 3/5*alpha_1; // bit code 100
alpha_5 = 1/5*alpha_0 + 4/5*alpha_1; // bit code 101
alpha_6 = 0; // bit code 110
alpha_7 = 255; // bit code 111
}
BC4
Use el formato BC4 para almacenar datos de color de un componente con 8 bits para cada color. Como resultado del aumento de la precisión (en comparación con BC1), BC4 es ideal para almacenar datos de punto flotante en el intervalo de [0 a 1] mediante el formato DXGI_FORMAT_BC4_UNORM y [-1 a +1] mediante el formato DXGI_FORMAT_BC4_SNORM. Suponiendo que una textura de 4×4 usando el formato de datos más grande posible, esta técnica de compresión reduce la memoria necesaria de 16 bytes (16 colores × 1 componentes/color × 1 byte/componente) a 8 bytes.
El algoritmo funciona en 4×4 bloques de elementos de textura. En lugar de almacenar 16 colores, el algoritmo almacena 2 colores de referencia (red_0 y red_1) y índices de color de 16 3 bits (rojo a rojo p), como se muestra en el diagrama siguiente.
El algoritmo usa los índices de 3 bits para buscar colores de una tabla de colores que contiene 8 colores. Los dos primeros colores, red_0 y red_1, son los colores mínimos y máximos. El algoritmo calcula los colores restantes mediante la interpolación lineal.
El algoritmo determina el número de valores de color interpolados examinando los dos valores de referencia. Si red_0 es mayor que red_1, BC4 interpola 6 valores de color; de lo contrario, interpola 4. Cuando BC4 interpola solo 4 valores de color, establece dos valores de color adicionales (0,0f para totalmente transparente y 1,0f para totalmente opaco). BC4 comprime los valores alfa en el área de textura 4×4 almacenando el código de bits correspondiente a los valores alfa interpolados que coinciden con el alfa original para un elemento de textura determinado.
BC4_UNORM
La interpolación de los datos de un solo componente se realiza como en el ejemplo de código siguiente.
unsigned word red_0, red_1;
if( red_0 > red_1 )
{
// 6 interpolated color values
red_2 = (6*red_0 + 1*red_1)/7.0f; // bit code 010
red_3 = (5*red_0 + 2*red_1)/7.0f; // bit code 011
red_4 = (4*red_0 + 3*red_1)/7.0f; // bit code 100
red_5 = (3*red_0 + 4*red_1)/7.0f; // bit code 101
red_6 = (2*red_0 + 5*red_1)/7.0f; // bit code 110
red_7 = (1*red_0 + 6*red_1)/7.0f; // bit code 111
}
else
{
// 4 interpolated color values
red_2 = (4*red_0 + 1*red_1)/5.0f; // bit code 010
red_3 = (3*red_0 + 2*red_1)/5.0f; // bit code 011
red_4 = (2*red_0 + 3*red_1)/5.0f; // bit code 100
red_5 = (1*red_0 + 4*red_1)/5.0f; // bit code 101
red_6 = 0.0f; // bit code 110
red_7 = 1.0f; // bit code 111
}
A los colores de referencia se les asignan índices de 3 bits (000–111, ya que hay 8 valores), que se guardarán en bloques rojo a través de p rojo durante la compresión.
BC4_SNORM
El DXGI_FORMAT_BC4_SNORM es exactamente el mismo, excepto que los datos se codifican en el intervalo SNORM y cuando se interpolan 4 valores de color. La interpolación de los datos de un solo componente se realiza como en el ejemplo de código siguiente.
signed word red_0, red_1;
if( red_0 > red_1 )
{
// 6 interpolated color values
red_2 = (6*red_0 + 1*red_1)/7.0f; // bit code 010
red_3 = (5*red_0 + 2*red_1)/7.0f; // bit code 011
red_4 = (4*red_0 + 3*red_1)/7.0f; // bit code 100
red_5 = (3*red_0 + 4*red_1)/7.0f; // bit code 101
red_6 = (2*red_0 + 5*red_1)/7.0f; // bit code 110
red_7 = (1*red_0 + 6*red_1)/7.0f; // bit code 111
}
else
{
// 4 interpolated color values
red_2 = (4*red_0 + 1*red_1)/5.0f; // bit code 010
red_3 = (3*red_0 + 2*red_1)/5.0f; // bit code 011
red_4 = (2*red_0 + 3*red_1)/5.0f; // bit code 100
red_5 = (1*red_0 + 4*red_1)/5.0f; // bit code 101
red_6 = -1.0f; // bit code 110
red_7 = 1.0f; // bit code 111
}
A los colores de referencia se les asignan índices de 3 bits (000–111, ya que hay 8 valores), que se guardarán en bloques rojo a través de p rojo durante la compresión.
BC5
Use el formato BC5 para almacenar datos de color de dos componentes con 8 bits para cada color. Como resultado del aumento de la precisión (en comparación con BC1), BC5 es ideal para almacenar datos de punto flotante en el intervalo de [0 a 1] mediante el formato de DXGI_FORMAT_BC5_UNORM y [-1 a +1] mediante el formato DXGI_FORMAT_BC5_SNORM. Suponiendo una textura de 4×4 con el formato de datos más grande posible, esta técnica de compresión reduce la memoria necesaria de 32 bytes (16 colores × 2 componentes/color × 1 byte/componente) a 16 bytes.
El algoritmo funciona en 4×4 bloques de elementos de textura. En lugar de almacenar 16 colores para ambos componentes, el algoritmo almacena 2 colores de referencia para cada componente (red_0, red_1, green_0 y green_1) y 16 índices de color de 3 bits para cada componente (rojo a p rojo y verde a través de p verde), como se muestra en el diagrama siguiente.
El algoritmo usa los índices de 3 bits para buscar colores de una tabla de colores que contiene 8 colores. Los dos primeros colores (red_0 y red_1 (o green_0 y green_1) son los colores mínimos y máximos. El algoritmo calcula los colores restantes mediante la interpolación lineal.
El algoritmo determina el número de valores de color interpolados examinando los dos valores de referencia. Si red_0 es mayor que red_1, BC5 interpola 6 valores de color; de lo contrario, interpola 4. Cuando BC5 interpola solo 4 valores de color, establece los dos valores de color restantes en 0,0f y 1,0f.
BC5_UNORM
La interpolación de los datos de un solo componente se realiza como en el ejemplo de código siguiente. Los cálculos de los componentes verdes son similares.
unsigned word red_0, red_1;
if( red_0 > red_1 )
{
// 6 interpolated color values
red_2 = (6*red_0 + 1*red_1)/7.0f; // bit code 010
red_3 = (5*red_0 + 2*red_1)/7.0f; // bit code 011
red_4 = (4*red_0 + 3*red_1)/7.0f; // bit code 100
red_5 = (3*red_0 + 4*red_1)/7.0f; // bit code 101
red_6 = (2*red_0 + 5*red_1)/7.0f; // bit code 110
red_7 = (1*red_0 + 6*red_1)/7.0f; // bit code 111
}
else
{
// 4 interpolated color values
red_2 = (4*red_0 + 1*red_1)/5.0f; // bit code 010
red_3 = (3*red_0 + 2*red_1)/5.0f; // bit code 011
red_4 = (2*red_0 + 3*red_1)/5.0f; // bit code 100
red_5 = (1*red_0 + 4*red_1)/5.0f; // bit code 101
red_6 = 0.0f; // bit code 110
red_7 = 1.0f; // bit code 111
}
A los colores de referencia se les asignan índices de 3 bits (000–111, ya que hay 8 valores), que se guardarán en bloques rojo a través de p rojo durante la compresión.
BC5_SNORM
El DXGI_FORMAT_BC5_SNORM es exactamente el mismo, excepto que los datos se codifican en el intervalo SNORM y cuando se interpolan 4 valores de datos, los dos valores adicionales son -1.0f y 1.0f. La interpolación de los datos de un solo componente se realiza como en el ejemplo de código siguiente. Los cálculos de los componentes verdes son similares.
signed word red_0, red_1;
if( red_0 > red_1 )
{
// 6 interpolated color values
red_2 = (6*red_0 + 1*red_1)/7.0f; // bit code 010
red_3 = (5*red_0 + 2*red_1)/7.0f; // bit code 011
red_4 = (4*red_0 + 3*red_1)/7.0f; // bit code 100
red_5 = (3*red_0 + 4*red_1)/7.0f; // bit code 101
red_6 = (2*red_0 + 5*red_1)/7.0f; // bit code 110
red_7 = (1*red_0 + 6*red_1)/7.0f; // bit code 111
}
else
{
// 4 interpolated color values
red_2 = (4*red_0 + 1*red_1)/5.0f; // bit code 010
red_3 = (3*red_0 + 2*red_1)/5.0f; // bit code 011
red_4 = (2*red_0 + 3*red_1)/5.0f; // bit code 100
red_5 = (1*red_0 + 4*red_1)/5.0f; // bit code 101
red_6 = -1.0f; // bit code 110
red_7 = 1.0f; // bit code 111
}
A los colores de referencia se les asignan índices de 3 bits (000–111, ya que hay 8 valores), que se guardarán en bloques rojo a través de p rojo durante la compresión.
Conversión de formato
Direct3D permite copias entre texturas preestructuradas y texturas comprimidas por bloques de los mismos anchos de bits.
Puede copiar recursos entre algunos tipos de formato. Este tipo de operación de copia realiza un tipo de conversión de formato que reinterpreta los datos de recursos como un tipo de formato diferente. Considere este ejemplo que muestra la diferencia entre la reinterpretación de datos con el modo en que se comporta un tipo de conversión más típico:
FLOAT32 f = 1.0f;
UINT32 u;
Para reinterpretar 'f' como el tipo de 'u', use memcpy:
memcpy( &u, &f, sizeof( f ) ); // 'u' becomes equal to 0x3F800000.
En la reinterpretación anterior, el valor subyacente de los datos no cambia; memcpy reinterpreta el float como un entero sin signo.
Para realizar el tipo de conversión más típico, use la asignación:
u = f; // 'u' becomes 1.
En la conversión anterior, cambia el valor subyacente de los datos.
En la tabla siguiente se enumeran los formatos de origen y destino permitidos que puede usar en este tipo de reinterpretación de conversión de formato. Debe codificar los valores correctamente para que la reinterpretación funcione según lo previsto.
Ancho de bits | Recurso sin comprimir | Recurso comprimido en bloques |
---|---|---|
32 | DXGI_FORMAT_R32_UINT DXGI_FORMAT_R32_SINT |
DXGI_FORMAT_R9G9B9E5_SHAREDEXP |
64 | DXGI_FORMAT_R16G16B16A16_UINT DXGI_FORMAT_R16G16B16A16_SINT DXGI_FORMAT_R32G32_UINT DXGI_FORMAT_R32G32_SINT |
DXGI_FORMAT_BC1_UNORM[_SRGB] DXGI_FORMAT_BC4_UNORM DXGI_FORMAT_BC4_SNORM |
128 | DXGI_FORMAT_R32G32B32A32_UINT DXGI_FORMAT_R32G32B32A32_SINT |
DXGI_FORMAT_BC2_UNORM[_SRGB] DXGI_FORMAT_BC3_UNORM[_SRGB] DXGI_FORMAT_BC5_UNORM DXGI_FORMAT_BC5_SNORM |