Instância, janela e renderizador Vulkan
Antes de continuarmos com o desenvolvimento do nosso aplicativo usando Vulkan, vamos entender as classes do Qt que precisaremos usar nele.
QVulkanInstance
Como já mencionamos anteriormente, a interação com o Vulkan começa com o objeto de instância do tipo VkInstance
. Um aplicativo geralmente cria um único objeto de instância que contém o estado do aplicativo inteiro. Todos os demais objetos Vulkan só podem ser criados a partir desse objeto. No Qt, a classe correspondente é QVulkanInstance
. Essa classe representa uma instância nativa do Vulkan, permitindo a renderização do Vulkan em um objeto do tipo QSurface
1.
1 A classe
QSurface
é uma abstração de superfícies renderizáveis no Qt.
QVulkanInstance
também fornece uma maneira conveniente de configurar o Vulkan e, em seguida, inicializá-lo com a configuração fornecida. Também podemos usar suas funções supportedExtensions()
e supportedLayers()
para consultar recursos suportados antes de usá-los. Após a configuração ser feita, devemos chamar a função create()
que realmente inicia o carregamento da biblioteca Vulkan e a criação de um objeto VkInstance
. Se essa função retornar true
, o objeto de instância do Vulkan estará pronto para ser usado. Essa forma de inicialização do objeto de instância permite usar QVulkanInstance
como uma variável membro simples, mantendo o controle sobre quando executar a inicialização.
QVulkanWindow
O próximo passo é criar uma janela capaz de renderizar imagens usando Vulkan. Isso é feito definindo uma classe como subclasse de QVulkanWindow
. Essa classe é uma subclasse de QWindow
2 capaz de acessar a API Vulkan que gerencia os seguintes recursos:
- um dispositivo Vulkan do tipo
VkDevice
; - uma fila de gráficos;
- um pool e um buffer de comandos;
- uma imagem de depth-stencil;
- um swapchain FIFO com buffer duplo;
- um framebuffer para cada imagem do swap chain.
QVulkanWindow
também cria um render pass com um subpass responsável por garantir que as imagens do swap chain e de depth-stencil sejam transferidas para um layout apropriado para apresentar essas imagens ao swap chain e um layout otimizado para uso do anexo de depth-stencil. Enquanto cuida do comportamento correto quando se trata de eventos como redimensionar o tamanho da janela, situações especiais como não ter uma fila de dispositivos que suporte gráficos e apresentações ao mesmo tempo, cenários de perda de dispositivos e funcionalidades adicionais, como ler o conteúdo renderizado de volta.
2
QVulkanWindow
nem sempre elimina a necessidade de implementar uma subclasseQWindow
totalmente customizada, pois ela não será necessariamente suficiente em casos de uso avançados. Mas para nosso caso vai funcionar bem.
Também podemos usar funções virtuais herdadas de QWindow
para manipular quaisquer eventos despachados pelo sistema de eventos do Qt. Conceitualmente, é a contraparte do QOpenGLWindow
no mundo Vulkan.
Um objeto do tipo QVulkanWindow
deve sempre ser associado a um objeto do tipo QVulkanInstance
e, portanto, devemos executar a criação da instância antes da janela e usar a função setVulkanInstance()
para configurá-la no objeto QVulkanWindow
. No entanto, as subclasses de QVulkanWindow
não devem executar nenhuma renderização real. Esta tarefa é delegada à classe QVulkanWindowRenderer
. A função virtual QVulkanWindow::createRenderer()
será chamada uma vez depois que a janela é mostrada pela primeira vez, e devemos reimplementar esta função para retornar seu objeto renderizador.
QVulkanWindowRenderer
Agora, sobre o próprio renderizador: QVulkanWindowRenderer
é uma classe simples contendo nada mais que um conjunto de funções virtuais. Ela permite implementar o gerenciamento de recursos do Vulkan e o buffer de comando que compõem a renderização da aplicação (QT, 2018c). Podemos criar nosso próprio renderizador definindo uma classe como subclasse de QVulkanWindowRenderer
e reimplementar a única função virtual pura chamada startNextFrame()
. Esta função será chamada quando a imagem do próximo quadro for solicitado. Podemos executar todas as operações de renderização necessárias nesta função e finalizá-la com uma chamada para QVulkanWindow::frameReady()
para indicar que a imagem está completa. Também podemos reimplementar outras funções virtuais do renderizador. As mais úteis são initResources()
e releaseResources()
, que permitem iniciar os recursos gráficos do renderizador e depois liberá-los quando necessário.
Agora que aprendemos um pouco sobre as classes principais do Qt que precisamos usar em um aplicativo Vulkan, veremos como colocá-las em prática.