Войти
ПрограммированиеСтатьиГрафика

Vulkan API «Hello Triangle» (7 стр)

Автор:

Конвейер

Вот мы и подошли к пожалуй самому важному и объемному объекту Vulkan API освещенному в этой статье - конвейеру.
Конвейер описывает все без исключения детали вывода примитивов, в том числе и операции реализованные в OpenGL как глобальные состояния, такие как тест глубины или смешивание.

Планировка и кэш конвейера

Для создания самого конвейера нам понадобятся два объекта: планировка конвейера и кэш конвейера.
Первый - дает информацию о дескрипторах и константах, второй - позволяет повторно использовать результат построения конвейера между несколькими конвейерами и даже между отдельными запусками приложения! Но наша вводная статья, конечно же, не об этом...

Планировка конвейера создается посредством следующих функций:

VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineLayout(
    VkDevice                                    device,
    const VkPipelineLayoutCreateInfo*           pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkPipelineLayout*                           pPipelineLayout);

typedef struct VkPipelineLayoutCreateInfo {
    VkStructureType                 sType;
    const void*                     pNext;
    VkPipelineLayoutCreateFlags     flags;
    uint32_t                        setLayoutCount;
    const VkDescriptorSetLayout*    pSetLayouts;
    uint32_t                        pushConstantRangeCount;
    const VkPushConstantRange*      pPushConstantRanges;
} VkPipelineLayoutCreateInfo;

Как видно из описания, поля setLayoutCount/pSetLayouts и pushConstantRangeCount/pPushConstantRanges отвечают за планировку дескрипторов и констант.

Для нас, к сожалениюсчастью, все это не пригодится, т.к. наш пример не включает в себя ни константы, ни объекты, требующие дескрипторы.
Еще более формальной вещью тут выступает кэш конвейера:

VkResult vkCreatePipelineCache(
    VkDevice                                    device,
    const VkPipelineCacheCreateInfo*            pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkPipelineCache*                            pPipelineCache);

typedef struct VkPipelineCacheCreateInfo {
    VkStructureType               sType;
    const void*                   pNext;
    VkPipelineCacheCreateFlags    flags;
    size_t                        initialDataSize;
    const void*                   pInitialData;
} VkPipelineCacheCreateInfo;

Для решения реальных задач мы могли бы найти эму применение, но в нашем примере он будет скорее "для галочки".

Итак, код:

VkPipelineLayout vk_pipelineLayout;
{
  VkPipelineLayoutCreateInfo vk_pipelineLayoutCreateInfo;
  {
    vk_pipelineLayoutCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    vk_pipelineLayoutCreateInfo.pNext = nullptr;
    vk_pipelineLayoutCreateInfo.flags = 0;
    vk_pipelineLayoutCreateInfo.setLayoutCount = 0;
    vk_pipelineLayoutCreateInfo.pSetLayouts = nullptr;
    vk_pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
    vk_pipelineLayoutCreateInfo.pPushConstantRanges = nullptr;
  }

  if(vkCreatePipelineLayout(vk_device, &vk_pipelineLayoutCreateInfo, nullptr, &vk_pipelineLayout) != VkResult::VK_SUCCESS)
  {
    throw std::exception("failed to create pipelane layout");
  }
}
VkPipelineCache vk_PipelineCache;
{
  VkPipelineCacheCreateInfo vk_pipelineCacheCreateInfo;
  {
    vk_pipelineCacheCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
    vk_pipelineCacheCreateInfo.pNext = nullptr;
    vk_pipelineCacheCreateInfo.flags = 0;
    vk_pipelineCacheCreateInfo.initialDataSize = 0;
    vk_pipelineCacheCreateInfo.pInitialData = nullptr;
  }

  if(vkCreatePipelineCache(vk_device, &vk_pipelineCacheCreateInfo, nullptr, &vk_PipelineCache) != VkResult::VK_SUCCESS)
  {
    throw std::exception("failed to create pipeline cache");
  }
}

Этапы и состояния графического конвейера

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

VkResult vkCreateGraphicsPipelines(
    VkDevice                                    device,
    VkPipelineCache                             pipelineCache,
    uint32_t                                    createInfoCount,
    const VkGraphicsPipelineCreateInfo*         pCreateInfos,
    const VkAllocationCallbacks*                pAllocator,
    VkPipeline*                                 pPipelines);

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

