封装render_context

This commit is contained in:
Nanako 2025-02-18 16:58:45 +08:00
parent 6a7ebf73a9
commit 45231d9953
17 changed files with 656 additions and 34 deletions

30
src/core/a.puml Normal file
View File

@ -0,0 +1,30 @@
@startuml
start
:主线程启动;
fork
:创建窗口;
:初始化命令缓冲池;
fork again
:启动工作线程;
repeat
:锁定次级CB队列;
if (有可用CB?) then (是)
:取出次级CB;
else (否)
:创建新次级CB;
endif
:录制绘制命令;
:返回就绪队列;
repeat while(有更多任务?)
end fork
:开始主CB录制;
while (遍历所有窗口?)
:合并次级CB;
:提交到GPU;
:呈现窗口;
endwhile
stop
@enduml

57
src/core/b.puml Normal file
View File

@ -0,0 +1,57 @@
@startuml
skinparam classFontSize 13
skinparam classAttributeIconSize 0
class GUIFramework {
+ WindowScheduler windowMgr
+ CommandBufferPool cmdPool
+ WidgetCacheSystem cacheSys
+ MainLoop()
+ SubmitFrame()
}
class WindowScheduler {
+ vector<WindowContext> windows
+ CreateWindow()
+ CloseWindow()
}
class WindowContext {
- LLGL::Window* window
- LLGL::CommandBuffer* primaryCB
+ vector<CommandBuffer*> secondaryCBs
+ CachedTextureManager textureCache
}
class CommandBufferPool {
- queue<LLGL::CommandBuffer*> freeBuffers
- queue<LLGL::CommandBuffer*> readyBuffers
+ AsyncRecord(function)
+ SyncThreads()
}
class ElementDrawer {
- LLGL::CommandBuffer* currentCB
+ DrawRect()
+ DrawText()
+ DrawRoundedRect()
+ SetTransform()
+ PushState()
+ PopState()
}
class WidgetCacheSystem {
- map<Widget*, CachedTexture> cacheMap
+ GetCachedTexture()
+ InvalidateCache()
+ UpdateCache()
}
GUIFramework "1" *-- "1" WindowScheduler
GUIFramework "1" *-- "1" CommandBufferPool
GUIFramework "1" *-- "1" WidgetCacheSystem
WindowScheduler "1" *-- "many" WindowContext
CommandBufferPool "1" *-- "many" LLGL::CommandBuffer
ElementDrawer "1" --* WidgetCacheSystem
ElementDrawer "1" --* LLGL::CommandBuffer
@enduml

26
src/core/c.puml Normal file
View File

@ -0,0 +1,26 @@
@startuml
actor MainThread
participant WorkerThread
participant GPU
MainThread -> WorkerThread : 分发录制任务
activate WorkerThread
WorkerThread -> WorkerThread : 获取次级CB
WorkerThread -> ElementDrawer : 录制绘制命令
WorkerThread -> CommandBufferPool : 提交就绪CB
deactivate WorkerThread
MainThread -> MainThread : 同步所有工作线程
MainThread -> WindowContext : 开始主CB录制
loop 每个窗口
WindowContext -> CommandBufferPool : 获取就绪CB列表
CommandBufferPool -> WindowContext : 返回次级CB集合
WindowContext -> LLGL::CommandBuffer : 执行次级CB
end
MainThread -> GPU : 提交主CB
GPU -> GPU : 执行绘制命令
GPU -> Window : 呈现画面
@enduml

16
src/core/d.puml Normal file
View File

@ -0,0 +1,16 @@
@startuml
state CommandBufferState {
[*] --> Idle
Idle --> Recording : Begin()
Recording --> Executable : End()
Executable --> Pending : Submit()
Pending --> Idle : Reset()
}
state CacheState {
[*] --> Valid
Valid --> Invalid : Widget Changed
Invalid --> Rendering : Start Update
Rendering --> Valid : Complete
}
@enduml

28
src/core/e.puml Normal file
View File

