Windows com C++

Colocando camadas na janela de alto desempenho usando o mecanismo de composição do Windows

Kenny Kerr

Kenny KerrSou fascinado por janelas em camadas desde a primeira vez que as vi no Windows XP. A capacidade de escapar dos limites da janela da área de trabalho tradicional retangular ou quase retangular, sempre pareceu muito intrigante para mim. E, então surgiu o Windows Vista. O tão terrível lançamento do Windows apontou o começo de algo muito mais emocionante e mais flexível. O Windows Vista começou algo que nós somente começamos a apreciar agora que o Windows 8 está aqui, mas também marcou o declínio lento da janela em camadas.

O Windows Vista introduziu um serviço chamado de Gerenciador de Janelas da Área de Trabalho. O nome foi e continua sendo enganoso. Pense nele como um compositor ou mecanismo de composição do Windows. Este mecanismo de composição mudou completamente a maneira que as janelas de aplicativos são renderizadas na área de trabalho. Ao invés de permitir que cada janela renderize diretamente no vídeo, ou adaptador de vídeo, cada janela renderiza em um buffer ou superfície fora da tela. O sistema aloca uma superfície desse tipo por janela de nível superior e todos os gráficos com GDI, Direct3D e, claro, Direct2D são renderizados para estas superfícies. Estas superfícies fora da tela são chamadas de superfícies de redirecionamento porque os comandos de desenho do GDI e mesmo as solicitações de apresentação da cadeia de troca do Direct3D são redirecionados ou copiados (dentro do GPU) para a superfície de redirecionamento.

Em algum momento, independente de qualquer janela, o mecanismo de composição decide que é hora de compor a área de trabalho devido ao lote mais recente de alterações. Isto envolve a composição de todas estas superfícies de redirecionamento juntas, adicionando as áreas não clientes (geralmente chamadas de cromado da janela), talvez adicionar algumas sombras e outros efeitos e apresentar o resultado final para o adaptador de vídeo.

Este processo de composição tem vários benefícios maravilhosos que explicarei durante os próximos meses enquanto exploro a composição do Windows em mais detalhes, mas ele também tem uma restrição potencialmente séria uma vez que estas superfícies de redirecionamento são opacas. A maioria dos aplicativos estão bem felizes com isto e isto faz bastante sentido a partir de uma perspectiva de desenvolvimento, porque a combinação alfa é cara. Mas isto deixa as janelas em camada na mão.

Se desejo uma janela em camadas, eu tenho que causar um impacto de desempenho. Descrevi as limitações arquiteturais específicas em minha coluna, “Janelas sobrepostas com o Direct2D” (msdn.microsoft.com/magazine/ee819134). Para resumir, as janelas em camadas são processadas pela CPU, principalmente para dar suporte ao teste de hit de pixels com combinação alfa. Isto significa que a CPU precisa de uma cópia dos pixels que compõe a área da superfície da janela em camadas. Ou eu renderizo na CPU, o que tende a ser mais lento do que a renderização pela GPU, ou eu renderizo na GPU, sendo que, neste caso devo pagar a tarifa de banda larga de barramento porque tudo que eu renderizo deve ser copiado de uma memória de vídeo para a memória do sistema. Na coluna exibida anteriormente, eu também mostro como talvez eu possa aproveitar ao máximo o desempenho do Direct2D do sistema, pois somente o Direct2D permite que eu escolha entre a renderização pela CPU e GPU. O problema é que embora a janela em camadas necessariamente reside na memória do sistema, o mecanismo de composição a copia imediatamente para a memória de vídeo, de tal maneira que a composição real da janela em camadas continua acelerada por hardware.

Enquanto eu não posso oferecer a você nenhuma esperança de janelas em camadas tradicionais voltando a ganhar espaço, eu tenho algumas boas notícias. As janelas em camadas tradicionais oferecem dois recursos específicos de interesse. O primeiro é a combinação alfa por pixel. Tudo que renderizo para a janela em camadas será combinado com alfa, com a área de trabalho e com tudo que estiver atrás da janela em um determinado momento. O segundo é a capacidade para o User32 de atingir as janelas em camadas dos testes com base nos valores alfa do pixel, permitindo que as mensagens do mouse falhem se o pixel em um determinado ponto é transparente. A partir do Windows 8 e 8.1, o User32 não tem alterado de forma significativa, mas o que mudou foi a capacidade de dar suporte a combinação alfa por pixel apenas na GPU e sem o custo de transmitir a superfície da janela para a memória do sistema. Isto significa que agora eu posso produzir o efeito de uma janela em camadas sem comprometer o desempenho, desde que eu não precise do teste de hit por pixel. Toda a janela passará por um teste de hit de maneira uniforme. Deixando de lado o teste de hit, isto me empolga porque é algo que o sistema obviamente pode fazer, mas nunca foi possível para os aplicativos aproveitar este poder. Se isso parece intrigante para você, então leia e mostrarei para você como é feito.

A chave para fazer isto acontecer envolve adotar o mecanismo de composição do Windows. O mecanismo de composição primeiro apareceu no Windows Vista como o Gerenciador de Janelas da Área de Trabalho com sua API limitada e seu efeito de vidro Aero transparente popular. Então o Windows 8 surgiu e introduziu a API DirectComposition. Esta é apenas uma API mais extensiva para o mesmo mecanismo de composição. Com o lançamento do Windows 8, a Microsoft finalmente permitiu que desenvolvedores de terceiros aproveitassem o poder deste mecanismo de composição que tem estado por aí por tanto tempo. E, claro, você precisará adotar uma API de gráficos ativada pelo Direct3D, como o Direct2D. Mas primeiro você precisa lidar com aquela superfície de redirecionamento opaca.

Como mencionei antes, o sistema aloca uma superfície de redirecionamento para cada janela de nível superior. Desde o Windows 8, você pode agora criar uma janela de nível superior e solicitar que ela seja criada sem uma superfície de redirecionamento. A rigor, isto não tem nada a ver com as janelas em camada, então não use o estilo estendido de janela WS_EX_LAYERED. (O suporte para as janelas em camada, na verdade, ganharam uma pequena melhoria no Windows 8, mas olharei isto mais de perto na próxima coluna.) Ao invés, você precisa usar o estilo estendido de janela WS_EX_NOREDIRECTIONBITMAP que diz para o mecanismo de composição não alocar uma superfície de redirecionamento para a janela. Vou começar com uma janela da área de trabalho tradicional e simples. A Figura 1 fornece um exemplo de preenchimento em uma estrutura WNDCLASS, registrando a classe da janela, criando a janela e bombeando mensagens de janela. Nada novo aqui, mas estes conceitos básicos continuam sendo essenciais. A variável da janela acaba não sendo usada, mas você precisará dela em um momento. Você pode copiar isto em um projeto do Visual C++ dentro do Visual Studio ou apenas compilá-la a partir do prompt de comando como a seguir:

cl /W4 /nologo Sample.cpp

Figura 1 Criando uma janela tradicional

#ifndef UNICODE #define UNICODE #endif #include <windows.h> #pragma comment(lib, "user32.lib") int __stdcall wWinMain(HINSTANCE module, HINSTANCE, PWSTR, int) { WNDCLASS wc = {}; wc.hCursor       = LoadCursor(nullptr, IDC_ARROW); wc.hInstance     = module; wc.lpszClassName = L"window"; wc.style         = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = [] (HWND window, UINT message, WPARAM wparam, LPARAM lparam) -> LRESULT { if (WM_DESTROY == message) { PostQuitMessage(0); return 0; } return DefWindowProc(window, message, wparam, lparam); }; RegisterClass(&wc); HWND const window = CreateWindow(wc.lpszClassName, L"Sample", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, module, nullptr); MSG message; while (BOOL result = GetMessage(&message, 0, 0, 0)) { if (-1 != result) DispatchMessage(&message); } }