typedef struct VkGraphicsPipelineCreateInfo {
    VkStructureType                                  sType;
    const void*                                      pNext;
    VkPipelineCreateFlags                            flags;
    uint32_t                                         stageCount;
    const VkPipelineShaderStageCreateInfo*           pStages;
    const VkPipelineVertexInputStateCreateInfo*      pVertexInputState;
    const VkPipelineInputAssemblyStateCreateInfo*    pInputAssemblyState;
    const VkPipelineTessellationStateCreateInfo*     pTessellationState;
    const VkPipelineViewportStateCreateInfo*         pViewportState;
    const VkPipelineRasterizationStateCreateInfo*    pRasterizationState;
    const VkPipelineMultisampleStateCreateInfo*      pMultisampleState;
    const VkPipelineDepthStencilStateCreateInfo*     pDepthStencilState;
    const VkPipelineColorBlendStateCreateInfo*       pColorBlendState;
    const VkPipelineDynamicStateCreateInfo*          pDynamicState;
    VkPipelineLayout                                 layout;
    VkRenderPass                                     renderPass;
    uint32_t                                         subpass;
    VkPipeline                                       basePipelineHandle;
    int32_t                                          basePipelineIndex;
} VkGraphicsPipelineCreateInfo;

Будем честны, задача не из легких. Обилие полей и возможное число комбинаций делает эту область особо привлекательной для багов. Если не иметь под рукой спецификации и не пользоваться всеми предоставленными Vulkan API средствами отладки - можно убить часы или даже дни на решение трудностей.

Большинство полей представляет из себя структуру из дюжины параметров, а потому далее не будет приводится их описание, а лишь их использование в исходном коде.

Этапы обработки примитивов

Поля stageCount/pStages отвечают за используемые конвейером шейдеры. Само собой, шейдер каждого типа должен встречаться не боле одного раза:

std::vector<VkPipelineShaderStageCreateInfo> vk_pipelineShaderStageCreateInfos(2);
{
  vk_pipelineShaderStageCreateInfos[0].sType  = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  vk_pipelineShaderStageCreateInfos[0].pNext  = nullptr;
  vk_pipelineShaderStageCreateInfos[0].flags  = 0;
  vk_pipelineShaderStageCreateInfos[0].stage  = VkShaderStageFlagBits::VK_SHADER_STAGE_VERTEX_BIT;
  vk_pipelineShaderStageCreateInfos[0].module =  vk_shaderModuleVertex;
  vk_pipelineShaderStageCreateInfos[0].pName = "main";
  vk_pipelineShaderStageCreateInfos[0].pSpecializationInfo = nullptr;

  vk_pipelineShaderStageCreateInfos[1].sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  vk_pipelineShaderStageCreateInfos[1].pNext = nullptr;
  vk_pipelineShaderStageCreateInfos[1].flags = 0;
  vk_pipelineShaderStageCreateInfos[1].stage = VkShaderStageFlagBits::VK_SHADER_STAGE_FRAGMENT_BIT;
  vk_pipelineShaderStageCreateInfos[1].module = vk_shaderModuleFragment;
  vk_pipelineShaderStageCreateInfos[1].pName = "main";
  vk_pipelineShaderStageCreateInfos[1].pSpecializationInfo = nullptr;
}

Информация о вершинах

pVertexInputState задает правила выборки информации о вершинах. Он использует информацию предоставляемую двумя массивами: один описывает используемые буферы, другой - атрибуты.

VkPipelineVertexInputStateCreateInfo vk_pipelineVertexInputStateCreateInfo;
{
  vk_pipelineVertexInputStateCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
  vk_pipelineVertexInputStateCreateInfo.pNext = nullptr;
  vk_pipelineVertexInputStateCreateInfo.flags = 0;
  vk_pipelineVertexInputStateCreateInfo.vertexBindingDescriptionCount = 1;
  vk_pipelineVertexInputStateCreateInfo.pVertexBindingDescriptions = &vk_vertexInputBindingDescription;
  vk_pipelineVertexInputStateCreateInfo.vertexAttributeDescriptionCount = 1;
  vk_pipelineVertexInputStateCreateInfo.pVertexAttributeDescriptions = &vk_vertexInputAttributeDescription;
}