@ -0,0 +1,28 @@
@startuml
skinparam componentStyle rectangle
skinparam nodesep 20
skinparam ranksep 30
package "GUI Framework" {
[Window Manager] as WM
[Command Buffer Pool] as CBP
[Cache System] as CS
[Element Drawer] as ED
}
package "LLGL Backend" {
[Command Buffer] as CB
[Texture] as Tex
[Render Context] as RC
}
WM --> CBP : manages
CBP --> CB : creates
ED --> CS : queries
ED --> CB : records
CS --> Tex : caches
[GUI Framework] ..> [LLGL Backend] : depends on
note right of CB: 管理图形API原生命令缓冲区
note bottom of Tex: 存储缓存的UI元素渲染结果
@enduml

View File

@ -3,10 +3,10 @@
#include "async/thread_pool.h"
#include "window/window_manager.h"
mirage::duration_type delta_time = {};
mirage::time_type begin_time = {};
mirage::time_type last_time = {};
LLGL::RenderSystemPtr renderer = nullptr;
mirage::duration_type delta_time = {};
mirage::time_type begin_time = {};
mirage::time_type last_time = {};
LLGL::RenderSystemPtr renderer = nullptr;
namespace mirage {
void on_llgl_log(LLGL::Log::ReportType type, const char* text, void* user_data) {
@ -51,6 +51,7 @@ namespace mirage {
spdlog::info("供应商: {}", renderer_info.vendorName.c_str());
spdlog::info("着色器语言: {}", renderer_info.shadingLanguageName.c_str());
spdlog::info("渲染器加载成功");
return true;
}
bool init_window(const init_info& in_info) {
@ -66,6 +67,10 @@ namespace mirage {
spdlog::info("主窗口创建成功");
return true;
}
void destroy_renderer() {
LLGL::RenderSystem::Unload(std::move(renderer));
}
bool init(const init_info& in_info) {
spdlog::info("初始化 mirage");
@ -84,7 +89,7 @@ namespace mirage {
if (!lazy_singleton<window_manager>::get().destroy()) {
spdlog::warn("窗口管理器销毋失败");
}
LLGL::RenderSystem::Unload(std::move(renderer));
destroy_renderer();
spdlog::info("mirage 销毁");
}
@ -114,6 +119,7 @@ namespace mirage {
return renderer_api::vulkan;
#endif
}
int run(const init_info& in_init_info) {
try {
if (!init(in_init_info)) {
@ -144,4 +150,7 @@ namespace mirage {
LLGL::RenderSystem* get_renderer() {
return renderer.get();
}
LLGL::CommandQueue* get_main_command_queue() {
return renderer->GetCommandQueue();
}
} // namespace mirage

View File

@ -20,4 +20,5 @@ namespace mirage {
duration_type get_total_time();
LLGL::RenderSystem* get_renderer();
LLGL::CommandQueue* get_main_command_queue();
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <chrono>
#include "LLGL/LLGL.h"
#include <Eigen/Eigen>
namespace mirage {
using time_type = decltype(std::chrono::high_resolution_clock::now());
@ -76,4 +77,49 @@ namespace mirage {
return desc;
}
};
}
struct vertex_param_pack {
union {
float x;
float r;
};
union {
float y;
float g;
};
union {
float z;
float b;
};
union {
float w;
float a;
};
vertex_param_pack(float in_x, float in_y, float in_z, float in_w) : x(in_x), y(in_y), z(in_z), w(in_w) {}
vertex_param_pack(const LLGL::ColorRGBAf& in) : r(in.r), g(in.g), b(in.b), a(in.a) {}
vertex_param_pack(const LLGL::Offset2D& in) : x(in.x), y(in.y), z(0), w(0) {}
vertex_param_pack(const LLGL::Extent2D& in) : x(in.width), y(in.height), z(0), w(0) {}
vertex_param_pack(const LLGL::Offset3D& in) : x(in.x), y(in.y), z(in.z), w(0) {}
vertex_param_pack(const LLGL::Extent3D& in) : x(in.width), y(in.height), z(in.depth), w(0) {}
vertex_param_pack(const Eigen::Vector2f& in) : x(in.x()), y(in.y()), z(0), w(0) {}
vertex_param_pack(const Eigen::Vector3f& in) : x(in.x()), y(in.y()), z(in.z()), w(0) {}
vertex_param_pack(const Eigen::Vector4f& in) : x(in.x()), y(in.y()), z(in.z()), w(in.w()) {}
};
struct vertex_t {
Eigen::Vector3d position;
Eigen::Vector2d uv;
LLGL::ColorRGBAf color;
vertex_param_pack param_a;
vertex_param_pack param_b;
vertex_param_pack param_c;
};
struct triangle_index_t {
int32_t vertex_index[3];
};
struct triangle_t {
vertex_t vertex[3];
};
}

View File

@ -0,0 +1,135 @@
#include "render_buffer.h"
namespace mirage {
render_buffer::~render_buffer() {
if (buffer) {
get_renderer()->Release(*buffer);
}
}
void render_buffer::init(const LLGL::BufferDescriptor& in_desc) {
if (buffer) {
throw std::runtime_error("Buffer already initialized");
}
LLGL::BufferDescriptor desc = in_desc;
desc.size = std::max(desc.size, DEFAULT_INITIAL_SIZE);
buffer = get_renderer()->CreateBuffer(desc);
if (!buffer) {
throw std::runtime_error("Failed to create buffer");
}
capacity = desc.size;
access_flags = desc.cpuAccessFlags;
}
void render_buffer::reserve(size_t new_capacity) {
if (new_capacity > capacity) {
resize(new_capacity, true);
}
}
void render_buffer::shrink_to_fit() {
if (capacity > size) {
resize(size, true);
}
}
void render_buffer::resize(size_t in_capacity_size, bool in_keep_data) {
if (in_capacity_size <= capacity) {
return;
}
LLGL::BufferDescriptor desc = buffer->GetDesc();
desc.size = in_capacity_size;
auto new_buffer = get_renderer()->CreateBuffer(desc);
if (!new_buffer) {
spdlog::error("无法创建缓冲区");
return;
}
if (in_keep_data) {
const auto old_data = read();
get_renderer()->WriteBuffer(*new_buffer, 0, old_data->get_data(), old_data->get_size_bytes());
} else {
size = 0;
}
if (buffer) {
get_renderer()->Release(*buffer);
}
buffer = new_buffer;
capacity = in_capacity_size;
}
void render_buffer::clear() {
size = 0;
}
heap_ptr render_buffer::read(size_t in_offset, size_t in_size) const {
if (in_offset + in_size > size) {
spdlog::error("读取缓冲区越界");
return {};
}
auto out_heap = std::make_shared<heap>(in_size);
get_renderer()->ReadBuffer(*buffer, in_offset, *out_heap, in_size);
return out_heap;
}
heap_ptr render_buffer::read() const {
return read(0, size);
}
void render_buffer::push(const void* data, size_t in_data_size) {
if (size + in_data_size > capacity) {
// 使用更智能的增长策略
const size_t new_capacity = std::max(static_cast<size_t>(capacity * GROWTH_FACTOR), size + in_data_size);
resize(new_capacity);
}
// 批量写入优化
if (access_flags & LLGL::CPUAccessFlags::Write) {
// 如果支持直接写入,使用映射方式
if (auto mapped_data = get_renderer()->MapBuffer(*buffer, LLGL::CPUAccess::WriteOnly)) {
std::memcpy(static_cast<uint8_t*>(mapped_data) + size, data, in_data_size);
get_renderer()->UnmapBuffer(*buffer);
}
} else {
// 否则使用WriteBuffer
get_renderer()->WriteBuffer(*buffer, size, data, in_data_size);
}
size += in_data_size;
}
void render_buffer::update_async(const void* data, size_t offset, size_t size) {
if (offset + size > capacity) {
throw std::out_of_range("Buffer update out of bounds");
}
if (offset + size > capacity) {
throw std::out_of_range("Buffer update out of bounds");
}
// 使用 RAII 方式获取命令缓冲区
const scoped_command cmd;
if (!cmd.is_valid()) {
throw std::runtime_error("Failed to acquire command buffer");
}
// 复制数据到临时缓冲区(如果需要)
std::vector temp_data(static_cast<const uint8_t*>(data), static_cast<const uint8_t*>(data) + size);
// 更新缓冲区
cmd->UpdateBuffer(*buffer, offset, temp_data.data(), size);
// 提交到命令队列scoped_command 析构时会自动 End
get_main_command_queue()->Submit(*cmd);
}
void render_buffer::batch_update_async(const std::vector<update_info>& updates) {
scoped_command cmd;
if (!cmd.is_valid()) {
throw std::runtime_error("Failed to acquire command buffer");
}
for (const auto& update : updates) {
if (update.offset + update.size > capacity) {
spdlog::warn("跳过越界更新");
continue;
}
cmd->UpdateBuffer(*buffer, update.offset, update.data, update.size);
}
get_main_command_queue()->Submit(*cmd);
}
} // namespace mirage

View File

@ -0,0 +1,110 @@
#pragma once
#include "LLGL/LLGL.h"
#include "mirage.h"
#include "render_command_pool.h"
#include <span>
namespace mirage {
struct heap {
explicit heap(size_t in_size) : size(in_size) {
data = std::make_unique<uint8_t[]>(in_size);
}
auto get_size() const {
return size;
}
auto get_size_bytes() const {
return size;
}
auto get_data() const {
return data.get();
}
operator std::span<uint8_t>() const {
return {data.get(), size};
}
operator std::span<const uint8_t>() const {
return {data.get(), size};
}
operator uint8_t*() const {
return data.get();
}
operator void*() const {
return data.get();
}
private:
std::unique_ptr<uint8_t[]> data;
size_t size;
};
using heap_ptr = std::shared_ptr<heap>;
class render_buffer {
public:
// 添加预分配大小的常量
static constexpr size_t DEFAULT_INITIAL_SIZE = 1024;
static constexpr float GROWTH_FACTOR = 2.0f;
render_buffer() : buffer(nullptr), size(0), capacity(0), access_flags(0) {}
// 禁用拷贝操作
render_buffer(const render_buffer&) = delete;
render_buffer& operator=(const render_buffer&) = delete;
~render_buffer();
void init(const LLGL::BufferDescriptor& in_desc);
// 预留空间
void reserve(size_t new_capacity);
// 收缩到适合大小
void shrink_to_fit();
void resize(size_t in_capacity_size, bool in_keep_data = true);
void clear();
[[nodiscard]] heap_ptr read(size_t in_offset, size_t in_size) const;
[[nodiscard]] heap_ptr read() const;
void push(const void* data, size_t in_data_size);
template <typename T>
void push(std::span<const T> data) {
push(data.data(), data.size_bytes());
}
template <class T>
void push(const T& data) {
push(&data, sizeof(T));
}
// 异步更新缓冲区
struct update_info {
const void* data;
size_t offset;
size_t size;
};
void update_async(const void* data, size_t offset, size_t size);
void batch_update_async(const std::vector<update_info>& updates);
[[nodiscard]] size_t get_size() const {
return size;
}
[[nodiscard]] size_t get_capacity() const {
return capacity;
}
[[nodiscard]] bool is_empty() const {
return size == 0;
}
operator LLGL::Buffer*() const {
return buffer;
}
operator bool() const {
return buffer;
}
private:
LLGL::Buffer* buffer;
size_t size;
size_t capacity;
long access_flags;
};
} // namespace mirage

View File

@ -0,0 +1,60 @@
#include "render_command_pool.h"
#include "mirage.h"
#include <spdlog/spdlog.h>
namespace mirage {
LLGL::CommandBuffer* render_command_pool::acquire() {
std::lock_guard lock(mutex);
// 检查是否有可用的命令缓冲区
for (auto& buffer : buffers) {
if (!buffer.in_use) {
buffer.in_use = true;
return buffer.cmd_buffer;
}
}
// 如果没有可用的,创建新的
return create_new_buffer();
}
void render_command_pool::release(const LLGL::CommandBuffer* in_cmd_buffer) {
std::lock_guard lock(mutex);
for (auto& buffer : buffers) {
if (buffer.cmd_buffer == in_cmd_buffer) {
buffer.in_use = false;
return;
}
}
}
void render_command_pool::cleanup() {
std::lock_guard lock(mutex);
for (auto& buffer : buffers) {
if (buffer.cmd_buffer) {
get_renderer()->Release(*buffer.cmd_buffer);
}
}
buffers.clear();
}
LLGL::CommandBuffer* render_command_pool::create_new_buffer() {
if (buffers.size() >= MAX_POOL_SIZE) {
spdlog::warn("Command pool reached maximum size");
return nullptr;
}
LLGL::CommandBufferDescriptor desc;
desc.flags = LLGL::CommandBufferFlags::ImmediateSubmit;
auto* cmd_buffer = get_renderer()->CreateCommandBuffer(desc);
if (!cmd_buffer) {
spdlog::error("Failed to create command buffer");
return nullptr;
}
buffers.push_back({cmd_buffer, true});
return cmd_buffer;
}
} // namespace mirage

View File

@ -0,0 +1,67 @@
#pragma once
#include "LLGL/LLGL.h"
#include "misc/lazy_singleton.h"
#include <mutex>
namespace mirage {
class render_command_pool {
friend class lazy_singleton_func;
public:
// 获取一个可用的命令缓冲区
LLGL::CommandBuffer* acquire();
// 释放命令缓冲区回池
void release(const LLGL::CommandBuffer* in_cmd_buffer);
// 清理所有资源
void cleanup();
private:
struct buffer_entry {
LLGL::CommandBuffer* cmd_buffer = nullptr;
bool in_use = false;
};
std::vector<buffer_entry> buffers;
std::mutex mutex;
static constexpr size_t INITIAL_POOL_SIZE = 4;
static constexpr size_t MAX_POOL_SIZE = 16;
render_command_pool() {
// 预创建一些命令缓冲区
for (size_t i = 0; i < INITIAL_POOL_SIZE; ++i) {
create_new_buffer();
}
}
~render_command_pool() {
cleanup();
}
LLGL::CommandBuffer* create_new_buffer();
};
// RAII 包装器用于自动管理命令缓冲区的获取和释放
class scoped_command {
public:
scoped_command() : cmd_buffer(lazy_singleton<render_command_pool>::get().acquire()) {
if (cmd_buffer) {
cmd_buffer->Begin();
}
}
~scoped_command() {
if (cmd_buffer) {
cmd_buffer->End();
lazy_singleton<render_command_pool>::get().release(cmd_buffer);
}
}
operator LLGL::CommandBuffer*() const { return cmd_buffer; }
LLGL::CommandBuffer* operator->() const { return cmd_buffer; }
[[nodiscard]] bool is_valid() const { return cmd_buffer != nullptr; }
private:
LLGL::CommandBuffer* cmd_buffer;
};
} // namespace mirage

View File

@ -1,17 +1,12 @@
#include "render_context.h"
#include "LLGL/Utils/VertexFormat.h"
#include "mirage.h"
namespace mirage {
render_context::~render_context() {
if (main_command_buffer) {
get_renderer()->Release(*main_command_buffer);
}
if (vertex_buffer) {
get_renderer()->Release(*vertex_buffer);
}
if (swap_chain) {
get_renderer()->Release(*swap_chain);
if (command_buffer) {
get_renderer()->Release(*command_buffer);
}
}
bool render_context::init(const swap_chain_descriptor& in_desc, const std::shared_ptr<LLGL::Surface>& in_surface) {
@ -22,30 +17,30 @@ namespace mirage {
spdlog::error("无法创建交换链");
return false;
}
main_command_buffer = get_renderer()->CreateCommandBuffer(LLGL::CommandBufferFlags::ImmediateSubmit);
if (main_command_buffer == nullptr) {
command_buffer = get_renderer()->CreateCommandBuffer(LLGL::CommandBufferFlags::ImmediateSubmit);
if (command_buffer == nullptr) {
spdlog::error("无法创建命令缓冲区");
return false;
}
set_vsync(in_desc.vsync);
// LLGL::BufferDescriptor vertex_buffer_descriptor{};
// vertex_buffer = get_renderer()->CreateBuffer(vertex_buffer_descriptor);
create_vertex_buffer();
create_index_buffer();
return true;
}
render_context::update_status render_context::update(const duration_type& in_delta_time) {
if (!main_command_buffer || !swap_chain) {
if (!command_buffer || !swap_chain) {
return update_status::fail;
}
if (!swap_chain->IsPresentable()) {
return update_status::wait_for_present;
}
main_command_buffer->Begin();
command_buffer->Begin();
{
main_command_buffer->BeginRenderPass(*swap_chain);
main_command_buffer->Clear(LLGL::ClearFlags::Color, {0.1f, 0.1f, 0.2f, 1.0f});
main_command_buffer->EndRenderPass();
command_buffer->BeginRenderPass(*swap_chain);
command_buffer->Clear(LLGL::ClearFlags::Color, {0.1f, 0.1f, 0.2f, 1.0f});
command_buffer->EndRenderPass();
}
main_command_buffer->End();
command_buffer->End();
swap_chain->Present();
return update_status::success;
}
@ -62,4 +57,29 @@ namespace mirage {
spdlog::info("垂直同步: {}", vsync);
}
}
void render_context::create_vertex_buffer() {
// 创建顶点格式布局
LLGL::VertexFormat vertexFormat;
// 添加顶点属性
vertexFormat.AppendAttribute({"position", LLGL::Format::RGB32Float}); // float3 position
vertexFormat.AppendAttribute({"texCoord0", LLGL::Format::RG32Float}); // float2 uv
vertexFormat.AppendAttribute({"color", LLGL::Format::RGBA32Float}); // float4 color
vertexFormat.AppendAttribute({"texCoord1", LLGL::Format::RGBA32Float}); // float4 param_a
vertexFormat.AppendAttribute({"texCoord2", LLGL::Format::RGBA32Float}); // float4 param_b
vertexFormat.AppendAttribute({"texCoord3", LLGL::Format::RGBA32Float}); // float4 param_c
LLGL::BufferDescriptor vertex_buffer_descriptor{};
vertex_buffer_descriptor.size = sizeof(vertex_t) * 512;
vertex_buffer_descriptor.bindFlags = LLGL::BindFlags::VertexBuffer;
vertex_buffer_descriptor.vertexAttribs = vertexFormat.attributes;
vertex_buffer.init(vertex_buffer_descriptor);
}
void render_context::create_index_buffer() {
LLGL::BufferDescriptor index_buffer_descriptor{};
index_buffer_descriptor.size = sizeof(triangle_index_t) * 512;
index_buffer_descriptor.bindFlags = LLGL::BindFlags::IndexBuffer;
index_buffer.init(index_buffer_descriptor);
}
} // namespace mirage

View File

@ -1,15 +1,13 @@
#pragma once
#include "LLGL/LLGL.h"
#include "misc/mirage_type.h"
#include "render_buffer.h"
#include <span>
namespace mirage {
class render_context {
public:
enum class update_status {
success,
wait_for_present,
fail
};
enum class update_status { success, wait_for_present, fail };
~render_context();
@ -24,10 +22,21 @@ namespace mirage {
[[nodiscard]] auto get_swap_chain() const {
return swap_chain;
}
[[nodiscard]] size_t get_vertex_count() const {
return vertex_buffer.get_size() / sizeof(vertex_t);
}
[[nodiscard]] size_t get_index_count() const {
return index_buffer.get_size() / sizeof(triangle_index_t);
}
private:
LLGL::SwapChain* swap_chain = nullptr;
LLGL::Buffer* vertex_buffer = nullptr;
LLGL::CommandBuffer* main_command_buffer = nullptr;
bool vsync = true;
void create_vertex_buffer();
void create_index_buffer();
render_buffer vertex_buffer;
render_buffer index_buffer;
LLGL::CommandBuffer* command_buffer = nullptr;
LLGL::SwapChain* swap_chain = nullptr;
bool vsync = true;
};
} // namespace mirage

View File

@ -0,0 +1 @@
#include "render_draw_command.h"

View File

@ -0,0 +1,7 @@
#pragma once
namespace mirage {
class render_draw_command {
};
}

View File

@ -1,5 +1,5 @@
struct VSInput {
float2 position : POSITION;
float3 position : POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR0;
float4 param_a : TEXCOORD1;