A Figura 2 mostra como isto se parece na minha área de trabalho. Observe que não tem nada de incomum aqui. Enquanto o exemplo não fornece nenhum comando de renderização ou de pintura, a área do cliente da janela é opaca e o mecanismo de composição adiciona à área não cliente, a borda e a barra de título. Ao aplicar o estilo estendido de janela WS_EX_NOREDIRECTIONBITMAP para se livrar da superfície de redirecionamento opaca que representa esta área do cliente é simplesmente uma questão de comutar a função CreateWindow para a função CreateWindowEx com seu parâmetro precedente que aceita estilos estendidos de janela:

 

HWND const window = CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP, wc.lpszClassName, L"Sample", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, module, nullptr);

A Traditional Window on the Desktop
Figura 2 Uma janela tradicional na área de trabalho

As únicas coisas que mudaram são a adição do argumento precedente, do estilo estendido de janela WS_EX_NOREDIRECTIONBITMAP e, claro, o uso da função CreateWindowEx ao invés da função mais simples CreateWindow. Os resultados na área de trabalho são, no entanto, muito mais radicais. A Figura 3 mostra como isto se parece na minha área de trabalho. Observe que a área do cliente da janela agora está completamente transparente. Mover a janela ilustrará este fato. Eu posso até ter um vídeo reproduzindo no fundo e não será obscurecido nem um pouco. Por outro lado, a janela inteira passará pelos testes de hit de maneira uniforme e o foco da janela não está perdido ao clicar dentro da área do cliente. Isto ocorre porque o subsistema responsável pelo teste de hit e pela entrada de mouse não está ciente de que a área do cliente está transparente.

A Window Without a Redirection Surface
Figura 3 Uma janela sem uma superfície de redirecionamento

Claro, a próxima pergunta é como você possivelmente pode processar qualquer coisa para a janela se não há superfície de redirecionamento para fornecer ao mecanismo de composição? A resposta vem da API DirectComposition e a sua integração profunda com o DirectX Graphics Infrastructure (DXGI). É a mesma técnica que ativa a implementação do Windows 8.1 XAML para fornecer uma composição com alto desempenho incrível de conteúdo dentro de um aplicativo XAML. O mecanismo de renderização do Internet Explorer Trident também usa a DirectComposition extensivamente para o movimento panorâmico e zoom de toque, bem como animações CSS3, transições e transformações.

Eu apenas vou usá-lo para compor uma cadeia de troca que forneça suporte à transparência com valores alfa pré-multiplicados em uma base por pixel e combiná-lo com o resto da área de trabalho. Os aplicativos DirectX tradicionais normalmente criam uma cadeia de troca com o método CreateSwapChainForHwnd do alocador do DXGI. A cadeia de troca é apoiada por um par ou coleção de buffers que seriam efetivamente trocados durante a apresentação, permitindo ao aplicativo renderizar o próximo quadro enquanto o quadro anterior é copiado. A superfície de cadeia de troca que o aplicativo processa é um buffer fora da tela opaco. Quando o aplicativo apresenta a cadeia de troca, o DirectX copia o conteúdo do buffer de apoio da cadeia de troca para a superfície de redirecionamento da janela. Com mencionei antes, o mecanismo de composição eventualmente compõe todas as superfícies de redirecionamento juntas para produzir a área de trabalho como um todo.

Neste caso, a janela não tem superfície de redirecionamento, então o método CreateSwapChainForHwnd do alocador do DXGI não pode ser usado. No entanto, eu ainda preciso de uma cadeia de troca para dar suporte ao processamento do Direct3D e Direct2D. É para isso que o método CreateSwapChainForComposition do alocador do DXGI serve. Posso usar este método para criar uma cadeia de troca sem janelas, juntamente com buffers, mas apresentar esta cadeia de troca não copia os bits para a superfície de redirecionamento (que não existe), mas, ao invés, a torna disponível para o mecanismo de composição diretamente. O mecanismo de composição pode então pegar essa superfície e usá-la diretamente e ao invés da superfície de redirecionamento da janela. Porque esta superfície não é opaca, mas seu formato de pixel é totalmente compatível com os valores alfa pré-multiplicados por pixel, o resultado é uma combinação perfeita de alfa por pixel na área de trabalho. É também incrivelmente rápido, pois não há cópia desnecessária dentro da GPU e certamente nenhuma cópia sobre o barramento para a memória do sistema.