Так как нам необходим минимум - мы обойдемся одним буфером и единственным атрибутом.

VkVertexInputBindingDescription vk_vertexInputBindingDescription;
{
  vk_vertexInputBindingDescription.binding = 0;
  vk_vertexInputBindingDescription.stride = sizeof(float)*2;
  vk_vertexInputBindingDescription.inputRate = VkVertexInputRate::VK_VERTEX_INPUT_RATE_VERTEX;
}

Мы явно указываем, что будем использовать буфер на позиции 0 (vk_vertexInputBindingDescription.binding = 0) как буфер с информацией о вершинах (vk_vertexInputBindingDescription.inputRate = VkVertexInputRate::VK_VERTEX_INPUT_RATE_VERTEX). Расстояние между соседними вершинами, кстати говоря, два floatа (vk_vertexInputBindingDescription.stride = sizeof(float)*2).

VkVertexInputAttributeDescription vk_vertexInputAttributeDescription;
{
  vk_vertexInputAttributeDescription.location = 0;
  vk_vertexInputAttributeDescription.binding = 0;
  vk_vertexInputAttributeDescription.format = VkFormat::VK_FORMAT_R32G32_SFLOAT;
  vk_vertexInputAttributeDescription.offset = 0;
}

Эта часть связывает определенный буфер с атрибутом в шейдере. Мы указываем, что расположение атрибута = 0 (vk_vertexInputAttributeDescription.location = 0), информация в него поступает из буфера на позиции 0 (vk_vertexInputAttributeDescription.binding = 0), формат нашего атрибута - двумерный вектор floatов (vk_vertexInputAttributeDescription.format = VkFormat::VK_FORMAT_R32G32_SFLOAT), а смещение данных отсутствует (vk_vertexInputAttributeDescription.offset = 0).

Информация о режиме рисования

Эта часть описывает тип выводимых примитивов (точки, линии, треугольники, etc.) и режим повторения (для рисования стрипами):

VkPipelineInputAssemblyStateCreateInfo vk_pipelineInputAssemblyStateCreateInfo;
{
  vk_pipelineInputAssemblyStateCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
  vk_pipelineInputAssemblyStateCreateInfo.pNext = nullptr;
  vk_pipelineInputAssemblyStateCreateInfo.flags = 0;
  vk_pipelineInputAssemblyStateCreateInfo.topology = VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
  vk_pipelineInputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE;
}

Состояние выводимой области

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

VkPipelineViewportStateCreateInfo vk_pipelineViewportStateCreateInfo;
{
  vk_pipelineViewportStateCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
  vk_pipelineViewportStateCreateInfo.pNext = nullptr;
  vk_pipelineViewportStateCreateInfo.flags = 0;
  vk_pipelineViewportStateCreateInfo.viewportCount = 1;
  vk_pipelineViewportStateCreateInfo.pViewports = &vk_viewport;
  vk_pipelineViewportStateCreateInfo.scissorCount = 1;
  vk_pipelineViewportStateCreateInfo.pScissors = &vk_scissor;
}

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

VkViewport vk_viewport{
  0.0f, 0.0f,
  (float)vk_surfaceCapabilitiesKHR.currentExtent.width,
  (float)vk_surfaceCapabilitiesKHR.currentExtent.height,
  0.0f, 1.0f
};

Операция ножниц отсекает изображение, не попадающее в определенную область области вывода.
Если урезать параметры операции отсечения вдвое, то выводимое изображение сохранит размер, но потеряет целостность в результате отсечения.

VkRect2D vk_scissor{
  {0, 0},
  {vk_surfaceCapabilitiesKHR.currentExtent.width, vk_surfaceCapabilitiesKHR.currentExtent.height}
};

Этап растеризации

Этот этап задает правила преобразования вершин в фрагменты (пиксели).

