Compartilhar via


Precisão e recorte numérico em grafos de efeito

Os aplicativos que renderizam efeitos usando Direct2D devem ter cuidado para alcançar o nível desejado de qualidade e previsibilidade em relação à precisão numérica. Este tópico descreve as práticas recomendadas e as configurações relevantes no Direct2D que serão úteis se:

  • O grafo de efeito depende de alta precisão numérica ou cores fora do intervalo [0, 1], e você deseja garantir que elas sempre estarão disponíveis
  • Ou o grafo de efeito depende da implementação de renderização para fixar cores intermediárias ao intervalo [0, 1], e você deseja garantir que essa fixação sempre ocorra

Direct2D geralmente divide um grafo de efeito em seções e renderiza cada seção em uma etapa separada. A saída de algumas etapas pode ser armazenada em texturas Direct3D intermediárias que, por padrão, têm intervalo numérico limitado e precisão. Direct2D não garante se ou onde essas texturas intermediárias são usadas. Esse comportamento pode variar de acordo com os recursos de GPU, bem como entre as versões do Windows.

Em Windows 10, Direct2D usa menos texturas intermediárias devido ao uso de vinculação de sombreador. Direct2D pode, portanto, produzir resultados diferentes com configurações padrão do que em versões anteriores do Windows. Isso afeta principalmente cenários em que a vinculação de sombreador é possível em um grafo de efeito e esse grafo também contém efeitos que produzem cores de saída de intervalo estendido.

Visão geral da renderização de efeitos e intermediários

Para renderizar um grafo de efeito, Direct2D primeiro encontra o grafo subjacente de "transformações", em que uma transformação é um nó de grafo usado dentro de um efeito. Há diferentes tipos de transformações, incluindo aquelas que fornecem sombreadores Direct3D para Direct2D usar.

Por exemplo, Direct2D pode renderizar um grafo de efeito da seguinte maneira:

grafo de efeito com texturas intermediárias

Direct2D procura oportunidades para reduzir o número de texturas intermediárias usadas para renderizar o grafo de efeito; essa lógica é opaca para aplicativos. Por exemplo, o grafo a seguir pode ser renderizado por Direct2D usando uma chamada de desenho direct3D e nenhuma textura intermediária:

grafo de efeito sem texturas intermediárias

Antes de Windows 10, Direct2D sempre usaria texturas intermediárias se vários sombreadores de pixels fossem usados no mesmo grafo de efeito. A maioria dos efeitos internos que simplesmente ajustam os valores de cor (por exemplo, Brilho ou Saturação) fazem isso usando sombreadores de pixel.

Em Windows 10, Direct2D agora pode evitar o uso de texturas intermediárias nesses casos. Ele faz isso vinculando internamente sombreadores de pixel adjacentes. Por exemplo:

grafo de efeito do windows 10 com vários sombreadores de pixel e sem texturas intermediárias

Observe que nem todos os sombreadores de pixel adjacentes em um grafo podem ser vinculados e, portanto, apenas determinados grafos produzirão uma saída diferente em Windows 10. Para obter detalhes completos, consulte Vinculação do sombreador de efeito. As principais restrições são:

  • Um efeito não será vinculado aos efeitos que consomem sua saída, se o primeiro efeito estiver conectado como uma entrada a vários efeitos.
  • Um efeito não será vinculado a um conjunto de efeitos como sua entrada, se o primeiro efeito amostrar sua entrada em uma posição lógica diferente de sua saída. Por exemplo, um efeito de Matriz de Cores pode estar vinculado à sua entrada, mas um efeito Convolution não será.

Comportamento de efeito interno

Muitos efeitos internos podem produzir cores fora do intervalo [0, 1] no espaço de cores não multiplicado, mesmo quando suas cores de entrada estão dentro desse intervalo. Quando isso acontece, essas cores podem estar sujeitas a recorte numérico. Observe que é importante considerar o intervalo de cores em espaço não multiplicado, embora efeitos internos normalmente produzam cores em espaço pré-multiplicado. Isso garante que as cores permaneçam dentro do intervalo, mesmo que outros efeitos, subsequentemente, as removam da preempção.

Alguns dos efeitos que podem emitir essas cores fora do intervalo oferecem uma propriedade "ClampOutput". Estão incluídos:

Definir a propriedade ClampOutput como TRUE nesses efeitos garante que um resultado consistente seja obtido, independentemente de fatores como a vinculação do sombreador. Observe que a fixação ocorre em espaço não multiplicado.

Outros efeitos internos também podem produzir cores de saída além do intervalo [0, 1] em espaço não multiplicado, mesmo quando suas cores pixels (e propriedades "Color", se houver) estiverem dentro desse intervalo. Estão incluídos:

Forçar recorte numérico dentro de um grafo de efeito

Ao usar efeitos listados acima que não têm uma propriedade ClampOutput, os aplicativos devem considerar forçar a fixação numérica. Isso pode ser feito inserindo um efeito adicional no grafo que fixa seus pixels. Um efeito de Matriz de Cores pode ser usado, com sua propriedade 'ClampOutput' definida como TRUE e deixando a propriedade 'ColorMatrix' como o valor padrão (passagem).

Uma segunda opção para obter resultados consistentes é solicitar que Direct2D use texturas intermediárias que tenham maior precisão. Isso é descrito abaixo.

Controlando a precisão de texturas intermediárias

Direct2D fornece algumas maneiras de controlar a precisão de um grafo. Antes de usar formatos de alta precisão em Direct2D, os aplicativos devem garantir que eles têm suporte suficiente pela GPU. Para marcar isso, use ID2D1DeviceContext::IsBufferPrecisionSupported.

Os aplicativos podem criar um dispositivo Direct3D usando WARP (emulação de software) para garantir que todas as precisões de buffer sejam compatíveis independentemente do hardware de GPU real no dispositivo. Isso é recomendado em cenários como aplicar efeitos a uma foto ao salvar em disco. Mesmo que Direct2D dê suporte a formatos de buffer de alta precisão na GPU, o uso de WARP é recomendado nesse cenário em GPUs de nível de recurso 9.X, devido à precisão limitada da aritmética do sombreador e à amostragem em algumas GPUs móveis de baixa potência.

Em cada caso abaixo, a precisão solicitada é, na verdade, a precisão mínima que Direct2D usará. Uma precisão mais alta poderá ser usada se intermediários não forem necessários. Direct2D também podem compartilhar texturas intermediárias para diferentes partes do mesmo grafo ou grafos diferentes inteiramente. Nesse caso, Direct2D usa a precisão máxima solicitada para todas as operações envolvidas.

Seleção de precisão de ID2D1DeviceContext::SetRenderingControls

A maneira mais simples de controlar a precisão das texturas intermediárias do Direct2D é usar ID2D1DeviceContext::SetRenderingControls. Isso controla a precisão de todas as texturas intermediárias, desde que uma precisão também não seja definida manualmente em efeitos ou transformações diretamente.

if (Device->IsBufferPrecisionSupported(D2D1_BUFFER_PRECISION_32BPC_FLOAT))
{
  // Get the current rendering controls
  D2D1_RENDERING_CONTROLS renderingControls = {};
  Context->GetRenderingControls(&renderingControls);

  // Switch the precision within the rendering controls and set it
  renderingControls.bufferPrecision = D2D1_BUFFER_PRECISION_32BPC_FLOAT;
  Context->SetRenderingControls(&renderingControls);
}
              

Seleção de precisão de entradas e destinos de renderização

Os aplicativos também podem depender da precisão das entradas para um grafo de efeito para controlar a precisão de texturas intermediárias. Isso é verdadeiro desde que uma precisão de buffer não seja especificada usando ID2D1DeviceContext::SetRenderingControls e não seja definida manualmente em efeitos e transformação diretamente.

As precisões de entradas para efeitos são propagadas por meio do grafo para selecionar a precisão de intermediários downstream. Quando diferentes ramificações no grafo de efeito se encontram, a maior precisão de qualquer entrada é usada.

A precisão selecionada com base em um bitmap Direct2D é determinada a partir de seu formato de pixel. A precisão selecionada para um ID2D1ImageSource é determinada no formato de pixel WIC do IWICBitmapSource subjacente usado para criar o ID2D1ImageSource. Observe que Direct2D não permite que fontes de imagem sejam criadas com fontes WIC usando precisões sem suporte por Direct2D e pela GPU.

É possível que Direct2D não possa atribuir um efeito a uma precisão com base em suas entradas. Isso acontece quando um efeito não tem entradas ou quando um ID2D1CommandList é usado, que não tem precisão específica. Nesse caso, a precisão de texturas intermediárias é determinada do conjunto de bitmap como o destino de renderização atual do contexto.

Seleção de precisão diretamente no efeito e nas transformações

A precisão mínima para texturas intermediárias também pode ser definida em locais explícitos dentro de um grafo de efeito. Isso só é recomendado para cenários avançados.

A precisão mínima pode ser definida usando uma propriedade em um efeito da seguinte maneira:

if (Device->IsBufferPrecisionSupported(D2D1_BUFFER_PRECISION_32BPC_FLOAT))
{
  hr = Effect->SetValue(D2D1_PROPERTY_PRECISION, D2D1_BUFFER_PRECISION_32BPC_FLOAT);
}
              

Dentro de uma implementação de efeito, a precisão mínima pode ser definida usando ID2D1RenderInfo::SetOutputPrecision da seguinte maneira:

if (EffectContext->IsBufferPrecisionSupported(D2D1_BUFFER_PRECISION_32BPC_FLOAT))
{
  hr = RenderInfo->SetOutputBuffer(
  D2D1_BUFFER_PRECISION_32BPC_FLOAT,
  D2D1_CHANNEL_DEPTH_4);
}
              

Observe que o conjunto de precisão em um efeito se propagará para efeitos downstream no mesmo grafo de efeito, a menos que uma precisão diferente seja definida nesses efeitos downstream. O conjunto de precisão em uma transformação dentro de um efeito não afeta a precisão para nós de transformação downstream.

Abaixo está a lógica recursiva completa usada para determinar a precisão mínima de um buffer intermediário que armazena a saída de um determinado nó de transformação:

Lógica de precisão mínima do buffer intermediário