Vulkan API «Hello Triangle» (3 стр)
Автор: The Player
Пасс вывода
Говорят, что эта часть Vulkan API появилась по инициативе мобильного сектора. Пассы вывода отвечают за состояние кадра и описывают куда и как производить вывод. Нам, пожалуй, понадобится один для дальнейшей работы.
VkResult vkCreateRenderPass(
VkDevice device,
const VkRenderPassCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkRenderPass* pRenderPass) ;
typedef struct VkRenderPassCreateInfo {
VkStructureType sType;
const void * pNext;
VkRenderPassCreateFlags flags;
uint32_t attachmentCount;
const VkAttachmentDescription* pAttachments;
uint32_t subpassCount;
const VkSubpassDescription* pSubpasses;
uint32_t dependencyCount;
const VkSubpassDependency* pDependencies;
} VkRenderPassCreateInfo; Необходимыми для нас полями являются присоединения (attachmentCount /pAttachments ) и подпассы (subpassCount/pSubpasses) на которые, в свою очередь, делятся пассы .
Присоединения описывают процесс, происходящий с текстурой во время вывода (операции очистки текстуры, формат , планировку и т.д.):
typedef struct VkAttachmentDescription {
VkAttachmentDescriptionFlags flags;
VkFormat format;
VkSampleCountFlagBits samples;
VkAttachmentLoadOp loadOp;
VkAttachmentStoreOp storeOp;
VkAttachmentLoadOp stencilLoadOp;
VkAttachmentStoreOp stencilStoreOp;
VkImageLayout initialLayout;
VkImageLayout finalLayout;
} VkAttachmentDescription;Подпассы описывают определенную фазу всего пасса и говорят, что, куда и как писать/читать:
typedef struct VkSubpassDescription {
VkSubpassDescriptionFlags flags;
VkPipelineBindPoint pipelineBindPoint;
uint32_t inputAttachmentCount;
const VkAttachmentReference* pInputAttachments;
uint32_t colorAttachmentCount;
const VkAttachmentReference* pColorAttachments;
const VkAttachmentReference* pResolveAttachments;
const VkAttachmentReference* pDepthStencilAttachment;
uint32_t preserveAttachmentCount;
const uint32_t* pPreserveAttachments;
} VkSubpassDescription;Интересными для нас являются поля для управления точкой привязки – pipelineBindPoint (указывают, используется подпасс графическим конвейером или вычислительным) и присоединениями – colorAttachmentCount /pColorAttachments . Последние описываются очень лаконичными структурами:
typedef struct VkAttachmentReference {
uint32_t attachment;
VkImageLayout layout;
} VkAttachmentReference;которые указывают на слот присоединения и его планировку .
Поскольку пасс вывода не содержит непосредственной информации о текстурах, мы сможем использовать его для вывода в оба изображения цепочки обмена :
VkRenderPass vk_renderPass;
{
VkAttachmentDescription vk_attachmentDescription;
{
vk_attachmentDescription.flags = 0 ;
vk_attachmentDescription.format = vk_surfaceFormats[ 0 ] .format;
vk_attachmentDescription.samples = VkSampleCountFlagBits::VK_SAMPLE_COUNT_1_BIT;
vk_attachmentDescription.loadOp = VkAttachmentLoadOp::VK_ATTACHMENT_LOAD_OP_CLEAR;
vk_attachmentDescription.storeOp = VkAttachmentStoreOp::VK_ATTACHMENT_STORE_OP_STORE;
vk_attachmentDescription.stencilLoadOp = VkAttachmentLoadOp::VK_ATTACHMENT_LOAD_OP_DONT_CARE;
vk_attachmentDescription.stencilStoreOp = VkAttachmentStoreOp::VK_ATTACHMENT_STORE_OP_DONT_CARE;
vk_attachmentDescription.initialLayout = VkImageLayout::VK_IMAGE_LAYOUT_UNDEFINED;
vk_attachmentDescription.finalLayout = VkImageLayout::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
VkAttachmentReference vk_attachmentReference;
{
vk_attachmentReference.attachment = 0 ;
vk_attachmentReference.layout = VkImageLayout::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
VkSubpassDescription vk_subpassDescription;
{
vk_subpassDescription.flags = 0 ;
vk_subpassDescription.pipelineBindPoint = VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS;
vk_subpassDescription.inputAttachmentCount = 0 ;
vk_subpassDescription.pInputAttachments = nullptr ;
vk_subpassDescription.colorAttachmentCount = 1 ;
vk_subpassDescription.pColorAttachments = &vk_attachmentReference;
vk_subpassDescription.pResolveAttachments = nullptr ;
vk_subpassDescription.pDepthStencilAttachment = nullptr ;
vk_subpassDescription.preserveAttachmentCount = 0 ;
vk_subpassDescription.pPreserveAttachments = nullptr ;
}
VkRenderPassCreateInfo vk_renderPassCreateInfo;
{
vk_renderPassCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
vk_renderPassCreateInfo.pNext = nullptr ;
vk_renderPassCreateInfo.flags = 0 ;
vk_renderPassCreateInfo.attachmentCount = 1 ;
vk_renderPassCreateInfo.pAttachments = &vk_attachmentDescription;
vk_renderPassCreateInfo.subpassCount = 1 ;
vk_renderPassCreateInfo.pSubpasses = &vk_subpassDescription;
vk_renderPassCreateInfo.dependencyCount = 0 ;
vk_renderPassCreateInfo.pDependencies = nullptr ;
}
if ( vkCreateRenderPass( vk_device, &vk_renderPassCreateInfo, nullptr , &vk_renderPass) != VkResult::VK_SUCCESS)
{
throw std::exception( "failed to create render pass" ) ;
}
}
Буфер кадра и вид изображения
Вывод графики не производится непосредственно в текстуру, для этого нужен посредник – буфер кадра . С другой стороны, чтение или запись одной и той же текстуры может производится по-разному, а потому есть смысл проводить все операции через объект, описывающий доступ к данным – вид изображения .
План:
• Для каждого изображения цепочки обмена создать соответствующий вид изображения
• Для каждого созданного вида изображения создать буфер кадра , рисующий в него
Вид изображения
Вид изображения определяет способ доступа к памяти изображения, т.е. тип, формат, компоненты и мип-уровни.
VkResult vkCreateImageView(
VkDevice device,
const VkImageViewCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkImageView* pView) ;
typedef struct VkImageViewCreateInfo {
VkStructureType sType;
const void * pNext;
VkImageViewCreateFlags flags;
VkImage image;
VkImageViewType viewType;
VkFormat format;
VkComponentMapping components;
VkImageSubresourceRange subresourceRange;
} VkImageViewCreateInfo; Последний параметр нас не сильно волнует, а вот с помощью format и components мы настроим способ записи в изображение:
typedef struct VkComponentMapping {
VkComponentSwizzle r;
VkComponentSwizzle g;
VkComponentSwizzle b;
VkComponentSwizzle a;
} VkComponentMapping;Структура говорит о том, как читать (или картировать - мапить) каждую компоненту. К примеру, мы можем читать изображение инверсно, если установим поле b значением VkComponentSwizzle::VK_COMPONENT_SWIZZLE_R и т.д.
Буфер кадра
Суть буфера кадра – говорить драйверу, в какие изображения (а точнее в их виды ) и с каким пассом производить вывод.
VkResult vkCreateFramebuffer(
VkDevice device,
const VkFramebufferCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkFramebuffer* pFramebuffer) ;
typedef struct VkFramebufferCreateInfo {
VkStructureType sType;
const void * pNext;
VkFramebufferCreateFlags flags;
VkRenderPass renderPass;
uint32_t attachmentCount;
const VkImageView* pAttachments;
uint32_t width;
uint32_t height;
uint32_t layers;
} VkFramebufferCreateInfo; Т.к. нам необходимо производить вывод по очереди в каждое изображение цепочки обмена , необходимо создать по буферу кадра на каждое:
std::vector< VkFramebuffer> vk_framebuffers( vk_swapchainImages.size( ) ) ;
std::vector< VkImageView> vk_imageViews( vk_swapchainImages.size( ) ) ;
{
for ( uint32_t i = 0 ; i < vk_swapchainImages.size( ) ; ++i)
{
auto &vk_imageView = vk_imageViews[ i] ;
{
VkImageViewCreateInfo vk_imageViewCreateInfo;
{
vk_imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
vk_imageViewCreateInfo.pNext = nullptr ;
vk_imageViewCreateInfo.flags = 0 ;
vk_imageViewCreateInfo.image = vk_swapchainImages[ i] ;
vk_imageViewCreateInfo.viewType = VkImageViewType::VK_IMAGE_VIEW_TYPE_2D;
vk_imageViewCreateInfo.format = vk_surfaceFormats[ 0 ] .format;
vk_imageViewCreateInfo.components = {
VkComponentSwizzle::VK_COMPONENT_SWIZZLE_R,
VkComponentSwizzle::VK_COMPONENT_SWIZZLE_G,
VkComponentSwizzle::VK_COMPONENT_SWIZZLE_B,
VkComponentSwizzle::VK_COMPONENT_SWIZZLE_A
};
vk_imageViewCreateInfo.subresourceRange;
{
vk_imageViewCreateInfo.subresourceRange.aspectMask = VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT;
vk_imageViewCreateInfo.subresourceRange.baseMipLevel = 0 ;
vk_imageViewCreateInfo.subresourceRange.levelCount = 1 ;
vk_imageViewCreateInfo.subresourceRange.baseArrayLayer = 0 ;
vk_imageViewCreateInfo.subresourceRange.layerCount = 1 ;
}
}
if ( vkCreateImageView( vk_device, &vk_imageViewCreateInfo, nullptr , &vk_imageView) != VkResult::VK_SUCCESS)
{
throw std::exception( "failed to create image view" ) ;
}
}
auto &vk_framebuffer = vk_framebuffers[ i] ;
{
VkFramebufferCreateInfo vk_framebufferCreateInfo;
{
vk_framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
vk_framebufferCreateInfo.pNext = nullptr ;
vk_framebufferCreateInfo.flags = 0 ;
vk_framebufferCreateInfo.renderPass = vk_renderPass;
vk_framebufferCreateInfo.attachmentCount = 1 ;
vk_framebufferCreateInfo.pAttachments = &vk_imageView;
vk_framebufferCreateInfo.width = vk_surfaceCapabilitiesKHR.currentExtent.width;
vk_framebufferCreateInfo.height = vk_surfaceCapabilitiesKHR.currentExtent.height;
vk_framebufferCreateInfo.layers = 1 ;
}
if ( vkCreateFramebuffer( vk_device, &vk_framebufferCreateInfo, nullptr , &vk_framebuffer) != VkResult::VK_SUCCESS)
{
throw std::exception( "failed to create framebuffer" ) ;
}
}
}
}