VkPipelineRasterizationStateCreateInfo vk_pipelineRasterizationStateCreateInfo;
{
  vk_pipelineRasterizationStateCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
  vk_pipelineRasterizationStateCreateInfo.pNext = nullptr;
  vk_pipelineRasterizationStateCreateInfo.flags = 0;
  vk_pipelineRasterizationStateCreateInfo.depthClampEnable = VK_FALSE;
  vk_pipelineRasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE;
  vk_pipelineRasterizationStateCreateInfo.polygonMode = VkPolygonMode::VK_POLYGON_MODE_FILL;
  vk_pipelineRasterizationStateCreateInfo.cullMode = VkCullModeFlagBits::VK_CULL_MODE_NONE;
  vk_pipelineRasterizationStateCreateInfo.frontFace = VkFrontFace::VK_FRONT_FACE_COUNTER_CLOCKWISE;
  vk_pipelineRasterizationStateCreateInfo.depthBiasEnable = VK_FALSE;
  vk_pipelineRasterizationStateCreateInfo.depthBiasConstantFactor = 0.0f;
  vk_pipelineRasterizationStateCreateInfo.depthBiasClamp = 0.0f;
  vk_pipelineRasterizationStateCreateInfo.depthBiasSlopeFactor = 0.0f;
  vk_pipelineRasterizationStateCreateInfo.lineWidth = 1.0f;
}

Мы задаем режимы ограничения глубины (vk_pipelineRasterizationStateCreateInfo.depthClampEnable), отмены растеризации (vk_pipelineRasterizationStateCreateInfo.rasterizerDiscardEnable), заполнения примитивов (vk_pipelineRasterizationStateCreateInfo.polygonMode), отбраковки (vk_pipelineRasterizationStateCreateInfo.cullMode) и лицевой стороны примитива (vk_pipelineRasterizationStateCreateInfo.frontFace). От описания остальных параметров, пожалуй, воздержимся.

Этап множественной выборки

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

VkPipelineMultisampleStateCreateInfo vk_pipelineMultisampleStateCreateInfo;
{
  vk_pipelineMultisampleStateCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
  vk_pipelineMultisampleStateCreateInfo.pNext = nullptr;
  vk_pipelineMultisampleStateCreateInfo.flags = 0;
  vk_pipelineMultisampleStateCreateInfo.rasterizationSamples = VkSampleCountFlagBits::VK_SAMPLE_COUNT_1_BIT;
  vk_pipelineMultisampleStateCreateInfo.sampleShadingEnable = VK_FALSE;
  vk_pipelineMultisampleStateCreateInfo.minSampleShading = 0;
  vk_pipelineMultisampleStateCreateInfo.pSampleMask = nullptr;
  vk_pipelineMultisampleStateCreateInfo.alphaToCoverageEnable = VK_FALSE;
  vk_pipelineMultisampleStateCreateInfo.alphaToOneEnable = VK_FALSE;
}

Этап теста глубины и трафарета

Этот этап определяет правила, по которым будут отбраковываться примитивы с определенной глубиной и трафаретом.

