/* * Copyright © 2025 Erico Nunes * * based on gl-shaders.c: * Copyright 2012 Intel Corporation * Copyright 2015,2019,2021 Collabora, Ltd. * Copyright 2016 NVIDIA Corporation * Copyright 2019 Harish Krupo * Copyright 2019 Intel Corporation * Copyright 2021 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "config.h" #include #include #include #include "vulkan-renderer.h" #include "vulkan-renderer-internal.h" /* const uint32_t vulkan_vertex_shader_surface[]; vulkan_vertex_shader_surface.vert */ #include "vulkan_vertex_shader_surface.spv.h" /* const uint32_t vulkan_vertex_shader_texcoord[]; vulkan_vertex_shader_texcoord.vert */ #include "vulkan_vertex_shader_texcoord.spv.h" /* const uint32_t vulkan_fragment_shader[]; vulkan_fragment_shader.frag */ #include "vulkan_fragment_shader.spv.h" struct vertex { float pos[2]; }; struct vertex_tc { float pos[2]; float texcoord[2]; }; struct fs_specialization_consts { uint32_t c_variant; uint32_t c_input_is_premult; uint32_t c_green_tint; }; static void create_graphics_pipeline(struct vulkan_renderer *vr, const struct vulkan_pipeline_requirements *req, struct vulkan_pipeline *pipeline) { VkResult result; VkShaderModule vs_module; VkShaderModuleCreateInfo vs_shader_module_create_info = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, }; switch (req->texcoord_input) { case SHADER_TEXCOORD_INPUT_ATTRIB: vs_shader_module_create_info.codeSize = sizeof(vulkan_vertex_shader_texcoord), vs_shader_module_create_info.pCode = (uint32_t *)vulkan_vertex_shader_texcoord; break; case SHADER_TEXCOORD_INPUT_SURFACE: vs_shader_module_create_info.codeSize = sizeof(vulkan_vertex_shader_surface); vs_shader_module_create_info.pCode = (uint32_t *)vulkan_vertex_shader_surface; break; default: weston_log("Invalid req->texcoord_input\n"); abort(); } vkCreateShaderModule(vr->dev, &vs_shader_module_create_info, NULL, &vs_module); const struct fs_specialization_consts fsc = { req->variant, req->input_is_premult, req->green_tint, }; const VkSpecializationMapEntry fsc_entries[] = { { 0, offsetof(struct fs_specialization_consts, c_variant), sizeof(fsc.c_variant) }, { 1, offsetof(struct fs_specialization_consts, c_input_is_premult), sizeof(fsc.c_input_is_premult) }, { 2, offsetof(struct fs_specialization_consts, c_green_tint), sizeof(fsc.c_green_tint) }, }; const VkSpecializationInfo fs_specialization = { .mapEntryCount = ARRAY_LENGTH(fsc_entries), .pMapEntries = fsc_entries, .dataSize = sizeof(fsc), .pData = &fsc, }; VkShaderModule fs_module; const VkShaderModuleCreateInfo fs_shader_module_create_info = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = sizeof(vulkan_fragment_shader), .pCode = (uint32_t *)vulkan_fragment_shader, }; vkCreateShaderModule(vr->dev, &fs_shader_module_create_info, NULL, &fs_module); const VkPipelineShaderStageCreateInfo vert_shader_stage_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = vs_module, .pName = "main", }; const VkPipelineShaderStageCreateInfo frag_shader_stage_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = fs_module, .pSpecializationInfo = &fs_specialization, .pName = "main", }; const VkPipelineShaderStageCreateInfo shader_stages[] = {vert_shader_stage_info, frag_shader_stage_info}; // SHADER_TEXCOORD_INPUT_ATTRIB const VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_attrib = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .vertexBindingDescriptionCount = 1, .pVertexBindingDescriptions = (VkVertexInputBindingDescription[]) { { .binding = 0, .stride = sizeof(struct vertex_tc), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX }, }, .vertexAttributeDescriptionCount = 2, .pVertexAttributeDescriptions = (VkVertexInputAttributeDescription[]) { { .binding = 0, .location = 0, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(struct vertex_tc, pos), }, { .binding = 0, .location = 1, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(struct vertex_tc, texcoord), }, } }; // SHADER_TEXCOORD_INPUT_SURFACE const VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_surface = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .vertexBindingDescriptionCount = 1, .pVertexBindingDescriptions = (VkVertexInputBindingDescription[]) { { .binding = 0, .stride = sizeof(struct vertex), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX }, }, .vertexAttributeDescriptionCount = 1, .pVertexAttributeDescriptions = (VkVertexInputAttributeDescription[]) { { .binding = 0, .location = 0, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(struct vertex, pos), }, } }; const VkPipelineVertexInputStateCreateInfo *pipeline_vertex_input_state_create_info; switch (req->texcoord_input) { case SHADER_TEXCOORD_INPUT_ATTRIB: pipeline_vertex_input_state_create_info = &pipeline_vertex_input_attrib; break; case SHADER_TEXCOORD_INPUT_SURFACE: pipeline_vertex_input_state_create_info = &pipeline_vertex_input_surface; break; default: weston_log("Invalid req->texcoord_input\n"); abort(); } const VkPipelineInputAssemblyStateCreateInfo input_assembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, .primitiveRestartEnable = VK_FALSE, }; const VkPipelineViewportStateCreateInfo viewport_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .viewportCount = 1, .scissorCount = 1, }; const VkPipelineRasterizationStateCreateInfo rasterizer = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .depthClampEnable = VK_FALSE, .rasterizerDiscardEnable = VK_FALSE, .polygonMode = VK_POLYGON_MODE_FILL, .lineWidth = 1.0f, .cullMode = VK_CULL_MODE_NONE, .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .depthBiasEnable = VK_FALSE, }; const VkPipelineMultisampleStateCreateInfo multisampling = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .sampleShadingEnable = VK_FALSE, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, }; VkPipelineColorBlendAttachmentState color_blend_attachment = { .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, .blendEnable = VK_FALSE, }; if (req->blend) { color_blend_attachment.blendEnable = VK_TRUE; color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; } const VkPipelineColorBlendStateCreateInfo color_blending = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .logicOpEnable = VK_FALSE, .logicOp = VK_LOGIC_OP_COPY, .attachmentCount = 1, .pAttachments = &color_blend_attachment, }; const VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; const VkPipelineDynamicStateCreateInfo dynamic_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .dynamicStateCount = ARRAY_LENGTH(dynamic_states), .pDynamicStates = dynamic_states, }; const VkPipelineLayoutCreateInfo pipeline_layout_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, .pSetLayouts = &pipeline->descriptor_set_layout, }; result = vkCreatePipelineLayout(vr->dev, &pipeline_layout_info, NULL, &pipeline->pipeline_layout); check_vk_success(result, "vkCreatePipelineLayout"); const VkGraphicsPipelineCreateInfo pipeline_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .stageCount = 2, .pStages = shader_stages, .pVertexInputState = pipeline_vertex_input_state_create_info, .pInputAssemblyState = &input_assembly, .pViewportState = &viewport_state, .pRasterizationState = &rasterizer, .pMultisampleState = &multisampling, .pColorBlendState = &color_blending, .pDynamicState = &dynamic_state, .layout = pipeline->pipeline_layout, .renderPass = req->renderpass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, }; result = vkCreateGraphicsPipelines(vr->dev, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &pipeline->pipeline); check_vk_success(result, "vkCreateGraphicsPipelines"); vkDestroyShaderModule(vr->dev, fs_module, NULL); vkDestroyShaderModule(vr->dev, vs_module, NULL); } static void create_descriptor_set_layout(struct vulkan_renderer *vr, struct vulkan_pipeline *pipeline) { VkResult result; const VkDescriptorSetLayoutBinding vs_ubo_layout_binding = { .binding = 0, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, }; const VkDescriptorSetLayoutBinding fs_ubo_layout_binding = { .binding = 1, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, }; const VkDescriptorSetLayoutBinding fs_sampler_layout_binding = { .binding = 2, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, }; const VkDescriptorSetLayoutBinding bindings[] = { vs_ubo_layout_binding, fs_ubo_layout_binding, fs_sampler_layout_binding, }; const VkDescriptorSetLayoutCreateInfo layout_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .bindingCount = ARRAY_LENGTH(bindings), .pBindings = bindings, }; result = vkCreateDescriptorSetLayout(vr->dev, &layout_info, NULL, &pipeline->descriptor_set_layout); check_vk_success(result, "vkCreateDescriptorSetLayout"); } static struct vulkan_pipeline * vulkan_pipeline_create(struct vulkan_renderer *vr, const struct vulkan_pipeline_requirements *reqs) { struct vulkan_pipeline *pipeline = NULL; pipeline = zalloc(sizeof *pipeline); if (!pipeline) { weston_log("could not create pipeline\n"); abort(); } wl_list_init(&pipeline->link); pipeline->key = *reqs; create_descriptor_set_layout(vr, pipeline); create_graphics_pipeline(vr, reqs, pipeline); wl_list_insert(&vr->pipeline_list, &pipeline->link); return pipeline; } void vulkan_pipeline_destroy(struct vulkan_renderer *vr, struct vulkan_pipeline *pipeline) { vkDestroyPipelineLayout(vr->dev, pipeline->pipeline_layout, NULL); vkDestroyPipeline(vr->dev, pipeline->pipeline, NULL); vkDestroyDescriptorSetLayout(vr->dev, pipeline->descriptor_set_layout, NULL); wl_list_remove(&pipeline->link); free(pipeline); } void vulkan_renderer_pipeline_list_destroy(struct vulkan_renderer *vr) { struct vulkan_pipeline *pipeline, *next_pipeline; wl_list_for_each_safe(pipeline, next_pipeline, &vr->pipeline_list, link) vulkan_pipeline_destroy(vr, pipeline); } static int vulkan_pipeline_requirements_cmp(const struct vulkan_pipeline_requirements *a, const struct vulkan_pipeline_requirements *b) { return memcmp(a, b, sizeof(*a)); } struct vulkan_pipeline * vulkan_renderer_get_pipeline(struct vulkan_renderer *vr, const struct vulkan_pipeline_requirements *reqs) { struct vulkan_pipeline *pipeline; wl_list_for_each(pipeline, &vr->pipeline_list, link) { if (vulkan_pipeline_requirements_cmp(reqs, &pipeline->key) == 0) return pipeline; } pipeline = vulkan_pipeline_create(vr, reqs); if (pipeline) return pipeline; return NULL; }