Проход рендеринга и прикрепления буфера кадров

Последнее обновление: 14.04.2025

(Практический пример по этой статье: Проход рендеринга и прикрепления буфера кадров в GLFW)

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

Что такое "прикрепление" или attachment в контексте Vulkan и вообще работы с графикой? Изображение — это просто фрагмент памяти с некоторыми метаданными о макете, формате и т. д. Буфер кадров (кадровый буфер - frame buffer) — это контейнер для нескольких изображений с дополнительными метаданными для каждого изображения, такими как цель использования, идентификатор (индекс) и тип (цвет, глубина и т. д.). Эти изображения, используемые в буфере кадров, называются прикреплениями, потому что они прикреплены к буферу кадров и принадлежат ему.

Прикрепления, которые используются в качестве входных данных, называются входными прикреплениями (Input Attachment). Прикрепления с информацией о цвете (используемых компонентах RGB) называются Color Attachment или "цветовыми прикреплениями", с информацией о глубине (depth) - Depth Attachment или "прикреплениями глубины", с информацией о шаблоне (stencil) Stencil Attachment или "прикреплениями шаблона". (Stencil (шаблон/трафарет) - представляет шаблон, используемый для рисования или раскрашивания идентичных букв, символов, фигур или узоров.)

Прикрепления буфера кадров описываются структурой VkAttachmentDescription:

typedef struct VkAttachmentDescription {
    VkAttachmentDescriptionFlags    flags;
    VkFormat                        format;
    VkSampleCountFlagBits           samples;
    VkAttachmentLoadOp              loadOp;
    VkAttachmentStoreOp             storeOp;
    VkAttachmentLoadOp              stencilLoadOp;
    VkAttachmentStoreOp             stencilStoreOp;
    VkImageLayout                   initialLayout;
    VkImageLayout                   finalLayout;
} VkAttachmentDescription;

Информация о прикрепления определяется через следующие поля:

  • flags: битовая маска VkAttachmentDescriptionFlagBits, указывающая дополнительные свойства прикрепления.

  • format: значение VkFormat, указывающее формат представления изображения, который будет использоваться для прикрепления.

  • samples: значение VkSampleCountFlagBits, указывающее количество образцов изображения.

  • loadOp: определяет, что делать с данными в цветовом прикреплении до рендеринга

  • storeOp: определяет, что делать с данными в цветовом прикреплении после рендеринга.

  • stencilLoadOp: определяет, что делать с данными в прикреплении шаблона до рендеринга.

  • stencilStoreOp: определяет, что делать с данными в прикреплении шаблона после рендеринга.

  • initialLayout: указывает, какой макет будет у изображения до начала прохода рендеринга. Некоторые из наиболее распространенных макетов:

    • VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: Изображения, используемые как цветовое прикрепление

    • VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: изображения, которые будут представлены в цепочке буферов

    • VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: изображения, которые будут использоваться как место назначения для операции копирования памяти

  • finalLayout: указывает, какой макет будет у изображения по завершении прохода рендеринга.

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

VkAttachmentDescription colorAttachment{};
colorAttachment.format = swapChainImageFormat;  // формат изображений, полученный из цепочки буферов
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;  // 1 образец на изображение
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;  // очистить буфер кадра до черного цвета перед рисованием нового кадра
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;  // отрисованное содержимое будет сохранено в памяти и может быть прочитано позже
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;  // не имеет значения
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;  // не имеет значения
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

Формат цветового прикрепления должен соответствовать формату изображений цепочки буферов

Поля loadOp и storeOp определяют, что делать с данными в прикреплении до рендеринга и после рендеринга. Eсть следующие варианты для loadOp:

  • VK_ATTACHMENT_LOAD_OP_LOAD: сохраняет существующее содержимое прикрепления

  • VK_ATTACHMENT_LOAD_OP_CLEAR: очищает значения до константы при запуске экземпляра прохода рендеринга

  • VK_ATTACHMENT_LOAD_OP_DONT_CARE: cуществующее содержимое не определено

В нашем случае мы собираемся использовать операцию очистки, чтобы очистить буфер кадра до черного цвета перед рисованием нового кадра. Для storeOp есть только две возможности:

  • VK_ATTACHMENT_STORE_OP_STORE: отрисованное содержимое будет сохранено в памяти и может быть прочитано позже

  • VK_ATTACHMENT_STORE_OP_DONT_CARE: cодержимое буфера кадра будет неопределенным после операции отрисовки

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

colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;  // очистить буфер кадра до черного цвета перед рисованием нового кадра
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;  // отрисованное содержимое будет сохранено в памяти и может быть прочитано позже

loadOp и storeOp применяются к данным цвета и глубины, а stencilLoadOp / stencilStoreOp применяются к данным шаблона. Наше приложение ничего не будет делать с буфером шаблона, поэтому результаты загрузки и сохранения не имеют значения.

colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;  // не имеет значения
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;  // не имеет значения

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

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

Использование VK_IMAGE_LAYOUT_UNDEFINED для initialLayout означает, что нам все равно, в каком макете ранее находилось изображение. Мы хотим, чтобы изображение было готово к представлению с использованием цепочки буферов после рендеринга, поэтому мы используем VK_IMAGE_LAYOUT_PRESENT_SRC_KHR в качестве finalLayout.