VkPipelineDepthStencilStateCreateInfo vk_pipelineDepthStencilStateCreateInfo;
{
  vk_pipelineDepthStencilStateCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
  vk_pipelineDepthStencilStateCreateInfo.pNext = nullptr;
  vk_pipelineDepthStencilStateCreateInfo.flags = 0;
  vk_pipelineDepthStencilStateCreateInfo.depthTestEnable = VK_FALSE;
  vk_pipelineDepthStencilStateCreateInfo.depthWriteEnable = VK_FALSE;
  vk_pipelineDepthStencilStateCreateInfo.depthCompareOp = VkCompareOp::VK_COMPARE_OP_ALWAYS;
  vk_pipelineDepthStencilStateCreateInfo.depthBoundsTestEnable = VK_FALSE;
  vk_pipelineDepthStencilStateCreateInfo.stencilTestEnable = VK_FALSE;
  vk_pipelineDepthStencilStateCreateInfo.front;
  {
    vk_pipelineDepthStencilStateCreateInfo.front.failOp = VkStencilOp::VK_STENCIL_OP_KEEP;
    vk_pipelineDepthStencilStateCreateInfo.front.passOp = VkStencilOp::VK_STENCIL_OP_KEEP;
    vk_pipelineDepthStencilStateCreateInfo.front.depthFailOp = VkStencilOp::VK_STENCIL_OP_KEEP;
    vk_pipelineDepthStencilStateCreateInfo.front.compareOp = VkCompareOp::VK_COMPARE_OP_ALWAYS;
    vk_pipelineDepthStencilStateCreateInfo.front.compareMask = 0;
    vk_pipelineDepthStencilStateCreateInfo.front.writeMask = 0;
    vk_pipelineDepthStencilStateCreateInfo.front.reference = 0;
  }
  vk_pipelineDepthStencilStateCreateInfo.back;
  {
    vk_pipelineDepthStencilStateCreateInfo.back.failOp = VkStencilOp::VK_STENCIL_OP_KEEP;
    vk_pipelineDepthStencilStateCreateInfo.back.passOp = VkStencilOp::VK_STENCIL_OP_KEEP;
    vk_pipelineDepthStencilStateCreateInfo.back.depthFailOp = VkStencilOp::VK_STENCIL_OP_KEEP;
    vk_pipelineDepthStencilStateCreateInfo.back.compareOp = VkCompareOp::VK_COMPARE_OP_ALWAYS;
    vk_pipelineDepthStencilStateCreateInfo.back.compareMask = 0;
    vk_pipelineDepthStencilStateCreateInfo.back.writeMask = 0;
    vk_pipelineDepthStencilStateCreateInfo.back.reference = 0;
  }
  vk_pipelineDepthStencilStateCreateInfo.minDepthBounds = 0.0f;
  vk_pipelineDepthStencilStateCreateInfo.maxDepthBounds = 0.0f;
}

Т.к. мы выводим лишь треугольник, никакие тесты нам не нужны. Мы отключаем этот этап (vk_pipelineDepthStencilStateCreateInfo.depthCompareOp = VkCompareOp::VK_COMPARE_OP_ALWAYS) и отменяем запись в буфер глубины (которого у нас вообще говоря и нет).

Этап смешивания

Этот этап может использоваться для создания эффектов полупрозрачности, суммирования цвета, коррекции, определения максимального значения, etc.

Само собой, нам ничего из этого не нужно, а потому мы отключаем смешивание и выставляем единичные коэффициенты для преобразований:

VkPipelineColorBlendAttachmentState vk_pipelineColorBlendAttachmentState;
{
  vk_pipelineColorBlendAttachmentState.blendEnable = VK_FALSE;
  vk_pipelineColorBlendAttachmentState.srcColorBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_ONE;
  vk_pipelineColorBlendAttachmentState.dstColorBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_ONE;
  vk_pipelineColorBlendAttachmentState.colorBlendOp = VkBlendOp::VK_BLEND_OP_ADD;
  vk_pipelineColorBlendAttachmentState.srcAlphaBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_ONE;
  vk_pipelineColorBlendAttachmentState.dstAlphaBlendFactor = VkBlendFactor::VK_BLEND_FACTOR_ONE;
  vk_pipelineColorBlendAttachmentState.alphaBlendOp = VkBlendOp::VK_BLEND_OP_ADD;
  vk_pipelineColorBlendAttachmentState.colorWriteMask = 
    VkColorComponentFlagBits::VK_COLOR_COMPONENT_R_BIT |
    VkColorComponentFlagBits::VK_COLOR_COMPONENT_G_BIT |
    VkColorComponentFlagBits::VK_COLOR_COMPONENT_B_BIT;
}
VkPipelineColorBlendStateCreateInfo vk_pipelineColorBlendStateCreateInfo;
{
  vk_pipelineColorBlendStateCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
  vk_pipelineColorBlendStateCreateInfo.pNext = nullptr;
  vk_pipelineColorBlendStateCreateInfo.flags = 0;
  vk_pipelineColorBlendStateCreateInfo.logicOpEnable = VK_FALSE;
  vk_pipelineColorBlendStateCreateInfo.logicOp = VkLogicOp::VK_LOGIC_OP_CLEAR;
  vk_pipelineColorBlendStateCreateInfo.attachmentCount = 1;
  vk_pipelineColorBlendStateCreateInfo.pAttachments = &vk_pipelineColorBlendAttachmentState;
  vk_pipelineColorBlendStateCreateInfo.blendConstants;
  {
    vk_pipelineColorBlendStateCreateInfo.blendConstants[0] = 1.0f;
    vk_pipelineColorBlendStateCreateInfo.blendConstants[1] = 1.0f;
    vk_pipelineColorBlendStateCreateInfo.blendConstants[2] = 1.0f;
    vk_pipelineColorBlendStateCreateInfo.blendConstants[3] = 1.0f;
  }
}

