2024-08-17 18:01:21 +08:00
|
|
|
|
#include "vkn/vulkan_api.h"
|
|
|
|
|
|
#include "vkn/vulkan_window.h"
|
2024-09-22 20:26:49 +08:00
|
|
|
|
#include "vkn/vulkan_api_help.h"
|
2024-08-27 20:21:32 +08:00
|
|
|
|
#include "vkn/wrapper/buffer.h"
|
2024-09-07 17:48:12 +08:00
|
|
|
|
#include "vkn/wrapper/device.h"
|
2024-08-27 20:21:32 +08:00
|
|
|
|
#include "vkn/thread/buffer_worker.h"
|
|
|
|
|
|
#include "vkn/thread/command_worker.h"
|
2024-10-25 22:57:25 +08:00
|
|
|
|
#include "vkn/loader/vulkan_glsl_loader.h"
|
2024-08-25 22:41:25 +08:00
|
|
|
|
#include "render/asset/mesh.h"
|
2024-09-15 11:31:27 +08:00
|
|
|
|
#include "meta/enum.h"
|
2024-09-21 17:19:22 +08:00
|
|
|
|
#include "tinyimageformat/tinyimageformat_apis.h"
|
2024-10-21 16:31:02 +08:00
|
|
|
|
#include "zlog.h"
|
2024-08-17 18:01:21 +08:00
|
|
|
|
namespace vkn {
|
2024-09-15 11:31:27 +08:00
|
|
|
|
inline bool operator==(const RenderPassKey& k1, const RenderPassKey& k2) {
|
|
|
|
|
|
if (k1.initialColorLayoutMask != k2.initialColorLayoutMask) return false;
|
|
|
|
|
|
for (int i = 0; i < MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
|
|
|
|
|
if (k1.colorFormat[i] != k2.colorFormat[i]) return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (k1.depthFormat != k2.depthFormat) return false;
|
|
|
|
|
|
if (k1.clear != k2.clear) return false;
|
|
|
|
|
|
if (k1.discardStart != k2.discardStart) return false;
|
|
|
|
|
|
if (k1.discardEnd != k2.discardEnd) return false;
|
|
|
|
|
|
if (k1.samples != k2.samples) return false;
|
|
|
|
|
|
if (k1.needsResolveMask != k2.needsResolveMask) return false;
|
|
|
|
|
|
if (k1.subpassMask != k2.subpassMask) return false;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-10-21 16:31:02 +08:00
|
|
|
|
inline bool operator==(const FramebufferKey& k1, const FramebufferKey& k2) {
|
|
|
|
|
|
if (k1.pass != k2.pass) return false;
|
|
|
|
|
|
if (k1.attachmentCount != k2.attachmentCount) return false;
|
|
|
|
|
|
for (int i = 0; i < k1.attachmentCount; i++) {
|
|
|
|
|
|
if (k1.imageViews[i] != k2.imageViews[i]) return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (k1.height != k2.height) return false;
|
|
|
|
|
|
if (k1.width != k2.width) return false;
|
|
|
|
|
|
if (k1.layers != k2.layers) return false;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-09-07 17:48:12 +08:00
|
|
|
|
VulkanAPI::VulkanAPI() : RenderAPI(new VulkanContext())
|
|
|
|
|
|
, window(*VulkanWindow::Ptr())
|
|
|
|
|
|
, backend(VulkanEngineName)
|
2024-08-17 18:01:21 +08:00
|
|
|
|
{
|
2024-08-23 22:13:05 +08:00
|
|
|
|
|
2024-08-17 18:01:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
void VulkanAPI::Init()
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
void VulkanAPI::Shutdown()
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2024-08-25 22:41:25 +08:00
|
|
|
|
void VulkanAPI::SetStaticMesh(Mesh& mesh)
|
|
|
|
|
|
{
|
2024-08-27 20:21:32 +08:00
|
|
|
|
auto& Indices = mesh.GetIndices();
|
|
|
|
|
|
auto& Vertices = mesh.GetVertices();
|
|
|
|
|
|
MeshVAO& VAO = MeshTable[mesh.GetGuid()];
|
|
|
|
|
|
VAO.indexCount = Indices.size();
|
|
|
|
|
|
VAO.vertexCount = Vertices.size();
|
2024-08-25 22:41:25 +08:00
|
|
|
|
|
2024-08-27 20:21:32 +08:00
|
|
|
|
Buffer indexBuffer{};
|
|
|
|
|
|
indexBuffer.ppBuffer = &VAO.indexBuffer;
|
|
|
|
|
|
indexBuffer.pCpuData = Indices.data();
|
|
|
|
|
|
indexBuffer.size = sizeof(decltype(Indices[0])) * Indices.size();
|
|
|
|
|
|
indexBuffer.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
|
|
|
|
|
Backend::TransferWorker->Invoke(indexBuffer);
|
|
|
|
|
|
|
|
|
|
|
|
Buffer vertexBuffer{};
|
|
|
|
|
|
vertexBuffer.ppBuffer = &VAO.vertexBuffer;
|
|
|
|
|
|
vertexBuffer.pCpuData = Vertices.data();
|
|
|
|
|
|
vertexBuffer.size = Vertices.data_size();
|
|
|
|
|
|
vertexBuffer.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
|
|
|
|
|
Backend::TransferWorker->Invoke(vertexBuffer);
|
2024-08-25 22:41:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
void VulkanAPI::DrawStaticMesh(Mesh& mesh)
|
|
|
|
|
|
{
|
2024-08-27 20:21:32 +08:00
|
|
|
|
|
2024-08-25 22:41:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
void VulkanAPI::LoadShader(Shader& shader)
|
|
|
|
|
|
{
|
2024-10-25 22:57:25 +08:00
|
|
|
|
pmr::vector<VkPipelineShaderStageCreateInfo> shaderStages;
|
|
|
|
|
|
std::map<VkShaderStageFlagBits, VkShaderModule> shaderModules;
|
|
|
|
|
|
auto& device = backend.GetDevice();
|
|
|
|
|
|
auto vertModule = shader.GetVertHandle<vkShaderProgram>()->Ptr();
|
|
|
|
|
|
shaderModules.insert(std::make_pair(VK_SHADER_STAGE_VERTEX_BIT, vertModule));
|
|
|
|
|
|
auto fragModule = shader.GetFragHandle<vkShaderProgram>()->Ptr();
|
|
|
|
|
|
shaderModules.insert(std::make_pair(VK_SHADER_STAGE_FRAGMENT_BIT, fragModule));
|
|
|
|
|
|
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 it = refl::find_info(shader.Name());
|
|
|
|
|
|
auto meta = it->GetMeta("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 = {};
|
|
|
|
|
|
/*
|
|
|
|
|
|
auto descriptorSetLayout = VulkanContext::CreateDescriptorSetLayout(shader.GetInfo());
|
|
|
|
|
|
auto pipelineLayout = VulkanContext::CreatePipelineLayout({ descriptorSetLayout }, {});
|
|
|
|
|
|
|
|
|
|
|
|
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 = PassList[RENDER_FORWARD_RENDERING]->Ptr();
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
VulkanPipeline& vulkan_pipeline = PipelineTable[shader.GetGuid()];
|
|
|
|
|
|
vulkan_pipeline.name = shader.Name();
|
|
|
|
|
|
vulkan_pipeline.pipeline = pipeLine;
|
|
|
|
|
|
vulkan_pipeline.inUse = true;
|
|
|
|
|
|
vulkan_pipeline.pipelineLayout = pipelineLayout;
|
|
|
|
|
|
vulkan_pipeline.descriptorSetLayout = descriptorSetLayout;
|
|
|
|
|
|
vulkan_pipeline.descriptorSet = backend.GetPool().Allocate(descriptorSetLayout);*/
|
2024-08-25 22:41:25 +08:00
|
|
|
|
}
|
2024-10-21 16:31:02 +08:00
|
|
|
|
ImagePtr VulkanAPI::CreateTexture(TextureDesc desc)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (desc.image) {
|
|
|
|
|
|
return desc.image;
|
|
|
|
|
|
}
|
|
|
|
|
|
uint32_t depth = any(desc.dimension & TextureDimension::TEX_3D) ? 3 : 1;
|
|
|
|
|
|
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.state),
|
|
|
|
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
|
|
|
|
.queueFamilyIndexCount = 0,
|
|
|
|
|
|
.pQueueFamilyIndices = NULL,
|
|
|
|
|
|
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
|
|
|
|
|
|
};
|
|
|
|
|
|
return ImagePtr();
|
|
|
|
|
|
}
|
|
|
|
|
|
ImageViewPtr VulkanAPI::CreateTextureView(TextureViewDesc 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;
|
|
|
|
|
|
}
|
2024-08-30 22:09:05 +08:00
|
|
|
|
void VulkanAPI::BeginFrame()
|
|
|
|
|
|
{
|
2024-10-12 17:40:59 +08:00
|
|
|
|
VulkanContext& ctx = *(VulkanContext*)&context;
|
|
|
|
|
|
window.Aquire(ctx);
|
|
|
|
|
|
ctx.command.BeginRecord(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
|
2024-08-30 22:09:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
void VulkanAPI::EndFrame()
|
|
|
|
|
|
{
|
2024-10-12 17:40:59 +08:00
|
|
|
|
VulkanContext& ctx = *(VulkanContext*)&context;
|
|
|
|
|
|
ctx.command.EndRecord();
|
2024-10-21 16:31:02 +08:00
|
|
|
|
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; // 等待渲染阶段
|
|
|
|
|
|
VkSemaphore waitSemaphores[] = { ctx.surfaceSemaphore };
|
|
|
|
|
|
VkSemaphore signalSemaphores[] = { ctx.presentSemaphore };// 渲染完成信号量
|
|
|
|
|
|
VkSubmitInfo submitInfo{};
|
|
|
|
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
|
|
|
|
submitInfo.commandBufferCount = 1;
|
|
|
|
|
|
submitInfo.pCommandBuffers = &ctx.command.Ptr();
|
|
|
|
|
|
|
|
|
|
|
|
submitInfo.waitSemaphoreCount = 1;
|
|
|
|
|
|
submitInfo.pWaitSemaphores = waitSemaphores;
|
|
|
|
|
|
submitInfo.pWaitDstStageMask = waitStages;
|
|
|
|
|
|
|
|
|
|
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
|
|
|
|
submitInfo.pSignalSemaphores = signalSemaphores;
|
|
|
|
|
|
vkQueueSubmit(Backend::RenderWorker->GetQueue().Ptr(), 1, &submitInfo, ctx.surfaceFence);
|
2024-10-12 17:40:59 +08:00
|
|
|
|
window.Present(ctx);
|
2024-09-07 17:48:12 +08:00
|
|
|
|
}
|
2024-09-21 17:19:22 +08:00
|
|
|
|
void VulkanAPI::ExecuteResourceBarriers(const ResourceBarrierDesc& desc) {
|
2024-09-22 20:26:49 +08:00
|
|
|
|
VulkanContext& ctx = *(VulkanContext*)&context;
|
2024-09-21 17:19:22 +08:00
|
|
|
|
pmr::vector<VkBufferMemoryBarrier> bufferBarriers{ FramePool() };
|
|
|
|
|
|
bufferBarriers.reserve(desc.bufferBarriersCount);
|
|
|
|
|
|
pmr::vector<VkImageMemoryBarrier> imageBarriers{ FramePool() };
|
|
|
|
|
|
imageBarriers.reserve(desc.textureBarriersCount);
|
2024-09-30 17:56:12 +08:00
|
|
|
|
VkPipelineStageFlags srcStageMask = 0, dstStageMask = 0;
|
2024-09-21 17:19:22 +08:00
|
|
|
|
for (uint32_t i = 0; i < desc.textureBarriersCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto& barrier = desc.pTextureBarriers[i];
|
2024-10-21 16:31:02 +08:00
|
|
|
|
auto desc = vkApiGetTextureTransition(srcStageMask, dstStageMask, barrier);
|
2024-09-22 20:26:49 +08:00
|
|
|
|
imageBarriers.push_back(desc);
|
2024-09-21 17:19:22 +08:00
|
|
|
|
}
|
2024-10-12 17:40:59 +08:00
|
|
|
|
if (dstStageMask == 0) {
|
|
|
|
|
|
dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
|
|
|
|
|
}
|
|
|
|
|
|
vkCmdPipelineBarrier(ctx.command.Ptr(), srcStageMask, dstStageMask, 0, 0, NULL, 0, NULL, imageBarriers.size(), imageBarriers.data());
|
2024-09-21 17:19:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
void VulkanAPI::BeginRenderPass(RenderPassNode* node)
|
|
|
|
|
|
{
|
2024-10-21 16:31:02 +08:00
|
|
|
|
RenderPassKey config{};
|
|
|
|
|
|
FramebufferKey frameKey{.layers = 1};
|
|
|
|
|
|
VkClearValue clearValues[2 * MAX_SUPPORTED_RENDER_TARGET_COUNT + 1] = { 0 };
|
2024-09-21 17:19:22 +08:00
|
|
|
|
int i = 0;
|
|
|
|
|
|
for (auto& it : node->outEdges) {
|
|
|
|
|
|
if (it->IsAttachment()) {
|
|
|
|
|
|
auto& desc = it->CastTo<api::AttachmentDesc>();
|
2024-10-21 16:31:02 +08:00
|
|
|
|
|
2024-09-21 17:19:22 +08:00
|
|
|
|
config.colorFormat[i] = (VkFormat)TinyImageFormat_ToVkFormat(desc.colorFormat);
|
2024-10-21 16:31:02 +08:00
|
|
|
|
config.samples = vkApiGetSmpleCountFlag(desc.sampleCount);
|
2024-10-12 17:40:59 +08:00
|
|
|
|
|
|
|
|
|
|
TargetBufferFlags flag = TargetBufferFlags(int(TargetBufferFlags::COLOR0) << i);
|
|
|
|
|
|
config.clear |= flag;
|
2024-10-21 16:31:02 +08:00
|
|
|
|
it.ResolveView(&graph);
|
|
|
|
|
|
frameKey.imageViews[i] = (VkImageView)desc.imageView;
|
|
|
|
|
|
frameKey.height = desc.height;
|
|
|
|
|
|
frameKey.width = desc.width;
|
|
|
|
|
|
|
|
|
|
|
|
//clearValues[i] =
|
2024-09-21 17:19:22 +08:00
|
|
|
|
i++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-10-21 16:31:02 +08:00
|
|
|
|
frameKey.attachmentCount = i;
|
|
|
|
|
|
VkRenderPass pass = GetRenderPass(config);
|
|
|
|
|
|
frameKey.pass = pass;
|
|
|
|
|
|
auto it = FramebufferCache.find(frameKey);
|
|
|
|
|
|
VkFramebuffer framebuffer = it->second;
|
|
|
|
|
|
if (it == FramebufferCache.end()) {
|
|
|
|
|
|
VkFramebufferCreateInfo framebufferInfo = {
|
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
|
|
|
|
|
.renderPass = 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 = pass,
|
|
|
|
|
|
.framebuffer = framebuffer,
|
|
|
|
|
|
.renderArea = renderAarea,
|
|
|
|
|
|
.clearValueCount = frameKey.attachmentCount,
|
|
|
|
|
|
.pClearValues = clearValues
|
|
|
|
|
|
};
|
|
|
|
|
|
VulkanContext& ctx = *(VulkanContext*)&context;
|
|
|
|
|
|
vkCmdBeginRenderPass(ctx.command.Ptr(), &beginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
2024-09-21 17:19:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
void VulkanAPI::EndRenderPass(RenderPassNode* node)
|
|
|
|
|
|
{
|
2024-10-21 16:31:02 +08:00
|
|
|
|
VulkanContext& ctx = *(VulkanContext*)&context;
|
|
|
|
|
|
vkCmdEndRenderPass(ctx.command.Ptr());
|
2024-09-21 17:19:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
VkRenderPass VulkanAPI::GetRenderPass(RenderPassKey& config) {
|
2024-09-15 11:31:27 +08:00
|
|
|
|
auto it = RenderPassCache.find(config);
|
|
|
|
|
|
if (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[MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
|
|
|
|
|
|
VkAttachmentReference depthAttachmentRef = {};
|
|
|
|
|
|
|
|
|
|
|
|
const bool hasSubpasses = config.subpassMask != 0;
|
|
|
|
|
|
const bool hasDepth = config.depthFormat != VK_FORMAT_UNDEFINED;
|
|
|
|
|
|
|
|
|
|
|
|
VkSubpassDescription subpasses[2] = { {
|
|
|
|
|
|
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
|
|
|
|
.pInputAttachments = nullptr,
|
|
|
|
|
|
.pColorAttachments = colorAttachmentRefs[0],
|
|
|
|
|
|
.pResolveAttachments = resolveAttachmentRef,
|
|
|
|
|
|
.pDepthStencilAttachment = hasDepth ? &depthAttachmentRef : nullptr
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
|
|
|
|
.pInputAttachments = inputAttachmentRef,
|
|
|
|
|
|
.pColorAttachments = colorAttachmentRefs[1],
|
|
|
|
|
|
.pResolveAttachments = resolveAttachmentRef,
|
|
|
|
|
|
.pDepthStencilAttachment = hasDepth ? &depthAttachmentRef : nullptr
|
|
|
|
|
|
} };
|
|
|
|
|
|
|
|
|
|
|
|
// The attachment list contains: Color Attachments, Resolve Attachments, and Depth Attachment.
|
|
|
|
|
|
// For simplicity, create an array that can hold the maximum possible number of attachments.
|
|
|
|
|
|
// Note that this needs to have the same ordering as the corollary array in getFramebuffer.
|
|
|
|
|
|
VkAttachmentDescription attachments[MAX_SUPPORTED_RENDER_TARGET_COUNT + MAX_SUPPORTED_RENDER_TARGET_COUNT + 1] = {};
|
|
|
|
|
|
|
|
|
|
|
|
// We support 2 subpasses, which means we need to supply 1 dependency struct.
|
|
|
|
|
|
VkSubpassDependency dependencies[1] = { {
|
|
|
|
|
|
.srcSubpass = 0,
|
|
|
|
|
|
.dstSubpass = 1,
|
|
|
|
|
|
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
|
|
|
|
.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
|
|
|
|
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
|
|
|
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
|
|
|
|
|
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT,
|
|
|
|
|
|
} };
|
2024-09-07 17:48:12 +08:00
|
|
|
|
// 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,
|
|
|
|
|
|
.pDependencies = dependencies
|
|
|
|
|
|
};
|
2024-09-15 11:31:27 +08:00
|
|
|
|
int attachmentIndex = 0;
|
|
|
|
|
|
// Populate the Color Attachments.
|
|
|
|
|
|
for (int i = 0; i < MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
|
|
|
|
|
if (config.colorFormat[i] == VK_FORMAT_UNDEFINED) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
const VkImageLayout subpassLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
|
|
uint32_t index;
|
|
|
|
|
|
|
|
|
|
|
|
if (!hasSubpasses) {
|
|
|
|
|
|
index = subpasses[0].colorAttachmentCount++;
|
|
|
|
|
|
colorAttachmentRefs[0][index].layout = subpassLayout;
|
|
|
|
|
|
colorAttachmentRefs[0][index].attachment = attachmentIndex;
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
|
|
|
|
// The Driver API consolidates all color attachments from the first and second subpasses
|
|
|
|
|
|
// into a single list, and uses a bitmask to mark attachments that belong only to the
|
|
|
|
|
|
// second subpass and should be available as inputs. All color attachments in the first
|
|
|
|
|
|
// subpass are automatically made available to the second subpass.
|
|
|
|
|
|
|
|
|
|
|
|
// If there are subpasses, we require the input attachment to be the first attachment.
|
|
|
|
|
|
// Breaking this assumption would likely require enhancements to the Driver API in order
|
|
|
|
|
|
// to supply Vulkan with all the information needed.
|
|
|
|
|
|
if (config.subpassMask & (1 << i)) {
|
|
|
|
|
|
index = subpasses[0].colorAttachmentCount++;
|
|
|
|
|
|
colorAttachmentRefs[0][index].layout = subpassLayout;
|
|
|
|
|
|
colorAttachmentRefs[0][index].attachment = attachmentIndex;
|
|
|
|
|
|
|
|
|
|
|
|
index = subpasses[1].inputAttachmentCount++;
|
|
|
|
|
|
inputAttachmentRef[index].layout = subpassLayout;
|
|
|
|
|
|
inputAttachmentRef[index].attachment = attachmentIndex;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
index = subpasses[1].colorAttachmentCount++;
|
|
|
|
|
|
colorAttachmentRefs[1][index].layout = subpassLayout;
|
|
|
|
|
|
colorAttachmentRefs[1][index].attachment = attachmentIndex;
|
|
|
|
|
|
}
|
|
|
|
|
|
const TargetBufferFlags flag = TargetBufferFlags(int(TargetBufferFlags::COLOR0) << i);
|
|
|
|
|
|
const bool clear = any(config.clear & flag);
|
|
|
|
|
|
const bool discard = any(config.discardStart & flag);
|
2024-10-21 16:31:02 +08:00
|
|
|
|
VkImageLayout layout = vkApiGetAttachmentLayout(config.colorFormat[i], true);
|
2024-09-15 11:31:27 +08:00
|
|
|
|
attachments[attachmentIndex++] = {
|
|
|
|
|
|
.format = config.colorFormat[i],
|
|
|
|
|
|
.samples = config.samples,
|
|
|
|
|
|
.loadOp = clear ? kClear : (discard ? kDontCare : kKeep),
|
|
|
|
|
|
.storeOp = kEnableStore,
|
|
|
|
|
|
.stencilLoadOp = kDontCare,
|
|
|
|
|
|
.stencilStoreOp = kDisableStore,
|
2024-10-21 16:31:02 +08:00
|
|
|
|
.initialLayout = layout,
|
|
|
|
|
|
.finalLayout = layout,
|
2024-09-15 11:31:27 +08:00
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
// Nulling out the zero-sized lists is necessary to avoid VK_ERROR_OUT_OF_HOST_MEMORY on Adreno.
|
|
|
|
|
|
if (subpasses[0].colorAttachmentCount == 0) {
|
|
|
|
|
|
subpasses[0].pColorAttachments = nullptr;
|
|
|
|
|
|
subpasses[0].pResolveAttachments = nullptr;
|
|
|
|
|
|
subpasses[1].pColorAttachments = nullptr;
|
|
|
|
|
|
subpasses[1].pResolveAttachments = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Populate the Resolve Attachments.
|
|
|
|
|
|
VkAttachmentReference* pResolveAttachment = resolveAttachmentRef;
|
|
|
|
|
|
for (int i = 0; i < MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
|
|
|
|
|
if (config.colorFormat[i] == VK_FORMAT_UNDEFINED) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!(config.needsResolveMask & (1 << i))) {
|
|
|
|
|
|
pResolveAttachment->attachment = VK_ATTACHMENT_UNUSED;
|
|
|
|
|
|
++pResolveAttachment;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pResolveAttachment->attachment = attachmentIndex;
|
|
|
|
|
|
pResolveAttachment->layout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
|
|
++pResolveAttachment;
|
|
|
|
|
|
|
|
|
|
|
|
attachments[attachmentIndex++] = {
|
|
|
|
|
|
.format = config.colorFormat[i],
|
|
|
|
|
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
|
|
|
|
.loadOp = kDontCare,
|
|
|
|
|
|
.storeOp = kEnableStore,
|
|
|
|
|
|
.stencilLoadOp = kDontCare,
|
|
|
|
|
|
.stencilStoreOp = kDisableStore,
|
|
|
|
|
|
.initialLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
|
|
|
|
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
// Populate the Depth Attachment.
|
|
|
|
|
|
if (hasDepth) {
|
|
|
|
|
|
const bool clear = any(config.clear & TargetBufferFlags::DEPTH);
|
|
|
|
|
|
const bool discardStart = any(config.discardStart & TargetBufferFlags::DEPTH);
|
|
|
|
|
|
const bool discardEnd = any(config.discardEnd & TargetBufferFlags::DEPTH);
|
|
|
|
|
|
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
|
|
depthAttachmentRef.attachment = attachmentIndex;
|
|
|
|
|
|
attachments[attachmentIndex++] = {
|
|
|
|
|
|
.format = config.depthFormat,
|
|
|
|
|
|
.samples = (VkSampleCountFlagBits)config.samples,
|
|
|
|
|
|
.loadOp = clear ? kClear : (discardStart ? kDontCare : kKeep),
|
|
|
|
|
|
.storeOp = discardEnd ? kDisableStore : kEnableStore,
|
|
|
|
|
|
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
|
|
|
|
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
|
|
|
|
.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
|
|
|
|
.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
renderPassInfo.attachmentCount = attachmentIndex;
|
2024-09-07 17:48:12 +08:00
|
|
|
|
VkRenderPass renderPass;
|
|
|
|
|
|
VkResult error = vkCreateRenderPass(backend.GetDevice().Ptr(), &renderPassInfo, nullptr, &renderPass);
|
2024-09-15 11:31:27 +08:00
|
|
|
|
RenderPassCache.emplace(config, renderPass);
|
|
|
|
|
|
return renderPass;
|
2024-08-30 22:09:05 +08:00
|
|
|
|
}
|
2024-08-17 18:01:21 +08:00
|
|
|
|
}
|