zengine/engine/modules/render/vulkan/src/vulkan_api.cpp
2024-12-26 21:54:38 +08:00

666 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "vkn/vulkan_api.h"
#include "vkn/vulkan_window.h"
#include "vkn/vulkan_api_help.h"
#include "vkn/wrapper/device.h"
#include "vkn/thread/buffer_worker.h"
#include "vkn/thread/command_worker.h"
#include "vkn/loader/vulkan_glsl_loader.h"
#include "render/asset/mesh.h"
#include "event/event_system.h"
#include "tinyimageformat/tinyimageformat_apis.h"
#include "zlog.h"
namespace vkn {
using api::EventSystem;
VulkanAPI::VulkanAPI(VulkanWindow* pWindow) : RenderAPI(new VulkanContext())
, window(*pWindow)
, backend(VulkanEngineName)
{
}
void VulkanAPI::Init()
{
Backend::TransferWorker->InitCommandBuffers(context.frameCount);
}
void VulkanAPI::Shutdown()
{
}
void VulkanAPI::SetStaticMesh(Mesh& mesh)
{
auto& Indices = mesh.GetIndices();
auto& Vertices = mesh.GetVertices();
MeshVAO& VAO = MeshTable[mesh.GetGuid()];
VAO.indexCount = Indices.size();
VAO.vertexCount = Vertices.size();
BufferUpload indexBuffer{};
indexBuffer.pBuffer = &VAO.indexBuffer;
indexBuffer.pAllocation = &VAO.indexAllocation;
indexBuffer.pCpuData = Indices.data();
indexBuffer.size = sizeof(decltype(Indices[0])) * Indices.size();
indexBuffer.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
Backend::TransferWorker->Invoke(indexBuffer);
BufferUpload vertexBuffer{};
vertexBuffer.pBuffer = &VAO.vertexBuffer;
vertexBuffer.pAllocation = &VAO.vertexAllocation;
vertexBuffer.pCpuData = Vertices.data();
vertexBuffer.size = Vertices.data_size();
vertexBuffer.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
Backend::TransferWorker->Invoke(vertexBuffer);
}
void VulkanAPI::DrawStaticMesh(Mesh& mesh)
{
MeshVAO& vulkanVAO = MeshTable[mesh.GetGuid()];
if (!vulkanVAO.indexBuffer || !vulkanVAO.vertexBuffer) {
return;
}
VulkanPipeline& pipeline = PipelineTable[mesh.GetShaderGuid()];
VulkanContext& ctx = *(VulkanContext*)&context;
VkCommandBuffer ptr = ctx.command;
VkBuffer vertexBuffers[] = { vulkanVAO.vertexBuffer };
VkDeviceSize offsets[] = { 0 };
vkCmdBindVertexBuffers(ptr, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(ptr, vulkanVAO.indexBuffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindPipeline(ptr, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline);
if (pipeline.descCount > 0) {
auto& materialInst = mesh.GetMaterialInstance();
auto& materialInfo = materialInst.GetInfo();
auto& gpuBlock = materialInfo.gpuBlock;
uint32_t staticCount = materialInfo.staticBlock.count;
materialInfo.staticBlock.Upload(gpuBlock.pMappingAddr);
materialInfo.classBlock.Upload(gpuBlock.pMappingAddr + staticCount);
vkCmdBindDescriptorSets(ptr, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipelineLayout, 0, pipeline.descCount, pipeline.descList, 0, VK_NULL_HANDLE);
}
vkCmdDrawIndexed(ptr, vulkanVAO.indexCount, 1, 0, 0, 0);
}
void VulkanAPI::SetUpMaterialInstance(MaterialInstance& material)
{
Shader& shader = *material->GetShader();
VulkanPipeline& pipeline = PipelineTable[shader.GetGuid()];
pmr::vector<api::ShaderProgram*> programList{ FramePool() };
programList.push_back(shader.GetVertHandle().Ptr());
programList.push_back(shader.GetFragHandle().Ptr());
VulkanGlslLoader::LoadShaderBuffer(pipeline.descList, programList, material.GetInfo());
}
void VulkanAPI::LoadShader(Shader& shader, size_t passKey)
{
auto itPass = RenderPassCache.find(passKey);
if (itPass == RenderPassCache.end()) {
return;
}
VkRenderPass renderpass = itPass->second.pass;
pmr::vector<VkPipelineShaderStageCreateInfo> shaderStages;
std::map<VkShaderStageFlagBits, VkShaderModule> shaderModules;
auto& device = backend.GetDevice();
pmr::vector<api::ShaderProgram*> programList{FramePool()};
programList.push_back(shader.GetVertHandle().Ptr());
programList.push_back(shader.GetFragHandle().Ptr());
for (auto program : programList) {
vkShaderProgram* vkProgram = (vkShaderProgram*)program;
auto shaderModule = vkProgram->Ptr();
shaderModules.insert(std::make_pair(vkProgram->GetVkStage(), shaderModule));
}
for (auto& shaderModule : shaderModules)
{
VkPipelineShaderStageCreateInfo shaderStageInfo = {};
shaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStageInfo.stage = shaderModule.first;
shaderStageInfo.module = shaderModule.second;
shaderStageInfo.pName = "main";
shaderStages.push_back(shaderStageInfo);
}
auto meta = refl::find_meta(shader.Name(),string_hash("vkMeta"));
// 设置顶点输入格式
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
VkVertexInputBindingDescription bindingDescription = {};
bindingDescription.binding = 0;
bindingDescription.stride = meta->size;
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
//这里顶点属性不能大余16
std::array<VkVertexInputAttributeDescription, 16> attributeDescriptions = { };
{
uint32_t count = 0;
for (auto& field : meta->GetFields(refl::FIND_ALL_MEMBER, Name(""))) {
auto& attr = attributeDescriptions[count];
attr.binding = 0;
attr.location = count++;
attr.format = field.GetMeta() ? (VkFormat)field.GetMeta().CastTo<uint32_t>() : VK_FORMAT_R32G32B32_SFLOAT;
attr.offset = field.GetOffset();
}
vertexInputInfo.vertexAttributeDescriptionCount = count;
}
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
vertexInputInfo.vertexBindingDescriptionCount = 1;
// 设置图元
VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo = {};
inputAssemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssemblyInfo.primitiveRestartEnable = VK_FALSE;
// ViewPort信息这里不直接设置下面弄成动态的
VkPipelineViewportStateCreateInfo viewportStateInfo = {};
viewportStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportStateInfo.viewportCount = 1;
viewportStateInfo.scissorCount = 1;
// View Port和Scissor设置为动态每帧绘制时决定
pmr::vector<VkDynamicState> dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dynamicStateInfo = {};
dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStateInfo.pDynamicStates = dynamicStates.data();
dynamicStateInfo.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
// 设置光栅化阶段
VkPipelineRasterizationStateCreateInfo rasterizationInfo = {};
rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
// 如果depthClampEnable设置为VK_TRUE超过远近裁剪面的片元会进行收敛而不是丢弃它们
rasterizationInfo.depthClampEnable = VK_FALSE;
// 如果rasterizerDiscardEnable设置为VK_TRUE那么几何图元永远不会传递到光栅化阶段
// 这是禁止任何数据输出到framebuffer的方法
rasterizationInfo.rasterizerDiscardEnable = VK_FALSE;
// 设置片元如何从几何模型中产生如果不是FILL需要开启GPU feature
// VK_POLYGON_MODE_FILL: 多边形区域填充
// VK_POLYGON_MODE_LINE: 多边形边缘线框绘制
// VK_POLYGON_MODE_POINT : 多边形顶点作为描点绘制
rasterizationInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizationInfo.lineWidth = 1.0f;
rasterizationInfo.cullMode = VK_CULL_MODE_FRONT_BIT;
rasterizationInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
// 渲染阴影的偏移配置
rasterizationInfo.depthBiasEnable = VK_FALSE;
rasterizationInfo.depthBiasConstantFactor = 0.0f;
rasterizationInfo.depthBiasClamp = 0.0f;
rasterizationInfo.depthBiasSlopeFactor = 0.0f;
// 设置Shader采样纹理的MSAA(不是输出到屏幕上的MSAA)需要创建逻辑设备的时候开启VkPhysicalDeviceFeatures里的sampleRateShading才能生效暂时关闭
VkPipelineMultisampleStateCreateInfo multisampleInfo = {};
multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleInfo.sampleShadingEnable = (VK_SAMPLE_COUNT_1_BIT & VK_SAMPLE_COUNT_1_BIT ? VK_FALSE : VK_TRUE);
multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
// 这个是调整sampleShading效果的越接近1效果越平滑越接近0性能越好
multisampleInfo.minSampleShading = 1.0f;
multisampleInfo.pSampleMask = VK_NULL_HANDLE;
multisampleInfo.alphaToCoverageEnable = VK_FALSE;
multisampleInfo.alphaToOneEnable = VK_FALSE;
// Color Blend
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.attachmentCount = 1;
// 深度和模板配置
VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {};
depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
// Depth
depthStencilInfo.depthWriteEnable = VK_FALSE;
depthStencilInfo.depthTestEnable = VK_TRUE;
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS;
depthStencilInfo.depthBoundsTestEnable = VK_FALSE;
depthStencilInfo.minDepthBounds = 0.0f;
depthStencilInfo.maxDepthBounds = 1.0f;
// Stencil
depthStencilInfo.stencilTestEnable = VK_FALSE;
depthStencilInfo.front = {};
depthStencilInfo.back = {};
VulkanPipeline& vulkan_pipeline = PipelineTable[shader.GetGuid()];
vulkan_pipeline.descCount = VulkanGlslLoader::GetShaderLayout(vulkan_pipeline.descLayoutList, programList);
VkDescriptorSet descriptorSet;
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = backend.GetPool().Ptr();
allocInfo.descriptorSetCount = vulkan_pipeline.descCount;
allocInfo.pSetLayouts = vulkan_pipeline.descLayoutList;
vkAllocateDescriptorSets(device.Ptr(), &allocInfo, vulkan_pipeline.descList);
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = vulkan_pipeline.descCount;
pipelineLayoutInfo.pSetLayouts = vulkan_pipeline.descLayoutList;
VkPipelineLayout pipelineLayout;
if (vkCreatePipelineLayout(device.Ptr(), &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS)
throw std::runtime_error("failed to create pipeline layout!");
VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.pStages = shaderStages.data();
pipelineInfo.stageCount = (uint32_t)shaderStages.size();
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssemblyInfo;
pipelineInfo.pViewportState = &viewportStateInfo;
pipelineInfo.pDynamicState = &dynamicStateInfo;
pipelineInfo.pRasterizationState = &rasterizationInfo;
pipelineInfo.pMultisampleState = &multisampleInfo;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDepthStencilState = nullptr; //&depthStencilInfo;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = renderpass;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
pipelineInfo.basePipelineIndex = -1;
VkPipeline pipeLine;
if (vkCreateGraphicsPipelines(device.Ptr(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeLine) != VK_SUCCESS)
throw std::runtime_error("failed to create graphics pipeline!");
for (auto& shaderModule : shaderModules)
vkDestroyShaderModule(device.Ptr(), shaderModule.second, nullptr);
vulkan_pipeline.name = shader.Name();
vulkan_pipeline.pipeline = pipeLine;
vulkan_pipeline.inUse = true;
vulkan_pipeline.pipelineLayout = pipelineLayout;
}
void VulkanAPI::CreateBuffer(BufferDesc& desc)
{
BufferCreator creator{};
creator.size = desc.size;
creator.pBuffer = (VkBuffer*)&desc.buffer;
creator.pAllocation =(VmaAllocation*) &desc.pAllocation;
creator.ppCpuData = &desc.pMappingAddr;
creator.memoryUsage = vkApiGetMemoryUsage(desc.memoryUsage);
creator.momoryFlags = vkApiGetMemoryFlags(desc.memoryUsage);
creator.usage = vkApiGetBufferUsage(desc.usage);
Backend::TransferWorker->CreateBuffer(creator);
}
void VulkanAPI::CreateTexture(TextureDesc& desc)
{
VkImageCreateInfo imageCreateInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = NULL,
.flags = vkApiGetImageCreateFlag(desc.dimension, desc.arraySize),
.imageType = vkApiGetImageType(desc.dimension),
.format = (VkFormat)TinyImageFormat_ToVkFormat(desc.format),
.extent = {
.width = (uint32_t)desc.width,
.height = (uint32_t)desc.height,
.depth = (uint32_t)desc.depth,
},
.mipLevels = desc.mipLevel,
.arrayLayers = desc.arraySize,
.samples = vkApiGetSmpleCountFlag(desc.sampleCount),
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = vkApiGetImageUsageFlags(desc.usage),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = NULL,
.initialLayout = vkApiGetImageLayout(desc.state)
};
ImageCreator imageCreator{ .imageInfo = imageCreateInfo ,.image = (VkImage*)&desc.image };
Backend::TransferWorker->CreateImage(imageCreator);
}
ImageViewPtr VulkanAPI::CreateTextureView(TextureViewKey desc)
{
VkImageViewCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = (VkImage)desc.image;
createInfo.viewType = vkApiGetImageViewType(desc.dimension, desc.layerCount);
createInfo.format = (VkFormat)TinyImageFormat_ToVkFormat(desc.format);
// components字段允许调整颜色通道的最终的映射逻辑
// 比如我们可以将所有颜色通道映射为红色通道以实现单色纹理我们也可以将通道映射具体的常量数值0和1
// 这里用默认的
createInfo.components.r = VK_COMPONENT_SWIZZLE_R;
createInfo.components.g = VK_COMPONENT_SWIZZLE_G;
createInfo.components.b = VK_COMPONENT_SWIZZLE_B;
createInfo.components.a = VK_COMPONENT_SWIZZLE_A;
// subresourceRangle字段用于描述图像的使用目标是什么以及可以被访问的有效区域
// 这个图像用作填充color还是depth stencil等
createInfo.subresourceRange.aspectMask = vkApiGetImageAspectMask(createInfo.format, false);
// 默认处理所有Mipmap
createInfo.subresourceRange.baseMipLevel = desc.baseMipLevel;
createInfo.subresourceRange.levelCount = desc.levelCount;
// 默认处理所有Layers
createInfo.subresourceRange.baseArrayLayer = desc.baseArrayLayer;
createInfo.subresourceRange.layerCount = desc.layerCount;
VkImageView imageView;
vkCreateImageView(backend.GetDevice().Ptr(), &createInfo, nullptr, &imageView);
return (ImageViewPtr)imageView;
}
SamplerPtr VulkanAPI::CreateTextureSampler(TextureSampler key)
{
VkSamplerCreateInfo samplerInfo = vkApiGetSamplerCreateInfo(key);
VkSampler sampler;
vkCreateSampler(backend.GetDevice().Ptr(), &samplerInfo, nullptr, &sampler);
return sampler;
}
void VulkanAPI::BeginFrame()
{
VulkanContext& ctx = *(VulkanContext*)&context;
window.Aquire(ctx);
graph.Input(ctx.surface);
EventSystem::Ptr()->BeginRenderFrame.Invoke(graph, ctx.frame);
}
void VulkanAPI::EndFrame()
{
VulkanContext& ctx = *(VulkanContext*)&context;
ctx.FlushCommand();
window.Present(ctx);
Backend::TransferWorker->TrySyncTransfer(ctx);
}
void VulkanAPI::BeginRenderPass(RenderPassNode* node, FnEnterRenderPass callback)
{
RenderPassKey config{};
FramebufferKey frameKey{.layers = 1};
VkClearValue clearValues[MAX_SUPPORTED_RENDER_TARGET_COUNT] = { 0 };
RenderPassParams& params = node->params;
int clearSurfaceIndex = -1;
int i = 0, attachmentCount = 0;
for (auto& it : node->outEdges) {
uint32_t flag = 1 << i;
TextureDesc& texture = it->CastTo<TextureDesc>();
frameKey.imageViews[attachmentCount++] = (VkImageView)graph.ResolveTextureView(texture);
frameKey.height = texture.height;
frameKey.width = texture.width;
config.colorFormat[i] = texture.format;
if (params.sampleMask & flag) {
TextureDesc resolve = texture.Resolve();
graph.AcquireTexture(resolve);
frameKey.imageViews[attachmentCount++] = (VkImageView)graph.ResolveTextureView(resolve);
}
if (texture.sampleCount > config.samples)
config.samples = texture.sampleCount;
VkClearValue& clearValue = clearValues[i];
const bool bclear = params.clear & flag;
if (texture.state == ResourceState::COLOR_ATTACHMENT) {
if (!bclear && context.surface.id == texture.id && node->IsFirstInput()) {
clearSurfaceIndex = i;//需要手动清除
}
if (bclear || clearSurfaceIndex == i) {
clearValue.color.float32[0] = params.clearColor.r;
clearValue.color.float32[1] = params.clearColor.g;
clearValue.color.float32[2] = params.clearColor.b;
clearValue.color.float32[3] = params.clearColor.a;
}
}
else {
config.depthMask |= flag;
if (bclear) {
clearValue.depthStencil = { (float)params.clearDepth, params.clearStencil };
}
}
i++;
}
frameKey.attachmentCount = attachmentCount;
config.clear = params.clear;
config.discardEnd = params.discardEnd;
config.discardStart = params.discardStart;
RenderPassInfo* passInfo = GetRenderPassInfo(node->name, node->hash);
if (!passInfo) {
passInfo = GetRenderPassInfo(node->hash, config);
}
frameKey.pass = passInfo->pass;
auto it = FramebufferCache.find(frameKey);
VkFramebuffer framebuffer = it->second;
if (it == FramebufferCache.end()) {
VkFramebufferCreateInfo framebufferInfo = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = frameKey.pass,
.attachmentCount = frameKey.attachmentCount,
.pAttachments = frameKey.imageViews,
.width = frameKey.width,
.height = frameKey.height,
.layers = frameKey.layers
};
vkCreateFramebuffer(backend.GetDevice().Ptr(), &framebufferInfo, nullptr, &framebuffer);
FramebufferCache.emplace(frameKey, framebuffer);
}
VkRect2D renderAarea = {
.offset = {0,0},
.extent = {frameKey.width,frameKey.height}
};
VkRenderPassBeginInfo beginInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = VK_NULL_HANDLE,
.renderPass = frameKey.pass,
.framebuffer = framebuffer,
.renderArea = renderAarea,
.clearValueCount = frameKey.attachmentCount,
.pClearValues = clearValues
};
VulkanContext& ctx = *(VulkanContext*)&context;
CommandBuffer cmd = passInfo->commands[context.frame];
ctx.command = cmd.Ptr();
cmd.BeginRecord(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
ctx.renderPassState = RenderPassState::BeginRecord;
if(callback) callback(node);
vkCmdBeginRenderPass(cmd.Ptr(), &beginInfo, VK_SUBPASS_CONTENTS_INLINE);
ctx.renderPassState = RenderPassState::BeginRender;
if (clearSurfaceIndex != -1)ctx.ClearSurface(clearValues[clearSurfaceIndex].color);
}
void VulkanAPI::EndRenderPass(RenderPassNode* node)
{
VulkanContext& ctx = *(VulkanContext*)&context;
RenderPassInfo* passInfo = GetRenderPassInfo(node->name, node->hash);
CommandBuffer cmd = passInfo->commands[context.frame];
vkCmdEndRenderPass(cmd.Ptr());
cmd.EndRecord();
ctx.renderPassState = RenderPassState::Default;
VkSemaphore waitSemaphores[8];
VkPipelineStageFlags waitDstStageMasks[8];
uint32_t semaphoreCount = 0;
if (node->IsFirstInput()) {
waitDstStageMasks[semaphoreCount] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
waitSemaphores[semaphoreCount++] = ctx.surfaceSemaphore;
gLogSemaphore("-----wait {:#x}", (uintptr_t)ctx.surfaceSemaphore);
}
for (auto& it : node->dependencies) {
RenderPassInfo* inputInfo = GetRenderPassInfo(it->name ,it->hash);
waitDstStageMasks[semaphoreCount] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
waitSemaphores[semaphoreCount++] = inputInfo->semaphores[context.frame];
gLogSemaphore("-----wait {:#x}", (uintptr_t)inputInfo->semaphores[context.frame]);
}
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &cmd.Ptr();
submitInfo.pSignalSemaphores = &passInfo->semaphores[context.frame];
submitInfo.signalSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitDstStageMasks;
submitInfo.waitSemaphoreCount = semaphoreCount;
VkFence fence = nullptr;
if (node->IsLastOutput()) {
ctx.graphSemaphore = passInfo->semaphores[context.frame];
TextureDesc& surface = graph.GetRenderSurface();
if (surface.state == ResourceState::PRESENT) {
fence = ctx.surfaceFence;
}
}
gLogSemaphore("+++++sign {:#x}", (uintptr_t)passInfo->semaphores[context.frame]);
vkQueueSubmit(Backend::RenderWorker->GetQueue().Ptr(), 1, &submitInfo, fence);
}
RenderPassInfo* VulkanAPI::GetRenderPassInfo(Name name, size_t hash) {
if (hash) {
auto it = RenderPassCache.find(hash);
if (it != RenderPassCache.end()) {
return &it->second;
}
}
if (name) {
auto it = RenderPassNameCache.find(name);
if (it != RenderPassNameCache.end()) {
return &it->second;
}
}
return nullptr;
}
//单一renderpassconfig.passMask 可以取 0
RenderPassInfo* VulkanAPI::GetRenderPassInfo(size_t& hash, const RenderPassKey& config) {
hash = config;
if (auto it = RenderPassCache.find(hash); it != RenderPassCache.end()) {
return &it->second;
}
// Set up some const aliases for terseness.
const VkAttachmentLoadOp kClear = VK_ATTACHMENT_LOAD_OP_CLEAR;
const VkAttachmentLoadOp kDontCare = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
const VkAttachmentLoadOp kKeep = VK_ATTACHMENT_LOAD_OP_LOAD;
const VkAttachmentStoreOp kDisableStore = VK_ATTACHMENT_STORE_OP_DONT_CARE;
const VkAttachmentStoreOp kEnableStore = VK_ATTACHMENT_STORE_OP_STORE;
VkAttachmentReference inputAttachmentRef[MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
VkAttachmentReference colorAttachmentRefs[2][MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
VkAttachmentReference resolveAttachmentRef[2][MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
VkAttachmentReference depthAttachmentRef[2] = {};
const bool hasSubpasses = config.subpassMask;
const bool hasDepth1 = (!hasSubpasses && config.depthMask) || config.depthMask & config.passMask;
const bool hasDepth2 = config.depthMask & config.subpassMask;
const bool hasSample1 = (!hasSubpasses && config.sampleMask) || config.sampleMask & config.passMask;
const bool hasSample2 = config.sampleMask & config.subpassMask;
VkSubpassDescription subpasses[2] = { {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.pInputAttachments = nullptr,
.pColorAttachments = colorAttachmentRefs[0],
.pResolveAttachments = hasSample1 ? resolveAttachmentRef[0] : nullptr,
.pDepthStencilAttachment = hasDepth1 ? &depthAttachmentRef[0] : nullptr
},
{
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.pInputAttachments = inputAttachmentRef,
.pColorAttachments = colorAttachmentRefs[1],
.pResolveAttachments = hasSample2 ? resolveAttachmentRef[1] : nullptr,
.pDepthStencilAttachment = hasDepth2 ? &depthAttachmentRef[1] : nullptr
} };
VkAttachmentDescription attachments[MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
// We support 2 subpasses, which means we need to supply 1 dependency struct.
VkSubpassDependency dependencies[1] = { {
.srcSubpass = 0,
.dstSubpass = 1,
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT,
} };
// Finally, create the VkRenderPass.
VkRenderPassCreateInfo renderPassInfo{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 0u,
.pAttachments = attachments,
.subpassCount = hasSubpasses ? 2u : 1u,
.pSubpasses = subpasses,
.dependencyCount = hasSubpasses ? 1u : 0u,
};
const VkSampleCountFlagBits samplecount = vkApiGetSmpleCountFlag(config.samples);
uint32_t attachmentIndex = 0, samplePassIndex1 = 0, samplePassIndex2 = 0;
// Populate the Color Attachments.
for (uint32_t i = 0; i < MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (config.colorFormat[i] == TinyImageFormat_UNDEFINED) {
break;
}
const uint8_t flag = 1 << i;
const bool clear = config.clear & flag;
const bool discard = config.discardStart & flag;
const bool discardEnd = config.discardEnd & flag;
const bool sample = config.sampleMask & flag;
const VkFormat format = (VkFormat)TinyImageFormat_ToVkFormat(config.colorFormat[i]);
VkImageLayout layout = vkApiGetAttachmentLayout(format, true);
attachments[attachmentIndex] = {
.format = format,
.samples = sample ? samplecount : VK_SAMPLE_COUNT_1_BIT,
.loadOp = clear ? kClear : (discard ? kDontCare : kKeep),
.storeOp = discardEnd ? kDisableStore : kEnableStore,
.stencilLoadOp = kDontCare,
.stencilStoreOp = kDisableStore,
.initialLayout = layout,
.finalLayout = layout,
};
if (sample) {
attachments[attachmentIndex + 1] = {
.format = format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = clear ? kClear : (discard ? kDontCare : kKeep),
.storeOp = discardEnd ? kDisableStore : kEnableStore,
.stencilLoadOp = kDontCare,
.stencilStoreOp = kDisableStore,
.initialLayout = layout,
.finalLayout = layout,
};
}
const bool isDepth = config.depthMask & flag;
const bool isMask1 = !hasSubpasses || config.passMask & flag;
const bool isMask2 = hasSubpasses && config.subpassMask & flag;
uint32_t index = 0;
if (isMask1) {
index = subpasses[0].colorAttachmentCount++;
colorAttachmentRefs[0][index].layout = layout;
colorAttachmentRefs[0][index].attachment = attachmentIndex;
if (sample) {
index = subpasses[0].colorAttachmentCount++;
colorAttachmentRefs[0][index].layout = layout;
colorAttachmentRefs[0][index].attachment = attachmentIndex + 1;
}
}
if (isMask2) {
if (isMask1) {
index = subpasses[1].inputAttachmentCount++;
inputAttachmentRef[index].layout = layout;
inputAttachmentRef[index].attachment = sample ? attachmentIndex + 1 : attachmentIndex;
if (isDepth) {
dependencies->srcStageMask |= VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies->dstStageMask |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependencies->srcAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies->dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
}
else {
dependencies->srcStageMask |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies->dstStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies->srcAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies->dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
}
}
else {
index = subpasses[1].colorAttachmentCount++;
colorAttachmentRefs[1][index].layout = layout;
colorAttachmentRefs[1][index].attachment = attachmentIndex;
if (sample) {
index = subpasses[1].colorAttachmentCount++;
colorAttachmentRefs[1][index].layout = layout;
colorAttachmentRefs[1][index].attachment = attachmentIndex + 1;
}
}
}
if (isDepth) {
if (isMask1) {
depthAttachmentRef[0].layout = layout;
depthAttachmentRef[0].attachment = attachmentIndex;
}
if (isMask2) {
depthAttachmentRef[1].layout = layout;
depthAttachmentRef[1].attachment = attachmentIndex;
}
}
attachmentIndex += sample ? 2 : 1;
}
renderPassInfo.pDependencies = dependencies->srcStageMask ? dependencies : nullptr;
renderPassInfo.attachmentCount = attachmentIndex;
VkRenderPass pass;
VkResult error = vkCreateRenderPass(backend.GetDevice().Ptr(), &renderPassInfo, nullptr, &pass);
RenderPassInfo info{pass, config};
backend.GetDevice().CreateSemaphores(info.semaphores, context.frameCount);
Backend::RenderWorker->GetCommandPool().PopList(info.commands, context.frameCount);
auto itr = RenderPassCache.emplace(hash, info);
return &itr.first->second;
}
void VulkanAPI::SetRenderPassInfo(Name name, VkRenderPass pass) {
RenderPassInfo info{pass};
backend.GetDevice().CreateSemaphores(info.semaphores, context.frameCount);
Backend::RenderWorker->GetCommandPool().PopList(info.commands, context.frameCount);
RenderPassNameCache.emplace(name, info);
}
}