feat(renderer): 添加渲染性能优化和资源热重载系统
- 新增 AssetFileSystem 封装文件系统操作,支持 RomFS - 新增 AssetAsyncLoader 实现异步资源加载 - 新增 AssetDependencyTracker 跟踪资源依赖关系 - 新增 AssetHotReloader 实现纹理和着色器热重载 - 优化 CommandQueue 性能:添加缓存机制,减少冗余状态切换 - 扩展 RenderGraph::execute 支持传递总时间参数 - 改进渲染统计信息,添加性能分析数据 - 重构 AssetsModule 使用新的子系统,提高代码可维护性
This commit is contained in:
parent
4a902134dc
commit
41817c9e8a
|
|
@ -1,12 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <assets/asset_storage.h>
|
#include <assets/asset_storage.h>
|
||||||
|
#include <assets/async/asset_async_loader.h>
|
||||||
|
#include <assets/deps/asset_dependency_tracker.h>
|
||||||
|
#include <assets/hot_reload/asset_hot_reloader.h>
|
||||||
|
#include <assets/io/asset_file_system.h>
|
||||||
#include <assets/asset_loader.h>
|
#include <assets/asset_loader.h>
|
||||||
#include <assets/handle.h>
|
#include <assets/handle.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <condition_variable>
|
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
#include <filesystem>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <module/module.h>
|
#include <module/module.h>
|
||||||
|
|
@ -18,7 +20,6 @@
|
||||||
#include <renderer/texture.h>
|
#include <renderer/texture.h>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
@ -239,38 +240,15 @@ public:
|
||||||
void setHotReloadInterval(float interval);
|
void setHotReloadInterval(float interval);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
|
||||||
* @brief 资源文件监控信息
|
|
||||||
*/
|
|
||||||
struct FileWatchInfo {
|
|
||||||
std::string path;
|
|
||||||
std::filesystem::file_time_type lastWriteTime;
|
|
||||||
Handle<Texture> textureHandle;
|
|
||||||
Handle<Shader> shaderHandle;
|
|
||||||
bool isTexture = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 添加文件监控
|
|
||||||
*/
|
|
||||||
void addFileWatch(const std::string &path, Handle<Texture> handle);
|
|
||||||
void addFileWatch(const std::string &path, Handle<Shader> handle);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief 内部实现:添加文件监控(减少重复代码)
|
|
||||||
*/
|
|
||||||
void addFileWatchInternal(const std::string &path, FileWatchInfo &&info);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 重新加载纹理
|
* @brief 重新加载纹理
|
||||||
*/
|
*/
|
||||||
void reloadTexture(const FileWatchInfo &info);
|
void reloadTexture(const AssetHotReloader::FileWatchInfo &info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 重新加载着色器
|
* @brief 重新加载着色器
|
||||||
*/
|
*/
|
||||||
void reloadShader(const FileWatchInfo &info);
|
void reloadShader(const AssetHotReloader::FileWatchInfo &info);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
@ -307,11 +285,8 @@ private:
|
||||||
Handle<Material> defaultMaterial_;
|
Handle<Material> defaultMaterial_;
|
||||||
Handle<Mesh> defaultQuad_;
|
Handle<Mesh> defaultQuad_;
|
||||||
|
|
||||||
// 热重载
|
AssetFileSystem fileSystem_;
|
||||||
bool hotReloadEnabled_ = false;
|
AssetHotReloader hotReloader_;
|
||||||
float hotReloadInterval_ = 1.0f;
|
|
||||||
float hotReloadTimer_ = 0.0f;
|
|
||||||
std::vector<FileWatchInfo> fileWatchList_;
|
|
||||||
|
|
||||||
// 线程安全
|
// 线程安全
|
||||||
mutable std::shared_mutex mutex_;
|
mutable std::shared_mutex mutex_;
|
||||||
|
|
@ -326,9 +301,6 @@ private:
|
||||||
// 异步加载系统
|
// 异步加载系统
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* @brief 异步加载任务
|
|
||||||
*/
|
|
||||||
struct LoadTask {
|
struct LoadTask {
|
||||||
enum class Type { Texture, Shader };
|
enum class Type { Texture, Shader };
|
||||||
enum class Priority { Low = 0, Normal = 1, High = 2 };
|
enum class Priority { Low = 0, Normal = 1, High = 2 };
|
||||||
|
|
@ -363,18 +335,7 @@ public:
|
||||||
void processAsyncCallbacks();
|
void processAsyncCallbacks();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 异步加载队列
|
AssetAsyncLoader asyncLoader_;
|
||||||
std::vector<LoadTask> loadQueue_;
|
|
||||||
std::vector<std::thread> workerThreads_;
|
|
||||||
std::mutex queueMutex_;
|
|
||||||
std::condition_variable queueCV_;
|
|
||||||
std::atomic<bool> asyncLoaderRunning_{false};
|
|
||||||
|
|
||||||
// 完成的回调队列(主线程处理)
|
|
||||||
std::vector<std::function<void()>> completedCallbacks_;
|
|
||||||
std::mutex callbackMutex_;
|
|
||||||
|
|
||||||
void workerThreadLoop();
|
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 资源依赖跟踪
|
// 资源依赖跟踪
|
||||||
|
|
@ -412,10 +373,7 @@ public:
|
||||||
void notifyShaderReloaded(Handle<Shader> shader);
|
void notifyShaderReloaded(Handle<Shader> shader);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 资源依赖映射
|
AssetDependencyTracker dependencyTracker_;
|
||||||
std::unordered_map<uint32_t, DependencyInfo> textureDependencies_;
|
|
||||||
std::unordered_map<uint32_t, DependencyInfo> shaderDependencies_;
|
|
||||||
std::shared_mutex dependencyMutex_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 全局访问
|
// 全局访问
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
class AssetAsyncLoader {
|
||||||
|
public:
|
||||||
|
enum class Priority { Low = 0, Normal = 1, High = 2 };
|
||||||
|
|
||||||
|
struct Task {
|
||||||
|
Priority priority = Priority::Normal;
|
||||||
|
std::function<void()> work;
|
||||||
|
std::function<void()> onComplete;
|
||||||
|
};
|
||||||
|
|
||||||
|
~AssetAsyncLoader();
|
||||||
|
|
||||||
|
void init(uint32_t threadCount = 0);
|
||||||
|
void shutdown();
|
||||||
|
bool isRunning() const;
|
||||||
|
void submit(const Task &task);
|
||||||
|
void processCallbacks();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void workerThreadLoop();
|
||||||
|
|
||||||
|
std::vector<Task> queue_;
|
||||||
|
std::vector<std::thread> workers_;
|
||||||
|
std::mutex queueMutex_;
|
||||||
|
std::condition_variable queueCV_;
|
||||||
|
std::atomic<bool> running_{false};
|
||||||
|
|
||||||
|
std::vector<std::function<void()>> completedCallbacks_;
|
||||||
|
std::mutex callbackMutex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assets/handle.h>
|
||||||
|
#include <renderer/material.h>
|
||||||
|
#include <renderer/shader.h>
|
||||||
|
#include <renderer/texture.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
class AssetDependencyTracker {
|
||||||
|
public:
|
||||||
|
struct DependencyInfo {
|
||||||
|
Handle<Texture> texture;
|
||||||
|
Handle<Shader> shader;
|
||||||
|
std::vector<Handle<Material>> dependentMaterials;
|
||||||
|
};
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
void registerMaterialDependency(Handle<Material> material,
|
||||||
|
Handle<Texture> texture);
|
||||||
|
void registerMaterialDependency(Handle<Material> material,
|
||||||
|
Handle<Shader> shader);
|
||||||
|
void notifyTextureReloaded(
|
||||||
|
Handle<Texture> texture,
|
||||||
|
const std::function<void(Handle<Material>)> &onMaterialUpdate) const;
|
||||||
|
void notifyShaderReloaded(
|
||||||
|
Handle<Shader> shader,
|
||||||
|
const std::function<void(Handle<Material>)> &onMaterialUpdate) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<uint32_t, DependencyInfo> textureDependencies_;
|
||||||
|
std::unordered_map<uint32_t, DependencyInfo> shaderDependencies_;
|
||||||
|
mutable std::shared_mutex mutex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assets/handle.h>
|
||||||
|
#include <assets/io/asset_file_system.h>
|
||||||
|
#include <renderer/shader.h>
|
||||||
|
#include <renderer/texture.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
class AssetHotReloader {
|
||||||
|
public:
|
||||||
|
struct FileWatchInfo {
|
||||||
|
std::string path;
|
||||||
|
std::filesystem::file_time_type lastWriteTime;
|
||||||
|
Handle<Texture> textureHandle;
|
||||||
|
Handle<Shader> shaderHandle;
|
||||||
|
bool isTexture = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit AssetHotReloader(const AssetFileSystem &fileSystem);
|
||||||
|
|
||||||
|
void enable(bool enable);
|
||||||
|
bool enabled() const;
|
||||||
|
void setInterval(float interval);
|
||||||
|
void addFileWatch(const std::string &path, Handle<Texture> handle);
|
||||||
|
void addFileWatch(const std::string &path, Handle<Shader> handle);
|
||||||
|
void clear();
|
||||||
|
void checkForChanges(
|
||||||
|
const std::function<void(const FileWatchInfo &)> &reloadTexture,
|
||||||
|
const std::function<void(const FileWatchInfo &)> &reloadShader);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addFileWatchInternal(const std::string &path, FileWatchInfo &&info);
|
||||||
|
|
||||||
|
const AssetFileSystem &fileSystem_;
|
||||||
|
bool enabled_ = false;
|
||||||
|
float interval_ = 1.0f;
|
||||||
|
std::vector<FileWatchInfo> watchList_;
|
||||||
|
mutable std::shared_mutex mutex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
class AssetFileSystem {
|
||||||
|
public:
|
||||||
|
std::string assetPath(const std::string &path) const;
|
||||||
|
bool isRomfsPath(const std::string &path) const;
|
||||||
|
bool exists(const std::string &path) const;
|
||||||
|
std::string readString(const std::string &path) const;
|
||||||
|
std::filesystem::file_time_type lastWriteTime(const std::string &path) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <assets/asset_loader.h>
|
#include <assets/asset_loader.h>
|
||||||
|
#include <assets/io/asset_file_system.h>
|
||||||
#include <renderer/shader.h>
|
#include <renderer/shader.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -12,6 +13,8 @@ namespace extra2d {
|
||||||
*/
|
*/
|
||||||
class ShaderLoader : public AssetLoader<Shader> {
|
class ShaderLoader : public AssetLoader<Shader> {
|
||||||
public:
|
public:
|
||||||
|
ShaderLoader() = default;
|
||||||
|
explicit ShaderLoader(AssetFileSystem fileSystem);
|
||||||
/**
|
/**
|
||||||
* @brief 从文件加载着色器
|
* @brief 从文件加载着色器
|
||||||
* @param path 单个文件路径(自动推断 .vert/.frag)
|
* @param path 单个文件路径(自动推断 .vert/.frag)
|
||||||
|
|
@ -50,6 +53,9 @@ public:
|
||||||
std::vector<std::string> getExtensions() const override {
|
std::vector<std::string> getExtensions() const override {
|
||||||
return {".glsl", ".vert", ".frag"};
|
return {".glsl", ".vert", ".frag"};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AssetFileSystem fileSystem_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@ private:
|
||||||
std::vector<DrawCommand> commands_; // 命令缓冲区(复用)
|
std::vector<DrawCommand> commands_; // 命令缓冲区(复用)
|
||||||
std::vector<uint32_t> sortedIndices_; // 排序索引缓冲区(复用)
|
std::vector<uint32_t> sortedIndices_; // 排序索引缓冲区(复用)
|
||||||
uint32_t commandCount_ = 0; // 当前命令数量
|
uint32_t commandCount_ = 0; // 当前命令数量
|
||||||
|
bool needsSort_ = false;
|
||||||
static constexpr uint32_t INITIAL_CAPACITY = 1024; // 初始容量
|
static constexpr uint32_t INITIAL_CAPACITY = 1024; // 初始容量
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -239,6 +240,14 @@ private:
|
||||||
*/
|
*/
|
||||||
class CommandQueue {
|
class CommandQueue {
|
||||||
public:
|
public:
|
||||||
|
struct ProfilingStats {
|
||||||
|
uint32_t commandCount = 0;
|
||||||
|
uint32_t batchCount = 0;
|
||||||
|
float sortMs = 0.0f;
|
||||||
|
float batchMs = 0.0f;
|
||||||
|
float executeMs = 0.0f;
|
||||||
|
float totalMs = 0.0f;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* @brief 默认构造函数
|
* @brief 默认构造函数
|
||||||
*/
|
*/
|
||||||
|
|
@ -286,7 +295,8 @@ public:
|
||||||
* @param color 颜色
|
* @param color 颜色
|
||||||
*/
|
*/
|
||||||
void submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
|
void submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
|
||||||
const struct Transform &transform, const Color &color);
|
const struct Transform &transform, const Color &color,
|
||||||
|
uint32_t sortKey = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 提交清除命令
|
* @brief 提交清除命令
|
||||||
|
|
@ -321,6 +331,7 @@ public:
|
||||||
* @param frameIndex 当前帧索引(用于双缓冲)
|
* @param frameIndex 当前帧索引(用于双缓冲)
|
||||||
*/
|
*/
|
||||||
void updateGlobalUBO(const Mat4& viewProjection, float deltaTime,
|
void updateGlobalUBO(const Mat4& viewProjection, float deltaTime,
|
||||||
|
float totalTime,
|
||||||
uint32_t screenWidth, uint32_t screenHeight, uint32_t frameIndex);
|
uint32_t screenWidth, uint32_t screenHeight, uint32_t frameIndex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -340,6 +351,7 @@ public:
|
||||||
* @return 渲染统计
|
* @return 渲染统计
|
||||||
*/
|
*/
|
||||||
const RenderStats& getStats() const { return stats_; }
|
const RenderStats& getStats() const { return stats_; }
|
||||||
|
const ProfilingStats &getProfilingStats() const { return profilingStats_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 重置统计信息
|
* @brief 重置统计信息
|
||||||
|
|
@ -357,6 +369,7 @@ private:
|
||||||
|
|
||||||
// 渲染统计
|
// 渲染统计
|
||||||
RenderStats stats_;
|
RenderStats stats_;
|
||||||
|
ProfilingStats profilingStats_;
|
||||||
|
|
||||||
// 全局 UBO 数据 - 必须与着色器中的 std140 布局完全匹配
|
// 全局 UBO 数据 - 必须与着色器中的 std140 布局完全匹配
|
||||||
// layout(std140, binding = 0) uniform GlobalUBO {
|
// layout(std140, binding = 0) uniform GlobalUBO {
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,8 @@ public:
|
||||||
* @param deltaTime 帧时间
|
* @param deltaTime 帧时间
|
||||||
* @param viewProjection 视图投影矩阵
|
* @param viewProjection 视图投影矩阵
|
||||||
*/
|
*/
|
||||||
void execute(float deltaTime, const Mat4& viewProjection = Mat4(1.0f));
|
void execute(float deltaTime, const Mat4& viewProjection = Mat4(1.0f),
|
||||||
|
float totalTime = 0.0f);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取纹理资源
|
* @brief 获取纹理资源
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ private:
|
||||||
* @param viewProj 视图投影矩阵
|
* @param viewProj 视图投影矩阵
|
||||||
*/
|
*/
|
||||||
void onRenderSetCamera(const Mat4 &viewProj);
|
void onRenderSetCamera(const Mat4 &viewProj);
|
||||||
|
void onUpdate(float dt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 渲染结束事件处理
|
* @brief 渲染结束事件处理
|
||||||
|
|
@ -199,6 +200,7 @@ private:
|
||||||
events::OnRenderSubmit::Listener onRenderSubmitListener_;
|
events::OnRenderSubmit::Listener onRenderSubmitListener_;
|
||||||
events::OnRenderSetCamera::Listener onRenderSetCameraListener_;
|
events::OnRenderSetCamera::Listener onRenderSetCameraListener_;
|
||||||
events::OnRenderEnd::Listener onRenderEndListener_;
|
events::OnRenderEnd::Listener onRenderEndListener_;
|
||||||
|
events::OnUpdate::Listener onUpdateListener_;
|
||||||
events::OnResize::Listener onResizeListener_;
|
events::OnResize::Listener onResizeListener_;
|
||||||
events::OnShow::Listener onShowListener_;
|
events::OnShow::Listener onShowListener_;
|
||||||
|
|
||||||
|
|
@ -216,6 +218,10 @@ private:
|
||||||
uint32 commandsSubmitted = 0; // 提交的命令数
|
uint32 commandsSubmitted = 0; // 提交的命令数
|
||||||
uint32 drawCalls = 0; // 绘制调用次数
|
uint32 drawCalls = 0; // 绘制调用次数
|
||||||
uint32 batches = 0; // 批次数
|
uint32 batches = 0; // 批次数
|
||||||
|
float queueSortMs = 0.0f;
|
||||||
|
float queueBatchMs = 0.0f;
|
||||||
|
float queueExecuteMs = 0.0f;
|
||||||
|
float queueTotalMs = 0.0f;
|
||||||
} stats_;
|
} stats_;
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
@ -236,6 +242,10 @@ private:
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
Mat4 viewProjectionMatrix_; // 当前视图投影矩阵
|
Mat4 viewProjectionMatrix_; // 当前视图投影矩阵
|
||||||
|
float frameDeltaTime_ = 1.0f / 60.0f;
|
||||||
|
float renderTotalTime_ = 0.0f;
|
||||||
|
Material *defaultMaterialPtr_ = nullptr;
|
||||||
|
Mesh *defaultMeshPtr_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
#include <renderer/rhi/rhi.h>
|
#include <renderer/rhi/rhi.h>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -89,6 +91,7 @@ public:
|
||||||
void setUniform(const char* name, const Mat4& value) override;
|
void setUniform(const char* name, const Mat4& value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
GLint getUniformLocation(GLuint program, const char *name);
|
||||||
// 状态缓存机制 - 避免冗余的 OpenGL 状态切换
|
// 状态缓存机制 - 避免冗余的 OpenGL 状态切换
|
||||||
struct StateCache {
|
struct StateCache {
|
||||||
// 管线状态
|
// 管线状态
|
||||||
|
|
@ -150,6 +153,8 @@ private:
|
||||||
|
|
||||||
bool recording_ = false;
|
bool recording_ = false;
|
||||||
StateCache stateCache_;
|
StateCache stateCache_;
|
||||||
|
std::unordered_map<GLuint, std::unordered_map<std::string, GLint>>
|
||||||
|
uniformLocationCache_;
|
||||||
|
|
||||||
// 统计信息(调试用)
|
// 统计信息(调试用)
|
||||||
struct Stats {
|
struct Stats {
|
||||||
|
|
|
||||||
|
|
@ -4,69 +4,14 @@
|
||||||
#include <assets/loaders/texture_loader.h>
|
#include <assets/loaders/texture_loader.h>
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <module/module_registry.h>
|
|
||||||
#include <platform/file_module.h>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取 FileModule 实例
|
|
||||||
*/
|
|
||||||
FileModule *getFileModule() { return getModule<FileModule>(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查路径是否为 RomFS 路径
|
|
||||||
*/
|
|
||||||
bool isRomfsPath(const std::string &path) {
|
|
||||||
FileModule *fileModule = getFileModule();
|
|
||||||
if (fileModule) {
|
|
||||||
return fileModule->isRomfsPath(path);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查文件是否存在(使用 FileModule)
|
|
||||||
*/
|
|
||||||
bool fileExists(const std::string &path) {
|
|
||||||
FileModule *fileModule = getFileModule();
|
|
||||||
if (fileModule) {
|
|
||||||
return fileModule->exists(path);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 读取文件内容为字符串(使用 FileModule,支持 RomFS)
|
|
||||||
*/
|
|
||||||
std::string readFileToString(const std::string &path) {
|
|
||||||
FileModule *fileModule = getFileModule();
|
|
||||||
if (fileModule) {
|
|
||||||
return fileModule->readString(path);
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取文件最后写入时间(支持 RomFS 路径)
|
|
||||||
*/
|
|
||||||
std::filesystem::file_time_type getLastWriteTime(const std::string &path) {
|
|
||||||
if (isRomfsPath(path)) {
|
|
||||||
// RomFS 文件是只读的,返回一个固定时间
|
|
||||||
return std::filesystem::file_time_type::min();
|
|
||||||
}
|
|
||||||
return std::filesystem::last_write_time(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
AssetsModule *g_assetsModule = nullptr;
|
AssetsModule *g_assetsModule = nullptr;
|
||||||
|
|
||||||
AssetsModule::AssetsModule() { g_assetsModule = this; }
|
AssetsModule::AssetsModule() : hotReloader_(fileSystem_) { g_assetsModule = this; }
|
||||||
|
|
||||||
AssetsModule::~AssetsModule() {
|
AssetsModule::~AssetsModule() {
|
||||||
if (g_assetsModule == this) {
|
if (g_assetsModule == this) {
|
||||||
|
|
@ -78,7 +23,7 @@ bool AssetsModule::init() {
|
||||||
E2D_INFO("资源模块正在初始化...");
|
E2D_INFO("资源模块正在初始化...");
|
||||||
|
|
||||||
textureLoader_ = std::make_unique<TextureLoader>();
|
textureLoader_ = std::make_unique<TextureLoader>();
|
||||||
shaderLoader_ = std::make_unique<ShaderLoader>();
|
shaderLoader_ = std::make_unique<ShaderLoader>(fileSystem_);
|
||||||
|
|
||||||
// 监听窗口显示事件,在 OpenGL 上下文创建完成后创建默认资源
|
// 监听窗口显示事件,在 OpenGL 上下文创建完成后创建默认资源
|
||||||
onShowListener_ = std::make_unique<events::OnShow::Listener>();
|
onShowListener_ = std::make_unique<events::OnShow::Listener>();
|
||||||
|
|
@ -125,15 +70,10 @@ void AssetsModule::shutdown() {
|
||||||
|
|
||||||
texturePathCache_.clear();
|
texturePathCache_.clear();
|
||||||
shaderPathCache_.clear();
|
shaderPathCache_.clear();
|
||||||
fileWatchList_.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空依赖关系
|
hotReloader_.clear();
|
||||||
{
|
dependencyTracker_.clear();
|
||||||
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
|
|
||||||
textureDependencies_.clear();
|
|
||||||
shaderDependencies_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
textureLoader_.reset();
|
textureLoader_.reset();
|
||||||
shaderLoader_.reset();
|
shaderLoader_.reset();
|
||||||
|
|
@ -191,8 +131,8 @@ Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
|
||||||
E2D_DEBUG("已加载纹理: {} -> 句柄索引 {}", path, handle.index());
|
E2D_DEBUG("已加载纹理: {} -> 句柄索引 {}", path, handle.index());
|
||||||
|
|
||||||
// 如果启用了热重载,添加文件监控
|
// 如果启用了热重载,添加文件监控
|
||||||
if (hotReloadEnabled_) {
|
if (hotReloader_.enabled()) {
|
||||||
addFileWatch(path, handle);
|
hotReloader_.addFileWatch(path, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
|
|
@ -272,8 +212,8 @@ template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
|
||||||
E2D_DEBUG("已加载着色器: {} -> 句柄索引 {}", path, handle.index());
|
E2D_DEBUG("已加载着色器: {} -> 句柄索引 {}", path, handle.index());
|
||||||
|
|
||||||
// 如果启用了热重载,添加文件监控
|
// 如果启用了热重载,添加文件监控
|
||||||
if (hotReloadEnabled_) {
|
if (hotReloader_.enabled()) {
|
||||||
addFileWatch(path, handle);
|
hotReloader_.addFileWatch(path, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
|
|
@ -326,9 +266,9 @@ Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
||||||
handle.index());
|
handle.index());
|
||||||
|
|
||||||
// 如果启用了热重载,添加文件监控
|
// 如果启用了热重载,添加文件监控
|
||||||
if (hotReloadEnabled_) {
|
if (hotReloader_.enabled()) {
|
||||||
addFileWatch(vertPath, handle);
|
hotReloader_.addFileWatch(vertPath, handle);
|
||||||
addFileWatch(fragPath, handle);
|
hotReloader_.addFileWatch(fragPath, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
|
|
@ -346,7 +286,7 @@ AssetsModule::loadDir<Texture>(const std::string &directory,
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::filesystem::path dirPath(directory);
|
std::filesystem::path dirPath(directory);
|
||||||
if (!fileExists(directory)) {
|
if (!fileSystem_.exists(directory)) {
|
||||||
E2D_WARN("目录未找到: {}", directory);
|
E2D_WARN("目录未找到: {}", directory);
|
||||||
return handles;
|
return handles;
|
||||||
}
|
}
|
||||||
|
|
@ -396,7 +336,7 @@ template <>
|
||||||
void AssetsModule::loadAsync<Texture>(
|
void AssetsModule::loadAsync<Texture>(
|
||||||
const std::string &path, std::function<void(Handle<Texture>)> callback) {
|
const std::string &path, std::function<void(Handle<Texture>)> callback) {
|
||||||
// 确保异步加载系统已初始化
|
// 确保异步加载系统已初始化
|
||||||
if (!asyncLoaderRunning_) {
|
if (!asyncLoader_.isRunning()) {
|
||||||
initAsyncLoader();
|
initAsyncLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -414,7 +354,7 @@ template <>
|
||||||
void AssetsModule::loadAsync<Shader>(
|
void AssetsModule::loadAsync<Shader>(
|
||||||
const std::string &path, std::function<void(Handle<Shader>)> callback) {
|
const std::string &path, std::function<void(Handle<Shader>)> callback) {
|
||||||
// 确保异步加载系统已初始化
|
// 确保异步加载系统已初始化
|
||||||
if (!asyncLoaderRunning_) {
|
if (!asyncLoader_.isRunning()) {
|
||||||
initAsyncLoader();
|
initAsyncLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -464,22 +404,17 @@ bool AssetsModule::createDefaultResources() {
|
||||||
{
|
{
|
||||||
// 从文件加载默认着色器
|
// 从文件加载默认着色器
|
||||||
// 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS)
|
// 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS)
|
||||||
FileModule *fileModule = getFileModule();
|
std::string vertPath = fileSystem_.assetPath("shader/default.vert");
|
||||||
std::string vertPath = fileModule
|
std::string fragPath = fileSystem_.assetPath("shader/default.frag");
|
||||||
? fileModule->assetPath("shader/default.vert")
|
|
||||||
: "shader/default.vert";
|
|
||||||
std::string fragPath = fileModule
|
|
||||||
? fileModule->assetPath("shader/default.frag")
|
|
||||||
: "shader/default.frag";
|
|
||||||
|
|
||||||
if (!fileExists(vertPath) || !fileExists(fragPath)) {
|
if (!fileSystem_.exists(vertPath) || !fileSystem_.exists(fragPath)) {
|
||||||
E2D_ERROR("默认着色器文件未找到: {}, {}", vertPath, fragPath);
|
E2D_ERROR("默认着色器文件未找到: {}, {}", vertPath, fragPath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取着色器文件内容
|
// 读取着色器文件内容
|
||||||
std::string vsSource = readFileToString(vertPath);
|
std::string vsSource = fileSystem_.readString(vertPath);
|
||||||
std::string fsSource = readFileToString(fragPath);
|
std::string fsSource = fileSystem_.readString(fragPath);
|
||||||
if (vsSource.empty() || fsSource.empty()) {
|
if (vsSource.empty() || fsSource.empty()) {
|
||||||
E2D_ERROR("读取默认着色器文件失败: {}, {}", vertPath, fragPath);
|
E2D_ERROR("读取默认着色器文件失败: {}, {}", vertPath, fragPath);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -577,7 +512,7 @@ Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(defaultQuad_); }
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
void AssetsModule::enableHotReload(bool enable) {
|
void AssetsModule::enableHotReload(bool enable) {
|
||||||
hotReloadEnabled_ = enable;
|
hotReloader_.enable(enable);
|
||||||
if (enable) {
|
if (enable) {
|
||||||
E2D_INFO("热重载已启用");
|
E2D_INFO("热重载已启用");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -586,45 +521,10 @@ void AssetsModule::enableHotReload(bool enable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::setHotReloadInterval(float interval) {
|
void AssetsModule::setHotReloadInterval(float interval) {
|
||||||
hotReloadInterval_ = interval;
|
hotReloader_.setInterval(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::addFileWatchInternal(const std::string &path,
|
void AssetsModule::reloadTexture(const AssetHotReloader::FileWatchInfo &info) {
|
||||||
FileWatchInfo &&info) {
|
|
||||||
try {
|
|
||||||
if (!fileExists(path)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
info.path = path;
|
|
||||||
info.lastWriteTime = getLastWriteTime(path);
|
|
||||||
|
|
||||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
|
||||||
fileWatchList_.push_back(std::move(info));
|
|
||||||
const char *type = fileWatchList_.back().isTexture ? "纹理" : "着色器";
|
|
||||||
E2D_DEBUG("正在监控 {} 文件: {}", type, path);
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
E2D_ERROR("添加文件监控失败 {}: {}", path, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetsModule::addFileWatch(const std::string &path,
|
|
||||||
Handle<Texture> handle) {
|
|
||||||
FileWatchInfo info;
|
|
||||||
info.textureHandle = handle;
|
|
||||||
info.isTexture = true;
|
|
||||||
addFileWatchInternal(path, std::move(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetsModule::addFileWatch(const std::string &path,
|
|
||||||
Handle<Shader> handle) {
|
|
||||||
FileWatchInfo info;
|
|
||||||
info.shaderHandle = handle;
|
|
||||||
info.isTexture = false;
|
|
||||||
addFileWatchInternal(path, std::move(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetsModule::reloadTexture(const FileWatchInfo &info) {
|
|
||||||
if (!textureLoader_ || !info.textureHandle.isValid()) {
|
if (!textureLoader_ || !info.textureHandle.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -648,7 +548,7 @@ void AssetsModule::reloadTexture(const FileWatchInfo &info) {
|
||||||
notifyTextureReloaded(info.textureHandle);
|
notifyTextureReloaded(info.textureHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::reloadShader(const FileWatchInfo &info) {
|
void AssetsModule::reloadShader(const AssetHotReloader::FileWatchInfo &info) {
|
||||||
if (!shaderLoader_ || !info.shaderHandle.isValid()) {
|
if (!shaderLoader_ || !info.shaderHandle.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -690,8 +590,8 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
|
||||||
std::string fragPath = cacheKey.substr(sepPos + 1);
|
std::string fragPath = cacheKey.substr(sepPos + 1);
|
||||||
|
|
||||||
// 读取着色器文件内容
|
// 读取着色器文件内容
|
||||||
std::string vsSource = readFileToString(vertPath);
|
std::string vsSource = fileSystem_.readString(vertPath);
|
||||||
std::string fsSource = readFileToString(fragPath);
|
std::string fsSource = fileSystem_.readString(fragPath);
|
||||||
if (vsSource.empty() || fsSource.empty()) {
|
if (vsSource.empty() || fsSource.empty()) {
|
||||||
E2D_ERROR("读取着色器文件失败: {}, {}", vertPath, fragPath);
|
E2D_ERROR("读取着色器文件失败: {}, {}", vertPath, fragPath);
|
||||||
return;
|
return;
|
||||||
|
|
@ -709,42 +609,13 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::checkForChanges() {
|
void AssetsModule::checkForChanges() {
|
||||||
if (!hotReloadEnabled_ || fileWatchList_.empty()) {
|
hotReloader_.checkForChanges(
|
||||||
return;
|
[this](const AssetHotReloader::FileWatchInfo &info) {
|
||||||
}
|
reloadTexture(info);
|
||||||
|
},
|
||||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
[this](const AssetHotReloader::FileWatchInfo &info) {
|
||||||
|
reloadShader(info);
|
||||||
for (auto &info : fileWatchList_) {
|
});
|
||||||
try {
|
|
||||||
// 跳过 RomFS 路径(只读文件系统)
|
|
||||||
if (isRomfsPath(info.path)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fileExists(info.path)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto currentTime = getLastWriteTime(info.path);
|
|
||||||
if (currentTime != info.lastWriteTime) {
|
|
||||||
info.lastWriteTime = currentTime;
|
|
||||||
|
|
||||||
// 解锁进行重载操作
|
|
||||||
lock.unlock();
|
|
||||||
|
|
||||||
if (info.isTexture) {
|
|
||||||
reloadTexture(info);
|
|
||||||
} else {
|
|
||||||
reloadShader(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock.lock();
|
|
||||||
}
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
E2D_ERROR("检查文件 {} 时出错: {}", info.path, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
@ -752,119 +623,57 @@ void AssetsModule::checkForChanges() {
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
void AssetsModule::initAsyncLoader(uint32_t threadCount) {
|
void AssetsModule::initAsyncLoader(uint32_t threadCount) {
|
||||||
if (asyncLoaderRunning_) {
|
if (asyncLoader_.isRunning()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
asyncLoader_.init(threadCount);
|
||||||
if (threadCount == 0) {
|
|
||||||
threadCount = std::max(1u, std::thread::hardware_concurrency() / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
asyncLoaderRunning_ = true;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < threadCount; ++i) {
|
|
||||||
workerThreads_.emplace_back(&AssetsModule::workerThreadLoop, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
E2D_INFO("异步加载器已初始化,使用 {} 个线程", threadCount);
|
E2D_INFO("异步加载器已初始化,使用 {} 个线程", threadCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::shutdownAsyncLoader() {
|
void AssetsModule::shutdownAsyncLoader() {
|
||||||
if (!asyncLoaderRunning_) {
|
if (!asyncLoader_.isRunning()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
asyncLoader_.shutdown();
|
||||||
asyncLoaderRunning_ = false;
|
|
||||||
queueCV_.notify_all();
|
|
||||||
|
|
||||||
for (auto &thread : workerThreads_) {
|
|
||||||
if (thread.joinable()) {
|
|
||||||
thread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
workerThreads_.clear();
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(queueMutex_);
|
|
||||||
loadQueue_.clear();
|
|
||||||
|
|
||||||
E2D_INFO("异步加载器已关闭");
|
E2D_INFO("异步加载器已关闭");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::submitLoadTask(const LoadTask &task) {
|
void AssetsModule::submitLoadTask(const LoadTask &task) {
|
||||||
{
|
AssetAsyncLoader::Task asyncTask;
|
||||||
std::lock_guard<std::mutex> lock(queueMutex_);
|
asyncTask.priority = static_cast<AssetAsyncLoader::Priority>(task.priority);
|
||||||
loadQueue_.push_back(task);
|
|
||||||
|
|
||||||
// 按优先级排序
|
if (task.type == LoadTask::Type::Texture) {
|
||||||
std::sort(loadQueue_.begin(), loadQueue_.end(),
|
auto handle = std::make_shared<Handle<Texture>>(Handle<Texture>::invalid());
|
||||||
[](const LoadTask &a, const LoadTask &b) {
|
asyncTask.work = [this, path = task.path, handle]() {
|
||||||
return static_cast<int>(a.priority) >
|
*handle = load<Texture>(path);
|
||||||
static_cast<int>(b.priority);
|
};
|
||||||
});
|
if (task.textureCallback) {
|
||||||
}
|
asyncTask.onComplete = [handle, callback = task.textureCallback]() {
|
||||||
queueCV_.notify_one();
|
callback(*handle);
|
||||||
}
|
};
|
||||||
|
|
||||||
void AssetsModule::workerThreadLoop() {
|
|
||||||
while (asyncLoaderRunning_) {
|
|
||||||
LoadTask task;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queueMutex_);
|
|
||||||
queueCV_.wait(
|
|
||||||
lock, [this] { return !loadQueue_.empty() || !asyncLoaderRunning_; });
|
|
||||||
|
|
||||||
if (!asyncLoaderRunning_) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loadQueue_.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
task = std::move(loadQueue_.back());
|
|
||||||
loadQueue_.pop_back();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// 执行加载任务
|
auto handle = std::make_shared<Handle<Shader>>(Handle<Shader>::invalid());
|
||||||
if (task.type == LoadTask::Type::Texture) {
|
asyncTask.work = [this, path = task.path, secondary = task.secondaryPath,
|
||||||
Handle<Texture> handle = load<Texture>(task.path);
|
handle]() {
|
||||||
|
if (secondary.empty()) {
|
||||||
if (task.textureCallback) {
|
*handle = load<Shader>(path);
|
||||||
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
|
|
||||||
completedCallbacks_.push_back(
|
|
||||||
[handle, callback = task.textureCallback]() { callback(handle); });
|
|
||||||
}
|
|
||||||
} else if (task.type == LoadTask::Type::Shader) {
|
|
||||||
Handle<Shader> handle;
|
|
||||||
if (task.secondaryPath.empty()) {
|
|
||||||
handle = load<Shader>(task.path);
|
|
||||||
} else {
|
} else {
|
||||||
handle = load<Shader>(task.path, task.secondaryPath);
|
*handle = load<Shader>(path, secondary);
|
||||||
}
|
|
||||||
|
|
||||||
if (task.shaderCallback) {
|
|
||||||
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
|
|
||||||
completedCallbacks_.push_back(
|
|
||||||
[handle, callback = task.shaderCallback]() { callback(handle); });
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
if (task.shaderCallback) {
|
||||||
|
asyncTask.onComplete = [handle, callback = task.shaderCallback]() {
|
||||||
|
callback(*handle);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asyncLoader_.submit(asyncTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::processAsyncCallbacks() {
|
void AssetsModule::processAsyncCallbacks() {
|
||||||
std::vector<std::function<void()>> callbacks;
|
asyncLoader_.processCallbacks();
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(callbackMutex_);
|
|
||||||
callbacks = std::move(completedCallbacks_);
|
|
||||||
completedCallbacks_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &callback : callbacks) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
@ -873,93 +682,36 @@ void AssetsModule::processAsyncCallbacks() {
|
||||||
|
|
||||||
void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
||||||
Handle<Texture> texture) {
|
Handle<Texture> texture) {
|
||||||
if (!material.isValid() || !texture.isValid()) {
|
dependencyTracker_.registerMaterialDependency(material, texture);
|
||||||
return;
|
E2D_DEBUG("已注册材质 {} 对纹理 {} 的依赖", material.index(), texture.index());
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
|
|
||||||
|
|
||||||
uint32_t textureIndex = texture.index();
|
|
||||||
auto &info = textureDependencies_[textureIndex];
|
|
||||||
info.texture = texture;
|
|
||||||
|
|
||||||
// 检查是否已存在
|
|
||||||
auto it = std::find(info.dependentMaterials.begin(),
|
|
||||||
info.dependentMaterials.end(), material);
|
|
||||||
if (it == info.dependentMaterials.end()) {
|
|
||||||
info.dependentMaterials.push_back(material);
|
|
||||||
E2D_DEBUG("已注册材质 {} 对纹理 {} 的依赖", material.index(), textureIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
||||||
Handle<Shader> shader) {
|
Handle<Shader> shader) {
|
||||||
if (!material.isValid() || !shader.isValid()) {
|
dependencyTracker_.registerMaterialDependency(material, shader);
|
||||||
return;
|
E2D_DEBUG("已注册材质 {} 对着色器 {} 的依赖", material.index(),
|
||||||
}
|
shader.index());
|
||||||
|
|
||||||
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
|
|
||||||
|
|
||||||
uint32_t shaderIndex = shader.index();
|
|
||||||
auto &info = shaderDependencies_[shaderIndex];
|
|
||||||
info.shader = shader;
|
|
||||||
|
|
||||||
// 检查是否已存在
|
|
||||||
auto it = std::find(info.dependentMaterials.begin(),
|
|
||||||
info.dependentMaterials.end(), material);
|
|
||||||
if (it == info.dependentMaterials.end()) {
|
|
||||||
info.dependentMaterials.push_back(material);
|
|
||||||
E2D_DEBUG("已注册材质 {} 对着色器 {} 的依赖", material.index(),
|
|
||||||
shaderIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) {
|
void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) {
|
||||||
if (!texture.isValid()) {
|
dependencyTracker_.notifyTextureReloaded(
|
||||||
return;
|
texture, [this](Handle<Material> materialHandle) {
|
||||||
}
|
Material *material = materials_.get(materialHandle);
|
||||||
|
if (material) {
|
||||||
std::shared_lock<std::shared_mutex> lock(dependencyMutex_);
|
E2D_DEBUG("材质 {} 已更新为重载后的纹理", materialHandle.index());
|
||||||
|
}
|
||||||
uint32_t textureIndex = texture.index();
|
});
|
||||||
auto it = textureDependencies_.find(textureIndex);
|
|
||||||
if (it != textureDependencies_.end()) {
|
|
||||||
E2D_INFO("通知 {} 个材质纹理已重载", it->second.dependentMaterials.size());
|
|
||||||
|
|
||||||
// 材质需要更新纹理引用
|
|
||||||
// 这里可以触发材质更新事件
|
|
||||||
for (const auto &materialHandle : it->second.dependentMaterials) {
|
|
||||||
Material *material = materials_.get(materialHandle);
|
|
||||||
if (material) {
|
|
||||||
// 材质自动使用新的纹理数据
|
|
||||||
E2D_DEBUG("材质 {} 已更新为重载后的纹理", materialHandle.index());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) {
|
void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) {
|
||||||
if (!shader.isValid()) {
|
dependencyTracker_.notifyShaderReloaded(
|
||||||
return;
|
shader, [this, shader](Handle<Material> materialHandle) {
|
||||||
}
|
Material *material = materials_.get(materialHandle);
|
||||||
|
if (material) {
|
||||||
std::shared_lock<std::shared_mutex> lock(dependencyMutex_);
|
material->setShader(getPtr(shader));
|
||||||
|
E2D_DEBUG("材质 {} 已更新为重载后的着色器", materialHandle.index());
|
||||||
uint32_t shaderIndex = shader.index();
|
}
|
||||||
auto it = shaderDependencies_.find(shaderIndex);
|
});
|
||||||
if (it != shaderDependencies_.end()) {
|
|
||||||
E2D_INFO("通知 {} 个材质着色器已重载",
|
|
||||||
it->second.dependentMaterials.size());
|
|
||||||
|
|
||||||
for (const auto &materialHandle : it->second.dependentMaterials) {
|
|
||||||
Material *material = materials_.get(materialHandle);
|
|
||||||
if (material) {
|
|
||||||
// 更新材质的着色器引用
|
|
||||||
material->setShader(getPtr(shader));
|
|
||||||
E2D_DEBUG("材质 {} 已更新为重载后的着色器", materialHandle.index());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
#include <algorithm>
|
||||||
|
#include <assets/async/asset_async_loader.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
AssetAsyncLoader::~AssetAsyncLoader() { shutdown(); }
|
||||||
|
|
||||||
|
void AssetAsyncLoader::init(uint32_t threadCount) {
|
||||||
|
if (running_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threadCount == 0) {
|
||||||
|
threadCount = std::max(1u, std::thread::hardware_concurrency() / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
running_ = true;
|
||||||
|
for (uint32_t i = 0; i < threadCount; ++i) {
|
||||||
|
workers_.emplace_back(&AssetAsyncLoader::workerThreadLoop, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetAsyncLoader::shutdown() {
|
||||||
|
if (!running_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
running_ = false;
|
||||||
|
queueCV_.notify_all();
|
||||||
|
|
||||||
|
for (auto &thread : workers_) {
|
||||||
|
if (thread.joinable()) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
workers_.clear();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(queueMutex_);
|
||||||
|
queue_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetAsyncLoader::isRunning() const { return running_; }
|
||||||
|
|
||||||
|
void AssetAsyncLoader::submit(const Task &task) {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(queueMutex_);
|
||||||
|
queue_.push_back(task);
|
||||||
|
std::sort(queue_.begin(), queue_.end(), [](const Task &a, const Task &b) {
|
||||||
|
return static_cast<int>(a.priority) > static_cast<int>(b.priority);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
queueCV_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetAsyncLoader::workerThreadLoop() {
|
||||||
|
while (running_) {
|
||||||
|
Task task;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queueMutex_);
|
||||||
|
queueCV_.wait(lock, [this] { return !queue_.empty() || !running_; });
|
||||||
|
if (!running_) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (queue_.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
task = std::move(queue_.back());
|
||||||
|
queue_.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.work) {
|
||||||
|
task.work();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.onComplete) {
|
||||||
|
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
|
||||||
|
completedCallbacks_.push_back(task.onComplete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetAsyncLoader::processCallbacks() {
|
||||||
|
std::vector<std::function<void()>> callbacks;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(callbackMutex_);
|
||||||
|
callbacks = std::move(completedCallbacks_);
|
||||||
|
completedCallbacks_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &callback : callbacks) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
#include <algorithm>
|
||||||
|
#include <assets/deps/asset_dependency_tracker.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
void AssetDependencyTracker::clear() {
|
||||||
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||||
|
textureDependencies_.clear();
|
||||||
|
shaderDependencies_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetDependencyTracker::registerMaterialDependency(Handle<Material> material,
|
||||||
|
Handle<Texture> texture) {
|
||||||
|
if (!material.isValid() || !texture.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||||
|
auto &info = textureDependencies_[texture.index()];
|
||||||
|
info.texture = texture;
|
||||||
|
auto it = std::find(info.dependentMaterials.begin(),
|
||||||
|
info.dependentMaterials.end(), material);
|
||||||
|
if (it == info.dependentMaterials.end()) {
|
||||||
|
info.dependentMaterials.push_back(material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetDependencyTracker::registerMaterialDependency(Handle<Material> material,
|
||||||
|
Handle<Shader> shader) {
|
||||||
|
if (!material.isValid() || !shader.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||||
|
auto &info = shaderDependencies_[shader.index()];
|
||||||
|
info.shader = shader;
|
||||||
|
auto it = std::find(info.dependentMaterials.begin(),
|
||||||
|
info.dependentMaterials.end(), material);
|
||||||
|
if (it == info.dependentMaterials.end()) {
|
||||||
|
info.dependentMaterials.push_back(material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetDependencyTracker::notifyTextureReloaded(
|
||||||
|
Handle<Texture> texture,
|
||||||
|
const std::function<void(Handle<Material>)> &onMaterialUpdate) const {
|
||||||
|
if (!texture.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Handle<Material>> materials;
|
||||||
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||||
|
auto it = textureDependencies_.find(texture.index());
|
||||||
|
if (it == textureDependencies_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
materials = it->second.dependentMaterials;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &material : materials) {
|
||||||
|
onMaterialUpdate(material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetDependencyTracker::notifyShaderReloaded(
|
||||||
|
Handle<Shader> shader,
|
||||||
|
const std::function<void(Handle<Material>)> &onMaterialUpdate) const {
|
||||||
|
if (!shader.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Handle<Material>> materials;
|
||||||
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||||
|
auto it = shaderDependencies_.find(shader.index());
|
||||||
|
if (it == shaderDependencies_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
materials = it->second.dependentMaterials;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &material : materials) {
|
||||||
|
onMaterialUpdate(material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
#include <assets/hot_reload/asset_hot_reloader.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
AssetHotReloader::AssetHotReloader(const AssetFileSystem &fileSystem)
|
||||||
|
: fileSystem_(fileSystem) {}
|
||||||
|
|
||||||
|
void AssetHotReloader::enable(bool enable) { enabled_ = enable; }
|
||||||
|
|
||||||
|
bool AssetHotReloader::enabled() const { return enabled_; }
|
||||||
|
|
||||||
|
void AssetHotReloader::setInterval(float interval) { interval_ = interval; }
|
||||||
|
|
||||||
|
void AssetHotReloader::addFileWatchInternal(const std::string &path,
|
||||||
|
FileWatchInfo &&info) {
|
||||||
|
try {
|
||||||
|
if (!fileSystem_.exists(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
info.path = path;
|
||||||
|
info.lastWriteTime = fileSystem_.lastWriteTime(path);
|
||||||
|
|
||||||
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||||
|
watchList_.push_back(std::move(info));
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
E2D_ERROR("添加文件监控失败 {}: {}", path, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetHotReloader::addFileWatch(const std::string &path,
|
||||||
|
Handle<Texture> handle) {
|
||||||
|
FileWatchInfo info;
|
||||||
|
info.textureHandle = handle;
|
||||||
|
info.isTexture = true;
|
||||||
|
addFileWatchInternal(path, std::move(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetHotReloader::addFileWatch(const std::string &path,
|
||||||
|
Handle<Shader> handle) {
|
||||||
|
FileWatchInfo info;
|
||||||
|
info.shaderHandle = handle;
|
||||||
|
info.isTexture = false;
|
||||||
|
addFileWatchInternal(path, std::move(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetHotReloader::clear() {
|
||||||
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||||
|
watchList_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetHotReloader::checkForChanges(
|
||||||
|
const std::function<void(const FileWatchInfo &)> &reloadTexture,
|
||||||
|
const std::function<void(const FileWatchInfo &)> &reloadShader) {
|
||||||
|
if (!enabled_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||||
|
if (watchList_.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &info : watchList_) {
|
||||||
|
try {
|
||||||
|
if (fileSystem_.isRomfsPath(info.path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!fileSystem_.exists(info.path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentTime = fileSystem_.lastWriteTime(info.path);
|
||||||
|
if (currentTime != info.lastWriteTime) {
|
||||||
|
info.lastWriteTime = currentTime;
|
||||||
|
lock.unlock();
|
||||||
|
if (info.isTexture) {
|
||||||
|
reloadTexture(info);
|
||||||
|
} else {
|
||||||
|
reloadShader(info);
|
||||||
|
}
|
||||||
|
lock.lock();
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
E2D_ERROR("检查文件 {} 时出错: {}", info.path, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include <assets/io/asset_file_system.h>
|
||||||
|
#include <module/module_registry.h>
|
||||||
|
#include <platform/file_module.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
std::string AssetFileSystem::assetPath(const std::string &path) const {
|
||||||
|
FileModule *fileModule = getModule<FileModule>();
|
||||||
|
if (!fileModule) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return fileModule->assetPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetFileSystem::isRomfsPath(const std::string &path) const {
|
||||||
|
FileModule *fileModule = getModule<FileModule>();
|
||||||
|
if (!fileModule) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return fileModule->isRomfsPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetFileSystem::exists(const std::string &path) const {
|
||||||
|
FileModule *fileModule = getModule<FileModule>();
|
||||||
|
if (!fileModule) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return fileModule->exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AssetFileSystem::readString(const std::string &path) const {
|
||||||
|
FileModule *fileModule = getModule<FileModule>();
|
||||||
|
if (!fileModule) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return fileModule->readString(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::file_time_type
|
||||||
|
AssetFileSystem::lastWriteTime(const std::string &path) const {
|
||||||
|
if (isRomfsPath(path)) {
|
||||||
|
return std::filesystem::file_time_type::min();
|
||||||
|
}
|
||||||
|
return std::filesystem::last_write_time(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
||||||
|
|
@ -1,41 +1,11 @@
|
||||||
#include <assets/loaders/shader_loader.h>
|
#include <assets/loaders/shader_loader.h>
|
||||||
#include <module/module_registry.h>
|
|
||||||
#include <platform/file_module.h>
|
|
||||||
#include <renderer/shader.h>
|
#include <renderer/shader.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
namespace {
|
ShaderLoader::ShaderLoader(AssetFileSystem fileSystem)
|
||||||
|
: fileSystem_(std::move(fileSystem)) {}
|
||||||
/**
|
|
||||||
* @brief 获取 FileModule 实例
|
|
||||||
*/
|
|
||||||
FileModule *getFileModule() { return getModule<FileModule>(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查文件是否存在(使用 FileModule)
|
|
||||||
*/
|
|
||||||
bool fileExists(const std::string &path) {
|
|
||||||
FileModule *fileModule = getFileModule();
|
|
||||||
if (fileModule) {
|
|
||||||
return fileModule->exists(path);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 读取文件内容为字符串(使用 FileModule,支持 RomFS)
|
|
||||||
*/
|
|
||||||
std::string readFileToString(const std::string &path) {
|
|
||||||
FileModule *fileModule = getFileModule();
|
|
||||||
if (fileModule) {
|
|
||||||
return fileModule->readString(path);
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
Ptr<Shader> ShaderLoader::load(const std::string &path) {
|
Ptr<Shader> ShaderLoader::load(const std::string &path) {
|
||||||
std::string basePath = path;
|
std::string basePath = path;
|
||||||
|
|
@ -48,10 +18,10 @@ Ptr<Shader> ShaderLoader::load(const std::string &path) {
|
||||||
std::string vertPath = basePath + ".vert";
|
std::string vertPath = basePath + ".vert";
|
||||||
std::string fragPath = basePath + ".frag";
|
std::string fragPath = basePath + ".frag";
|
||||||
|
|
||||||
if (!fileExists(vertPath)) {
|
if (!fileSystem_.exists(vertPath)) {
|
||||||
vertPath = basePath + ".vs";
|
vertPath = basePath + ".vs";
|
||||||
}
|
}
|
||||||
if (!fileExists(fragPath)) {
|
if (!fileSystem_.exists(fragPath)) {
|
||||||
fragPath = basePath + ".fs";
|
fragPath = basePath + ".fs";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,14 +31,14 @@ Ptr<Shader> ShaderLoader::load(const std::string &path) {
|
||||||
Ptr<Shader> ShaderLoader::load(const std::string &vertPath,
|
Ptr<Shader> ShaderLoader::load(const std::string &vertPath,
|
||||||
const std::string &fragPath) {
|
const std::string &fragPath) {
|
||||||
// 读取顶点着色器文件
|
// 读取顶点着色器文件
|
||||||
std::string vsSource = readFileToString(vertPath);
|
std::string vsSource = fileSystem_.readString(vertPath);
|
||||||
if (vsSource.empty()) {
|
if (vsSource.empty()) {
|
||||||
E2D_ERROR("ShaderLoader: 读取顶点着色器失败: {}", vertPath);
|
E2D_ERROR("ShaderLoader: 读取顶点着色器失败: {}", vertPath);
|
||||||
return Ptr<Shader>();
|
return Ptr<Shader>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取片段着色器文件
|
// 读取片段着色器文件
|
||||||
std::string fsSource = readFileToString(fragPath);
|
std::string fsSource = fileSystem_.readString(fragPath);
|
||||||
if (fsSource.empty()) {
|
if (fsSource.empty()) {
|
||||||
E2D_ERROR("ShaderLoader: 读取片段着色器失败: {}", fragPath);
|
E2D_ERROR("ShaderLoader: 读取片段着色器失败: {}", fragPath);
|
||||||
return Ptr<Shader>();
|
return Ptr<Shader>();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <renderer/command_queue.h>
|
#include <renderer/command_queue.h>
|
||||||
#include <renderer/material.h>
|
#include <renderer/material.h>
|
||||||
|
|
@ -31,8 +32,9 @@ CommandSorter::~CommandSorter() = default;
|
||||||
CommandSorter::CommandSorter(CommandSorter &&other) noexcept
|
CommandSorter::CommandSorter(CommandSorter &&other) noexcept
|
||||||
: commands_(std::move(other.commands_)),
|
: commands_(std::move(other.commands_)),
|
||||||
sortedIndices_(std::move(other.sortedIndices_)),
|
sortedIndices_(std::move(other.sortedIndices_)),
|
||||||
commandCount_(other.commandCount_) {
|
commandCount_(other.commandCount_), needsSort_(other.needsSort_) {
|
||||||
other.commandCount_ = 0;
|
other.commandCount_ = 0;
|
||||||
|
other.needsSort_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandSorter &CommandSorter::operator=(CommandSorter &&other) noexcept {
|
CommandSorter &CommandSorter::operator=(CommandSorter &&other) noexcept {
|
||||||
|
|
@ -40,13 +42,18 @@ CommandSorter &CommandSorter::operator=(CommandSorter &&other) noexcept {
|
||||||
commands_ = std::move(other.commands_);
|
commands_ = std::move(other.commands_);
|
||||||
sortedIndices_ = std::move(other.sortedIndices_);
|
sortedIndices_ = std::move(other.sortedIndices_);
|
||||||
commandCount_ = other.commandCount_;
|
commandCount_ = other.commandCount_;
|
||||||
|
needsSort_ = other.needsSort_;
|
||||||
other.commandCount_ = 0;
|
other.commandCount_ = 0;
|
||||||
|
other.needsSort_ = false;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CommandSorter::addCommand(const DrawCommand &cmd) {
|
uint32_t CommandSorter::addCommand(const DrawCommand &cmd) {
|
||||||
uint32_t index = commandCount_;
|
uint32_t index = commandCount_;
|
||||||
|
if (index > 0 && cmd.key < commands_[index - 1].key) {
|
||||||
|
needsSort_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
// 如果缓冲区不够大,扩展它
|
// 如果缓冲区不够大,扩展它
|
||||||
if (index >= commands_.size()) {
|
if (index >= commands_.size()) {
|
||||||
|
|
@ -62,8 +69,9 @@ uint32_t CommandSorter::addCommand(const DrawCommand &cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandSorter::sort() {
|
void CommandSorter::sort() {
|
||||||
if (commandCount_ == 0)
|
if (commandCount_ == 0 || !needsSort_) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 只排序有效范围的索引
|
// 只排序有效范围的索引
|
||||||
auto indicesBegin = sortedIndices_.begin();
|
auto indicesBegin = sortedIndices_.begin();
|
||||||
|
|
@ -73,11 +81,13 @@ void CommandSorter::sort() {
|
||||||
std::stable_sort(indicesBegin, indicesEnd, [this](uint32_t a, uint32_t b) {
|
std::stable_sort(indicesBegin, indicesEnd, [this](uint32_t a, uint32_t b) {
|
||||||
return commands_[a].key < commands_[b].key;
|
return commands_[a].key < commands_[b].key;
|
||||||
});
|
});
|
||||||
|
needsSort_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandSorter::clear() {
|
void CommandSorter::clear() {
|
||||||
// 重置计数器,保留内存
|
// 重置计数器,保留内存
|
||||||
commandCount_ = 0;
|
commandCount_ = 0;
|
||||||
|
needsSort_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandSorter::reserve(uint32_t capacity) {
|
void CommandSorter::reserve(uint32_t capacity) {
|
||||||
|
|
@ -260,6 +270,7 @@ void CommandQueue::beginFrame() {
|
||||||
|
|
||||||
// 重置统计
|
// 重置统计
|
||||||
stats_ = {};
|
stats_ = {};
|
||||||
|
profilingStats_ = {};
|
||||||
|
|
||||||
// 开始录制命令
|
// 开始录制命令
|
||||||
if (commandList_) {
|
if (commandList_) {
|
||||||
|
|
@ -346,7 +357,7 @@ UniformBuffer *CommandQueue::getCurrentMaterialUBO() const {
|
||||||
|
|
||||||
void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
|
void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
|
||||||
const struct Transform &transform,
|
const struct Transform &transform,
|
||||||
const Color &color) {
|
const Color &color, uint32_t sortKey) {
|
||||||
if (!material || !mesh || !material->getShader()) {
|
if (!material || !mesh || !material->getShader()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -355,7 +366,9 @@ void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
|
||||||
|
|
||||||
// 构建排序键
|
// 构建排序键
|
||||||
uint32_t materialId = getMaterialId(material.get());
|
uint32_t materialId = getMaterialId(material.get());
|
||||||
cmd.key = DrawKey::make(materialId, 0, 0);
|
uint16_t depth = static_cast<uint16_t>((sortKey >> 8) & 0xFFFF);
|
||||||
|
uint8_t layer = static_cast<uint8_t>(sortKey & 0xFF);
|
||||||
|
cmd.key = DrawKey::make(materialId, depth, layer);
|
||||||
|
|
||||||
// 设置管线
|
// 设置管线
|
||||||
cmd.pipeline = material->getPipeline();
|
cmd.pipeline = material->getPipeline();
|
||||||
|
|
@ -438,6 +451,7 @@ void CommandQueue::setViewport(int32_t x, int32_t y, int32_t width,
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandQueue::updateGlobalUBO(const Mat4 &viewProjection, float deltaTime,
|
void CommandQueue::updateGlobalUBO(const Mat4 &viewProjection, float deltaTime,
|
||||||
|
float totalTime,
|
||||||
uint32_t screenWidth, uint32_t screenHeight,
|
uint32_t screenWidth, uint32_t screenHeight,
|
||||||
uint32_t frameIndex) {
|
uint32_t frameIndex) {
|
||||||
if (!uboManager_) {
|
if (!uboManager_) {
|
||||||
|
|
@ -452,7 +466,7 @@ void CommandQueue::updateGlobalUBO(const Mat4 &viewProjection, float deltaTime,
|
||||||
globalUBOData_.cameraPosition[1] = 0.0f;
|
globalUBOData_.cameraPosition[1] = 0.0f;
|
||||||
globalUBOData_.cameraPosition[2] = 0.0f;
|
globalUBOData_.cameraPosition[2] = 0.0f;
|
||||||
globalUBOData_.cameraPosition[3] = 1.0f;
|
globalUBOData_.cameraPosition[3] = 1.0f;
|
||||||
globalUBOData_.time = 0.0f; // TODO: 传递实际时间
|
globalUBOData_.time = totalTime;
|
||||||
globalUBOData_.deltaTime = deltaTime;
|
globalUBOData_.deltaTime = deltaTime;
|
||||||
globalUBOData_.screenSize[0] = static_cast<float>(screenWidth);
|
globalUBOData_.screenSize[0] = static_cast<float>(screenWidth);
|
||||||
globalUBOData_.screenSize[1] = static_cast<float>(screenHeight);
|
globalUBOData_.screenSize[1] = static_cast<float>(screenHeight);
|
||||||
|
|
@ -468,22 +482,43 @@ void CommandQueue::execute(uint32_t frameIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using Clock = std::chrono::high_resolution_clock;
|
||||||
|
const auto totalStart = Clock::now();
|
||||||
|
|
||||||
// 在排序前刷新材质 UBO 数据到 GPU
|
// 在排序前刷新材质 UBO 数据到 GPU
|
||||||
flushMaterialUBOToGPU();
|
flushMaterialUBOToGPU();
|
||||||
|
|
||||||
// 排序命令
|
// 排序命令
|
||||||
|
const auto sortStart = Clock::now();
|
||||||
sorter_.sort();
|
sorter_.sort();
|
||||||
|
const auto sortEnd = Clock::now();
|
||||||
|
|
||||||
// 批处理
|
// 批处理
|
||||||
|
const auto batchStart = Clock::now();
|
||||||
batcher_.process(sorter_);
|
batcher_.process(sorter_);
|
||||||
|
const auto batchEnd = Clock::now();
|
||||||
|
|
||||||
// 执行批次
|
// 执行批次
|
||||||
|
const auto executeStart = Clock::now();
|
||||||
for (uint32_t i = 0; i < batcher_.getBatchCount(); ++i) {
|
for (uint32_t i = 0; i < batcher_.getBatchCount(); ++i) {
|
||||||
executeBatch(i, batcher_.getBatch(i), frameIndex);
|
executeBatch(i, batcher_.getBatch(i), frameIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交命令到 GPU
|
// 提交命令到 GPU
|
||||||
commandList_->submit();
|
commandList_->submit();
|
||||||
|
const auto executeEnd = Clock::now();
|
||||||
|
|
||||||
|
profilingStats_.commandCount = sorter_.getCount();
|
||||||
|
profilingStats_.batchCount = batcher_.getBatchCount();
|
||||||
|
profilingStats_.sortMs =
|
||||||
|
std::chrono::duration<float, std::milli>(sortEnd - sortStart).count();
|
||||||
|
profilingStats_.batchMs =
|
||||||
|
std::chrono::duration<float, std::milli>(batchEnd - batchStart).count();
|
||||||
|
profilingStats_.executeMs = std::chrono::duration<float, std::milli>(
|
||||||
|
executeEnd - executeStart)
|
||||||
|
.count();
|
||||||
|
profilingStats_.totalMs =
|
||||||
|
std::chrono::duration<float, std::milli>(executeEnd - totalStart).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
|
void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
|
||||||
|
|
@ -520,29 +555,43 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行批次中的所有命令
|
// 执行批次中的所有命令
|
||||||
|
RHIBuffer *lastVertexBuffer = nullptr;
|
||||||
|
RHIBuffer *lastIndexBuffer = nullptr;
|
||||||
|
uint32_t lastMaterialOffset = static_cast<uint32_t>(-1);
|
||||||
|
uint32_t lastMaterialSize = static_cast<uint32_t>(-1);
|
||||||
for (uint32_t i = 0; i < batch.count; ++i) {
|
for (uint32_t i = 0; i < batch.count; ++i) {
|
||||||
const auto &cmd = batcher_.getCommand(batchIndex, i);
|
const auto &cmd = batcher_.getCommand(batchIndex, i);
|
||||||
|
|
||||||
// 绑定顶点缓冲区
|
// 绑定顶点缓冲区
|
||||||
if (cmd.vertexBuffer.isValid()) {
|
if (cmd.vertexBuffer.isValid() && cmd.vertexBuffer.get() != lastVertexBuffer) {
|
||||||
commandList_->setVertexBuffer(0, cmd.vertexBuffer.get(), 0);
|
commandList_->setVertexBuffer(0, cmd.vertexBuffer.get(), 0);
|
||||||
stats_.bufferBinds++;
|
stats_.bufferBinds++;
|
||||||
|
lastVertexBuffer = cmd.vertexBuffer.get();
|
||||||
} else {
|
} else {
|
||||||
E2D_WARN("绘制命令没有有效的顶点缓冲区!");
|
if (!cmd.vertexBuffer.isValid()) {
|
||||||
|
E2D_WARN("绘制命令没有有效的顶点缓冲区!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定索引缓冲区(如果有)
|
// 绑定索引缓冲区(如果有)
|
||||||
if (cmd.isIndexed() && cmd.indexBuffer.isValid()) {
|
if (cmd.isIndexed() && cmd.indexBuffer.isValid() &&
|
||||||
|
cmd.indexBuffer.get() != lastIndexBuffer) {
|
||||||
commandList_->setIndexBuffer(cmd.indexBuffer.get(), IndexType::UInt16, 0);
|
commandList_->setIndexBuffer(cmd.indexBuffer.get(), IndexType::UInt16, 0);
|
||||||
stats_.bufferBinds++;
|
stats_.bufferBinds++;
|
||||||
|
lastIndexBuffer = cmd.indexBuffer.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定材质 UBO (binding = 1)
|
// 绑定材质 UBO (binding = 1)
|
||||||
if (cmd.materialUBOSize > 0 && currentMaterialUBO_) {
|
if (cmd.materialUBOSize > 0 && currentMaterialUBO_) {
|
||||||
commandList_->setUniformBuffer(1, currentMaterialUBO_->getRHIBuffer(),
|
if (cmd.materialUBOOffset != lastMaterialOffset ||
|
||||||
cmd.materialUBOOffset,
|
cmd.materialUBOSize != lastMaterialSize) {
|
||||||
cmd.materialUBOSize);
|
commandList_->setUniformBuffer(1, currentMaterialUBO_->getRHIBuffer(),
|
||||||
stats_.bufferBinds++;
|
cmd.materialUBOOffset,
|
||||||
|
cmd.materialUBOSize);
|
||||||
|
stats_.bufferBinds++;
|
||||||
|
lastMaterialOffset = cmd.materialUBOOffset;
|
||||||
|
lastMaterialSize = cmd.materialUBOSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置模型矩阵
|
// 设置模型矩阵
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,8 @@ bool RenderGraph::compile() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderGraph::execute(float deltaTime, const Mat4 &viewProjection) {
|
void RenderGraph::execute(float deltaTime, const Mat4 &viewProjection,
|
||||||
|
float totalTime) {
|
||||||
if (!compiled_) {
|
if (!compiled_) {
|
||||||
if (!compile()) {
|
if (!compile()) {
|
||||||
E2D_ERROR("编译渲染图失败");
|
E2D_ERROR("编译渲染图失败");
|
||||||
|
|
@ -188,7 +189,7 @@ void RenderGraph::execute(float deltaTime, const Mat4 &viewProjection) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新全局 UBO(使用双缓冲)
|
// 更新全局 UBO(使用双缓冲)
|
||||||
commandQueue_.updateGlobalUBO(viewProjection, deltaTime, outputWidth_,
|
commandQueue_.updateGlobalUBO(viewProjection, deltaTime, totalTime, outputWidth_,
|
||||||
outputHeight_, frameIndex_);
|
outputHeight_, frameIndex_);
|
||||||
|
|
||||||
// 获取 RHI 上下文
|
// 获取 RHI 上下文
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ bool RendererModule::init() {
|
||||||
onRenderSetCameraListener_.bind(
|
onRenderSetCameraListener_.bind(
|
||||||
[this](const Mat4 &viewProj) { onRenderSetCamera(viewProj); });
|
[this](const Mat4 &viewProj) { onRenderSetCamera(viewProj); });
|
||||||
onRenderEndListener_.bind([this]() { onRenderEnd(); });
|
onRenderEndListener_.bind([this]() { onRenderEnd(); });
|
||||||
|
onUpdateListener_.bind([this](float dt) { onUpdate(dt); });
|
||||||
onResizeListener_.bind([this](int32 w, int32 h) { onResize(w, h); });
|
onResizeListener_.bind([this](int32 w, int32 h) { onResize(w, h); });
|
||||||
onShowListener_.bind([this]() { onWindowShow(); });
|
onShowListener_.bind([this]() { onWindowShow(); });
|
||||||
|
|
||||||
|
|
@ -156,6 +157,13 @@ void RendererModule::onRenderBegin() {
|
||||||
if (commandQueue_) {
|
if (commandQueue_) {
|
||||||
commandQueue_->beginFrame();
|
commandQueue_->beginFrame();
|
||||||
}
|
}
|
||||||
|
defaultMaterialPtr_ = nullptr;
|
||||||
|
defaultMeshPtr_ = nullptr;
|
||||||
|
auto *assets = getAssets();
|
||||||
|
if (assets) {
|
||||||
|
defaultMaterialPtr_ = assets->get(assets->getDefaultMaterial());
|
||||||
|
defaultMeshPtr_ = assets->get(assets->getDefaultQuad());
|
||||||
|
}
|
||||||
|
|
||||||
// 帧开始
|
// 帧开始
|
||||||
}
|
}
|
||||||
|
|
@ -166,8 +174,6 @@ void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)cmd; // 避免未使用警告
|
|
||||||
|
|
||||||
// 将渲染命令转换为绘制命令提交到队列
|
// 将渲染命令转换为绘制命令提交到队列
|
||||||
switch (cmd.type) {
|
switch (cmd.type) {
|
||||||
case RenderCommandType::DrawMesh: {
|
case RenderCommandType::DrawMesh: {
|
||||||
|
|
@ -175,23 +181,29 @@ void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
||||||
if (!assets)
|
if (!assets)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 获取材质,如果没有指定则使用默认材质
|
|
||||||
Material *material = assets->get(cmd.drawMesh.material);
|
Material *material = assets->get(cmd.drawMesh.material);
|
||||||
|
if (!material) {
|
||||||
|
material = defaultMaterialPtr_;
|
||||||
|
}
|
||||||
if (!material) {
|
if (!material) {
|
||||||
material = assets->get(assets->getDefaultMaterial());
|
material = assets->get(assets->getDefaultMaterial());
|
||||||
|
defaultMaterialPtr_ = material;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取网格,如果没有指定则使用默认四边形
|
|
||||||
Mesh *mesh = assets->get(cmd.drawMesh.mesh);
|
Mesh *mesh = assets->get(cmd.drawMesh.mesh);
|
||||||
|
if (!mesh) {
|
||||||
|
mesh = defaultMeshPtr_;
|
||||||
|
}
|
||||||
if (!mesh) {
|
if (!mesh) {
|
||||||
mesh = assets->get(assets->getDefaultQuad());
|
mesh = assets->get(assets->getDefaultQuad());
|
||||||
|
defaultMeshPtr_ = mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (material && mesh) {
|
if (material && mesh) {
|
||||||
Transform transform(cmd.drawMesh.pos, cmd.drawMesh.scale,
|
Transform transform(cmd.drawMesh.pos, cmd.drawMesh.scale,
|
||||||
cmd.drawMesh.rot);
|
cmd.drawMesh.rot);
|
||||||
commandQueue_->submitDraw(Ptr<Material>(material), Ptr<Mesh>(mesh),
|
commandQueue_->submitDraw(Ptr<Material>(material), Ptr<Mesh>(mesh),
|
||||||
transform, cmd.drawMesh.color);
|
transform, cmd.drawMesh.color, cmd.sortKey);
|
||||||
stats_.commandsSubmitted++;
|
stats_.commandsSubmitted++;
|
||||||
} else {
|
} else {
|
||||||
E2D_WARN("提交绘制命令失败: 材质={}, 网格={}", material ? "有效" : "空",
|
E2D_WARN("提交绘制命令失败: 材质={}, 网格={}", material ? "有效" : "空",
|
||||||
|
|
@ -224,6 +236,11 @@ void RendererModule::onRenderSetCamera(const Mat4 &viewProj) {
|
||||||
// 实际的 UBO 更新会在 RenderGraph 执行时进行
|
// 实际的 UBO 更新会在 RenderGraph 执行时进行
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererModule::onUpdate(float dt) {
|
||||||
|
frameDeltaTime_ = dt;
|
||||||
|
renderTotalTime_ += dt;
|
||||||
|
}
|
||||||
|
|
||||||
void RendererModule::onRenderEnd() {
|
void RendererModule::onRenderEnd() {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
E2D_WARN("onRenderEnd: 渲染模块未初始化");
|
E2D_WARN("onRenderEnd: 渲染模块未初始化");
|
||||||
|
|
@ -231,20 +248,31 @@ void RendererModule::onRenderEnd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行渲染图,传递视图投影矩阵
|
// 执行渲染图,传递视图投影矩阵
|
||||||
renderGraph_.execute(0.016f, viewProjectionMatrix_);
|
renderGraph_.execute(frameDeltaTime_, viewProjectionMatrix_,
|
||||||
|
renderTotalTime_);
|
||||||
|
|
||||||
// 获取统计信息
|
// 获取统计信息
|
||||||
if (commandQueue_) {
|
if (commandQueue_) {
|
||||||
const auto &stats = commandQueue_->getStats();
|
const auto &stats = commandQueue_->getStats();
|
||||||
|
const auto &profiling = commandQueue_->getProfilingStats();
|
||||||
stats_.drawCalls = stats.drawCalls;
|
stats_.drawCalls = stats.drawCalls;
|
||||||
|
stats_.batches = profiling.batchCount;
|
||||||
|
stats_.queueSortMs = profiling.sortMs;
|
||||||
|
stats_.queueBatchMs = profiling.batchMs;
|
||||||
|
stats_.queueExecuteMs = profiling.executeMs;
|
||||||
|
stats_.queueTotalMs = profiling.totalMs;
|
||||||
|
|
||||||
// 每60帧输出一次统计信息
|
// 每60帧输出一次统计信息
|
||||||
static uint32_t frameCount = 0;
|
static uint32_t frameCount = 0;
|
||||||
if (++frameCount % 60 == 0) {
|
if (++frameCount % 60 == 0) {
|
||||||
E2D_INFO("渲染统计: 绘制调用={}, 三角形数={}, 顶点数={}, "
|
E2D_INFO("渲染统计: 绘制调用={}, 三角形数={}, 顶点数={}, "
|
||||||
"管线绑定={}, 纹理绑定={}, 缓冲区绑定={}",
|
"管线绑定={}, 纹理绑定={}, 缓冲区绑定={}, 命令数={}, 批次数={}, "
|
||||||
|
"排序耗时={:.3f}ms, 批处理耗时={:.3f}ms, 执行耗时={:.3f}ms, "
|
||||||
|
"队列总耗时={:.3f}ms",
|
||||||
stats.drawCalls, stats.triangles, stats.vertices,
|
stats.drawCalls, stats.triangles, stats.vertices,
|
||||||
stats.pipelineBinds, stats.textureBinds, stats.bufferBinds);
|
stats.pipelineBinds, stats.textureBinds, stats.bufferBinds,
|
||||||
|
profiling.commandCount, profiling.batchCount, profiling.sortMs,
|
||||||
|
profiling.batchMs, profiling.executeMs, profiling.totalMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,20 @@ GLCommandList::GLCommandList() = default;
|
||||||
|
|
||||||
GLCommandList::~GLCommandList() = default;
|
GLCommandList::~GLCommandList() = default;
|
||||||
|
|
||||||
|
GLint GLCommandList::getUniformLocation(GLuint program, const char *name) {
|
||||||
|
if (program == 0 || name == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
auto &programCache = uniformLocationCache_[program];
|
||||||
|
auto it = programCache.find(name);
|
||||||
|
if (it != programCache.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
GLint location = glGetUniformLocation(program, name);
|
||||||
|
programCache.emplace(name, location);
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
void GLCommandList::begin() {
|
void GLCommandList::begin() {
|
||||||
// 开始记录命令
|
// 开始记录命令
|
||||||
recording_ = true;
|
recording_ = true;
|
||||||
|
|
@ -321,7 +335,7 @@ bool GLCommandList::isRecording() const {
|
||||||
|
|
||||||
void GLCommandList::setUniform(const char* name, float value) {
|
void GLCommandList::setUniform(const char* name, float value) {
|
||||||
if (stateCache_.shaderProgram != 0) {
|
if (stateCache_.shaderProgram != 0) {
|
||||||
GLint location = glGetUniformLocation(stateCache_.shaderProgram, name);
|
GLint location = getUniformLocation(stateCache_.shaderProgram, name);
|
||||||
if (location != -1) {
|
if (location != -1) {
|
||||||
glUniform1f(location, value);
|
glUniform1f(location, value);
|
||||||
}
|
}
|
||||||
|
|
@ -330,7 +344,7 @@ void GLCommandList::setUniform(const char* name, float value) {
|
||||||
|
|
||||||
void GLCommandList::setUniform(const char* name, const Vec2& value) {
|
void GLCommandList::setUniform(const char* name, const Vec2& value) {
|
||||||
if (stateCache_.shaderProgram != 0) {
|
if (stateCache_.shaderProgram != 0) {
|
||||||
GLint location = glGetUniformLocation(stateCache_.shaderProgram, name);
|
GLint location = getUniformLocation(stateCache_.shaderProgram, name);
|
||||||
if (location != -1) {
|
if (location != -1) {
|
||||||
glUniform2f(location, value.x, value.y);
|
glUniform2f(location, value.x, value.y);
|
||||||
}
|
}
|
||||||
|
|
@ -339,7 +353,7 @@ void GLCommandList::setUniform(const char* name, const Vec2& value) {
|
||||||
|
|
||||||
void GLCommandList::setUniform(const char* name, const Vec3& value) {
|
void GLCommandList::setUniform(const char* name, const Vec3& value) {
|
||||||
if (stateCache_.shaderProgram != 0) {
|
if (stateCache_.shaderProgram != 0) {
|
||||||
GLint location = glGetUniformLocation(stateCache_.shaderProgram, name);
|
GLint location = getUniformLocation(stateCache_.shaderProgram, name);
|
||||||
if (location != -1) {
|
if (location != -1) {
|
||||||
glUniform3f(location, value.x, value.y, value.z);
|
glUniform3f(location, value.x, value.y, value.z);
|
||||||
}
|
}
|
||||||
|
|
@ -348,7 +362,7 @@ void GLCommandList::setUniform(const char* name, const Vec3& value) {
|
||||||
|
|
||||||
void GLCommandList::setUniform(const char* name, const Color& value) {
|
void GLCommandList::setUniform(const char* name, const Color& value) {
|
||||||
if (stateCache_.shaderProgram != 0) {
|
if (stateCache_.shaderProgram != 0) {
|
||||||
GLint location = glGetUniformLocation(stateCache_.shaderProgram, name);
|
GLint location = getUniformLocation(stateCache_.shaderProgram, name);
|
||||||
if (location != -1) {
|
if (location != -1) {
|
||||||
glUniform4f(location, value.r, value.g, value.b, value.a);
|
glUniform4f(location, value.r, value.g, value.b, value.a);
|
||||||
}
|
}
|
||||||
|
|
@ -357,7 +371,7 @@ void GLCommandList::setUniform(const char* name, const Color& value) {
|
||||||
|
|
||||||
void GLCommandList::setUniform(const char* name, const Mat4& value) {
|
void GLCommandList::setUniform(const char* name, const Mat4& value) {
|
||||||
if (stateCache_.shaderProgram != 0) {
|
if (stateCache_.shaderProgram != 0) {
|
||||||
GLint location = glGetUniformLocation(stateCache_.shaderProgram, name);
|
GLint location = getUniformLocation(stateCache_.shaderProgram, name);
|
||||||
if (location != -1) {
|
if (location != -1) {
|
||||||
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
|
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue