Renderização fora da tela do Direct2d e aceleração de hardware

7

Estou tentando usar o direct2d para renderizar imagens fora da tela usando o WindowsAPICodePack . Isso é facilmente alcançado usando o WicBitmapRenderTarget, mas infelizmente não é acelerado por hardware.

Então, eu estou tentando esta rota:

  1. Criar dispositivo direct3d
  2. Criar texture2d
  3. Use a superfície da textura para criar um destino de renderização usando CreateDxgiSurfaceRenderTarget
  4. Desenhe algumas formas

Enquanto isso renderiza a imagem, parece que a GPU não está sendo usada, enquanto a CPU é muito usada.

Estou fazendo algo errado? Existe uma maneira de verificar se a renderização de hardware ou software é usada?

Exemplo de código:

var device = D3DDevice1.CreateDevice1(null,
                DriverType.Hardware,
                null,
                CreateDeviceOptions.SupportBgra,
                FeatureLevel.Ten
                );

var txd = new Texture2DDescription();
txd.Width = 256;
txd.Height = 256;
txd.MipLevels = 1;
txd.ArraySize = 1;
txd.Format = Format.B8G8R8A8UNorm; //DXGI_FORMAT_R32G32B32A32_FLOAT;
txd.SampleDescription = new SampleDescription(1,0);
txd.Usage = Usage.Default;
txd.BindingOptions = BindingOptions.RenderTarget | BindingOptions.ShaderResource;
txd.MiscellaneousResourceOptions = MiscellaneousResourceOptions.None;
txd.CpuAccessOptions = CpuAccessOptions.None;

var tx = device.CreateTexture2D(txd);

var srfc = tx.GraphicsSurface;

var d2dFactory = D2DFactory.CreateFactory();

var renderTargetProperties = new RenderTargetProperties
{
    PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Premultiplied),
    DpiX = 96,
    DpiY = 96,
    RenderTargetType = RenderTargetType.Default,
};

using(var renderTarget = d2dFactory.CreateGraphicsSurfaceRenderTarget(srfc, renderTargetProperties))
{
    renderTarget.BeginDraw();
    var clearColor = new ColorF(1f,1f,1f,1f);
    renderTarget.Clear(clearColor);
    using (var strokeBrush = renderTarget.CreateSolidColorBrush(new ColorF(0.2f,0.2f,0.2f,1f)))
    {
        for (var i = 0; i < 100000; i++)
        {
            renderTarget.DrawEllipse(new Ellipse(new Point2F(i, i), 10, 10), strokeBrush, 2);
        }
    }

    var hr = renderTarget.EndDraw();
}
Goran
fonte

Respostas:

5

O Direct2D está usando a GPU, a menos que você tenha especificado não usá-la ... mas, infelizmente, o Direct2D não é tão eficiente quanto seria de esperar. Havia uma pergunta semelhante no fórum gamedev " A maneira mais rápida de desenhar linhas conectadas? " Eu executei novamente o teste no seu caso de teste e os resultados são novamente um pouco assustadores ...

Quando você está tentando desenhar uma elipse 10000 na tela (tamanho 10,10), a GPU está efetivamente recebendo:

  • 1.500.000 vértices => 500.000 triângulos a serem renderizados, se você desativar o modo de suavização de serrilhado no destino da renderização
  • 4.411.734 vértices => 1.470.578 triângulos a serem renderizados, com o modo antialias ativado (por padrão, no seu caso)

Como a geometria não é conhecida antecipadamente, sempre que você chama DrawEllipse entre renderTarget.BeginDraw e EndDraw, a parte do código do Direct2D que precisa ser executada na CPU é bastante intensa, pois precisa gerar todos esses vértices (para triangularizar o toda a superfície e gere pequenos triângulos para cobrir a linha externa da elipse) e envie todos os comandos para a GPU (várias centenas de comandos do Direct3D10). Na verdade, cada triângulo cobre quase uma região de pixels.

Você pode diminuir o uso da CPU ativando o modo AntiAlias, mas precisará usar os shaders de pós-efeitos do AntiAlias ​​para reduzir o alias.

Para verificar isso sozinho, você pode executar seu aplicativo com o depurador PIX disponível no DirectX SDK.

Em uma nota lateral, você não deve usar o WindowsAPICodePack, pois é uma API que não é realmente eficiente e não é mantida ativamente. Sugiro que você use o SharpDX , que oferece suporte a todos os recursos do Direct2D / DirectWrite (incluindo retornos de chamada, por exemplo, para mosaico, que não são suportados por nenhuma outra API gerenciada).

xoofx
fonte
Obrigado por tomar o tempo. E obrigado pela sugestão da SharpDX. Vou testar o projeto no meu caso ...
Goran
Sua resposta cobre basicamente, mas há uma terceira opção para AA. Você pode desativar o D2D AntiAliasing, mas renderizar em um destino MSAA D3D e usar o ResolveSubresource. Descobri que isso resulta em qualidade semelhante ao método analítico do D2D, mas com muito melhor desempenho.
23411 Lucas
@Lucas, na verdade, para MSAA como Ele requer menos configuração do que usar pós-efeitos técnicas de anti-aliasing, como FXAA, SMAA ... etc.
xoofx
1

Ele usa aceleração de hardware, mas a GPU é usada muito menos porque não há desenho na área visível. O uso da GPU depende do desempenho das placas; portanto, se você tentar isso com recursos avançados, haverá muito pouco uso da GPU. Quando você tenta isso em um nível baixo (com energia suficiente da CPU), o uso aumenta magicamente.

Portanto, o uso da GPU informa se o acesso está ativado. Se RenderTargetType for o padrão, a aceleração será usada se houver suporte. Se você configurar o hardware, ele será usado ou ocorrerá um erro.

De qualquer forma, se você não estiver usando o Direct3D, use BitmapRenderTarget e d2dbitmap como um buffer fora da tela. É mais rápido.

Savier
fonte