Anunciando o ComputeSharp 2.0 — execute C# na GPU com facilidade através do DirectX 12 e D2D1!

Após mais de 2 anos de trabalho, estou feliz em anunciar que o ComputeSharp 2.0 está finalmente disponível! Esta é uma reescrita completa da biblioteca, apresentando suporte para .NET Standard 2.0 e .NET 6, novos geradores de fonte para substituir totalmente a geração de código em tempo de execução, muitas novas APIs, novo suporte para sombreadores de pixel D2D1 também, controles XAML para UWP e WinUI 3 e muito mais!
Muito esforço foi feito para este lançamento, e o escopo de todo o projeto cresceu muito ao longo do tempo, muito mais do que eu havia previsto originalmente. Nesta postagem do blog, quero apresentar a biblioteca para aqueles que não a conhecem, destacar alguns dos novos recursos e mostrar alguns exemplos do que pode ser feito com ela.
Para aqueles que preferem um formulário de vídeo, aqui está um link para minha palestra sobre ComputeSharp no .NET Conf 2022 , e para aqueles que querem apenas pular direto para o código, aqui está um link para o repositório do GitHub também. Vamos pular para o ComputeSharp 2.0!
Vamos começar com um discurso de elevador para a biblioteca: ComputeSharp é uma biblioteca .NET para executar código C# em paralelo na GPU por meio de DX12, D2D1 e sombreadores de computação HLSL gerados dinamicamente, com o objetivo de tornar a computação de GPU fácil de usar para todos. NET desenvolvedores! Ele permite que você escreva shaders (ou seja, código que pode ser executado na GPU) usando C# e, em seguida, faz todo o trabalho pesado para transformar esse código e realmente despachá-lo para a GPU em uso em sua máquina. Isso pode ser usado para acelerar o hardware de todos os tipos de cargas de trabalho - processamento de imagem, cálculos paralelos, geração de animações sofisticadas, o que você quiser! E tudo sem que você precise se aprofundar nas APIs específicas da plataforma necessárias para fazer isso funcionar, nem aprender como realmente escrever HLSL, que é a linguagem de programação que o DirectX usa para sombreadores.
Para um exemplo prático, aqui está o aplicativo de amostra ComputeSharp em ação:
Aqui você pode ver um dos controles XAML que o ComputeSharp oferece para UWP e WinUI 3 sendo usado para renderizar vários sombreadores de computação animados, neste caso, executando em um aplicativo WinUI 3. Todo o código-fonte está disponível no GitHub , e todos os shaders que você vê foram portados de shaders GLSL de shadertoy para apenas C#, e são todos alimentados por ComputeSharp e executados usando shaders de computação DirectX 12.
E aqui está outro exemplo do ComputeSharp em ação:

Passei os últimos meses adicionando suporte Direct2D ao ComputeSharp também, que agora é um componente fundamental da próxima versão do Paint.NET 5.0 . ComputeSharp.D2D1 (a versão da biblioteca para sombreadores de pixel D2D1) está sendo usado para implementar dezenas de efeitos de imagem no aplicativo, que são totalmente escritos em C# e acelerados por GPU por meio do poder do D2D. Isso permitiu que Rick Brewster , o desenvolvedor do Paint.NET, aumentasse muito o número de efeitos que ele poderia executar na GPU, ao mesmo tempo em que reduzia significativamente a complexidade do aplicativo em relação aos efeitos da GPU D2D, pois não havia mais a necessidade de escrever efeitos manualmente em HLSL e compilá-los com uma etapa de compilação personalizada — a ComputeSharp está fazendo todo esse trabalho nos bastidores!
Curiosidade — o desfoque bokeh exibido na captura de tela do Paint.NET acima é a porta de Rick da minha amostra de desfoque bokeh ComputeSharp.D2D1 , que é uma porta que fiz da minha amostra de desfoque bokeh ComputeSharp , que é uma porta da versão C# que escrevi para ImageSharp , que é uma porta da versão Python que o professor Mike Pound escreveu para seu vídeo Computerphile sobre efeitos de desfoque bokeh . Vá conferir!
E isso também está disponível para autores de plug-ins, o que significa que agora qualquer pessoa também pode usar o ComputeSharp.D2D1 para escrever efeitos totalmente personalizados e acelerados por GPU para integrar no Paint.NET. O que costumava exigir conhecimento interno de HLSL e D2D agora é facilmente acessível a todos os desenvolvedores C#, com apenas algumas linhas de código e APIs amigáveis de alto nível.
Se você está curioso para ver a aparência de um sombreador ComputeSharp, aqui está um muito simples: o sombreador hello world, que apenas exibe um gradiente de cores animado que muda com o tempo. Aqui está o código C # para isso:

Você pode ver como o sombreador é simplesmente um tipo de estrutura C#, que captura alguns valores (neste caso, um float para indicar o tempo passado) e, em seguida, executa alguns cálculos para calcular a cor a ser exibida na saída. Este é o shader completo, que o ComputeSharp cuidará do processamento no momento da compilação por meio de um gerador de origem, para convertê-lo em HLSL e também gerar código adicional para suportar sua execução.
Aqui está a aparência do código HLSL resultante para este sombreador:

É fácil ver algumas das transformações que o ComputeSharp fez nos bastidores: o sombreador agora tem um buffer constante, que é usado para agrupar os valores capturados para a GPU, tem anotações de tamanho de thread para controlar o despacho do sombreador, verificações automáticas de limites têm foi adicionado, e o sombreador também está capturando implicitamente a textura de saída e atribuindo o pixel resultante a ela. Como um desenvolvedor C# usando o ComputeSharp, você nunca terá que se preocupar com nada disso: o gerador de origem incluído no ComputeSharp cuidará de todo esse trabalho pesado enquanto você pode escrever C# e se beneficiar do IntelliSense e de todas as excelentes ferramentas que o C# oferece.
Também quero mostrar alguns exemplos completos de como é a execução do código na GPU usando o ComputeSharp. Vimos como você pode escrever um shader, mas você pode estar se perguntando se despachá-lo é realmente difícil ou não. Não é! E também há várias maneiras de fazer isso, para que você possa escolher a que melhor se adapta às suas necessidades.
O ComputeSharp oferece um modo de execução ágil e um modo de gráfico computacional. O primeiro é particularmente útil se você estiver apenas começando com a biblioteca, quiser experimentar ou apenas precisar executar algum script simples e não quiser se preocupar com a configuração de um pipeline de execução mais complexo. Aqui está como você pode executar um sombreador de teste muito simples:

Isso é tudo que você precisa fazer para executar o código C# na GPU! Você pode ver como temos um sombreador muito simples, que apenas multiplica todos os valores em uma textura por 2 e, em seguida, o despachamos por um buffer gravável que alocamos na GPU com alguns valores aleatórios. Depois de despachar o sombreador, também podemos copiar o buffer da GPU de volta na CPU para que possamos ler seu conteúdo. O ComputeSharp expõe dezenas de APIs muito fáceis de usar para executar operações comuns como essas, que podem se adaptar a todos os cenários, desde scripts mínimos simples até arquiteturas mais complicadas.
Vejamos também um exemplo usando o modo gráfico computacional:

Este modo é perfeito se você deseja criar um pipeline de execução totalmente personalizado que também pode extrair o máximo de desempenho da GPU. Nesse modo, primeiro criamos um ComputeContext , que é um objeto que nos permite enfileirar operações na GPU. Em seguida, estamos criando nosso pipeline (incluindo operações como despacho de shaders, controle de transições para sincronizar acessos a recursos compartilhados e mais) e, finalmente, estamos despachando isso na GPU ao sair do escopo do contexto. Você também pode ver como aqui estamos aproveitando o await usando a sintaxe para executar todo esse trabalho de forma assíncrona na GPU, para não bloquearmos o thread atual até que a GPU termine de processar nossa solicitação.
E o suporte a XAML? O ComputeSharp oferece dois controles ( ComputeShaderPanel e AnimatedComputeShaderPanel ), que facilitam muito a integração de um efeito desenvolvido pelo ComputeSharp em um aplicativo UWP ou WinUI 3: basta colocar o controle XAML em sua exibição, atribuir um objeto de execução de sombreador a ele e você terá você está pronto para ir!

Veja como um ComputeShaderPanel pode ser facilmente declarado em uma exibição. Então, basta uma instância IShaderRunner para saber como renderizar cada quadro. Para casos simples, por exemplo, quando queremos apenas exibir uma animação de shader, o tipo interno ShaderRunner<T> é mais que suficiente:

E é isso! O painel usará o shader runner para desenhar cada quadro e cuidará de toda a configuração necessária para realmente exibir cada quadro gerado. Para cenários mais avançados, você também pode implementar o shader runner por conta própria, o que lhe dá controle total sobre o tempo do quadro, o gráfico de computação usado para produzir cada imagem a ser exibida e muito mais!
Por fim, também quero mostrar um exemplo de sombreador de pixel D2D1. Esses são os shaders que o Paint.NET usa e também fazem parte da infraestrutura em que estou trabalhando atualmente para permitir seu uso em aplicativos UWP e WinUI 3 por meio da biblioteca Win2D (você também pode ver minha proposta para isso aqui ). Eles são muito diferentes dos sombreadores de computação DirectX 12, mas, graças à infraestrutura do ComputeSharp, são igualmente fáceis de implementar e também podem ser executados sem muito esforço, aproveitando o efeito de sombreamento de pixel integrado que o ComputeSharp.D2D1 inclui.
Aqui está um exemplo de efeito pixelado do Paint.NET:

Você notará que há algumas diferenças entre este shader e o shader DX12 mostrado acima, mas a experiência do desenvolvedor é basicamente a mesma: você só precisa criar um tipo C#, usar as projeções HLSL disponíveis e APIs que o ComputeSharp inclui, deixe o IntelliSense guiar você e deixe o ComputeSharp fazer todo o resto do trabalho para você em tempo de compilação.
Vejamos também o código transpilado para este shader:

Mais uma vez, o tipo C# foi transformado conforme necessário para seguir as convenções HLSL necessárias, neste caso especificamente para sombreadores de pixel D2D1. Declaramos campos para mapear os vários valores capturados em C#, todos os intrínsecos foram encaminhados para seus equivalentes HLSL, a sintaxe do sombreador foi atualizada e muito mais. E todos esses shaders, assim como os DX12, também podem ser pré-compilados no momento da compilação, o que significa que seus aplicativos podem ter um desempenho de inicialização extremamente rápido e não há necessidade de invocar ou agrupar o compilador de shader uma vez implantado!
Há muito mais disponível no ComputeSharp, e este também é apenas o primeiro lançamento estável após a reescrita - há muitos novos recursos e melhorias nos quais já estou trabalhando e que pretendo incluir em lançamentos futuros! Você pode conferir o repositório no GitHub , experimentar a biblioteca ( você pode encontrá-la no NuGet! ) e deixar comentários e ideias!
E se você usá-lo e construir algo - por favor, compartilhe! Eu absolutamente amo ver todas as coisas legais que as pessoas podem construir usando o ComputeSharp!