Essa é a teoria. Agora, é hora de fazer isso acontecer. O DirectX se refere aos elementos essenciais do COM, então vou usar o modelo de classe da Biblioteca de Modelo C++ do Tempo de Execução do Windows para gerenciar os ponteiros de interface. Eu também precisarei incluir e vincular as APIs DXGI, Direct3D, Direct2D e DirectComposition. O código a seguir mostra como isso é feito:

#include <wrl.h> using namespace Microsoft::WRL; #include <dxgi1_3.h> #include <d3d11_2.h> #include <d2d1_2.h> #include <d2d1_2helper.h> #include <dcomp.h> #pragma comment(lib, "dxgi") #pragma comment(lib, "d3d11") #pragma comment(lib, "d2d1") #pragma comment(lib, "dcomp")

Normalmente, eu incluo estes em meu cabeçalho pré-compilado. Nesse caso, eu omitiria a diretiva que está sendo usada e somente incluiria aquela em meu arquivo de origem do aplicativo.

Eu odeio os exemplos de códigos em que a manipulação de erros sobrecarrega e desvia das especificações do próprio tópico, então eu também armazenarei isto com uma classe de exceção e uma função HR para verificar os erros. Você pode encontrar uma documentação simples na Figura 4, mas, claro, você pode decidir na sua própria política de manipulação de erros.

Figura 4 Transformando os erros HRESULT em execeções

struct ComException { HRESULT result; ComException(HRESULT const value) : result(value) {} }; void HR(HRESULT const result) { if (S_OK != result) { throw ComException(result); } }

Agora eu posso começar a montar a pilha de renderização e isso naturalmente começa com um dispositivo Direct3D. Vou passar por isso rapidamente, pois já descrevi a infraestrutura do DirectX em detalhes em minha coluna de março de 2013, “Apresentando o Direct2D 1.1” (msdn.microsoft.com/magazine/dn198239). Aqui está o ponteiro de interface do Direct3D 11:

ComPtr<ID3D11Device> direct3dDevice;

Este é o ponteiro de interface para o dispositivo e a função D3D11Create­Device pode ser usada para criar o dispositivo:

