Files
RedScarfEngine/graphics/src/pipeline_builder.c
T

620 lines
23 KiB
C

/**
* @file pipeline_builder.c
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief
* @version 0.1
* @date 2024-03-01
*
* @copyright Copyright (c) 2024
*
*/
#include "pipeline_builder.h"
#include <assert.h>
#include <stdint.h>
#include "src/graphics_context.h"
#include "utilities/commons.h"
#include "utilities/errors_common.h"
#include "utilities/file_utils.h"
#include "utilities/linked_list.h"
#include "utilities/localization.h"
#include "vulkan/vulkan_core.h"
#include "vulkan_errors.h"
#include "vulkan_image.h"
#define MAX_BINDLESS_RESOURCES 1000
RSE_LINKED_LIST_DEFINE(vertex_binding_list_t, VkVertexInputBindingDescription);
RSE_LINKED_LIST_DEFINE(shader_attribute_list_t,
VkVertexInputAttributeDescription);
RSE_LINKED_LIST_DEFINE(dynamic_state_list_t, VkDynamicState);
RSE_LINKED_LIST_DEFINE(pipeline_infos_list_t, VkGraphicsPipelineCreateInfo);
RSE_STACK_DEFINE(required_dynamic_states_t, VkDynamicState, 16U);
struct pipeline_t {
VkPipeline pipeline;
uint8_t id;
/* Shader stages */
size_t shader_stages_count;
VkPipelineShaderStageCreateInfo shader_stages[MAX_SHADER_STAGES];
/* Input assembly */
VkPipelineInputAssemblyStateCreateInfo input_assembly;
/* Viewport state */
VkPipelineViewportStateCreateInfo viewport_state;
/* Rasterizer */
VkPipelineRasterizationStateCreateInfo rasterizer;
/* Multisampling */
VkPipelineMultisampleStateCreateInfo multisampling;
/* Color blending */
VkPipelineColorBlendAttachmentState color_blend_attachment;
VkPipelineColorBlendStateCreateInfo color_blending;
/* Depth stencil */
VkPipelineDepthStencilStateCreateInfo depth_stencil;
struct vertex_binding_list_t* vertex_bindings;
struct shader_attribute_list_t* shader_attributes;
struct dynamic_state_list_t* dynamic_states;
struct required_dynamic_states_t required_dynamic_states;
};
/**
* @brief Creates shader module
*
* @param binary_data Shader compiled code
* @return rse_err_t RSE_ERROR_NO_ERROR on success
*/
static rse_err_t create_shader_module(struct graphics_context_t* context,
struct pipeline_t* pipeline,
const char* binary_data, size_t data_size)
{
VkShaderModuleCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.pNext = NULL;
create_info.flags = 0U;
create_info.codeSize = data_size;
create_info.pCode = (const uint32_t*)(binary_data);
RSE_LINKED_LIST_PUSH_FRONT(context->shader_modules, (VkShaderModule){0});
if (VK_SUCCESS !=
vkCreateShaderModule(context->device, &create_info, NULL,
&RSE_LINKED_LIST_FRONT(context->shader_modules))) {
LOGF(_("Failed to create a shader\n"));
return VULKAN_ERROR_SHADER_CREATION_FAILED;
}
return RSE_ERROR_NO_ERROR;
}
static rse_err_t pipeline_add_input_assembly(struct pipeline_t* pipeline)
{
/* For now input assebly is hardcoded */
pipeline->input_assembly.sType =
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
pipeline->input_assembly.pNext = NULL;
pipeline->input_assembly.flags = 0U;
pipeline->input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
pipeline->input_assembly.primitiveRestartEnable = VK_FALSE;
return RSE_ERROR_NO_ERROR;
}
static rse_err_t pipeline_add_viewport_state(struct pipeline_t* pipeline)
{
/* For now viewport is hardcoded */
pipeline->viewport_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
pipeline->viewport_state.pNext = NULL;
pipeline->viewport_state.flags = 0U;
pipeline->viewport_state.viewportCount = 1U;
pipeline->viewport_state.pViewports =
NULL; /* This is set during rendering */
pipeline->viewport_state.scissorCount = 1U;
pipeline->viewport_state.pScissors =
NULL; /* This is set during rendering */
if (pipeline->viewport_state.pViewports == NULL ||
pipeline->viewport_state.viewportCount == 0U) {
RSE_STACK_PUSH(pipeline->required_dynamic_states,
VK_DYNAMIC_STATE_VIEWPORT);
}
if (pipeline->viewport_state.pScissors == NULL ||
pipeline->viewport_state.scissorCount == 0U) {
RSE_STACK_PUSH(pipeline->required_dynamic_states,
VK_DYNAMIC_STATE_SCISSOR);
}
return RSE_ERROR_NO_ERROR;
}
static rse_err_t pipeline_add_rasterizer(struct pipeline_t* pipeline)
{
/* For now rasterizer is hardcoded */
pipeline->rasterizer.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
pipeline->rasterizer.pNext = NULL;
pipeline->rasterizer.flags = 0U;
pipeline->rasterizer.depthClampEnable =
VK_FALSE; /* VK_TRUE to play with shadow maps */
pipeline->rasterizer.rasterizerDiscardEnable =
VK_FALSE; /* Disable/Enable output to framebuffer*/
pipeline->rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
pipeline->rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
pipeline->rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
pipeline->rasterizer.depthBiasEnable = VK_FALSE;
pipeline->rasterizer.depthBiasConstantFactor = 0.0f;
pipeline->rasterizer.depthBiasClamp = 0.0f;
pipeline->rasterizer.depthBiasSlopeFactor = 0.0f;
pipeline->rasterizer.lineWidth = 1.0f;
return RSE_ERROR_NO_ERROR;
}
static rse_err_t pipeline_add_multisampling(struct pipeline_t* pipeline)
{
/* For now multisampling is hardcoded */
pipeline->multisampling.sType =
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
pipeline->multisampling.pNext = NULL;
pipeline->multisampling.flags = 0U;
pipeline->multisampling.rasterizationSamples = VK_SAMPLE_COUNT_8_BIT;
pipeline->multisampling.sampleShadingEnable = VK_FALSE;
pipeline->multisampling.minSampleShading = 1.0f;
pipeline->multisampling.pSampleMask = NULL;
pipeline->multisampling.alphaToCoverageEnable = VK_FALSE;
pipeline->multisampling.alphaToOneEnable = VK_FALSE;
return RSE_ERROR_NO_ERROR;
}
static rse_err_t pipeline_add_color_blending(struct pipeline_t* pipeline)
{
/* For now color blending is hardcoded */
pipeline->color_blend_attachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
pipeline->color_blend_attachment.blendEnable = VK_TRUE;
pipeline->color_blend_attachment.srcColorBlendFactor =
VK_BLEND_FACTOR_SRC_ALPHA;
pipeline->color_blend_attachment.dstColorBlendFactor =
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
pipeline->color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
pipeline->color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
pipeline->color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
pipeline->color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
pipeline->color_blending.sType =
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
pipeline->color_blending.pNext = NULL;
pipeline->color_blending.flags = 0U;
pipeline->color_blending.logicOpEnable = VK_FALSE;
pipeline->color_blending.logicOp = VK_LOGIC_OP_COPY;
pipeline->color_blending.attachmentCount = 1U;
pipeline->color_blending.pAttachments = &pipeline->color_blend_attachment;
pipeline->color_blending.blendConstants[0] = 0.0f;
pipeline->color_blending.blendConstants[1] = 0.0f;
pipeline->color_blending.blendConstants[2] = 0.0f;
pipeline->color_blending.blendConstants[3] = 0.0f;
return RSE_ERROR_NO_ERROR;
}
static rse_err_t pipeline_add_depth_stencil(struct pipeline_t* pipeline)
{
/* For now depth stencil is hardcoded */
pipeline->depth_stencil.sType =
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
pipeline->depth_stencil.pNext = NULL;
pipeline->depth_stencil.flags = 0U;
pipeline->depth_stencil.depthTestEnable = VK_TRUE;
pipeline->depth_stencil.depthWriteEnable = VK_TRUE;
pipeline->depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS;
pipeline->depth_stencil.depthBoundsTestEnable = VK_FALSE;
pipeline->depth_stencil.stencilTestEnable = VK_FALSE;
pipeline->depth_stencil.minDepthBounds = 0.0f;
pipeline->depth_stencil.maxDepthBounds = 1.0f;
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Create a Pipeline Layout
*
* @return rse_err_t RSE_ERROR_NO_ERROR on success. Possible errors:
* VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED
*/
static rse_err_t create_pipeline_layout(struct graphics_context_t* context)
{
VkPipelineLayoutCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
create_info.pNext = NULL;
create_info.flags = 0U;
create_info.setLayoutCount =
context->pipelines_data.descriptor_set_layouts_count;
create_info.pSetLayouts = context->pipelines_data.descriptor_set_layouts;
create_info.pushConstantRangeCount = 0U;
create_info.pPushConstantRanges = NULL;
if (VK_SUCCESS !=
vkCreatePipelineLayout(context->device, &create_info, NULL,
&context->pipelines_data.pipeline_layout)) {
LOGF(_("Failed to create pipeline layout\n"));
return VULKAN_ERROR_PIPELINE_LAYOUT_CREATION_FAILED;
}
return RSE_ERROR_NO_ERROR;
}
/**
* @brief Clean up pipeline builder. This function should be called after all
* pipelines are built and no more pipelines are needed. It frees all resources
* used by pipeline builder.
*
* @param[in/out] pipelines List of pipelines to clean up
*/
static void pipeline_builder_cleanup(struct pipeline_infos_list_t* pipelines)
{
size_t pipeline_count = RSE_LINKED_LIST_SIZE(pipelines);
VkVertexInputBindingDescription* vertex_bindings = NULL;
VkVertexInputAttributeDescription* shader_attributes = NULL;
VkDynamicState* dynamic_states = NULL;
for (size_t i = 0; i < pipeline_count; i++) {
VkGraphicsPipelineCreateInfo* pipeline_info =
&RSE_LINKED_LIST_FRONT(pipelines);
if (pipeline_info != NULL) {
vertex_bindings =
(VkVertexInputBindingDescription*)pipeline_info
->pVertexInputState->pVertexBindingDescriptions;
shader_attributes =
(VkVertexInputAttributeDescription*)pipeline_info
->pVertexInputState->pVertexAttributeDescriptions;
dynamic_states =
(VkDynamicState*)pipeline_info->pDynamicState->pDynamicStates;
rse_free((VkPipelineVertexInputStateCreateInfo*)
pipeline_info->pVertexInputState);
rse_free((VkPipelineDynamicStateCreateInfo*)
pipeline_info->pDynamicState);
rse_free(vertex_bindings);
rse_free(shader_attributes);
rse_free(dynamic_states);
}
RSE_LINKED_LIST_POP_FRONT(pipelines);
}
if (RSE_LINKED_LIST_IS_EMPTY(pipelines) == 0) {
LOGF(_("Failed to clean up pipelines\n"));
}
}
rse_err_t pipeline_add_dynamic_state(struct pipeline_t* pipeline,
VkDynamicState dynamic_state)
{
if (dynamic_state >= VK_DYNAMIC_STATE_MAX_ENUM) {
LOGF(_("Invalid dynamic state in pipeline\n"));
return VULKAN_ERROR_PIPELINE_INVALID_DYNAMIC_STATE;
}
RSE_LINKED_LIST_PUSH_FRONT(pipeline->dynamic_states, dynamic_state);
return RSE_ERROR_NO_ERROR;
}
rse_err_t pipeline_builder_init(struct pipeline_t** pipeline)
{
rse_err_t status = RSE_ERROR_NO_ERROR;
struct pipeline_t* pipeline_ptr = NULL;
if (*pipeline != NULL) {
LOGF(
_("Pipeline is not NULL. This function will initialize everything "
"for you, no need for manual allocation\n"));
return VULKAN_ERROR_PIPELINE_ALREADY_INITIALIZED;
}
rse_malloc(pipeline_ptr, sizeof(struct pipeline_t));
rse_memset(pipeline_ptr, 0, sizeof(struct pipeline_t));
*pipeline = pipeline_ptr;
return status;
}
rse_err_t pipeline_add_shader_stage(struct graphics_context_t* context,
struct pipeline_t* pipeline,
const char* shader_path,
const VkShaderStageFlagBits shader_stage)
{
size_t i = 0;
rse_err_t status = RSE_ERROR_NO_ERROR;
char* buffer = NULL;
size_t buffer_size = 0U;
/* Check if we have enough space for new shader stages */
if (pipeline->shader_stages_count + 1 > MAX_SHADER_STAGES) {
LOGF(_("Too many shader stages in pipeline\n"));
return VULKAN_ERROR_PIPELINE_TOO_MANY_SHADER_STAGES;
}
i = pipeline->shader_stages_count++;
/* Check if shader module is valid */
if (shader_stage >= VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM) {
LOGF(_("Invalid shader stage in pipeline\n"));
return VULKAN_ERROR_PIPELINE_INVALID_SHADER_STAGE;
}
/* Read shader file */
read_file(shader_path, &buffer_size, NULL);
rse_malloc(buffer, buffer_size);
read_file(shader_path, &buffer_size, buffer);
STATUS_CHECK(create_shader_module(context, pipeline, buffer, buffer_size));
free(buffer);
pipeline->shader_stages[i].sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
pipeline->shader_stages[i].pNext = NULL;
pipeline->shader_stages[i].flags = 0U;
pipeline->shader_stages[i].stage = shader_stage;
pipeline->shader_stages[i].module = RSE_LINKED_LIST_FRONT(
context->shader_modules); /* This is automatically updated during
shader module creation */
pipeline->shader_stages[i].pName = "main";
pipeline->shader_stages[i].pSpecializationInfo = NULL;
return status;
}
rse_err_t pipeline_add_vertex_input_binding(struct pipeline_t* pipeline,
const uint8_t binding,
const uint8_t stride,
const VkVertexInputRate input_rate)
{
VkVertexInputBindingDescription binding_pipeline_description = {};
if (input_rate >= VK_VERTEX_INPUT_RATE_MAX_ENUM) {
LOGF(_("Invalid vertex input rate\n"));
return VULKAN_ERROR_PIPELINE_INVALID_VERTEX_INPUT_RATE;
}
binding_pipeline_description.binding = binding;
binding_pipeline_description.stride = stride;
binding_pipeline_description.inputRate = input_rate;
RSE_LINKED_LIST_PUSH_FRONT(pipeline->vertex_bindings,
binding_pipeline_description);
return RSE_ERROR_NO_ERROR;
}
rse_err_t pipeline_add_vertex_input_attribute(struct pipeline_t* pipeline,
const uint8_t binding,
const uint8_t location,
const VkFormat format,
const uint8_t offset)
{
VkVertexInputAttributeDescription attribute_description = {};
if (binding >= 16U) {
LOGF(_("Invalid binding in pipeline attribute description\n"));
return VULKAN_ERROR_PIPELINE_INVALID_BINDING;
}
if (format >= VK_FORMAT_MAX_ENUM) {
LOGF(_("Invalid format in pipeline attribute description\n"));
return VULKAN_ERROR_PIPELINE_INVALID_FORMAT;
}
attribute_description.binding = binding;
attribute_description.location = location;
attribute_description.format = format;
attribute_description.offset = offset;
RSE_LINKED_LIST_PUSH_FRONT(pipeline->shader_attributes,
attribute_description);
return RSE_ERROR_NO_ERROR;
}
rse_err_t pipelines_build(struct graphics_context_t* context,
struct pipeline_infos_list_t* pipelines_infos_list)
{
assert(NULL != context);
assert(NULL != pipelines_infos_list);
size_t i = 0U;
rse_err_t status = RSE_ERROR_NO_ERROR;
size_t pipelines_count = RSE_LINKED_LIST_SIZE(pipelines_infos_list);
VkGraphicsPipelineCreateInfo* pipeline_infos = NULL;
context->pipelines_data.pipelines_count = pipelines_count;
STATUS_CHECK(create_pipeline_layout(context));
rse_malloc(pipeline_infos,
sizeof(VkGraphicsPipelineCreateInfo) * pipelines_count);
RSE_LINKED_LIST_TO_ARRAY(pipelines_infos_list, pipeline_infos);
/* FIXME: layout is created to late. Maybe move it somewhere else so we
* don't need this loop? */
for (i = 0; i < pipelines_count; ++i) {
pipeline_infos[i].layout = context->pipelines_data.pipeline_layout;
}
if (VK_SUCCESS !=
vkCreateGraphicsPipelines(context->device, VK_NULL_HANDLE,
pipelines_count, pipeline_infos, NULL,
context->pipelines_data.pipelines)) {
LOGF(_("Failed to create graphics pipelines\n"));
status = VULKAN_ERROR_GRAPHICSPIPELINE_CREATION_FAILED;
}
rse_free(pipeline_infos);
pipeline_builder_cleanup(pipelines_infos_list);
return status;
}
rse_err_t add_pipeline(const struct graphics_context_t* context,
struct pipeline_t* pipeline,
struct pipeline_infos_list_t** pipelines_infos)
{
/* Check for send and required dynamic states */
rse_err_t status = RSE_ERROR_NO_ERROR;
VkGraphicsPipelineCreateInfo pipeline_info = {};
size_t vertex_bindings_count = 0U;
VkVertexInputBindingDescription* vertex_bindings = NULL;
VkPipelineVertexInputStateCreateInfo* vertex_bindings_ci = NULL;
size_t shader_attributes_count = 0U;
VkVertexInputAttributeDescription* shader_attributes = NULL;
size_t dynamic_states_count = 0U;
VkDynamicState* dynamic_states = NULL;
VkPipelineDynamicStateCreateInfo* dynamic_states_ci = NULL;
uint8_t dynamic_state_found = 0;
while (RSE_STACK_IS_EMPTY(pipeline->required_dynamic_states) == 0) {
dynamic_state_found = 0;
VkDynamicState dynamic_state =
RSE_STACK_POP(pipeline->required_dynamic_states);
RSE_LINKED_LIST_FOREACH(pipeline->dynamic_states, dynamic_state_ptr)
{
if (dynamic_state_ptr->data == dynamic_state) {
dynamic_state_found = 1;
break;
}
}
if (dynamic_state_found == 0) {
LOGF(_("Missing dynamic state in pipeline\n"));
return VULKAN_ERROR_PIPELINE_MISSING_DYNAMIC_STATE;
}
}
/* Check for shader modules */
if (RSE_LINKED_LIST_IS_EMPTY(context->shader_modules) == 1) {
LOGF(_("Missing shader modules in pipeline\n"));
return VULKAN_ERROR_PIPELINE_MISSING_SHADER_MODULES;
}
/* Check for required shader stages */
if (pipeline->shader_stages_count == 0) {
LOGF(
_("Missing shader stages in pipeline. There must be at least "
"one.\n"));
return VULKAN_ERROR_PIPELINE_MISSING_SHADER_STAGES;
}
/* Check for required vertex bindings */
if (pipeline->vertex_bindings == NULL) {
LOGF(_("Missing vertex bindings in pipeline\n"));
return VULKAN_ERROR_PIPELINE_MISSING_VERTEX_BINDINGS;
}
/* Check for required shader attributes */
if (pipeline->shader_attributes == NULL) {
LOGF(_("Missing shader attributes in pipeline\n"));
return VULKAN_ERROR_PIPELINE_MISSING_SHADER_ATTRIBUTES;
}
/* Convert linked list to array */
vertex_bindings_count =
RSE_LINKED_LIST_TO_ARRAY(pipeline->vertex_bindings, vertex_bindings);
shader_attributes_count = RSE_LINKED_LIST_TO_ARRAY(
pipeline->shader_attributes, shader_attributes);
dynamic_states_count =
RSE_LINKED_LIST_TO_ARRAY(pipeline->dynamic_states, dynamic_states);
/* Build constant pipeline. TODO: Some parts may be customizable */
pipeline_add_input_assembly(pipeline);
pipeline_add_viewport_state(pipeline);
pipeline_add_rasterizer(pipeline);
pipeline_add_multisampling(pipeline);
pipeline_add_color_blending(pipeline);
pipeline_add_depth_stencil(pipeline);
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info.pNext = NULL;
pipeline_info.flags = 0U;
pipeline_info.stageCount = pipeline->shader_stages_count;
pipeline_info.pStages = pipeline->shader_stages;
rse_malloc(vertex_bindings_ci,
sizeof(VkPipelineVertexInputStateCreateInfo));
vertex_bindings_ci->sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_bindings_ci->pNext = NULL;
vertex_bindings_ci->flags = 0U;
vertex_bindings_ci->vertexBindingDescriptionCount = vertex_bindings_count;
vertex_bindings_ci->pVertexBindingDescriptions = vertex_bindings;
vertex_bindings_ci->vertexAttributeDescriptionCount =
shader_attributes_count;
vertex_bindings_ci->pVertexAttributeDescriptions = shader_attributes;
pipeline_info.pVertexInputState = vertex_bindings_ci;
pipeline_info.pInputAssemblyState = &pipeline->input_assembly;
pipeline_info.pTessellationState = NULL;
pipeline_info.pViewportState = &pipeline->viewport_state;
pipeline_info.pRasterizationState = &pipeline->rasterizer;
pipeline_info.pMultisampleState = &pipeline->multisampling;
pipeline_info.pDepthStencilState = &pipeline->depth_stencil;
pipeline_info.pColorBlendState = &pipeline->color_blending;
rse_malloc(dynamic_states_ci, sizeof(VkPipelineDynamicStateCreateInfo));
dynamic_states_ci->sType =
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_states_ci->pNext = NULL;
dynamic_states_ci->flags = 0U;
dynamic_states_ci->dynamicStateCount = dynamic_states_count;
dynamic_states_ci->pDynamicStates = dynamic_states;
pipeline_info.pDynamicState = dynamic_states_ci;
pipeline_info.layout = context->pipelines_data.pipeline_layout;
pipeline_info.renderPass = context->render_pass;
pipeline_info.subpass = 0;
pipeline_info.basePipelineHandle =
VK_NULL_HANDLE; // TODO: Add possiblity do derive from another pipeline
pipeline_info.basePipelineIndex = -1;
RSE_LINKED_LIST_PUSH_BACK(*pipelines_infos, pipeline_info);
return status;
}
void destroy_pipelines(struct graphics_context_t* context)
{
size_t i = 0;
size_t shader_modules_count = RSE_LINKED_LIST_SIZE(context->shader_modules);
for (i = 0; i < context->pipelines_data.pipelines_count; i++) {
if (context->pipelines_data.pipelines[i] != VK_NULL_HANDLE) {
vkDestroyPipeline(context->device,
context->pipelines_data.pipelines[i], NULL);
}
}
for (i = 0; i < context->pipelines_data.descriptor_set_layouts_count; i++) {
if (context->pipelines_data.descriptor_set_layouts[i] !=
VK_NULL_HANDLE) {
vkDestroyDescriptorSetLayout(
context->device,
context->pipelines_data.descriptor_set_layouts[i], NULL);
}
}
for (i = 0; i < shader_modules_count; i++) {
if (RSE_LINKED_LIST_FRONT(context->shader_modules) != VK_NULL_HANDLE) {
vkDestroyShaderModule(
context->device, RSE_LINKED_LIST_FRONT(context->shader_modules),
NULL);
}
RSE_LINKED_LIST_POP_FRONT(context->shader_modules);
}
}