Подпроходы и прикрепления

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

Каждый подпроход использует одно или несколько прикреплений (attachment). Ссылки на эти прикрепления представляют структуры типа VkAttachmentReference:

typedef struct VkAttachmentReference {
    uint32_t         attachment;  // идентификатор прикрепления
    VkImageLayout    layout;  // используемый макет изображения
} VkAttachmentReference;

Структура хранит числовой идентификатор прикрепления (числовой индекс в массиве описаний прикреплений, представленных структурами VkAttachmentDescription). Второе поле представляет макет изображения, который прикрепление использует во время подпроходов. Vulkan автоматически переведет прикрепление в этот макет при запуске подпрохода. ПРимер определения структуры:

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

В данном случае предполагаем, что наш массив состоит из одного значения VkAttachmentDescription (из одного описания прикрепления), поэтому его индекс равен 0. Здесь преполашается, что прикрепление будет использоваться для работы в качестве цветового прикрепления, а макет VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL обеспечит наилучшую производительность.

Описание подпрохода

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

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;

Поля структуры:

  • flags: битовая маска VkSubpassDescriptionFlagBits, которая определяет использование подпрохода.

  • pipelineBindPoint: значение VkPipelineBindPoint, которое указывает на тип конвейера, поддерживаемого для этого подпрохода. ПРинимает следующие значения:

    • VK_PIPELINE_BIND_POINT_COMPUTE: вычислительный конвейер.

    • VK_PIPELINE_BIND_POINT_GRAPHICS: графический конвейер.

    • VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR: конвейер трассировки лучей.

    • VK_PIPELINE_BIND_POINT_SUBPASS_SHADING_HUAWEI: конвейер затенения подпрохода.

    • VK_PIPELINE_BIND_POINT_EXECUTION_GRAPH_AMDX: конвейер графа выполнения.

  • inputAttachmentCount: количество входных прикреплений.

  • pInputAttachments: указатель на массив входных прикреплений, которые считываются шейдером.

  • colorAttachmentCount: количество цветовых прикреплений из поля pColorAttachments

  • pColorAttachments: указатель на массив цветовых прикреплений.

  • pResolveAttachments: NULL или указатель на массив структур VkAttachmentReference, определяющих разрешения прикреплений для этого подпрохода и их макеты.

  • pDepthStencilAttachment: указатель на прикрепление глубины/шаблона.

  • preserveAttachmentCount: количество сохраненных прикреплений.

  • pPreserveAttachments: указатель на массив из индексов прикреплений, которые идентифицируют прикрепления и которые не используются этим подпроходом, но содержимое которых должно сохраняться на протяжении всего подпрохода.

Пример определения структуры:

VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; // тип конвейера - графический конвейер
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef; // выше созданная ссылка на цветовое прикрепление 

Здесь в массиве subpass.pColorAttachments устанавливается только одно цветовое прикрепление (с помощью ранее созданной ссылки на цветовое прикрепление). Причем во фрагментном шейдерк с помощью директивы layout(location = 0) out vec4 outColor через индекс прикрепления мы можем напрямую ссылаться на прикрепление в этом массиве.

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

Проход рендеринга представляет объект VkRenderPass (непрозрачный указатель (opaque handle)).

Для создания прохода рендеринга применяется функция vkCreateRenderPass():

VkResult vkCreateRenderPass(
    VkDevice                                    device,
    const VkRenderPassCreateInfo*               pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkRenderPass*                               pRenderPass);

В нее передаются

  • device: логическое устройство, создающее проход рендеринга.

  • pCreateInfo: указатель на структуру VkRenderPassCreateInfo, которая описывает параметры прохода рендеринга.

  • pAllocator: аллокатор памяти хоста

  • pRenderPass: указатель на дескриптор VkRenderPass, через который возвращается результирующий объект прохода рендеринга.

Итак, для определения прохода рендеринга нам необходима структура VkRenderPassCreateInfo, которая описывает параметры прохода рендеринга:

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;

Поля структуры:

  • sType: тип структуры, должен представлять значение VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO.

  • pNext: NULL или указатель на структуру, расширяющую эту структуру.

  • flags: битовая маска VkRenderPassCreateFlagBits, которая определяет дополнительные свойства для прохода рендеринга

  • attachmentCount: количество прикреплений, используемых этим проходом рендеринга.

  • pAttachments: указатель на массив прикреплений - структур VkAttachmentDescription, используемые проходом рендеринга.

  • subpassCount: количество подпроходов.

  • pSubpasses: указатель на массив структур VkSubpassDescription, описывающих каждый подпроход.

  • dependencyCount: количество зависимостей подпроходов.

  • pDependencies: указатель на массив зависимостей подпроходов - структур VkSubpassDependency.

Пример создания прохода рендеринга:

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) != VK_SUCCESS) {
    throw std::runtime_error("Не удалось создать проход рендеринга!");
}

После завершения работы необходимо удалить проход рендеринга с помощью функции vkDestroyRenderPass:

void vkDestroyRenderPass(
    VkDevice                        device,     // логическое устройство
    VkRenderPass                    renderPass, // удаляемый проход рендеринга
    const VkAllocationCallbacks*    pAllocator);    // аллокатор памяти (если применяется)

(Практический пример по этой статье: Проход рендеринга и прикрепления буфера кадров в GLFW)

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850