296 lines
11 KiB
C++
296 lines
11 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 "render/asset/mesh.h"
|
|
#include "meta/enum.h"
|
|
#include "tinyimageformat/tinyimageformat_apis.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;
|
|
}
|
|
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)
|
|
{
|
|
}
|
|
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();
|
|
ctx.command.Submit(Backend::RenderWorker->GetQueue().Ptr(), 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);
|
|
using api::ResourceState;
|
|
VkPipelineStageFlags srcStageMask = 0, dstStageMask = 0;
|
|
for (uint32_t i = 0; i < desc.textureBarriersCount; i++)
|
|
{
|
|
auto& barrier = desc.pTextureBarriers[i];
|
|
auto desc = GetVkTextureTransition(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{};
|
|
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 = (VkSampleCountFlagBits)desc.sampleCount;
|
|
|
|
TargetBufferFlags flag = TargetBufferFlags(int(TargetBufferFlags::COLOR0) << i);
|
|
config.clear |= flag;
|
|
i++;
|
|
}
|
|
}
|
|
node->pass = GetRenderPass(config);
|
|
}
|
|
void VulkanAPI::EndRenderPass(RenderPassNode* node)
|
|
{
|
|
|
|
}
|
|
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);
|
|
attachments[attachmentIndex++] = {
|
|
.format = config.colorFormat[i],
|
|
.samples = config.samples,
|
|
.loadOp = clear ? kClear : (discard ? kDontCare : kKeep),
|
|
.storeOp = kEnableStore,
|
|
.stencilLoadOp = kDontCare,
|
|
.stencilStoreOp = kDisableStore,
|
|
.initialLayout = ((!discard && config.initialColorLayoutMask & (1 << i)) || clear)
|
|
? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
|
|
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
};
|
|
}
|
|
// 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;
|
|
}
|
|
}
|