Copiando o buffer para a imagem
Antes de voltarmos na função addTextureImage
, vamos escrever mais uma função auxiliar chamada copyBufferToImage
que irá copiar os dados de um buffer para um objeto de imagem:
void Renderer::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
endSingleTimeCommands(commandBuffer);
}
De forma semelhante às copias de buffers, precisamos especificar qual parte do buffer será copiada para qual parte da imagem. Isso é feito por meio da estrutura VkBufferImageCopy
:
VkBufferImageCopy region = {};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
O campo bufferOffset
dessa estrutura especifica o deslocamento desde o início da memória de um buffer a partir da qual os dados devem ser copiados. Os campos bufferRowLenght
e bufferImageHeight
definem, respectivamente, o comprimento de dados que representa uma única linha no buffer e a altura da imagem imaginária armazenada no buffer. Por exemplo, podemos ter alguns bytes de preenchimento entre as linhas da imagem. Se bufferRowLength
for zero, presume-se que a imagem esteja compactada no buffer e, portanto, igual a largura da imagem de destino. Da mesma forma, se bufferImageHeight
for zero, o número de linhas na imagem de origem será igual à altura da extensão da imagem. Especificar 0
para ambos indica que os pixels são simplesmente compactados, como são no nosso caso.
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
As partes da imagem que queremos modificar são especificadas no campo imageSubresource
. Este é uma instância da estrutura VkImageSubresourceLayers
. O campo aspectMask
dessa estrutura contém o aspecto ou aspectos que são o destino da cópia da imagem. Normalmente, isso será um único bit da enumeração VkImageAspectFlagBits
. Se a imagem de destino for uma imagem colorida, como no nosso caso, isso deve ser definido simplesmente como VK_IMAGE_ASPECT_COLOR_BIT
.
region.imageSubresource.mipLevel = 0;
O nível do mapeamento MIP de destino é especificado no campo mipLevel
de VkImageSubresourceLayers
. Não utilizaremos mapeamento MIP, portando definimos esse campo como 0
.
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
Se a imagem de destino for uma imagem de array, podemos especificar a camada inicial e o número de camadas para a cópia de imagem em baseArrayLayer
e layerCount
, respectivamente. Se a imagem não for uma imagem de array, como no nosso caso, esses campos deverão ser definidos como 0
e 1
.
region.imageOffset.x = 0;
region.imageOffset.y = 0;
region.imageOffset.z = 0;
region.imageExtent.width = width;
region.imageExtent.height = height;
region.imageExtent.depth = 1;
Cada região pode segmentar uma imagem inteira ou uma janela menor dentro da imagem. O deslocamento da janela é especificado em imageOffset
e o tamanho da janela é especificado em imageExtent
. Queremos sobrescrever toda a imagem, portando, é definido imageOffset.x
, imageOffset.y
e imageOffset.z
como 0
, e configurado imageExtent.width
e imageExtent.height
para o tamanho da imagem. Como estamos tratando de imagens 2D, imageExtent.depth
é definido como 1
.
Como mencionado anteriormente para copiar dados de um buffer para uma imagem, precisamos executar o comando vkCmdCopyBufferToImage
:
m_deviceFunctions->vkCmdCopyBufferToImage(
commandBuffer,
buffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
®ion
);
Os três primeiros parâmetros são auto-explicativos. O quarto parâmetro especifica o layout da imagem de destino das cópias, espera-se que isso seja VK_IMAGE_LAYOUT_GENERAL
ou VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
. Estamos assumindo aqui que a imagem já foi transferida para o layout ideal para copiar pixels, através do sinalizador VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
. Os dois últimos parâmetros especificam, respectivamente, o número de regiões a serem atualizadas e um ponteiro para um array de estruturas VkBufferImageCopy
, cada uma definindo uma região da imagem para copiar os dados. Isso significa que é possível executar várias cópias diferentes desse buffer para a imagem em uma única operação.