583 lines
24 KiB
C++
583 lines
24 KiB
C++
#include "vkn/vulkan_api.h"
|
||
#include "vkn/vulkan_window.h"
|
||
#include "vkn/vulkan_api_help.h"
|
||
#include "vkn/wrapper/buffer.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 "meta/enum.h"
|
||
#include "tinyimageformat/tinyimageformat_apis.h"
|
||
#include "zlog.h"
|
||
namespace vkn {
|
||
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;
|
||
}
|
||
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;
|
||
}
|
||
VulkanAPI::VulkanAPI() : RenderAPI(new VulkanContext())
|
||
, window(*VulkanWindow::Ptr())
|
||
, backend(VulkanEngineName)
|
||
{
|
||
|
||
}
|
||
void VulkanAPI::Init()
|
||
{
|
||
|
||
}
|
||
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();
|
||
|
||
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);
|
||
}
|
||
void VulkanAPI::DrawStaticMesh(Mesh& mesh)
|
||
{
|
||
|
||
}
|
||
void VulkanAPI::LoadShader(Shader& shader)
|
||
{
|
||
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);*/
|
||
}
|
||
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;
|
||
}
|
||
void VulkanAPI::BeginFrame()
|
||
{
|
||
VulkanContext& ctx = *(VulkanContext*)&context;
|
||
window.Aquire(ctx);
|
||
ctx.command.BeginRecord(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
|
||
}
|
||
void VulkanAPI::EndFrame()
|
||
{
|
||
VulkanContext& ctx = *(VulkanContext*)&context;
|
||
ctx.command.EndRecord();
|
||
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);
|
||
window.Present(ctx);
|
||
}
|
||
void VulkanAPI::ExecuteResourceBarriers(const ResourceBarrierDesc& desc) {
|
||
VulkanContext& ctx = *(VulkanContext*)&context;
|
||
pmr::vector<VkBufferMemoryBarrier> bufferBarriers{ FramePool() };
|
||
bufferBarriers.reserve(desc.bufferBarriersCount);
|
||
pmr::vector<VkImageMemoryBarrier> imageBarriers{ FramePool() };
|
||
imageBarriers.reserve(desc.textureBarriersCount);
|
||
VkPipelineStageFlags srcStageMask = 0, dstStageMask = 0;
|
||
for (uint32_t i = 0; i < desc.textureBarriersCount; i++)
|
||
{
|
||
auto& barrier = desc.pTextureBarriers[i];
|
||
auto desc = vkApiGetTextureTransition(srcStageMask, dstStageMask, barrier);
|
||
imageBarriers.push_back(desc);
|
||
}
|
||
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());
|
||
}
|
||
void VulkanAPI::BeginRenderPass(RenderPassNode* node)
|
||
{
|
||
RenderPassKey config{};
|
||
FramebufferKey frameKey{.layers = 1};
|
||
VkClearValue clearValues[2 * MAX_SUPPORTED_RENDER_TARGET_COUNT + 1] = { 0 };
|
||
int i = 0;
|
||
for (auto& it : node->outEdges) {
|
||
if (it->IsAttachment()) {
|
||
auto& desc = it->CastTo<api::AttachmentDesc>();
|
||
|
||
config.colorFormat[i] = (VkFormat)TinyImageFormat_ToVkFormat(desc.colorFormat);
|
||
config.samples = vkApiGetSmpleCountFlag(desc.sampleCount);
|
||
|
||
TargetBufferFlags flag = TargetBufferFlags(int(TargetBufferFlags::COLOR0) << i);
|
||
config.clear |= flag;
|
||
it.ResolveView(&graph);
|
||
frameKey.imageViews[i] = (VkImageView)desc.imageView;
|
||
frameKey.height = desc.height;
|
||
frameKey.width = desc.width;
|
||
|
||
//clearValues[i] =
|
||
i++;
|
||
}
|
||
}
|
||
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);
|
||
}
|
||
void VulkanAPI::EndRenderPass(RenderPassNode* node)
|
||
{
|
||
VulkanContext& ctx = *(VulkanContext*)&context;
|
||
vkCmdEndRenderPass(ctx.command.Ptr());
|
||
}
|
||
VkRenderPass VulkanAPI::GetRenderPass(RenderPassKey& config) {
|
||
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,
|
||
} };
|
||
// 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
|
||
};
|
||
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);
|
||
VkImageLayout layout = vkApiGetAttachmentLayout(config.colorFormat[i], true);
|
||
attachments[attachmentIndex++] = {
|
||
.format = config.colorFormat[i],
|
||
.samples = config.samples,
|
||
.loadOp = clear ? kClear : (discard ? kDontCare : kKeep),
|
||
.storeOp = kEnableStore,
|
||
.stencilLoadOp = kDontCare,
|
||
.stencilStoreOp = kDisableStore,
|
||
.initialLayout = layout,
|
||
.finalLayout = layout,
|
||
};
|
||
}
|
||
// 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;
|
||
VkRenderPass renderPass;
|
||
VkResult error = vkCreateRenderPass(backend.GetDevice().Ptr(), &renderPassInfo, nullptr, &renderPass);
|
||
RenderPassCache.emplace(config, renderPass);
|
||
return renderPass;
|
||
}
|
||
}
|