Modules/RasterRender/private/VulkanObjectPipeline.cpp
Шурупов Илья Викторович 6d200eb171 Port rendering stack from OpenGL to Vulkan
Replace the OpenGL rendering backend with Vulkan across the engine:

- Graphics: add VulkanContext (instance/device/swapchain/renderpass with
  depth-stencil), port Canvas to nanovg-on-Vulkan and DebugGUI to the imgui
  Vulkan backend, drop GLEW. Wait for the Wayland configure handshake before
  the first present and rebuild the swapchain to the confirmed size so the
  window maps reliably on fractional-scale outputs.
- RasterRender: reimplement RenderBuffer, shaders and object pipeline on
  Vulkan (offscreen VkImage target with MSAA resolve, SPIR-V pipelines,
  push-constant MVP, depth remap for Vulkan clip space).
- Sketch3D: port the stroke renderer (geometry-shader line expansion) and
  stroke GPU buffers to Vulkan.
- 3DEditor: port the path-tracer texture upload to a Vulkan staging upload
  and keep raster/path-tracer viewports oriented consistently.
- Add a nanovg external-VkImage bridge plus a render-target registry so 3D
  render targets can be shown inside the 2D canvas.
- Prefer the Wayland platform in GLFW regardless of DISPLAY.
- Widgets: make BasicLayout size distribution converge with a sub-pixel
  epsilon instead of an exact-zero comparison, which could otherwise spin
  forever on fractional-scale displays and block the first frame.

Vendor nanovg_vulkan and add a standalone VulkanGraphics module.
2026-10-13 03:04:08 +03:00

188 lines
6.1 KiB
C++

#include "VulkanObjectPipeline.hpp"
#include <array>
#include <cstdint>
#include <stdexcept>
#include <vector>
namespace {
const uint32_t kDefaultVert[] =
#include "default.vert.inc"
;
const uint32_t kDefaultFrag[] =
#include "default.frag.inc"
;
const uint32_t kSolidFrag[] =
#include "solid.frag.inc"
;
}
namespace tp {
VulkanObjectPipeline::VulkanObjectPipeline(VulkanContext* context, VkRenderPass renderPass,
VkSampleCountFlagBits samples, bool solidColor)
: mContext(context) {
const VkDevice device = mContext->device();
VkShaderModule vertModule = createShaderModule(kDefaultVert, sizeof(kDefaultVert));
VkShaderModule fragModule = solidColor ? createShaderModule(kSolidFrag, sizeof(kSolidFrag))
: createShaderModule(kDefaultFrag, sizeof(kDefaultFrag));
VkPushConstantRange pushRange{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0,
.size = sizeof(ObjectPushConstants),
};
VkPipelineLayoutCreateInfo layoutInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushRange,
};
if (vkCreatePipelineLayout(device, &layoutInfo, nullptr, &mPipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("object pipeline: failed to create layout");
}
const std::array<VkPipelineShaderStageCreateInfo, 2> stages = {
VkPipelineShaderStageCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vertModule,
.pName = "main",
},
VkPipelineShaderStageCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = fragModule,
.pName = "main",
},
};
VkVertexInputBindingDescription binding{
.binding = 0,
.stride = sizeof(Vec3F),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
};
VkVertexInputAttributeDescription attribute{
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = 0,
};
VkPipelineVertexInputStateCreateInfo vertexInput{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &binding,
.vertexAttributeDescriptionCount = 1,
.pVertexAttributeDescriptions = &attribute,
};
VkPipelineInputAssemblyStateCreateInfo inputAssembly{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
};
VkPipelineViewportStateCreateInfo viewportState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1,
};
VkPipelineRasterizationStateCreateInfo rasterization{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.lineWidth = 1.0f,
};
VkPipelineMultisampleStateCreateInfo multisample{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = samples,
};
VkPipelineDepthStencilStateCreateInfo depthStencil{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL,
};
VkPipelineColorBlendAttachmentState blendAttachment{
.blendEnable = VK_FALSE,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
};
VkPipelineColorBlendStateCreateInfo colorBlend{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &blendAttachment,
};
const std::array<VkDynamicState, 2> dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamicState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size()),
.pDynamicStates = dynamicStates.data(),
};
VkGraphicsPipelineCreateInfo pipelineInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = static_cast<uint32_t>(stages.size()),
.pStages = stages.data(),
.pVertexInputState = &vertexInput,
.pInputAssemblyState = &inputAssembly,
.pViewportState = &viewportState,
.pRasterizationState = &rasterization,
.pMultisampleState = &multisample,
.pDepthStencilState = &depthStencil,
.pColorBlendState = &colorBlend,
.pDynamicState = &dynamicState,
.layout = mPipelineLayout,
.renderPass = renderPass,
.subpass = 0,
};
const VkResult result =
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &mPipeline);
vkDestroyShaderModule(device, vertModule, nullptr);
vkDestroyShaderModule(device, fragModule, nullptr);
if (result != VK_SUCCESS) {
throw std::runtime_error("object pipeline: failed to create graphics pipeline");
}
}
VulkanObjectPipeline::~VulkanObjectPipeline() {
const VkDevice device = mContext->device();
if (mPipeline != VK_NULL_HANDLE) vkDestroyPipeline(device, mPipeline, nullptr);
if (mPipelineLayout != VK_NULL_HANDLE) vkDestroyPipelineLayout(device, mPipelineLayout, nullptr);
}
VkShaderModule VulkanObjectPipeline::createShaderModule(const uint32_t* code, size_t sizeBytes) {
VkShaderModuleCreateInfo createInfo{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = sizeBytes,
.pCode = code,
};
VkShaderModule module = VK_NULL_HANDLE;
if (vkCreateShaderModule(mContext->device(), &createInfo, nullptr, &module) != VK_SUCCESS) {
throw std::runtime_error("object pipeline: failed to create shader module");
}
return module;
}
void VulkanObjectPipeline::bind(VkCommandBuffer commandBuffer) {
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mPipeline);
}
void VulkanObjectPipeline::pushUniforms(VkCommandBuffer commandBuffer, const ObjectPushConstants& uniforms) {
vkCmdPushConstants(commandBuffer, mPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0,
sizeof(ObjectPushConstants), &uniforms);
}
}