Compactação de bloco
A compactação de bloco é uma técnica de compactação de textura com perdas para reduzir o tamanho da textura e o volume de memória, proporcionando um aumento de desempenho. Uma textura compactada em bloco pode ser menor do que uma textura com 32 bits por cor.
A compactação de bloco é uma técnica de compactação de textura para reduzir o tamanho da textura. Quando comparada a uma textura com 32 bits por cor, uma textura compactada em bloco pode ser até 75% menor. Os aplicativos geralmente veem um aumento de desempenho ao usar a compactação de bloco devido ao menor volume de memória.
Embora com perdas, a compactação de blocos funciona bem e é recomendada para todas as texturas que são transformadas e filtradas pelo pipeline. Texturas mapeadas diretamente para a tela (elementos da interface do usuário, como ícones e texto) não são boas opções para compactação porque os artefatos são mais perceptíveis.
Uma textura compactada em bloco deve ser criada como um múltiplo de tamanho 4 em todas as dimensões e não pode ser usada como uma saída do pipeline.
Como funciona a compactação de blocos
A compactação de bloco é uma técnica para reduzir a quantidade de memória necessária para armazenar dados de cores. Ao armazenar algumas cores em seu tamanho original e outras cores usando um esquema de codificação, você pode reduzir drasticamente a quantidade de memória necessária para armazenar a imagem. Como o hardware decodifica automaticamente os dados compactados, não há penalidade de desempenho para o uso de texturas compactadas.
Para ver como a compactação funciona, veja os dois exemplos a seguir. O primeiro exemplo descreve a quantidade de memória usada ao armazenar dados não compactados; O segundo exemplo descreve a quantidade de memória usada ao armazenar dados compactados.
Armazenando dados não compactados
A ilustração a seguir representa uma textura 4×4 não compactada. Suponha que cada cor contenha um único componente de cor (vermelho, por exemplo) e seja armazenada em um byte de memória.
Os dados descompactados são dispostos na memória sequencialmente e requerem 16 bytes, conforme mostrado na ilustração a seguir.
Armazenando dados compactados
Agora que você viu quanta memória uma imagem não compactada usa, dê uma olhada em quanta memória uma imagem compactada economiza. O formato de compactação BC4 armazena 2 cores (1 byte cada) e 16 índices de 3 bits (48 bits ou 6 bytes) que são usados para interpolar as cores originais na textura, conforme mostrado na ilustração a seguir.
O espaço total necessário para armazenar os dados compactados é de 8 bytes, o que representa uma economia de memória de 50% em relação ao exemplo não compactado. A economia é ainda maior quando mais de um componente de cor é usado.
A economia substancial de memória fornecida pela compactação de bloco pode levar a um aumento no desempenho. Esse desempenho tem o custo da qualidade da imagem (devido à interpolação de cores); no entanto, a qualidade inferior geralmente não é perceptível.
A próxima seção mostra como o Direct3D habilita o uso da compactação de bloco em um aplicativo.
Usando compactação de bloco
Crie uma textura compactada em bloco como uma textura não compactada, exceto que você especifica um formato compactado em bloco.
Em seguida, crie uma exibição para associar a textura ao pipeline Como uma textura compactada em bloco pode ser usada apenas como uma entrada para um estágio de sombreador, você deseja criar uma exibição de recurso de sombreador.
Use uma textura compactada em bloco da mesma forma que usaria uma textura não compactada. Se o aplicativo obtiver um ponteiro de memória para bloquear dados compactados, você precisará considerar o preenchimento de memória em um mipmap que faz com que o tamanho declarado seja diferente do tamanho real.
Tamanho virtual versus tamanho físico
Se você tiver um código de aplicativo que usa um ponteiro de memória para percorrer a memória de uma textura compactada em bloco, há uma consideração importante que pode exigir uma modificação no código do aplicativo. Uma textura compactada em bloco deve ser um múltiplo de 4 em todas as dimensões porque os algoritmos de compactação de bloco operam em blocos texel 4x4. Isso será um problema para um mipmap cujas dimensões iniciais são divisíveis por 4, mas os níveis subdivididos não. O diagrama a seguir mostra a diferença de área entre o tamanho virtual (declarado) e o tamanho físico (real) de cada nível de mipmap.
O lado esquerdo do diagrama mostra os tamanhos de nível de mipmap gerados para uma textura 60×40 não compactada. O tamanho do nível superior é obtido da chamada de API que gera a textura; Cada nível subsequente tem metade do tamanho do nível anterior. Para uma textura não compactada, não há diferença entre o tamanho virtual (declarado) e o tamanho físico (real).
O lado direito do diagrama mostra os tamanhos de nível de mipmap gerados para a mesma textura 60×40 com compactação. Observe que o segundo e o terceiro níveis têm preenchimento de memória para tornar os fatores de tamanho de 4 em todos os níveis. Isso é necessário para que os algoritmos possam operar em blocos de texel 4×4. Isso é especialmente evidente se você considerar níveis de mipmap menores que 4×4; O tamanho desses níveis de MIPMAP muito pequenos será arredondado para o fator mais próximo de 4 quando a memória de textura for alocada.
O hardware de amostragem usa o tamanho virtual; Quando a textura é amostrada, o preenchimento de memória é ignorado. Para níveis de mipmap menores que 4×4, somente os quatro primeiros texels serão usados para um mapa 2×2 e somente o primeiro texel será usado por um bloco 1×1. No entanto, não há nenhuma estrutura de API que exponha o tamanho físico (incluindo o preenchimento de memória).
Em resumo, tenha cuidado ao usar blocos de memória alinhados ao copiar regiões que contêm dados compactados em bloco. Para fazer isso em um aplicativo que obtém um ponteiro de memória, certifique-se de que o ponteiro use o passo de superfície para considerar o tamanho da memória física.
Algoritmos de compressão
As técnicas de compactação de bloco no Direct3D dividem os dados de textura não compactados em blocos 4×4, compactam cada bloco e armazenam os dados. Por esse motivo, espera-se que as texturas sejam compactadas devem ter dimensões de textura que sejam múltiplos de 4.
O diagrama anterior mostra uma textura particionada em blocos de texel. O primeiro bloco mostra o layout dos 16 texels rotulados como a-p, mas cada bloco tem a mesma organização de dados.
O Direct3D implementa vários esquemas de compactação, cada um implementa uma compensação diferente entre o número de componentes armazenados, o número de bits por componente e a quantidade de memória consumida. Use esta tabela para ajudar a escolher o formato que funciona melhor com o tipo de dados e a resolução de dados que melhor se adapta ao seu aplicativo.
Dados de origem | Resolução de compactação de dados (em bits) | Escolha este formato de compactação |
---|---|---|
Cor e alfa de três componentes | Cor (5:6:5), Alfa (1) ou sem alfa | BC1 |
Cor e alfa de três componentes | Cor (5:6:5), Alfa (4) | BC2 |
Cor e alfa de três componentes | Cor (5:6:5), Alfa (8) | BC3 |
Cor de um componente | Um componente (8) | BC4 |
Cor de dois componentes | Dois componentes (8:8) | BC5 |
BC1
Use o primeiro formato de compactação de bloco (BC1) (DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT_BC1_UNORM ou DXGI_BC1_UNORM_SRGB) para armazenar dados de cores de três componentes usando uma cor 5:6:5 (5 bits vermelho, 6 bits verde, 5 bits azul). Isso é verdadeiro mesmo que os dados também contenham alfa de 1 bit. Assumindo uma textura 4×4 usando o maior formato de dados possível, o formato BC1 reduz a memória necessária de 48 bytes (16 cores × 3 componentes/cor × 1 byte/componente) para 8 bytes de memória.
O algoritmo funciona em 4×4 blocos de texels. Em vez de armazenar 16 cores, o algoritmo salva 2 cores de referência (color_0 e color_1) e 16 índices de cores de 2 bits (blocos a–p), conforme mostrado no diagrama a seguir.
Os índices de cores (a–p) são usados para procurar as cores originais de uma tabela de cores. A tabela de cores contém 4 cores. As duas primeiras cores — color_0 e color_1 — são as cores mínima e máxima. As outras duas cores, color_2 e color_3, são cores intermediárias calculadas com interpolação linear.
color_2 = 2/3*color_0 + 1/3*color_1
color_3 = 1/3*color_0 + 2/3*color_1
As quatro cores recebem valores de índice de 2 bits que serão salvos nos blocos a–p.
color_0 = 00
color_1 = 01
color_2 = 10
color_3 = 11
Finalmente, cada uma das cores nos blocos a–p é comparada com as quatro cores na tabela de cores, e o índice para a cor mais próxima é armazenado nos blocos de 2 bits.
Esse algoritmo também se presta a dados que contêm alfa de 1 bit. A única diferença é que color_3 é definido como 0 (que representa uma cor transparente) e color_2 é uma mistura linear de color_0 e color_1.
color_2 = 1/2*color_0 + 1/2*color_1;
color_3 = 0;
BC2
Use o formato BC2 (DXGI_FORMAT_BC2_TYPELESS, DXGI_FORMAT_BC2_UNORM ou DXGI_BC2_UNORM_SRGB) para armazenar dados que contenham dados coloridos e alfa com baixa coerência (use BC3 para dados alfa altamente coerentes). O formato BC2 armazena dados RGB como uma cor 5:6:5 (5 bits vermelho, 6 bits verde, 5 bits azul) e alfa como um valor separado de 4 bits. Assumindo uma textura 4×4 usando o maior formato de dados possível, essa técnica de compactação reduz a memória necessária de 64 bytes (16 cores × 4 componentes/cor × 1 byte/componente) para 16 bytes de memória.
O formato BC2 armazena cores com o mesmo número de bits e layout de dados que o formato BC1; no entanto, o BC2 requer 64 bits adicionais de memória para armazenar os dados alfa, conforme mostrado no diagrama a seguir.
BC3
Use o formato BC3 (DXGI_FORMAT_BC3_TYPELESS, DXGI_FORMAT_BC3_UNORM ou DXGI_BC3_UNORM_SRGB) para armazenar dados de cores altamente coerentes (use BC2 com dados alfa menos coerentes). O formato BC3 armazena dados de cores usando cores 5:6:5 (5 bits vermelho, 6 bits verde, 5 bits azul) e dados alfa usando um byte. Assumindo uma textura 4×4 usando o maior formato de dados possível, essa técnica de compactação reduz a memória necessária de 64 bytes (16 cores × 4 componentes/cor × 1 byte/componente) para 16 bytes de memória.
O formato BC3 armazena cores com o mesmo número de bits e layout de dados que o formato BC1; no entanto, o BC3 requer 64 bits adicionais de memória para armazenar os dados alfa. O formato BC3 lida com alfa armazenando dois valores de referência e interpolando entre eles (da mesma forma que BC1 armazena cores RGB).
O algoritmo funciona em 4×4 blocos de texels. Em vez de armazenar 16 valores alfa, o algoritmo armazena 2 alfas de referência (alpha_0 e alpha_1) e 16 índices de cores de 3 bits (alfa a a p), conforme mostrado no diagrama a seguir.
O formato BC3 usa os índices alfa (a–p) para pesquisar as cores originais de uma tabela de pesquisa que contém 8 valores. Os dois primeiros valores — alpha_0 e alpha_1 — são os valores mínimo e máximo; Os outros seis valores intermediários são calculados usando interpolação linear.
O algoritmo determina o número de valores alfa interpolados examinando os dois valores alfa de referência. Se alpha_0 for maior que alpha_1, BC3 interpola 6 valores alfa; caso contrário, interpola 4. Quando BC3 interpola apenas 4 valores alfa, ele define dois valores alfa adicionais (0 para totalmente transparente e 255 para totalmente opaco). O BC3 compacta os valores alfa na área de texel 4×4 armazenando o código de bits correspondente aos valores alfa interpolados que mais se aproximam do alfa original de um determinado texel.
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 o formato BC4 para armazenar dados de cores de um componente usando 8 bits para cada cor. Como resultado da maior precisão (em comparação com BC1), o BC4 é ideal para armazenar dados de ponto flutuante no intervalo de [0 a 1] usando o formato DXGI_FORMAT_BC4_UNORM e [-1 a +1] usando o formato DXGI_FORMAT_BC4_SNORM. Assumindo uma textura 4×4 usando o maior formato de dados possível, essa técnica de compactação reduz a memória necessária de 16 bytes (16 cores × 1 componente/cor × 1 byte/componente) para 8 bytes.
O algoritmo funciona em 4×4 blocos de texels. Em vez de armazenar 16 cores, o algoritmo armazena 2 cores de referência (red_0 e red_1) e 16 índices de cores de 3 bits (vermelho a a vermelho p), conforme mostrado no diagrama a seguir.
O algoritmo usa os índices de 3 bits para pesquisar cores de uma tabela de cores que contém 8 cores. As duas primeiras cores — red_0 e red_1 — são as cores mínima e máxima. O algoritmo calcula as cores restantes usando interpolação linear.
O algoritmo determina o número de valores de cor interpolados examinando os dois valores de referência. Se red_0 for maior que red_1, BC4 interpolará 6 valores de cor; caso contrário, interpola 4. Quando BC4 interpola apenas 4 valores de cor, ele define dois valores de cor adicionais (0,0f para totalmente transparente e 1,0f para totalmente opaco). O BC4 compacta os valores alfa na área de texel 4×4 armazenando o código de bits correspondente aos valores alfa interpolados que mais se aproximam do alfa original de um determinado texel.
BC4_UNORM
A interpolação dos dados de componente único é feita como no exemplo de código a seguir.
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
}
As cores de referência são atribuídas a índices de 3 bits (000–111, pois há 8 valores), que serão salvos nos blocos vermelho a a vermelho p durante a compactação.
BC4_SNORM
O DXGI_FORMAT_BC4_SNORM é exatamente o mesmo, exceto que os dados são codificados no intervalo SNORM e quando 4 valores de cor são interpolados. A interpolação dos dados de componente único é feita como no exemplo de código a seguir.
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
}
As cores de referência são atribuídas a índices de 3 bits (000–111, pois há 8 valores), que serão salvos nos blocos vermelho a a vermelho p durante a compactação.
BC5
Use o formato BC5 para armazenar dados de cores de dois componentes usando 8 bits para cada cor. Como resultado da maior precisão (em comparação com BC1), o BC5 é ideal para armazenar dados de ponto flutuante no intervalo de [0 a 1] usando o formato DXGI_FORMAT_BC5_UNORM e [-1 a +1] usando o formato DXGI_FORMAT_BC5_SNORM. Assumindo uma textura 4×4 usando o maior formato de dados possível, essa técnica de compactação reduz a memória necessária de 32 bytes (16 cores × 2 componentes/cor × 1 byte/componente) para 16 bytes.
O algoritmo funciona em 4×4 blocos de texels. Em vez de armazenar 16 cores para ambos os componentes, o algoritmo armazena 2 cores de referência para cada componente (red_0, red_1, green_0 e green_1) e 16 índices de cores de 3 bits para cada componente (vermelho a vermelho p e verde a verde a verde p), conforme mostrado no diagrama a seguir.
O algoritmo usa os índices de 3 bits para pesquisar cores de uma tabela de cores que contém 8 cores. As duas primeiras cores — red_0 e red_1 (ou green_0 e green_1) — são as cores mínima e máxima. O algoritmo calcula as cores restantes usando interpolação linear.
O algoritmo determina o número de valores de cor interpolados examinando os dois valores de referência. Se red_0 for maior que red_1, BC5 interpolará 6 valores de cor; caso contrário, interpola 4. Quando BC5 interpola apenas 4 valores de cor, ele define os dois valores de cor restantes em 0,0f e 1,0f.
BC5_UNORM
A interpolação dos dados de componente único é feita como no exemplo de código a seguir. Os cálculos para os componentes verdes são semelhantes.
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
}
As cores de referência são atribuídas a índices de 3 bits (000–111, pois há 8 valores), que serão salvos nos blocos vermelho a a vermelho p durante a compactação.
BC5_SNORM
O DXGI_FORMAT_BC5_SNORM é exatamente o mesmo, exceto que os dados são codificados no intervalo SNORM e quando 4 valores de dados são interpolados, os dois valores adicionais são -1,0f e 1,0f. A interpolação dos dados de componente único é feita como no exemplo de código a seguir. Os cálculos para os componentes verdes são semelhantes.
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
}
As cores de referência são atribuídas a índices de 3 bits (000–111, pois há 8 valores), que serão salvos nos blocos vermelho a a vermelho p durante a compactação.
Conversão de formato
O Direct3D permite cópias entre texturas de tipo pré-estruturado e texturas compactadas em bloco das mesmas larguras de bits.
Você pode copiar recursos entre alguns tipos de formato. Esse tipo de operação de cópia executa um tipo de conversão de formato que reinterpreta os dados do recurso como um tipo de formato diferente. Considere este exemplo que mostra a diferença entre reinterpretar dados com a maneira como um tipo mais típico de conversão se comporta:
FLOAT32 f = 1.0f;
UINT32 u;
Para reinterpretar 'f' como o tipo de 'u', use memcpy:
memcpy( &u, &f, sizeof( f ) ); // 'u' becomes equal to 0x3F800000.
Na reinterpretação anterior, o valor subjacente dos dados não é alterado; memcpy reinterpreta o float como um inteiro sem sinal.
Para executar o tipo mais comum de conversão, use a atribuição:
u = f; // 'u' becomes 1.
Na conversão anterior, o valor subjacente dos dados é alterado.
A tabela a seguir lista os formatos de origem e destino permitidos que você pode usar nesse tipo de reinterpretação de conversão de formato. Você deve codificar os valores corretamente para que a reinterpretação funcione conforme o esperado.
Largura de bits | Recurso descompactado | Recurso compactado em bloco |
---|---|---|
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 |