vulkan api bugfix

This commit is contained in:
ouczbs 2024-10-21 16:31:02 +08:00
parent c39296d050
commit 9b1704246d
21 changed files with 576 additions and 157 deletions

View File

@ -10,7 +10,7 @@ public:
~Singleton() {
ms_Singleton = nullptr;
}
static constexpr T* Ptr(void) {
static T* Ptr(void) {
return ms_Singleton;
}
};

View File

@ -1,6 +1,7 @@
#include "render/renderapi.h"
#include "render/module.h"
namespace api {
RenderAPI* API;
IMPLEMENT_STATIC_MODULE(RENDER_API, RenderModule, render);
void RenderAPI::RenderView(FRenderView& view)
{
@ -15,4 +16,15 @@ namespace api {
RenderView(view);
}
}
RenderAPI::RenderAPI(RenderContext* ctx) : context(*ctx)
{
API = this;
}
RenderAPI::~RenderAPI()
{
delete& context;
}
RenderAPI* RenderAPI::Ptr() {
return API;
}
}

View File

@ -10,15 +10,16 @@ namespace api {
public:
TextureDesc mSurface;
table<Name, TextureDesc> mResourceTable;
table<TextureDesc::Key, TextureDesc> mResourcePool;
table<TextureDesc::Key, TextureDesc> mResourceViewPool;
table<TextureDesc, ImagePtr> mResourcePool;
table<TextureViewDesc, ImageViewPtr> mResourceViewPool;
lemon::ListGraph mGraph;
pmr::vector<FrameGraphNodePtr> mNodes{FramePool()};
public:
template<typename T>
FrameGraphNodePtr AddRenderPass() { return AddRenderPass(&T::Setup, &T::Execute); }
using RenderPassSetupFunction = std::function<void(FrameGraph&, RenderPassBuilder&)>;
using RenderPassSetupFunction = std::function<bool(FrameGraph&, RenderPassBuilder&)>;
FrameGraphNodePtr AddRenderPass(const RenderPassSetupFunction& setup, const RenderPassNodeExecuteFn& executor);
FrameGraphNodePtr AddPresent(const FrameGraphEdgePtr& edge);
using TextureSetupFunction = std::function<void(FrameGraph&, class TextureBuilder&)>;
FrameGraphEdgePtr CreateTexture(const TextureSetupFunction& setup);
@ -26,12 +27,18 @@ namespace api {
void Execute(FRenderView& view);
void Clear();
TextureDesc Resource(Name name) {
constexpr size_t surface = pmr::string_hash("surface");
if (name.Hash() == surface) {
return mSurface;
}
auto it = mResourceTable.find(name);
if (it == mResourceTable.end()) {
return {};
}
return it->second;
}
ImagePtr ResolveTexture(TextureDesc desc);
ImageViewPtr ResolveTextureView(TextureViewDesc desc);
public:
void ExecuteRenderPass(RenderPassNode* node, FRenderView& view);
void ExecutePresentPass(RenderPassNode* node, FRenderView& view);
@ -41,3 +48,4 @@ namespace api {
void ExecuteResourceBarriers(RenderPassNode* node);
};
}
#include "frame_graph_builder.inl"

View File

@ -1,5 +1,4 @@
#pragma once
#include "frame_graph.h"
namespace api {
struct FrameGraph::RenderPassBuilder {
FrameGraph& graph;
@ -9,19 +8,18 @@ namespace api {
RenderPassBuilder(FrameGraph* graph, FrameGraphNodePtr& node) noexcept
: graph(*graph) , node(node) {};
FrameGraph::RenderPassBuilder& Name(pmr::Name name);
FrameGraph::RenderPassBuilder& Pass(RenderPass* pass);
FrameGraph::RenderPassBuilder& Read(TextureDesc desc, ResourceState state);
FrameGraph::RenderPassBuilder& Write(BufferDesc desc, ResourceState state);
FrameGraph::RenderPassBuilder& Attach(AttachmentDesc desc, ResourceState state);
FrameGraph::RenderPassBuilder& Read(const FrameGraphEdgePtr& edge);
FrameGraph::RenderPassBuilder& Write(const FrameGraphEdgePtr& edge);
FrameGraph::RenderPassBuilder& Read(const FrameGraphEdgePtr& refEdge, ResourceState state);
FrameGraph::RenderPassBuilder& Write(const FrameGraphEdgePtr& refEdge, ResourceState state);
FrameGraph::RenderPassBuilder& Present(const FrameGraphEdgePtr& refEdge, ResourceState state);
};
struct FrameGraph::TextureBuilder {
FrameGraph& graph;
FrameGraphEdgePtr& edge;
public:
TextureBuilder(FrameGraph* graph, FrameGraphEdgePtr& edge)noexcept : graph(*graph), edge(edge) {};
FrameGraph::TextureBuilder& Name(pmr::Name name);
FrameGraph::TextureBuilder& Import(ImagePtr& ptr, AttachmentDesc desc, ResourceState state);
FrameGraph::TextureBuilder& Import(pmr::Name name, AttachmentDesc desc, ResourceState state);
};
}

View File

@ -27,6 +27,7 @@ namespace api {
NodeType type{ NodeType::Render };
RenderPassNode* node;
FrameGraphNodePtr() : node(nullptr){};
FrameGraphNodePtr(GraphNodeRef ref, NodeType type = NodeType::Present);
FrameGraphNodePtr(GraphNodeRef ref, const RenderPassNodeExecuteFn& executor, NodeType type = NodeType::Render);
operator bool() const {
return node;
@ -34,6 +35,7 @@ namespace api {
RenderPassNode* operator ->()const {
return node;
}
void Presnet(FrameGraph&, RenderPassContext&);
};
struct FrameResource {
using Resource = std::variant<TextureDesc, BufferDesc, AttachmentDesc>;
@ -59,8 +61,11 @@ namespace api {
struct FrameGraphEdgePtr {
ResourceState targetState;
FrameResource* resource;
ImagePtr* ppImage;
FrameGraphEdgePtr() = default;
FrameGraphEdgePtr(const FrameGraphEdgePtr& edge, ResourceState state) {
targetState = state;
resource = edge.resource;
}
FrameResource* Make() {
resource = new (FramePool()) FrameResource();
return resource;
@ -72,11 +77,11 @@ namespace api {
return resource;
}
void Resolve(FrameGraph* graph);
void ResolveView(FrameGraph* graph);
};
using RenderPassEdgeIterFn = std::function<void(FrameResource*, FrameGraphEdgePtr)>;
struct RenderPassNode {
Name name;
const void* pass;
RenderPassNodeExecuteFn executor;
pmr::vector<FrameGraphEdgePtr> inEdges{ FramePool() };
pmr::vector<FrameGraphEdgePtr> outEdges{ FramePool() };

View File

@ -1,10 +1,10 @@
#pragma once
#include "render_pass.h"
#include "render/graph/frame_graph_builder.h"
#include "render/graph/frame_graph.h"
namespace api {
class DemoPass : public RenderPass {
public:
static void Setup(FrameGraph& graph, FrameGraph::RenderPassBuilder& builder);
static bool Setup(FrameGraph& graph, FrameGraph::RenderPassBuilder& builder);
static void Execute(FrameGraph&, RenderPassContext&);
};

View File

@ -7,19 +7,21 @@ namespace api {
class Mesh;
class Shader;
class RenderPassNode;
class RENDER_API RenderAPI : public Singleton<RenderAPI>
class RENDER_API RenderAPI
{
public:
RenderContext& context;
FrameGraph graph;
RenderAPI(RenderContext* ctx) : context(*ctx){};
virtual ~RenderAPI() { delete &context; };
RenderAPI(RenderContext* ctx);
virtual ~RenderAPI();
public:
void* operator new(size_t size) {
return ::operator new(size, GlobalPool());
}
void operator delete(void* p) {}
static RenderAPI* Ptr();
public:
virtual void Init() = 0;
virtual void Shutdown() = 0;
@ -27,6 +29,8 @@ namespace api {
virtual void DrawStaticMesh(Mesh& mesh) = 0;
virtual void LoadShader(Shader& shader) = 0;
virtual ImagePtr CreateTexture(TextureDesc desc) = 0;
virtual ImageViewPtr CreateTextureView(TextureViewDesc desc) = 0;
virtual void BeginFrame() = 0;
virtual void EndFrame() = 0;

View File

@ -17,30 +17,6 @@ namespace api {
SAMPLE_COUNT_16 = 16,
SAMPLE_COUNT_COUNT = 5,
};
struct BufferDesc {
static BufferDesc Make() {
return {};
}
};
using ImagePtr = void*;
struct TextureDesc {
using Key = uint32_t;
ImagePtr image;
static TextureDesc Make() {
return {};
}
};
struct AttachmentDesc {
ImagePtr image;
TinyImageFormat colorFormat;
SampleCount sampleCount;
static AttachmentDesc Make() {
return {};
}
TextureDesc ToTexture() {
return TextureDesc{image};
}
};
enum class TargetBufferFlags : uint32_t {
NONE = 0x0u, //!< No buffer selected.
COLOR0 = 0x00000001u, //!< Color buffer selected.
@ -81,8 +57,64 @@ namespace api {
// For color attachments, but also used when the image is a sampler.
// TODO: explore separate layout policies for attachment+sampling and just attachment.
COLOR_ATTACHMENT,
// For color attachment MSAA resolves.
COLOR_ATTACHMENT_RESOLVE,
};
enum class TextureDimension
{
TEX_1D = 0x01,
TEX_2D = 0x02,
TEX_3D = 0x04,
TEX_CUBE = 0x08,
};
using ImagePtr = void*;
using ImageViewPtr = void*;
struct BufferDesc {
static BufferDesc Make() {
return {};
}
};
struct TextureDesc {
ImagePtr image;
uint32_t width;
uint32_t height;
uint32_t depth : 16;
TinyImageFormat format : 16;
ResourceState state : 8;
SampleCount sampleCount : 8;
uint32_t arraySize : 8;
TextureDimension dimension : 4;
uint32_t mipLevel : 4;
};
struct TextureViewDesc {
ImagePtr image;
TinyImageFormat format : 16;
ResourceState state : 8;
TextureDimension dimension: 8;
uint32_t baseArrayLayer : 8;
uint32_t layerCount : 8;
uint32_t baseMipLevel : 8;
uint32_t levelCount : 8;
};
struct AttachmentDesc {
ImagePtr image;
ImageViewPtr imageView;
uint32_t width;
uint32_t height;
TextureDimension dimension : 16;
TinyImageFormat colorFormat : 16;
SampleCount sampleCount : 16;
AttachmentDesc& FromTexture(const TextureDesc& desc) {
width = desc.width;
height = desc.height;
image = desc.image;
return *this;
}
TextureDesc ToTexture() {
return TextureDesc{
.image = image,
.width = width,
.height = height,
};
}
};
struct TextureBarrier
{
@ -115,3 +147,22 @@ namespace api {
uint32_t textureBarriersCount;
};
}
#include "meta/hash.h"
namespace std {
template<>
struct hash<api::TextureViewDesc>
{
size_t operator()(const api::TextureViewDesc& key) const noexcept
{
return meta::MurmurHashFn(key);
}
};
template<>
struct hash<api::TextureDesc>
{
size_t operator()(const api::TextureDesc& key) const noexcept
{
return meta::MurmurHashFn(key);
}
};
}

View File

@ -1,12 +1,43 @@
#include "render/graph/frame_graph.h"
#include "render/graph/frame_graph_builder.h"
#include "render/renderapi.h"
namespace api {
inline bool operator==(const TextureViewDesc& k1, const TextureViewDesc& k2) {
if (k1.image != k2.image) return false;
if (k1.format != k2.format) return false;
if (k1.dimension != k2.dimension) return false;
if (k1.state != k2.state) return false;
if (k1.baseArrayLayer != k2.baseArrayLayer) return false;
if (k1.layerCount != k2.layerCount) return false;
if (k1.baseMipLevel != k2.baseMipLevel) return false;
if (k1.levelCount != k2.levelCount) return false;
return true;
}
inline bool operator==(const TextureDesc& k1, const TextureDesc& k2) {
if (k1.image != k2.image) return false;
if (k1.format != k2.format) return false;
if (k1.dimension != k2.dimension) return false;
if (k1.state != k2.state) return false;
if (k1.width != k2.width) return false;
if (k1.height != k2.height) return false;
if (k1.arraySize != k2.arraySize) return false;
if (k1.mipLevel != k2.mipLevel) return false;
if (k1.sampleCount != k2.sampleCount) return false;
if (k1.depth != k2.depth) return false;
return true;
}
FrameGraphNodePtr FrameGraph::AddPresent(const FrameGraphEdgePtr& edge)
{
FrameGraphNodePtr node_ptr{ mGraph.addNode()};
RenderPassBuilder builder{ this, node_ptr };
builder.Read(edge, ResourceState::PRESENT);
mNodes.push_back(node_ptr);
return node_ptr;
}
FrameGraphNodePtr FrameGraph::AddRenderPass(const RenderPassSetupFunction& setup, const RenderPassNodeExecuteFn& executor)
{
FrameGraphNodePtr node_ptr{ mGraph.addNode(), executor };
RenderPassBuilder builder{ this, node_ptr };
setup(*this, builder);
if(setup(*this, builder))
mNodes.push_back(node_ptr);
return node_ptr;
}
@ -63,8 +94,7 @@ namespace api {
}
void FrameGraph::ExecutePresentPass(RenderPassNode* node, FRenderView& view)
{
RenderPassContext context{};
std::get<RenderPassExecuteFunction>(node->executor)(*this, context);
ExecuteResourceBarriers(node);
}
void FrameGraph::ExecuteComputePass(RenderPassNode* node, FRenderView& view)
{
@ -104,6 +134,7 @@ namespace api {
}
textureBarrier.push_back(barrier);
}
resource->sourceState = edge.targetState;
});
if (bufferBarrier.empty() && textureBarrier.empty()) {
return;
@ -123,4 +154,24 @@ namespace api {
fn(edge.resource, edge);
}
}
ImagePtr FrameGraph::ResolveTexture(TextureDesc desc)
{
auto it = mResourcePool.find(desc);
if (it != mResourcePool.end()) {
return it->second;
}
ImagePtr image = RenderAPI::Ptr()->CreateTexture(desc);
mResourcePool.emplace(desc, image);
return image;
}
ImageViewPtr FrameGraph::ResolveTextureView(TextureViewDesc desc)
{
auto it = mResourceViewPool.find(desc);
if (it != mResourceViewPool.end()) {
return it->second;
}
ImageViewPtr view = RenderAPI::Ptr()->CreateTextureView(desc);
mResourceViewPool.emplace(desc, view);
return view;
}
}

View File

@ -1,16 +1,12 @@
#include "render/graph/frame_graph_builder.h"
#include "render/graph/frame_graph.h"
#include "render/pass/render_pass.h"
#include "meta/enum.h"
namespace api {
FrameGraph::RenderPassBuilder& FrameGraph::RenderPassBuilder::Name(pmr::Name name)
{
node->name = name;
return *this;
}
FrameGraph::RenderPassBuilder& FrameGraph::RenderPassBuilder::Pass(RenderPass* pass)
{
node->pass = pass;
return *this;
}
FrameGraph::RenderPassBuilder& FrameGraph::RenderPassBuilder::Read(TextureDesc desc, ResourceState state)
{
FrameGraphEdgePtr edge{};
@ -44,34 +40,39 @@ namespace api {
return *this;
}
FrameGraph::RenderPassBuilder& FrameGraph::RenderPassBuilder::Read(const FrameGraphEdgePtr& edge)
FrameGraph::RenderPassBuilder& FrameGraph::RenderPassBuilder::Read(const FrameGraphEdgePtr& refEdge, ResourceState state)
{
if (!edge) { return *this; }
if (!refEdge) { return *this; }
FrameGraphEdgePtr edge{ refEdge , state};
node->inEdges.emplace_back(edge);
edge->targets.emplace_back(node);
if(edge->source)
graph.mGraph.addEdge(edge->source.ref, node.ref);
refEdge->targets.emplace_back(node);
if(refEdge->source)
graph.mGraph.addEdge(refEdge->source.ref, node.ref);
return *this;
}
FrameGraph::RenderPassBuilder& FrameGraph::RenderPassBuilder::Write(const FrameGraphEdgePtr& edge)
FrameGraph::RenderPassBuilder& FrameGraph::RenderPassBuilder::Write(const FrameGraphEdgePtr& refEdge, ResourceState state)
{
if (!edge) { return *this; }
if (!refEdge) { return *this; }
FrameGraphEdgePtr edge{ refEdge , state };
node->outEdges.emplace_back(edge);
edge->targets.emplace_back(node);
if (edge->source)
graph.mGraph.addEdge(node.ref, edge->source.ref);
refEdge->targets.emplace_back(node);
if (refEdge->source)
graph.mGraph.addEdge(node.ref, refEdge->source.ref);
return *this;
}
FrameGraph::TextureBuilder& FrameGraph::TextureBuilder::Name(pmr::Name name)
FrameGraph::RenderPassBuilder& FrameGraph::RenderPassBuilder::Present(const FrameGraphEdgePtr& refEdge, ResourceState state)
{
Write(refEdge, state);
graph.mNodes.push_back(node);
graph.AddPresent(refEdge);
return *this;
}
FrameGraph::TextureBuilder& FrameGraph::TextureBuilder::Import(pmr::Name name, AttachmentDesc desc, ResourceState state)
{
desc.image = nullptr;
edge->name = name;
return *this;
}
FrameGraph::TextureBuilder& FrameGraph::TextureBuilder::Import(ImagePtr& ptr, AttachmentDesc desc, ResourceState state)
{
edge.ppImage = &ptr;
edge.targetState = state;
edge->resource = desc;
edge.targetState = state;
return *this;
}
}

View File

@ -1,4 +1,5 @@
#include "render/graph/type.h"
#include "render/renderapi.h"
namespace api {
void FrameGraphEdgePtr::Resolve(FrameGraph* graph)
{
@ -17,8 +18,42 @@ namespace api {
if (*imageptr) {
return;
}
if (ppImage) {
*imageptr = *ppImage;
TextureDesc desc = graph->Resource(resource->name);
if (!desc.image) {
graph->ResolveTexture(desc);
}
else {
*imageptr = desc.image;
}
}
void FrameGraphEdgePtr::ResolveView(FrameGraph* graph)
{
if (!resource) {
Make();
}
if (resource->IsAttachment()) {
AttachmentDesc& attach = resource->CastTo<AttachmentDesc>();
TextureViewDesc view{};
view.image = attach.image;
view.format = attach.colorFormat;
view.state = targetState;
view.baseArrayLayer = 0;
view.baseMipLevel = 0;
view.layerCount = 1;
view.levelCount = 1;
view.dimension = attach.dimension;
attach.imageView = graph->ResolveTextureView(view);
}
}
FrameGraphNodePtr::FrameGraphNodePtr(GraphNodeRef ref, NodeType type)
: ref(ref), type(type)
{
node = new (FramePool()) RenderPassNode();
node->executor = [&](FrameGraph& graph, RenderPassContext& ctx) {
this->Presnet(graph, ctx);
};
}
void FrameGraphNodePtr::Presnet(FrameGraph&, RenderPassContext&) {
}
}

View File

@ -1,18 +1,20 @@
#include "render/pass/demo_pass.h"
namespace api {
void DemoPass::Setup(FrameGraph& graph, FrameGraph::RenderPassBuilder& builder)
bool DemoPass::Setup(FrameGraph& graph, FrameGraph::RenderPassBuilder& builder)
{
AttachmentDesc surface{};
surface.colorFormat = TinyImageFormat_B8G8R8A8_UNORM;
surface.FromTexture(graph.mSurface);
surface.colorFormat = TinyImageFormat_B8G8R8A8_SRGB;
surface.sampleCount = SAMPLE_COUNT_1;
surface.dimension = TextureDimension::TEX_2D;
auto edge = graph.CreateTexture(
[=](FrameGraph& graph, FrameGraph::TextureBuilder& builder) {
builder.Name("import")
.Import(graph.mSurface.image, surface, ResourceState::PRESENT);
builder.Import("surface", surface, ResourceState::COLOR_ATTACHMENT);
});
builder.Name("MiniPass")
.Write(edge);
.Present(edge, edge.targetState);
return false;
}
void DemoPass::Execute(FrameGraph&, RenderPassContext&)
{

View File

@ -1,6 +1,6 @@
static_component("render","engine")
add_includedirs("3rdparty", {public = true})
add_headerfiles("include/**.h", "impl/*.inl")
add_headerfiles("include/**.h", "include/**.inl", "impl/*.inl")
add_files("src/**.cpp")
add_deps("asset", "zlib", "core")
add_syslinks("user32", {public = true})

View File

@ -15,6 +15,14 @@ namespace vkn {
using voidFn = std::function<void()>;
using commandFn = std::function<void(CommandBuffer& cmd)>;
using api::TargetBufferFlags;
using api::ResourceBarrierDesc;
using api::TextureViewDesc;
using api::ImageViewPtr;
using api::ImagePtr;
using api::TextureDimension;
using api::SampleCount;
using api::ResourceState;
using api::TextureDesc;
constexpr string_view VulkanEngineName = "vulkan";
constexpr uint8_t MAX_SUPPORTED_RENDER_TARGET_COUNT = 8u;
@ -37,8 +45,15 @@ namespace vkn {
uint8_t initialColorLayoutMask;// 1 byte
uint8_t needsResolveMask; // 1 byte
};
struct FramebufferKey {
VkRenderPass pass;
VkImageView imageViews[MAX_SUPPORTED_RENDER_TARGET_COUNT * 2 + 1];
uint32_t attachmentCount;
uint32_t width;
uint32_t height;
uint32_t layers;
};
}
#include "meta/hash.h"
namespace std {
template<>
struct hash<vkn::RenderPassKey>
@ -48,4 +63,12 @@ namespace std {
return meta::MurmurHashFn(key);
}
};
template<>
struct hash<vkn::FramebufferKey>
{
size_t operator()(const vkn::FramebufferKey& key) const noexcept
{
return meta::MurmurHashFn(key);
}
};
}

View File

@ -13,7 +13,6 @@ namespace vkn {
using api::Mesh;
using api::Shader;
using api::RenderPassNode;
using api::ResourceBarrierDesc;
struct VulkanContext : public api::RenderContext {
VkFence surfaceFence;;
VkSemaphore surfaceSemaphore;
@ -26,6 +25,7 @@ namespace vkn {
Backend backend;
table<Guid, MeshVAO> MeshTable;
table<RenderPassKey, VkRenderPass> RenderPassCache;
table<FramebufferKey, VkFramebuffer> FramebufferCache;
public:
VulkanAPI();
@ -36,6 +36,8 @@ namespace vkn {
void DrawStaticMesh(Mesh& mesh)override;
void LoadShader(Shader& shader)override;
ImagePtr CreateTexture(TextureDesc desc)override;
ImageViewPtr CreateTextureView(TextureViewDesc desc)override;
void BeginFrame()override;
void EndFrame()override;
@ -43,7 +45,7 @@ namespace vkn {
void EndRenderPass(RenderPassNode* node) override;
void ExecuteResourceBarriers(const ResourceBarrierDesc& desc) override;
VkPipeline GetPipeline();
VkPipeline GetPipeline() { return nullptr; };
VkRenderPass GetRenderPass(RenderPassKey& config);
Backend& GetBackend() {

View File

@ -10,6 +10,13 @@ namespace vkn {
VkImageLayout mSrcState;
VkImageLayout mDstState;
};
VkImageLayout GetVkLayout(ResourceState layout);
VkImageMemoryBarrier GetVkTextureTransition(VkPipelineStageFlags& mSrcStage, VkPipelineStageFlags mDstStage, const TextureBarrier& barrier);
VkImageLayout vkApiGetAttachmentLayout(VkFormat format, bool includeStencilBit);
VkImageLayout vkApiGetImageLayout(ResourceState layout);
VkImageMemoryBarrier vkApiGetTextureTransition(VkPipelineStageFlags& mSrcStage, VkPipelineStageFlags& mDstStage, const TextureBarrier& barrier);
VkImageAspectFlags vkApiGetImageAspectMask(VkFormat format, bool includeStencilBit);
VkImageUsageFlags vkApiGetImageUsageFlags(ResourceState startState);
VkImageViewType vkApiGetImageViewType(TextureDimension dimension, uint32_t arraySize);
VkImageType vkApiGetImageType(TextureDimension dimension);
VkImageCreateFlags vkApiGetImageCreateFlag(TextureDimension dimension, uint32_t arraySize);
VkSampleCountFlagBits vkApiGetSmpleCountFlag(SampleCount sample);
}

View File

@ -20,10 +20,11 @@ namespace vkn {
};
class VulkanSwapchain {
private:
friend class VulkanWindow;
Device& mDevice;
VkSwapchainKHR mPtr;
int mFrames;
pmr::vector<TextureDesc> mSurfaces{GlobalPool()};
pmr::vector<TextureDesc> mSurfaces{ GlobalPool() };
pmr::vector<VkCommandBuffer> mCommands{ GlobalPool() };
pmr::vector<VkFence> mFences{ GlobalPool() };
pmr::vector<VkSemaphore> mSemaphores{ GlobalPool() };

View File

@ -8,7 +8,7 @@
#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;
@ -24,6 +24,17 @@ namespace vkn {
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)
@ -67,6 +78,65 @@ namespace vkn {
void VulkanAPI::LoadShader(Shader& shader)
{
}
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;
@ -77,7 +147,21 @@ namespace vkn {
{
VulkanContext& ctx = *(VulkanContext*)&context;
ctx.command.EndRecord();
ctx.command.Submit(Backend::RenderWorker->GetQueue().Ptr(), ctx.surfaceFence);
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) {
@ -86,12 +170,11 @@ namespace vkn {
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);
auto desc = vkApiGetTextureTransition(srcStageMask, dstStageMask, barrier);
imageBarriers.push_back(desc);
}
if (dstStageMask == 0) {
@ -102,23 +185,65 @@ namespace vkn {
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 = (VkSampleCountFlagBits)desc.sampleCount;
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++;
}
}
node->pass = GetRenderPass(config);
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);
@ -221,6 +346,7 @@ namespace vkn {
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,
@ -228,9 +354,8 @@ namespace vkn {
.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,
.initialLayout = layout,
.finalLayout = layout,
};
}
// Nulling out the zero-sized lists is necessary to avoid VK_ERROR_OUT_OF_HOST_MEMORY on Adreno.

View File

@ -1,7 +1,32 @@
#include <tuple>
#include "vkn/vulkan_api_help.h"
#include "meta/enum.h"
namespace vkn {
VkImageLayout GetVkLayout(ResourceState layout) {
VkImageLayout vkApiGetAttachmentLayout(VkFormat format, bool includeStencilBit)
{
switch (format)
{
// Depth
case VK_FORMAT_D16_UNORM:
case VK_FORMAT_X8_D24_UNORM_PACK32:
case VK_FORMAT_D32_SFLOAT:
return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
// Stencil
case VK_FORMAT_S8_UINT:
return VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL;
// Depth/stencil
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
if (includeStencilBit)
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
// Assume everything else is Color
default:
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
}
VkImageLayout vkApiGetImageLayout(ResourceState layout) {
switch (layout) {
case ResourceState::UNDEFINED:
return VK_IMAGE_LAYOUT_UNDEFINED;
@ -19,31 +44,22 @@ namespace vkn {
return VK_IMAGE_LAYOUT_GENERAL;
case ResourceState::PRESENT:
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
// Filament sometimes samples from one miplevel while writing to another level in the
// same texture (e.g. bloom does this). Moreover we'd like to avoid lots of expensive
// layout transitions. So, keep it simple and use GENERAL for all color-attachable
// textures.
case ResourceState::COLOR_ATTACHMENT:
return VK_IMAGE_LAYOUT_GENERAL;
case ResourceState::COLOR_ATTACHMENT_RESOLVE:
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
}
VkImageMemoryBarrier GetVkTextureTransition(VkPipelineStageFlags& mSrcStage, VkPipelineStageFlags mDstStage, const TextureBarrier& barrier) {
VkImageMemoryBarrier vkApiGetTextureTransition(VkPipelineStageFlags& mSrcStage, VkPipelineStageFlags& mDstStage, const TextureBarrier& barrier) {
VkAccessFlags srcAccessMask, dstAccessMask;
VkPipelineStageFlags srcStage, dstStage;
switch (barrier.mSrcState) {
case ResourceState::UNDEFINED:
srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
srcStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
srcAccessMask = 0;
srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
break;
case ResourceState::COLOR_ATTACHMENT:
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case ResourceState::READ_WRITE:
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
@ -69,23 +85,16 @@ namespace vkn {
srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case ResourceState::COLOR_ATTACHMENT_RESOLVE:
srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case ResourceState::PRESENT:
srcAccessMask = VK_ACCESS_NONE;
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
srcStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
break;
}
switch (barrier.mDstState) {
case ResourceState::COLOR_ATTACHMENT:
dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case ResourceState::READ_WRITE:
dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
@ -115,10 +124,9 @@ namespace vkn {
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
break;
case ResourceState::PRESENT:
case ResourceState::COLOR_ATTACHMENT_RESOLVE:
case ResourceState::UNDEFINED:
dstAccessMask = 0;
dstStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dstStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
break;
}
mSrcStage |= srcStage;
@ -134,12 +142,95 @@ namespace vkn {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = srcAccessMask,
.dstAccessMask = dstAccessMask,
.oldLayout = GetVkLayout(barrier.mSrcState),
.newLayout = GetVkLayout(barrier.mDstState),
.oldLayout = vkApiGetImageLayout(barrier.mSrcState),
.newLayout = vkApiGetImageLayout(barrier.mDstState),
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = (VkImage)barrier.mTexture.image,
.subresourceRange = subresources
};
}
VkImageAspectFlags vkApiGetImageAspectMask(VkFormat format, bool includeStencilBit)
{
VkImageAspectFlags result = 0;
switch (format)
{
// Depth
case VK_FORMAT_D16_UNORM:
case VK_FORMAT_X8_D24_UNORM_PACK32:
case VK_FORMAT_D32_SFLOAT:
result = VK_IMAGE_ASPECT_DEPTH_BIT;
break;
// Stencil
case VK_FORMAT_S8_UINT:
result = VK_IMAGE_ASPECT_STENCIL_BIT;
break;
// Depth/stencil
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
result = VK_IMAGE_ASPECT_DEPTH_BIT;
if (includeStencilBit)
result |= VK_IMAGE_ASPECT_STENCIL_BIT;
break;
// Assume everything else is Color
default:
result = VK_IMAGE_ASPECT_COLOR_BIT;
break;
}
return result;
}
VkImageUsageFlags vkApiGetImageUsageFlags(ResourceState startState)
{
VkImageUsageFlags usageFlags = 0;
if (any(startState & ResourceState::COLOR_ATTACHMENT))
usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
else if (any(startState & ResourceState::DEPTH_ATTACHMENT))
usageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
return usageFlags;
}
VkImageViewType vkApiGetImageViewType(TextureDimension dimension, uint32_t arraySize)
{
if (any(dimension & TextureDimension::TEX_1D)) {
return arraySize > 1 ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D;
}
if (any(dimension & TextureDimension::TEX_CUBE)) {
return arraySize > 1 ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE;
}
if (any(dimension & TextureDimension::TEX_2D)) {
return arraySize > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
}
if (any(dimension & TextureDimension::TEX_3D)) {
return VK_IMAGE_VIEW_TYPE_3D;
}
return VK_IMAGE_VIEW_TYPE_MAX_ENUM;
}
VkImageType vkApiGetImageType(TextureDimension dimension)
{
if (any(dimension & TextureDimension::TEX_1D)) {
return VK_IMAGE_TYPE_1D;
}
if (any(dimension & TextureDimension::TEX_2D)) {
return VK_IMAGE_TYPE_2D;
}
if (any(dimension & TextureDimension::TEX_3D)) {
return VK_IMAGE_TYPE_3D;
}
return VK_IMAGE_TYPE_MAX_ENUM;
}
VkImageCreateFlags vkApiGetImageCreateFlag(TextureDimension dimension, uint32_t arraySize)
{
VkImageCreateFlags flag{};
if (any(dimension & TextureDimension::TEX_CUBE)) {
flag |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
}
if (arraySize > 0) {
flag |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR;
}
return flag;
}
VkSampleCountFlagBits vkApiGetSmpleCountFlag(SampleCount sample)
{
return (VkSampleCountFlagBits)sample;
}
}

View File

@ -8,6 +8,7 @@
#include "asset/resource_system.h"
#include "zlog.h"
#include <algorithm>
#include <tinyimageformat/tinyimageformat_apis.h>
namespace vkn {
bool VulkanWindow::CreateRender(CreatePFN createPFN, VulkanWindowArgs& args)
{
@ -21,6 +22,7 @@ namespace vkn {
args.width = mWidth;
args.height = mHeight;
mSwapchain = new (GlobalPool()) VulkanSwapchain(backend.GetDevice(), surface, args);
api->graph.mSurface = mSwapchain->mSurfaces[0];
return true;
}
VulkanSwapchain::VulkanSwapchain(Device& device, VkSurfaceKHR surface, VulkanWindowArgs& args)
@ -63,8 +65,16 @@ namespace vkn {
mSurfaces.reserve(mFrames);
mCommands.reserve(mFrames);
mSemaphores.reserve(mFrames + mFrames);
TextureDesc desc{};
desc.width = args.width;
desc.height = args.height;
desc.format = TinyImageFormat_FromVkFormat((TinyImageFormat_VkFormat)args.imageFormat);
desc.state = ResourceState::PRESENT;
desc.sampleCount = SampleCount::SAMPLE_COUNT_1;
desc.arraySize = 1;
desc.mipLevel = 1;
desc.depth = 1;
for (int i = 0; i < mFrames; i++) {
api::TextureDesc desc;
desc.image = swapchain_images[i];
mSurfaces.push_back(desc);
mSemaphores.push_back(mDevice.CreateSemaphore());
@ -91,7 +101,7 @@ namespace vkn {
{
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pWaitSemaphores = &ctx.surfaceSemaphore;
presentInfo.pWaitSemaphores = &ctx.presentSemaphore;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pSwapchains = &mPtr;
presentInfo.swapchainCount = 1;

View File

@ -5,14 +5,6 @@
#include "render/pass/demo_pass.h"
#include <iostream>
using namespace api;
FrameGraphNodePtr FrameGraph::AddRenderPass(const RenderPassSetupFunction& setup, const RenderPassNodeExecuteFn& executor)
{
FrameGraphNodePtr node_ptr{ mGraph.addNode(), executor };
RenderPassBuilder builder{ this, node_ptr };
setup(*this, builder);
mNodes.push_back(node_ptr);
return node_ptr;
}
RenderAPI* API;
void ZWorldModule::OnLoad(int argc, char** argv)
{
@ -24,6 +16,7 @@ void ZWorldModule::OnLoad(int argc, char** argv)
if (!window->CreateRender(&SDL_Vulkan_CreateSurface, args)) {
zlog::errorf("SDL_Vulkan_CreateSurface failed {}", SDL_GetError());
}
API->Init();
API->context.views.push_back({});
}