Vulkan API — урок 25. Проход рендера (render pass)

До того, как мы закончим создание конвейера, необходимо рассказать Vulkan’у о вложениях фреймбуфера, которые будут использованы во время рендеринга. Нам необходимо указать количество цветов и глубину буфера, как много моделей (sample) будет использоваться для каждого из них и как их содержание должно быть обработано операциями рендеринга. Вся эта информация будет находиться в объекте render pass (проход рендеринга/рендера, так же в тексте будут упоминаться подпроходы – subpasses), для которой создадим новую функцию createRenderPass. Вызов которой будет в initVulkan перед createGraphicsPipeline:

void initVulkan() {
    createInstance();
    setupDebugCallback();
    createSurface();
    pickPhysicalDevice();
    createLogicalDevice();
    createSwapChain();
    createImageViews();
    createRenderPass();
    createGraphicsPipeline();
}

...

void createRenderPass() {

}

Описание вложения

В нашем случае понадобится вложение только для буфера цвета (в будущем можете встретить термир color attachment — цветное вложение, вложение(как существительное, не глагол) цвета), представляемое одним из изображений из swap chain.

void createRenderPass() {
    VkAttachmentDescription colorAttachment = {};
    colorAttachment.format = swapChainImageFormat;
    colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
}

format вложения должен соответствовать формату изображений swap chain и мы никак не используем multisampling, так что мы указываем 1 для samples.

colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;

loadOp и storeOp определяют, что делать с  данными во вложении  перед и после рендеринга. Для loadOp возможны значения:

  • VK_ATTACHMENT_LOAD_OP_LOAD: Сохранение существующего содержимого вложения
  • VK_ATTACHMENT_LOAD_OP_CLEAR: Очистка значения до константы
  • VK_ATTACHMENT_LOAD_OP_DONT_CARE: Существующее содержание не определено, что с ним произойдет нас не волнует

В нашем случае была выбрана операция очистки, для очистки фреймбуфера до черного цвета перед отрисовкой нового кадра.

Для storeOp имеется только два возможных значения:

  • VK_ATTACHMENT_STORE_OP_STORE: Отрендеренное содержимое останется в памяти и его будет возможно прочитать позже
  • VK_ATTACHMENT_STORE_OP_DONT_CARE: Содержимое фреймбуфера будет неопределено после операции рендеринга

Мы заинтересовоны наблюдать отрендеренный треугольник на экране, потому выберем операцию хранения.

colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;

loadOp и storeOp применяются к цвету и глубине, а  stencilLoadOp / stencilStoreOp применяются к трафарету (stencil). Мы не собираемся делать что-либо с stencil buffer, так что результат загрузки и хранения нам безразличен.

colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

Текстуры и фреймбуферы в Vulkan’е представлены объектами VkImage с определенным форматом пикселей. Однако, формат размещения (layout) пикселей в памяти может меняться в зависимости от того, что вы пытаетесь сделать с изображением.

Наиболее распространенные варианты:

  • VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: Изображение используется как цветное вложение
  • VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: Изображение показывается в swap chain
  • VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: Изображение используется как место в памяти для операций копирования

Эту тему более подробно обсудим в главе посвященной текстурированию, но сейчас следует знать, что изображения должны быть преведены к конкретному виду (layout), который соответствует операциям, в которых они будут принимать участие в дальнейшем.

initialLayout указывает, какой layout изображение будет иметь до начала render pass. finalLayout определяет к какому layout’у будет совершен переход по окончании рендеринга. Использование  VK_IMAGE_LAYOUT_UNDEFINED для initialLayout означает, что нас не волнует, какой layout был в предыдущем изображении. Особенность этого значения в том, что содержимое изображения не будет гарантированно сохранено, но для нас это не имеет значения, ведь итак собираемся его очистить. Нужно что бы изображение было готово для представления с помощью swap chain после рендеринга, потому как  finalLayout используется VK_IMAGE_LAYOUT_PRESENT_SRC_KHR.

Подпроходы и ссылки на вложения

Один render pass может содержать несколько подпроходов. Подпроходы являются последовательными операциями, зависящими от содержимого фреймбуферов предыдущих проходов. К примеру последовательность эффектов пособработки, которые идут в определенной последовательности. Если сгруппировать эти операции рендеринга в одном проходе, тогда Vulkan способен изменить порядок операций и сохранить пропускную способность памяти для наилучшей производительности. Однако для первого триугольника оставим единственный подпроход.

Каждый подпроход ссылается на одно или несколько вложений, которые были описаны ранее. Сами ссылки являются структурами VkAttachmentReference, на подобии этой:

VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

Параметр attachment определяет какое вложение по данному индексу в массиве описывающем вложения. Наш массив состоит из одно VkAttachmentDescription, потому и индекс 0. layout определяет, какой layout мы предпочитаем использовать с вложением во время подпрохода. Vulkan автоматически передает вложение данному layout, после того как стартовал подпроход. В данной программе мы намерены использовать вложение в качестве буфера цвета и lauot VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL обеспечит наилучшую производительность, как видно из названия.

Подпроцесс описывается при помощи структуры VkSubpassDescription:

VkSubpassDescription subPass = {};
subPass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;

Vulkan так же поддерживает вычислительные подпроходы, потому мы указываем, что работаем с графикой.

Далее указываем ссылку на вложение:

subPass.colorAttachmentCount = 1;
subPass.pColorAttachments = &colorAttachmentRef;

Индекс вложения в данном массиве получают непосредственно по ссылке из fragment shader с директивой layout(location = 0) out vec4 outColor.

Следующие типы вложений в подпроходе, на которые могут ссылаться:

  • pInputAttachments: Вложения, которые могут быть прочитаны из шейдеров
  • pResolveAttachments: Вложения используются для multisampling color attachments (~сглаживания цветных вложений)
  • pDepthStencilAttachment: Вложения для глубины и трафарета
  • pPreserveAttachments: Вложения, которые не используются данным подпроходом, но данные которых должны быть сохранены.

Проход рендеринга

Теперь, когда вложения и ссылки базовых подпроходов описаны, можно создать render pass. Создадим новую переменню для хранения объекта VkRenderPass сразу за переменной pipelineLayout:

VDeleter<VkRenderPass> renderPass{device, vkDestroyRenderPass};

Далее массивом вложений и подпроходами заполняется структура VkRenderPassCreateInfo. Объекты  VkAttachmentReference ссылаются на вложения, используя индексы данного массива.

VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subPass;

if (vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) {
    throw std::runtime_error("failed to create render pass!");
}

Большая часть проделана, в следующем уроке создадим объект графического конвейера.

Main Admin

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *