framegraph
This commit is contained in:
parent
d154ac2251
commit
c652f8c86d
@ -41,4 +41,22 @@ namespace api {
|
||||
// For color attachment MSAA resolves.
|
||||
COLOR_ATTACHMENT_RESOLVE,
|
||||
};
|
||||
enum class TargetBufferFlags : uint32_t {
|
||||
NONE = 0x0u, //!< No buffer selected.
|
||||
COLOR0 = 0x00000001u, //!< Color buffer selected.
|
||||
COLOR1 = 0x00000002u, //!< Color buffer selected.
|
||||
COLOR2 = 0x00000004u, //!< Color buffer selected.
|
||||
COLOR3 = 0x00000008u, //!< Color buffer selected.
|
||||
COLOR4 = 0x00000010u, //!< Color buffer selected.
|
||||
COLOR5 = 0x00000020u, //!< Color buffer selected.
|
||||
COLOR6 = 0x00000040u, //!< Color buffer selected.
|
||||
COLOR7 = 0x00000080u, //!< Color buffer selected.
|
||||
|
||||
COLOR = COLOR0, //!< \deprecated
|
||||
COLOR_ALL = COLOR0 | COLOR1 | COLOR2 | COLOR3 | COLOR4 | COLOR5 | COLOR6 | COLOR7,
|
||||
DEPTH = 0x10000000u, //!< Depth buffer selected.
|
||||
STENCIL = 0x20000000u, //!< Stencil buffer selected.
|
||||
DEPTH_AND_STENCIL = DEPTH | STENCIL, //!< depth and stencil buffer selected.
|
||||
ALL = COLOR_ALL | DEPTH | STENCIL //!< Color, depth and stencil buffer selected.
|
||||
};
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
#include "render/graph/frame_graph.h"
|
||||
#include "render/graph/frame_graph_builder.h"
|
||||
#include "render/renderapi.h"
|
||||
namespace api {
|
||||
FrameGraphNodePtr FrameGraph::AddRenderPass(const RenderPassSetupFunction& setup, const RenderPassNodeExecuteFn& executor)
|
||||
{
|
||||
@ -50,9 +51,14 @@ namespace api {
|
||||
{
|
||||
mGraph.clear();
|
||||
mNodes.clear();
|
||||
}
|
||||
void BeginRenderPass() {
|
||||
|
||||
}
|
||||
void FrameGraph::ExecuteRenderPass(RenderPassNode* node, FRenderView& view)
|
||||
{
|
||||
BeginRenderPass();
|
||||
//RenderAPI::Ptr()->BeginRenderPass();
|
||||
RenderPassContext context{};
|
||||
std::get<RenderPassExecuteFunction>(node->executor)(*this, context);
|
||||
}
|
||||
|
||||
15
engine/modules/engine/zlib/include/meta/enum.h
Normal file
15
engine/modules/engine/zlib/include/meta/enum.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
namespace meta {
|
||||
template<typename Enum>
|
||||
concept is_enum_t = requires { std::is_enum_v<Enum>; };
|
||||
}
|
||||
template<meta::is_enum_t Enum>
|
||||
inline constexpr Enum operator&(Enum lhs, Enum rhs) noexcept {
|
||||
using underlying = std::underlying_type_t<Enum>;
|
||||
return Enum(underlying(lhs) & underlying(rhs));
|
||||
}
|
||||
template<meta::is_enum_t Enum>
|
||||
inline constexpr bool any(Enum lhs) noexcept {
|
||||
using underlying = std::underlying_type_t<Enum>;
|
||||
return (underlying)lhs != 0;
|
||||
}
|
||||
29
engine/modules/engine/zlib/include/meta/hash.h
Normal file
29
engine/modules/engine/zlib/include/meta/hash.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
namespace meta {
|
||||
// Hash function that takes an arbitrary swath of word-aligned data.
|
||||
inline uint32_t murmur3(const uint32_t* key, size_t wordCount, uint32_t seed) noexcept {
|
||||
uint32_t h = seed;
|
||||
size_t i = wordCount;
|
||||
do {
|
||||
uint32_t k = *key++;
|
||||
k *= 0xcc9e2d51u;
|
||||
k = (k << 15u) | (k >> 17u);
|
||||
k *= 0x1b873593u;
|
||||
h ^= k;
|
||||
h = (h << 13u) | (h >> 19u);
|
||||
h = (h * 5u) + 0xe6546b64u;
|
||||
} while (--i);
|
||||
h ^= wordCount;
|
||||
h ^= h >> 16u;
|
||||
h *= 0x85ebca6bu;
|
||||
h ^= h >> 13u;
|
||||
h *= 0xc2b2ae35u;
|
||||
h ^= h >> 16u;
|
||||
return h;
|
||||
}
|
||||
template<typename T>
|
||||
inline uint32_t MurmurHashFn(const T& key) noexcept {
|
||||
static_assert(0 == (sizeof(key) & 3u), "Hashing requires a size that is a multiple of 4.");
|
||||
return murmur3((const uint32_t*)&key, sizeof(key) / 4, 0);
|
||||
}
|
||||
}
|
||||
@ -5,17 +5,19 @@
|
||||
#include <functional>
|
||||
#define VK_NO_PROTOTYPES
|
||||
#include "volk/volk.h"
|
||||
#include <render/type.h>
|
||||
#include "render/type.h"
|
||||
#define Z_RENDER_DEBUG 1
|
||||
namespace vkn {
|
||||
using pmr::Name;
|
||||
using pmr::table;
|
||||
using std::string_view;
|
||||
inline constexpr string_view VulkanEngineName = "vulkan";
|
||||
class CommandBuffer;
|
||||
using voidFn = std::function<void()>;
|
||||
using commandFn = std::function<void(CommandBuffer& cmd)>;
|
||||
static constexpr uint8_t MAX_SUPPORTED_RENDER_TARGET_COUNT = 8u;
|
||||
using api::TargetBufferFlags;
|
||||
|
||||
constexpr string_view VulkanEngineName = "vulkan";
|
||||
constexpr uint8_t MAX_SUPPORTED_RENDER_TARGET_COUNT = 8u;
|
||||
struct MeshVAO
|
||||
{
|
||||
uint32_t indexCount = 0; // 索引数量
|
||||
@ -24,48 +26,26 @@ namespace vkn {
|
||||
VkBuffer vertexBuffer = VK_NULL_HANDLE;
|
||||
bool inUse = false;
|
||||
};
|
||||
enum class TargetBufferFlags : uint32_t {
|
||||
NONE = 0x0u, //!< No buffer selected.
|
||||
COLOR0 = 0x00000001u, //!< Color buffer selected.
|
||||
COLOR1 = 0x00000002u, //!< Color buffer selected.
|
||||
COLOR2 = 0x00000004u, //!< Color buffer selected.
|
||||
COLOR3 = 0x00000008u, //!< Color buffer selected.
|
||||
COLOR4 = 0x00000010u, //!< Color buffer selected.
|
||||
COLOR5 = 0x00000020u, //!< Color buffer selected.
|
||||
COLOR6 = 0x00000040u, //!< Color buffer selected.
|
||||
COLOR7 = 0x00000080u, //!< Color buffer selected.
|
||||
|
||||
COLOR = COLOR0, //!< \deprecated
|
||||
COLOR_ALL = COLOR0 | COLOR1 | COLOR2 | COLOR3 | COLOR4 | COLOR5 | COLOR6 | COLOR7,
|
||||
DEPTH = 0x10000000u, //!< Depth buffer selected.
|
||||
STENCIL = 0x20000000u, //!< Stencil buffer selected.
|
||||
DEPTH_AND_STENCIL = DEPTH | STENCIL, //!< depth and stencil buffer selected.
|
||||
ALL = COLOR_ALL | DEPTH | STENCIL //!< Color, depth and stencil buffer selected.
|
||||
};
|
||||
|
||||
struct alignas(8) RenderPassKey {
|
||||
// For each target, we need to know three image layouts: the layout BEFORE the pass, the
|
||||
// layout DURING the pass, and the layout AFTER the pass. Here are the rules:
|
||||
// - For depth, we explicitly specify all three layouts.
|
||||
// - Color targets have their initial image layout specified with a bitmask.
|
||||
// - For each color target, the pre-existing layout is either UNDEFINED (0) or GENERAL (1).
|
||||
// - The render pass and final images layout for color buffers is always
|
||||
// VulkanLayout::COLOR_ATTACHMENT.
|
||||
uint8_t initialColorLayoutMask;
|
||||
|
||||
// Note that if VulkanLayout grows beyond 16, we'd need to up this.
|
||||
api::RenderLayout initialDepthLayout : 8;
|
||||
uint8_t padding0;
|
||||
uint8_t padding1;
|
||||
|
||||
VkFormat colorFormat[MAX_SUPPORTED_RENDER_TARGET_COUNT]; // 32 bytes
|
||||
VkFormat depthFormat; // 4 bytes
|
||||
struct RenderPassKey {
|
||||
VkFormat colorFormat[8];
|
||||
VkFormat depthFormat;
|
||||
VkSampleCountFlagBits samples;
|
||||
TargetBufferFlags clear; // 4 bytes
|
||||
TargetBufferFlags discardStart; // 4 bytes
|
||||
TargetBufferFlags discardEnd; // 4 bytes
|
||||
uint8_t samples; // 1 byte
|
||||
uint8_t needsResolveMask; // 1 byte
|
||||
uint8_t subpassMask;// 1 byte
|
||||
uint8_t viewCount; // 1 byte
|
||||
uint8_t initialColorLayoutMask;// 1 byte
|
||||
uint8_t needsResolveMask; // 1 byte
|
||||
};
|
||||
}
|
||||
#include "meta/hash.h"
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<vkn::RenderPassKey>
|
||||
{
|
||||
size_t operator()(const vkn::RenderPassKey& key) const noexcept
|
||||
{
|
||||
return meta::MurmurHashFn(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -7,6 +7,7 @@ namespace vkn {
|
||||
class Backend;
|
||||
class VulkanWindow;
|
||||
struct MeshVAO;
|
||||
struct RenderPassKey;
|
||||
using api::Guid;
|
||||
using api::Mesh;
|
||||
using api::Shader;
|
||||
@ -18,8 +19,7 @@ namespace vkn {
|
||||
VulkanWindow& window;
|
||||
Backend backend;
|
||||
table<Guid, MeshVAO> MeshTable;
|
||||
table<Guid, VkRenderPass> RenderPassCache;
|
||||
|
||||
table<RenderPassKey, VkRenderPass> RenderPassCache;
|
||||
public:
|
||||
VulkanAPI();
|
||||
|
||||
@ -33,7 +33,7 @@ namespace vkn {
|
||||
|
||||
void BeginFrame()override;
|
||||
void EndFrame()override;
|
||||
|
||||
VkPipeline GetPipeline();
|
||||
VkRenderPass GetRenderPass(RenderPassKey config);
|
||||
|
||||
Backend& GetBackend() {
|
||||
|
||||
@ -5,7 +5,22 @@
|
||||
#include "vkn/thread/buffer_worker.h"
|
||||
#include "vkn/thread/command_worker.h"
|
||||
#include "render/asset/mesh.h"
|
||||
#include "meta/enum.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)
|
||||
@ -58,6 +73,55 @@ namespace vkn {
|
||||
window.Present(*(VulkanContext*)&context);
|
||||
}
|
||||
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,
|
||||
@ -68,7 +132,116 @@ namespace vkn {
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
shared_module("vulkan","engine")
|
||||
add_headerfiles("include/**.h")
|
||||
add_headerfiles("include/**.h","include/**.inl")
|
||||
add_files("src/**.cpp", "include/volk/volk.c")
|
||||
add_packages("vulkansdk", {public = true})
|
||||
add_dependency("engine", {public = true})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user