Создание конвейера

Когда все приготовления готовы - можно приступать к созданию конвейера:

VkGraphicsPipelineCreateInfo vk_graphicsPipelineCreateInfo;
{
  vk_graphicsPipelineCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
  vk_graphicsPipelineCreateInfo.pNext = nullptr;
  vk_graphicsPipelineCreateInfo.flags = 0;
  vk_graphicsPipelineCreateInfo.stageCount = vk_pipelineShaderStageCreateInfos.size();
  vk_graphicsPipelineCreateInfo.pStages = vk_pipelineShaderStageCreateInfos.data();
  vk_graphicsPipelineCreateInfo.pVertexInputState = &vk_pipelineVertexInputStateCreateInfo;
  vk_graphicsPipelineCreateInfo.pInputAssemblyState = &vk_pipelineInputAssemblyStateCreateInfo;
  vk_graphicsPipelineCreateInfo.pTessellationState = VK_NULL_HANDLE;
  vk_graphicsPipelineCreateInfo.pViewportState = &vk_pipelineViewportStateCreateInfo;
  vk_graphicsPipelineCreateInfo.pRasterizationState = &vk_pipelineRasterizationStateCreateInfo;
  vk_graphicsPipelineCreateInfo.pMultisampleState = &vk_pipelineMultisampleStateCreateInfo;
  vk_graphicsPipelineCreateInfo.pDepthStencilState = &vk_pipelineDepthStencilStateCreateInfo;
  vk_graphicsPipelineCreateInfo.pColorBlendState = &vk_pipelineColorBlendStateCreateInfo;
  vk_graphicsPipelineCreateInfo.pDynamicState = nullptr;
  vk_graphicsPipelineCreateInfo.layout = vk_pipelineLayout;
  vk_graphicsPipelineCreateInfo.renderPass = vk_renderPass;
  vk_graphicsPipelineCreateInfo.subpass = 0;
  vk_graphicsPipelineCreateInfo.basePipelineIndex = 0;
  vk_graphicsPipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
}

if(vkCreateGraphicsPipelines(vk_device, vk_PipelineCache, 1, &vk_graphicsPipelineCreateInfo, nullptr, &vk_pipeline) != VkResult::VK_SUCCESS)
{
  throw std::exception("failed to create pipeline");
}

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

Модификация командных буферов

Последний штрих - добавить команду, которая запустит обработку определенного буфера определенным конвейером и произведет вывод всего этого на экран. Изменения следует добавить непосредственно в код пасса вывода:

vkCmdBeginRenderPass(
  vk_commandBuffer,
  &vk_renderPassBeginInfo,
  VkSubpassContents::VK_SUBPASS_CONTENTS_INLINE
);
{
  vkCmdBindPipeline(
    vk_commandBuffer,
    VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS,
    vk_pipeline
  );

  std::vector<VkBuffer> vk_vertexBuffers = {vk_buffer};
  std::vector<VkDeviceSize> vk_vertexOffsets = {0};
  vkCmdBindVertexBuffers(
    vk_commandBuffer,
    0,
    1,
    vk_vertexBuffers.data(),
    vk_vertexOffsets.data()
  );

  vkCmdDraw(
    vk_commandBuffer,
    3,
    1,
    0,
    0
  );
}
vkCmdEndRenderPass(vk_commandBuffer);

Как видите, мы делаем конвейер активным (vkCmdBindPipeline), после чего привязываем наш буфер данных на позицию 0 (vkCmdBindVertexBuffers) и производим вывод трех вершин (vkCmdDraw).

Результатом нашей долгой работы является зеленый треугольник на красном фоне:

2016-09-12 14_40_57-window | Vulkan API «Hello Triangle»

Исходный код: VulkanTutorial

Страницы: 1 2 3 4 5 6 7

#Vulkan, #основы

15 сентября 2016

Комментарии [69]