update thread_worker

This commit is contained in:
ouczbs 2024-08-27 20:21:32 +08:00
parent 25664db985
commit 8b234cd835
18 changed files with 234 additions and 133 deletions

View File

@ -5,7 +5,7 @@ namespace refl {
constexpr std::string_view func_signature() noexcept { constexpr std::string_view func_signature() noexcept {
# if defined(__clang__) # if defined(__clang__)
auto sign = std::string_view{ __PRETTY_FUNCTION__ }; auto sign = std::string_view{ __PRETTY_FUNCTION__ };
return sign.substr(53, sign.size() - 54); return sign.substr(47, sign.size() - 47);
# elif defined(__GNUC__) # elif defined(__GNUC__)
auto sign = std::string_view{ __PRETTY_FUNCTION__ }; auto sign = std::string_view{ __PRETTY_FUNCTION__ };
return sign.substr(62, sign.size() - 62); return sign.substr(62, sign.size() - 62);

View File

@ -10,7 +10,7 @@ namespace refl {
parray(): m_cls(nullptr), m_ptr(nullptr),m_count(0) {} parray(): m_cls(nullptr), m_ptr(nullptr),m_count(0) {}
template<typename C> template<typename C>
requires std::is_base_of_v<T, C> requires std::is_base_of_v<T, C>
parray(std::vector<C>& vec) : m_cls(&TypeInfo<C>::StaticClass), m_ptr(nullptr){ parray(std::vector<C>& vec) : m_cls(type_info<C>()), m_ptr(nullptr){
m_count = vec.size(); m_count = vec.size();
if (m_count > 0) { if (m_count > 0) {
C* ptr = new C[m_count]; C* ptr = new C[m_count];

View File

@ -3,7 +3,7 @@
#include <semaphore> #include <semaphore>
#include <concepts> #include <concepts>
namespace zstd { namespace zstd {
template<std::move_constructible T> template<typename T>
class channel { class channel {
protected: protected:
int m_tail; int m_tail;

View File

@ -0,0 +1,29 @@
#pragma once
#include "channel.h"
#include <thread>
namespace zstd{
template<typename value_type, typename Worker>
class ThreadWorker {
public:
using Element = value_type;
std::thread mThread;
channel<Element> mChannel;
void WorkLoop() {
mThread.detach();
}
void Loop() {
Worker* worker = dynamic_cast<Worker*>(this);
worker->Loop();
}
public:
ThreadWorker() : ThreadWorker(64) {}
ThreadWorker(int buffer) : mChannel(buffer)
{
mThread = std::thread(&ThreadWorker::WorkLoop, this);
}
void Invoke(const Element& elem) {
mChannel.release(elem);
}
};
}

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#include "thread/worker.h" #include "vkn/type.h"
namespace vkn { namespace vkn {
class Device; class Device;
class Instance; class Instance;
@ -7,16 +7,15 @@ namespace vkn {
protected: protected:
Instance* mInstance; Instance* mInstance;
Device* mDevice; Device* mDevice;
table<Name, CommandWorker*> mWorkerMap;
public: public:
Backend(string_view appName); Backend(string_view appName);
~Backend(); ~Backend();
void InitWorker(Name name, VkCommandPoolCreateFlags flag); template<typename Worker>
CommandWorker* GetWorker(Name name); Worker* InitWorker(Name name, VkCommandPoolCreateFlags flag);
public: public:
static CommandWorker* TransferWorker; static struct BufferWorker* TransferWorker;
static CommandWorker* RenderWorker; static struct CommandWorker* RenderWorker;
static CommandWorker* PresentWorker; static struct CommandWorker* PresentWorker;
}; };
}; };

View File

@ -0,0 +1,14 @@
#pragma once
#include "worker.h"
#include "vkn/wrapper/buffer.h"
namespace vkn {
class Device;
class Queue;
struct Buffer;
class BufferWorker : public ThreadWorker<Buffer, BufferWorker>{
public:
using ThreadWorker<Buffer, BufferWorker>::ThreadWorker;
void InitVmaAllocator(VkInstance instance);
void Loop();
};
};

View File

@ -0,0 +1,22 @@
#pragma once
#include "worker.h"
#include <semaphore>
namespace vkn {
class Device;
class Queue;
class CommandWorker : public ThreadWorker<voidFn, CommandWorker> {
protected:
std::binary_semaphore mSemaphore{0};
public:
using ThreadWorker<voidFn, CommandWorker>::ThreadWorker;
void InvokeBuffer(const commandFn& fn, const Element& callback);
void Buffer(CommandBuffer& cmd, const commandFn& fn, const Element& callback);
void Flush();
void ImmediatelyExecute(const commandFn& fn, const Element& callback) {
Buffer(mImmediateExeCmd, fn , callback);
}
bool Present(VkPresentInfoKHR& presentInfo);
void Loop();
void SyncInvoke(const Element& fn);
};
};

View File

@ -1,21 +0,0 @@
#pragma once
#include <thread>
#include "vkn/type.h"
#include <semaphore>
namespace vkn {
class CommandThreadWorker {
protected:
std::thread mThread;
Name mName;
channel<voidFn> mChannel;
std::binary_semaphore mSemaphore;
protected:
void workloop();
public:
CommandThreadWorker(Name name, int buffer);
~CommandThreadWorker();
void Invoke(const voidFn& fn);
void SyncInvoke(const voidFn& fn);
};
};

View File

@ -1,32 +1,31 @@
#pragma once #pragma once
#include "std/thread.h"
#include "vkn/wrapper/commandpool.h" #include "vkn/wrapper/commandpool.h"
#include "thread_worker.h" #include "vkn/wrapper/queue.h"
namespace vkn { namespace vkn {
class Device; using zstd::channel;
class Queue; template<typename value_type, typename Worker>
class CommandWorker { class ThreadWorker : public zstd::ThreadWorker<value_type, Worker> {
protected: protected:
Name mName;
Device& mDevice; Device& mDevice;
Queue& mQueue; Queue& mQueue;
Name mName;
CommandThreadWorker mWork;
CommandPool mCommandPool; CommandPool mCommandPool;
CommandBuffer mImmediateExeCmd; CommandBuffer mImmediateExeCmd;
public: public:
CommandWorker(Name name, Device& device, Queue& queue, VkCommandPoolCreateFlags queueFlags); ThreadWorker(Name name, Device& device, Queue& queue, VkCommandPoolCreateFlags queueFlags)
: zstd::ThreadWorker<value_type, Worker>(64)
, mName(name)
, mDevice(device)
, mQueue(queue)
, mCommandPool(device, queueFlags, queue.QueueFamilyIndex())
, mImmediateExeCmd(mCommandPool.AllocateBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY))
{}
CommandPool& GetCommandPool() { CommandPool& GetCommandPool() {
return mCommandPool; return mCommandPool;
} }
Queue& GetQueue() { Queue& GetQueue() {
return mQueue; return mQueue;
} }
void Invoke(const voidFn& fn);
void InvokeBuffer(const commandFn& fn, const voidFn& callback);
void Buffer(CommandBuffer& cmd, const commandFn& fn, const voidFn& callback);
void Flush();
void ImmediatelyExecute(const commandFn& fn, const voidFn callback) {
Buffer(mImmediateExeCmd, fn , callback);
}
bool Present(VkPresentInfoKHR& presentInfo);
}; };
}; }

View File

@ -1,5 +1,4 @@
#pragma once #pragma once
#include "std/channel.h"
#include "pmr/frame_allocator.h" #include "pmr/frame_allocator.h"
#include "pmr/name.h" #include "pmr/name.h"
#include <Windows.h> #include <Windows.h>
@ -11,7 +10,6 @@ namespace vkn {
using pmr::Name; using pmr::Name;
using pmr::table; using pmr::table;
using std::string_view; using std::string_view;
using zstd::channel;
inline constexpr string_view VulkanEngineName = "vulkan"; inline constexpr string_view VulkanEngineName = "vulkan";
class CommandBuffer; class CommandBuffer;
using voidFn = std::function<void()>; using voidFn = std::function<void()>;

View File

@ -1,16 +1,20 @@
#pragma once #pragma once
#include "type.h" #include "type.h"
#include "asset/res/guid.h"
#include "render/renderapi.h" #include "render/renderapi.h"
#include "backend.h" #include "backend.h"
namespace vkn { namespace vkn {
class Backend; class Backend;
class VulkanWindow; class VulkanWindow;
struct MeshVAO;
using api::Guid;
using api::Mesh; using api::Mesh;
using api::Shader; using api::Shader;
class VULKAN_API VulkanAPI : public api::RenderAPI { class VULKAN_API VulkanAPI : public api::RenderAPI {
private: private:
VulkanWindow& window; VulkanWindow& window;
Backend backend; Backend backend;
table<Guid, MeshVAO> MeshTable;
public: public:
VulkanAPI(); VulkanAPI();

View File

@ -0,0 +1,18 @@
#pragma once
#include "vkn/type.h"
namespace vkn {
struct MeshVAO
{
uint32_t indexCount = 0; // 索引数量
VkBuffer indexBuffer = VK_NULL_HANDLE;
uint32_t vertexCount = 0; // 顶点数量
VkBuffer vertexBuffer = VK_NULL_HANDLE;
bool inUse = false;
};
struct Buffer {
VkBuffer* ppBuffer;
void* pCpuData;
VkBufferUsageFlags usage;
uint32_t size;
};
}

View File

@ -4,11 +4,19 @@
#include "vkn/wrapper/instance.h" #include "vkn/wrapper/instance.h"
#include "vkn/wrapper/instance_create.h" #include "vkn/wrapper/instance_create.h"
#include "vkn/wrapper/queue.h" #include "vkn/wrapper/queue.h"
#include "vkn/thread/buffer_worker.h"
#include "vkn/thread/command_worker.h"
namespace vkn { namespace vkn {
CommandWorker* Backend::TransferWorker; BufferWorker* Backend::TransferWorker;
CommandWorker* Backend::RenderWorker; CommandWorker* Backend::RenderWorker;
CommandWorker* Backend::PresentWorker; CommandWorker* Backend::PresentWorker;
Backend::Backend(string_view appName) : mWorkerMap(GlobalPool()) template<typename Worker>
inline Worker* Backend::InitWorker(Name name, VkCommandPoolCreateFlags flag)
{
auto queue = mDevice->GetQueue(name);
return new Worker(name, *mDevice, *queue, flag);
}
Backend::Backend(string_view appName)
{ {
InstanceCreator instanceCreator{}; InstanceCreator instanceCreator{};
mInstance = new (GlobalPool()) Instance(instanceCreator); mInstance = new (GlobalPool()) Instance(instanceCreator);
@ -21,33 +29,14 @@ namespace vkn {
deviceCreator.AddQueue(Queue::PresentQueue, VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT, 1.0); deviceCreator.AddQueue(Queue::PresentQueue, VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT, 1.0);
mDevice = new (GlobalPool()) Device(deviceCreator); mDevice = new (GlobalPool()) Device(deviceCreator);
InitWorker(Queue::TransferQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); Backend::TransferWorker = InitWorker<BufferWorker>(Queue::TransferQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
InitWorker(Queue::RenderQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); Backend::RenderWorker = InitWorker<CommandWorker>(Queue::RenderQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
InitWorker(Queue::ComputeQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); Backend::PresentWorker = InitWorker<CommandWorker>(Queue::ComputeQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
InitWorker(Queue::PresentQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); Backend::TransferWorker->InitVmaAllocator(mInstance->Ptr());
Backend::TransferWorker = GetWorker(Queue::TransferQueue);
Backend::RenderWorker = GetWorker(Queue::RenderQueue);
} }
Backend::~Backend() Backend::~Backend()
{ {
mInstance->~Instance(); mInstance->~Instance();
mDevice->~Device(); mDevice->~Device();
} }
void Backend::InitWorker(Name name, VkCommandPoolCreateFlags flag)
{
auto queue = mDevice->GetQueue(name);
if (queue) {
auto worker = new CommandWorker(name, *mDevice, *queue, flag);
mWorkerMap.emplace(name, worker);
}
}
CommandWorker* Backend::GetWorker(Name name)
{
auto it = mWorkerMap.find(name);
if (it != mWorkerMap.end()) {
return it->second;
}
return nullptr;
}
} }

View File

@ -0,0 +1,63 @@
#include "vkn/thread/buffer_worker.h"
#include "vkn/wrapper/queue.h"
#include "vkn/wrapper/device.h"
#include "vma/vk_mem_alloc.h"
#include "zlog.h"
namespace vkn {
static VmaAllocator vmaAllocator;
void BufferWorker::InitVmaAllocator(VkInstance instance)
{
// 因为用volk库手动加载所有Vulkan函数了所以这里要给VMA传递获取函数地址的方法让VMA可以正确获取Vulkan函数
VmaVulkanFunctions vmaVkFunctions = {};
vmaVkFunctions.vkGetInstanceProcAddr = vkGetInstanceProcAddr;
vmaVkFunctions.vkGetDeviceProcAddr = vkGetDeviceProcAddr;
VmaAllocatorCreateInfo vmaInfo = {};
vmaInfo.vulkanApiVersion = VK_HEADER_VERSION_COMPLETE;
vmaInfo.instance = instance;
vmaInfo.physicalDevice = mDevice.GetPhysical();
vmaInfo.device = mDevice.Ptr();
vmaInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
// 如果不手动加载Vulkan函数这里可以填NULL
vmaInfo.pVulkanFunctions = &vmaVkFunctions;
vmaCreateAllocator(&vmaInfo, &vmaAllocator);
}
void BufferWorker::Loop()
{
while (true) {
Element elem = mChannel.acquire();
VkBufferCreateInfo bufferInfo = {};
bufferInfo.size = elem.size;
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocation stagingAllocation;
VmaAllocationCreateInfo allocationInfo = {};
allocationInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
VkBuffer stagingBuffer;
vmaCreateBuffer(vmaAllocator, &bufferInfo, &allocationInfo, &stagingBuffer, &stagingAllocation, nullptr);
// 将数据复制到 Staging Buffer
void* data;
vmaMapMemory(vmaAllocator, stagingAllocation, &data);
memcpy(data, elem.pCpuData, elem.size);
vmaUnmapMemory(vmaAllocator, stagingAllocation);
// 创建 GPU Buffer, GPU内部缓冲区访问速度非常快
VkBuffer& gpuBuffer = *elem.ppBuffer;
VmaAllocation gpuBufferAllocation;
bufferInfo.usage = elem.usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
allocationInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
vmaCreateBuffer(vmaAllocator, &bufferInfo, &allocationInfo, &gpuBuffer, &gpuBufferAllocation, nullptr);
// 复制数据
VkBufferCopy copyRegion = {};
copyRegion.size = elem.size;
vkCmdCopyBuffer(mImmediateExeCmd.Ptr(), stagingBuffer, gpuBuffer, 1, &copyRegion);
}
}
}

View File

@ -1,30 +1,15 @@
#include "vkn/thread/worker.h" #include "vkn/thread/command_worker.h"
#include "vkn/wrapper/queue.h"
#include "vkn/wrapper/device.h" #include "vkn/wrapper/device.h"
namespace vkn { namespace vkn {
CommandWorker::CommandWorker(Name name, Device& device, Queue& queue, VkCommandPoolCreateFlags queueFlags) void CommandWorker::InvokeBuffer(const commandFn& fn, const Element& callback)
:mName(name)
,mDevice(device)
,mQueue(queue)
,mCommandPool(device, queueFlags, queue.QueueFamilyIndex())
,mWork(name, 64)
,mImmediateExeCmd(mCommandPool.AllocateBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY))
{ {
} Invoke([=, this]() {
void CommandWorker::Invoke(const voidFn& fn)
{
mWork.Invoke(fn);
}
void CommandWorker::InvokeBuffer(const commandFn& fn, const voidFn& callback)
{
mWork.Invoke([=, this]() {
CommandBuffer cmd = mCommandPool.Pop(); CommandBuffer cmd = mCommandPool.Pop();
Buffer(cmd, fn, callback); Buffer(cmd, fn, callback);
mCommandPool.Push(cmd); mCommandPool.Push(cmd);
}); });
} }
void CommandWorker::Buffer(CommandBuffer& cmd, const commandFn& fn, const voidFn& callback) void CommandWorker::Buffer(CommandBuffer& cmd, const commandFn& fn, const Element& callback)
{ {
cmd.BeginRecord(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); cmd.BeginRecord(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
fn(cmd); fn(cmd);
@ -37,11 +22,26 @@ namespace vkn {
void CommandWorker::Flush() void CommandWorker::Flush()
{ {
mWork.SyncInvoke([]{}); SyncInvoke([]{});
} }
bool CommandWorker::Present(VkPresentInfoKHR& presentInfo) bool CommandWorker::Present(VkPresentInfoKHR& presentInfo)
{ {
VkResult result = vkQueuePresentKHR(mQueue.Ptr(), &presentInfo); VkResult result = vkQueuePresentKHR(mQueue.Ptr(), &presentInfo);
return result == VK_SUCCESS; return result == VK_SUCCESS;
} }
void CommandWorker::SyncInvoke(const Element& fn)
{
Invoke([=, this]() {
fn();
mSemaphore.release();
});
mSemaphore.acquire();
}
void CommandWorker::Loop()
{
while (true) {
Element elem = mChannel.acquire();
elem();
}
}
} }

View File

@ -1,36 +0,0 @@
#include "vkn/thread/thread_worker.h"
#include "zlog.h"
namespace vkn {
CommandThreadWorker::CommandThreadWorker(Name name, int buffer)
: mName(name)
, mChannel(buffer)
, mSemaphore(0)
{
mThread = std::thread(&CommandThreadWorker::workloop, this);
}
CommandThreadWorker::~CommandThreadWorker() {
zlog::info("~CommandThreadWorker");
zlog::flush();
}
void CommandThreadWorker::workloop()
{
mThread.detach();
while (true) {
voidFn fn = mChannel.acquire();
fn();
}
}
void CommandThreadWorker::Invoke(const voidFn& fn)
{
mChannel.release(fn);
}
void CommandThreadWorker::SyncInvoke(const voidFn& fn)
{
Invoke([=,this]() {
fn();
mSemaphore.release();
});
mSemaphore.acquire();
}
}

View File

@ -1,5 +1,8 @@
#include "vkn/vulkan_api.h" #include "vkn/vulkan_api.h"
#include "vkn/vulkan_window.h" #include "vkn/vulkan_window.h"
#include "vkn/wrapper/buffer.h"
#include "vkn/thread/buffer_worker.h"
#include "vkn/thread/command_worker.h"
#include "render/asset/mesh.h" #include "render/asset/mesh.h"
namespace vkn { namespace vkn {
VulkanAPI::VulkanAPI() : window(*VulkanWindow::Ptr()), backend(VulkanEngineName) VulkanAPI::VulkanAPI() : window(*VulkanWindow::Ptr()), backend(VulkanEngineName)
@ -16,12 +19,29 @@ namespace vkn {
} }
void VulkanAPI::SetStaticMesh(Mesh& mesh) void VulkanAPI::SetStaticMesh(Mesh& mesh)
{ {
auto Indices = mesh.GetIndices(); auto& Indices = mesh.GetIndices();
auto Vertices = mesh.GetVertices(); 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::DrawStaticMesh(Mesh& mesh)
{ {
} }
void VulkanAPI::LoadShader(Shader& shader) void VulkanAPI::LoadShader(Shader& shader)
{ {

View File

@ -0,0 +1,3 @@
namespace vkn{
}