Então, estou desenvolvendo o DirectX, usando o SharpDX no .NET para ser exato (mas as soluções da API DirectX / C ++ são aplicáveis). Estou procurando a maneira mais rápida de renderizar linhas em uma projeção ortogonal (por exemplo, simulando o desenho de linhas 2D para aplicativos científicos) usando o DirectX.
Segue uma captura de tela dos tipos de plotagens que estou tentando renderizar:
Não é incomum que esse tipo de plotagem tenha linhas com milhões de segmentos, de espessura variável, com ou sem antialiasing por linha (ou AA de tela cheia ativada / desativada). Preciso atualizar os vértices para as linhas com muita frequência (por exemplo, 20 vezes / segundo) e descarregar o máximo possível para a GPU.
Até agora eu tentei:
- Renderização de software, por exemplo, GDI +, na verdade, não apresenta desempenho ruim, mas obviamente é pesado na CPU
- API Direct2D - mais lenta que a GDI, especialmente com Antialiasing em
- Direct3D10 usando esse método para emular AA usando cores de vértice e mosaico no lado da CPU. Também lento (criei um perfil e 80% do tempo é gasto computando posições de vértice)
Para o terceiro método, estou usando os Vertex Buffers para enviar uma faixa triangular para a GPU e atualizando a cada 200ms com novos vértices. Estou obtendo uma taxa de atualização de cerca de 5FPS para 100.000 segmentos de linha. Eu preciso de milhões idealmente!
Agora, estou pensando que a maneira mais rápida seria fazer o mosaico na GPU, por exemplo, em um Geometry Shader. Eu poderia enviar os vértices como uma lista de linhas ou empacotar em uma textura e descompactar em um Geometry Shader para criar os quadríceps. Ou envie apenas pontos brutos para um pixel shader e implemente o desenho da linha Bresenham inteiramente em um pixel shader. Meu HLSL é um modelo de shader enferrujado 2 de 2006, então eu não sei sobre as coisas loucas que as GPUs modernas podem fazer.
Portanto, a pergunta é: - alguém já fez isso antes e você tem alguma sugestão para tentar? - Você tem alguma sugestão para melhorar o desempenho com a atualização rápida da geometria (por exemplo, nova lista de vértices a cada 20ms)?
ATUALIZAÇÃO 21 de jan
Desde então, implementei o método (3) acima usando shaders de geometria usando LineStrip e Dynamic Vertex Buffers. Agora estou recebendo 100FPS em 100k pontos e 10FPS em 1.000.000 pontos. Essa é uma grande melhoria, mas agora estou com taxa de preenchimento e computação limitadas, então pensei em outras técnicas / idéias.
- E a instância de hardware de uma geometria de segmento de linha?
- E o Sprite Batch?
- E quanto a outros métodos orientados (pixel shader)?
- Posso selecionar com eficiência a GPU ou CPU?
Seus comentários e sugestões muito apreciados!
Respostas:
Se você for renderizar
Y = f(X)
apenas gráficos, sugiro tentar o seguinte método.Os dados da curva são passados como dados de textura , tornando-os persistentes e permitindo atualizações parciais,
glTexSubImage2D
por exemplo. Se você precisar rolar, poderá implementar um buffer circular e atualizar apenas alguns valores por quadro. Cada curva é renderizada como um quad de tela cheia e todo o trabalho é feito pelo pixel shader.O conteúdo da textura de um componente pode ficar assim:
O trabalho do pixel shader é o seguinte:
41.3
ele escolheria40
,41
,42
e43
.X,Y
pares em espaço na telaVocê pode substituir 4 por valores maiores, dependendo do nível de zoom em potencial.
Eu escrevi um shader GLSL muito rápido e sujo implementando esse recurso . Posso adicionar a versão HLSL posteriormente, mas você poderá convertê-la sem muito esforço. O resultado pode ser visto abaixo, com diferentes tamanhos de linha e densidades de dados:
Uma vantagem clara é que a quantidade de dados transferidos é muito baixa e o número de chamadas de chamada é apenas um.
fonte
Havia um capítulo da GPU Gems sobre a renderização de linhas sem suavização de linhas: Linhas pré-filtradas rápidas . A idéia básica é renderizar cada segmento de linha como um quad e calcular, em cada pixel, uma função gaussiana da distância do centro de pixels ao segmento de linha.
Isso significa desenhar cada segmento de linha no gráfico como um quad separado, mas no D3D11 você certamente poderia usar um shader de geometria e / ou instanciamento para gerar os quads, reduzindo a quantidade de dados a serem transferidos para a GPU para apenas os pontos de dados si mesmos. Provavelmente eu configuraria os pontos de dados como um StructuredBuffer para ser lido pelo sombreador de vértice / geometria e, em seguida, faria uma chamada de empate especificando o número de segmentos a serem desenhados. Não haveria nenhum buffer de vértice real; o sombreador de vértice usaria apenas SV_VertexID ou SV_InstanceID para determinar quais pontos de dados examinar.
fonte
@ Dr.ABT - Desculpas por isso é uma pergunta, não uma resposta. Não encontrei uma maneira de fazer isso na resposta de Sam Hocevar acima.
Você poderia compartilhar mais detalhes sobre como finalmente implementou as linhas do seu gráfico? Tenho a mesma necessidade de um aplicativo WPF. Agradecemos antecipadamente por todos os detalhes ou códigos que você pode compartilhar sobre isso.
fonte