HR(D3D11CreateDevice(nullptr,    // Adapter D3D_DRIVER_TYPE_HARDWARE, nullptr,    // Module D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, // Highest available feature level D3D11_SDK_VERSION, &direct3dDevice, nullptr,    // Actual feature level nullptr));  // Device context

Não há nada muito surpreendente aqui. Estou criando um dispositivo Direct3D apoiado por uma GPU. O sinalizador D3D11_CREATE_DEVICE_BGRA_SUPPORT habilita a interoperabilidade com o Direct2D. A família do DirectX e mantida junto com o DXGI, que fornece gerenciamento de recursos por várias APIs do DirectX. No entanto, preciso consultar o dispositivo Direct3D para sua interface DXGI:

ComPtr<IDXGIDevice> dxgiDevice; HR(direct3dDevice.As(&dxgiDevice));

O método ComPtr As é apenas um invólucro para o método QueryInterface. Com o dispositivo Direct3D criado, eu posso então criar a cadeia de troca que será usada para a composição. Para fazer isso, primeiro preciso obter o alocador do DXGI:

ComPtr<IDXGIFactory2> dxFactory; HR(CreateDXGIFactory2( DXGI_CREATE_FACTORY_DEBUG, __uuidof(dxFactory), reinterpret_cast<void **>(dxFactory.GetAddressOf())));

Aqui, estou escolhendo informações de depuração adicionais—uma ajuda valiosa durante o desenvolvimento. A parte mais difícil de criar uma cadeia de troca é descobrir como descrever a cadeia de troca desejada para o alocador do DXGI. Esta informação de depuração é imensamente útil no ajuste fino da estrutura DXGI_SWAP_CHAIN_DESC1 necessária:

DXGI_SWAP_CHAIN_DESC1 description = {};

Isto inicia a estrutura para todos os zeros. Eu posso então começar a preencher qualquer propriedade interessante:

description.Format           = DXGI_FORMAT_B8G8R8A8_UNORM; description.BufferUsage      = DXGI_USAGE_RENDER_TARGET_OUTPUT; description.SwapEffect       = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; description.BufferCount      = 2; description.SampleDesc.Count = 1; description.AlphaMode        = DXGI_ALPHA_MODE_PREMULTIPLIED;

O formato específico, um formato do pixel de 32 bits com 8 bits para cada canal de cor juntamente com um componente alfa de 8 bits pré-multiplicado, é a sua única opção, mas fornece o melhor desempenho e compatibilidade nos dispositivos e APIs.

O uso do buffer da cadeia de troca deve ser definido para permitir renderizar o destino para ser redirecionado a ele. Isto é necessário para que o contexto do dispositivo Direct2D possa criar um bitmap para visar a superfície DXGI com os comandos de desenho. O próprio bitmap do Direct2D é meramente uma abstração apoiado pela cadeia de troca.

As cadeias de troca de composição somente oferecem suporte ao efeito de troca sequencial de flip. Esta é a forma que a cadeia de troca relaciona o mecanismo de composição ao invés de uma superfície de redirecionamento. No modelo flip, todos os buffers são compartilhados diretamente com o mecanismo de composição. O mecanismo de composição pode então compor a área de trabalho diretamente do buffer de fundo da cadeia de troca sem cópia adicional. Este é geralmente o modelo mais eficiente. Ele também é necessário para a composição, então é isso que uso. O modelo flip também necessariamente exige pelo menos dois buffers, mas não fornece suporte ao multisampling, então o BufferCount é configurado para dois e o SampleDesc.Count é configurado para um. Esta contagem é o número de multisamples por pixel. Configurar esta para um efetivamente desabilita o multisampling.

Finalmente, o modo alfa é essencial. Isto normalmente seria ignorado para as cadeias de troca opacas, mas neste caso eu realmente desejo que transparência no comportamento seja incluída. Os valores alfa pré-multiplicados normalmente fornecem o melhor desempenho e também é a única opção compatível com o modelo flip.

O ingrediente final antes que eu possa criar uma cadeia de troca é determinar o tamanho desejado dos buffers. Normalmente, ao chamar o método CreateSwapChainForHwnd, eu posso ignorar o tamanho e o alocador do DXGI consultará a janela em relação ao tamanho da área do cliente. Nesse caso, o DXGI não tem ideia o que eu planejo fazer com a cadeia de troca, então eu preciso informá-lo especificamente que tamanho ela tem que ser. Com a janela criada, isto é uma simples questão de consultar a área de trabalho da janela e atualizar a descrição da cadeia de troca de forma adequada:

RECT rect = {}; GetClientRect(window, &rect); description.Width  = rect.right - rect.left; description.Height = rect.bottom - rect.top;

Agora eu posso criar a cadeia de troca de composição com esta descrição e criar um ponteiro para o dispositivo Direct3D. Os ponteiros de interface do Direct3D ou DXGI podem ser usados:

ComPtr<IDXGISwapChain1> swapChain; HR(dxFactory->CreateSwapChainForComposition(dxgiDevice.Get(), &description, nullptr, // Don’t restrict swapChain.GetAddressOf()));

Agora que a cadeia de troca foi criada, eu posso usar qualquer código de renderização de gráficos do Direct3D ou Direct2D para desenhar o aplicativo, usando valores alfa conforme necessário para criar a transparência desejada. Não há nada de novo aqui, então eu encaminharei você novamente para a minha coluna de março de 2013 para as especificações da renderização para uma cadeia de troca com o Direct2D. A Figura 5 fornece um exemplo simples se você estiver acompanhando. Apenas não esqueça de dar suporte ao reconhecimento de DPI por monitor como descrevi na minha coluna de fevereiro de 2014, “Escreva aplicativos com alto DPI para o Windows 8.1” (msdn.microsoft.com/magazine/dn574798).

Figura 5 Desenho da cadeia de troca com o Direct2D

// Create a single-threaded Direct2D factory with debugging information ComPtr<ID2D1Factory2> d2Factory; D2D1_FACTORY_OPTIONS const options = { D2D1_DEBUG_LEVEL_INFORMATION }; HR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, options, d2Factory.GetAddressOf())); // Create the Direct2D device that links back to the Direct3D device ComPtr<ID2D1Device1> d2Device; HR(d2Factory->CreateDevice(dxgiDevice.Get(), d2Device.GetAddressOf())); // Create the Direct2D device context that is the actual render target // and exposes drawing commands ComPtr<ID2D1DeviceContext> dc; HR(d2Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, dc.GetAddressOf())); // Retrieve the swap chain's back buffer ComPtr<IDXGISurface2> surface; HR(swapChain->GetBuffer( 0, // index __uuidof(surface), reinterpret_cast<void **>(surface.GetAddressOf()))); // Create a Direct2D bitmap that points to the swap chain surface D2D1_BITMAP_PROPERTIES1 properties = {}; properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; properties.pixelFormat.format    = DXGI_FORMAT_B8G8R8A8_UNORM; properties.bitmapOptions         = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW; ComPtr<ID2D1Bitmap1> bitmap; HR(dc->CreateBitmapFromDxgiSurface(surface.Get(), properties, bitmap.GetAddressOf())); // Point the device context to the bitmap for rendering dc->SetTarget(bitmap.Get()); // Draw something dc->BeginDraw(); dc->Clear(); ComPtr<ID2D1SolidColorBrush> brush; D2D1_COLOR_F const brushColor = D2D1::ColorF(0.18f,  // red 0.55f,  // green 0.34f,  // blue 0.75f); // alpha HR(dc->CreateSolidColorBrush(brushColor, brush.GetAddressOf())); D2D1_POINT_2F const ellipseCenter = D2D1::Point2F(150.0f,  // x 150.0f); // y D2D1_ELLIPSE const ellipse = D2D1::Ellipse(ellipseCenter, 100.0f,  // x radius 100.0f); // y radius dc->FillEllipse(ellipse, brush.Get()); HR(dc->EndDraw()); // Make the swap chain available to the composition engine HR(swapChain->Present(1,   // sync 0)); // flags

Agora eu finalmente posso começar a usar a API do DirectComposition para unir tudo. Enquanto o mecanismo de composição do Windows lida com a renderização e composição da área de trabalho como um todo, a API do DirectComposition permite que você use esta mesma tecnologia para compor elementos visuais para seus aplicativos. Os aplicativos compõem juntos diferentes elementos, chamados de elementos visuais, para produzir a aparência da própria janela do aplicativo. Estes elementos visuais podem ser animados e transformados de várias maneiras para produzir interfaces de usuário sofisticadas e fluidas. O próprio processo de composição é também realizado juntamente com a composição da área de trabalho como um todo, então mais da apresentação do seu aplicativo é retirado dos threads do aplicativo para o aumento da capacidade de resposta.

O DirectComposition é essencialmente sobre compor diferentes bitmaps juntos. Como com o Direct2D, o conceito de um bitmap aqui é mais uma abstração para permitir pilhas de renderização diferentes para cooperar e produzir aplicativos UXes simples e atraentes.

Como o Direct3D e Direct2D, o DirectComposition é uma API do DirectX que é apoiada e ativada pela GPU. Um dispositivo DirectComposition é criado ao apontar de volta para o dispositivo Direct3D, quase da mesma maneira que um dispositivo Direct2D aponta de volta para o dispositivo Direct3D subjacente. Eu uso o mesmo dispositivo Direct3D que usei anteriormente para criar a cadeia de troca e o destino de renderização do Direct2D para criar um dispositivo DirectComposition:

ComPtr<IDCompositionDevice> dcompDevice; HR(DCompositionCreateDevice( dxgiDevice.Get(), __uuidof(dcompDevice), reinterpret_cast<void **>(dcompDevice.GetAddressOf())));

A função DCompositionCreateDevice espera a interface do DXGI do dispositivo Direct3D e retorna um ponteiro de interface do IDCompositionDevice para o dispositivo DirectComposition recém-criado. O dispositivo DirectComposition atua com um alocador para os objetos do DirectComposition e fornece o método Commit importante que confirma o lote de renderização de comandos para o mecanismo de composição para apresentação e composição eventuais.

Em seguida, eu preciso criar um destino da composição que associa os elementos visuais que serão compostos com o destino, que é a janela do aplicativo:

ComPtr<IDCompositionTarget> target; HR(dcompDevice->CreateTargetForHwnd(window, true, // Top most target.GetAddressOf()));

O primeiro parâmetro do método CreateTargetForHwnd é identificador de janela retornado pela função CreateWindowEx. O segundo parâmetro indica como os elementos visuais serão combinados com qualquer outro elemento da janela. O resultado é um ponteiro de interface IDCompositionTarget cujo o único método é chamado de SetRoot. Permite que eu configure o visual raiz em um possível árvore de elementos visuais para serem compostos juntos. Eu não preciso de um árvore visual completa, mas eu preciso pelo menos de um objeto visual, e para isso eu posso mais uma vez ver o dispositivo DirectComposition:

ComPtr<IDCompositionVisual> visual; HR(dcompDevice->CreateVisual(visual.GetAddressOf()));

O elemento visual contém uma referência para um bitmap e fornece um conjunto de propriedades que afetam como aquele elemento visual será renderizado e composto com relação aos outros elementos visuais na árvore e no próprio destino. Eu já tenho o conteúdo que desejo que este elemento visual transporte para o mecanismo de composição. É a cadeia de troca que foi criada antes:

HR(visual->SetContent(swapChain.Get()));

O elemento visual está pronto e eu posso simplesmente defini-lo como a raiz para o destino da composição:

HR(target->SetRoot(visual.Get()));

Finalmente, quando a forma da árvore visual tiver sido estabelecida, eu simplesmente posso informar o mecanismo de composição que estou pronto ao chamar o método Commit no dispositivo DirectComposition:

HR(dcompDevice->Commit());

Para este aplicativo específico, onde a árvore visual não se altera, eu preciso apenas chamar o Commit uma vez no início do aplicativo e nunca mais. Originalmente eu presumi que o método Commit precisava ser chamado após apresentar a cadeia de troca, mas esse não é o caso porque a apresentação da cadeia de troca não está sincronizada com as alterações para a árvore visual.

A Figura 6 mostra como a janela do aplicativo se parece agora que o Direct2D renderizou a cadeia de troca e o Direct­Composition forneceu a cadeia de troca parcialmente transparente ao mecanismo de composição.

Direct2D Drawing on a DirectComposition Surface
Figura 6 Desenho do Direct2D em uma superfície do DirectComposition

Estou empolgado de finalmente ter uma solução para um problema antigo: a capacidade de produzir janelas de alto desempenho que são combinadas com alfa com o resto da área de trabalho. Estou empolgado com as possibilidades que a API do DirectComposition habilita e o que ela significa para o futuro do design UX do aplicativo e do desenvolvimento em código nativo.

Deseja desenhar seu próprio cromado da janela? Não tem problema; apenas substitua o estilo da janela WS_OVERLAPPEDWINDOW pelo estilo da janela WS_POPUP ao criar a janela. Boa codificação.

Kenny Kerr é programador de computador, assim como autor da Pluralsight e Microsoft MVP que mora no Canadá. Ele mantém um blog em kennykerr.ca e pode ser seguido no Twitter em twitter.com/kennykerr.

Agradecemos ao seguinte especialista técnico pela revisão deste artigo: Leonardo Blanco (Microsoft)