refactor(render): 重构渲染系统,移除旧图形模块并添加新渲染核心组件
- 移除旧的图形模块,包括GPU上下文、纹理、字体、帧缓冲等 - 添加新的渲染核心组件:RenderStats和Component - 将渲染命令相关代码迁移到render/core目录 - 引入新的渲染队列系统替代旧批处理机制 - 清理不再使用的OpenGL和Vulkan后端实现
This commit is contained in:
parent
a705a3d300
commit
89d1612336
|
|
@ -35,6 +35,15 @@
|
|||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#include <extra2d/graphics/texture/texture_pool.h>
|
||||
|
||||
// RHI (Render Hardware Interface)
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_buffer.h>
|
||||
#include <extra2d/render/rhi/rhi_texture.h>
|
||||
#include <extra2d/render/rhi/rhi_shader.h>
|
||||
#include <extra2d/render/rhi/rhi_pipeline.h>
|
||||
#include <extra2d/render/rhi/rhi_framebuffer.h>
|
||||
|
||||
// Scene
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
|
|
|
|||
|
|
@ -1,124 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace graphics {
|
||||
|
||||
/**
|
||||
* @brief 图形后端工厂
|
||||
* 用于注册和创建图形渲染后端
|
||||
*/
|
||||
class BackendFactory {
|
||||
public:
|
||||
using BackendFn = std::function<UniquePtr<RenderBackend>()>;
|
||||
|
||||
/**
|
||||
* @brief 注册图形后端
|
||||
* @param name 后端名称
|
||||
* @param backend 后端创建函数
|
||||
* @param windowBackends 支持的窗口后端名称列表
|
||||
*/
|
||||
static void reg(const std::string &name, BackendFn backend,
|
||||
const std::vector<std::string> &windowBackends = {});
|
||||
|
||||
/**
|
||||
* @brief 创建渲染后端实例
|
||||
* @param name 后端名称
|
||||
* @return 渲染后端实例,如果后端不存在返回 nullptr
|
||||
*/
|
||||
static UniquePtr<RenderBackend> createBackend(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 创建默认渲染后端
|
||||
* @return 默认渲染后端实例
|
||||
*/
|
||||
static UniquePtr<RenderBackend> createDefaultBackend();
|
||||
|
||||
/**
|
||||
* @brief 创建与指定窗口后端兼容的渲染后端
|
||||
* @param windowBackend 窗口后端名称
|
||||
* @return 兼容的渲染后端实例
|
||||
*/
|
||||
static UniquePtr<RenderBackend>
|
||||
createBackendForWindow(const std::string &windowBackend);
|
||||
|
||||
/**
|
||||
* @brief 获取所有已注册的后端名称
|
||||
*/
|
||||
static std::vector<std::string> backends();
|
||||
|
||||
/**
|
||||
* @brief 检查后端是否存在
|
||||
*/
|
||||
static bool has(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 获取推荐的后端名称
|
||||
* @return 推荐的后端名称
|
||||
*/
|
||||
static std::string getRecommendedBackend();
|
||||
|
||||
/**
|
||||
* @brief 获取与指定窗口后端兼容的推荐图形后端
|
||||
* @param windowBackend 窗口后端名称
|
||||
* @return 推荐的图形后端名称
|
||||
*/
|
||||
static std::string
|
||||
getRecommendedBackendForWindow(const std::string &windowBackend);
|
||||
|
||||
/**
|
||||
* @brief 检查图形后端是否支持指定窗口后端
|
||||
* @param graphicsBackend 图形后端名称
|
||||
* @param windowBackend 窗口后端名称
|
||||
* @return 支持返回 true
|
||||
*/
|
||||
static bool isCompatible(const std::string &graphicsBackend,
|
||||
const std::string &windowBackend);
|
||||
|
||||
/**
|
||||
* @brief 获取图形后端支持的窗口后端列表
|
||||
* @param graphicsBackend 图形后端名称
|
||||
* @return 支持的窗口后端列表
|
||||
*/
|
||||
static std::vector<std::string>
|
||||
getSupportedWindowBackends(const std::string &graphicsBackend);
|
||||
|
||||
private:
|
||||
struct BackendEntry {
|
||||
BackendFn createFn;
|
||||
std::vector<std::string> windowBackends;
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, BackendEntry> ®istry();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 图形后端注册宏
|
||||
* 在全局作用域使用此宏注册图形后端
|
||||
*
|
||||
* @example
|
||||
* E2D_REG_GRAPHICS_BACKEND(opengl, GLRenderer, {"sdl2", "glfw"})
|
||||
*/
|
||||
#define E2D_REG_GRAPHICS_BACKEND(name, RendererClass, windowBackends) \
|
||||
namespace { \
|
||||
__attribute__((used)) static struct E2D_GRAPHICS_BACKEND_REG_##name { \
|
||||
E2D_GRAPHICS_BACKEND_REG_##name() { \
|
||||
::extra2d::graphics::BackendFactory::reg( \
|
||||
#name, \
|
||||
[]() -> ::extra2d::UniquePtr<::extra2d::RenderBackend> { \
|
||||
return ::extra2d::makeUnique<RendererClass>(); \
|
||||
}, \
|
||||
windowBackends); \
|
||||
} \
|
||||
} e2d_graphics_backend_reg_##name; \
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/resources/buffer.h>
|
||||
#include <glad/glad.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 缓冲区实现
|
||||
// ============================================================================
|
||||
class GLBuffer : public Buffer {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLBuffer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLBuffer() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化缓冲区
|
||||
* @param desc 缓冲区描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(const BufferDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 关闭缓冲区,释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// Buffer 接口实现
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
void setData(const void* data, size_t size) override;
|
||||
void updateData(const void* data, size_t offset, size_t size) override;
|
||||
void* map() override;
|
||||
void unmap() override;
|
||||
size_t getSize() const override { return size_; }
|
||||
BufferType getType() const override { return type_; }
|
||||
BufferUsage getUsage() const override { return usage_; }
|
||||
bool isValid() const override { return bufferID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(bufferID_); }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 缓冲区 ID
|
||||
* @return 缓冲区 ID
|
||||
*/
|
||||
GLuint getBufferID() const { return bufferID_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 缓冲区目标类型
|
||||
* @return 缓冲区目标类型
|
||||
*/
|
||||
GLenum getTarget() const { return target_; }
|
||||
|
||||
private:
|
||||
GLuint bufferID_ = 0;
|
||||
GLenum target_ = GL_ARRAY_BUFFER;
|
||||
size_t size_ = 0;
|
||||
BufferType type_ = BufferType::Vertex;
|
||||
BufferUsage usage_ = BufferUsage::Static;
|
||||
GLenum glUsage_ = GL_STATIC_DRAW;
|
||||
bool mapped_ = false;
|
||||
void* mappedPtr_ = nullptr;
|
||||
|
||||
/**
|
||||
* @brief 转换使用模式到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertUsage(BufferUsage usage);
|
||||
|
||||
/**
|
||||
* @brief 转换缓冲区类型到 OpenGL 目标
|
||||
*/
|
||||
static GLenum convertType(BufferType type);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 版本信息
|
||||
// ============================================================================
|
||||
struct GLVersion {
|
||||
int major = 0;
|
||||
int minor = 0;
|
||||
bool es = false; // 是否为 ES 版本
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 上下文管理类
|
||||
// ============================================================================
|
||||
class GLContext {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取全局 GLContext 实例
|
||||
*/
|
||||
static GLContext &get();
|
||||
|
||||
/**
|
||||
* @brief 初始化 OpenGL 上下文
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭 OpenGL 上下文
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查上下文是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
bool isValid() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 版本信息
|
||||
*/
|
||||
const GLVersion &getVersion() const { return version_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 版本字符串
|
||||
*/
|
||||
std::string getVersionString() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 GPU 厂商信息
|
||||
*/
|
||||
std::string getVendor() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 GPU 渲染器信息
|
||||
*/
|
||||
std::string getRenderer() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持指定扩展
|
||||
* @param extension 扩展名称
|
||||
* @return 支持返回 true
|
||||
*/
|
||||
bool hasExtension(const std::string &extension) const;
|
||||
|
||||
/**
|
||||
* @brief 获取最大纹理尺寸
|
||||
*/
|
||||
int getMaxTextureSize() const;
|
||||
|
||||
/**
|
||||
* @brief 获取最大纹理单元数
|
||||
*/
|
||||
int getMaxTextureUnits() const;
|
||||
|
||||
/**
|
||||
* @brief 获取最大顶点属性数
|
||||
*/
|
||||
int getMaxVertexAttribs() const;
|
||||
|
||||
/**
|
||||
* @brief 获取最大 uniform 缓冲区绑定点数
|
||||
*/
|
||||
int getMaxUniformBufferBindings() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否为 OpenGL ES
|
||||
*/
|
||||
bool isGLES() const { return version_.es; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持 VAO
|
||||
*/
|
||||
bool hasVAO() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持 FBO
|
||||
*/
|
||||
bool hasFBO() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持 Shader
|
||||
*/
|
||||
bool hasShader() const;
|
||||
|
||||
private:
|
||||
GLContext() = default;
|
||||
~GLContext() = default;
|
||||
|
||||
GLContext(const GLContext &) = delete;
|
||||
GLContext &operator=(const GLContext &) = delete;
|
||||
|
||||
bool initialized_ = false;
|
||||
GLVersion version_;
|
||||
|
||||
// 缓存的限制值
|
||||
mutable int maxTextureSize_ = -1;
|
||||
mutable int maxTextureUnits_ = -1;
|
||||
mutable int maxVertexAttribs_ = -1;
|
||||
mutable int maxUniformBufferBindings_ = -1;
|
||||
|
||||
/**
|
||||
* @brief 解析 OpenGL 版本
|
||||
*/
|
||||
void parseVersion();
|
||||
|
||||
/**
|
||||
* @brief 加载 OpenGL 扩展
|
||||
*/
|
||||
bool loadExtensions();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture/font.h>
|
||||
|
||||
#include <stb/stb_truetype.h>
|
||||
#include <stb/stb_rect_pack.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 字体图集实现 (使用 STB 库)
|
||||
// 使用 stb_rect_pack 进行动态矩形打包,支持动态缓存字形
|
||||
// ============================================================================
|
||||
class GLFontAtlas : public FontAtlas {
|
||||
public:
|
||||
GLFontAtlas(const std::string& filepath, int fontSize, bool useSDF = false);
|
||||
~GLFontAtlas() override;
|
||||
|
||||
// FontAtlas 接口实现
|
||||
const Glyph* getGlyph(char32_t codepoint) const override;
|
||||
Texture* getTexture() const override { return texture_.get(); }
|
||||
int getFontSize() const override { return fontSize_; }
|
||||
float getAscent() const override { return ascent_; }
|
||||
float getDescent() const override { return descent_; }
|
||||
float getLineGap() const override { return lineGap_; }
|
||||
float getLineHeight() const override { return lineHeight_; }
|
||||
bool isSDF() const override { return useSDF_; }
|
||||
Vec2 measureText(const std::string& text) override;
|
||||
|
||||
private:
|
||||
// 字形数据内部结构
|
||||
struct GlyphData {
|
||||
float width;
|
||||
float height;
|
||||
float bearingX;
|
||||
float bearingY;
|
||||
float advance;
|
||||
float u0, v0, u1, v1;
|
||||
};
|
||||
|
||||
// 图集配置 - 增大尺寸以支持更多字符
|
||||
static constexpr int ATLAS_WIDTH = 1024;
|
||||
static constexpr int ATLAS_HEIGHT = 1024;
|
||||
static constexpr int PADDING = 2; // 字形之间的间距
|
||||
|
||||
bool useSDF_;
|
||||
int fontSize_;
|
||||
|
||||
Ptr<GLTexture> texture_;
|
||||
std::unordered_map<char32_t, GlyphData> glyphs_;
|
||||
float lineHeight_;
|
||||
float ascent_;
|
||||
float descent_;
|
||||
float lineGap_;
|
||||
|
||||
// 字体数据
|
||||
std::vector<unsigned char> fontData_;
|
||||
stbtt_fontinfo fontInfo_;
|
||||
float scale_;
|
||||
|
||||
// stb_rect_pack 上下文 - 持久化以支持增量打包
|
||||
mutable stbrp_context packContext_;
|
||||
mutable std::vector<stbrp_node> packNodes_;
|
||||
|
||||
// 预分配缓冲区,避免每次动态分配
|
||||
mutable std::vector<uint8_t> glyphBitmapCache_;
|
||||
mutable std::vector<uint8_t> glyphRgbaCache_;
|
||||
|
||||
// 初始化字体
|
||||
bool initFont(const std::string& filepath);
|
||||
// 创建空白图集纹理
|
||||
void createAtlas();
|
||||
// 缓存字形到图集
|
||||
void cacheGlyph(char32_t codepoint);
|
||||
// 更新图集纹理区域
|
||||
void updateAtlas(int x, int y, int width, int height,
|
||||
const std::vector<uint8_t>& data);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/resources/framebuffer.h>
|
||||
#include <glad/glad.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 帧缓冲实现
|
||||
// ============================================================================
|
||||
class GLFramebuffer : public Framebuffer {
|
||||
public:
|
||||
// 最大颜色附件数
|
||||
static constexpr int MAX_COLOR_ATTACHMENTS = 8;
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLFramebuffer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLFramebuffer() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化帧缓冲
|
||||
* @param desc 帧缓冲描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(const FramebufferDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 关闭帧缓冲,释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// Framebuffer 接口实现
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
void attachColorTexture(Ptr<Texture> texture, int attachment = 0) override;
|
||||
void attachDepthTexture(Ptr<Texture> texture) override;
|
||||
void attachDepthStencilTexture(Ptr<Texture> texture) override;
|
||||
bool isComplete() override;
|
||||
Ptr<Texture> getColorTexture(int attachment = 0) const override;
|
||||
Ptr<Texture> getDepthTexture() const override;
|
||||
int getWidth() const override { return width_; }
|
||||
int getHeight() const override { return height_; }
|
||||
Size getSize() const override { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
|
||||
bool isValid() const override { return fboID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(fboID_); }
|
||||
void clear(const Color& color, bool clearColor = true,
|
||||
bool clearDepth = true, bool clearStencil = false) override;
|
||||
void setViewport(int x, int y, int width, int height) override;
|
||||
bool readPixels(int x, int y, int width, int height,
|
||||
std::vector<uint8_t>& outData) override;
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL FBO ID
|
||||
* @return FBO ID
|
||||
*/
|
||||
GLuint getFboID() const { return fboID_; }
|
||||
|
||||
/**
|
||||
* @brief 创建带内置纹理的帧缓冲(便捷方法)
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param colorFormat 颜色格式
|
||||
* @param depthFormat 深度格式(可选)
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool createWithTextures(int width, int height,
|
||||
PixelFormat colorFormat = PixelFormat::RGBA8,
|
||||
PixelFormat depthFormat = PixelFormat::Depth24);
|
||||
|
||||
private:
|
||||
GLuint fboID_ = 0;
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
int numColorAttachments_ = 1;
|
||||
bool hasDepth_ = false;
|
||||
bool hasStencil_ = false;
|
||||
|
||||
// 附件纹理
|
||||
std::array<Ptr<Texture>, MAX_COLOR_ATTACHMENTS> colorTextures_;
|
||||
Ptr<Texture> depthTexture_;
|
||||
Ptr<Texture> depthStencilTexture_;
|
||||
|
||||
// 是否为内置纹理(需要自动清理)
|
||||
bool hasInternalTextures_ = false;
|
||||
|
||||
/**
|
||||
* @brief 检查并更新完整状态
|
||||
*/
|
||||
bool checkStatus();
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 附件枚举
|
||||
*/
|
||||
static GLenum getColorAttachment(int index);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/resources/pipeline.h>
|
||||
#include <glad/glad.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 管线状态实现
|
||||
// ============================================================================
|
||||
class GLPipeline : public Pipeline {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLPipeline();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLPipeline() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化管线
|
||||
* @param desc 管线描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(const PipelineDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 关闭管线,释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// Pipeline 接口实现
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
void setBlendMode(BlendMode mode) override;
|
||||
BlendMode getBlendMode() const override { return blendMode_; }
|
||||
void setDepthTest(bool enabled) override;
|
||||
void setDepthWrite(bool enabled) override;
|
||||
void setDepthFunc(DepthFunc func) override;
|
||||
void setCullMode(CullMode mode) override;
|
||||
bool isValid() const override { return initialized_; }
|
||||
uintptr_t getNativeHandle() const override { return 0; } // OpenGL 管线没有单一句柄
|
||||
|
||||
/**
|
||||
* @brief 设置视口
|
||||
* @param x 视口左下角X坐标
|
||||
* @param y 视口左下角Y坐标
|
||||
* @param width 视口宽度
|
||||
* @param height 视口高度
|
||||
*/
|
||||
void setViewport(int x, int y, int width, int height);
|
||||
|
||||
/**
|
||||
* @brief 获取当前视口
|
||||
* @param x 输出X坐标
|
||||
* @param y 输出Y坐标
|
||||
* @param width 输出宽度
|
||||
* @param height 输出高度
|
||||
*/
|
||||
void getViewport(int& x, int& y, int& width, int& height) const;
|
||||
|
||||
/**
|
||||
* @brief 应用所有状态(用于初始化或重置)
|
||||
*/
|
||||
void applyAllStates();
|
||||
|
||||
private:
|
||||
bool initialized_ = false;
|
||||
|
||||
// 当前状态
|
||||
BlendMode blendMode_ = BlendMode::Alpha;
|
||||
bool blendEnabled_ = true;
|
||||
bool depthTest_ = false;
|
||||
bool depthWrite_ = false;
|
||||
DepthFunc depthFunc_ = DepthFunc::Less;
|
||||
CullMode cullMode_ = CullMode::None;
|
||||
|
||||
// 视口
|
||||
int viewportX_ = 0;
|
||||
int viewportY_ = 0;
|
||||
int viewportWidth_ = 0;
|
||||
int viewportHeight_ = 0;
|
||||
|
||||
// 状态缓存(避免冗余 GL 调用)
|
||||
BlendMode cachedBlendMode_ = BlendMode::None;
|
||||
bool cachedBlendEnabled_ = false;
|
||||
bool cachedDepthTest_ = false;
|
||||
bool cachedDepthWrite_ = false;
|
||||
DepthFunc cachedDepthFunc_ = DepthFunc::Less;
|
||||
CullMode cachedCullMode_ = CullMode::None;
|
||||
int cachedViewportX_ = -1;
|
||||
int cachedViewportY_ = -1;
|
||||
int cachedViewportWidth_ = -1;
|
||||
int cachedViewportHeight_ = -1;
|
||||
|
||||
/**
|
||||
* @brief 应用混合状态
|
||||
*/
|
||||
void applyBlendState();
|
||||
|
||||
/**
|
||||
* @brief 应用深度状态
|
||||
*/
|
||||
void applyDepthState();
|
||||
|
||||
/**
|
||||
* @brief 应用裁剪状态
|
||||
*/
|
||||
void applyCullState();
|
||||
|
||||
/**
|
||||
* @brief 转换混合模式到 OpenGL 枚举
|
||||
*/
|
||||
static void getBlendFactors(BlendMode mode, GLenum& srcFactor, GLenum& dstFactor);
|
||||
|
||||
/**
|
||||
* @brief 转换深度函数到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertDepthFunc(DepthFunc func);
|
||||
|
||||
/**
|
||||
* @brief 转换裁剪模式到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertCullMode(CullMode mode);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/backends/opengl/gl_buffer.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_pipeline.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_sprite_batch.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
|
||||
#include <array>
|
||||
#include <glad/glad.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class IWindow;
|
||||
class GLContext;
|
||||
class GLFramebuffer;
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 渲染器实现
|
||||
// ============================================================================
|
||||
class GLRenderer : public RenderBackend {
|
||||
public:
|
||||
GLRenderer();
|
||||
~GLRenderer() override;
|
||||
|
||||
// RenderBackend 接口实现
|
||||
bool init(IWindow* window) override;
|
||||
void shutdown() override;
|
||||
|
||||
void beginFrame(const Color &clearColor) override;
|
||||
void endFrame() override;
|
||||
void setViewport(int x, int y, int width, int height) override;
|
||||
void setVSync(bool enabled) override;
|
||||
|
||||
void setBlendMode(BlendMode mode) override;
|
||||
void setViewProjection(const glm::mat4 &matrix) override;
|
||||
|
||||
// 变换矩阵栈
|
||||
void pushTransform(const glm::mat4 &transform) override;
|
||||
void popTransform() override;
|
||||
glm::mat4 getCurrentTransform() const override;
|
||||
|
||||
Ptr<Texture> createTexture(int width, int height, const uint8_t *pixels,
|
||||
int channels) override;
|
||||
Ptr<Texture> loadTexture(const std::string &filepath) override;
|
||||
|
||||
void beginSpriteBatch() override;
|
||||
void drawSprite(const Texture &texture, const Rect &destRect,
|
||||
const Rect &srcRect, const Color &tint, float rotation,
|
||||
const Vec2 &anchor) override;
|
||||
void drawSprite(const Texture &texture, const Vec2 &position,
|
||||
const Color &tint) override;
|
||||
void endSpriteBatch() override;
|
||||
void flush() override;
|
||||
|
||||
void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
|
||||
float width) override;
|
||||
void drawRect(const Rect &rect, const Color &color, float width) override;
|
||||
void fillRect(const Rect &rect, const Color &color) override;
|
||||
void drawCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments, float width) override;
|
||||
void fillCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments) override;
|
||||
void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color, float width) override;
|
||||
void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color) override;
|
||||
void drawPolygon(const std::vector<Vec2> &points, const Color &color,
|
||||
float width) override;
|
||||
void fillPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color) override;
|
||||
|
||||
Ptr<FontAtlas> createFontAtlas(const std::string &filepath, int fontSize,
|
||||
bool useSDF = false) override;
|
||||
void drawText(const FontAtlas &font, const std::string &text,
|
||||
const Vec2 &position, const Color &color) override;
|
||||
void drawText(const FontAtlas &font, const std::string &text, float x,
|
||||
float y, const Color &color) override;
|
||||
|
||||
Stats getStats() const override { return stats_; }
|
||||
void resetStats() override;
|
||||
|
||||
// GLFramebuffer 相关方法
|
||||
|
||||
/**
|
||||
* @brief 创建帧缓冲对象
|
||||
* @param desc 帧缓冲描述
|
||||
* @return 创建的帧缓冲智能指针
|
||||
*/
|
||||
Ptr<GLFramebuffer> createFramebuffer(const FramebufferDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 绑定帧缓冲(作为渲染目标)
|
||||
* @param framebuffer 帧缓冲对象指针,传入 nullptr 则绑定默认帧缓冲
|
||||
*/
|
||||
void bindFramebuffer(GLFramebuffer* framebuffer);
|
||||
|
||||
/**
|
||||
* @brief 解绑帧缓冲(恢复到默认帧缓冲)
|
||||
*/
|
||||
void unbindFramebuffer();
|
||||
|
||||
/**
|
||||
* @brief 获取默认帧缓冲
|
||||
* @return 默认帧缓冲智能指针
|
||||
*/
|
||||
Ptr<GLFramebuffer> getDefaultFramebuffer() const;
|
||||
|
||||
/**
|
||||
* @brief 清除当前绑定的帧缓冲
|
||||
* @param color 清除颜色
|
||||
* @param clearColor 是否清除颜色缓冲
|
||||
* @param clearDepth 是否清除深度缓冲
|
||||
* @param clearStencil 是否清除模板缓冲
|
||||
*/
|
||||
void clearFramebuffer(const Color& color, bool clearColor = true,
|
||||
bool clearDepth = true, bool clearStencil = false);
|
||||
|
||||
private:
|
||||
// 形状批处理常量
|
||||
static constexpr size_t MAX_CIRCLE_SEGMENTS = 128;
|
||||
static constexpr size_t MAX_SHAPE_VERTICES = 8192; // 最大形状顶点数
|
||||
static constexpr size_t MAX_LINE_VERTICES = 16384; // 最大线条顶点数
|
||||
|
||||
// 形状顶点结构(包含颜色)
|
||||
struct ShapeVertex {
|
||||
float x, y;
|
||||
float r, g, b, a;
|
||||
};
|
||||
|
||||
IWindow* window_;
|
||||
GLSpriteBatch spriteBatch_;
|
||||
Ptr<IShader> shapeShader_;
|
||||
Ptr<IShader> sdfFontShader_; // SDF字体专用着色器
|
||||
|
||||
GLuint shapeVao_; // 形状 VAO(手动管理,用于顶点属性配置)
|
||||
GLBuffer shapeBuffer_; // 形状 VBO(使用 GLBuffer 管理)
|
||||
GLuint lineVao_; // 线条 VAO(手动管理,用于顶点属性配置)
|
||||
GLBuffer lineBuffer_; // 线条 VBO(使用 GLBuffer 管理)
|
||||
|
||||
glm::mat4 viewProjection_;
|
||||
std::vector<glm::mat4> transformStack_;
|
||||
Stats stats_;
|
||||
bool vsync_;
|
||||
|
||||
// 形状批处理缓冲区(预分配,避免每帧内存分配)
|
||||
std::array<ShapeVertex, MAX_SHAPE_VERTICES> shapeVertexCache_;
|
||||
size_t shapeVertexCount_ = 0;
|
||||
GLenum currentShapeMode_ = GL_TRIANGLES;
|
||||
|
||||
// 线条批处理缓冲区
|
||||
std::array<ShapeVertex, MAX_LINE_VERTICES> lineVertexCache_;
|
||||
size_t lineVertexCount_ = 0;
|
||||
float currentLineWidth_ = 1.0f;
|
||||
|
||||
// OpenGL 管线状态管理
|
||||
GLPipeline pipeline_;
|
||||
|
||||
// 自动批处理状态
|
||||
bool batchActive_ = false; // 批处理是否激活
|
||||
bool autoBatchEnabled_ = true; // 是否启用自动批处理
|
||||
const Texture* currentBatchTexture_ = nullptr; // 当前批处理的纹理
|
||||
std::vector<SpriteData> pendingSprites_; // 待提交的精灵
|
||||
static constexpr size_t MAX_BATCH_SPRITES = 1000; // 最大批处理精灵数
|
||||
|
||||
// 帧缓冲管理
|
||||
mutable Ptr<GLFramebuffer> defaultFramebuffer_; // 默认帧缓冲(延迟创建)
|
||||
GLFramebuffer* currentFramebuffer_ = nullptr; // 当前绑定的帧缓冲
|
||||
|
||||
void initShapeRendering();
|
||||
void ensureBatchActive(); // 确保批处理已激活
|
||||
void submitPendingSprites(); // 提交待处理的精灵
|
||||
void flushShapeBatch();
|
||||
void flushLineBatch();
|
||||
void addShapeVertex(float x, float y, const Color &color);
|
||||
void addLineVertex(float x, float y, const Color &color);
|
||||
void submitShapeBatch(GLenum mode);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
#include <glad/glad.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class GLShader : public IShader {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLShader();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLShader() override;
|
||||
|
||||
/**
|
||||
* @brief 绑定Shader程序
|
||||
*/
|
||||
void bind() const override;
|
||||
|
||||
/**
|
||||
* @brief 解绑Shader程序
|
||||
*/
|
||||
void unbind() const override;
|
||||
|
||||
/**
|
||||
* @brief 设置布尔类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 布尔值
|
||||
*/
|
||||
void setBool(const std::string& name, bool value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置整数类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 整数值
|
||||
*/
|
||||
void setInt(const std::string& name, int value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置浮点类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 浮点值
|
||||
*/
|
||||
void setFloat(const std::string& name, float value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 二维向量值
|
||||
*/
|
||||
void setVec2(const std::string& name, const glm::vec2& value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 三维向量值
|
||||
*/
|
||||
void setVec3(const std::string& name, const glm::vec3& value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 四维向量值
|
||||
*/
|
||||
void setVec4(const std::string& name, const glm::vec4& value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置4x4矩阵类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 4x4矩阵值
|
||||
*/
|
||||
void setMat4(const std::string& name, const glm::mat4& value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置颜色类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param color 颜色值
|
||||
*/
|
||||
void setColor(const std::string& name, const Color& color) override;
|
||||
|
||||
/**
|
||||
* @brief 检查Shader是否有效
|
||||
* @return 有效返回true,否则返回false
|
||||
*/
|
||||
bool isValid() const override { return programID_ != 0; }
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(OpenGL程序ID)
|
||||
* @return OpenGL程序ID
|
||||
*/
|
||||
uint32_t getNativeHandle() const override { return programID_; }
|
||||
|
||||
/**
|
||||
* @brief 获取Shader名称
|
||||
* @return Shader名称
|
||||
*/
|
||||
const std::string& getName() const override { return name_; }
|
||||
|
||||
/**
|
||||
* @brief 设置Shader名称
|
||||
* @param name Shader名称
|
||||
*/
|
||||
void setName(const std::string& name) override { name_ = name; }
|
||||
|
||||
/**
|
||||
* @brief 从源码编译Shader
|
||||
* @param vertexSource 顶点着色器源码
|
||||
* @param fragmentSource 片段着色器源码
|
||||
* @return 编译成功返回true,失败返回false
|
||||
*/
|
||||
bool compileFromSource(const char* vertexSource, const char* fragmentSource);
|
||||
|
||||
/**
|
||||
* @brief 从二进制数据创建Shader
|
||||
* @param binary 二进制数据
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*/
|
||||
bool compileFromBinary(const std::vector<uint8_t>& binary);
|
||||
|
||||
/**
|
||||
* @brief 获取Shader二进制数据
|
||||
* @param outBinary 输出的二进制数据
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool getBinary(std::vector<uint8_t>& outBinary);
|
||||
|
||||
/**
|
||||
* @brief 获取OpenGL程序ID
|
||||
* @return OpenGL程序ID
|
||||
*/
|
||||
GLuint getProgramID() const { return programID_; }
|
||||
|
||||
private:
|
||||
GLuint programID_ = 0;
|
||||
std::string name_;
|
||||
std::unordered_map<std::string, GLint> uniformCache_;
|
||||
|
||||
/**
|
||||
* @brief 编译单个着色器
|
||||
* @param type 着色器类型
|
||||
* @param source 着色器源码
|
||||
* @return 着色器ID,失败返回0
|
||||
*/
|
||||
GLuint compileShader(GLenum type, const char* source);
|
||||
|
||||
/**
|
||||
* @brief 获取uniform位置
|
||||
* @param name uniform变量名
|
||||
* @return uniform位置
|
||||
*/
|
||||
GLint getUniformLocation(const std::string& name);
|
||||
};
|
||||
|
||||
class GLShaderFactory : public IShaderFactory {
|
||||
public:
|
||||
/**
|
||||
* @brief 从源码创建Shader
|
||||
* @param name Shader名称
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
Ptr<IShader> createFromSource(
|
||||
const std::string& name,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource) override;
|
||||
|
||||
/**
|
||||
* @brief 从缓存二进制创建Shader
|
||||
* @param name Shader名称
|
||||
* @param binary 编译后的二进制数据
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
Ptr<IShader> createFromBinary(
|
||||
const std::string& name,
|
||||
const std::vector<uint8_t>& binary) override;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader的二进制数据
|
||||
* @param shader Shader实例
|
||||
* @param outBinary 输出的二进制数据
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool getShaderBinary(const IShader& shader,
|
||||
std::vector<uint8_t>& outBinary) override;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/backends/opengl/gl_buffer.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/batch/sprite_batch.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 精灵批处理渲染器
|
||||
// 使用 batch/sprite_batch 作为后端无关的批处理层
|
||||
// ============================================================================
|
||||
class GLSpriteBatch {
|
||||
public:
|
||||
GLSpriteBatch();
|
||||
~GLSpriteBatch();
|
||||
|
||||
// 初始化/关闭
|
||||
bool init();
|
||||
void shutdown();
|
||||
|
||||
// 批处理生命周期
|
||||
void begin(const glm::mat4 &viewProjection);
|
||||
void end();
|
||||
|
||||
// 使用指定着色器开始批处理
|
||||
void begin(const glm::mat4 &viewProjection, Ptr<IShader> shader);
|
||||
|
||||
// 绘制单个精灵
|
||||
void draw(const Texture &texture, const SpriteData &data);
|
||||
|
||||
// 批量绘制(用于文本渲染优化)
|
||||
void drawBatch(const Texture &texture,
|
||||
const std::vector<SpriteData> &sprites);
|
||||
|
||||
// 获取绘制调用次数
|
||||
uint32_t getDrawCallCount() const { return drawCallCount_; }
|
||||
|
||||
// 设置自定义着色器(用于SDF字体等特殊渲染)
|
||||
void setShader(Ptr<IShader> shader);
|
||||
|
||||
// 获取当前着色器
|
||||
Ptr<IShader> getShader() const { return shader_; }
|
||||
|
||||
// 设置额外的uniform值(用于SDF字体等特殊渲染)
|
||||
void setExtraUniforms(const UniformValueMap &uniforms) { extraUniforms_ = uniforms; }
|
||||
void clearExtraUniforms() { extraUniforms_.clear(); }
|
||||
|
||||
private:
|
||||
// OpenGL 对象
|
||||
GLuint vao_;
|
||||
GLBuffer vbo_; // 顶点缓冲区(动态)
|
||||
GLBuffer ebo_; // 索引缓冲区(静态)
|
||||
|
||||
// 后端无关的批处理层
|
||||
SpriteBatch batch_;
|
||||
|
||||
// 批次管理
|
||||
struct Batch {
|
||||
const GLTexture *texture;
|
||||
size_t startVertex;
|
||||
size_t vertexCount;
|
||||
};
|
||||
std::vector<Batch> batches_;
|
||||
const GLTexture *currentTexture_;
|
||||
|
||||
// 着色器和矩阵
|
||||
Ptr<IShader> shader_;
|
||||
uint32_t drawCallCount_;
|
||||
glm::mat4 viewProjection_;
|
||||
|
||||
// 额外的uniform值(用于SDF字体等特殊渲染)
|
||||
UniformValueMap extraUniforms_;
|
||||
|
||||
// 内部方法
|
||||
void flush();
|
||||
void submitBatch();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/graphics/texture/alpha_mask.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 纹理实现
|
||||
// ============================================================================
|
||||
class GLTexture : public Texture {
|
||||
public:
|
||||
GLTexture(int width, int height, const uint8_t* pixels, int channels);
|
||||
GLTexture(const std::string& filepath);
|
||||
~GLTexture();
|
||||
|
||||
// Texture 接口实现
|
||||
int getWidth() const override { return width_; }
|
||||
int getHeight() const override { return height_; }
|
||||
Size getSize() const override { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
|
||||
int getChannels() const override { return channels_; }
|
||||
PixelFormat getFormat() const override;
|
||||
void* getNativeHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(textureID_)); }
|
||||
bool isValid() const override { return textureID_ != 0; }
|
||||
void setFilter(bool linear) override;
|
||||
void setWrap(bool repeat) override;
|
||||
|
||||
// 从参数创建纹理的工厂方法
|
||||
static Ptr<Texture> create(int width, int height, PixelFormat format);
|
||||
|
||||
// 加载压缩纹理(KTX/DDS 格式)
|
||||
bool loadCompressed(const std::string& filepath);
|
||||
|
||||
// OpenGL 特定
|
||||
GLuint getTextureID() const { return textureID_; }
|
||||
void bind(unsigned int slot = 0) const;
|
||||
void unbind() const;
|
||||
|
||||
// 获取纹理数据大小(字节),用于 VRAM 跟踪
|
||||
size_t getDataSize() const { return dataSize_; }
|
||||
|
||||
// Alpha 遮罩
|
||||
bool hasAlphaMask() const { return alphaMask_ != nullptr && alphaMask_->isValid(); }
|
||||
const AlphaMask* getAlphaMask() const { return alphaMask_.get(); }
|
||||
void generateAlphaMask(); // 从当前纹理数据生成遮罩
|
||||
|
||||
private:
|
||||
GLuint textureID_;
|
||||
int width_;
|
||||
int height_;
|
||||
int channels_;
|
||||
PixelFormat format_;
|
||||
size_t dataSize_;
|
||||
|
||||
// 原始像素数据(用于生成遮罩)
|
||||
std::vector<uint8_t> pixelData_;
|
||||
std::unique_ptr<AlphaMask> alphaMask_;
|
||||
|
||||
void createTexture(const uint8_t* pixels);
|
||||
|
||||
// KTX 文件加载
|
||||
bool loadKTX(const std::string& filepath);
|
||||
// DDS 文件加载
|
||||
bool loadDDS(const std::string& filepath);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief Vulkan 渲染器实现(占位)
|
||||
*
|
||||
* 这是一个占位实现,用于展示Vulkan后端应该包含的内容。
|
||||
* 完整的实现需要包含Vulkan上下文、设备、交换链、管线等。
|
||||
*/
|
||||
class VulkanRenderer : public RenderBackend {
|
||||
public:
|
||||
VulkanRenderer();
|
||||
~VulkanRenderer() override;
|
||||
|
||||
// RenderBackend 接口实现
|
||||
bool init(IWindow* window) override;
|
||||
void shutdown() override;
|
||||
|
||||
void beginFrame(const Color &clearColor) override;
|
||||
void endFrame() override;
|
||||
void setViewport(int x, int y, int width, int height) override;
|
||||
void setVSync(bool enabled) override;
|
||||
|
||||
void setBlendMode(BlendMode mode) override;
|
||||
void setViewProjection(const glm::mat4 &matrix) override;
|
||||
|
||||
void pushTransform(const glm::mat4 &transform) override;
|
||||
void popTransform() override;
|
||||
glm::mat4 getCurrentTransform() const override;
|
||||
|
||||
Ptr<Texture> createTexture(int width, int height, const uint8_t *pixels,
|
||||
int channels) override;
|
||||
Ptr<Texture> loadTexture(const std::string &filepath) override;
|
||||
|
||||
void beginSpriteBatch() override;
|
||||
void drawSprite(const Texture &texture, const Rect &destRect,
|
||||
const Rect &srcRect, const Color &tint, float rotation,
|
||||
const Vec2 &anchor) override;
|
||||
void drawSprite(const Texture &texture, const Vec2 &position,
|
||||
const Color &tint) override;
|
||||
void endSpriteBatch() override;
|
||||
|
||||
void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
|
||||
float width) override;
|
||||
void drawRect(const Rect &rect, const Color &color, float width) override;
|
||||
void fillRect(const Rect &rect, const Color &color) override;
|
||||
void drawCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments, float width) override;
|
||||
void fillCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments) override;
|
||||
void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color, float width) override;
|
||||
void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color) override;
|
||||
void drawPolygon(const std::vector<Vec2> &points, const Color &color,
|
||||
float width) override;
|
||||
void fillPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color) override;
|
||||
|
||||
Ptr<FontAtlas> createFontAtlas(const std::string &filepath, int fontSize,
|
||||
bool useSDF = false) override;
|
||||
void drawText(const FontAtlas &font, const std::string &text,
|
||||
const Vec2 &position, const Color &color) override;
|
||||
void drawText(const FontAtlas &font, const std::string &text, float x,
|
||||
float y, const Color &color) override;
|
||||
|
||||
Stats getStats() const override { return stats_; }
|
||||
void resetStats() override;
|
||||
|
||||
private:
|
||||
Stats stats_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 形状顶点结构
|
||||
// ============================================================================
|
||||
struct ShapeVertex {
|
||||
float x, y; // 位置
|
||||
float r, g, b, a; // 颜色
|
||||
|
||||
ShapeVertex() = default;
|
||||
ShapeVertex(float px, float py, const Color& c)
|
||||
: x(px), y(py), r(c.r), g(c.g), b(c.b), a(c.a) {}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 形状批处理抽象接口 - 后端无关
|
||||
// ============================================================================
|
||||
class ShapeBatch {
|
||||
public:
|
||||
virtual ~ShapeBatch() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化形状批处理
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
virtual bool init() = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭形状批处理,释放资源
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 开始批处理
|
||||
* @param viewProjection 视图投影矩阵
|
||||
*/
|
||||
virtual void begin(const glm::mat4& viewProjection) = 0;
|
||||
|
||||
/**
|
||||
* @brief 结束批处理并提交绘制
|
||||
*/
|
||||
virtual void end() = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制线段
|
||||
* @param start 起点
|
||||
* @param end 终点
|
||||
* @param color 颜色
|
||||
* @param width 线宽
|
||||
*/
|
||||
virtual void drawLine(const Vec2& start, const Vec2& end,
|
||||
const Color& color, float width = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制矩形边框
|
||||
* @param rect 矩形区域
|
||||
* @param color 颜色
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
virtual void drawRect(const Rect& rect, const Color& color,
|
||||
float width = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 填充矩形
|
||||
* @param rect 矩形区域
|
||||
* @param color 颜色
|
||||
*/
|
||||
virtual void fillRect(const Rect& rect, const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制圆形边框
|
||||
* @param center 圆心
|
||||
* @param radius 半径
|
||||
* @param color 颜色
|
||||
* @param segments 分段数
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
virtual void drawCircle(const Vec2& center, float radius,
|
||||
const Color& color, int segments = 32,
|
||||
float width = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 填充圆形
|
||||
* @param center 圆心
|
||||
* @param radius 半径
|
||||
* @param color 颜色
|
||||
* @param segments 分段数
|
||||
*/
|
||||
virtual void fillCircle(const Vec2& center, float radius,
|
||||
const Color& color, int segments = 32) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制三角形边框
|
||||
* @param p1 顶点1
|
||||
* @param p2 顶点2
|
||||
* @param p3 顶点3
|
||||
* @param color 颜色
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
virtual void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
|
||||
const Color& color, float width = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 填充三角形
|
||||
* @param p1 顶点1
|
||||
* @param p2 顶点2
|
||||
* @param p3 顶点3
|
||||
* @param color 颜色
|
||||
*/
|
||||
virtual void fillTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
|
||||
const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制多边形边框
|
||||
* @param points 顶点数组
|
||||
* @param color 颜色
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
virtual void drawPolygon(const std::vector<Vec2>& points,
|
||||
const Color& color, float width = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 填充多边形
|
||||
* @param points 顶点数组
|
||||
* @param color 颜色
|
||||
*/
|
||||
virtual void fillPolygon(const std::vector<Vec2>& points,
|
||||
const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取绘制调用次数
|
||||
* @return 绘制调用次数
|
||||
*/
|
||||
virtual uint32_t getDrawCallCount() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 重置绘制调用计数
|
||||
*/
|
||||
virtual void resetDrawCallCount() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 三角函数查表 - 避免每帧计算 sin/cos
|
||||
// ============================================================================
|
||||
class TrigLookup {
|
||||
public:
|
||||
TrigLookup();
|
||||
|
||||
// 通过角度(0-360)获取 sin/cos
|
||||
float sin(int angle) const;
|
||||
float cos(int angle) const;
|
||||
|
||||
// 通过弧度获取 sin/cos
|
||||
float sinRad(float rad) const;
|
||||
float cosRad(float rad) const;
|
||||
|
||||
private:
|
||||
static constexpr int TABLE_SIZE = 360 * 4; // 0.25度精度
|
||||
std::array<float, TABLE_SIZE> sinTable_;
|
||||
std::array<float, TABLE_SIZE> cosTable_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 精灵批次数据 - 后端无关
|
||||
// ============================================================================
|
||||
struct SpriteVertex {
|
||||
Vec2 position;
|
||||
Vec2 texCoord;
|
||||
Color color;
|
||||
};
|
||||
|
||||
struct SpriteData {
|
||||
Vec2 position;
|
||||
Vec2 size;
|
||||
float rotation;
|
||||
Vec2 pivot;
|
||||
Color color;
|
||||
const Texture* texture;
|
||||
Rect uvRect;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 通用精灵批处理 - 后端无关
|
||||
// 负责:顶点生成、批次管理、三角函数查表
|
||||
// ============================================================================
|
||||
class SpriteBatch {
|
||||
public:
|
||||
static constexpr size_t MAX_SPRITES = 10000;
|
||||
static constexpr size_t VERTICES_PER_SPRITE = 4;
|
||||
static constexpr size_t INDICES_PER_SPRITE = 6;
|
||||
static constexpr size_t MAX_VERTICES = MAX_SPRITES * VERTICES_PER_SPRITE;
|
||||
static constexpr size_t MAX_INDICES = MAX_SPRITES * INDICES_PER_SPRITE;
|
||||
|
||||
SpriteBatch();
|
||||
~SpriteBatch() = default;
|
||||
|
||||
// 开始批次
|
||||
void begin(const glm::mat4& viewProjection);
|
||||
|
||||
// 结束批次 - 返回需要绘制的批次列表
|
||||
void end();
|
||||
|
||||
// 绘制单个精灵
|
||||
void draw(const SpriteData& sprite);
|
||||
|
||||
// 批量绘制 - 一次性处理多个精灵
|
||||
void drawBatch(const std::vector<SpriteData>& sprites);
|
||||
|
||||
// 立即绘制 - 不缓存,直接提交
|
||||
void drawImmediate(const SpriteData& sprite);
|
||||
|
||||
// 获取当前批次数据
|
||||
const std::vector<SpriteVertex>& getVertices() const { return vertices_; }
|
||||
const std::vector<uint16_t>& getIndices() const { return indices_; }
|
||||
size_t getSpriteCount() const { return spriteCount_; }
|
||||
|
||||
// 检查是否需要刷新
|
||||
bool needsFlush() const { return spriteCount_ >= MAX_SPRITES; }
|
||||
|
||||
// 清空批次
|
||||
void clear();
|
||||
|
||||
private:
|
||||
// 三角函数查表
|
||||
TrigLookup trigLookup_;
|
||||
|
||||
// 顶点数据 - 使用固定大小数组避免动态分配
|
||||
std::vector<SpriteVertex> vertices_;
|
||||
std::vector<uint16_t> indices_;
|
||||
size_t spriteCount_;
|
||||
|
||||
// 变换矩阵
|
||||
glm::mat4 viewProjection_;
|
||||
glm::mat4 cachedVP_;
|
||||
bool vpDirty_;
|
||||
|
||||
// 生成索引
|
||||
void generateIndices();
|
||||
|
||||
// 生成顶点
|
||||
void generateVertices(const SpriteData& sprite, size_t vertexOffset);
|
||||
|
||||
// 刷新批次
|
||||
void flush();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class ViewportAdapter;
|
||||
|
||||
// ============================================================================
|
||||
// 2D 正交相机
|
||||
// ============================================================================
|
||||
class Camera {
|
||||
public:
|
||||
Camera();
|
||||
Camera(float left, float right, float bottom, float top);
|
||||
Camera(const Size &viewport);
|
||||
~Camera() = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 位置和变换
|
||||
// ------------------------------------------------------------------------
|
||||
void setPos(const Vec2 &position);
|
||||
void setPos(float x, float y);
|
||||
Vec2 getPosition() const { return position_; }
|
||||
|
||||
void setRotation(float degrees);
|
||||
float getRotation() const { return rotation_; }
|
||||
|
||||
void setZoom(float zoom);
|
||||
float getZoom() const { return zoom_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 视口设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setViewport(float left, float right, float bottom, float top);
|
||||
void setViewport(const Rect &rect);
|
||||
Rect getViewport() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 矩阵获取
|
||||
// ------------------------------------------------------------------------
|
||||
glm::mat4 getViewMatrix() const;
|
||||
glm::mat4 getProjectionMatrix() const;
|
||||
glm::mat4 getViewProjectionMatrix() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 坐标转换
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 screenToWorld(const Vec2 &screenPos) const;
|
||||
Vec2 worldToScreen(const Vec2 &worldPos) const;
|
||||
Vec2 screenToWorld(float x, float y) const;
|
||||
Vec2 worldToScreen(float x, float y) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 移动相机
|
||||
// ------------------------------------------------------------------------
|
||||
void move(const Vec2 &offset);
|
||||
void move(float x, float y);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 边界限制
|
||||
// ------------------------------------------------------------------------
|
||||
void setBounds(const Rect &bounds);
|
||||
void clearBounds();
|
||||
void clampToBounds();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 视口适配器
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief 设置视口适配器
|
||||
* @param adapter 视口适配器指针
|
||||
*/
|
||||
void setViewportAdapter(ViewportAdapter *adapter);
|
||||
|
||||
/**
|
||||
* @brief 根据视口适配器自动设置视口
|
||||
*/
|
||||
void applyViewportAdapter();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 快捷方法:看向某点
|
||||
// ------------------------------------------------------------------------
|
||||
void lookAt(const Vec2 &target);
|
||||
|
||||
private:
|
||||
Vec2 position_ = Vec2::Zero();
|
||||
float rotation_ = 0.0f;
|
||||
float zoom_ = 1.0f;
|
||||
|
||||
float left_ = -1.0f;
|
||||
float right_ = 1.0f;
|
||||
float bottom_ = -1.0f;
|
||||
float top_ = 1.0f;
|
||||
|
||||
Rect bounds_;
|
||||
bool hasBounds_ = false;
|
||||
|
||||
ViewportAdapter *viewportAdapter_ = nullptr;
|
||||
|
||||
mutable glm::mat4 viewMatrix_;
|
||||
mutable glm::mat4 projMatrix_;
|
||||
mutable glm::mat4 vpMatrix_;
|
||||
mutable bool viewDirty_ = true;
|
||||
mutable bool projDirty_ = true;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,332 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 视口适配模式枚举
|
||||
// ============================================================================
|
||||
enum class ViewportMode {
|
||||
AspectRatio,
|
||||
Stretch,
|
||||
Center,
|
||||
Custom
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 黑边位置枚举
|
||||
// ============================================================================
|
||||
enum class LetterboxPosition {
|
||||
Center,
|
||||
LeftTop,
|
||||
RightTop,
|
||||
LeftBottom,
|
||||
RightBottom
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 视口配置结构体
|
||||
// ============================================================================
|
||||
struct ViewportConfig {
|
||||
float logicWidth = 1920.0f;
|
||||
float logicHeight = 1080.0f;
|
||||
ViewportMode mode = ViewportMode::AspectRatio;
|
||||
LetterboxPosition letterboxPosition = LetterboxPosition::Center;
|
||||
Color letterboxColor = Colors::Black;
|
||||
bool autoScaleInCenterMode = true;
|
||||
float customScale = 1.0f;
|
||||
Vec2 customOffset = Vec2::Zero();
|
||||
Rect customViewport = Rect::Zero();
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 视口计算结果结构体
|
||||
// ============================================================================
|
||||
struct ViewportResult {
|
||||
Rect viewport;
|
||||
float scaleX = 1.0f;
|
||||
float scaleY = 1.0f;
|
||||
float uniformScale = 1.0f;
|
||||
Vec2 offset;
|
||||
bool hasLetterbox = false;
|
||||
|
||||
struct Letterbox {
|
||||
Rect top;
|
||||
Rect bottom;
|
||||
Rect left;
|
||||
Rect right;
|
||||
} letterbox;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 视口适配器类
|
||||
// ============================================================================
|
||||
class ViewportAdapter {
|
||||
public:
|
||||
ViewportAdapter();
|
||||
ViewportAdapter(float logicWidth, float logicHeight);
|
||||
~ViewportAdapter() = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 配置设置
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置视口配置
|
||||
* @param config 视口配置结构体
|
||||
*/
|
||||
void setConfig(const ViewportConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 获取当前视口配置
|
||||
* @return 当前视口配置
|
||||
*/
|
||||
const ViewportConfig &getConfig() const { return config_; }
|
||||
|
||||
/**
|
||||
* @brief 设置逻辑分辨率
|
||||
* @param width 逻辑宽度
|
||||
* @param height 逻辑高度
|
||||
*/
|
||||
void setLogicSize(float width, float height);
|
||||
|
||||
/**
|
||||
* @brief 设置视口适配模式
|
||||
* @param mode 适配模式
|
||||
*/
|
||||
void setMode(ViewportMode mode);
|
||||
|
||||
/**
|
||||
* @brief 设置黑边位置
|
||||
* @param position 黑边位置
|
||||
*/
|
||||
void setLetterboxPosition(LetterboxPosition position);
|
||||
|
||||
/**
|
||||
* @brief 设置黑边颜色
|
||||
* @param color 黑边颜色
|
||||
*/
|
||||
void setLetterboxColor(const Color &color);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 更新和计算
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 更新视口适配计算
|
||||
* @param screenWidth 屏幕宽度
|
||||
* @param screenHeight 屏幕高度
|
||||
*/
|
||||
void update(int screenWidth, int screenHeight);
|
||||
|
||||
/**
|
||||
* @brief 获取计算结果
|
||||
* @return 视口计算结果
|
||||
*/
|
||||
const ViewportResult &getResult() const { return result_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 坐标转换
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 屏幕坐标转逻辑坐标
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 逻辑坐标
|
||||
*/
|
||||
Vec2 screenToLogic(const Vec2 &screenPos) const;
|
||||
|
||||
/**
|
||||
* @brief 逻辑坐标转屏幕坐标
|
||||
* @param logicPos 逻辑坐标
|
||||
* @return 屏幕坐标
|
||||
*/
|
||||
Vec2 logicToScreen(const Vec2 &logicPos) const;
|
||||
|
||||
/**
|
||||
* @brief 屏幕坐标转逻辑坐标(分量形式)
|
||||
* @param x 屏幕X坐标
|
||||
* @param y 屏幕Y坐标
|
||||
* @return 逻辑坐标
|
||||
*/
|
||||
Vec2 screenToLogic(float x, float y) const;
|
||||
|
||||
/**
|
||||
* @brief 逻辑坐标转屏幕坐标(分量形式)
|
||||
* @param x 逻辑X坐标
|
||||
* @param y 逻辑Y坐标
|
||||
* @return 屏幕坐标
|
||||
*/
|
||||
Vec2 logicToScreen(float x, float y) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 矩阵获取
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取视口变换矩阵
|
||||
* @return 视口变换矩阵(从逻辑坐标到屏幕坐标)
|
||||
*/
|
||||
glm::mat4 getMatrix() const;
|
||||
|
||||
/**
|
||||
* @brief 获取反向视口变换矩阵
|
||||
* @return 反向视口变换矩阵(从屏幕坐标到逻辑坐标)
|
||||
*/
|
||||
glm::mat4 getInvMatrix() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 区域检测
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 检查屏幕坐标是否在视口内
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 如果在视口内返回 true
|
||||
*/
|
||||
bool isInViewport(const Vec2 &screenPos) const;
|
||||
|
||||
/**
|
||||
* @brief 检查屏幕坐标是否在黑边区域
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 如果在黑边区域返回 true
|
||||
*/
|
||||
bool isInLetterbox(const Vec2 &screenPos) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Getter 方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取逻辑宽度
|
||||
* @return 逻辑宽度
|
||||
*/
|
||||
float getLogicWidth() const { return config_.logicWidth; }
|
||||
|
||||
/**
|
||||
* @brief 获取逻辑高度
|
||||
* @return 逻辑高度
|
||||
*/
|
||||
float getLogicHeight() const { return config_.logicHeight; }
|
||||
|
||||
/**
|
||||
* @brief 获取逻辑尺寸
|
||||
* @return 逻辑尺寸
|
||||
*/
|
||||
Size getLogicSize() const {
|
||||
return Size(config_.logicWidth, config_.logicHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取屏幕宽度
|
||||
* @return 屏幕宽度
|
||||
*/
|
||||
int getScreenWidth() const { return screenWidth_; }
|
||||
|
||||
/**
|
||||
* @brief 获取屏幕高度
|
||||
* @return 屏幕高度
|
||||
*/
|
||||
int getScreenHeight() const { return screenHeight_; }
|
||||
|
||||
/**
|
||||
* @brief 获取屏幕尺寸
|
||||
* @return 屏幕尺寸
|
||||
*/
|
||||
Size getScreenSize() const {
|
||||
return Size(static_cast<float>(screenWidth_),
|
||||
static_cast<float>(screenHeight_));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取X方向缩放比例
|
||||
* @return X方向缩放比例
|
||||
*/
|
||||
float getScaleX() const { return result_.scaleX; }
|
||||
|
||||
/**
|
||||
* @brief 获取Y方向缩放比例
|
||||
* @return Y方向缩放比例
|
||||
*/
|
||||
float getScaleY() const { return result_.scaleY; }
|
||||
|
||||
/**
|
||||
* @brief 获取统一缩放比例
|
||||
* @return 统一缩放比例
|
||||
*/
|
||||
float getUniformScale() const { return result_.uniformScale; }
|
||||
|
||||
/**
|
||||
* @brief 获取视口偏移
|
||||
* @return 视口偏移
|
||||
*/
|
||||
Vec2 getOffset() const { return result_.offset; }
|
||||
|
||||
/**
|
||||
* @brief 获取视口矩形
|
||||
* @return 视口矩形
|
||||
*/
|
||||
Rect getViewport() const { return result_.viewport; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否有黑边
|
||||
* @return 如果有黑边返回 true
|
||||
*/
|
||||
bool hasLetterbox() const { return result_.hasLetterbox; }
|
||||
|
||||
/**
|
||||
* @brief 获取黑边信息
|
||||
* @return 黑边信息结构体
|
||||
*/
|
||||
const ViewportResult::Letterbox &getLetterbox() const {
|
||||
return result_.letterbox;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 计算宽高比适配模式
|
||||
*/
|
||||
void calculateAspectRatio();
|
||||
|
||||
/**
|
||||
* @brief 计算拉伸适配模式
|
||||
*/
|
||||
void calculateStretch();
|
||||
|
||||
/**
|
||||
* @brief 计算居中适配模式
|
||||
*/
|
||||
void calculateCenter();
|
||||
|
||||
/**
|
||||
* @brief 计算自定义适配模式
|
||||
*/
|
||||
void calculateCustom();
|
||||
|
||||
/**
|
||||
* @brief 计算黑边区域
|
||||
*/
|
||||
void calculateLetterbox();
|
||||
|
||||
/**
|
||||
* @brief 根据黑边位置调整偏移
|
||||
* @param extraWidth 额外宽度
|
||||
* @param extraHeight 额外高度
|
||||
*/
|
||||
void applyLetterboxPosition(float extraWidth, float extraHeight);
|
||||
|
||||
ViewportConfig config_;
|
||||
ViewportResult result_;
|
||||
int screenWidth_ = 0;
|
||||
int screenHeight_ = 0;
|
||||
|
||||
mutable glm::mat4 viewportMatrix_;
|
||||
mutable glm::mat4 inverseViewportMatrix_;
|
||||
mutable bool matrixDirty_ = true;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/resources/pipeline.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class IWindow;
|
||||
class Texture;
|
||||
class FontAtlas;
|
||||
class Shader;
|
||||
|
||||
// BlendMode 定义在 pipeline.h 中
|
||||
|
||||
// ============================================================================
|
||||
// 渲染后端抽象接口
|
||||
// ============================================================================
|
||||
class RenderBackend {
|
||||
public:
|
||||
virtual ~RenderBackend() = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 生命周期
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool init(IWindow* window) = 0;
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 帧管理
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void beginFrame(const Color &clearColor) = 0;
|
||||
virtual void endFrame() = 0;
|
||||
virtual void setViewport(int x, int y, int width, int height) = 0;
|
||||
virtual void setVSync(bool enabled) = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 状态设置
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void setBlendMode(BlendMode mode) = 0;
|
||||
virtual void setViewProjection(const glm::mat4 &matrix) = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 变换矩阵栈
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void pushTransform(const glm::mat4 &transform) = 0;
|
||||
virtual void popTransform() = 0;
|
||||
virtual glm::mat4 getCurrentTransform() const = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 纹理
|
||||
// ------------------------------------------------------------------------
|
||||
virtual Ptr<Texture> createTexture(int width, int height,
|
||||
const uint8_t *pixels, int channels) = 0;
|
||||
virtual Ptr<Texture> loadTexture(const std::string &filepath) = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 精灵批渲染
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief 开始手动精灵批处理(高级用法)
|
||||
* @note 一般情况下不需要调用,drawSprite/drawText 会自动管理批处理
|
||||
*/
|
||||
virtual void beginSpriteBatch() = 0;
|
||||
virtual void drawSprite(const Texture &texture, const Rect &destRect,
|
||||
const Rect &srcRect, const Color &tint,
|
||||
float rotation, const Vec2 &anchor) = 0;
|
||||
virtual void drawSprite(const Texture &texture, const Vec2 &position,
|
||||
const Color &tint) = 0;
|
||||
virtual void endSpriteBatch() = 0;
|
||||
|
||||
/**
|
||||
* @brief 立即提交当前批处理
|
||||
* @note 手动控制批处理提交时机,一般情况下不需要调用
|
||||
*/
|
||||
virtual void flush() = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 形状渲染
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
|
||||
float width = 1.0f) = 0;
|
||||
virtual void drawRect(const Rect &rect, const Color &color,
|
||||
float width = 1.0f) = 0;
|
||||
virtual void fillRect(const Rect &rect, const Color &color) = 0;
|
||||
virtual void drawCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments = 32, float width = 1.0f) = 0;
|
||||
virtual void fillCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments = 32) = 0;
|
||||
virtual void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color, float width = 1.0f) = 0;
|
||||
virtual void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color) = 0;
|
||||
virtual void drawPolygon(const std::vector<Vec2> &points, const Color &color,
|
||||
float width = 1.0f) = 0;
|
||||
virtual void fillPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color) = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字渲染
|
||||
// ------------------------------------------------------------------------
|
||||
virtual Ptr<FontAtlas> createFontAtlas(const std::string &filepath,
|
||||
int fontSize, bool useSDF = false) = 0;
|
||||
virtual void drawText(const FontAtlas &font, const std::string &text,
|
||||
const Vec2 &position, const Color &color) = 0;
|
||||
virtual void drawText(const FontAtlas &font, const std::string &text, float x,
|
||||
float y, const Color &color) = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 统计信息
|
||||
// ------------------------------------------------------------------------
|
||||
struct Stats {
|
||||
uint32_t drawCalls = 0;
|
||||
uint32_t triangleCount = 0;
|
||||
uint32_t textureBinds = 0;
|
||||
uint32_t shaderBinds = 0;
|
||||
};
|
||||
virtual Stats getStats() const = 0;
|
||||
virtual void resetStats() = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Texture;
|
||||
class FontAtlas;
|
||||
|
||||
/**
|
||||
* @brief 渲染命令类型枚举
|
||||
*/
|
||||
enum class RenderCommandType : uint8_t {
|
||||
None = 0,
|
||||
Sprite, // 精灵绘制
|
||||
Line, // 线条绘制
|
||||
Rect, // 矩形绘制
|
||||
FilledRect, // 填充矩形
|
||||
Circle, // 圆形绘制
|
||||
FilledCircle, // 填充圆形
|
||||
Triangle, // 三角形绘制
|
||||
FilledTriangle, // 填充三角形
|
||||
Polygon, // 多边形绘制
|
||||
FilledPolygon, // 填充多边形
|
||||
Text, // 文本绘制
|
||||
Custom // 自定义绘制
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 精灵渲染命令数据
|
||||
*/
|
||||
struct SpriteCommandData {
|
||||
const Texture* texture;
|
||||
Rect destRect;
|
||||
Rect srcRect;
|
||||
Color tint;
|
||||
float rotation;
|
||||
Vec2 anchor;
|
||||
uint32_t sortKey; // 用于自动排序的键值
|
||||
|
||||
SpriteCommandData()
|
||||
: texture(nullptr), destRect(), srcRect(), tint(Colors::White),
|
||||
rotation(0.0f), anchor(0.0f, 0.0f), sortKey(0) {}
|
||||
SpriteCommandData(const Texture* tex, const Rect& dest, const Rect& src,
|
||||
const Color& t, float rot, const Vec2& anc, uint32_t key)
|
||||
: texture(tex), destRect(dest), srcRect(src), tint(t),
|
||||
rotation(rot), anchor(anc), sortKey(key) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 线条渲染命令数据
|
||||
*/
|
||||
struct LineCommandData {
|
||||
Vec2 start;
|
||||
Vec2 end;
|
||||
Color color;
|
||||
float width;
|
||||
|
||||
LineCommandData() : start(), end(), color(Colors::White), width(1.0f) {}
|
||||
LineCommandData(const Vec2& s, const Vec2& e, const Color& c, float w)
|
||||
: start(s), end(e), color(c), width(w) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 矩形渲染命令数据
|
||||
*/
|
||||
struct RectCommandData {
|
||||
Rect rect;
|
||||
Color color;
|
||||
float width;
|
||||
bool filled;
|
||||
|
||||
RectCommandData() : rect(), color(Colors::White), width(1.0f), filled(false) {}
|
||||
RectCommandData(const Rect& r, const Color& c, float w, bool f)
|
||||
: rect(r), color(c), width(w), filled(f) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 圆形渲染命令数据
|
||||
*/
|
||||
struct CircleCommandData {
|
||||
Vec2 center;
|
||||
float radius;
|
||||
Color color;
|
||||
int segments;
|
||||
float width;
|
||||
bool filled;
|
||||
|
||||
CircleCommandData() : center(), radius(0.0f), color(Colors::White),
|
||||
segments(32), width(1.0f), filled(false) {}
|
||||
CircleCommandData(const Vec2& c, float r, const Color& col, int seg, float w, bool f)
|
||||
: center(c), radius(r), color(col), segments(seg), width(w), filled(f) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 三角形渲染命令数据
|
||||
*/
|
||||
struct TriangleCommandData {
|
||||
Vec2 p1, p2, p3;
|
||||
Color color;
|
||||
float width;
|
||||
bool filled;
|
||||
|
||||
TriangleCommandData() : p1(), p2(), p3(), color(Colors::White),
|
||||
width(1.0f), filled(false) {}
|
||||
TriangleCommandData(const Vec2& a, const Vec2& b, const Vec2& c, const Color& col, float w, bool f)
|
||||
: p1(a), p2(b), p3(c), color(col), width(w), filled(f) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 多边形渲染命令数据
|
||||
*/
|
||||
struct PolygonCommandData {
|
||||
std::vector<Vec2> points;
|
||||
Color color;
|
||||
float width;
|
||||
bool filled;
|
||||
|
||||
PolygonCommandData() : color(Colors::White), width(1.0f), filled(false) {}
|
||||
PolygonCommandData(std::vector<Vec2> pts, const Color& col, float w, bool f)
|
||||
: points(std::move(pts)), color(col), width(w), filled(f) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 文本渲染命令数据
|
||||
*/
|
||||
struct TextCommandData {
|
||||
const FontAtlas* font;
|
||||
std::string text;
|
||||
Vec2 position;
|
||||
Color color;
|
||||
|
||||
TextCommandData() : font(nullptr), text(), position(), color(Colors::White) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 统一渲染命令结构
|
||||
* 使用 variant 存储不同类型的命令数据,减少内存分配
|
||||
*/
|
||||
struct RenderCommand {
|
||||
RenderCommandType type;
|
||||
uint32_t layer; // 渲染层级,用于排序
|
||||
uint32_t order; // 提交顺序,保证同层级内稳定排序
|
||||
glm::mat4 transform; // 变换矩阵
|
||||
|
||||
// 使用 variant 存储具体数据
|
||||
std::variant<
|
||||
SpriteCommandData,
|
||||
LineCommandData,
|
||||
RectCommandData,
|
||||
CircleCommandData,
|
||||
TriangleCommandData,
|
||||
PolygonCommandData,
|
||||
TextCommandData
|
||||
> data;
|
||||
|
||||
RenderCommand() : type(RenderCommandType::None), layer(0), order(0),
|
||||
transform(1.0f) {}
|
||||
|
||||
// 便捷构造函数
|
||||
static RenderCommand makeSprite(const Texture* tex, const Rect& dest,
|
||||
const Rect& src, const Color& tint,
|
||||
float rot = 0.0f, const Vec2& anc = Vec2(0, 0),
|
||||
uint32_t lyr = 0);
|
||||
static RenderCommand makeLine(const Vec2& s, const Vec2& e, const Color& c,
|
||||
float w = 1.0f, uint32_t lyr = 0);
|
||||
static RenderCommand makeRect(const Rect& r, const Color& c,
|
||||
float w = 1.0f, bool fill = false, uint32_t lyr = 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染命令缓冲区
|
||||
* 用于收集和批量处理渲染命令
|
||||
*/
|
||||
class RenderCommandBuffer {
|
||||
public:
|
||||
static constexpr size_t INITIAL_CAPACITY = 1024;
|
||||
static constexpr size_t MAX_CAPACITY = 65536;
|
||||
|
||||
RenderCommandBuffer();
|
||||
~RenderCommandBuffer();
|
||||
|
||||
// 添加渲染命令
|
||||
void addCommand(const RenderCommand& cmd);
|
||||
void addCommand(RenderCommand&& cmd);
|
||||
|
||||
// 批量添加(预留空间后使用)
|
||||
RenderCommand& emplaceCommand();
|
||||
|
||||
// 排序命令(按纹理、层级等)
|
||||
void sortCommands();
|
||||
|
||||
// 清空缓冲区
|
||||
void clear();
|
||||
|
||||
// 获取命令列表
|
||||
const std::vector<RenderCommand>& getCommands() const { return commands_; }
|
||||
std::vector<RenderCommand>& getCommands() { return commands_; }
|
||||
|
||||
// 统计
|
||||
size_t size() const { return commands_.size(); }
|
||||
bool empty() const { return commands_.empty(); }
|
||||
size_t capacity() const { return commands_.capacity(); }
|
||||
|
||||
// 预分配空间
|
||||
void reserve(size_t capacity);
|
||||
|
||||
private:
|
||||
std::vector<RenderCommand> commands_;
|
||||
uint32_t nextOrder_;
|
||||
|
||||
// 排序比较函数
|
||||
static bool compareCommands(const RenderCommand& a, const RenderCommand& b);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <functional>
|
||||
#include <typeindex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 渲染模块配置结构
|
||||
*/
|
||||
struct RenderCfg {
|
||||
std::string backend;
|
||||
int targetFPS;
|
||||
bool vsync;
|
||||
int multisamples;
|
||||
int priority;
|
||||
|
||||
RenderCfg()
|
||||
: backend("")
|
||||
, targetFPS(60)
|
||||
, vsync(true)
|
||||
, multisamples(0)
|
||||
, priority(10)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染模块
|
||||
* 管理渲染后端,自动根据窗口后端选择兼容的渲染器
|
||||
*/
|
||||
class RenderModule : public Module {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数(Lambda 配置)
|
||||
* @param configFn 配置函数
|
||||
*/
|
||||
explicit RenderModule(std::function<void(RenderCfg&)> configFn);
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~RenderModule() override;
|
||||
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
bool ok() const override { return initialized_; }
|
||||
const char* name() const override { return "render"; }
|
||||
int priority() const override { return cfg_.priority; }
|
||||
|
||||
/**
|
||||
* @brief 获取依赖
|
||||
* @return 依赖模块类型列表
|
||||
*/
|
||||
std::vector<std::type_index> deps() const override {
|
||||
return {std::type_index(typeid(WindowModule))};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 是否允许并行初始化
|
||||
* RenderModule 需要 OpenGL 上下文,必须在主线程初始化
|
||||
* @return 不允许并行初始化返回 false
|
||||
*/
|
||||
bool allowParallelInit() const override { return false; }
|
||||
|
||||
/**
|
||||
* @brief 获取渲染器
|
||||
* @return 渲染后端指针
|
||||
*/
|
||||
RenderBackend* renderer() const { return renderer_.get(); }
|
||||
|
||||
private:
|
||||
RenderCfg cfg_;
|
||||
UniquePtr<RenderBackend> renderer_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,313 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 渲染目标配置
|
||||
// ============================================================================
|
||||
struct RenderTargetConfig {
|
||||
int width = 800; // 宽度
|
||||
int height = 600; // 高度
|
||||
PixelFormat colorFormat = PixelFormat::RGBA8; // 颜色格式
|
||||
bool hasDepth = true; // 是否包含深度缓冲
|
||||
bool hasStencil = false; // 是否包含模板缓冲
|
||||
int samples = 1; // 多重采样数 (1 = 无MSAA)
|
||||
bool autoResize = true; // 是否自动调整大小
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 渲染目标 - 基于FBO的离屏渲染
|
||||
// ============================================================================
|
||||
class RenderTarget {
|
||||
public:
|
||||
RenderTarget();
|
||||
~RenderTarget();
|
||||
|
||||
// 禁止拷贝
|
||||
RenderTarget(const RenderTarget &) = delete;
|
||||
RenderTarget &operator=(const RenderTarget &) = delete;
|
||||
|
||||
// 允许移动
|
||||
RenderTarget(RenderTarget &&other) noexcept;
|
||||
RenderTarget &operator=(RenderTarget &&other) noexcept;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 创建和销毁
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 创建渲染目标
|
||||
*/
|
||||
bool create(const RenderTargetConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 从现有纹理创建渲染目标
|
||||
*/
|
||||
bool createFromTexture(Ptr<Texture> texture, bool hasDepth = false);
|
||||
|
||||
/**
|
||||
* @brief 销毁渲染目标
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 检查是否有效
|
||||
*/
|
||||
bool isValid() const { return fbo_ != 0; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 尺寸和格式
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
int getWidth() const { return width_; }
|
||||
int getHeight() const { return height_; }
|
||||
Vec2 getSize() const {
|
||||
return Vec2(static_cast<float>(width_), static_cast<float>(height_));
|
||||
}
|
||||
PixelFormat getColorFormat() const { return colorFormat_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 绑定和解绑
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 绑定为当前渲染目标
|
||||
*/
|
||||
void bind();
|
||||
|
||||
/**
|
||||
* @brief 解绑(恢复默认渲染目标)
|
||||
*/
|
||||
void unbind();
|
||||
|
||||
/**
|
||||
* @brief 清除渲染目标
|
||||
*/
|
||||
void clear(const Color &color = Colors::Transparent);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 纹理访问
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取颜色纹理
|
||||
*/
|
||||
Ptr<Texture> getColorTexture() const { return colorTexture_; }
|
||||
|
||||
/**
|
||||
* @brief 获取深度纹理(如果有)
|
||||
*/
|
||||
Ptr<Texture> getDepthTexture() const { return depthTexture_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 视口和裁剪
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置视口(相对于渲染目标)
|
||||
*/
|
||||
void setViewport(int x, int y, int width, int height);
|
||||
|
||||
/**
|
||||
* @brief 获取完整视口
|
||||
*/
|
||||
void getFullViewport(int &x, int &y, int &width, int &height) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 工具方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 调整大小(会销毁并重新创建)
|
||||
*/
|
||||
bool resize(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief 复制到另一个渲染目标
|
||||
*/
|
||||
void copyTo(RenderTarget &target);
|
||||
|
||||
/**
|
||||
* @brief 复制到屏幕
|
||||
*/
|
||||
void copyToScreen(int screenWidth, int screenHeight);
|
||||
|
||||
/**
|
||||
* @brief 保存为图像文件
|
||||
*/
|
||||
bool saveToFile(const std::string &filepath);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 创建渲染目标的静态工厂方法
|
||||
*/
|
||||
static Ptr<RenderTarget> createFromConfig(const RenderTargetConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 获取当前绑定的渲染目标ID
|
||||
*/
|
||||
static GLuint getCurrentFBO();
|
||||
|
||||
/**
|
||||
* @brief 绑定默认渲染目标(屏幕)
|
||||
*/
|
||||
static void bindDefault();
|
||||
|
||||
/**
|
||||
* @brief 获取FBO ID(供内部使用)
|
||||
*/
|
||||
GLuint getFBO() const { return fbo_; }
|
||||
|
||||
protected:
|
||||
GLuint fbo_ = 0; // 帧缓冲对象
|
||||
GLuint rbo_ = 0; // 渲染缓冲对象(深度/模板)
|
||||
|
||||
Ptr<Texture> colorTexture_; // 颜色纹理
|
||||
Ptr<Texture> depthTexture_; // 深度纹理(可选)
|
||||
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
PixelFormat colorFormat_ = PixelFormat::RGBA8;
|
||||
bool hasDepth_ = false;
|
||||
bool hasStencil_ = false;
|
||||
int samples_ = 1;
|
||||
|
||||
bool createFBO();
|
||||
void deleteFBO();
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 多重采样渲染目标(用于MSAA)
|
||||
// ============================================================================
|
||||
class MultisampleRenderTarget : public RenderTarget {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建多重采样渲染目标
|
||||
*/
|
||||
bool create(int width, int height, int samples = 4);
|
||||
|
||||
/**
|
||||
* @brief 解析到普通渲染目标(用于显示)
|
||||
*/
|
||||
void resolveTo(RenderTarget &target);
|
||||
|
||||
/**
|
||||
* @brief 销毁渲染目标
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
GLuint colorRBO_ = 0; // 多重采样颜色渲染缓冲
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 渲染目标栈(用于嵌套渲染)
|
||||
// ============================================================================
|
||||
class RenderTargetStack {
|
||||
public:
|
||||
static RenderTargetStack &get();
|
||||
|
||||
/**
|
||||
* @brief 压入渲染目标
|
||||
*/
|
||||
void push(RenderTarget *target);
|
||||
|
||||
/**
|
||||
* @brief 弹出渲染目标
|
||||
*/
|
||||
void pop();
|
||||
|
||||
/**
|
||||
* @brief 获取当前渲染目标
|
||||
*/
|
||||
RenderTarget *getCurrent() const;
|
||||
|
||||
/**
|
||||
* @brief 获取栈大小
|
||||
*/
|
||||
size_t size() const;
|
||||
|
||||
/**
|
||||
* @brief 清空栈
|
||||
*/
|
||||
void clear();
|
||||
|
||||
private:
|
||||
RenderTargetStack() = default;
|
||||
~RenderTargetStack() = default;
|
||||
|
||||
std::vector<RenderTarget *> stack_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 渲染目标管理器 - 全局渲染目标管理
|
||||
// ============================================================================
|
||||
class RenderTargetMgr {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static RenderTargetMgr& get();
|
||||
|
||||
/**
|
||||
* @brief 初始化渲染目标管理器
|
||||
* @param width 默认宽度
|
||||
* @param height 默认高度
|
||||
*/
|
||||
bool init(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief 关闭渲染目标管理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 创建新的渲染目标
|
||||
*/
|
||||
Ptr<RenderTarget> createRenderTarget(const RenderTargetConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 获取默认渲染目标
|
||||
*/
|
||||
RenderTarget *getDefaultRenderTarget() const {
|
||||
return defaultRenderTarget_.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 调整所有受管渲染目标的大小
|
||||
*/
|
||||
void resize(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
RenderTargetMgr() = default;
|
||||
~RenderTargetMgr() = default;
|
||||
RenderTargetMgr(const RenderTargetMgr &) = delete;
|
||||
RenderTargetMgr &operator=(const RenderTargetMgr &) = delete;
|
||||
|
||||
Ptr<RenderTarget> defaultRenderTarget_;
|
||||
std::vector<Ptr<RenderTarget>> renderTargets_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 便捷宏
|
||||
// ============================================================================
|
||||
#define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::get()
|
||||
#define E2D_RENDER_TARGET_MGR() ::extra2d::RenderTargetMgr::get()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// GPU 上下文状态管理器
|
||||
// 用于跟踪 OpenGL/Vulkan 等 GPU 上下文的生命周期状态
|
||||
// 确保在 GPU 资源析构时能安全地检查上下文是否有效
|
||||
// ============================================================================
|
||||
|
||||
class GPUContext {
|
||||
public:
|
||||
/// 获取单例实例
|
||||
static GPUContext& get();
|
||||
|
||||
/// 标记 GPU 上下文为有效(在初始化完成后调用)
|
||||
void markValid();
|
||||
|
||||
/// 标记 GPU 上下文为无效(在销毁前调用)
|
||||
void markInvalid();
|
||||
|
||||
/// 检查 GPU 上下文是否有效
|
||||
bool isValid() const;
|
||||
|
||||
private:
|
||||
GPUContext() = default;
|
||||
~GPUContext() = default;
|
||||
GPUContext(const GPUContext&) = delete;
|
||||
GPUContext& operator=(const GPUContext&) = delete;
|
||||
|
||||
std::atomic<bool> valid_{false};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// VRAM 管理器 - 跟踪显存使用情况
|
||||
// ============================================================================
|
||||
class VRAMMgr {
|
||||
public:
|
||||
static VRAMMgr& get();
|
||||
|
||||
// 纹理显存跟踪
|
||||
void allocTexture(size_t size);
|
||||
void freeTexture(size_t size);
|
||||
|
||||
// VBO/FBO 显存跟踪
|
||||
void allocBuffer(size_t size);
|
||||
void freeBuffer(size_t size);
|
||||
|
||||
// 查询显存使用情况
|
||||
size_t getUsedVRAM() const;
|
||||
size_t getTextureVRAM() const;
|
||||
size_t getBufferVRAM() const;
|
||||
size_t getAvailableVRAM() const;
|
||||
|
||||
// 显存预算管理
|
||||
void setVRAMBudget(size_t budget);
|
||||
size_t getVRAMBudget() const;
|
||||
bool isOverBudget() const;
|
||||
|
||||
// 统计信息
|
||||
void printStats() const;
|
||||
|
||||
// 重置计数器
|
||||
void reset();
|
||||
|
||||
private:
|
||||
VRAMMgr();
|
||||
~VRAMMgr() = default;
|
||||
VRAMMgr(const VRAMMgr&) = delete;
|
||||
VRAMMgr& operator=(const VRAMMgr&) = delete;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
size_t textureVRAM_;
|
||||
size_t bufferVRAM_;
|
||||
size_t vramBudget_;
|
||||
|
||||
// 统计
|
||||
uint32_t textureAllocCount_;
|
||||
uint32_t textureFreeCount_;
|
||||
uint32_t bufferAllocCount_;
|
||||
uint32_t bufferFreeCount_;
|
||||
size_t peakTextureVRAM_;
|
||||
size_t peakBufferVRAM_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 缓冲区类型枚举
|
||||
// ============================================================================
|
||||
enum class BufferType {
|
||||
Vertex, // 顶点缓冲
|
||||
Index, // 索引缓冲
|
||||
Uniform // 统一缓冲
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 缓冲区使用模式枚举
|
||||
// ============================================================================
|
||||
enum class BufferUsage {
|
||||
Static, // 静态数据,很少更新
|
||||
Dynamic, // 动态数据,频繁更新
|
||||
Stream // 流式数据,每帧更新
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 缓冲区描述结构
|
||||
// ============================================================================
|
||||
struct BufferDesc {
|
||||
BufferType type = BufferType::Vertex;
|
||||
BufferUsage usage = BufferUsage::Static;
|
||||
size_t size = 0; // 缓冲区大小(字节)
|
||||
const void* initialData = nullptr; // 初始数据
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 缓冲区抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class Buffer {
|
||||
public:
|
||||
virtual ~Buffer() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定缓冲区
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑缓冲区
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置缓冲区数据(完全替换)
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小(字节)
|
||||
*/
|
||||
virtual void setData(const void* data, size_t size) = 0;
|
||||
|
||||
/**
|
||||
* @brief 更新缓冲区部分数据
|
||||
* @param data 数据指针
|
||||
* @param offset 偏移量(字节)
|
||||
* @param size 数据大小(字节)
|
||||
*/
|
||||
virtual void updateData(const void* data, size_t offset, size_t size) = 0;
|
||||
|
||||
/**
|
||||
* @brief 映射缓冲区到内存(用于直接写入)
|
||||
* @return 映射后的内存指针,失败返回 nullptr
|
||||
*/
|
||||
virtual void* map() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解除缓冲区映射
|
||||
*/
|
||||
virtual void unmap() = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区大小
|
||||
* @return 缓冲区大小(字节)
|
||||
*/
|
||||
virtual size_t getSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区类型
|
||||
* @return 缓冲区类型
|
||||
*/
|
||||
virtual BufferType getType() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区使用模式
|
||||
* @return 使用模式
|
||||
*/
|
||||
virtual BufferUsage getUsage() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查缓冲区是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(后端特定)
|
||||
* @return 原生句柄值
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 字形信息结构
|
||||
// ============================================================================
|
||||
struct Glyph {
|
||||
float width = 0; // 字形宽度
|
||||
float height = 0; // 字形高度
|
||||
float bearingX = 0; // 水平偏移
|
||||
float bearingY = 0; // 垂直偏移(从基线到字形顶部)
|
||||
float advance = 0; // 水平步进
|
||||
float u0 = 0, v0 = 0; // 纹理坐标左下角
|
||||
float u1 = 0, v1 = 0; // 纹理坐标右上角
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 字体图集描述结构
|
||||
// ============================================================================
|
||||
struct FontAtlasDesc {
|
||||
std::string filepath; // 字体文件路径
|
||||
int fontSize = 16; // 字体大小
|
||||
bool useSDF = false; // 是否使用SDF渲染
|
||||
int atlasSize = 512; // 图集大小
|
||||
int padding = 2; // 字形间距
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 字体图集抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class FontAtlas {
|
||||
public:
|
||||
virtual ~FontAtlas() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化字体图集
|
||||
* @param desc 字体图集描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
virtual bool init(const FontAtlasDesc& desc) = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭字体图集,释放资源
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取字形信息
|
||||
* @param codepoint Unicode 码点
|
||||
* @return 字形信息指针,未找到返回 nullptr
|
||||
*/
|
||||
virtual const Glyph* getGlyph(char32_t codepoint) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取纹理
|
||||
* @return 纹理对象
|
||||
*/
|
||||
virtual Ptr<Texture> getTexture() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取字体大小
|
||||
* @return 字体大小
|
||||
*/
|
||||
virtual int getFontSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取行高
|
||||
* @return 行高
|
||||
*/
|
||||
virtual float getLineHeight() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取基线到顶部的距离
|
||||
* @return 上升高度
|
||||
*/
|
||||
virtual float getAscent() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取基线到底部的距离
|
||||
* @return 下降高度
|
||||
*/
|
||||
virtual float getDescent() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 计算文本宽度
|
||||
* @param text 文本内容
|
||||
* @return 文本宽度
|
||||
*/
|
||||
virtual float measureText(const std::string& text) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 计算文本尺寸
|
||||
* @param text 文本内容
|
||||
* @return 文本尺寸
|
||||
*/
|
||||
virtual Size measureTextSize(const std::string& text) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 是否使用SDF渲染
|
||||
* @return 使用SDF返回 true
|
||||
*/
|
||||
virtual bool isSDF() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查字体图集是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 预加载字符到图集
|
||||
* @param text 需要预加载的文本
|
||||
* @return 成功加载的字符数
|
||||
*/
|
||||
virtual int preloadGlyphs(const std::string& text) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清空已加载的字形缓存
|
||||
*/
|
||||
virtual void clearCache() = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 帧缓冲描述结构
|
||||
// ============================================================================
|
||||
struct FramebufferDesc {
|
||||
int width = 0; // 帧缓冲宽度
|
||||
int height = 0; // 帧缓冲高度
|
||||
int colorAttachments = 1; // 颜色附件数量
|
||||
bool hasDepth = false; // 是否有深度附件
|
||||
bool hasStencil = false; // 是否有模板附件
|
||||
bool multisample = false; // 是否多重采样
|
||||
int samples = 4; // 采样数(多重采样时有效)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 帧缓冲抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class Framebuffer {
|
||||
public:
|
||||
virtual ~Framebuffer() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定帧缓冲(作为渲染目标)
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑帧缓冲(恢复到默认帧缓冲)
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 附加颜色纹理
|
||||
* @param texture 纹理对象
|
||||
* @param attachment 附件索引(0-7)
|
||||
*/
|
||||
virtual void attachColorTexture(Ptr<Texture> texture, int attachment = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 附加深度纹理
|
||||
* @param texture 纹理对象
|
||||
*/
|
||||
virtual void attachDepthTexture(Ptr<Texture> texture) = 0;
|
||||
|
||||
/**
|
||||
* @brief 附加深度模板纹理
|
||||
* @param texture 纹理对象
|
||||
*/
|
||||
virtual void attachDepthStencilTexture(Ptr<Texture> texture) = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查帧缓冲是否完整
|
||||
* @return 完整返回 true
|
||||
*/
|
||||
virtual bool isComplete() = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取颜色附件纹理
|
||||
* @param attachment 附件索引
|
||||
* @return 纹理对象
|
||||
*/
|
||||
virtual Ptr<Texture> getColorTexture(int attachment = 0) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取深度附件纹理
|
||||
* @return 纹理对象
|
||||
*/
|
||||
virtual Ptr<Texture> getDepthTexture() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取帧缓冲宽度
|
||||
* @return 宽度
|
||||
*/
|
||||
virtual int getWidth() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取帧缓冲高度
|
||||
* @return 高度
|
||||
*/
|
||||
virtual int getHeight() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取尺寸
|
||||
* @return 尺寸
|
||||
*/
|
||||
virtual Size getSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查帧缓冲是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(后端特定)
|
||||
* @return 原生句柄值
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除帧缓冲
|
||||
* @param color 清除颜色
|
||||
* @param clearColor 是否清除颜色缓冲
|
||||
* @param clearDepth 是否清除深度缓冲
|
||||
* @param clearStencil 是否清除模板缓冲
|
||||
*/
|
||||
virtual void clear(const Color& color, bool clearColor = true,
|
||||
bool clearDepth = true, bool clearStencil = false) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置视口
|
||||
* @param x 视口左下角X坐标
|
||||
* @param y 视口左下角Y坐标
|
||||
* @param width 视口宽度
|
||||
* @param height 视口高度
|
||||
*/
|
||||
virtual void setViewport(int x, int y, int width, int height) = 0;
|
||||
|
||||
/**
|
||||
* @brief 读取像素数据
|
||||
* @param x 起始X坐标
|
||||
* @param y 起始Y坐标
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param outData 输出数据缓冲区
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
virtual bool readPixels(int x, int y, int width, int height,
|
||||
std::vector<uint8_t>& outData) = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 混合模式枚举
|
||||
// ============================================================================
|
||||
enum class BlendMode {
|
||||
None, // 不混合
|
||||
Alpha, // 标准 Alpha 混合
|
||||
Additive, // 加法混合
|
||||
Multiply // 乘法混合
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 深度测试函数枚举
|
||||
// ============================================================================
|
||||
enum class DepthFunc {
|
||||
Never, // 永不通过
|
||||
Less, // 小于
|
||||
Equal, // 等于
|
||||
LessEqual, // 小于等于
|
||||
Greater, // 大于
|
||||
NotEqual, // 不等于
|
||||
GreaterEqual,// 大于等于
|
||||
Always // 总是通过
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 裁剪模式枚举
|
||||
// ============================================================================
|
||||
enum class CullMode {
|
||||
None, // 不裁剪
|
||||
Front, // 裁剪正面
|
||||
Back, // 裁剪背面
|
||||
Both // 裁剪双面
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 顶点属性格式枚举
|
||||
// ============================================================================
|
||||
enum class VertexFormat {
|
||||
Float1, // 1个float
|
||||
Float2, // 2个float
|
||||
Float3, // 3个float
|
||||
Float4, // 4个float
|
||||
Byte4, // 4个byte
|
||||
UByte4, // 4个ubyte
|
||||
Short2, // 2个short
|
||||
Short4 // 4个short
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 顶点属性描述
|
||||
// ============================================================================
|
||||
struct VertexAttribute {
|
||||
uint32_t location = 0; // 属性位置
|
||||
VertexFormat format = VertexFormat::Float3; // 数据格式
|
||||
uint32_t offset = 0; // 在顶点结构中的偏移
|
||||
uint32_t stride = 0; // 顶点结构大小
|
||||
bool normalized = false; // 是否归一化
|
||||
|
||||
VertexAttribute() = default;
|
||||
VertexAttribute(uint32_t loc, VertexFormat fmt, uint32_t off, uint32_t str, bool norm = false)
|
||||
: location(loc), format(fmt), offset(off), stride(str), normalized(norm) {}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 管线描述结构
|
||||
// ============================================================================
|
||||
struct PipelineDesc {
|
||||
// 混合状态
|
||||
BlendMode blendMode = BlendMode::Alpha;
|
||||
bool blendEnabled = true;
|
||||
|
||||
// 深度状态
|
||||
bool depthTest = false;
|
||||
bool depthWrite = false;
|
||||
DepthFunc depthFunc = DepthFunc::Less;
|
||||
|
||||
// 裁剪状态
|
||||
CullMode cullMode = CullMode::None;
|
||||
|
||||
// 顶点布局
|
||||
std::vector<VertexAttribute> vertexAttributes;
|
||||
|
||||
// 着色器(由后端特定实现设置)
|
||||
void* vertexShader = nullptr;
|
||||
void* fragmentShader = nullptr;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 渲染管线抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class Pipeline {
|
||||
public:
|
||||
virtual ~Pipeline() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定管线
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑管线
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置混合模式
|
||||
* @param mode 混合模式
|
||||
*/
|
||||
virtual void setBlendMode(BlendMode mode) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取当前混合模式
|
||||
* @return 混合模式
|
||||
*/
|
||||
virtual BlendMode getBlendMode() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置深度测试
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
virtual void setDepthTest(bool enabled) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置深度写入
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
virtual void setDepthWrite(bool enabled) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置深度测试函数
|
||||
* @param func 深度测试函数
|
||||
*/
|
||||
virtual void setDepthFunc(DepthFunc func) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置裁剪模式
|
||||
* @param mode 裁剪模式
|
||||
*/
|
||||
virtual void setCullMode(CullMode mode) = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查管线是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(后端特定)
|
||||
* @return 原生句柄值
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 着色器类型枚举
|
||||
// ============================================================================
|
||||
enum class ShaderType {
|
||||
Vertex, // 顶点着色器
|
||||
Fragment, // 片段着色器
|
||||
Geometry, // 几何着色器
|
||||
Compute // 计算着色器
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 着色器描述结构
|
||||
// ============================================================================
|
||||
struct ShaderDesc {
|
||||
std::string name; // 着色器名称
|
||||
std::string vertexSource; // 顶点着色器源码
|
||||
std::string fragmentSource; // 片段着色器源码
|
||||
std::string geometrySource; // 几何着色器源码(可选)
|
||||
std::vector<uint8_t> binaryData; // 预编译二进制数据(可选)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 着色器抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class Shader {
|
||||
public:
|
||||
virtual ~Shader() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定着色器程序
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑着色器程序
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置布尔类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 布尔值
|
||||
*/
|
||||
virtual void setBool(const std::string& name, bool value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置整数类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 整数值
|
||||
*/
|
||||
virtual void setInt(const std::string& name, int value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置浮点类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 浮点值
|
||||
*/
|
||||
virtual void setFloat(const std::string& name, float value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 二维向量值
|
||||
*/
|
||||
virtual void setVec2(const std::string& name, const glm::vec2& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 三维向量值
|
||||
*/
|
||||
virtual void setVec3(const std::string& name, const glm::vec3& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 四维向量值
|
||||
*/
|
||||
virtual void setVec4(const std::string& name, const glm::vec4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置 4x4 矩阵类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 4x4 矩阵值
|
||||
*/
|
||||
virtual void setMat4(const std::string& name, const glm::mat4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置颜色类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param color 颜色值
|
||||
*/
|
||||
virtual void setColor(const std::string& name, const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置纹理采样器
|
||||
* @param name uniform 变量名
|
||||
* @param slot 纹理槽位
|
||||
*/
|
||||
virtual void setTexture(const std::string& name, int slot) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取着色器名称
|
||||
* @return 着色器名称
|
||||
*/
|
||||
virtual const std::string& getName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查着色器是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(后端特定)
|
||||
* @return 原生句柄值
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Shader缓存条目
|
||||
// ============================================================================
|
||||
struct ShaderCacheEntry {
|
||||
std::string name;
|
||||
std::string sourceHash;
|
||||
uint64_t compileTime = 0;
|
||||
std::vector<uint8_t> binary;
|
||||
std::vector<std::string> dependencies;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Shader缓存管理器
|
||||
// ============================================================================
|
||||
class ShaderCache {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 缓存管理器实例引用
|
||||
*/
|
||||
static ShaderCache& getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化缓存系统
|
||||
* @param cacheDir 缓存目录路径
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool init(const std::string& cacheDir);
|
||||
|
||||
/**
|
||||
* @brief 关闭缓存系统
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查缓存是否有效
|
||||
* @param name Shader名称
|
||||
* @param sourceHash 源码哈希值
|
||||
* @return 缓存有效返回true,否则返回false
|
||||
*/
|
||||
bool hasValidCache(const std::string& name, const std::string& sourceHash);
|
||||
|
||||
/**
|
||||
* @brief 加载缓存的二进制数据
|
||||
* @param name Shader名称
|
||||
* @return 缓存条目指针,不存在返回nullptr
|
||||
*/
|
||||
Ptr<ShaderCacheEntry> loadCache(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 保存编译结果到缓存
|
||||
* @param entry 缓存条目
|
||||
* @return 保存成功返回true,失败返回false
|
||||
*/
|
||||
bool saveCache(const ShaderCacheEntry& entry);
|
||||
|
||||
/**
|
||||
* @brief 使缓存失效
|
||||
* @param name Shader名称
|
||||
*/
|
||||
void invalidate(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 清除所有缓存
|
||||
*/
|
||||
void clearAll();
|
||||
|
||||
/**
|
||||
* @brief 计算源码哈希值
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 哈希值字符串
|
||||
*/
|
||||
static std::string computeHash(const std::string& vertSource,
|
||||
const std::string& fragSource);
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 已初始化返回true,否则返回false
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
ShaderCache() = default;
|
||||
~ShaderCache() = default;
|
||||
ShaderCache(const ShaderCache&) = delete;
|
||||
ShaderCache& operator=(const ShaderCache&) = delete;
|
||||
|
||||
std::string cacheDir_;
|
||||
std::unordered_map<std::string, ShaderCacheEntry> cacheMap_;
|
||||
bool initialized_ = false;
|
||||
|
||||
/**
|
||||
* @brief 加载缓存索引
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool loadCacheIndex();
|
||||
|
||||
/**
|
||||
* @brief 保存缓存索引
|
||||
* @return 保存成功返回true,失败返回false
|
||||
*/
|
||||
bool saveCacheIndex();
|
||||
|
||||
/**
|
||||
* @brief 获取缓存文件路径
|
||||
* @param name Shader名称
|
||||
* @return 缓存文件完整路径
|
||||
*/
|
||||
std::string getCachePath(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 确保缓存目录存在
|
||||
* @return 目录存在或创建成功返回true,否则返回false
|
||||
*/
|
||||
bool ensureCacheDirectory();
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_SHADER_CACHE() ::extra2d::ShaderCache::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 文件变化事件
|
||||
// ============================================================================
|
||||
struct FileChangeEvent {
|
||||
std::string filepath;
|
||||
|
||||
enum class Type { Created, Modified, Deleted, Renamed } type;
|
||||
|
||||
uint64_t timestamp = 0;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 文件变化回调
|
||||
// ============================================================================
|
||||
using FileChangeCallback = std::function<void(const FileChangeEvent &)>;
|
||||
|
||||
// ============================================================================
|
||||
// Shader热重载管理器
|
||||
// ============================================================================
|
||||
class ShaderHotReloader {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 热重载管理器实例引用
|
||||
*/
|
||||
static ShaderHotReloader &getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化热重载系统
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭热重载系统
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 注册Shader文件监视
|
||||
* @param shaderName Shader名称
|
||||
* @param filePaths 要监视的文件列表
|
||||
* @param callback 文件变化时的回调
|
||||
*/
|
||||
void watch(const std::string &shaderName,
|
||||
const std::vector<std::string> &filePaths,
|
||||
FileChangeCallback callback);
|
||||
|
||||
/**
|
||||
* @brief 取消监视
|
||||
* @param shaderName Shader名称
|
||||
*/
|
||||
void unwatch(const std::string &shaderName);
|
||||
|
||||
/**
|
||||
* @brief 更新文件监视(在主循环中调用)
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用热重载
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 检查是否启用
|
||||
* @return 启用返回true,否则返回false
|
||||
*/
|
||||
bool isEnabled() const { return enabled_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 已初始化返回true,否则返回false
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
ShaderHotReloader() = default;
|
||||
~ShaderHotReloader() = default;
|
||||
ShaderHotReloader(const ShaderHotReloader &) = delete;
|
||||
ShaderHotReloader &operator=(const ShaderHotReloader &) = delete;
|
||||
|
||||
bool enabled_ = false;
|
||||
bool initialized_ = false;
|
||||
|
||||
struct WatchInfo {
|
||||
std::vector<std::string> filePaths;
|
||||
FileChangeCallback callback;
|
||||
std::unordered_map<std::string, uint64_t> modifiedTimes;
|
||||
};
|
||||
std::unordered_map<std::string, WatchInfo> watchMap_;
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE watchHandle_ = nullptr;
|
||||
std::vector<uint8_t> buffer_;
|
||||
std::string watchDir_;
|
||||
bool watching_ = false;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 轮询检查文件变化
|
||||
*/
|
||||
void pollChanges();
|
||||
|
||||
/**
|
||||
* @brief 获取文件修改时间
|
||||
* @param filepath 文件路径
|
||||
* @return 修改时间戳
|
||||
*/
|
||||
static uint64_t getFileModifiedTime(const std::string &filepath);
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_SHADER_HOT_RELOADER() ::extra2d::ShaderHotReloader::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Color;
|
||||
|
||||
// ============================================================================
|
||||
// Shader抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class IShader {
|
||||
public:
|
||||
virtual ~IShader() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定Shader程序
|
||||
*/
|
||||
virtual void bind() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑Shader程序
|
||||
*/
|
||||
virtual void unbind() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置布尔类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 布尔值
|
||||
*/
|
||||
virtual void setBool(const std::string& name, bool value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置整数类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 整数值
|
||||
*/
|
||||
virtual void setInt(const std::string& name, int value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置浮点类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 浮点值
|
||||
*/
|
||||
virtual void setFloat(const std::string& name, float value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 二维向量值
|
||||
*/
|
||||
virtual void setVec2(const std::string& name, const glm::vec2& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 三维向量值
|
||||
*/
|
||||
virtual void setVec3(const std::string& name, const glm::vec3& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 四维向量值
|
||||
*/
|
||||
virtual void setVec4(const std::string& name, const glm::vec4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置4x4矩阵类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 4x4矩阵值
|
||||
*/
|
||||
virtual void setMat4(const std::string& name, const glm::mat4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置颜色类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param color 颜色值
|
||||
*/
|
||||
virtual void setColor(const std::string& name, const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查Shader是否有效
|
||||
* @return 有效返回true,否则返回false
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(如OpenGL程序ID)
|
||||
* @return 原生句柄值
|
||||
*/
|
||||
virtual uint32_t getNativeHandle() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader名称
|
||||
* @return Shader名称
|
||||
*/
|
||||
virtual const std::string& getName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置Shader名称
|
||||
* @param name Shader名称
|
||||
*/
|
||||
virtual void setName(const std::string& name) = 0;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Shader工厂接口 - 用于创建渲染后端特定的Shader实例
|
||||
// ============================================================================
|
||||
class IShaderFactory {
|
||||
public:
|
||||
virtual ~IShaderFactory() = default;
|
||||
|
||||
/**
|
||||
* @brief 从源码创建Shader
|
||||
* @param name Shader名称
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
virtual Ptr<IShader> createFromSource(
|
||||
const std::string& name,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从缓存二进制创建Shader
|
||||
* @param name Shader名称
|
||||
* @param binary 编译后的二进制数据
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
virtual Ptr<IShader> createFromBinary(
|
||||
const std::string& name,
|
||||
const std::vector<uint8_t>& binary) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader的二进制数据(用于缓存)
|
||||
* @param shader Shader实例
|
||||
* @param outBinary 输出的二进制数据
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
virtual bool getShaderBinary(const IShader& shader,
|
||||
std::vector<uint8_t>& outBinary) = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,294 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Shader加载结果
|
||||
// ============================================================================
|
||||
struct ShaderLoadResult {
|
||||
bool success = false;
|
||||
std::string errorMessage;
|
||||
std::string vertSource;
|
||||
std::string fragSource;
|
||||
std::vector<std::string> dependencies;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Shader Uniform 定义
|
||||
// ============================================================================
|
||||
struct ShaderUniformDef {
|
||||
std::string type;
|
||||
std::string description;
|
||||
float defaultValue = 0.0f; // 默认值(用于float类型)
|
||||
float defaultVec2[2] = {0, 0}; // 默认值(用于vec2类型)
|
||||
float defaultVec3[3] = {0, 0, 0}; // 默认值(用于vec3类型)
|
||||
float defaultVec4[4] = {0, 0, 0, 0}; // 默认值(用于vec4类型)
|
||||
float defaultMat4[16] = {
|
||||
1, 0, 0, 0, 0, 1, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 1}; // 默认值(用于mat4类型,默认单位矩阵)
|
||||
int defaultInt = 0; // 默认值(用于int类型)
|
||||
bool defaultBool = false; // 默认值(用于bool类型)
|
||||
bool hasDefault = false; // 是否有默认值
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Shader Uniform 值类型
|
||||
// ============================================================================
|
||||
struct ShaderUniformValue {
|
||||
enum class Type { Float, Int, Bool, Vec2, Vec3, Vec4, Mat4 } type;
|
||||
|
||||
union {
|
||||
float f[16]; // 足够存储 mat4
|
||||
int i;
|
||||
bool b;
|
||||
} data;
|
||||
|
||||
// 构造函数
|
||||
ShaderUniformValue() : type(Type::Float) { data.f[0] = 0; }
|
||||
ShaderUniformValue(float v) : type(Type::Float) { data.f[0] = v; }
|
||||
ShaderUniformValue(int v) : type(Type::Int) { data.i = v; }
|
||||
ShaderUniformValue(bool v) : type(Type::Bool) { data.b = v; }
|
||||
ShaderUniformValue(const glm::vec2 &v) : type(Type::Vec2) {
|
||||
data.f[0] = v.x;
|
||||
data.f[1] = v.y;
|
||||
}
|
||||
ShaderUniformValue(const glm::vec3 &v) : type(Type::Vec3) {
|
||||
data.f[0] = v.x;
|
||||
data.f[1] = v.y;
|
||||
data.f[2] = v.z;
|
||||
}
|
||||
ShaderUniformValue(const glm::vec4 &v) : type(Type::Vec4) {
|
||||
data.f[0] = v.x;
|
||||
data.f[1] = v.y;
|
||||
data.f[2] = v.z;
|
||||
data.f[3] = v.w;
|
||||
}
|
||||
ShaderUniformValue(const glm::mat4 &m) : type(Type::Mat4) {
|
||||
const float *ptr = glm::value_ptr(m);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
data.f[i] = ptr[i];
|
||||
}
|
||||
};
|
||||
|
||||
// Uniform 值映射表
|
||||
using UniformValueMap = std::unordered_map<std::string, ShaderUniformValue>;
|
||||
|
||||
// ============================================================================
|
||||
// Shader Sampler 定义
|
||||
// ============================================================================
|
||||
struct ShaderSamplerDef {
|
||||
std::string type;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Shader元数据
|
||||
// ============================================================================
|
||||
struct ShaderMetadata {
|
||||
std::string name;
|
||||
std::string category;
|
||||
std::string version;
|
||||
std::string description;
|
||||
std::string vertPath;
|
||||
std::string fragPath;
|
||||
std::string combinedPath;
|
||||
uint64_t lastModified = 0;
|
||||
std::vector<std::string> defines;
|
||||
std::unordered_map<std::string, std::string> uniforms;
|
||||
std::unordered_map<std::string, ShaderUniformDef> uniformDefs;
|
||||
std::unordered_map<std::string, ShaderSamplerDef> samplerDefs;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ShaderLoader接口 - 支持多种文件格式加载
|
||||
// ============================================================================
|
||||
class IShaderLoader {
|
||||
public:
|
||||
virtual ~IShaderLoader() = default;
|
||||
|
||||
/**
|
||||
* @brief 从分离文件加载Shader (.vert + .frag)
|
||||
* @param name Shader名称
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
virtual ShaderLoadResult
|
||||
loadFromSeparateFiles(const std::string &name, const std::string &vertPath,
|
||||
const std::string &fragPath) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从组合文件加载Shader (.shader)
|
||||
* @param path 组合Shader文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
virtual ShaderLoadResult loadFromCombinedFile(const std::string &path) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从源码字符串加载Shader
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载结果
|
||||
*/
|
||||
virtual ShaderLoadResult loadFromSource(const std::string &vertSource,
|
||||
const std::string &fragSource) = 0;
|
||||
|
||||
/**
|
||||
* @brief 处理Shader源码中的#include指令
|
||||
* @param source 原始源码
|
||||
* @param baseDir 基础目录
|
||||
* @param outDependencies 输出依赖列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
virtual std::string
|
||||
processIncludes(const std::string &source, const std::string &baseDir,
|
||||
std::vector<std::string> &outDependencies) = 0;
|
||||
|
||||
/**
|
||||
* @brief 应用预处理器定义
|
||||
* @param source 原始源码
|
||||
* @param defines 预处理器定义列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
virtual std::string applyDefines(const std::string &source,
|
||||
const std::vector<std::string> &defines) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader元数据
|
||||
* @param path Shader文件路径
|
||||
* @return 元数据
|
||||
*/
|
||||
virtual ShaderMetadata getMetadata(const std::string &path) = 0;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 默认ShaderLoader实现
|
||||
// ============================================================================
|
||||
class ShaderLoader : public IShaderLoader {
|
||||
public:
|
||||
ShaderLoader();
|
||||
~ShaderLoader() override = default;
|
||||
|
||||
/**
|
||||
* @brief 从分离文件加载Shader (.vert + .frag)
|
||||
* @param name Shader名称
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult loadFromSeparateFiles(const std::string &name,
|
||||
const std::string &vertPath,
|
||||
const std::string &fragPath) override;
|
||||
|
||||
/**
|
||||
* @brief 从组合文件加载Shader (.shader)
|
||||
* @param path 组合Shader文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult loadFromCombinedFile(const std::string &path) override;
|
||||
|
||||
/**
|
||||
* @brief 从源码字符串加载Shader
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult loadFromSource(const std::string &vertSource,
|
||||
const std::string &fragSource) override;
|
||||
|
||||
/**
|
||||
* @brief 处理Shader源码中的#include指令
|
||||
* @param source 原始源码
|
||||
* @param baseDir 基础目录
|
||||
* @param outDependencies 输出依赖列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
std::string
|
||||
processIncludes(const std::string &source, const std::string &baseDir,
|
||||
std::vector<std::string> &outDependencies) override;
|
||||
|
||||
/**
|
||||
* @brief 应用预处理器定义
|
||||
* @param source 原始源码
|
||||
* @param defines 预处理器定义列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
std::string applyDefines(const std::string &source,
|
||||
const std::vector<std::string> &defines) override;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader元数据
|
||||
* @param path Shader文件路径
|
||||
* @return 元数据
|
||||
*/
|
||||
ShaderMetadata getMetadata(const std::string &path) override;
|
||||
|
||||
/**
|
||||
* @brief 添加include搜索路径
|
||||
* @param path 搜索路径
|
||||
*/
|
||||
void addIncludePath(const std::string &path);
|
||||
|
||||
/**
|
||||
* @brief 读取文件内容
|
||||
* @param filepath 文件路径
|
||||
* @return 文件内容字符串
|
||||
*/
|
||||
static std::string readFile(const std::string &filepath);
|
||||
|
||||
/**
|
||||
* @brief 获取文件修改时间
|
||||
* @param filepath 文件路径
|
||||
* @return 修改时间戳
|
||||
*/
|
||||
static uint64_t getFileModifiedTime(const std::string &filepath);
|
||||
|
||||
/**
|
||||
* @brief 检查文件是否存在
|
||||
* @param filepath 文件路径
|
||||
* @return 存在返回true,否则返回false
|
||||
*/
|
||||
static bool fileExists(const std::string &filepath);
|
||||
|
||||
private:
|
||||
std::vector<std::string> includePaths_;
|
||||
std::unordered_map<std::string, std::string> includeCache_;
|
||||
|
||||
/**
|
||||
* @brief 解析组合Shader文件
|
||||
* @param content 文件内容
|
||||
* @param outVert 输出顶点着色器源码
|
||||
* @param outFrag 输出片段着色器源码
|
||||
* @param outMetadata 输出元数据
|
||||
* @return 解析成功返回true,失败返回false
|
||||
*/
|
||||
bool parseCombinedFile(const std::string &content, std::string &outVert,
|
||||
std::string &outFrag, ShaderMetadata &outMetadata);
|
||||
|
||||
/**
|
||||
* @brief 解析元数据JSON块
|
||||
* @param jsonContent JSON内容
|
||||
* @param outMetadata 输出元数据
|
||||
* @return 解析成功返回true,失败返回false
|
||||
*/
|
||||
bool parseMetadata(const std::string &jsonContent,
|
||||
ShaderMetadata &outMetadata);
|
||||
|
||||
/**
|
||||
* @brief 查找include文件路径
|
||||
* @param includeName include文件名
|
||||
* @param baseDir 基础目录
|
||||
* @return 找到的完整路径,未找到返回空字符串
|
||||
*/
|
||||
std::string findIncludeFile(const std::string &includeName,
|
||||
const std::string &baseDir);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/shader/shader_cache.h>
|
||||
#include <extra2d/graphics/shader/shader_hot_reloader.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
#include <extra2d/graphics/shader/shader_loader.h>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Shader重载回调
|
||||
// ============================================================================
|
||||
using ShaderReloadCallback = std::function<void(Ptr<IShader> newShader)>;
|
||||
|
||||
// ============================================================================
|
||||
// Shader管理器 - 统一入口
|
||||
// ============================================================================
|
||||
class ShaderManager {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return Shader管理器实例引用
|
||||
*/
|
||||
static ShaderManager& getInstance();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 初始化和关闭
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 使用平台默认路径初始化Shader系统
|
||||
* 自动检测平台并使用正确的路径(romfs/sdmc/相对路径)
|
||||
* @param factory 渲染后端Shader工厂
|
||||
* @param appName 应用名称(用于缓存目录)
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool init(Ptr<IShaderFactory> factory, const std::string& appName = "extra2d");
|
||||
|
||||
/**
|
||||
* @brief 初始化Shader系统
|
||||
* @param shaderDir Shader文件目录
|
||||
* @param cacheDir 缓存目录
|
||||
* @param factory 渲染后端Shader工厂
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool init(const std::string& shaderDir,
|
||||
const std::string& cacheDir,
|
||||
Ptr<IShaderFactory> factory);
|
||||
|
||||
/**
|
||||
* @brief 关闭Shader系统
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 已初始化返回true,否则返回false
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 检查当前平台是否支持热重载
|
||||
* Switch平台使用romfs,不支持热重载
|
||||
* @return 支持热重载返回true
|
||||
*/
|
||||
bool isHotReloadSupported() const { return hotReloadSupported_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Shader加载
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 从分离文件加载Shader
|
||||
* @param name Shader名称
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> loadFromFiles(const std::string& name,
|
||||
const std::string& vertPath,
|
||||
const std::string& fragPath);
|
||||
|
||||
/**
|
||||
* @brief 从组合文件加载Shader
|
||||
* @param path 组合Shader文件路径
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> loadFromCombinedFile(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 从源码加载Shader
|
||||
* @param name Shader名称
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> loadFromSource(const std::string& name,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource);
|
||||
|
||||
/**
|
||||
* @brief 获取已加载的Shader
|
||||
* @param name Shader名称
|
||||
* @return Shader实例,不存在返回nullptr
|
||||
*/
|
||||
Ptr<IShader> get(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 检查Shader是否存在
|
||||
* @param name Shader名称
|
||||
* @return 存在返回true,否则返回false
|
||||
*/
|
||||
bool has(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 移除Shader
|
||||
* @param name Shader名称
|
||||
*/
|
||||
void remove(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 清除所有Shader
|
||||
*/
|
||||
void clear();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 热重载
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 注册重载回调
|
||||
* @param name Shader名称
|
||||
* @param callback 重载回调函数
|
||||
*/
|
||||
void setReloadCallback(const std::string& name, ShaderReloadCallback callback);
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用热重载
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void setHotReloadEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 检查热重载是否启用
|
||||
* @return 启用返回true,否则返回false
|
||||
*/
|
||||
bool isHotReloadEnabled() const;
|
||||
|
||||
/**
|
||||
* @brief 更新热重载系统(主循环调用)
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief 手动重载Shader
|
||||
* @param name Shader名称
|
||||
* @return 重载成功返回true,失败返回false
|
||||
*/
|
||||
bool reload(const std::string& name);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内置Shader
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取内置Shader
|
||||
* @param name 内置Shader名称
|
||||
* @return Shader实例
|
||||
*/
|
||||
Ptr<IShader> getBuiltin(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 加载所有内置Shader
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool loadBuiltinShaders();
|
||||
|
||||
/**
|
||||
* @brief 从JSON元数据文件加载Shader(多后端支持)
|
||||
* @param jsonPath JSON元数据文件路径
|
||||
* @param name Shader名称
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> loadFromMetadata(const std::string& jsonPath, const std::string& name);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 工具方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取Shader目录
|
||||
* @return Shader目录路径
|
||||
*/
|
||||
const std::string& getShaderDir() const { return shaderDir_; }
|
||||
|
||||
/**
|
||||
* @brief 获取ShaderLoader
|
||||
* @return ShaderLoader引用
|
||||
*/
|
||||
ShaderLoader& getLoader() { return loader_; }
|
||||
|
||||
/**
|
||||
* @brief 获取Shader元数据
|
||||
* @param name Shader名称
|
||||
* @return Shader元数据,不存在返回空元数据
|
||||
*/
|
||||
ShaderMetadata getMetadata(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader的uniform定义
|
||||
* @param name Shader名称
|
||||
* @return uniform定义映射,不存在返回空映射
|
||||
*/
|
||||
std::unordered_map<std::string, ShaderUniformDef> getUniformDefs(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader的sampler定义
|
||||
* @param name Shader名称
|
||||
* @return sampler定义映射,不存在返回空映射
|
||||
*/
|
||||
std::unordered_map<std::string, ShaderSamplerDef> getSamplerDefs(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 自动应用uniform值到着色器
|
||||
* 根据JSON元数据中的uniform定义,自动设置对应的uniform值
|
||||
* @param shader 目标着色器
|
||||
* @param shaderName Shader名称(用于查找元数据)
|
||||
* @param values uniform值映射表
|
||||
*/
|
||||
void applyUniforms(Ptr<IShader> shader, const std::string& shaderName, const UniformValueMap& values);
|
||||
|
||||
/**
|
||||
* @brief 自动应用sampler绑定到着色器
|
||||
* 根据JSON元数据中的sampler定义,自动设置对应的纹理单元
|
||||
* @param shader 目标着色器
|
||||
* @param shaderName Shader名称(用于查找元数据)
|
||||
*/
|
||||
void applySamplers(Ptr<IShader> shader, const std::string& shaderName);
|
||||
|
||||
private:
|
||||
ShaderManager() = default;
|
||||
~ShaderManager() = default;
|
||||
ShaderManager(const ShaderManager&) = delete;
|
||||
ShaderManager& operator=(const ShaderManager&) = delete;
|
||||
|
||||
std::string shaderDir_;
|
||||
std::string cacheDir_;
|
||||
Ptr<IShaderFactory> factory_;
|
||||
ShaderLoader loader_;
|
||||
|
||||
struct ShaderInfo {
|
||||
Ptr<IShader> shader;
|
||||
ShaderMetadata metadata;
|
||||
ShaderReloadCallback reloadCallback;
|
||||
std::string vertSource;
|
||||
std::string fragSource;
|
||||
std::vector<std::string> filePaths;
|
||||
};
|
||||
std::unordered_map<std::string, ShaderInfo> shaders_;
|
||||
|
||||
bool initialized_ = false;
|
||||
bool hotReloadEnabled_ = false;
|
||||
bool hotReloadSupported_ = true;
|
||||
|
||||
/**
|
||||
* @brief 从缓存加载Shader
|
||||
* @param name Shader名称
|
||||
* @param sourceHash 源码哈希值
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return Shader实例
|
||||
*/
|
||||
Ptr<IShader> loadFromCache(const std::string& name,
|
||||
const std::string& sourceHash,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource);
|
||||
|
||||
/**
|
||||
* @brief 处理文件变化事件
|
||||
* @param shaderName Shader名称
|
||||
* @param event 文件变化事件
|
||||
*/
|
||||
void handleFileChange(const std::string& shaderName, const FileChangeEvent& event);
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_SHADER_MANAGER() ::extra2d::ShaderManager::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
struct WaterParams {
|
||||
float waveSpeed = 1.0f;
|
||||
float waveAmplitude = 0.02f;
|
||||
float waveFrequency = 4.0f;
|
||||
};
|
||||
|
||||
struct OutlineParams {
|
||||
Color color = Colors::Black;
|
||||
float thickness = 2.0f;
|
||||
};
|
||||
|
||||
struct DistortionParams {
|
||||
float distortionAmount = 0.02f;
|
||||
float timeScale = 1.0f;
|
||||
};
|
||||
|
||||
struct PixelateParams {
|
||||
float pixelSize = 8.0f;
|
||||
};
|
||||
|
||||
struct InvertParams {
|
||||
float strength = 1.0f;
|
||||
};
|
||||
|
||||
struct GrayscaleParams {
|
||||
float intensity = 1.0f;
|
||||
};
|
||||
|
||||
struct BlurParams {
|
||||
float radius = 5.0f;
|
||||
};
|
||||
|
||||
class ShaderPreset {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建水波纹效果着色器
|
||||
* @param params 水波纹效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Water(const WaterParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建描边效果着色器
|
||||
* @param params 描边效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Outline(const OutlineParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建扭曲效果着色器
|
||||
* @param params 扭曲效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Distortion(const DistortionParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建像素化效果着色器
|
||||
* @param params 像素化效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Pixelate(const PixelateParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建反相效果着色器
|
||||
* @param params 反相效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Invert(const InvertParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建灰度效果着色器
|
||||
* @param params 灰度效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Grayscale(const GrayscaleParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建模糊效果着色器
|
||||
* @param params 模糊效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Blur(const BlurParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建灰度+描边组合效果着色器
|
||||
* @param grayParams 灰度效果参数
|
||||
* @param outlineParams 描边效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> GrayscaleOutline(const GrayscaleParams& grayParams,
|
||||
const OutlineParams& outlineParams);
|
||||
|
||||
/**
|
||||
* @brief 创建像素化+反相组合效果着色器
|
||||
* @param pixParams 像素化效果参数
|
||||
* @param invParams 反相效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> PixelateInvert(const PixelateParams& pixParams,
|
||||
const InvertParams& invParams);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Alpha 遮罩 - 存储图片的非透明区域信息
|
||||
// ============================================================================
|
||||
class AlphaMask {
|
||||
public:
|
||||
AlphaMask() = default;
|
||||
AlphaMask(int width, int height);
|
||||
|
||||
/// 从像素数据创建遮罩
|
||||
static AlphaMask createFromPixels(const uint8_t *pixels, int width,
|
||||
int height, int channels);
|
||||
|
||||
/// 获取指定位置的透明度(0-255)
|
||||
uint8_t getAlpha(int x, int y) const;
|
||||
|
||||
/// 检查指定位置是否不透明
|
||||
bool isOpaque(int x, int y, uint8_t threshold = 128) const;
|
||||
|
||||
/// 检查指定位置是否在遮罩范围内
|
||||
bool isValid(int x, int y) const;
|
||||
|
||||
/// 获取遮罩尺寸
|
||||
int getWidth() const { return width_; }
|
||||
int getHeight() const { return height_; }
|
||||
Size getSize() const {
|
||||
return Size(static_cast<float>(width_), static_cast<float>(height_));
|
||||
}
|
||||
|
||||
/// 获取原始数据
|
||||
const std::vector<uint8_t> &getData() const { return data_; }
|
||||
|
||||
/// 检查遮罩是否有效
|
||||
bool isValid() const { return !data_.empty() && width_ > 0 && height_ > 0; }
|
||||
|
||||
private:
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
std::vector<uint8_t> data_; // Alpha值数组
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 字形信息
|
||||
// ============================================================================
|
||||
struct Glyph {
|
||||
float u0, v0; // 纹理坐标左下角
|
||||
float u1, v1; // 纹理坐标右上角
|
||||
float width; // 字形宽度(像素)
|
||||
float height; // 字形高度(像素)
|
||||
float bearingX; // 水平偏移
|
||||
float bearingY; // 垂直偏移
|
||||
float advance; // 前进距离
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 字体图集接口
|
||||
// ============================================================================
|
||||
class FontAtlas {
|
||||
public:
|
||||
virtual ~FontAtlas() = default;
|
||||
|
||||
// 获取字形信息
|
||||
virtual const Glyph *getGlyph(char32_t codepoint) const = 0;
|
||||
|
||||
// 获取纹理
|
||||
virtual class Texture *getTexture() const = 0;
|
||||
|
||||
// 获取字体大小
|
||||
virtual int getFontSize() const = 0;
|
||||
|
||||
virtual float getAscent() const = 0;
|
||||
virtual float getDescent() const = 0;
|
||||
virtual float getLineGap() const = 0;
|
||||
virtual float getLineHeight() const = 0;
|
||||
|
||||
// 计算文字尺寸
|
||||
virtual Vec2 measureText(const std::string &text) = 0;
|
||||
|
||||
// 是否支持 SDF 渲染
|
||||
virtual bool isSDF() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 像素格式枚举
|
||||
// ============================================================================
|
||||
enum class PixelFormat {
|
||||
R8, // 单通道灰度
|
||||
RG8, // 双通道
|
||||
RGB8, // RGB 24位
|
||||
RGBA8, // RGBA 32位(默认)
|
||||
RGB16F, // RGB 半精度浮点
|
||||
RGBA16F, // RGBA 半精度浮点
|
||||
RGB32F, // RGB 全精度浮点
|
||||
RGBA32F, // RGBA 全精度浮点
|
||||
Depth16, // 16位深度
|
||||
Depth24, // 24位深度
|
||||
Depth32F, // 32位浮点深度
|
||||
Depth24Stencil8, // 24位深度 + 8位模板
|
||||
|
||||
// 压缩纹理格式
|
||||
ETC2_RGB8, // ETC2 RGB 压缩
|
||||
ETC2_RGBA8, // ETC2 RGBA 压缩
|
||||
ASTC_4x4, // ASTC 4x4 压缩
|
||||
ASTC_6x6, // ASTC 6x6 压缩
|
||||
ASTC_8x8 // ASTC 8x8 压缩
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理接口
|
||||
// ============================================================================
|
||||
class Texture {
|
||||
public:
|
||||
virtual ~Texture() = default;
|
||||
|
||||
// 获取尺寸
|
||||
virtual int getWidth() const = 0;
|
||||
virtual int getHeight() const = 0;
|
||||
virtual Size getSize() const = 0;
|
||||
|
||||
// 获取通道数
|
||||
virtual int getChannels() const = 0;
|
||||
|
||||
// 获取像素格式
|
||||
virtual PixelFormat getFormat() const = 0;
|
||||
|
||||
// 获取原始句柄(用于底层渲染)
|
||||
virtual void* getNativeHandle() const = 0;
|
||||
|
||||
// 是否有效
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
// 设置过滤模式
|
||||
virtual void setFilter(bool linear) = 0;
|
||||
|
||||
// 设置环绕模式
|
||||
virtual void setWrap(bool repeat) = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 纹理图集 - 自动将小纹理合并到大图集以减少 DrawCall
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 图集中的单个纹理条目
|
||||
*/
|
||||
struct AtlasEntry {
|
||||
std::string name; // 原始纹理名称/路径
|
||||
Rect uvRect; // 在图集中的 UV 坐标范围
|
||||
Vec2 originalSize; // 原始纹理尺寸
|
||||
uint32_t padding; // 边距(用于避免纹理 bleeding)
|
||||
|
||||
AtlasEntry() : uvRect(), originalSize(), padding(2) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理图集页面
|
||||
* 当单个图集放不下时,创建多个页面
|
||||
*/
|
||||
class TextureAtlasPage {
|
||||
public:
|
||||
static constexpr int DEFAULT_SIZE = 2048;
|
||||
static constexpr int MAX_SIZE = 4096;
|
||||
static constexpr int MIN_TEXTURE_SIZE = 32; // 小于此大小的纹理才考虑合并
|
||||
static constexpr int PADDING = 2; // 纹理间边距
|
||||
|
||||
TextureAtlasPage(int width = DEFAULT_SIZE, int height = DEFAULT_SIZE);
|
||||
~TextureAtlasPage();
|
||||
|
||||
// 尝试添加纹理到图集
|
||||
// 返回是否成功,如果成功则输出 uvRect
|
||||
bool tryAddTexture(const std::string& name, int texWidth, int texHeight,
|
||||
const uint8_t* pixels, Rect& outUvRect);
|
||||
|
||||
// 获取图集纹理
|
||||
Ptr<Texture> getTexture() const { return texture_; }
|
||||
|
||||
// 获取条目
|
||||
const AtlasEntry* getEntry(const std::string& name) const;
|
||||
|
||||
// 获取使用率
|
||||
float getUsageRatio() const;
|
||||
|
||||
// 获取尺寸
|
||||
int getWidth() const { return width_; }
|
||||
int getHeight() const { return height_; }
|
||||
|
||||
// 是否已满
|
||||
bool isFull() const { return isFull_; }
|
||||
|
||||
private:
|
||||
int width_, height_;
|
||||
Ptr<Texture> texture_;
|
||||
std::unordered_map<std::string, AtlasEntry> entries_;
|
||||
|
||||
// 矩形打包数据
|
||||
struct PackNode {
|
||||
int x, y, width, height;
|
||||
bool used;
|
||||
std::unique_ptr<PackNode> left;
|
||||
std::unique_ptr<PackNode> right;
|
||||
|
||||
PackNode(int x_, int y_, int w, int h)
|
||||
: x(x_), y(y_), width(w), height(h), used(false) {}
|
||||
};
|
||||
|
||||
std::unique_ptr<PackNode> root_;
|
||||
bool isFull_;
|
||||
int usedArea_;
|
||||
|
||||
// 递归插入
|
||||
PackNode* insert(PackNode* node, int width, int height);
|
||||
void writePixels(int x, int y, int w, int h, const uint8_t* pixels);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理图集管理器
|
||||
* 自动管理多个图集页面,提供统一的纹理查询接口
|
||||
*/
|
||||
class TextureAtlas {
|
||||
public:
|
||||
TextureAtlas();
|
||||
~TextureAtlas();
|
||||
|
||||
// 初始化
|
||||
void init(int pageSize = TextureAtlasPage::DEFAULT_SIZE);
|
||||
|
||||
// 添加纹理到图集
|
||||
// 如果纹理太大,返回 false,应该作为独立纹理加载
|
||||
bool addTexture(const std::string& name, int width, int height,
|
||||
const uint8_t* pixels);
|
||||
|
||||
// 查询纹理是否在图集中
|
||||
bool contains(const std::string& name) const;
|
||||
|
||||
// 获取纹理在图集中的信息
|
||||
// 返回图集纹理和 UV 坐标
|
||||
const Texture* getAtlasTexture(const std::string& name) const;
|
||||
Rect getUVRect(const std::string& name) const;
|
||||
|
||||
// 获取原始纹理尺寸
|
||||
Vec2 getOriginalSize(const std::string& name) const;
|
||||
|
||||
// 获取所有图集页面
|
||||
const std::vector<std::unique_ptr<TextureAtlasPage>>& getPages() const { return pages_; }
|
||||
|
||||
// 获取总使用率
|
||||
float getTotalUsageRatio() const;
|
||||
|
||||
// 清空所有图集
|
||||
void clear();
|
||||
|
||||
// 设置是否启用自动图集
|
||||
void setEnabled(bool enabled) { enabled_ = enabled; }
|
||||
bool isEnabled() const { return enabled_; }
|
||||
|
||||
// 设置纹理大小阈值(小于此大小的纹理才进入图集)
|
||||
void setSizeThreshold(int threshold) { sizeThreshold_ = threshold; }
|
||||
int getSizeThreshold() const { return sizeThreshold_; }
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<TextureAtlasPage>> pages_;
|
||||
std::unordered_map<std::string, TextureAtlasPage*> entryToPage_;
|
||||
|
||||
int pageSize_;
|
||||
int sizeThreshold_;
|
||||
bool enabled_;
|
||||
bool initialized_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 全局图集管理器(单例)
|
||||
*/
|
||||
class TextureAtlasMgr {
|
||||
public:
|
||||
static TextureAtlasMgr& get();
|
||||
|
||||
// 获取主图集
|
||||
TextureAtlas& getAtlas() { return atlas_; }
|
||||
|
||||
// 快捷方法
|
||||
bool addTexture(const std::string& name, int width, int height,
|
||||
const uint8_t* pixels) {
|
||||
return atlas_.addTexture(name, width, height, pixels);
|
||||
}
|
||||
|
||||
bool contains(const std::string& name) const {
|
||||
return atlas_.contains(name);
|
||||
}
|
||||
|
||||
const Texture* getAtlasTexture(const std::string& name) const {
|
||||
return atlas_.getAtlasTexture(name);
|
||||
}
|
||||
|
||||
Rect getUVRect(const std::string& name) const {
|
||||
return atlas_.getUVRect(name);
|
||||
}
|
||||
|
||||
private:
|
||||
TextureAtlasMgr() = default;
|
||||
~TextureAtlasMgr() = default;
|
||||
|
||||
TextureAtlasMgr(const TextureAtlasMgr&) = delete;
|
||||
TextureAtlasMgr& operator=(const TextureAtlasMgr&) = delete;
|
||||
|
||||
TextureAtlas atlas_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,562 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Scene;
|
||||
class RenderBackend;
|
||||
|
||||
// ============================================================================
|
||||
// 纹理加载选项
|
||||
// ============================================================================
|
||||
struct TextureLoadOptions {
|
||||
bool generateMipmaps = true; // 是否生成 mipmaps
|
||||
bool sRGB = true; // 是否使用 sRGB 色彩空间
|
||||
bool premultiplyAlpha = false; // 是否预乘 Alpha
|
||||
PixelFormat preferredFormat = PixelFormat::RGBA8; // 首选像素格式
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理键 - 用于唯一标识纹理缓存条目
|
||||
// ============================================================================
|
||||
struct TextureKey {
|
||||
std::string path; // 纹理文件路径
|
||||
Rect region; // 纹理区域(用于纹理图集)
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
TextureKey() = default;
|
||||
|
||||
/**
|
||||
* @brief 构造函数(仅路径)
|
||||
* @param p 纹理文件路径
|
||||
*/
|
||||
explicit TextureKey(const std::string &p) : path(p), region(Rect::Zero()) {}
|
||||
|
||||
/**
|
||||
* @brief 构造函数(路径 + 区域)
|
||||
* @param p 纹理文件路径
|
||||
* @param r 纹理区域
|
||||
*/
|
||||
TextureKey(const std::string &p, const Rect &r) : path(p), region(r) {}
|
||||
|
||||
/**
|
||||
* @brief 相等比较运算符
|
||||
* @param other 另一个 TextureKey
|
||||
* @return 是否相等
|
||||
*/
|
||||
bool operator==(const TextureKey &other) const {
|
||||
return path == other.path && region == other.region;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 不等比较运算符
|
||||
* @param other 另一个 TextureKey
|
||||
* @return 是否不等
|
||||
*/
|
||||
bool operator!=(const TextureKey &other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// TextureKey 哈希函子
|
||||
// ============================================================================
|
||||
struct TextureKeyHash {
|
||||
/**
|
||||
* @brief 计算 TextureKey 的哈希值
|
||||
* @param key 纹理键
|
||||
* @return 哈希值
|
||||
*/
|
||||
size_t operator()(const TextureKey &key) const {
|
||||
size_t h1 = std::hash<std::string>{}(key.path);
|
||||
size_t h2 = std::hash<float>{}(key.region.origin.x);
|
||||
size_t h3 = std::hash<float>{}(key.region.origin.y);
|
||||
size_t h4 = std::hash<float>{}(key.region.size.width);
|
||||
size_t h5 = std::hash<float>{}(key.region.size.height);
|
||||
|
||||
// 组合哈希值
|
||||
size_t result = h1;
|
||||
result ^= h2 + 0x9e3779b9 + (result << 6) + (result >> 2);
|
||||
result ^= h3 + 0x9e3779b9 + (result << 6) + (result >> 2);
|
||||
result ^= h4 + 0x9e3779b9 + (result << 6) + (result >> 2);
|
||||
result ^= h5 + 0x9e3779b9 + (result << 6) + (result >> 2);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理池条目
|
||||
// ============================================================================
|
||||
struct TexturePoolEntry {
|
||||
Ptr<Texture> texture; // 纹理对象
|
||||
mutable std::atomic<uint32_t> refCount; // 引用计数
|
||||
TextureKey key; // 纹理键
|
||||
size_t memorySize; // 内存占用(字节)
|
||||
mutable uint64_t lastAccessTime; // 最后访问时间戳
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
TexturePoolEntry()
|
||||
: texture(nullptr), refCount(0), key(), memorySize(0), lastAccessTime(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param tex 纹理对象
|
||||
* @param k 纹理键
|
||||
* @param memSize 内存占用
|
||||
*/
|
||||
TexturePoolEntry(Ptr<Texture> tex, const TextureKey &k, size_t memSize)
|
||||
: texture(tex), refCount(1), key(k), memorySize(memSize),
|
||||
lastAccessTime(getCurrentTime()) {}
|
||||
|
||||
/**
|
||||
* @brief 移动构造函数
|
||||
* @param other 另一个条目
|
||||
*/
|
||||
TexturePoolEntry(TexturePoolEntry &&other) noexcept
|
||||
: texture(std::move(other.texture)),
|
||||
refCount(other.refCount.load(std::memory_order_relaxed)),
|
||||
key(std::move(other.key)), memorySize(other.memorySize),
|
||||
lastAccessTime(other.lastAccessTime) {}
|
||||
|
||||
/**
|
||||
* @brief 移动赋值运算符
|
||||
* @param other 另一个条目
|
||||
* @return 引用
|
||||
*/
|
||||
TexturePoolEntry &operator=(TexturePoolEntry &&other) noexcept {
|
||||
if (this != &other) {
|
||||
texture = std::move(other.texture);
|
||||
refCount.store(other.refCount.load(std::memory_order_relaxed),
|
||||
std::memory_order_relaxed);
|
||||
key = std::move(other.key);
|
||||
memorySize = other.memorySize;
|
||||
lastAccessTime = other.lastAccessTime;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 禁止拷贝
|
||||
TexturePoolEntry(const TexturePoolEntry &) = delete;
|
||||
TexturePoolEntry &operator=(const TexturePoolEntry &) = delete;
|
||||
|
||||
/**
|
||||
* @brief 更新最后访问时间
|
||||
*/
|
||||
void touch() const { lastAccessTime = getCurrentTime(); }
|
||||
|
||||
/**
|
||||
* @brief 获取当前时间戳
|
||||
* @return 时间戳(毫秒)
|
||||
*/
|
||||
static uint64_t getCurrentTime() {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch());
|
||||
return static_cast<uint64_t>(duration.count());
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理引用智能指针 - 自动管理纹理池引用计数
|
||||
// ============================================================================
|
||||
class TextureRef {
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
TextureRef() : texture_(nullptr), entry_(nullptr), mutex_(nullptr) {}
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param texture 纹理对象
|
||||
* @param entry 纹理池条目
|
||||
* @param mutex 互斥锁
|
||||
*/
|
||||
TextureRef(Ptr<Texture> texture, TexturePoolEntry *entry, std::mutex *mutex)
|
||||
: texture_(texture), entry_(entry), mutex_(mutex) {}
|
||||
|
||||
/**
|
||||
* @brief 创建独立的纹理引用(不管理引用计数)
|
||||
* @param texture 纹理对象
|
||||
* @return 独立的纹理引用
|
||||
*/
|
||||
static TextureRef fromTexture(Ptr<Texture> texture) {
|
||||
return TextureRef(texture, nullptr, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 拷贝构造函数
|
||||
* @param other 另一个 TextureRef
|
||||
*/
|
||||
TextureRef(const TextureRef &other)
|
||||
: texture_(other.texture_), entry_(other.entry_), mutex_(other.mutex_) {
|
||||
if (entry_ && entry_->refCount.load(std::memory_order_relaxed) > 0) {
|
||||
entry_->refCount.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移动构造函数
|
||||
* @param other 另一个 TextureRef
|
||||
*/
|
||||
TextureRef(TextureRef &&other) noexcept
|
||||
: texture_(std::move(other.texture_)), entry_(other.entry_),
|
||||
mutex_(other.mutex_) {
|
||||
other.entry_ = nullptr;
|
||||
other.mutex_ = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~TextureRef() { reset(); }
|
||||
|
||||
/**
|
||||
* @brief 拷贝赋值运算符
|
||||
* @param other 另一个 TextureRef
|
||||
* @return 引用
|
||||
*/
|
||||
TextureRef &operator=(const TextureRef &other) {
|
||||
if (this != &other) {
|
||||
reset();
|
||||
texture_ = other.texture_;
|
||||
entry_ = other.entry_;
|
||||
mutex_ = other.mutex_;
|
||||
if (entry_ && entry_->refCount.load(std::memory_order_relaxed) > 0) {
|
||||
entry_->refCount.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移动赋值运算符
|
||||
* @param other 另一个 TextureRef
|
||||
* @return 引用
|
||||
*/
|
||||
TextureRef &operator=(TextureRef &&other) noexcept {
|
||||
if (this != &other) {
|
||||
reset();
|
||||
texture_ = std::move(other.texture_);
|
||||
entry_ = other.entry_;
|
||||
mutex_ = other.mutex_;
|
||||
other.entry_ = nullptr;
|
||||
other.mutex_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重置引用
|
||||
*/
|
||||
void reset() {
|
||||
if (entry_ && mutex_) {
|
||||
std::lock_guard<std::mutex> lock(*mutex_);
|
||||
if (entry_->refCount.load(std::memory_order_relaxed) > 0) {
|
||||
entry_->refCount.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
texture_.reset();
|
||||
entry_ = nullptr;
|
||||
mutex_ = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取纹理对象
|
||||
* @return 纹理对象指针
|
||||
*/
|
||||
Texture *get() const { return texture_.get(); }
|
||||
|
||||
/**
|
||||
* @brief 获取纹理对象(智能指针)
|
||||
* @return 纹理对象智能指针
|
||||
*/
|
||||
Ptr<Texture> getPtr() const { return texture_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否有效
|
||||
* @return 是否有效
|
||||
*/
|
||||
bool valid() const { return texture_ != nullptr; }
|
||||
|
||||
/**
|
||||
* @brief 布尔转换运算符
|
||||
*/
|
||||
explicit operator bool() const { return valid(); }
|
||||
|
||||
/**
|
||||
* @brief 箭头运算符
|
||||
*/
|
||||
Texture *operator->() const { return texture_.get(); }
|
||||
|
||||
/**
|
||||
* @brief 解引用运算符
|
||||
*/
|
||||
Texture &operator*() const { return *texture_; }
|
||||
|
||||
private:
|
||||
Ptr<Texture> texture_;
|
||||
TexturePoolEntry *entry_;
|
||||
std::mutex *mutex_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理池 - 纹理缓存和内存管理系统
|
||||
// 特性:
|
||||
// - 纹理缓存和复用
|
||||
// - 引用计数管理
|
||||
// - 内存使用限制
|
||||
// - LRU 淘汰策略
|
||||
// - 线程安全
|
||||
// ============================================================================
|
||||
class TexturePool {
|
||||
public:
|
||||
// ========================================================================
|
||||
// 统计信息
|
||||
// ========================================================================
|
||||
struct Stats {
|
||||
size_t textureCount = 0; // 纹理数量
|
||||
size_t memoryUsage = 0; // 内存使用量(字节)
|
||||
size_t maxMemoryUsage = 0; // 最大内存使用量
|
||||
size_t cacheHits = 0; // 缓存命中次数
|
||||
size_t cacheMisses = 0; // 缓存未命中次数
|
||||
size_t evictionCount = 0; // 淘汰次数
|
||||
};
|
||||
|
||||
// ========================================================================
|
||||
// 构造和析构
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
TexturePool();
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param scene 场景指针
|
||||
* @param maxMemoryUsage 最大内存使用量(0 表示无限制)
|
||||
*/
|
||||
explicit TexturePool(Scene *scene, size_t maxMemoryUsage = 0);
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~TexturePool();
|
||||
|
||||
// 禁止拷贝
|
||||
TexturePool(const TexturePool &) = delete;
|
||||
TexturePool &operator=(const TexturePool &) = delete;
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理池
|
||||
* @param scene 场景指针
|
||||
* @param maxMemoryUsage 最大内存使用量(0 表示无限制)
|
||||
*/
|
||||
void init(Scene *scene, size_t maxMemoryUsage = 0);
|
||||
|
||||
// ========================================================================
|
||||
// 纹理加载
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理
|
||||
* @param path 文件路径
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*/
|
||||
TextureRef load(const std::string &path,
|
||||
const TextureLoadOptions &options = TextureLoadOptions());
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理区域
|
||||
* @param path 文件路径
|
||||
* @param region 纹理区域
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*/
|
||||
TextureRef load(const std::string &path, const Rect ®ion,
|
||||
const TextureLoadOptions &options = TextureLoadOptions());
|
||||
|
||||
/**
|
||||
* @brief 从内存加载纹理
|
||||
* @param data 像素数据
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param channels 通道数
|
||||
* @param key 缓存键
|
||||
* @return 纹理引用
|
||||
*/
|
||||
TextureRef loadFromMemory(const uint8_t *data, int width, int height,
|
||||
int channels, const std::string &key);
|
||||
|
||||
/**
|
||||
* @brief 获取或加载纹理
|
||||
* @param path 文件路径
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*/
|
||||
TextureRef
|
||||
getOrLoad(const std::string &path,
|
||||
const TextureLoadOptions &options = TextureLoadOptions());
|
||||
|
||||
/**
|
||||
* @brief 获取或加载纹理区域
|
||||
* @param path 文件路径
|
||||
* @param region 纹理区域
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*/
|
||||
TextureRef
|
||||
getOrLoad(const std::string &path, const Rect ®ion,
|
||||
const TextureLoadOptions &options = TextureLoadOptions());
|
||||
|
||||
// ========================================================================
|
||||
// 引用计数管理
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 增加引用计数
|
||||
* @param key 纹理键
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool addRef(const TextureKey &key);
|
||||
|
||||
/**
|
||||
* @brief 减少引用计数
|
||||
* @param key 纹理键
|
||||
* @return 减少后的引用计数
|
||||
*/
|
||||
uint32_t release(const TextureKey &key);
|
||||
|
||||
/**
|
||||
* @brief 获取引用计数
|
||||
* @param key 纹理键
|
||||
* @return 引用计数
|
||||
*/
|
||||
uint32_t getRefCount(const TextureKey &key) const;
|
||||
|
||||
// ========================================================================
|
||||
// 缓存管理
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 检查纹理是否已缓存
|
||||
* @param key 纹理键
|
||||
* @return 是否已缓存
|
||||
*/
|
||||
bool isCached(const TextureKey &key) const;
|
||||
|
||||
/**
|
||||
* @brief 从缓存中移除纹理
|
||||
* @param key 纹理键
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool removeFromCache(const TextureKey &key);
|
||||
|
||||
/**
|
||||
* @brief 垃圾回收(移除引用计数为 0 的纹理)
|
||||
* @return 移除的纹理数量
|
||||
*/
|
||||
size_t collectGarbage();
|
||||
|
||||
/**
|
||||
* @brief 清空所有缓存
|
||||
*/
|
||||
void clear();
|
||||
|
||||
// ========================================================================
|
||||
// 内存管理
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取当前内存使用量
|
||||
* @return 内存使用量(字节)
|
||||
*/
|
||||
size_t getMemoryUsage() const;
|
||||
|
||||
/**
|
||||
* @brief 设置最大内存使用量
|
||||
* @param maxMemory 最大内存使用量(0 表示无限制)
|
||||
*/
|
||||
void setMaxMemoryUsage(size_t maxMemory);
|
||||
|
||||
/**
|
||||
* @brief 获取最大内存使用量
|
||||
* @return 最大内存使用量
|
||||
*/
|
||||
size_t getMaxMemoryUsage() const { return maxMemoryUsage_; }
|
||||
|
||||
/**
|
||||
* @brief 执行 LRU 淘汰
|
||||
* @param targetMemory 目标内存使用量
|
||||
* @return 淘汰的纹理数量
|
||||
*/
|
||||
size_t evictLRU(size_t targetMemory = 0);
|
||||
|
||||
// ========================================================================
|
||||
// 统计信息
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
* @return 统计信息
|
||||
*/
|
||||
Stats getStats() const;
|
||||
|
||||
/**
|
||||
* @brief 重置统计信息
|
||||
*/
|
||||
void resetStats();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 计算纹理内存大小
|
||||
* @param texture 纹理对象
|
||||
* @return 内存大小(字节)
|
||||
*/
|
||||
static size_t calculateTextureMemory(const Texture *texture);
|
||||
|
||||
/**
|
||||
* @brief 检查是否需要淘汰
|
||||
* @return 是否需要淘汰
|
||||
*/
|
||||
bool needsEviction() const;
|
||||
|
||||
/**
|
||||
* @brief 尝试自动淘汰
|
||||
*/
|
||||
void tryAutoEvict();
|
||||
|
||||
Scene *scene_; // 场景指针
|
||||
mutable std::mutex mutex_; // 互斥锁
|
||||
std::unordered_map<TextureKey, TexturePoolEntry, TextureKeyHash>
|
||||
cache_; // 纹理缓存
|
||||
|
||||
size_t maxMemoryUsage_; // 最大内存使用量
|
||||
size_t currentMemoryUsage_; // 当前内存使用量
|
||||
|
||||
// 统计信息
|
||||
mutable std::atomic<size_t> cacheHits_;
|
||||
mutable std::atomic<size_t> cacheMisses_;
|
||||
mutable std::atomic<size_t> evictionCount_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/render/rhi/rhi_buffer.h>
|
||||
#include <glad/glad.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 缓冲区实现
|
||||
*
|
||||
* 使用 OpenGL 4.5 DSA (Direct State Access) 特性
|
||||
*/
|
||||
class GLRHIBuffer : public RHIBuffer {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLRHIBuffer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLRHIBuffer() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化缓冲区
|
||||
* @param desc 缓冲区描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(const BufferDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// RHIBuffer 接口实现
|
||||
BufferType getType() const override { return type_; }
|
||||
size_t getSize() const override { return size_; }
|
||||
BufferUsage getUsage() const override { return usage_; }
|
||||
void setData(const void* data, size_t size, size_t offset = 0) override;
|
||||
void getData(void* data, size_t size, size_t offset = 0) const override;
|
||||
void* map(size_t offset, size_t size, bool read = false, bool write = true) override;
|
||||
void unmap() override;
|
||||
bool isValid() const override { return bufferID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(bufferID_); }
|
||||
const std::string& getDebugName() const override { return debugName_; }
|
||||
void setDebugName(const std::string& name) override { debugName_ = name; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 缓冲区 ID
|
||||
*/
|
||||
GLuint getBufferID() const { return bufferID_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 目标类型
|
||||
*/
|
||||
GLenum getTarget() const { return target_; }
|
||||
|
||||
/**
|
||||
* @brief 绑定到指定绑定点
|
||||
*/
|
||||
void bindBase(uint32_t index);
|
||||
|
||||
private:
|
||||
GLuint bufferID_ = 0;
|
||||
GLenum target_ = GL_ARRAY_BUFFER;
|
||||
size_t size_ = 0;
|
||||
BufferType type_ = BufferType::Vertex;
|
||||
BufferUsage usage_ = BufferUsage::Static;
|
||||
std::string debugName_;
|
||||
bool mapped_ = false;
|
||||
void* mappedPtr_ = nullptr;
|
||||
|
||||
/**
|
||||
* @brief 转换缓冲区类型到 OpenGL 目标
|
||||
*/
|
||||
static GLenum convertType(BufferType type);
|
||||
|
||||
/**
|
||||
* @brief 转换使用方式到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertUsage(BufferUsage usage);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief OpenGL 顶点缓冲区实现
|
||||
*/
|
||||
class GLRHIVertexBuffer : public RHIVertexBuffer {
|
||||
public:
|
||||
GLRHIVertexBuffer() = default;
|
||||
~GLRHIVertexBuffer() override = default;
|
||||
|
||||
bool init(size_t size, BufferUsage usage, const void* data = nullptr);
|
||||
void shutdown();
|
||||
|
||||
BufferType getType() const override { return BufferType::Vertex; }
|
||||
size_t getSize() const override { return buffer_.getSize(); }
|
||||
BufferUsage getUsage() const override { return buffer_.getUsage(); }
|
||||
void setData(const void* data, size_t size, size_t offset = 0) override { buffer_.setData(data, size, offset); }
|
||||
void getData(void* data, size_t size, size_t offset = 0) const override { buffer_.getData(data, size, offset); }
|
||||
void* map(size_t offset, size_t size, bool read = false, bool write = true) override { return buffer_.map(offset, size, read, write); }
|
||||
void unmap() override { buffer_.unmap(); }
|
||||
bool isValid() const override { return buffer_.isValid(); }
|
||||
uintptr_t getNativeHandle() const override { return buffer_.getNativeHandle(); }
|
||||
const std::string& getDebugName() const override { return buffer_.getDebugName(); }
|
||||
void setDebugName(const std::string& name) override { buffer_.setDebugName(name); }
|
||||
|
||||
GLuint getBufferID() const { return buffer_.getBufferID(); }
|
||||
|
||||
private:
|
||||
GLRHIBuffer buffer_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief OpenGL 索引缓冲区实现
|
||||
*/
|
||||
class GLRHIIndexBuffer : public RHIIndexBuffer {
|
||||
public:
|
||||
GLRHIIndexBuffer() = default;
|
||||
~GLRHIIndexBuffer() override = default;
|
||||
|
||||
bool init(size_t size, BufferUsage usage, const void* data = nullptr);
|
||||
void shutdown();
|
||||
|
||||
BufferType getType() const override { return BufferType::Index; }
|
||||
size_t getSize() const override { return buffer_.getSize(); }
|
||||
BufferUsage getUsage() const override { return buffer_.getUsage(); }
|
||||
void setData(const void* data, size_t size, size_t offset = 0) override { buffer_.setData(data, size, offset); }
|
||||
void getData(void* data, size_t size, size_t offset = 0) const override { buffer_.getData(data, size, offset); }
|
||||
void* map(size_t offset, size_t size, bool read = false, bool write = true) override { return buffer_.map(offset, size, read, write); }
|
||||
void unmap() override { buffer_.unmap(); }
|
||||
bool isValid() const override { return buffer_.isValid(); }
|
||||
uintptr_t getNativeHandle() const override { return buffer_.getNativeHandle(); }
|
||||
const std::string& getDebugName() const override { return buffer_.getDebugName(); }
|
||||
void setDebugName(const std::string& name) override { buffer_.setDebugName(name); }
|
||||
|
||||
GLuint getBufferID() const { return buffer_.getBufferID(); }
|
||||
|
||||
private:
|
||||
GLRHIBuffer buffer_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief OpenGL Uniform 缓冲区实现
|
||||
*/
|
||||
class GLRHIUniformBuffer : public RHIUniformBuffer {
|
||||
public:
|
||||
GLRHIUniformBuffer() = default;
|
||||
~GLRHIUniformBuffer() override = default;
|
||||
|
||||
bool init(size_t size, BufferUsage usage);
|
||||
void shutdown();
|
||||
|
||||
BufferType getType() const override { return BufferType::Uniform; }
|
||||
size_t getSize() const override { return buffer_.getSize(); }
|
||||
BufferUsage getUsage() const override { return buffer_.getUsage(); }
|
||||
void setData(const void* data, size_t size, size_t offset = 0) override { buffer_.setData(data, size, offset); }
|
||||
void getData(void* data, size_t size, size_t offset = 0) const override { buffer_.getData(data, size, offset); }
|
||||
void* map(size_t offset, size_t size, bool read = false, bool write = true) override { return buffer_.map(offset, size, read, write); }
|
||||
void unmap() override { buffer_.unmap(); }
|
||||
bool isValid() const override { return buffer_.isValid(); }
|
||||
uintptr_t getNativeHandle() const override { return buffer_.getNativeHandle(); }
|
||||
const std::string& getDebugName() const override { return buffer_.getDebugName(); }
|
||||
void setDebugName(const std::string& name) override { buffer_.setDebugName(name); }
|
||||
|
||||
GLuint getBufferID() const { return buffer_.getBufferID(); }
|
||||
void bindBase(uint32_t index) { buffer_.bindBase(index); }
|
||||
|
||||
private:
|
||||
GLRHIBuffer buffer_;
|
||||
};
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_framebuffer.h>
|
||||
#include <glad/glad.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class IWindow;
|
||||
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief OpenGL RHI 设备实现
|
||||
*
|
||||
* 使用 OpenGL 4.5 Core Profile + DSA 特性
|
||||
*/
|
||||
class GLRHIDevice : public RHIDevice {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLRHIDevice();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLRHIDevice() override;
|
||||
|
||||
// ========================================================================
|
||||
// 生命周期
|
||||
// ========================================================================
|
||||
|
||||
bool init(IWindow* window) override;
|
||||
void shutdown() override;
|
||||
bool isValid() const override { return initialized_; }
|
||||
GraphicsAPI getAPI() const override { return GraphicsAPI::OpenGL; }
|
||||
const DeviceCaps& getCaps() const override { return caps_; }
|
||||
|
||||
// ========================================================================
|
||||
// 帧管理
|
||||
// ========================================================================
|
||||
|
||||
void beginFrame() override;
|
||||
void endFrame() override;
|
||||
void present() override;
|
||||
void setVSync(bool enabled) override;
|
||||
bool isVSyncEnabled() const override { return vsyncEnabled_; }
|
||||
void setDefaultFramebufferSize(uint32_t width, uint32_t height) override;
|
||||
|
||||
// ========================================================================
|
||||
// 资源创建 - 缓冲区
|
||||
// ========================================================================
|
||||
|
||||
Ptr<RHIBuffer> createBuffer(const BufferDesc& desc) override;
|
||||
Ptr<RHIVertexBuffer> createVertexBuffer(size_t size, BufferUsage usage = BufferUsage::Static,
|
||||
const void* data = nullptr) override;
|
||||
Ptr<RHIIndexBuffer> createIndexBuffer(size_t size, BufferUsage usage = BufferUsage::Static,
|
||||
const void* data = nullptr) override;
|
||||
Ptr<RHIUniformBuffer> createUniformBuffer(size_t size, BufferUsage usage = BufferUsage::Dynamic) override;
|
||||
|
||||
// ========================================================================
|
||||
// 资源创建 - 纹理
|
||||
// ========================================================================
|
||||
|
||||
Ptr<RHITexture> createTexture(const TextureDesc& desc) override;
|
||||
Ptr<RHITexture> createTexture2D(uint32_t width, uint32_t height, Format format,
|
||||
const void* data = nullptr) override;
|
||||
Ptr<RHISampler> createSampler(const SamplerDesc& desc) override;
|
||||
Ptr<RHITextureView> createTextureView(Ptr<RHITexture> texture, const TextureViewDesc& desc) override;
|
||||
|
||||
// ========================================================================
|
||||
// 资源创建 - 着色器
|
||||
// ========================================================================
|
||||
|
||||
Ptr<RHIShader> createShader(const ShaderDesc& desc) override;
|
||||
Ptr<RHIShader> createShaderFromSource(const std::string& name,
|
||||
const std::string& vertexSource,
|
||||
const std::string& fragmentSource) override;
|
||||
Ptr<RHIShader> createShaderFromFile(const std::string& vertexPath,
|
||||
const std::string& fragmentPath) override;
|
||||
|
||||
// ========================================================================
|
||||
// 资源创建 - 管线
|
||||
// ========================================================================
|
||||
|
||||
Ptr<RHIPipeline> createPipeline(const PipelineDesc& desc, Ptr<RHIShader> shader) override;
|
||||
Ptr<RHIPipeline> createPipeline(const PipelineBuilder& builder) override;
|
||||
|
||||
// ========================================================================
|
||||
// 资源创建 - 帧缓冲
|
||||
// ========================================================================
|
||||
|
||||
Ptr<RHIFramebuffer> createFramebuffer(const FramebufferDesc& desc) override;
|
||||
Ptr<RHIFramebuffer> createFramebuffer(const FramebufferBuilder& builder) override;
|
||||
|
||||
// ========================================================================
|
||||
// 渲染状态设置
|
||||
// ========================================================================
|
||||
|
||||
void setViewport(const Viewport& viewport) override;
|
||||
void setScissorRect(const ScissorRect& rect) override;
|
||||
void setScissorEnabled(bool enabled) override;
|
||||
|
||||
// ========================================================================
|
||||
// 渲染命令 - 绑定资源
|
||||
// ========================================================================
|
||||
|
||||
void setPipeline(Ptr<RHIPipeline> pipeline) override;
|
||||
void setShader(Ptr<RHIShader> shader) override;
|
||||
void setVertexBuffer(uint32_t slot, Ptr<RHIBuffer> buffer, uint32_t offset = 0) override;
|
||||
void setIndexBuffer(Ptr<RHIBuffer> buffer, IndexFormat format) override;
|
||||
void setTexture(uint32_t slot, Ptr<RHITexture> texture, Ptr<RHISampler> sampler = nullptr) override;
|
||||
void setUniformBuffer(uint32_t slot, Ptr<RHIBuffer> buffer) override;
|
||||
void setFramebuffer(Ptr<RHIFramebuffer> framebuffer) override;
|
||||
void setDefaultFramebuffer() override;
|
||||
|
||||
// ========================================================================
|
||||
// 渲染命令 - Uniform 设置
|
||||
// ========================================================================
|
||||
|
||||
void setUniformFloat(const std::string& name, float value) override;
|
||||
void setUniformVec2(const std::string& name, const glm::vec2& value) override;
|
||||
void setUniformVec3(const std::string& name, const glm::vec3& value) override;
|
||||
void setUniformVec4(const std::string& name, const glm::vec4& value) override;
|
||||
void setUniformMat4(const std::string& name, const glm::mat4& value) override;
|
||||
void setUniformInt(const std::string& name, int value) override;
|
||||
|
||||
// ========================================================================
|
||||
// 渲染命令 - 绘制
|
||||
// ========================================================================
|
||||
|
||||
void draw(const DrawCommand& cmd) override;
|
||||
void drawIndexed(const DrawIndexedCommand& cmd) override;
|
||||
void drawIndirect(Ptr<RHIBuffer> indirectBuffer, size_t offset) override;
|
||||
void drawIndexedIndirect(Ptr<RHIBuffer> indirectBuffer, size_t offset) override;
|
||||
|
||||
// ========================================================================
|
||||
// 渲染命令 - 清除
|
||||
// ========================================================================
|
||||
|
||||
void clearColor(const ColorValue& color) override;
|
||||
void clearDepth(float depth = 1.0f) override;
|
||||
void clearStencil(uint8_t stencil = 0) override;
|
||||
void clear(const ColorValue& color, float depth = 1.0f, uint8_t stencil = 0) override;
|
||||
|
||||
// ========================================================================
|
||||
// 统计与调试
|
||||
// ========================================================================
|
||||
|
||||
const DeviceStats& getStats() const override { return stats_; }
|
||||
void resetStats() override;
|
||||
bool checkError() override;
|
||||
const std::string& getDebugName() const override { return debugName_; }
|
||||
void setDebugName(const std::string& name) override { debugName_ = name; }
|
||||
|
||||
private:
|
||||
bool initialized_ = false;
|
||||
bool vsyncEnabled_ = true;
|
||||
IWindow* window_ = nullptr;
|
||||
DeviceCaps caps_;
|
||||
DeviceStats stats_;
|
||||
std::string debugName_;
|
||||
|
||||
// 当前绑定的资源
|
||||
Ptr<RHIPipeline> currentPipeline_;
|
||||
Ptr<RHIShader> currentShader_;
|
||||
Ptr<RHIFramebuffer> currentFramebuffer_;
|
||||
IndexFormat currentIndexFormat_ = IndexFormat::UInt16;
|
||||
|
||||
// 默认帧缓冲尺寸
|
||||
uint32_t defaultFBWidth_ = 0;
|
||||
uint32_t defaultFBHeight_ = 0;
|
||||
|
||||
// 裁剪测试状态
|
||||
bool scissorEnabled_ = false;
|
||||
|
||||
/**
|
||||
* @brief 初始化设备能力
|
||||
*/
|
||||
void initCaps();
|
||||
|
||||
/**
|
||||
* @brief 设置默认状态
|
||||
*/
|
||||
void setDefaultState();
|
||||
|
||||
/**
|
||||
* @brief 应用管线状态
|
||||
*/
|
||||
void applyPipelineState(const BlendState& blend,
|
||||
const DepthStencilState& depthStencil,
|
||||
const RasterizerState& rasterizer);
|
||||
};
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/render/rhi/rhi_pipeline.h>
|
||||
#include <glad/glad.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 渲染管线实现
|
||||
*
|
||||
* 封装 OpenGL VAO 和管线状态
|
||||
*/
|
||||
class GLRHIPipeline : public RHIPipeline {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLRHIPipeline();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLRHIPipeline() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化管线
|
||||
*/
|
||||
bool init(const PipelineDesc& desc, Ptr<RHIShader> shader);
|
||||
|
||||
/**
|
||||
* @brief 释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// RHIPipeline 接口实现
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
|
||||
// 状态访问
|
||||
const BlendState& getBlendState() const override { return blendState_; }
|
||||
const DepthStencilState& getDepthStencilState() const override { return depthStencilState_; }
|
||||
const RasterizerState& getRasterizerState() const override { return rasterizerState_; }
|
||||
PrimitiveType getPrimitiveType() const override { return primitiveType_; }
|
||||
Ptr<RHIShader> getShader() const override { return shader_; }
|
||||
|
||||
// 动态状态设置
|
||||
void setBlendMode(BlendFactor srcFactor, BlendFactor dstFactor, BlendOp op = BlendOp::Add) override;
|
||||
void setBlendState(const BlendState& state) override;
|
||||
void setBlendEnabled(bool enabled) override;
|
||||
void setDepthTestEnabled(bool enabled) override;
|
||||
void setDepthWriteEnabled(bool enabled) override;
|
||||
void setDepthCompareFunc(CompareFunc func) override;
|
||||
void setCullMode(CullMode mode) override;
|
||||
void setFrontFace(FrontFace face) override;
|
||||
void setWireframe(bool enabled) override;
|
||||
|
||||
// 顶点布局
|
||||
const std::vector<VertexAttribute>& getVertexAttributes() const override { return vertexAttributes_; }
|
||||
const std::vector<VertexBufferBinding>& getVertexBufferBindings() const override { return vertexBindings_; }
|
||||
uint32_t calculateVertexStride() const override;
|
||||
|
||||
// 有效性检查
|
||||
bool isValid() const override { return vaoID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(vaoID_); }
|
||||
const std::string& getDebugName() const override { return debugName_; }
|
||||
void setDebugName(const std::string& name) override { debugName_ = name; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL VAO ID
|
||||
*/
|
||||
GLuint getVAOID() const { return vaoID_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 图元类型
|
||||
*/
|
||||
GLenum getGLPrimitiveType() const { return glPrimitiveType_; }
|
||||
|
||||
private:
|
||||
GLuint vaoID_ = 0;
|
||||
Ptr<RHIShader> shader_;
|
||||
BlendState blendState_;
|
||||
DepthStencilState depthStencilState_;
|
||||
RasterizerState rasterizerState_;
|
||||
PrimitiveType primitiveType_ = PrimitiveType::Triangles;
|
||||
GLenum glPrimitiveType_ = GL_TRIANGLES;
|
||||
std::vector<VertexAttribute> vertexAttributes_;
|
||||
std::vector<VertexBufferBinding> vertexBindings_;
|
||||
std::string debugName_;
|
||||
|
||||
/**
|
||||
* @brief 设置 VAO 顶点属性
|
||||
*/
|
||||
void setupVertexAttributes();
|
||||
|
||||
/**
|
||||
* @brief 转换图元类型到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertPrimitiveType(PrimitiveType type);
|
||||
|
||||
/**
|
||||
* @brief 转换混合因子到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertBlendFactor(BlendFactor factor);
|
||||
|
||||
/**
|
||||
* @brief 转换混合操作到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertBlendOp(BlendOp op);
|
||||
|
||||
/**
|
||||
* @brief 转换比较函数到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertCompareFunc(CompareFunc func);
|
||||
|
||||
/**
|
||||
* @brief 转换裁剪模式到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertCullMode(CullMode mode);
|
||||
|
||||
/**
|
||||
* @brief 转换正面方向到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertFrontFace(FrontFace face);
|
||||
|
||||
/**
|
||||
* @brief 转换顶点格式到 OpenGL 类型
|
||||
*/
|
||||
static GLenum convertVertexFormat(Format format, bool& normalized);
|
||||
|
||||
/**
|
||||
* @brief 获取顶点格式分量数
|
||||
*/
|
||||
static GLint getFormatComponents(Format format);
|
||||
};
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/render/rhi/rhi_shader.h>
|
||||
#include <glad/glad.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 着色器实现
|
||||
*
|
||||
* 封装 OpenGL 着色器程序
|
||||
*/
|
||||
class GLRHIShader : public RHIShader {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLRHIShader();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLRHIShader() override;
|
||||
|
||||
/**
|
||||
* @brief 从描述初始化着色器
|
||||
*/
|
||||
bool init(const ShaderDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 从源码编译着色器
|
||||
*/
|
||||
bool compileFromSource(const std::string& name,
|
||||
const std::string& vertexSource,
|
||||
const std::string& fragmentSource,
|
||||
const std::string& geometrySource = "");
|
||||
|
||||
/**
|
||||
* @brief 释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// RHIShader 接口实现
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
const std::string& getName() const override { return name_; }
|
||||
bool isValid() const override { return programID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(programID_); }
|
||||
|
||||
// Uniform 设置
|
||||
void setBool(const std::string& name, bool value) override;
|
||||
void setInt(const std::string& name, int value) override;
|
||||
void setUInt(const std::string& name, uint32_t value) override;
|
||||
void setFloat(const std::string& name, float value) override;
|
||||
void setVec2(const std::string& name, const glm::vec2& value) override;
|
||||
void setVec3(const std::string& name, const glm::vec3& value) override;
|
||||
void setVec4(const std::string& name, const glm::vec4& value) override;
|
||||
void setMat2(const std::string& name, const glm::mat2& value) override;
|
||||
void setMat3(const std::string& name, const glm::mat3& value) override;
|
||||
void setMat4(const std::string& name, const glm::mat4& value) override;
|
||||
void setTexture(const std::string& name, uint32_t slot) override;
|
||||
void setIntArray(const std::string& name, const int* values, uint32_t count) override;
|
||||
void setFloatArray(const std::string& name, const float* values, uint32_t count) override;
|
||||
void setVec2Array(const std::string& name, const glm::vec2* values, uint32_t count) override;
|
||||
void setVec3Array(const std::string& name, const glm::vec3* values, uint32_t count) override;
|
||||
void setVec4Array(const std::string& name, const glm::vec4* values, uint32_t count) override;
|
||||
void setMat4Array(const std::string& name, const glm::mat4* values, uint32_t count) override;
|
||||
|
||||
// Uniform 查询
|
||||
bool hasUniform(const std::string& name) const override;
|
||||
int getUniformLocation(const std::string& name) const override;
|
||||
const std::vector<UniformInfo>& getUniforms() const override { return uniforms_; }
|
||||
const std::vector<AttributeInfo>& getAttributes() const override { return attributes_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 程序 ID
|
||||
*/
|
||||
GLuint getProgramID() const { return programID_; }
|
||||
|
||||
private:
|
||||
GLuint programID_ = 0;
|
||||
std::string name_;
|
||||
mutable std::unordered_map<std::string, GLint> uniformCache_;
|
||||
std::vector<UniformInfo> uniforms_;
|
||||
std::vector<AttributeInfo> attributes_;
|
||||
|
||||
/**
|
||||
* @brief 编译单个着色器
|
||||
*/
|
||||
GLuint compileShader(GLenum type, const char* source);
|
||||
|
||||
/**
|
||||
* @brief 链接着色器程序
|
||||
*/
|
||||
bool linkProgram(GLuint vertexShader, GLuint fragmentShader, GLuint geometryShader);
|
||||
|
||||
/**
|
||||
* @brief 反射获取 Uniform 信息
|
||||
*/
|
||||
void reflectUniforms();
|
||||
|
||||
/**
|
||||
* @brief 反射获取属性信息
|
||||
*/
|
||||
void reflectAttributes();
|
||||
|
||||
/**
|
||||
* @brief 获取或缓存 Uniform 位置
|
||||
*/
|
||||
GLint getOrCacheUniformLocation(const std::string& name) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief OpenGL 着色器模块实现
|
||||
*/
|
||||
class GLRHIShaderModule : public RHIShaderModule {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLRHIShaderModule();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLRHIShaderModule() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化着色器模块
|
||||
*/
|
||||
bool init(ShaderStage stage, const std::string& source, const std::string& entryPoint = "main");
|
||||
|
||||
/**
|
||||
* @brief 释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// RHIShaderModule 接口实现
|
||||
ShaderStage getStage() const override { return stage_; }
|
||||
const std::string& getEntryPoint() const override { return entryPoint_; }
|
||||
bool isValid() const override { return shaderID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(shaderID_); }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 着色器 ID
|
||||
*/
|
||||
GLuint getShaderID() const { return shaderID_; }
|
||||
|
||||
private:
|
||||
GLuint shaderID_ = 0;
|
||||
ShaderStage stage_ = ShaderStage::Vertex;
|
||||
std::string entryPoint_;
|
||||
};
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/render/rhi/rhi_texture.h>
|
||||
#include <glad/glad.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 纹理实现
|
||||
*
|
||||
* 使用 OpenGL 4.5 DSA 特性
|
||||
*/
|
||||
class GLRHITexture : public RHITexture {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLRHITexture();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLRHITexture() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理
|
||||
* @param desc 纹理描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(const TextureDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// RHITexture 接口实现
|
||||
TextureType getType() const override { return type_; }
|
||||
Format getFormat() const override { return format_; }
|
||||
uint32_t getWidth() const override { return width_; }
|
||||
uint32_t getHeight() const override { return height_; }
|
||||
uint32_t getDepth() const override { return depth_; }
|
||||
uint32_t getMipLevels() const override { return mipLevels_; }
|
||||
uint32_t getArrayLayers() const override { return arrayLayers_; }
|
||||
uint32_t getSampleCount() const override { return sampleCount_; }
|
||||
void setData(const void* data, uint32_t mipLevel = 0, uint32_t arrayLayer = 0) override;
|
||||
void setSubData(const void* data, uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height, uint32_t mipLevel = 0) override;
|
||||
void generateMipmaps() override;
|
||||
bool isValid() const override { return textureID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(textureID_); }
|
||||
const std::string& getDebugName() const override { return debugName_; }
|
||||
void setDebugName(const std::string& name) override { debugName_ = name; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 纹理 ID
|
||||
*/
|
||||
GLuint getTextureID() const { return textureID_; }
|
||||
|
||||
/**
|
||||
* @brief 绑定到纹理单元
|
||||
*/
|
||||
void bind(uint32_t unit);
|
||||
|
||||
/**
|
||||
* @brief 转换纹理类型到 OpenGL 目标
|
||||
*/
|
||||
static GLenum convertType(TextureType type);
|
||||
|
||||
/**
|
||||
* @brief 转换格式到 OpenGL 内部格式
|
||||
*/
|
||||
static GLenum convertInternalFormat(Format format);
|
||||
|
||||
/**
|
||||
* @brief 转换格式到 OpenGL 格式
|
||||
*/
|
||||
static GLenum convertFormat(Format format);
|
||||
|
||||
/**
|
||||
* @brief 转换格式到 OpenGL 类型
|
||||
*/
|
||||
static GLenum convertDataType(Format format);
|
||||
|
||||
/**
|
||||
* @brief 计算格式字节大小
|
||||
*/
|
||||
static uint32_t getFormatBytes(Format format);
|
||||
|
||||
private:
|
||||
GLuint textureID_ = 0;
|
||||
TextureType type_ = TextureType::Texture2D;
|
||||
Format format_ = Format::RGBA8_UNORM;
|
||||
uint32_t width_ = 0;
|
||||
uint32_t height_ = 0;
|
||||
uint32_t depth_ = 1;
|
||||
uint32_t mipLevels_ = 1;
|
||||
uint32_t arrayLayers_ = 1;
|
||||
uint32_t sampleCount_ = 1;
|
||||
std::string debugName_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief OpenGL 采样器实现
|
||||
*/
|
||||
class GLRHISampler : public RHISampler {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLRHISampler();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLRHISampler() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化采样器
|
||||
* @param desc 采样器描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(const SamplerDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// RHISampler 接口实现
|
||||
FilterMode getMinFilter() const override { return minFilter_; }
|
||||
FilterMode getMagFilter() const override { return magFilter_; }
|
||||
FilterMode getMipFilter() const override { return mipFilter_; }
|
||||
AddressMode getAddressModeU() const override { return addressU_; }
|
||||
AddressMode getAddressModeV() const override { return addressV_; }
|
||||
AddressMode getAddressModeW() const override { return addressW_; }
|
||||
float getMaxAnisotropy() const override { return maxAnisotropy_; }
|
||||
ColorValue getBorderColor() const override { return borderColor_; }
|
||||
bool isValid() const override { return samplerID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(samplerID_); }
|
||||
const std::string& getDebugName() const override { return debugName_; }
|
||||
void setDebugName(const std::string& name) override { debugName_ = name; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 采样器 ID
|
||||
*/
|
||||
GLuint getSamplerID() const { return samplerID_; }
|
||||
|
||||
/**
|
||||
* @brief 绑定到纹理单元
|
||||
*/
|
||||
void bind(uint32_t unit);
|
||||
|
||||
private:
|
||||
GLuint samplerID_ = 0;
|
||||
FilterMode minFilter_ = FilterMode::Linear;
|
||||
FilterMode magFilter_ = FilterMode::Linear;
|
||||
FilterMode mipFilter_ = FilterMode::LinearMipmapLinear;
|
||||
AddressMode addressU_ = AddressMode::ClampToEdge;
|
||||
AddressMode addressV_ = AddressMode::ClampToEdge;
|
||||
AddressMode addressW_ = AddressMode::ClampToEdge;
|
||||
float maxAnisotropy_ = 1.0f;
|
||||
ColorValue borderColor_;
|
||||
std::string debugName_;
|
||||
|
||||
/**
|
||||
* @brief 转换过滤模式到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertFilter(FilterMode filter, FilterMode mipFilter);
|
||||
|
||||
/**
|
||||
* @brief 转换寻址模式到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertAddressMode(AddressMode mode);
|
||||
|
||||
/**
|
||||
* @brief 转换比较函数到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertCompareFunc(CompareFunc func);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief OpenGL 纹理视图实现
|
||||
*/
|
||||
class GLRHITextureView : public RHITextureView {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLRHITextureView();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLRHITextureView() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理视图
|
||||
*/
|
||||
bool init(Ptr<RHITexture> texture, const TextureViewDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// RHITextureView 接口实现
|
||||
Ptr<RHITexture> getTexture() const override { return texture_; }
|
||||
Format getFormat() const override { return format_; }
|
||||
uint32_t getBaseMipLevel() const override { return baseMipLevel_; }
|
||||
uint32_t getMipLevels() const override { return mipLevels_; }
|
||||
uint32_t getBaseArrayLayer() const override { return baseArrayLayer_; }
|
||||
uint32_t getArrayLayers() const override { return arrayLayers_; }
|
||||
bool isValid() const override { return viewID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(viewID_); }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 纹理视图 ID
|
||||
*/
|
||||
GLuint getViewID() const { return viewID_; }
|
||||
|
||||
private:
|
||||
GLuint viewID_ = 0;
|
||||
Ptr<RHITexture> texture_;
|
||||
Format format_ = Format::RGBA8_UNORM;
|
||||
uint32_t baseMipLevel_ = 0;
|
||||
uint32_t mipLevels_ = 1;
|
||||
uint32_t baseArrayLayer_ = 0;
|
||||
uint32_t arrayLayers_ = 1;
|
||||
};
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/core/render_command.h>
|
||||
#include <extra2d/render/rhi/rhi_buffer.h>
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_pipeline.h>
|
||||
#include <extra2d/render/rhi/rhi_shader.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 形状顶点结构
|
||||
*/
|
||||
struct ShapeVertex {
|
||||
Vec2 position;
|
||||
Color color;
|
||||
|
||||
ShapeVertex() : position(), color(Colors::White) {}
|
||||
|
||||
ShapeVertex(const Vec2& pos, const Color& col)
|
||||
: position(pos), color(col) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 形状批次数据
|
||||
*/
|
||||
struct ShapeBatch {
|
||||
rhi::PrimitiveType primitiveType;
|
||||
uint32_t startIndex;
|
||||
uint32_t indexCount;
|
||||
rhi::BlendState blendState;
|
||||
float lineWidth;
|
||||
|
||||
ShapeBatch()
|
||||
: primitiveType(rhi::PrimitiveType::Triangles), startIndex(0),
|
||||
indexCount(0), blendState(rhi::BlendState::alphaBlend()), lineWidth(1.0f) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 形状批处理器配置
|
||||
*/
|
||||
struct ShapeBatcherConfig {
|
||||
uint32_t maxBatchSize;
|
||||
uint32_t circleSegments;
|
||||
bool antialiasLines;
|
||||
|
||||
ShapeBatcherConfig()
|
||||
: maxBatchSize(5000),
|
||||
circleSegments(32),
|
||||
antialiasLines(true) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 形状批处理器
|
||||
*
|
||||
* 支持绘制基本形状:线条、矩形、圆形、三角形、多边形
|
||||
* 自动合并相同渲染状态的形状,减少 Draw Call
|
||||
*/
|
||||
class ShapeBatcher {
|
||||
public:
|
||||
static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 5000;
|
||||
static constexpr uint32_t DEFAULT_CIRCLE_SEGMENTS = 32;
|
||||
|
||||
ShapeBatcher();
|
||||
explicit ShapeBatcher(const ShapeBatcherConfig& config);
|
||||
~ShapeBatcher();
|
||||
|
||||
/**
|
||||
* @brief 初始化批处理器
|
||||
* @param device RHI 设备
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(Ptr<rhi::RHIDevice> device);
|
||||
|
||||
/**
|
||||
* @brief 关闭批处理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 开始批处理
|
||||
*/
|
||||
void begin();
|
||||
|
||||
/**
|
||||
* @brief 开始批处理(指定视图投影矩阵)
|
||||
*/
|
||||
void begin(const glm::mat4& viewProj);
|
||||
|
||||
/**
|
||||
* @brief 结束批处理
|
||||
*/
|
||||
void end();
|
||||
|
||||
/**
|
||||
* @brief 绘制线条
|
||||
*/
|
||||
void drawLine(const Vec2& start, const Vec2& end,
|
||||
const Color& color = Colors::White,
|
||||
float width = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 绘制多条线段
|
||||
*/
|
||||
void drawLines(const std::vector<Vec2>& points,
|
||||
const Color& color = Colors::White,
|
||||
float width = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 绘制折线
|
||||
*/
|
||||
void drawPolyline(const std::vector<Vec2>& points,
|
||||
const Color& color = Colors::White,
|
||||
float width = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 绘制矩形(轮廓)
|
||||
*/
|
||||
void drawRect(const Rect& rect,
|
||||
const Color& color = Colors::White,
|
||||
float width = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 绘制填充矩形
|
||||
*/
|
||||
void drawFilledRect(const Rect& rect,
|
||||
const Color& color = Colors::White);
|
||||
|
||||
/**
|
||||
* @brief 绘制圆形(轮廓)
|
||||
*/
|
||||
void drawCircle(const Vec2& center, float radius,
|
||||
const Color& color = Colors::White,
|
||||
int segments = 0,
|
||||
float width = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 绘制填充圆形
|
||||
*/
|
||||
void drawFilledCircle(const Vec2& center, float radius,
|
||||
const Color& color = Colors::White,
|
||||
int segments = 0);
|
||||
|
||||
/**
|
||||
* @brief 绘制椭圆(轮廓)
|
||||
*/
|
||||
void drawEllipse(const Vec2& center, float radiusX, float radiusY,
|
||||
const Color& color = Colors::White,
|
||||
int segments = 0,
|
||||
float width = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 绘制填充椭圆
|
||||
*/
|
||||
void drawFilledEllipse(const Vec2& center, float radiusX, float radiusY,
|
||||
const Color& color = Colors::White,
|
||||
int segments = 0);
|
||||
|
||||
/**
|
||||
* @brief 绘制三角形(轮廓)
|
||||
*/
|
||||
void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
|
||||
const Color& color = Colors::White,
|
||||
float width = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 绘制填充三角形
|
||||
*/
|
||||
void drawFilledTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
|
||||
const Color& color = Colors::White);
|
||||
|
||||
/**
|
||||
* @brief 绘制多边形(轮廓)
|
||||
*/
|
||||
void drawPolygon(const std::vector<Vec2>& points,
|
||||
const Color& color = Colors::White,
|
||||
float width = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 绘制填充多边形
|
||||
*/
|
||||
void drawFilledPolygon(const std::vector<Vec2>& points,
|
||||
const Color& color = Colors::White);
|
||||
|
||||
/**
|
||||
* @brief 绘制圆弧
|
||||
*/
|
||||
void drawArc(const Vec2& center, float radius,
|
||||
float startAngle, float endAngle,
|
||||
const Color& color = Colors::White,
|
||||
int segments = 0,
|
||||
float width = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 绘制渲染命令
|
||||
*/
|
||||
void draw(const RenderCommand& command);
|
||||
|
||||
/**
|
||||
* @brief 设置视图投影矩阵
|
||||
*/
|
||||
void setViewProjection(const glm::mat4& viewProj);
|
||||
|
||||
/**
|
||||
* @brief 获取当前视图投影矩阵
|
||||
*/
|
||||
const glm::mat4& getViewProjection() const { return viewProj_; }
|
||||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
*/
|
||||
uint32_t getDrawCallCount() const { return drawCallCount_; }
|
||||
uint32_t getShapeCount() const { return shapeCount_; }
|
||||
uint32_t getBatchCount() const { return batchCount_; }
|
||||
|
||||
/**
|
||||
* @brief 重置统计信息
|
||||
*/
|
||||
void resetStats();
|
||||
|
||||
/**
|
||||
* @brief 获取配置
|
||||
*/
|
||||
const ShapeBatcherConfig& getConfig() const { return config_; }
|
||||
|
||||
/**
|
||||
* @brief 设置配置
|
||||
*/
|
||||
void setConfig(const ShapeBatcherConfig& config);
|
||||
|
||||
/**
|
||||
* @brief 获取 RHI 设备
|
||||
*/
|
||||
Ptr<rhi::RHIDevice> getDevice() const { return device_; }
|
||||
|
||||
/**
|
||||
* @brief 获取着色器
|
||||
*/
|
||||
Ptr<rhi::RHIShader> getShader() const { return shader_; }
|
||||
|
||||
/**
|
||||
* @brief 设置自定义着色器
|
||||
*/
|
||||
void setShader(Ptr<rhi::RHIShader> shader);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 创建着色器
|
||||
*/
|
||||
bool createShader();
|
||||
|
||||
/**
|
||||
* @brief 创建管线
|
||||
*/
|
||||
bool createPipeline();
|
||||
|
||||
/**
|
||||
* @brief 创建缓冲区
|
||||
*/
|
||||
bool createBuffers();
|
||||
|
||||
/**
|
||||
* @brief 刷新当前批次
|
||||
*/
|
||||
void flushBatch(rhi::PrimitiveType primitiveType, float lineWidth = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 添加顶点
|
||||
*/
|
||||
uint32_t addVertex(const Vec2& position, const Color& color);
|
||||
|
||||
/**
|
||||
* @brief 添加索引
|
||||
*/
|
||||
void addIndex(uint32_t index);
|
||||
|
||||
/**
|
||||
* @brief 生成圆形顶点
|
||||
*/
|
||||
void generateCircleVertices(const Vec2& center, float radius,
|
||||
int segments, std::vector<Vec2>& vertices) const;
|
||||
|
||||
/**
|
||||
* @brief 生成椭圆顶点
|
||||
*/
|
||||
void generateEllipseVertices(const Vec2& center, float radiusX, float radiusY,
|
||||
int segments, std::vector<Vec2>& vertices) const;
|
||||
|
||||
Ptr<rhi::RHIDevice> device_;
|
||||
Ptr<rhi::RHIShader> shader_;
|
||||
Ptr<rhi::RHIPipeline> pipeline_;
|
||||
Ptr<rhi::RHIPipeline> linePipeline_;
|
||||
Ptr<rhi::RHIVertexBuffer> vertexBuffer_;
|
||||
Ptr<rhi::RHIIndexBuffer> indexBuffer_;
|
||||
Ptr<rhi::RHIUniformBuffer> uniformBuffer_;
|
||||
|
||||
std::vector<ShapeVertex> vertices_;
|
||||
std::vector<uint32_t> indices_;
|
||||
std::vector<ShapeBatch> batches_;
|
||||
|
||||
ShapeBatcherConfig config_;
|
||||
glm::mat4 viewProj_;
|
||||
|
||||
rhi::BlendState currentBlend_;
|
||||
uint32_t currentVertexIndex_;
|
||||
uint32_t currentIndexIndex_;
|
||||
|
||||
uint32_t drawCallCount_;
|
||||
uint32_t shapeCount_;
|
||||
uint32_t batchCount_;
|
||||
|
||||
bool initialized_;
|
||||
bool inBatch_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/core/render_command.h>
|
||||
#include <extra2d/render/rhi/rhi_buffer.h>
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_pipeline.h>
|
||||
#include <extra2d/render/rhi/rhi_shader.h>
|
||||
#include <extra2d/render/rhi/rhi_texture.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 精灵顶点结构
|
||||
*/
|
||||
struct SpriteVertex {
|
||||
Vec2 position;
|
||||
Vec2 texCoord;
|
||||
Color color;
|
||||
|
||||
SpriteVertex() : position(), texCoord(), color(Colors::White) {}
|
||||
|
||||
SpriteVertex(const Vec2& pos, const Vec2& uv, const Color& col)
|
||||
: position(pos), texCoord(uv), color(col) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 精灵批次数据
|
||||
*/
|
||||
struct SpriteBatch {
|
||||
Ptr<rhi::RHITexture> texture;
|
||||
uint32_t startIndex;
|
||||
uint32_t indexCount;
|
||||
rhi::BlendState blendState;
|
||||
glm::mat4 transform;
|
||||
|
||||
SpriteBatch()
|
||||
: texture(nullptr), startIndex(0), indexCount(0),
|
||||
blendState(rhi::BlendState::alphaBlend()), transform(1.0f) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 精灵批处理器配置
|
||||
*/
|
||||
struct SpriteBatcherConfig {
|
||||
uint32_t maxBatchSize;
|
||||
uint32_t maxTextureSlots;
|
||||
bool enableInstancing;
|
||||
bool dynamicVertices;
|
||||
|
||||
SpriteBatcherConfig()
|
||||
: maxBatchSize(10000),
|
||||
maxTextureSlots(8),
|
||||
enableInstancing(false),
|
||||
dynamicVertices(true) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 精灵批处理器
|
||||
*
|
||||
* 自动合并相同纹理和混合状态的精灵,减少 Draw Call
|
||||
* 支持动态顶点缓冲区,适合频繁更新的 2D 场景
|
||||
*/
|
||||
class SpriteBatcher {
|
||||
public:
|
||||
static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 10000;
|
||||
static constexpr uint32_t VERTEX_PER_SPRITE = 4;
|
||||
static constexpr uint32_t INDEX_PER_SPRITE = 6;
|
||||
|
||||
SpriteBatcher();
|
||||
explicit SpriteBatcher(const SpriteBatcherConfig& config);
|
||||
~SpriteBatcher();
|
||||
|
||||
/**
|
||||
* @brief 初始化批处理器
|
||||
* @param device RHI 设备
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(Ptr<rhi::RHIDevice> device);
|
||||
|
||||
/**
|
||||
* @brief 关闭批处理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 开始批处理
|
||||
*/
|
||||
void begin();
|
||||
|
||||
/**
|
||||
* @brief 开始批处理(指定视图投影矩阵)
|
||||
*/
|
||||
void begin(const glm::mat4& viewProj);
|
||||
|
||||
/**
|
||||
* @brief 结束批处理
|
||||
*/
|
||||
void end();
|
||||
|
||||
/**
|
||||
* @brief 绘制精灵
|
||||
* @param texture 纹理
|
||||
* @param destRect 目标矩形
|
||||
* @param srcRect 源矩形(纹理坐标)
|
||||
* @param color 颜色调制
|
||||
* @param rotation 旋转角度(度)
|
||||
* @param anchor 锚点
|
||||
* @param blend 混合状态
|
||||
*/
|
||||
void draw(Ptr<rhi::RHITexture> texture,
|
||||
const Rect& destRect,
|
||||
const Rect& srcRect,
|
||||
const Color& color = Colors::White,
|
||||
float rotation = 0.0f,
|
||||
const Vec2& anchor = Vec2(0.5f, 0.5f),
|
||||
const rhi::BlendState& blend = rhi::BlendState::alphaBlend());
|
||||
|
||||
/**
|
||||
* @brief 绘制精灵(使用变换矩阵)
|
||||
*/
|
||||
void draw(Ptr<rhi::RHITexture> texture,
|
||||
const Rect& srcRect,
|
||||
const glm::mat4& transform,
|
||||
const Color& color = Colors::White,
|
||||
const rhi::BlendState& blend = rhi::BlendState::alphaBlend());
|
||||
|
||||
/**
|
||||
* @brief 绘制精灵(使用渲染命令)
|
||||
*/
|
||||
void draw(const RenderCommand& command);
|
||||
|
||||
/**
|
||||
* @brief 绘制精灵序列
|
||||
*/
|
||||
void drawBatch(const std::vector<SpriteRenderData>& sprites);
|
||||
|
||||
/**
|
||||
* @brief 设置视图投影矩阵
|
||||
*/
|
||||
void setViewProjection(const glm::mat4& viewProj);
|
||||
|
||||
/**
|
||||
* @brief 获取当前视图投影矩阵
|
||||
*/
|
||||
const glm::mat4& getViewProjection() const { return viewProj_; }
|
||||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
*/
|
||||
uint32_t getDrawCallCount() const { return drawCallCount_; }
|
||||
uint32_t getSpriteCount() const { return spriteCount_; }
|
||||
uint32_t getBatchCount() const { return batchCount_; }
|
||||
|
||||
/**
|
||||
* @brief 重置统计信息
|
||||
*/
|
||||
void resetStats();
|
||||
|
||||
/**
|
||||
* @brief 获取配置
|
||||
*/
|
||||
const SpriteBatcherConfig& getConfig() const { return config_; }
|
||||
|
||||
/**
|
||||
* @brief 设置配置
|
||||
*/
|
||||
void setConfig(const SpriteBatcherConfig& config);
|
||||
|
||||
/**
|
||||
* @brief 获取最大批次大小
|
||||
*/
|
||||
uint32_t getMaxBatchSize() const { return config_.maxBatchSize; }
|
||||
|
||||
/**
|
||||
* @brief 获取 RHI 设备
|
||||
*/
|
||||
Ptr<rhi::RHIDevice> getDevice() const { return device_; }
|
||||
|
||||
/**
|
||||
* @brief 获取着色器
|
||||
*/
|
||||
Ptr<rhi::RHIShader> getShader() const { return shader_; }
|
||||
|
||||
/**
|
||||
* @brief 设置自定义着色器
|
||||
*/
|
||||
void setShader(Ptr<rhi::RHIShader> shader);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 创建着色器
|
||||
*/
|
||||
bool createShader();
|
||||
|
||||
/**
|
||||
* @brief 创建管线
|
||||
*/
|
||||
bool createPipeline();
|
||||
|
||||
/**
|
||||
* @brief 创建缓冲区
|
||||
*/
|
||||
bool createBuffers();
|
||||
|
||||
/**
|
||||
* @brief 刷新当前批次
|
||||
*/
|
||||
void flushBatch();
|
||||
|
||||
/**
|
||||
* @brief 检查是否可以添加到当前批次
|
||||
*/
|
||||
bool canAddToBatch(Ptr<rhi::RHITexture> texture, const rhi::BlendState& blend) const;
|
||||
|
||||
/**
|
||||
* @brief 添加精灵顶点
|
||||
*/
|
||||
void addSpriteVertices(const Rect& destRect,
|
||||
const Rect& srcRect,
|
||||
const Color& color,
|
||||
float rotation,
|
||||
const Vec2& anchor);
|
||||
|
||||
/**
|
||||
* @brief 计算纹理坐标
|
||||
*/
|
||||
Vec2 calcTexCoord(const Vec2& pos, uint32_t texWidth, uint32_t texHeight) const;
|
||||
|
||||
/**
|
||||
* @brief 计算旋转后的顶点
|
||||
*/
|
||||
void rotatePoint(Vec2& point, float rotation, const Vec2& center) const;
|
||||
|
||||
Ptr<rhi::RHIDevice> device_;
|
||||
Ptr<rhi::RHIShader> shader_;
|
||||
Ptr<rhi::RHIPipeline> pipeline_;
|
||||
Ptr<rhi::RHIVertexBuffer> vertexBuffer_;
|
||||
Ptr<rhi::RHIIndexBuffer> indexBuffer_;
|
||||
Ptr<rhi::RHIUniformBuffer> uniformBuffer_;
|
||||
|
||||
std::vector<SpriteVertex> vertices_;
|
||||
std::vector<uint32_t> indices_;
|
||||
std::vector<SpriteBatch> batches_;
|
||||
|
||||
SpriteBatcherConfig config_;
|
||||
glm::mat4 viewProj_;
|
||||
glm::mat4 currentTransform_;
|
||||
|
||||
Ptr<rhi::RHITexture> currentTexture_;
|
||||
rhi::BlendState currentBlend_;
|
||||
uint32_t currentVertexIndex_;
|
||||
uint32_t currentIndexIndex_;
|
||||
|
||||
uint32_t drawCallCount_;
|
||||
uint32_t spriteCount_;
|
||||
uint32_t batchCount_;
|
||||
|
||||
bool initialized_;
|
||||
bool inBatch_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/core/render_command.h>
|
||||
#include <extra2d/render/rhi/rhi_buffer.h>
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_pipeline.h>
|
||||
#include <extra2d/render/rhi/rhi_shader.h>
|
||||
#include <extra2d/render/rhi/rhi_texture.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <extra2d/resources/font_atlas.h>
|
||||
#include <extra2d/resources/font_manager.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 文本顶点结构
|
||||
*/
|
||||
struct TextVertex {
|
||||
Vec2 position;
|
||||
Vec2 texCoord;
|
||||
Color color;
|
||||
|
||||
TextVertex() : position(), texCoord(), color(Colors::White) {}
|
||||
|
||||
TextVertex(const Vec2& pos, const Vec2& uv, const Color& col)
|
||||
: position(pos), texCoord(uv), color(col) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 文本批次数据
|
||||
*/
|
||||
struct TextBatch {
|
||||
Ptr<rhi::RHITexture> texture;
|
||||
uint32_t startIndex;
|
||||
uint32_t indexCount;
|
||||
rhi::BlendState blendState;
|
||||
|
||||
TextBatch()
|
||||
: texture(nullptr), startIndex(0), indexCount(0),
|
||||
blendState(rhi::BlendState::alphaBlend()) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 文本对齐方式
|
||||
*/
|
||||
enum class TextAlignment {
|
||||
Left,
|
||||
Center,
|
||||
Right
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 文本垂直对齐方式
|
||||
*/
|
||||
enum class TextVerticalAlignment {
|
||||
Top,
|
||||
Middle,
|
||||
Bottom
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 文本绘制选项
|
||||
*/
|
||||
struct TextDrawOptions {
|
||||
Color color = Colors::White;
|
||||
float scale = 1.0f;
|
||||
TextAlignment alignment = TextAlignment::Left;
|
||||
TextVerticalAlignment verticalAlignment = TextVerticalAlignment::Top;
|
||||
float maxWidth = 0.0f;
|
||||
float lineHeight = 0.0f;
|
||||
bool wordWrap = false;
|
||||
rhi::BlendState blend = rhi::BlendState::alphaBlend();
|
||||
|
||||
TextDrawOptions() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 文本批处理器配置
|
||||
*/
|
||||
struct TextBatcherConfig {
|
||||
uint32_t maxBatchSize;
|
||||
bool enableSDF;
|
||||
|
||||
TextBatcherConfig()
|
||||
: maxBatchSize(5000),
|
||||
enableSDF(true) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 文本批处理器
|
||||
*
|
||||
* 支持使用字体图集渲染文本
|
||||
* 自动缓存字形,支持动态加载
|
||||
*/
|
||||
class TextBatcher {
|
||||
public:
|
||||
static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 5000;
|
||||
static constexpr uint32_t VERTEX_PER_GLYPH = 4;
|
||||
static constexpr uint32_t INDEX_PER_GLYPH = 6;
|
||||
|
||||
TextBatcher();
|
||||
explicit TextBatcher(const TextBatcherConfig& config);
|
||||
~TextBatcher();
|
||||
|
||||
/**
|
||||
* @brief 初始化批处理器
|
||||
* @param device RHI 设备
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(Ptr<rhi::RHIDevice> device);
|
||||
|
||||
/**
|
||||
* @brief 关闭批处理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 开始批处理
|
||||
*/
|
||||
void begin();
|
||||
|
||||
/**
|
||||
* @brief 开始批处理(指定视图投影矩阵)
|
||||
*/
|
||||
void begin(const glm::mat4& viewProj);
|
||||
|
||||
/**
|
||||
* @brief 结束批处理
|
||||
*/
|
||||
void end();
|
||||
|
||||
/**
|
||||
* @brief 绘制文本
|
||||
* @param text 文本(UTF-32)
|
||||
* @param position 位置
|
||||
* @param font 字体数据
|
||||
* @param fontSize 字体大小
|
||||
* @param options 绘制选项
|
||||
*/
|
||||
void drawText(const std::u32string& text,
|
||||
const Vec2& position,
|
||||
FontData* font,
|
||||
int fontSize,
|
||||
const TextDrawOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 绘制文本(UTF-8)
|
||||
*/
|
||||
void drawTextUTF8(const std::string& text,
|
||||
const Vec2& position,
|
||||
FontData* font,
|
||||
int fontSize,
|
||||
const TextDrawOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 绘制文本(使用资源句柄)
|
||||
*/
|
||||
void drawText(const std::u32string& text,
|
||||
const Vec2& position,
|
||||
Ptr<FontData> font,
|
||||
int fontSize,
|
||||
const TextDrawOptions& options = {}) {
|
||||
drawText(text, position, font.get(), fontSize, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制文本(使用资源句柄,UTF-8)
|
||||
*/
|
||||
void drawTextUTF8(const std::string& text,
|
||||
const Vec2& position,
|
||||
Ptr<FontData> font,
|
||||
int fontSize,
|
||||
const TextDrawOptions& options = {}) {
|
||||
drawTextUTF8(text, position, font.get(), fontSize, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制渲染命令
|
||||
*/
|
||||
void draw(const RenderCommand& command);
|
||||
|
||||
/**
|
||||
* @brief 计算文本尺寸
|
||||
*/
|
||||
Size measureText(const std::u32string& text,
|
||||
FontData* font,
|
||||
int fontSize,
|
||||
float scale = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 计算文本宽度
|
||||
*/
|
||||
float measureTextWidth(const std::u32string& text,
|
||||
FontData* font,
|
||||
int fontSize,
|
||||
float scale = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 计算文本尺寸(使用资源句柄)
|
||||
*/
|
||||
Size measureText(const std::u32string& text,
|
||||
Ptr<FontData> font,
|
||||
int fontSize,
|
||||
float scale = 1.0f) {
|
||||
return measureText(text, font.get(), fontSize, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算文本宽度(使用资源句柄)
|
||||
*/
|
||||
float measureTextWidth(const std::u32string& text,
|
||||
Ptr<FontData> font,
|
||||
int fontSize,
|
||||
float scale = 1.0f) {
|
||||
return measureTextWidth(text, font.get(), fontSize, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视图投影矩阵
|
||||
*/
|
||||
void setViewProjection(const glm::mat4& viewProj);
|
||||
|
||||
/**
|
||||
* @brief 获取当前视图投影矩阵
|
||||
*/
|
||||
const glm::mat4& getViewProjection() const { return viewProj_; }
|
||||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
*/
|
||||
uint32_t getDrawCallCount() const { return drawCallCount_; }
|
||||
uint32_t getGlyphCount() const { return glyphCount_; }
|
||||
uint32_t getBatchCount() const { return batchCount_; }
|
||||
|
||||
/**
|
||||
* @brief 重置统计信息
|
||||
*/
|
||||
void resetStats();
|
||||
|
||||
/**
|
||||
* @brief 获取配置
|
||||
*/
|
||||
const TextBatcherConfig& getConfig() const { return config_; }
|
||||
|
||||
/**
|
||||
* @brief 设置配置
|
||||
*/
|
||||
void setConfig(const TextBatcherConfig& config);
|
||||
|
||||
/**
|
||||
* @brief 获取 RHI 设备
|
||||
*/
|
||||
Ptr<rhi::RHIDevice> getDevice() const { return device_; }
|
||||
|
||||
/**
|
||||
* @brief 获取着色器
|
||||
*/
|
||||
Ptr<rhi::RHIShader> getShader() const { return shader_; }
|
||||
|
||||
/**
|
||||
* @brief 设置自定义着色器
|
||||
*/
|
||||
void setShader(Ptr<rhi::RHIShader> shader);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 创建着色器
|
||||
*/
|
||||
bool createShader();
|
||||
|
||||
/**
|
||||
* @brief 创建管线
|
||||
*/
|
||||
bool createPipeline();
|
||||
|
||||
/**
|
||||
* @brief 创建缓冲区
|
||||
*/
|
||||
bool createBuffers();
|
||||
|
||||
/**
|
||||
* @brief 刷新当前批次
|
||||
*/
|
||||
void flushBatch();
|
||||
|
||||
/**
|
||||
* @brief 检查是否可以添加到当前批次
|
||||
*/
|
||||
bool canAddToBatch(Ptr<rhi::RHITexture> texture, const rhi::BlendState& blend) const;
|
||||
|
||||
/**
|
||||
* @brief 添加字形顶点
|
||||
*/
|
||||
void addGlyphVertices(const Vec2& position,
|
||||
const GlyphInfo* glyph,
|
||||
const Color& color,
|
||||
float scale);
|
||||
|
||||
/**
|
||||
* @brief 获取字体图集
|
||||
*/
|
||||
Ptr<FontAtlas> getFontAtlas(FontData* font, int fontSize);
|
||||
|
||||
Ptr<rhi::RHIDevice> device_;
|
||||
Ptr<rhi::RHIShader> shader_;
|
||||
Ptr<rhi::RHIPipeline> pipeline_;
|
||||
Ptr<rhi::RHIVertexBuffer> vertexBuffer_;
|
||||
Ptr<rhi::RHIIndexBuffer> indexBuffer_;
|
||||
Ptr<rhi::RHIUniformBuffer> uniformBuffer_;
|
||||
|
||||
std::vector<TextVertex> vertices_;
|
||||
std::vector<uint32_t> indices_;
|
||||
std::vector<TextBatch> batches_;
|
||||
|
||||
TextBatcherConfig config_;
|
||||
glm::mat4 viewProj_;
|
||||
|
||||
Ptr<rhi::RHITexture> currentTexture_;
|
||||
rhi::BlendState currentBlend_;
|
||||
uint32_t currentVertexIndex_;
|
||||
uint32_t currentIndexIndex_;
|
||||
|
||||
uint32_t drawCallCount_;
|
||||
uint32_t glyphCount_;
|
||||
uint32_t batchCount_;
|
||||
|
||||
bool initialized_;
|
||||
bool inBatch_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace rhi {
|
||||
class RHITexture;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染命令类型枚举
|
||||
*/
|
||||
enum class RenderCommandType : uint8_t {
|
||||
None = 0,
|
||||
Sprite,
|
||||
Text,
|
||||
Shape,
|
||||
Custom
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 形状类型枚举
|
||||
*/
|
||||
enum class ShapeType : uint8_t {
|
||||
Line,
|
||||
Rect,
|
||||
FilledRect,
|
||||
Circle,
|
||||
FilledCircle,
|
||||
Triangle,
|
||||
FilledTriangle,
|
||||
Polygon,
|
||||
FilledPolygon
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 精灵渲染数据
|
||||
*/
|
||||
struct SpriteRenderData {
|
||||
Ptr<rhi::RHITexture> texture;
|
||||
Rect destRect;
|
||||
Rect srcRect;
|
||||
Color tint;
|
||||
float rotation;
|
||||
Vec2 anchor;
|
||||
rhi::BlendState blend;
|
||||
uint64_t sortKey;
|
||||
|
||||
SpriteRenderData()
|
||||
: texture(nullptr), destRect(), srcRect(), tint(Colors::White),
|
||||
rotation(0.0f), anchor(0.0f, 0.0f), blend(rhi::BlendState::alphaBlend()), sortKey(0) {}
|
||||
|
||||
SpriteRenderData(Ptr<rhi::RHITexture> tex, const Rect& dest, const Rect& src,
|
||||
const Color& t, float rot, const Vec2& anc,
|
||||
const rhi::BlendState& b = rhi::BlendState::alphaBlend(),
|
||||
uint64_t key = 0)
|
||||
: texture(tex), destRect(dest), srcRect(src), tint(t),
|
||||
rotation(rot), anchor(anc), blend(b), sortKey(key) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 文本渲染数据
|
||||
*/
|
||||
struct TextRenderData {
|
||||
std::u32string text;
|
||||
Vec2 position;
|
||||
Color color;
|
||||
float fontSize;
|
||||
std::string fontName;
|
||||
rhi::BlendState blend;
|
||||
|
||||
TextRenderData()
|
||||
: text(), position(), color(Colors::White),
|
||||
fontSize(16.0f), fontName(), blend(rhi::BlendState::alphaBlend()) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 形状渲染数据
|
||||
*/
|
||||
struct ShapeRenderData {
|
||||
ShapeType shapeType;
|
||||
std::vector<Vec2> points;
|
||||
Color color;
|
||||
float lineWidth;
|
||||
float radius;
|
||||
int segments;
|
||||
rhi::BlendState blend;
|
||||
|
||||
ShapeRenderData()
|
||||
: shapeType(ShapeType::Rect), points(), color(Colors::White),
|
||||
lineWidth(1.0f), radius(0.0f), segments(32),
|
||||
blend(rhi::BlendState::alphaBlend()) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 自定义渲染数据
|
||||
*/
|
||||
struct CustomRenderData {
|
||||
uint32_t callbackId;
|
||||
void* userData;
|
||||
rhi::BlendState blend;
|
||||
|
||||
CustomRenderData()
|
||||
: callbackId(0), userData(nullptr), blend(rhi::BlendState::alphaBlend()) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 统一渲染命令结构
|
||||
*/
|
||||
struct RenderCommand {
|
||||
RenderCommandType type;
|
||||
int layer;
|
||||
int zOrder;
|
||||
glm::mat4 transform;
|
||||
std::variant<SpriteRenderData, TextRenderData, ShapeRenderData, CustomRenderData> data;
|
||||
|
||||
RenderCommand()
|
||||
: type(RenderCommandType::None), layer(0), zOrder(0), transform(1.0f) {}
|
||||
|
||||
/**
|
||||
* @brief 创建精灵渲染命令
|
||||
*/
|
||||
static RenderCommand makeSprite(Ptr<rhi::RHITexture> texture,
|
||||
const Rect& destRect,
|
||||
const Rect& srcRect,
|
||||
const Color& tint = Colors::White,
|
||||
float rotation = 0.0f,
|
||||
const Vec2& anchor = Vec2(0.0f, 0.0f),
|
||||
const rhi::BlendState& blend = rhi::BlendState::alphaBlend(),
|
||||
int layer = 0,
|
||||
int zOrder = 0);
|
||||
|
||||
/**
|
||||
* @brief 创建文本渲染命令
|
||||
*/
|
||||
static RenderCommand makeText(const std::u32string& text,
|
||||
const Vec2& position,
|
||||
const Color& color = Colors::White,
|
||||
float fontSize = 16.0f,
|
||||
const std::string& fontName = "",
|
||||
int layer = 0,
|
||||
int zOrder = 0);
|
||||
|
||||
/**
|
||||
* @brief 创建线条渲染命令
|
||||
*/
|
||||
static RenderCommand makeLine(const Vec2& start,
|
||||
const Vec2& end,
|
||||
const Color& color = Colors::White,
|
||||
float width = 1.0f,
|
||||
int layer = 0,
|
||||
int zOrder = 0);
|
||||
|
||||
/**
|
||||
* @brief 创建矩形渲染命令
|
||||
*/
|
||||
static RenderCommand makeRect(const Rect& rect,
|
||||
const Color& color = Colors::White,
|
||||
float width = 1.0f,
|
||||
bool filled = false,
|
||||
int layer = 0,
|
||||
int zOrder = 0);
|
||||
|
||||
/**
|
||||
* @brief 创建圆形渲染命令
|
||||
*/
|
||||
static RenderCommand makeCircle(const Vec2& center,
|
||||
float radius,
|
||||
const Color& color = Colors::White,
|
||||
int segments = 32,
|
||||
bool filled = false,
|
||||
int layer = 0,
|
||||
int zOrder = 0);
|
||||
|
||||
/**
|
||||
* @brief 创建三角形渲染命令
|
||||
*/
|
||||
static RenderCommand makeTriangle(const Vec2& p1,
|
||||
const Vec2& p2,
|
||||
const Vec2& p3,
|
||||
const Color& color = Colors::White,
|
||||
bool filled = false,
|
||||
int layer = 0,
|
||||
int zOrder = 0);
|
||||
|
||||
/**
|
||||
* @brief 创建多边形渲染命令
|
||||
*/
|
||||
static RenderCommand makePolygon(const std::vector<Vec2>& points,
|
||||
const Color& color = Colors::White,
|
||||
bool filled = false,
|
||||
int layer = 0,
|
||||
int zOrder = 0);
|
||||
|
||||
/**
|
||||
* @brief 创建自定义渲染命令
|
||||
*/
|
||||
static RenderCommand makeCustom(uint32_t callbackId,
|
||||
void* userData = nullptr,
|
||||
const rhi::BlendState& blend = rhi::BlendState::alphaBlend(),
|
||||
int layer = 0,
|
||||
int zOrder = 0);
|
||||
|
||||
/**
|
||||
* @brief 计算排序键
|
||||
*/
|
||||
uint64_t calculateSortKey() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染命令比较器
|
||||
*/
|
||||
struct RenderCommandComparator {
|
||||
bool operator()(const RenderCommand& a, const RenderCommand& b) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/core/render_queue.h>
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class IWindow;
|
||||
|
||||
/**
|
||||
* @brief 渲染统计信息
|
||||
*/
|
||||
struct RenderStats {
|
||||
uint32_t drawCalls;
|
||||
uint32_t batchCount;
|
||||
uint32_t vertexCount;
|
||||
uint32_t triangleCount;
|
||||
uint32_t spriteCount;
|
||||
uint32_t textCount;
|
||||
uint32_t shapeCount;
|
||||
float frameTime;
|
||||
float renderTime;
|
||||
|
||||
RenderStats()
|
||||
: drawCalls(0), batchCount(0), vertexCount(0), triangleCount(0),
|
||||
spriteCount(0), textCount(0), shapeCount(0), frameTime(0.0f), renderTime(0.0f) {}
|
||||
|
||||
void reset() {
|
||||
drawCalls = 0;
|
||||
batchCount = 0;
|
||||
vertexCount = 0;
|
||||
triangleCount = 0;
|
||||
spriteCount = 0;
|
||||
textCount = 0;
|
||||
shapeCount = 0;
|
||||
frameTime = 0.0f;
|
||||
renderTime = 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染上下文配置
|
||||
*/
|
||||
struct RenderContextConfig {
|
||||
uint32_t maxBatchSize;
|
||||
bool enableBatching;
|
||||
bool enableScissor;
|
||||
bool enableStats;
|
||||
rhi::GraphicsAPI preferredAPI;
|
||||
|
||||
RenderContextConfig()
|
||||
: maxBatchSize(10000),
|
||||
enableBatching(true),
|
||||
enableScissor(true),
|
||||
enableStats(true),
|
||||
preferredAPI(rhi::GraphicsAPI::OpenGL) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染上下文
|
||||
*
|
||||
* 管理渲染状态、命令队列和 RHI 设备
|
||||
*/
|
||||
class RenderContext {
|
||||
public:
|
||||
static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 10000;
|
||||
|
||||
RenderContext();
|
||||
explicit RenderContext(const RenderContextConfig& config);
|
||||
~RenderContext();
|
||||
|
||||
/**
|
||||
* @brief 初始化渲染上下文
|
||||
*/
|
||||
bool init(IWindow* window);
|
||||
bool init(IWindow* window, rhi::GraphicsAPI api);
|
||||
|
||||
/**
|
||||
* @brief 关闭渲染上下文
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否有效
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/**
|
||||
* @brief 开始帧
|
||||
*/
|
||||
void beginFrame();
|
||||
|
||||
/**
|
||||
* @brief 结束帧
|
||||
*/
|
||||
void endFrame();
|
||||
|
||||
/**
|
||||
* @brief 呈现帧
|
||||
*/
|
||||
void present();
|
||||
|
||||
/**
|
||||
* @brief 清除屏幕
|
||||
*/
|
||||
void clear(const Color& color = Colors::Black);
|
||||
void clear(const Color& color, float depth, uint8_t stencil = 0);
|
||||
|
||||
/**
|
||||
* @brief 获取/设置视口
|
||||
*/
|
||||
void setViewport(const rhi::Viewport& viewport);
|
||||
void setViewport(float x, float y, float width, float height);
|
||||
rhi::Viewport getViewport() const;
|
||||
|
||||
/**
|
||||
* @brief 获取/设置裁剪区域
|
||||
*/
|
||||
void setScissorRect(const rhi::ScissorRect& rect);
|
||||
void setScissorRect(int32_t x, int32_t y, uint32_t width, uint32_t height);
|
||||
void setScissorEnabled(bool enabled);
|
||||
rhi::ScissorRect getScissorRect() const;
|
||||
|
||||
/**
|
||||
* @brief 变换矩阵栈操作
|
||||
*/
|
||||
void pushTransform(const glm::mat4& transform);
|
||||
void popTransform();
|
||||
void resetTransform();
|
||||
glm::mat4 getCurrentTransform() const;
|
||||
|
||||
/**
|
||||
* @brief 混合状态操作
|
||||
*/
|
||||
void setBlendState(const rhi::BlendState& blend);
|
||||
rhi::BlendState getBlendState() const;
|
||||
void pushBlendState(const rhi::BlendState& blend);
|
||||
void popBlendState();
|
||||
|
||||
/**
|
||||
* @brief 渲染队列操作
|
||||
*/
|
||||
RenderQueue& getQueue() { return queue_; }
|
||||
const RenderQueue& getQueue() const { return queue_; }
|
||||
void clearQueue();
|
||||
|
||||
/**
|
||||
* @brief 提交渲染队列
|
||||
*/
|
||||
void flushQueue();
|
||||
|
||||
/**
|
||||
* @brief 获取 RHI 设备
|
||||
*/
|
||||
Ptr<rhi::RHIDevice> getDevice() const { return device_; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口
|
||||
*/
|
||||
IWindow* getWindow() const { return window_; }
|
||||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
*/
|
||||
const RenderStats& getStats() const { return stats_; }
|
||||
void resetStats();
|
||||
|
||||
/**
|
||||
* @brief 获取配置
|
||||
*/
|
||||
const RenderContextConfig& getConfig() const { return config_; }
|
||||
void setConfig(const RenderContextConfig& config);
|
||||
|
||||
/**
|
||||
* @brief 获取帧缓冲尺寸
|
||||
*/
|
||||
uint32_t getFramebufferWidth() const { return fbWidth_; }
|
||||
uint32_t getFramebufferHeight() const { return fbHeight_; }
|
||||
|
||||
/**
|
||||
* @brief 设置垂直同步
|
||||
*/
|
||||
void setVSync(bool enabled);
|
||||
bool isVSyncEnabled() const;
|
||||
|
||||
/**
|
||||
* @brief 设置默认帧缓冲尺寸
|
||||
*/
|
||||
void setDefaultFramebufferSize(uint32_t width, uint32_t height);
|
||||
|
||||
private:
|
||||
void initDefaultState();
|
||||
|
||||
Ptr<rhi::RHIDevice> device_;
|
||||
IWindow* window_;
|
||||
RenderQueue queue_;
|
||||
RenderStats stats_;
|
||||
RenderContextConfig config_;
|
||||
|
||||
std::stack<glm::mat4> transformStack_;
|
||||
std::stack<rhi::BlendState> blendStack_;
|
||||
rhi::BlendState currentBlend_;
|
||||
rhi::Viewport viewport_;
|
||||
rhi::ScissorRect scissorRect_;
|
||||
bool scissorEnabled_;
|
||||
|
||||
uint32_t fbWidth_;
|
||||
uint32_t fbHeight_;
|
||||
bool initialized_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/render/core/render_command.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 渲染队列排序模式
|
||||
*/
|
||||
enum class RenderSortMode {
|
||||
None,
|
||||
BackToFront,
|
||||
FrontToBack,
|
||||
ByTexture,
|
||||
ByLayer,
|
||||
Custom
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染队列统计信息
|
||||
*/
|
||||
struct RenderQueueStats {
|
||||
uint32_t totalCommands;
|
||||
uint32_t spriteCommands;
|
||||
uint32_t textCommands;
|
||||
uint32_t shapeCommands;
|
||||
uint32_t customCommands;
|
||||
uint32_t batchCount;
|
||||
uint32_t drawCalls;
|
||||
|
||||
RenderQueueStats()
|
||||
: totalCommands(0), spriteCommands(0), textCommands(0),
|
||||
shapeCommands(0), customCommands(0), batchCount(0), drawCalls(0) {}
|
||||
|
||||
void reset() {
|
||||
totalCommands = 0;
|
||||
spriteCommands = 0;
|
||||
textCommands = 0;
|
||||
shapeCommands = 0;
|
||||
customCommands = 0;
|
||||
batchCount = 0;
|
||||
drawCalls = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染队列
|
||||
*
|
||||
* 收集、排序和管理渲染命令
|
||||
*/
|
||||
class RenderQueue {
|
||||
public:
|
||||
using CustomSortFunc = std::function<bool(const RenderCommand&, const RenderCommand&)>;
|
||||
|
||||
static constexpr size_t DEFAULT_CAPACITY = 1024;
|
||||
static constexpr size_t MAX_CAPACITY = 65536;
|
||||
|
||||
RenderQueue();
|
||||
explicit RenderQueue(size_t initialCapacity);
|
||||
~RenderQueue();
|
||||
|
||||
/**
|
||||
* @brief 添加渲染命令
|
||||
*/
|
||||
void addCommand(const RenderCommand& cmd);
|
||||
void addCommand(RenderCommand&& cmd);
|
||||
|
||||
/**
|
||||
* @brief 批量添加渲染命令
|
||||
*/
|
||||
void addCommands(const std::vector<RenderCommand>& commands);
|
||||
|
||||
/**
|
||||
* @brief 原地构造渲染命令
|
||||
*/
|
||||
template<typename... Args>
|
||||
RenderCommand& emplaceCommand(Args&&... args) {
|
||||
commands_.emplace_back(std::forward<Args>(args)...);
|
||||
stats_.totalCommands++;
|
||||
updateStatsForType(commands_.back().type);
|
||||
return commands_.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 排序渲染命令
|
||||
*/
|
||||
void sort(RenderSortMode mode = RenderSortMode::ByTexture);
|
||||
void sortCustom(CustomSortFunc sortFunc);
|
||||
|
||||
/**
|
||||
* @brief 清空队列
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 预分配空间
|
||||
*/
|
||||
void reserve(size_t capacity);
|
||||
|
||||
/**
|
||||
* @brief 获取命令列表
|
||||
*/
|
||||
const std::vector<RenderCommand>& getCommands() const { return commands_; }
|
||||
std::vector<RenderCommand>& getCommands() { return commands_; }
|
||||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
*/
|
||||
const RenderQueueStats& getStats() const { return stats_; }
|
||||
|
||||
/**
|
||||
* @brief 获取命令数量
|
||||
*/
|
||||
size_t size() const { return commands_.size(); }
|
||||
bool empty() const { return commands_.empty(); }
|
||||
size_t capacity() const { return commands_.capacity(); }
|
||||
|
||||
/**
|
||||
* @brief 设置排序模式
|
||||
*/
|
||||
void setSortMode(RenderSortMode mode) { sortMode_ = mode; }
|
||||
RenderSortMode getSortMode() const { return sortMode_; }
|
||||
|
||||
/**
|
||||
* @brief 过滤命令
|
||||
*/
|
||||
template<typename Predicate>
|
||||
void filter(Predicate pred) {
|
||||
auto it = std::remove_if(commands_.begin(), commands_.end(),
|
||||
[&pred](const RenderCommand& cmd) { return !pred(cmd); });
|
||||
commands_.erase(it, commands_.end());
|
||||
recalculateStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按类型获取命令
|
||||
*/
|
||||
std::vector<RenderCommand> getCommandsByType(RenderCommandType type) const;
|
||||
|
||||
/**
|
||||
* @brief 按层级获取命令
|
||||
*/
|
||||
std::vector<RenderCommand> getCommandsByLayer(int layer) const;
|
||||
|
||||
/**
|
||||
* @brief 合并队列
|
||||
*/
|
||||
void merge(const RenderQueue& other);
|
||||
void merge(RenderQueue&& other);
|
||||
|
||||
private:
|
||||
void updateStatsForType(RenderCommandType type);
|
||||
void recalculateStats();
|
||||
void sortBackToFront();
|
||||
void sortFrontToBack();
|
||||
void sortByTexture();
|
||||
void sortByLayer();
|
||||
|
||||
std::vector<RenderCommand> commands_;
|
||||
RenderQueueStats stats_;
|
||||
RenderSortMode sortMode_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <chrono>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 渲染统计信息
|
||||
*/
|
||||
struct RenderStatsData {
|
||||
uint32_t drawCalls;
|
||||
uint32_t batchCount;
|
||||
uint32_t vertexCount;
|
||||
uint32_t triangleCount;
|
||||
uint32_t spriteCount;
|
||||
uint32_t textCount;
|
||||
uint32_t shapeCount;
|
||||
uint32_t textureBinds;
|
||||
uint32_t shaderBinds;
|
||||
uint32_t bufferBinds;
|
||||
float frameTimeMs;
|
||||
float renderTimeMs;
|
||||
float fps;
|
||||
|
||||
RenderStatsData()
|
||||
: drawCalls(0), batchCount(0), vertexCount(0), triangleCount(0),
|
||||
spriteCount(0), textCount(0), shapeCount(0), textureBinds(0),
|
||||
shaderBinds(0), bufferBinds(0), frameTimeMs(0.0f), renderTimeMs(0.0f), fps(0.0f) {}
|
||||
|
||||
void reset() {
|
||||
drawCalls = 0;
|
||||
batchCount = 0;
|
||||
vertexCount = 0;
|
||||
triangleCount = 0;
|
||||
spriteCount = 0;
|
||||
textCount = 0;
|
||||
shapeCount = 0;
|
||||
textureBinds = 0;
|
||||
shaderBinds = 0;
|
||||
bufferBinds = 0;
|
||||
frameTimeMs = 0.0f;
|
||||
renderTimeMs = 0.0f;
|
||||
fps = 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染统计收集器
|
||||
*/
|
||||
class RenderStatsCollector {
|
||||
public:
|
||||
RenderStatsCollector();
|
||||
~RenderStatsCollector() = default;
|
||||
|
||||
/**
|
||||
* @brief 开始帧计时
|
||||
*/
|
||||
void beginFrame();
|
||||
|
||||
/**
|
||||
* @brief 结束帧计时
|
||||
*/
|
||||
void endFrame();
|
||||
|
||||
/**
|
||||
* @brief 开始渲染计时
|
||||
*/
|
||||
void beginRender();
|
||||
|
||||
/**
|
||||
* @brief 结束渲染计时
|
||||
*/
|
||||
void endRender();
|
||||
|
||||
/**
|
||||
* @brief 记录绘制调用
|
||||
*/
|
||||
void recordDrawCall(uint32_t vertexCount = 0, uint32_t triangleCount = 0);
|
||||
|
||||
/**
|
||||
* @brief 记录批处理
|
||||
*/
|
||||
void recordBatch();
|
||||
|
||||
/**
|
||||
* @brief 记录精灵
|
||||
*/
|
||||
void recordSprite(uint32_t count = 1);
|
||||
|
||||
/**
|
||||
* @brief 记录文本
|
||||
*/
|
||||
void recordText(uint32_t count = 1);
|
||||
|
||||
/**
|
||||
* @brief 记录形状
|
||||
*/
|
||||
void recordShape(uint32_t count = 1);
|
||||
|
||||
/**
|
||||
* @brief 记录纹理绑定
|
||||
*/
|
||||
void recordTextureBind();
|
||||
|
||||
/**
|
||||
* @brief 记录着色器绑定
|
||||
*/
|
||||
void recordShaderBind();
|
||||
|
||||
/**
|
||||
* @brief 记录缓冲区绑定
|
||||
*/
|
||||
void recordBufferBind();
|
||||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
*/
|
||||
const RenderStatsData& getStats() const { return stats_; }
|
||||
|
||||
/**
|
||||
* @brief 重置统计
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* @brief 获取平均帧时间
|
||||
*/
|
||||
float getAverageFrameTime() const { return avgFrameTime_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 FPS
|
||||
*/
|
||||
float getFPS() const { return fps_; }
|
||||
|
||||
private:
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
using TimePoint = std::chrono::time_point<Clock>;
|
||||
|
||||
RenderStatsData stats_;
|
||||
TimePoint frameStartTime_;
|
||||
TimePoint renderStartTime_;
|
||||
float frameTimeAccum_;
|
||||
uint32_t frameCount_;
|
||||
float avgFrameTime_;
|
||||
float fps_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief RHI 缓冲区接口
|
||||
*
|
||||
* 抽象 GPU 缓冲区,支持顶点、索引、Uniform 等类型
|
||||
*/
|
||||
class RHIBuffer {
|
||||
public:
|
||||
virtual ~RHIBuffer() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区类型
|
||||
*/
|
||||
virtual BufferType getType() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区大小(字节)
|
||||
*/
|
||||
virtual size_t getSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区使用方式
|
||||
*/
|
||||
virtual BufferUsage getUsage() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 上传数据到缓冲区
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
* @param offset 偏移量
|
||||
*/
|
||||
virtual void setData(const void* data, size_t size, size_t offset = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 读取缓冲区数据
|
||||
* @param data 目标缓冲区
|
||||
* @param size 读取大小
|
||||
* @param offset 偏移量
|
||||
*/
|
||||
virtual void getData(void* data, size_t size, size_t offset = 0) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 映射缓冲区到内存
|
||||
* @param offset 偏移量
|
||||
* @param size 映射大小
|
||||
* @param read 是否可读
|
||||
* @param write 是否可写
|
||||
* @return 映射的内存指针
|
||||
*/
|
||||
virtual void* map(size_t offset, size_t size, bool read = false, bool write = true) = 0;
|
||||
|
||||
/**
|
||||
* @brief 取消映射缓冲区
|
||||
*/
|
||||
virtual void unmap() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查缓冲区是否有效
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取调试名称
|
||||
*/
|
||||
virtual const std::string& getDebugName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置调试名称
|
||||
*/
|
||||
virtual void setDebugName(const std::string& name) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 顶点缓冲区便捷类
|
||||
*/
|
||||
class RHIVertexBuffer : public RHIBuffer {
|
||||
public:
|
||||
BufferType getType() const override { return BufferType::Vertex; }
|
||||
|
||||
/**
|
||||
* @brief 设置顶点数据
|
||||
* @tparam T 顶点类型
|
||||
* @param vertices 顶点数组
|
||||
*/
|
||||
template<typename T>
|
||||
void setVertices(const std::vector<T>& vertices) {
|
||||
setData(vertices.data(), vertices.size() * sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取顶点数量
|
||||
* @param stride 顶点步长
|
||||
*/
|
||||
uint32_t getVertexCount(uint32_t stride) const {
|
||||
return static_cast<uint32_t>(getSize() / stride);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 索引缓冲区便捷类
|
||||
*/
|
||||
class RHIIndexBuffer : public RHIBuffer {
|
||||
public:
|
||||
BufferType getType() const override { return BufferType::Index; }
|
||||
|
||||
/**
|
||||
* @brief 设置 16 位索引数据
|
||||
*/
|
||||
void setIndices16(const std::vector<uint16_t>& indices) {
|
||||
setData(indices.data(), indices.size() * sizeof(uint16_t));
|
||||
indexFormat_ = IndexFormat::UInt16;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置 32 位索引数据
|
||||
*/
|
||||
void setIndices32(const std::vector<uint32_t>& indices) {
|
||||
setData(indices.data(), indices.size() * sizeof(uint32_t));
|
||||
indexFormat_ = IndexFormat::UInt32;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取索引格式
|
||||
*/
|
||||
IndexFormat getIndexFormat() const { return indexFormat_; }
|
||||
|
||||
/**
|
||||
* @brief 获取索引数量
|
||||
*/
|
||||
uint32_t getIndexCount() const {
|
||||
return static_cast<uint32_t>(getSize() / getIndexFormatSize(indexFormat_));
|
||||
}
|
||||
|
||||
protected:
|
||||
IndexFormat indexFormat_ = IndexFormat::UInt16;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Uniform 缓冲区便捷类
|
||||
*/
|
||||
class RHIUniformBuffer : public RHIBuffer {
|
||||
public:
|
||||
BufferType getType() const override { return BufferType::Uniform; }
|
||||
|
||||
/**
|
||||
* @brief 设置单个值
|
||||
* @tparam T 值类型
|
||||
* @param value 值
|
||||
* @param offset 偏移量
|
||||
*/
|
||||
template<typename T>
|
||||
void setValue(const T& value, size_t offset = 0) {
|
||||
setData(&value, sizeof(T), offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取单个值
|
||||
* @tparam T 值类型
|
||||
* @param offset 偏移量
|
||||
*/
|
||||
template<typename T>
|
||||
T getValue(size_t offset = 0) const {
|
||||
T value;
|
||||
getData(&value, sizeof(T), offset);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,481 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_buffer.h>
|
||||
#include <extra2d/render/rhi/rhi_framebuffer.h>
|
||||
#include <extra2d/render/rhi/rhi_pipeline.h>
|
||||
#include <extra2d/render/rhi/rhi_shader.h>
|
||||
#include <extra2d/render/rhi/rhi_texture.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class IWindow;
|
||||
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief RHI 设备能力信息
|
||||
*/
|
||||
struct DeviceCaps {
|
||||
GraphicsAPI api = GraphicsAPI::OpenGL;
|
||||
std::string apiVersion;
|
||||
std::string deviceName;
|
||||
std::string vendorName;
|
||||
uint32_t maxTextureSize = 0;
|
||||
uint32_t maxTextureUnits = 0;
|
||||
uint32_t maxVertexAttributes = 0;
|
||||
uint32_t maxUniformBufferBindings = 0;
|
||||
uint32_t maxColorAttachments = 0;
|
||||
float maxAnisotropy = 1.0f;
|
||||
bool supportsCompute = false;
|
||||
bool supportsGeometry = false;
|
||||
bool supportsTessellation = false;
|
||||
bool supportsInstancing = false;
|
||||
bool supportsIndirectDraw = false;
|
||||
bool supportsMultiDrawIndirect = false;
|
||||
bool supportsPersistentMapping = false;
|
||||
bool supportsBindlessTextures = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RHI 设备统计信息
|
||||
*/
|
||||
struct DeviceStats {
|
||||
uint32_t drawCalls = 0;
|
||||
uint32_t triangleCount = 0;
|
||||
uint32_t vertexCount = 0;
|
||||
uint32_t textureBinds = 0;
|
||||
uint32_t shaderBinds = 0;
|
||||
uint32_t bufferBinds = 0;
|
||||
uint32_t framebufferBinds = 0;
|
||||
uint64_t textureMemory = 0;
|
||||
uint64_t bufferMemory = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RHI 设备接口
|
||||
*
|
||||
* 核心渲染硬件接口抽象层
|
||||
* 提供资源创建、渲染命令提交等功能
|
||||
*/
|
||||
class RHIDevice {
|
||||
public:
|
||||
virtual ~RHIDevice() = default;
|
||||
|
||||
// ========================================================================
|
||||
// 生命周期
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 初始化设备
|
||||
* @param window 窗口接口
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
virtual bool init(IWindow* window) = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭设备
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查设备是否有效
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取图形 API 类型
|
||||
*/
|
||||
virtual GraphicsAPI getAPI() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取设备能力
|
||||
*/
|
||||
virtual const DeviceCaps& getCaps() const = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 帧管理
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 开始帧
|
||||
*/
|
||||
virtual void beginFrame() = 0;
|
||||
|
||||
/**
|
||||
* @brief 结束帧
|
||||
*/
|
||||
virtual void endFrame() = 0;
|
||||
|
||||
/**
|
||||
* @brief 呈现帧
|
||||
*/
|
||||
virtual void present() = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置垂直同步
|
||||
*/
|
||||
virtual void setVSync(bool enabled) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取垂直同步状态
|
||||
*/
|
||||
virtual bool isVSyncEnabled() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置默认帧缓冲尺寸
|
||||
*/
|
||||
virtual void setDefaultFramebufferSize(uint32_t width, uint32_t height) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 资源创建 - 缓冲区
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建缓冲区
|
||||
*/
|
||||
virtual Ptr<RHIBuffer> createBuffer(const BufferDesc& desc) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建顶点缓冲区
|
||||
*/
|
||||
virtual Ptr<RHIVertexBuffer> createVertexBuffer(size_t size, BufferUsage usage = BufferUsage::Static,
|
||||
const void* data = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建索引缓冲区
|
||||
*/
|
||||
virtual Ptr<RHIIndexBuffer> createIndexBuffer(size_t size, BufferUsage usage = BufferUsage::Static,
|
||||
const void* data = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建 Uniform 缓冲区
|
||||
*/
|
||||
virtual Ptr<RHIUniformBuffer> createUniformBuffer(size_t size, BufferUsage usage = BufferUsage::Dynamic) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 资源创建 - 纹理
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建纹理
|
||||
*/
|
||||
virtual Ptr<RHITexture> createTexture(const TextureDesc& desc) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建 2D 纹理
|
||||
*/
|
||||
virtual Ptr<RHITexture> createTexture2D(uint32_t width, uint32_t height, Format format,
|
||||
const void* data = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建采样器
|
||||
*/
|
||||
virtual Ptr<RHISampler> createSampler(const SamplerDesc& desc) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建纹理视图
|
||||
*/
|
||||
virtual Ptr<RHITextureView> createTextureView(Ptr<RHITexture> texture,
|
||||
const TextureViewDesc& desc) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 资源创建 - 着色器
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建着色器
|
||||
*/
|
||||
virtual Ptr<RHIShader> createShader(const ShaderDesc& desc) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从源码创建着色器
|
||||
*/
|
||||
virtual Ptr<RHIShader> createShaderFromSource(const std::string& name,
|
||||
const std::string& vertexSource,
|
||||
const std::string& fragmentSource) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从文件创建着色器
|
||||
*/
|
||||
virtual Ptr<RHIShader> createShaderFromFile(const std::string& vertexPath,
|
||||
const std::string& fragmentPath) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 资源创建 - 管线
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建渲染管线
|
||||
*/
|
||||
virtual Ptr<RHIPipeline> createPipeline(const PipelineDesc& desc, Ptr<RHIShader> shader) = 0;
|
||||
|
||||
/**
|
||||
* @brief 使用构建器创建渲染管线
|
||||
*/
|
||||
virtual Ptr<RHIPipeline> createPipeline(const PipelineBuilder& builder) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 资源创建 - 帧缓冲
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建帧缓冲
|
||||
*/
|
||||
virtual Ptr<RHIFramebuffer> createFramebuffer(const FramebufferDesc& desc) = 0;
|
||||
|
||||
/**
|
||||
* @brief 使用构建器创建帧缓冲
|
||||
*/
|
||||
virtual Ptr<RHIFramebuffer> createFramebuffer(const FramebufferBuilder& builder) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 渲染状态设置
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置视口
|
||||
*/
|
||||
virtual void setViewport(const Viewport& viewport) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置视口(简化版)
|
||||
*/
|
||||
void setViewport(float x, float y, float width, float height,
|
||||
float minDepth = 0.0f, float maxDepth = 1.0f) {
|
||||
setViewport(Viewport(x, y, width, height, minDepth, maxDepth));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置裁剪矩形
|
||||
*/
|
||||
virtual void setScissorRect(const ScissorRect& rect) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置裁剪矩形(简化版)
|
||||
*/
|
||||
void setScissorRect(int32_t x, int32_t y, uint32_t width, uint32_t height) {
|
||||
setScissorRect(ScissorRect(x, y, width, height));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用裁剪测试
|
||||
*/
|
||||
virtual void setScissorEnabled(bool enabled) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 渲染命令 - 绑定资源
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 绑定管线
|
||||
*/
|
||||
virtual void setPipeline(Ptr<RHIPipeline> pipeline) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定着色器
|
||||
*/
|
||||
virtual void setShader(Ptr<RHIShader> shader) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定顶点缓冲区
|
||||
*/
|
||||
virtual void setVertexBuffer(uint32_t slot, Ptr<RHIBuffer> buffer, uint32_t offset = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定索引缓冲区
|
||||
*/
|
||||
virtual void setIndexBuffer(Ptr<RHIBuffer> buffer, IndexFormat format) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定纹理
|
||||
*/
|
||||
virtual void setTexture(uint32_t slot, Ptr<RHITexture> texture, Ptr<RHISampler> sampler = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定 Uniform 缓冲区
|
||||
*/
|
||||
virtual void setUniformBuffer(uint32_t slot, Ptr<RHIBuffer> buffer) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定帧缓冲
|
||||
*/
|
||||
virtual void setFramebuffer(Ptr<RHIFramebuffer> framebuffer) = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑帧缓冲(恢复默认)
|
||||
*/
|
||||
virtual void setDefaultFramebuffer() = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 渲染命令 - Uniform 设置
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置浮点 Uniform
|
||||
*/
|
||||
virtual void setUniformFloat(const std::string& name, float value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置向量 Uniform
|
||||
*/
|
||||
virtual void setUniformVec2(const std::string& name, const glm::vec2& value) = 0;
|
||||
virtual void setUniformVec3(const std::string& name, const glm::vec3& value) = 0;
|
||||
virtual void setUniformVec4(const std::string& name, const glm::vec4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置矩阵 Uniform
|
||||
*/
|
||||
virtual void setUniformMat4(const std::string& name, const glm::mat4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置整数 Uniform
|
||||
*/
|
||||
virtual void setUniformInt(const std::string& name, int value) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 渲染命令 - 绘制
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 绘制
|
||||
*/
|
||||
virtual void draw(const DrawCommand& cmd) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制(简化版)
|
||||
*/
|
||||
void draw(uint32_t vertexCount, uint32_t firstVertex = 0, uint32_t instanceCount = 1) {
|
||||
DrawCommand cmd;
|
||||
cmd.vertexCount = vertexCount;
|
||||
cmd.firstVertex = firstVertex;
|
||||
cmd.instanceCount = instanceCount;
|
||||
draw(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 索引绘制
|
||||
*/
|
||||
virtual void drawIndexed(const DrawIndexedCommand& cmd) = 0;
|
||||
|
||||
/**
|
||||
* @brief 索引绘制(简化版)
|
||||
*/
|
||||
void drawIndexed(uint32_t indexCount, uint32_t firstIndex = 0, int32_t vertexOffset = 0,
|
||||
uint32_t instanceCount = 1) {
|
||||
DrawIndexedCommand cmd;
|
||||
cmd.indexCount = indexCount;
|
||||
cmd.firstIndex = firstIndex;
|
||||
cmd.vertexOffset = vertexOffset;
|
||||
cmd.instanceCount = instanceCount;
|
||||
drawIndexed(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 间接绘制
|
||||
*/
|
||||
virtual void drawIndirect(Ptr<RHIBuffer> indirectBuffer, size_t offset) = 0;
|
||||
|
||||
/**
|
||||
* @brief 索引间接绘制
|
||||
*/
|
||||
virtual void drawIndexedIndirect(Ptr<RHIBuffer> indirectBuffer, size_t offset) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 渲染命令 - 清除
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 清除颜色缓冲
|
||||
*/
|
||||
virtual void clearColor(const ColorValue& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除深度缓冲
|
||||
*/
|
||||
virtual void clearDepth(float depth = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除模板缓冲
|
||||
*/
|
||||
virtual void clearStencil(uint8_t stencil = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除所有缓冲
|
||||
*/
|
||||
virtual void clear(const ColorValue& color, float depth = 1.0f, uint8_t stencil = 0) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 统计与调试
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
*/
|
||||
virtual const DeviceStats& getStats() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 重置统计信息
|
||||
*/
|
||||
virtual void resetStats() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查错误
|
||||
*/
|
||||
virtual bool checkError() = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取调试名称
|
||||
*/
|
||||
virtual const std::string& getDebugName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置调试名称
|
||||
*/
|
||||
virtual void setDebugName(const std::string& name) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 静态工厂方法
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建 RHI 设备
|
||||
* @param api 图形 API 类型
|
||||
* @return 设备实例
|
||||
*/
|
||||
static Ptr<RHIDevice> create(GraphicsAPI api);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RHI 设备作用域绑定器
|
||||
*/
|
||||
class RHIDeviceScope {
|
||||
public:
|
||||
explicit RHIDeviceScope(Ptr<RHIDevice> device) : device_(device) {
|
||||
if (device_) {
|
||||
device_->beginFrame();
|
||||
}
|
||||
}
|
||||
|
||||
~RHIDeviceScope() {
|
||||
if (device_) {
|
||||
device_->endFrame();
|
||||
}
|
||||
}
|
||||
|
||||
RHIDeviceScope(const RHIDeviceScope&) = delete;
|
||||
RHIDeviceScope& operator=(const RHIDeviceScope&) = delete;
|
||||
|
||||
RHIDevice* operator->() { return device_.get(); }
|
||||
RHIDevice* get() { return device_.get(); }
|
||||
|
||||
private:
|
||||
Ptr<RHIDevice> device_;
|
||||
};
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_texture.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief RHI 帧缓冲接口
|
||||
*
|
||||
* 抽象渲染目标,支持多渲染目标(MRT)
|
||||
*/
|
||||
class RHIFramebuffer {
|
||||
public:
|
||||
virtual ~RHIFramebuffer() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定帧缓冲
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑帧缓冲
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取帧缓冲宽度
|
||||
*/
|
||||
virtual uint32_t getWidth() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取帧缓冲高度
|
||||
*/
|
||||
virtual uint32_t getHeight() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取颜色附件数量
|
||||
*/
|
||||
virtual uint32_t getColorAttachmentCount() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取颜色附件纹理
|
||||
* @param index 附件索引
|
||||
*/
|
||||
virtual Ptr<RHITexture> getColorAttachment(uint32_t index = 0) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取深度模板附件纹理
|
||||
*/
|
||||
virtual Ptr<RHITexture> getDepthStencilAttachment() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查是否有深度附件
|
||||
*/
|
||||
virtual bool hasDepthAttachment() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查是否有模板附件
|
||||
*/
|
||||
virtual bool hasStencilAttachment() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查帧缓冲是否完整
|
||||
*/
|
||||
virtual bool isComplete() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查帧缓冲是否有效
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取调试名称
|
||||
*/
|
||||
virtual const std::string& getDebugName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置调试名称
|
||||
*/
|
||||
virtual void setDebugName(const std::string& name) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取完整描述
|
||||
*/
|
||||
virtual FramebufferDesc getDesc() const = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 清除操作
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 清除颜色附件
|
||||
* @param index 附件索引
|
||||
* @param color 清除颜色
|
||||
*/
|
||||
virtual void clearColor(uint32_t index, const ColorValue& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除深度缓冲
|
||||
* @param depth 深度值
|
||||
*/
|
||||
virtual void clearDepth(float depth = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除模板缓冲
|
||||
* @param stencil 模板值
|
||||
*/
|
||||
virtual void clearStencil(uint8_t stencil = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除深度和模板缓冲
|
||||
*/
|
||||
virtual void clearDepthStencil(float depth = 1.0f, uint8_t stencil = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除所有附件
|
||||
*/
|
||||
virtual void clearAll(const ColorValue& color, float depth = 1.0f, uint8_t stencil = 0) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// Blit 操作
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 将帧缓冲内容复制到另一个帧缓冲
|
||||
* @param dst 目标帧缓冲
|
||||
* @param srcRect 源矩形
|
||||
* @param dstRect 目标矩形
|
||||
* @param mask 要复制的缓冲区掩码
|
||||
* @param filter 过滤模式
|
||||
*/
|
||||
virtual void blitTo(RHIFramebuffer* dst,
|
||||
const ScissorRect& srcRect, const ScissorRect& dstRect,
|
||||
bool color = true, bool depth = false, bool stencil = false,
|
||||
FilterMode filter = FilterMode::Nearest) = 0;
|
||||
|
||||
/**
|
||||
* @brief 将帧缓冲内容复制到屏幕
|
||||
*/
|
||||
virtual void blitToScreen(const ScissorRect& srcRect, const ScissorRect& dstRect,
|
||||
FilterMode filter = FilterMode::Nearest) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染通道描述
|
||||
*/
|
||||
struct RenderPassDesc {
|
||||
struct ColorAttachment {
|
||||
Ptr<RHIFramebuffer> framebuffer;
|
||||
ColorValue clearColor = ColorValue::black();
|
||||
bool load = false;
|
||||
bool store = true;
|
||||
};
|
||||
|
||||
struct DepthStencilAttachment {
|
||||
Ptr<RHIFramebuffer> framebuffer;
|
||||
float clearDepth = 1.0f;
|
||||
uint8_t clearStencil = 0;
|
||||
bool loadDepth = false;
|
||||
bool storeDepth = true;
|
||||
bool loadStencil = false;
|
||||
bool storeStencil = true;
|
||||
};
|
||||
|
||||
std::vector<ColorAttachment> colorAttachments;
|
||||
DepthStencilAttachment depthStencilAttachment;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RHI 渲染通道接口
|
||||
*
|
||||
* 表示一次渲染过程,管理渲染目标的状态
|
||||
*/
|
||||
class RHIRenderPass {
|
||||
public:
|
||||
virtual ~RHIRenderPass() = default;
|
||||
|
||||
/**
|
||||
* @brief 开始渲染通道
|
||||
*/
|
||||
virtual void begin() = 0;
|
||||
|
||||
/**
|
||||
* @brief 结束渲染通道
|
||||
*/
|
||||
virtual void end() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查渲染通道是否正在进行
|
||||
*/
|
||||
virtual bool isActive() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取渲染通道描述
|
||||
*/
|
||||
virtual const RenderPassDesc& getDesc() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取当前帧缓冲
|
||||
*/
|
||||
virtual Ptr<RHIFramebuffer> getFramebuffer() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 帧缓冲构建器
|
||||
*/
|
||||
class FramebufferBuilder {
|
||||
public:
|
||||
FramebufferBuilder& setSize(uint32_t width, uint32_t height) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FramebufferBuilder& addColorAttachment(Format format, uint32_t sampleCount = 1) {
|
||||
AttachmentDesc desc;
|
||||
desc.format = format;
|
||||
desc.sampleCount = sampleCount;
|
||||
desc.isDepthStencil = false;
|
||||
colorAttachments_.push_back(desc);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FramebufferBuilder& addColorTexture(Ptr<RHITexture> texture) {
|
||||
colorTextures_.push_back(texture);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FramebufferBuilder& setDepthStencilAttachment(Format format, uint32_t sampleCount = 1) {
|
||||
depthStencilAttachment_.format = format;
|
||||
depthStencilAttachment_.sampleCount = sampleCount;
|
||||
depthStencilAttachment_.isDepthStencil = true;
|
||||
hasDepthStencil_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FramebufferBuilder& setDepthTexture(Ptr<RHITexture> texture) {
|
||||
depthTexture_ = texture;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FramebufferBuilder& setDebugName(const std::string& name) {
|
||||
debugName_ = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint32_t getWidth() const { return width_; }
|
||||
uint32_t getHeight() const { return height_; }
|
||||
const std::vector<AttachmentDesc>& getColorAttachments() const { return colorAttachments_; }
|
||||
const AttachmentDesc& getDepthStencilAttachment() const { return depthStencilAttachment_; }
|
||||
bool hasDepthStencil() const { return hasDepthStencil_; }
|
||||
const std::string& getDebugName() const { return debugName_; }
|
||||
const std::vector<Ptr<RHITexture>>& getColorTextures() const { return colorTextures_; }
|
||||
Ptr<RHITexture> getDepthTexture() const { return depthTexture_; }
|
||||
|
||||
FramebufferDesc build() const {
|
||||
FramebufferDesc desc;
|
||||
desc.width = width_;
|
||||
desc.height = height_;
|
||||
desc.colorAttachments = colorAttachments_;
|
||||
if (hasDepthStencil_) {
|
||||
desc.depthStencilAttachment = depthStencilAttachment_;
|
||||
}
|
||||
desc.debugName = debugName_;
|
||||
return desc;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t width_ = 0;
|
||||
uint32_t height_ = 0;
|
||||
std::vector<AttachmentDesc> colorAttachments_;
|
||||
AttachmentDesc depthStencilAttachment_;
|
||||
bool hasDepthStencil_ = false;
|
||||
std::string debugName_;
|
||||
std::vector<Ptr<RHITexture>> colorTextures_;
|
||||
Ptr<RHITexture> depthTexture_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 常用帧缓冲预设
|
||||
*/
|
||||
namespace FramebufferPresets {
|
||||
|
||||
/**
|
||||
* @brief 创建标准颜色帧缓冲
|
||||
*/
|
||||
inline FramebufferBuilder colorOnly(uint32_t width, uint32_t height) {
|
||||
return FramebufferBuilder()
|
||||
.setSize(width, height)
|
||||
.addColorAttachment(Format::RGBA8_UNORM);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建带深度的帧缓冲
|
||||
*/
|
||||
inline FramebufferBuilder withDepth(uint32_t width, uint32_t height) {
|
||||
return FramebufferBuilder()
|
||||
.setSize(width, height)
|
||||
.addColorAttachment(Format::RGBA8_UNORM)
|
||||
.setDepthStencilAttachment(Format::D24_UNORM_S8_UINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建 HDR 帧缓冲
|
||||
*/
|
||||
inline FramebufferBuilder hdr(uint32_t width, uint32_t height) {
|
||||
return FramebufferBuilder()
|
||||
.setSize(width, height)
|
||||
.addColorAttachment(Format::RGBA16_FLOAT)
|
||||
.setDepthStencilAttachment(Format::D24_UNORM_S8_UINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建多目标帧缓冲(MRT)
|
||||
*/
|
||||
inline FramebufferBuilder mrt2(uint32_t width, uint32_t height) {
|
||||
return FramebufferBuilder()
|
||||
.setSize(width, height)
|
||||
.addColorAttachment(Format::RGBA8_UNORM)
|
||||
.addColorAttachment(Format::RGBA8_UNORM)
|
||||
.setDepthStencilAttachment(Format::D24_UNORM_S8_UINT);
|
||||
}
|
||||
|
||||
} // namespace FramebufferPresets
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_shader.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief RHI 渲染管线接口
|
||||
*
|
||||
* 抽象 GPU 渲染管线状态对象(PSO)
|
||||
* 封装着色器、混合状态、深度状态、光栅化状态等
|
||||
*/
|
||||
class RHIPipeline {
|
||||
public:
|
||||
virtual ~RHIPipeline() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定管线
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑管线
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 状态访问
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取混合状态
|
||||
*/
|
||||
virtual const BlendState& getBlendState() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取深度模板状态
|
||||
*/
|
||||
virtual const DepthStencilState& getDepthStencilState() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取光栅化状态
|
||||
*/
|
||||
virtual const RasterizerState& getRasterizerState() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取图元类型
|
||||
*/
|
||||
virtual PrimitiveType getPrimitiveType() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取关联的着色器
|
||||
*/
|
||||
virtual Ptr<RHIShader> getShader() const = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 状态设置(动态状态)
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置混合模式
|
||||
*/
|
||||
virtual void setBlendMode(BlendFactor srcFactor, BlendFactor dstFactor,
|
||||
BlendOp op = BlendOp::Add) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置混合模式(预设)
|
||||
*/
|
||||
virtual void setBlendState(const BlendState& state) = 0;
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用混合
|
||||
*/
|
||||
virtual void setBlendEnabled(bool enabled) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置深度测试
|
||||
*/
|
||||
virtual void setDepthTestEnabled(bool enabled) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置深度写入
|
||||
*/
|
||||
virtual void setDepthWriteEnabled(bool enabled) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置深度比较函数
|
||||
*/
|
||||
virtual void setDepthCompareFunc(CompareFunc func) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置裁剪模式
|
||||
*/
|
||||
virtual void setCullMode(CullMode mode) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置正面方向
|
||||
*/
|
||||
virtual void setFrontFace(FrontFace face) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置线框模式
|
||||
*/
|
||||
virtual void setWireframe(bool enabled) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 顶点布局
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取顶点属性列表
|
||||
*/
|
||||
virtual const std::vector<VertexAttribute>& getVertexAttributes() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取顶点缓冲区绑定
|
||||
*/
|
||||
virtual const std::vector<VertexBufferBinding>& getVertexBufferBindings() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 计算顶点步长
|
||||
*/
|
||||
virtual uint32_t calculateVertexStride() const = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 有效性检查
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 检查管线是否有效
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取调试名称
|
||||
*/
|
||||
virtual const std::string& getDebugName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置调试名称
|
||||
*/
|
||||
virtual void setDebugName(const std::string& name) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 管线构建器
|
||||
*/
|
||||
class PipelineBuilder {
|
||||
public:
|
||||
PipelineBuilder& setShader(Ptr<RHIShader> shader) {
|
||||
shader_ = shader;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& setBlendState(const BlendState& state) {
|
||||
blendState_ = state;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& setDepthStencilState(const DepthStencilState& state) {
|
||||
depthStencilState_ = state;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& setRasterizerState(const RasterizerState& state) {
|
||||
rasterizerState_ = state;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& setPrimitiveType(PrimitiveType type) {
|
||||
primitiveType_ = type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& addVertexAttribute(const VertexAttribute& attr) {
|
||||
vertexAttributes_.push_back(attr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& addVertexAttributes(const std::vector<VertexAttribute>& attrs) {
|
||||
vertexAttributes_.insert(vertexAttributes_.end(), attrs.begin(), attrs.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& addVertexBufferBinding(const VertexBufferBinding& binding) {
|
||||
vertexBindings_.push_back(binding);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& setColorFormat(Format format) {
|
||||
colorFormat_ = format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& setDepthStencilFormat(Format format) {
|
||||
depthStencilFormat_ = format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& setDebugName(const std::string& name) {
|
||||
debugName_ = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& enableBlend(bool enabled = true) {
|
||||
blendState_.enabled = enabled;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& enableDepthTest(bool enabled = true) {
|
||||
depthStencilState_.depthTestEnabled = enabled;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& enableDepthWrite(bool enabled = true) {
|
||||
depthStencilState_.depthWriteEnabled = enabled;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PipelineBuilder& setCullMode(CullMode mode) {
|
||||
rasterizerState_.cullMode = mode;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Ptr<RHIShader> getShader() const { return shader_; }
|
||||
const BlendState& getBlendState() const { return blendState_; }
|
||||
const DepthStencilState& getDepthStencilState() const { return depthStencilState_; }
|
||||
const RasterizerState& getRasterizerState() const { return rasterizerState_; }
|
||||
PrimitiveType getPrimitiveType() const { return primitiveType_; }
|
||||
const std::vector<VertexAttribute>& getVertexAttributes() const { return vertexAttributes_; }
|
||||
const std::vector<VertexBufferBinding>& getVertexBufferBindings() const { return vertexBindings_; }
|
||||
Format getColorFormat() const { return colorFormat_; }
|
||||
Format getDepthStencilFormat() const { return depthStencilFormat_; }
|
||||
const std::string& getDebugName() const { return debugName_; }
|
||||
|
||||
PipelineDesc build() const {
|
||||
PipelineDesc desc;
|
||||
desc.blendState = blendState_;
|
||||
desc.depthStencilState = depthStencilState_;
|
||||
desc.rasterizerState = rasterizerState_;
|
||||
desc.primitiveType = primitiveType_;
|
||||
desc.vertexAttributes = vertexAttributes_;
|
||||
desc.vertexBindings = vertexBindings_;
|
||||
desc.colorFormat = colorFormat_;
|
||||
desc.depthStencilFormat = depthStencilFormat_;
|
||||
desc.debugName = debugName_;
|
||||
return desc;
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<RHIShader> shader_;
|
||||
BlendState blendState_;
|
||||
DepthStencilState depthStencilState_;
|
||||
RasterizerState rasterizerState_;
|
||||
PrimitiveType primitiveType_ = PrimitiveType::Triangles;
|
||||
std::vector<VertexAttribute> vertexAttributes_;
|
||||
std::vector<VertexBufferBinding> vertexBindings_;
|
||||
Format colorFormat_ = Format::RGBA8_UNORM;
|
||||
Format depthStencilFormat_ = Format::D24_UNORM_S8_UINT;
|
||||
std::string debugName_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 输入布局描述
|
||||
*/
|
||||
struct InputLayoutDesc {
|
||||
std::vector<VertexAttribute> attributes;
|
||||
std::vector<VertexBufferBinding> bindings;
|
||||
|
||||
/**
|
||||
* @brief 添加顶点属性
|
||||
*/
|
||||
InputLayoutDesc& addAttribute(uint32_t location, Format format, uint32_t offset,
|
||||
uint32_t bufferSlot = 0, bool normalized = false) {
|
||||
attributes.emplace_back(location, format, offset, bufferSlot, normalized);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加顶点缓冲区绑定
|
||||
*/
|
||||
InputLayoutDesc& addBinding(uint32_t slot, uint32_t stride, uint32_t offset = 0,
|
||||
uint32_t divisor = 0) {
|
||||
bindings.emplace_back(slot, stride, offset, divisor);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 常用顶点布局
|
||||
*/
|
||||
namespace VertexLayouts {
|
||||
|
||||
/**
|
||||
* @brief 位置 + 纹理坐标
|
||||
*/
|
||||
inline InputLayoutDesc positionTexcoord() {
|
||||
InputLayoutDesc desc;
|
||||
desc.addAttribute(0, Format::RG32_FLOAT, 0);
|
||||
desc.addAttribute(1, Format::RG32_FLOAT, 8);
|
||||
desc.addBinding(0, 16);
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 位置 + 纹理坐标 + 颜色
|
||||
*/
|
||||
inline InputLayoutDesc positionTexcoordColor() {
|
||||
InputLayoutDesc desc;
|
||||
desc.addAttribute(0, Format::RG32_FLOAT, 0);
|
||||
desc.addAttribute(1, Format::RG32_FLOAT, 8);
|
||||
desc.addAttribute(2, Format::RGBA8_UNORM, 16, 0, true);
|
||||
desc.addBinding(0, 20);
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 位置 + 法线 + 纹理坐标
|
||||
*/
|
||||
inline InputLayoutDesc positionNormalTexcoord() {
|
||||
InputLayoutDesc desc;
|
||||
desc.addAttribute(0, Format::RGB32_FLOAT, 0);
|
||||
desc.addAttribute(1, Format::RGB32_FLOAT, 12);
|
||||
desc.addAttribute(2, Format::RG32_FLOAT, 24);
|
||||
desc.addBinding(0, 32);
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 位置 + 颜色
|
||||
*/
|
||||
inline InputLayoutDesc positionColor() {
|
||||
InputLayoutDesc desc;
|
||||
desc.addAttribute(0, Format::RG32_FLOAT, 0);
|
||||
desc.addAttribute(1, Format::RGBA8_UNORM, 8, 0, true);
|
||||
desc.addBinding(0, 12);
|
||||
return desc;
|
||||
}
|
||||
|
||||
} // namespace VertexLayouts
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,301 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief Uniform 变量信息
|
||||
*/
|
||||
struct UniformInfo {
|
||||
std::string name;
|
||||
uint32_t location = 0;
|
||||
uint32_t size = 0;
|
||||
uint32_t offset = 0;
|
||||
ShaderStage stage = ShaderStage::Vertex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 顶点属性信息
|
||||
*/
|
||||
struct AttributeInfo {
|
||||
std::string name;
|
||||
uint32_t location = 0;
|
||||
Format format = Format::RGBA32_FLOAT;
|
||||
uint32_t offset = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RHI 着色器接口
|
||||
*
|
||||
* 抽象 GPU 着色器程序
|
||||
*/
|
||||
class RHIShader {
|
||||
public:
|
||||
virtual ~RHIShader() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定着色器
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑着色器
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取着色器名称
|
||||
*/
|
||||
virtual const std::string& getName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查着色器是否有效
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
|
||||
// ========================================================================
|
||||
// Uniform 设置方法
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置布尔值
|
||||
*/
|
||||
virtual void setBool(const std::string& name, bool value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置整数值
|
||||
*/
|
||||
virtual void setInt(const std::string& name, int value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置无符号整数值
|
||||
*/
|
||||
virtual void setUInt(const std::string& name, uint32_t value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置浮点值
|
||||
*/
|
||||
virtual void setFloat(const std::string& name, float value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量
|
||||
*/
|
||||
virtual void setVec2(const std::string& name, const glm::vec2& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量
|
||||
*/
|
||||
virtual void setVec3(const std::string& name, const glm::vec3& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量
|
||||
*/
|
||||
virtual void setVec4(const std::string& name, const glm::vec4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置 2x2 矩阵
|
||||
*/
|
||||
virtual void setMat2(const std::string& name, const glm::mat2& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置 3x3 矩阵
|
||||
*/
|
||||
virtual void setMat3(const std::string& name, const glm::mat3& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置 4x4 矩阵
|
||||
*/
|
||||
virtual void setMat4(const std::string& name, const glm::mat4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置纹理采样器
|
||||
*/
|
||||
virtual void setTexture(const std::string& name, uint32_t slot) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置整数数组
|
||||
*/
|
||||
virtual void setIntArray(const std::string& name, const int* values, uint32_t count) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置浮点数组
|
||||
*/
|
||||
virtual void setFloatArray(const std::string& name, const float* values, uint32_t count) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置向量数组
|
||||
*/
|
||||
virtual void setVec2Array(const std::string& name, const glm::vec2* values, uint32_t count) = 0;
|
||||
virtual void setVec3Array(const std::string& name, const glm::vec3* values, uint32_t count) = 0;
|
||||
virtual void setVec4Array(const std::string& name, const glm::vec4* values, uint32_t count) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置矩阵数组
|
||||
*/
|
||||
virtual void setMat4Array(const std::string& name, const glm::mat4* values, uint32_t count) = 0;
|
||||
|
||||
// ========================================================================
|
||||
// Uniform 查询方法
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 检查是否有指定的 Uniform
|
||||
*/
|
||||
virtual bool hasUniform(const std::string& name) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取 Uniform 位置
|
||||
*/
|
||||
virtual int getUniformLocation(const std::string& name) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取所有 Uniform 信息
|
||||
*/
|
||||
virtual const std::vector<UniformInfo>& getUniforms() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取所有顶点属性信息
|
||||
*/
|
||||
virtual const std::vector<AttributeInfo>& getAttributes() const = 0;
|
||||
|
||||
// ========================================================================
|
||||
// 便捷方法
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量(分离参数)
|
||||
*/
|
||||
void setVec2(const std::string& name, float x, float y) {
|
||||
setVec2(name, glm::vec2(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量(分离参数)
|
||||
*/
|
||||
void setVec3(const std::string& name, float x, float y, float z) {
|
||||
setVec3(name, glm::vec3(x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量(分离参数)
|
||||
*/
|
||||
void setVec4(const std::string& name, float x, float y, float z, float w) {
|
||||
setVec4(name, glm::vec4(x, y, z, w));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置颜色(RGBA)
|
||||
*/
|
||||
void setColor(const std::string& name, float r, float g, float b, float a = 1.0f) {
|
||||
setVec4(name, glm::vec4(r, g, b, a));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器模块接口
|
||||
*
|
||||
* 表示单个着色器阶段(如顶点着色器、片段着色器)
|
||||
*/
|
||||
class RHIShaderModule {
|
||||
public:
|
||||
virtual ~RHIShaderModule() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取着色器阶段
|
||||
*/
|
||||
virtual ShaderStage getStage() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取入口点名称
|
||||
*/
|
||||
virtual const std::string& getEntryPoint() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查模块是否有效
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器程序构建器
|
||||
*/
|
||||
class ShaderProgramBuilder {
|
||||
public:
|
||||
ShaderProgramBuilder& setName(const std::string& name) {
|
||||
name_ = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ShaderProgramBuilder& setVertexSource(const std::string& source) {
|
||||
vertexSource_ = source;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ShaderProgramBuilder& setFragmentSource(const std::string& source) {
|
||||
fragmentSource_ = source;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ShaderProgramBuilder& setGeometrySource(const std::string& source) {
|
||||
geometrySource_ = source;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ShaderProgramBuilder& setBinaryData(const std::vector<uint8_t>& data) {
|
||||
binaryData_ = data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ShaderProgramBuilder& addDefine(const std::string& name, const std::string& value = "") {
|
||||
defines_[name] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::string& getName() const { return name_; }
|
||||
const std::string& getVertexSource() const { return vertexSource_; }
|
||||
const std::string& getFragmentSource() const { return fragmentSource_; }
|
||||
const std::string& getGeometrySource() const { return geometrySource_; }
|
||||
const std::vector<uint8_t>& getBinaryData() const { return binaryData_; }
|
||||
const std::unordered_map<std::string, std::string>& getDefines() const { return defines_; }
|
||||
|
||||
ShaderDesc build() const {
|
||||
ShaderDesc desc;
|
||||
desc.name = name_;
|
||||
desc.vertexSource = vertexSource_;
|
||||
desc.fragmentSource = fragmentSource_;
|
||||
desc.geometrySource = geometrySource_;
|
||||
desc.binaryData = binaryData_;
|
||||
return desc;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::string vertexSource_;
|
||||
std::string fragmentSource_;
|
||||
std::string geometrySource_;
|
||||
std::vector<uint8_t> binaryData_;
|
||||
std::unordered_map<std::string, std::string> defines_;
|
||||
};
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,251 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief RHI 纹理接口
|
||||
*
|
||||
* 抽象 GPU 纹理资源,支持 2D、3D、立方体纹理等
|
||||
*/
|
||||
class RHITexture {
|
||||
public:
|
||||
virtual ~RHITexture() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取纹理类型
|
||||
*/
|
||||
virtual TextureType getType() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取纹理格式
|
||||
*/
|
||||
virtual Format getFormat() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取纹理宽度
|
||||
*/
|
||||
virtual uint32_t getWidth() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取纹理高度
|
||||
*/
|
||||
virtual uint32_t getHeight() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取纹理深度(3D 纹理)
|
||||
*/
|
||||
virtual uint32_t getDepth() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取 Mip 级别数
|
||||
*/
|
||||
virtual uint32_t getMipLevels() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取数组层数
|
||||
*/
|
||||
virtual uint32_t getArrayLayers() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取采样数(多重采样纹理)
|
||||
*/
|
||||
virtual uint32_t getSampleCount() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 上传纹理数据
|
||||
* @param data 数据指针
|
||||
* @param mipLevel Mip 级别
|
||||
* @param arrayLayer 数组层
|
||||
*/
|
||||
virtual void setData(const void* data, uint32_t mipLevel = 0, uint32_t arrayLayer = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 上传纹理子区域数据
|
||||
* @param data 数据指针
|
||||
* @param x X 偏移
|
||||
* @param y Y 偏移
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param mipLevel Mip 级别
|
||||
*/
|
||||
virtual void setSubData(const void* data, uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height, uint32_t mipLevel = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 生成 Mipmaps
|
||||
*/
|
||||
virtual void generateMipmaps() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查纹理是否有效
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取调试名称
|
||||
*/
|
||||
virtual const std::string& getDebugName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置调试名称
|
||||
*/
|
||||
virtual void setDebugName(const std::string& name) = 0;
|
||||
|
||||
/**
|
||||
* @brief 计算数据大小
|
||||
*/
|
||||
size_t calculateDataSize(uint32_t mipLevel = 0) const {
|
||||
uint32_t w = getWidth() >> mipLevel;
|
||||
uint32_t h = getHeight() >> mipLevel;
|
||||
if (w == 0) w = 1;
|
||||
if (h == 0) h = 1;
|
||||
return static_cast<size_t>(w) * h * getFormatSize(getFormat());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RHI 采样器接口
|
||||
*
|
||||
* 抽象纹理采样器状态
|
||||
*/
|
||||
class RHISampler {
|
||||
public:
|
||||
virtual ~RHISampler() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取最小化过滤模式
|
||||
*/
|
||||
virtual FilterMode getMinFilter() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取最大化过滤模式
|
||||
*/
|
||||
virtual FilterMode getMagFilter() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取 Mip 过滤模式
|
||||
*/
|
||||
virtual FilterMode getMipFilter() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取 U 方向寻址模式
|
||||
*/
|
||||
virtual AddressMode getAddressModeU() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取 V 方向寻址模式
|
||||
*/
|
||||
virtual AddressMode getAddressModeV() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取 W 方向寻址模式
|
||||
*/
|
||||
virtual AddressMode getAddressModeW() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取最大各向异性
|
||||
*/
|
||||
virtual float getMaxAnisotropy() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取边框颜色
|
||||
*/
|
||||
virtual ColorValue getBorderColor() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查采样器是否有效
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取调试名称
|
||||
*/
|
||||
virtual const std::string& getDebugName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置调试名称
|
||||
*/
|
||||
virtual void setDebugName(const std::string& name) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理视图描述
|
||||
*/
|
||||
struct TextureViewDesc {
|
||||
Format format = Format::Undefined;
|
||||
uint32_t baseMipLevel = 0;
|
||||
uint32_t mipLevels = 0;
|
||||
uint32_t baseArrayLayer = 0;
|
||||
uint32_t arrayLayers = 0;
|
||||
TextureType viewType = TextureType::Texture2D;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RHI 纹理视图接口
|
||||
*
|
||||
* 用于创建纹理的不同视图
|
||||
*/
|
||||
class RHITextureView {
|
||||
public:
|
||||
virtual ~RHITextureView() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取源纹理
|
||||
*/
|
||||
virtual Ptr<RHITexture> getTexture() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取视图格式
|
||||
*/
|
||||
virtual Format getFormat() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取基础 Mip 级别
|
||||
*/
|
||||
virtual uint32_t getBaseMipLevel() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取 Mip 级别数
|
||||
*/
|
||||
virtual uint32_t getMipLevels() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取基础数组层
|
||||
*/
|
||||
virtual uint32_t getBaseArrayLayer() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取数组层数
|
||||
*/
|
||||
virtual uint32_t getArrayLayers() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查视图是否有效
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
};
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,685 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace rhi {
|
||||
|
||||
/**
|
||||
* @brief 图形 API 类型
|
||||
*/
|
||||
enum class GraphicsAPI {
|
||||
OpenGL,
|
||||
Vulkan,
|
||||
DirectX11,
|
||||
DirectX12,
|
||||
Metal,
|
||||
WebGPU
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理格式
|
||||
*/
|
||||
enum class Format {
|
||||
Undefined,
|
||||
// 8-bit 无符号归一化
|
||||
R8_UNORM,
|
||||
RG8_UNORM,
|
||||
RGB8_UNORM,
|
||||
RGBA8_UNORM,
|
||||
// 8-bit 有符号归一化
|
||||
R8_SNORM,
|
||||
RG8_SNORM,
|
||||
RGB8_SNORM,
|
||||
RGBA8_SNORM,
|
||||
// 16-bit 浮点
|
||||
R16_FLOAT,
|
||||
RG16_FLOAT,
|
||||
RGBA16_FLOAT,
|
||||
// 32-bit 浮点
|
||||
R32_FLOAT,
|
||||
RG32_FLOAT,
|
||||
RGB32_FLOAT,
|
||||
RGBA32_FLOAT,
|
||||
// 32-bit 整数
|
||||
R32_UINT,
|
||||
RG32_UINT,
|
||||
RGBA32_UINT,
|
||||
// 深度/模板
|
||||
D16_UNORM,
|
||||
D24_UNORM_S8_UINT,
|
||||
D32_FLOAT,
|
||||
D32_FLOAT_S8_UINT,
|
||||
// 压缩格式
|
||||
BC1_RGB_UNORM,
|
||||
BC1_RGBA_UNORM,
|
||||
BC3_RGBA_UNORM,
|
||||
BC5_RG_UNORM,
|
||||
BC7_RGBA_UNORM,
|
||||
ETC2_RGB8_UNORM,
|
||||
ETC2_RGBA8_UNORM,
|
||||
ASTC_4x4_UNORM,
|
||||
ASTC_8x8_UNORM
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 缓冲区类型
|
||||
*/
|
||||
enum class BufferType {
|
||||
Vertex,
|
||||
Index,
|
||||
Uniform,
|
||||
Storage,
|
||||
Staging,
|
||||
Indirect
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 缓冲区使用方式
|
||||
*/
|
||||
enum class BufferUsage {
|
||||
Static,
|
||||
Dynamic,
|
||||
Stream
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 图元类型
|
||||
*/
|
||||
enum class PrimitiveType {
|
||||
Points,
|
||||
Lines,
|
||||
LineStrip,
|
||||
LineLoop,
|
||||
Triangles,
|
||||
TriangleStrip,
|
||||
TriangleFan
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 混合因子
|
||||
*/
|
||||
enum class BlendFactor {
|
||||
Zero,
|
||||
One,
|
||||
SrcColor,
|
||||
OneMinusSrcColor,
|
||||
DstColor,
|
||||
OneMinusDstColor,
|
||||
SrcAlpha,
|
||||
OneMinusSrcAlpha,
|
||||
DstAlpha,
|
||||
OneMinusDstAlpha,
|
||||
ConstantColor,
|
||||
OneMinusConstantColor,
|
||||
ConstantAlpha,
|
||||
OneMinusConstantAlpha,
|
||||
SrcAlphaSaturate
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 混合操作
|
||||
*/
|
||||
enum class BlendOp {
|
||||
Add,
|
||||
Subtract,
|
||||
ReverseSubtract,
|
||||
Min,
|
||||
Max
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 比较函数
|
||||
*/
|
||||
enum class CompareFunc {
|
||||
Never,
|
||||
Less,
|
||||
Equal,
|
||||
LessEqual,
|
||||
Greater,
|
||||
NotEqual,
|
||||
GreaterEqual,
|
||||
Always
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 裁剪模式
|
||||
*/
|
||||
enum class CullMode {
|
||||
None,
|
||||
Front,
|
||||
Back
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 正面方向
|
||||
*/
|
||||
enum class FrontFace {
|
||||
CounterClockwise,
|
||||
Clockwise
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理过滤模式
|
||||
*/
|
||||
enum class FilterMode {
|
||||
Nearest,
|
||||
Linear,
|
||||
NearestMipmapNearest,
|
||||
LinearMipmapNearest,
|
||||
NearestMipmapLinear,
|
||||
LinearMipmapLinear
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理寻址模式
|
||||
*/
|
||||
enum class AddressMode {
|
||||
Repeat,
|
||||
MirroredRepeat,
|
||||
ClampToEdge,
|
||||
ClampToBorder,
|
||||
MirrorClampToEdge
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理类型
|
||||
*/
|
||||
enum class TextureType {
|
||||
Texture2D,
|
||||
Texture2DArray,
|
||||
Texture3D,
|
||||
TextureCube,
|
||||
TextureCubeArray
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 索引格式
|
||||
*/
|
||||
enum class IndexFormat {
|
||||
UInt16,
|
||||
UInt32
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 混合状态
|
||||
*/
|
||||
struct BlendState {
|
||||
bool enabled = true;
|
||||
BlendFactor srcColorFactor = BlendFactor::SrcAlpha;
|
||||
BlendFactor dstColorFactor = BlendFactor::OneMinusSrcAlpha;
|
||||
BlendOp colorOp = BlendOp::Add;
|
||||
BlendFactor srcAlphaFactor = BlendFactor::One;
|
||||
BlendFactor dstAlphaFactor = BlendFactor::OneMinusSrcAlpha;
|
||||
BlendOp alphaOp = BlendOp::Add;
|
||||
|
||||
/**
|
||||
* @brief 创建不透明混合状态
|
||||
*/
|
||||
static BlendState opaque() {
|
||||
BlendState state;
|
||||
state.enabled = false;
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建标准 Alpha 混合状态
|
||||
*/
|
||||
static BlendState alphaBlend() {
|
||||
return BlendState{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建加法混合状态
|
||||
*/
|
||||
static BlendState additive() {
|
||||
BlendState state;
|
||||
state.srcColorFactor = BlendFactor::SrcAlpha;
|
||||
state.dstColorFactor = BlendFactor::One;
|
||||
state.srcAlphaFactor = BlendFactor::One;
|
||||
state.dstAlphaFactor = BlendFactor::One;
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建乘法混合状态
|
||||
*/
|
||||
static BlendState multiply() {
|
||||
BlendState state;
|
||||
state.srcColorFactor = BlendFactor::DstColor;
|
||||
state.dstColorFactor = BlendFactor::OneMinusSrcAlpha;
|
||||
state.srcAlphaFactor = BlendFactor::DstAlpha;
|
||||
state.dstAlphaFactor = BlendFactor::OneMinusSrcAlpha;
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建预乘 Alpha 混合状态
|
||||
*/
|
||||
static BlendState premultiplied() {
|
||||
BlendState state;
|
||||
state.srcColorFactor = BlendFactor::One;
|
||||
state.dstColorFactor = BlendFactor::OneMinusSrcAlpha;
|
||||
state.srcAlphaFactor = BlendFactor::One;
|
||||
state.dstAlphaFactor = BlendFactor::OneMinusSrcAlpha;
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 深度模板状态
|
||||
*/
|
||||
struct DepthStencilState {
|
||||
bool depthTestEnabled = false;
|
||||
bool depthWriteEnabled = true;
|
||||
CompareFunc depthCompareFunc = CompareFunc::Less;
|
||||
bool stencilEnabled = false;
|
||||
uint8_t stencilReadMask = 0xFF;
|
||||
uint8_t stencilWriteMask = 0xFF;
|
||||
uint8_t stencilRef = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建禁用深度测试的状态
|
||||
*/
|
||||
static DepthStencilState disabled() {
|
||||
return DepthStencilState{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建标准深度测试状态
|
||||
*/
|
||||
static DepthStencilState defaultDepth() {
|
||||
DepthStencilState state;
|
||||
state.depthTestEnabled = true;
|
||||
state.depthWriteEnabled = true;
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建只读深度测试状态
|
||||
*/
|
||||
static DepthStencilState readOnly() {
|
||||
DepthStencilState state;
|
||||
state.depthTestEnabled = true;
|
||||
state.depthWriteEnabled = false;
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 光栅化状态
|
||||
*/
|
||||
struct RasterizerState {
|
||||
CullMode cullMode = CullMode::None;
|
||||
FrontFace frontFace = FrontFace::CounterClockwise;
|
||||
bool wireframe = false;
|
||||
float lineWidth = 1.0f;
|
||||
|
||||
/**
|
||||
* @brief 创建无裁剪状态
|
||||
*/
|
||||
static RasterizerState noCull() {
|
||||
return RasterizerState{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建背面裁剪状态
|
||||
*/
|
||||
static RasterizerState cullBack() {
|
||||
RasterizerState state;
|
||||
state.cullMode = CullMode::Back;
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建正面裁剪状态
|
||||
*/
|
||||
static RasterizerState cullFront() {
|
||||
RasterizerState state;
|
||||
state.cullMode = CullMode::Front;
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 顶点属性
|
||||
*/
|
||||
struct VertexAttribute {
|
||||
uint32_t location = 0;
|
||||
Format format = Format::RGBA32_FLOAT;
|
||||
uint32_t offset = 0;
|
||||
uint32_t bufferSlot = 0;
|
||||
bool normalized = false;
|
||||
|
||||
VertexAttribute() = default;
|
||||
|
||||
VertexAttribute(uint32_t loc, Format fmt, uint32_t off, uint32_t slot = 0, bool norm = false)
|
||||
: location(loc), format(fmt), offset(off), bufferSlot(slot), normalized(norm) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 顶点缓冲区绑定
|
||||
*/
|
||||
struct VertexBufferBinding {
|
||||
uint32_t slot = 0;
|
||||
uint32_t stride = 0;
|
||||
uint32_t offset = 0;
|
||||
uint32_t divisor = 0;
|
||||
|
||||
VertexBufferBinding() = default;
|
||||
|
||||
VertexBufferBinding(uint32_t s, uint32_t str, uint32_t off = 0, uint32_t div = 0)
|
||||
: slot(s), stride(str), offset(off), divisor(div) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 视口
|
||||
*/
|
||||
struct Viewport {
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float width = 0.0f;
|
||||
float height = 0.0f;
|
||||
float minDepth = 0.0f;
|
||||
float maxDepth = 1.0f;
|
||||
|
||||
Viewport() = default;
|
||||
|
||||
Viewport(float x_, float y_, float w, float h, float minD = 0.0f, float maxD = 1.0f)
|
||||
: x(x_), y(y_), width(w), height(h), minDepth(minD), maxDepth(maxD) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 裁剪矩形
|
||||
*/
|
||||
struct ScissorRect {
|
||||
int32_t x = 0;
|
||||
int32_t y = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
|
||||
ScissorRect() = default;
|
||||
|
||||
ScissorRect(int32_t x_, int32_t y_, uint32_t w, uint32_t h)
|
||||
: x(x_), y(y_), width(w), height(h) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 颜色
|
||||
*/
|
||||
struct ColorValue {
|
||||
float r = 0.0f;
|
||||
float g = 0.0f;
|
||||
float b = 0.0f;
|
||||
float a = 1.0f;
|
||||
|
||||
ColorValue() = default;
|
||||
|
||||
ColorValue(float r_, float g_, float b_, float a_ = 1.0f)
|
||||
: r(r_), g(g_), b(b_), a(a_) {}
|
||||
|
||||
static ColorValue black() { return ColorValue(0.0f, 0.0f, 0.0f, 1.0f); }
|
||||
static ColorValue white() { return ColorValue(1.0f, 1.0f, 1.0f, 1.0f); }
|
||||
static ColorValue red() { return ColorValue(1.0f, 0.0f, 0.0f, 1.0f); }
|
||||
static ColorValue green() { return ColorValue(0.0f, 1.0f, 0.0f, 1.0f); }
|
||||
static ColorValue blue() { return ColorValue(0.0f, 0.0f, 1.0f, 1.0f); }
|
||||
static ColorValue transparent() { return ColorValue(0.0f, 0.0f, 0.0f, 0.0f); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 缓冲区描述
|
||||
*/
|
||||
struct BufferDesc {
|
||||
BufferType type = BufferType::Vertex;
|
||||
BufferUsage usage = BufferUsage::Static;
|
||||
size_t size = 0;
|
||||
const void* data = nullptr;
|
||||
std::string debugName;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理描述
|
||||
*/
|
||||
struct TextureDesc {
|
||||
TextureType type = TextureType::Texture2D;
|
||||
Format format = Format::RGBA8_UNORM;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t depth = 1;
|
||||
uint32_t mipLevels = 1;
|
||||
uint32_t arrayLayers = 1;
|
||||
uint32_t sampleCount = 1;
|
||||
const void* data = nullptr;
|
||||
std::string debugName;
|
||||
|
||||
/**
|
||||
* @brief 创建 2D 纹理描述
|
||||
*/
|
||||
static TextureDesc texture2D(uint32_t w, uint32_t h, Format fmt, const void* data = nullptr) {
|
||||
TextureDesc desc;
|
||||
desc.type = TextureType::Texture2D;
|
||||
desc.format = fmt;
|
||||
desc.width = w;
|
||||
desc.height = h;
|
||||
desc.data = data;
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief创建立方体纹理描述
|
||||
*/
|
||||
static TextureDesc textureCube(uint32_t size, Format fmt) {
|
||||
TextureDesc desc;
|
||||
desc.type = TextureType::TextureCube;
|
||||
desc.format = fmt;
|
||||
desc.width = size;
|
||||
desc.height = size;
|
||||
desc.arrayLayers = 6;
|
||||
return desc;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 采样器描述
|
||||
*/
|
||||
struct SamplerDesc {
|
||||
FilterMode minFilter = FilterMode::Linear;
|
||||
FilterMode magFilter = FilterMode::Linear;
|
||||
FilterMode mipFilter = FilterMode::LinearMipmapLinear;
|
||||
AddressMode addressU = AddressMode::ClampToEdge;
|
||||
AddressMode addressV = AddressMode::ClampToEdge;
|
||||
AddressMode addressW = AddressMode::ClampToEdge;
|
||||
float maxAnisotropy = 1.0f;
|
||||
float minLOD = 0.0f;
|
||||
float maxLOD = 1000.0f;
|
||||
float lodBias = 0.0f;
|
||||
ColorValue borderColor = ColorValue::black();
|
||||
bool compareEnabled = false;
|
||||
CompareFunc compareFunc = CompareFunc::Less;
|
||||
|
||||
/**
|
||||
* @brief 创建点采样器
|
||||
*/
|
||||
static SamplerDesc point() {
|
||||
SamplerDesc desc;
|
||||
desc.minFilter = FilterMode::Nearest;
|
||||
desc.magFilter = FilterMode::Nearest;
|
||||
desc.mipFilter = FilterMode::Nearest;
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建线性采样器
|
||||
*/
|
||||
static SamplerDesc linear() {
|
||||
return SamplerDesc{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建三线性采样器
|
||||
*/
|
||||
static SamplerDesc trilinear() {
|
||||
SamplerDesc desc;
|
||||
desc.minFilter = FilterMode::LinearMipmapLinear;
|
||||
desc.magFilter = FilterMode::Linear;
|
||||
desc.mipFilter = FilterMode::LinearMipmapLinear;
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建重复采样器
|
||||
*/
|
||||
static SamplerDesc repeat() {
|
||||
SamplerDesc desc;
|
||||
desc.addressU = AddressMode::Repeat;
|
||||
desc.addressV = AddressMode::Repeat;
|
||||
desc.addressW = AddressMode::Repeat;
|
||||
return desc;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器阶段
|
||||
*/
|
||||
enum class ShaderStage {
|
||||
Vertex,
|
||||
Fragment,
|
||||
Geometry,
|
||||
TessControl,
|
||||
TessEvaluation,
|
||||
Compute
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器描述
|
||||
*/
|
||||
struct ShaderDesc {
|
||||
std::string name;
|
||||
std::string vertexSource;
|
||||
std::string fragmentSource;
|
||||
std::string geometrySource;
|
||||
std::vector<uint8_t> binaryData;
|
||||
std::string entryPoint = "main";
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 管线描述
|
||||
*/
|
||||
struct PipelineDesc {
|
||||
BlendState blendState;
|
||||
DepthStencilState depthStencilState;
|
||||
RasterizerState rasterizerState;
|
||||
PrimitiveType primitiveType = PrimitiveType::Triangles;
|
||||
std::vector<VertexAttribute> vertexAttributes;
|
||||
std::vector<VertexBufferBinding> vertexBindings;
|
||||
Format colorFormat = Format::RGBA8_UNORM;
|
||||
Format depthStencilFormat = Format::D24_UNORM_S8_UINT;
|
||||
std::string debugName;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绘制命令
|
||||
*/
|
||||
struct DrawCommand {
|
||||
uint32_t vertexCount = 0;
|
||||
uint32_t instanceCount = 1;
|
||||
uint32_t firstVertex = 0;
|
||||
uint32_t firstInstance = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 索引绘制命令
|
||||
*/
|
||||
struct DrawIndexedCommand {
|
||||
uint32_t indexCount = 0;
|
||||
uint32_t instanceCount = 1;
|
||||
uint32_t firstIndex = 0;
|
||||
int32_t vertexOffset = 0;
|
||||
uint32_t firstInstance = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 帧缓冲附件描述
|
||||
*/
|
||||
struct AttachmentDesc {
|
||||
Format format = Format::RGBA8_UNORM;
|
||||
uint32_t sampleCount = 1;
|
||||
bool isDepthStencil = false;
|
||||
|
||||
AttachmentDesc() = default;
|
||||
|
||||
AttachmentDesc(Format fmt, uint32_t samples = 1, bool depthStencil = false)
|
||||
: format(fmt), sampleCount(samples), isDepthStencil(depthStencil) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 帧缓冲描述
|
||||
*/
|
||||
struct FramebufferDesc {
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
std::vector<AttachmentDesc> colorAttachments;
|
||||
AttachmentDesc depthStencilAttachment;
|
||||
std::string debugName;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取格式字节大小
|
||||
*/
|
||||
inline uint32_t getFormatSize(Format format) {
|
||||
switch (format) {
|
||||
case Format::R8_UNORM:
|
||||
case Format::R8_SNORM:
|
||||
return 1;
|
||||
case Format::RG8_UNORM:
|
||||
case Format::RG8_SNORM:
|
||||
case Format::R16_FLOAT:
|
||||
return 2;
|
||||
case Format::RGB8_UNORM:
|
||||
case Format::RGB8_SNORM:
|
||||
return 3;
|
||||
case Format::RGBA8_UNORM:
|
||||
case Format::RGBA8_SNORM:
|
||||
case Format::RG16_FLOAT:
|
||||
case Format::R32_FLOAT:
|
||||
case Format::R32_UINT:
|
||||
return 4;
|
||||
case Format::RGBA16_FLOAT:
|
||||
case Format::RG32_FLOAT:
|
||||
case Format::RG32_UINT:
|
||||
return 8;
|
||||
case Format::RGBA32_FLOAT:
|
||||
case Format::RGBA32_UINT:
|
||||
return 16;
|
||||
case Format::D16_UNORM:
|
||||
return 2;
|
||||
case Format::D24_UNORM_S8_UINT:
|
||||
return 4;
|
||||
case Format::D32_FLOAT:
|
||||
return 4;
|
||||
case Format::D32_FLOAT_S8_UINT:
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取索引格式字节大小
|
||||
*/
|
||||
inline uint32_t getIndexFormatSize(IndexFormat format) {
|
||||
switch (format) {
|
||||
case IndexFormat::UInt16:
|
||||
return 2;
|
||||
case IndexFormat::UInt32:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rhi
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_texture.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 字形信息结构
|
||||
*/
|
||||
struct GlyphInfo {
|
||||
uint32_t codepoint = 0;
|
||||
float width = 0;
|
||||
float height = 0;
|
||||
float bearingX = 0;
|
||||
float bearingY = 0;
|
||||
float advance = 0;
|
||||
float u0 = 0, v0 = 0;
|
||||
float u1 = 0, v1 = 0;
|
||||
bool isSDF = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 字体图集描述
|
||||
*/
|
||||
struct FontAtlasDesc {
|
||||
std::string filepath;
|
||||
std::vector<uint8_t> fontData;
|
||||
int fontSize = 16;
|
||||
bool useSDF = false;
|
||||
int atlasWidth = 1024;
|
||||
int atlasHeight = 1024;
|
||||
int padding = 2;
|
||||
float sdfEdgeValue = 0.5f;
|
||||
float sdfPixelDistScale = 4.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 字体图集
|
||||
*
|
||||
* 使用 stb_truetype 解析字体,stb_rect_pack 进行图集打包
|
||||
* 支持 Unicode 字符集,动态扩展,SDF 渲染
|
||||
*/
|
||||
class FontAtlas {
|
||||
public:
|
||||
FontAtlas();
|
||||
~FontAtlas();
|
||||
|
||||
/**
|
||||
* @brief 初始化字体图集
|
||||
* @param device RHI 设备
|
||||
* @param desc 字体图集描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(Ptr<rhi::RHIDevice> device, const FontAtlasDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 关闭字体图集
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否有效
|
||||
*/
|
||||
bool isValid() const { return valid_; }
|
||||
|
||||
/**
|
||||
* @brief 获取字形信息
|
||||
* @param codepoint Unicode 码点
|
||||
* @return 字形信息指针,未找到返回 nullptr
|
||||
*/
|
||||
const GlyphInfo* getGlyph(uint32_t codepoint);
|
||||
|
||||
/**
|
||||
* @brief 预加载字符
|
||||
* @param chars 字符列表(UTF-32)
|
||||
* @return 成功加载的字符数
|
||||
*/
|
||||
int preloadChars(const std::u32string& chars);
|
||||
|
||||
/**
|
||||
* @brief 预加载字符(UTF-8)
|
||||
* @param chars 字符列表(UTF-8)
|
||||
* @return 成功加载的字符数
|
||||
*/
|
||||
int preloadCharsUTF8(const std::string& chars);
|
||||
|
||||
/**
|
||||
* @brief 获取纹理
|
||||
*/
|
||||
Ptr<rhi::RHITexture> getTexture() const { return texture_; }
|
||||
|
||||
/**
|
||||
* @brief 获取采样器
|
||||
*/
|
||||
Ptr<rhi::RHISampler> getSampler() const { return sampler_; }
|
||||
|
||||
/**
|
||||
* @brief 获取字体大小
|
||||
*/
|
||||
int getFontSize() const { return fontSize_; }
|
||||
|
||||
/**
|
||||
* @brief 获取行高
|
||||
*/
|
||||
float getLineHeight() const { return lineHeight_; }
|
||||
|
||||
/**
|
||||
* @brief 获取上升高度
|
||||
*/
|
||||
float getAscent() const { return ascent_; }
|
||||
|
||||
/**
|
||||
* @brief 获取下降高度
|
||||
*/
|
||||
float getDescent() const { return descent_; }
|
||||
|
||||
/**
|
||||
* @brief 是否使用 SDF
|
||||
*/
|
||||
bool isSDF() const { return useSDF_; }
|
||||
|
||||
/**
|
||||
* @brief 计算文本宽度
|
||||
* @param text 文本(UTF-32)
|
||||
* @param scale 缩放比例
|
||||
* @return 文本宽度
|
||||
*/
|
||||
float measureText(const std::u32string& text, float scale = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 计算文本尺寸
|
||||
* @param text 文本(UTF-32)
|
||||
* @param scale 缩放比例
|
||||
* @return 文本尺寸
|
||||
*/
|
||||
Size measureTextSize(const std::u32string& text, float scale = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 获取已缓存字形数量
|
||||
*/
|
||||
size_t getGlyphCount() const { return glyphs_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 获取图集使用率
|
||||
*/
|
||||
float getUsageRatio() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 缓存单个字形
|
||||
*/
|
||||
bool cacheGlyph(uint32_t codepoint);
|
||||
|
||||
/**
|
||||
* @brief 更新纹理区域
|
||||
*/
|
||||
void updateTextureRegion(int x, int y, int width, int height, const uint8_t* data);
|
||||
|
||||
/**
|
||||
* @brief 初始化打包上下文
|
||||
*/
|
||||
bool initPackContext();
|
||||
|
||||
/**
|
||||
* @brief 在图集中分配空间
|
||||
*/
|
||||
bool allocateRect(int width, int height, int& outX, int& outY);
|
||||
|
||||
Ptr<rhi::RHIDevice> device_;
|
||||
Ptr<rhi::RHITexture> texture_;
|
||||
Ptr<rhi::RHISampler> sampler_;
|
||||
|
||||
std::vector<uint8_t> fontData_;
|
||||
std::vector<uint8_t> atlasData_;
|
||||
std::unordered_map<uint32_t, GlyphInfo> glyphs_;
|
||||
|
||||
int atlasWidth_ = 0;
|
||||
int atlasHeight_ = 0;
|
||||
int fontSize_ = 0;
|
||||
int padding_ = 2;
|
||||
bool useSDF_ = false;
|
||||
bool valid_ = false;
|
||||
|
||||
float lineHeight_ = 0;
|
||||
float ascent_ = 0;
|
||||
float descent_ = 0;
|
||||
float sdfEdgeValue_ = 0.5f;
|
||||
float sdfPixelDistScale_ = 4.0f;
|
||||
|
||||
void* stbFontInfo_ = nullptr;
|
||||
void* stbPackContext_ = nullptr;
|
||||
std::vector<uint8_t> packNodes_;
|
||||
std::mutex mutex_;
|
||||
|
||||
int usedArea_ = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/resources/font_atlas.h>
|
||||
#include <extra2d/resources/resource_handle.h>
|
||||
#include <extra2d/resources/resource_system.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 字体数据结构
|
||||
*/
|
||||
struct FontData {
|
||||
std::string name;
|
||||
std::string path;
|
||||
std::vector<uint8_t> fontFileData;
|
||||
std::unordered_map<int, Ptr<FontAtlas>> atlasBySize;
|
||||
int defaultFontSize = 16;
|
||||
bool useSDF = false;
|
||||
size_t memorySize = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 字体加载选项
|
||||
*/
|
||||
struct FontLoadOptions {
|
||||
int fontSize = 16;
|
||||
bool useSDF = false;
|
||||
int atlasWidth = 1024;
|
||||
int atlasHeight = 1024;
|
||||
int padding = 2;
|
||||
float sdfEdgeValue = 0.5f;
|
||||
float sdfPixelDistScale = 4.0f;
|
||||
std::u32string preloadChars;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 字体管理器
|
||||
*
|
||||
* 管理多个字体,支持不同字号和 SDF 渲染
|
||||
*/
|
||||
class FontManager : public BaseResourceManager<FontData> {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static FontManager& getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化字体管理器
|
||||
* @param device RHI 设备
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(Ptr<rhi::RHIDevice> device);
|
||||
|
||||
/**
|
||||
* @brief 关闭字体管理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 从文件加载字体
|
||||
* @param path 字体文件路径
|
||||
* @param options 加载选项
|
||||
* @return 字体句柄
|
||||
*/
|
||||
ResourceHandle<FontData> load(const std::string& path,
|
||||
const FontLoadOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 从内存数据加载字体
|
||||
* @param name 字体名称
|
||||
* @param data 字体数据
|
||||
* @param options 加载选项
|
||||
* @return 字体句柄
|
||||
*/
|
||||
ResourceHandle<FontData> loadFromMemory(const std::string& name,
|
||||
const std::vector<uint8_t>& data,
|
||||
const FontLoadOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 获取字体
|
||||
* @param path 字体路径或名称
|
||||
* @return 字体句柄
|
||||
*/
|
||||
ResourceHandle<FontData> get(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 检查字体是否存在
|
||||
* @param path 字体路径
|
||||
*/
|
||||
bool has(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* @brief 移除字体
|
||||
* @param path 字体路径
|
||||
*/
|
||||
void remove(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 清除所有字体
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取字体的图集
|
||||
* @param font 字体数据
|
||||
* @param fontSize 字体大小
|
||||
* @return 字体图集
|
||||
*/
|
||||
Ptr<FontAtlas> getAtlas(Ptr<FontData> font, int fontSize);
|
||||
|
||||
/**
|
||||
* @brief 获取字体的图集(原始指针版本)
|
||||
*/
|
||||
Ptr<FontAtlas> getAtlas(FontData* font, int fontSize);
|
||||
|
||||
/**
|
||||
* @brief 获取字体的图集(句柄版本)
|
||||
*/
|
||||
Ptr<FontAtlas> getAtlas(const FontHandle& font, int fontSize) {
|
||||
return getAtlas(font.get(), fontSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取字形信息
|
||||
* @param font 字体数据
|
||||
* @param fontSize 字体大小
|
||||
* @param codepoint Unicode 码点
|
||||
* @return 字形信息
|
||||
*/
|
||||
const GlyphInfo* getGlyph(Ptr<FontData> font, int fontSize, uint32_t codepoint);
|
||||
|
||||
/**
|
||||
* @brief 获取字形信息(原始指针版本)
|
||||
*/
|
||||
const GlyphInfo* getGlyph(FontData* font, int fontSize, uint32_t codepoint);
|
||||
|
||||
/**
|
||||
* @brief 获取字形信息(句柄版本)
|
||||
*/
|
||||
const GlyphInfo* getGlyph(const FontHandle& font, int fontSize, uint32_t codepoint) {
|
||||
return getGlyph(font.get(), fontSize, codepoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算文本宽度
|
||||
* @param font 字体数据
|
||||
* @param fontSize 字体大小
|
||||
* @param text 文本(UTF-32)
|
||||
* @return 文本宽度
|
||||
*/
|
||||
float measureText(Ptr<FontData> font, int fontSize, const std::u32string& text);
|
||||
|
||||
/**
|
||||
* @brief 计算文本宽度(原始指针版本)
|
||||
*/
|
||||
float measureText(FontData* font, int fontSize, const std::u32string& text);
|
||||
|
||||
/**
|
||||
* @brief 计算文本宽度(句柄版本)
|
||||
*/
|
||||
float measureText(const FontHandle& font, int fontSize, const std::u32string& text) {
|
||||
return measureText(font.get(), fontSize, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算文本尺寸
|
||||
* @param font 字体数据
|
||||
* @param fontSize 字体大小
|
||||
* @param text 文本(UTF-32)
|
||||
* @return 文本尺寸
|
||||
*/
|
||||
Size measureTextSize(Ptr<FontData> font, int fontSize, const std::u32string& text);
|
||||
|
||||
/**
|
||||
* @brief 计算文本尺寸(原始指针版本)
|
||||
*/
|
||||
Size measureTextSize(FontData* font, int fontSize, const std::u32string& text);
|
||||
|
||||
/**
|
||||
* @brief 计算文本尺寸(句柄版本)
|
||||
*/
|
||||
Size measureTextSize(const FontHandle& font, int fontSize, const std::u32string& text) {
|
||||
return measureTextSize(font.get(), fontSize, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取字体内存使用量
|
||||
*/
|
||||
size_t getFontMemory() const;
|
||||
|
||||
/**
|
||||
* @brief 获取字体数量
|
||||
*/
|
||||
size_t getFontCount() const;
|
||||
|
||||
/**
|
||||
* @brief 设置默认加载选项
|
||||
*/
|
||||
void setDefaultOptions(const FontLoadOptions& options) { defaultOptions_ = options; }
|
||||
|
||||
/**
|
||||
* @brief 获取默认加载选项
|
||||
*/
|
||||
const FontLoadOptions& getDefaultOptions() const { return defaultOptions_; }
|
||||
|
||||
private:
|
||||
FontManager() = default;
|
||||
~FontManager() = default;
|
||||
FontManager(const FontManager&) = delete;
|
||||
FontManager& operator=(const FontManager&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 创建字体图集
|
||||
*/
|
||||
Ptr<FontAtlas> createAtlas(FontData* fontData, int fontSize);
|
||||
|
||||
Ptr<rhi::RHIDevice> device_;
|
||||
bool initialized_ = false;
|
||||
FontLoadOptions defaultOptions_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 字体句柄类型别名
|
||||
*/
|
||||
using FontHandle = ResourceHandle<FontData>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_pipeline.h>
|
||||
#include <extra2d/resources/resource_handle.h>
|
||||
#include <extra2d/resources/resource_system.h>
|
||||
#include <extra2d/resources/shader_manager.h>
|
||||
#include <extra2d/resources/texture_manager.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 材质属性值
|
||||
*/
|
||||
struct MaterialProperty {
|
||||
enum class Type {
|
||||
Float,
|
||||
Vec2,
|
||||
Vec3,
|
||||
Vec4,
|
||||
Mat4,
|
||||
Int,
|
||||
Texture
|
||||
};
|
||||
|
||||
Type type = Type::Float;
|
||||
union {
|
||||
float floatValue = 0.0f;
|
||||
float vec2Value[2];
|
||||
float vec3Value[3];
|
||||
float vec4Value[4];
|
||||
float mat4Value[16];
|
||||
int intValue;
|
||||
};
|
||||
std::string texturePath;
|
||||
uint32_t textureSlot = 0;
|
||||
|
||||
MaterialProperty() { floatValue = 0.0f; }
|
||||
explicit MaterialProperty(float v) : type(Type::Float) { floatValue = v; }
|
||||
MaterialProperty(float x, float y) : type(Type::Vec2) {
|
||||
vec2Value[0] = x; vec2Value[1] = y;
|
||||
}
|
||||
MaterialProperty(float x, float y, float z) : type(Type::Vec3) {
|
||||
vec3Value[0] = x; vec3Value[1] = y; vec3Value[2] = z;
|
||||
}
|
||||
MaterialProperty(float x, float y, float z, float w) : type(Type::Vec4) {
|
||||
vec4Value[0] = x; vec4Value[1] = y; vec4Value[2] = z; vec4Value[3] = w;
|
||||
}
|
||||
explicit MaterialProperty(int v) : type(Type::Int) { intValue = v; }
|
||||
explicit MaterialProperty(const std::string& texPath, uint32_t slot = 0)
|
||||
: type(Type::Texture), texturePath(texPath), textureSlot(slot) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 材质数据结构
|
||||
*/
|
||||
struct MaterialData {
|
||||
std::string name;
|
||||
std::string path;
|
||||
std::string shaderName;
|
||||
ResourceHandle<ShaderData> shader;
|
||||
std::unordered_map<std::string, MaterialProperty> properties;
|
||||
rhi::BlendState blendState = rhi::BlendState::alphaBlend();
|
||||
rhi::DepthStencilState depthState = rhi::DepthStencilState::disabled();
|
||||
rhi::RasterizerState rasterizerState = rhi::RasterizerState::noCull();
|
||||
size_t memorySize = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 材质加载选项
|
||||
*/
|
||||
struct MaterialLoadOptions {
|
||||
std::string shaderName;
|
||||
rhi::BlendState blendState = rhi::BlendState::alphaBlend();
|
||||
rhi::DepthStencilState depthState = rhi::DepthStencilState::disabled();
|
||||
rhi::RasterizerState rasterizerState = rhi::RasterizerState::noCull();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 材质管理器
|
||||
*
|
||||
* 管理材质资源,支持属性设置、纹理绑定
|
||||
*/
|
||||
class MaterialManager : public BaseResourceManager<MaterialData> {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static MaterialManager& getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化材质管理器
|
||||
* @param device RHI 设备
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(Ptr<rhi::RHIDevice> device);
|
||||
|
||||
/**
|
||||
* @brief 关闭材质管理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 创建材质
|
||||
* @param name 材质名称
|
||||
* @param shaderName 着色器名称
|
||||
* @param options 加载选项
|
||||
* @return 材质句柄
|
||||
*/
|
||||
ResourceHandle<MaterialData> create(const std::string& name,
|
||||
const std::string& shaderName,
|
||||
const MaterialLoadOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 从 JSON 文件加载材质
|
||||
* @param path JSON 文件路径
|
||||
* @return 材质句柄
|
||||
*/
|
||||
ResourceHandle<MaterialData> load(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 获取材质
|
||||
* @param name 材质名称
|
||||
* @return 材质句柄
|
||||
*/
|
||||
ResourceHandle<MaterialData> get(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 检查材质是否存在
|
||||
* @param name 材质名称
|
||||
*/
|
||||
bool has(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 移除材质
|
||||
* @param name 材质名称
|
||||
*/
|
||||
void remove(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 清除所有材质
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 设置材质属性
|
||||
* @param material 材质数据
|
||||
* @param name 属性名称
|
||||
* @param value 属性值
|
||||
*/
|
||||
void setProperty(MaterialData* material, const std::string& name,
|
||||
const MaterialProperty& value);
|
||||
|
||||
/**
|
||||
* @brief 设置材质纹理
|
||||
* @param material 材质数据
|
||||
* @param name 属性名称
|
||||
* @param texturePath 纹理路径
|
||||
* @param slot 纹理槽
|
||||
*/
|
||||
void setTexture(MaterialData* material, const std::string& name,
|
||||
const std::string& texturePath, uint32_t slot = 0);
|
||||
|
||||
/**
|
||||
* @brief 应用材质到设备
|
||||
* @param material 材质数据
|
||||
* @param device RHI 设备
|
||||
*/
|
||||
void apply(MaterialData* material, rhi::RHIDevice* device);
|
||||
|
||||
/**
|
||||
* @brief 获取材质数量
|
||||
*/
|
||||
size_t getMaterialCount() const;
|
||||
|
||||
private:
|
||||
MaterialManager() = default;
|
||||
~MaterialManager() = default;
|
||||
MaterialManager(const MaterialManager&) = delete;
|
||||
MaterialManager& operator=(const MaterialManager&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 从 JSON 解析材质
|
||||
*/
|
||||
bool parseFromJson(const std::string& jsonContent, MaterialData* material);
|
||||
|
||||
Ptr<rhi::RHIDevice> device_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 材质句柄类型别名
|
||||
*/
|
||||
using MaterialHandle = ResourceHandle<MaterialData>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_buffer.h>
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <extra2d/resources/resource_handle.h>
|
||||
#include <extra2d/resources/resource_system.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 顶点格式
|
||||
*/
|
||||
struct Vertex {
|
||||
Vec2 position;
|
||||
Vec2 texCoord;
|
||||
Color color;
|
||||
|
||||
Vertex() = default;
|
||||
Vertex(const Vec2& pos, const Vec2& uv, const Color& col = Colors::White)
|
||||
: position(pos), texCoord(uv), color(col) {}
|
||||
Vertex(float x, float y, float u = 0, float v = 0, const Color& col = Colors::White)
|
||||
: position(x, y), texCoord(u, v), color(col) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 网格数据结构
|
||||
*/
|
||||
struct MeshData {
|
||||
std::string name;
|
||||
std::string path;
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<uint16_t> indices16;
|
||||
std::vector<uint32_t> indices32;
|
||||
Ptr<rhi::RHIBuffer> vertexBuffer;
|
||||
Ptr<rhi::RHIBuffer> indexBuffer;
|
||||
rhi::IndexFormat indexFormat = rhi::IndexFormat::UInt16;
|
||||
uint32_t vertexCount = 0;
|
||||
uint32_t indexCount = 0;
|
||||
rhi::PrimitiveType primitiveType = rhi::PrimitiveType::Triangles;
|
||||
bool dynamic = false;
|
||||
size_t memorySize = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 网格加载选项
|
||||
*/
|
||||
struct MeshLoadOptions {
|
||||
rhi::BufferUsage usage = rhi::BufferUsage::Static;
|
||||
rhi::PrimitiveType primitiveType = rhi::PrimitiveType::Triangles;
|
||||
bool use32BitIndices = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 网格构建器
|
||||
*/
|
||||
class MeshBuilder {
|
||||
public:
|
||||
MeshBuilder& addVertex(const Vertex& vertex);
|
||||
MeshBuilder& addVertex(const Vec2& pos, const Vec2& texCoord, const Color& color = Colors::White);
|
||||
MeshBuilder& addIndex(uint32_t index);
|
||||
MeshBuilder& addTriangle(uint32_t i0, uint32_t i1, uint32_t i2);
|
||||
MeshBuilder& addQuad(const Vec2& pos, const Vec2& size, const Vec2& uv0, const Vec2& uv1, const Color& color = Colors::White);
|
||||
|
||||
const std::vector<Vertex>& getVertices() const { return vertices_; }
|
||||
const std::vector<uint32_t>& getIndices() const { return indices_; }
|
||||
size_t getVertexCount() const { return vertices_.size(); }
|
||||
size_t getIndexCount() const { return indices_.size(); }
|
||||
void clear() { vertices_.clear(); indices_.clear(); }
|
||||
|
||||
private:
|
||||
std::vector<Vertex> vertices_;
|
||||
std::vector<uint32_t> indices_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 网格管理器
|
||||
*
|
||||
* 管理网格资源,支持动态和静态网格
|
||||
*/
|
||||
class MeshManager : public BaseResourceManager<MeshData> {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static MeshManager& getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化网格管理器
|
||||
* @param device RHI 设备
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(Ptr<rhi::RHIDevice> device);
|
||||
|
||||
/**
|
||||
* @brief 关闭网格管理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 创建网格
|
||||
* @param name 网格名称
|
||||
* @param vertices 顶点数据
|
||||
* @param indices 索引数据
|
||||
* @param options 加载选项
|
||||
* @return 网格句柄
|
||||
*/
|
||||
ResourceHandle<MeshData> create(const std::string& name,
|
||||
const std::vector<Vertex>& vertices,
|
||||
const std::vector<uint32_t>& indices,
|
||||
const MeshLoadOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 从构建器创建网格
|
||||
* @param name 网格名称
|
||||
* @param builder 网格构建器
|
||||
* @param options 加载选项
|
||||
* @return 网格句柄
|
||||
*/
|
||||
ResourceHandle<MeshData> create(const std::string& name,
|
||||
const MeshBuilder& builder,
|
||||
const MeshLoadOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 创建四边形网格
|
||||
* @param name 网格名称
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @return 网格句柄
|
||||
*/
|
||||
ResourceHandle<MeshData> createQuad(const std::string& name,
|
||||
float width = 1.0f, float height = 1.0f);
|
||||
|
||||
/**
|
||||
* @brief 创建动态网格
|
||||
* @param name 网格名称
|
||||
* @param maxVertices 最大顶点数
|
||||
* @param maxIndices 最大索引数
|
||||
* @param options 加载选项
|
||||
* @return 网格句柄
|
||||
*/
|
||||
ResourceHandle<MeshData> createDynamic(const std::string& name,
|
||||
uint32_t maxVertices,
|
||||
uint32_t maxIndices,
|
||||
const MeshLoadOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 更新动态网格数据
|
||||
* @param mesh 网格数据
|
||||
* @param vertices 顶点数据
|
||||
* @param vertexCount 顶点数量
|
||||
* @param indices 索引数据
|
||||
* @param indexCount 索引数量
|
||||
*/
|
||||
void updateDynamic(MeshData* mesh,
|
||||
const Vertex* vertices, uint32_t vertexCount,
|
||||
const uint32_t* indices, uint32_t indexCount);
|
||||
|
||||
/**
|
||||
* @brief 获取网格
|
||||
* @param name 网格名称
|
||||
* @return 网格句柄
|
||||
*/
|
||||
ResourceHandle<MeshData> get(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 检查网格是否存在
|
||||
* @param name 网格名称
|
||||
*/
|
||||
bool has(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 移除网格
|
||||
* @param name 网格名称
|
||||
*/
|
||||
void remove(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 清除所有网格
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取网格内存使用量
|
||||
*/
|
||||
size_t getMeshMemory() const;
|
||||
|
||||
/**
|
||||
* @brief 获取网格数量
|
||||
*/
|
||||
size_t getMeshCount() const;
|
||||
|
||||
private:
|
||||
MeshManager() = default;
|
||||
~MeshManager() = default;
|
||||
MeshManager(const MeshManager&) = delete;
|
||||
MeshManager& operator=(const MeshManager&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 创建顶点缓冲区
|
||||
*/
|
||||
Ptr<rhi::RHIBuffer> createVertexBuffer(const std::vector<Vertex>& vertices,
|
||||
rhi::BufferUsage usage);
|
||||
|
||||
/**
|
||||
* @brief 创建索引缓冲区
|
||||
*/
|
||||
Ptr<rhi::RHIBuffer> createIndexBuffer(const std::vector<uint32_t>& indices,
|
||||
bool use32Bit, rhi::BufferUsage usage);
|
||||
|
||||
Ptr<rhi::RHIDevice> device_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 网格句柄类型别名
|
||||
*/
|
||||
using MeshHandle = ResourceHandle<MeshData>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_shader.h>
|
||||
#include <extra2d/resources/resource_handle.h>
|
||||
#include <extra2d/resources/resource_system.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 着色器数据结构
|
||||
*/
|
||||
struct ShaderData {
|
||||
std::string name;
|
||||
std::string path;
|
||||
Ptr<rhi::RHIShader> shader;
|
||||
std::string vertexSource;
|
||||
std::string fragmentSource;
|
||||
std::vector<std::string> dependencies;
|
||||
std::unordered_map<std::string, std::string> uniforms;
|
||||
size_t memorySize = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器加载选项
|
||||
*/
|
||||
struct ShaderLoadOptions {
|
||||
std::vector<std::string> defines;
|
||||
bool enableCache = true;
|
||||
bool enableHotReload = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器 Uniform 定义
|
||||
*/
|
||||
struct ShaderUniformDef {
|
||||
std::string type;
|
||||
std::string description;
|
||||
float defaultValue = 0.0f;
|
||||
float defaultVec2[2] = {0, 0};
|
||||
float defaultVec3[3] = {0, 0, 0};
|
||||
float defaultVec4[4] = {0, 0, 0, 0};
|
||||
int defaultInt = 0;
|
||||
bool defaultBool = false;
|
||||
bool hasDefault = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器 Sampler 定义
|
||||
*/
|
||||
struct ShaderSamplerDef {
|
||||
std::string type;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器元数据
|
||||
*/
|
||||
struct ShaderMetadata {
|
||||
std::string name;
|
||||
std::string category;
|
||||
std::string version;
|
||||
std::string description;
|
||||
std::string vertPath;
|
||||
std::string fragPath;
|
||||
std::unordered_map<std::string, std::string> uniforms;
|
||||
std::unordered_map<std::string, ShaderUniformDef> uniformDefs;
|
||||
std::unordered_map<std::string, ShaderSamplerDef> samplerDefs;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器管理器
|
||||
*
|
||||
* 使用 RHI 接口创建着色器,保留 JSON 元数据加载功能
|
||||
* 支持同步加载、缓存管理、引用计数
|
||||
*/
|
||||
class ShaderManager : public BaseResourceManager<ShaderData> {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static ShaderManager& getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化着色器管理器
|
||||
* @param device RHI 设备
|
||||
* @param shaderDir 着色器目录
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(Ptr<rhi::RHIDevice> device, const std::string& shaderDir = "shaders");
|
||||
|
||||
/**
|
||||
* @brief 关闭着色器管理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 从文件加载着色器
|
||||
* @param name 着色器名称
|
||||
* @param vertPath 顶点着色器路径
|
||||
* @param fragPath 片段着色器路径
|
||||
* @param options 加载选项
|
||||
* @return 着色器句柄
|
||||
*/
|
||||
ResourceHandle<ShaderData> loadFromFiles(const std::string& name,
|
||||
const std::string& vertPath,
|
||||
const std::string& fragPath,
|
||||
const ShaderLoadOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 从源码加载着色器
|
||||
* @param name 着色器名称
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 着色器句柄
|
||||
*/
|
||||
ResourceHandle<ShaderData> loadFromSource(const std::string& name,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource);
|
||||
|
||||
/**
|
||||
* @brief 从 JSON 元数据文件加载着色器
|
||||
* @param jsonPath JSON 文件路径
|
||||
* @param name 着色器名称
|
||||
* @return 着色器句柄
|
||||
*/
|
||||
ResourceHandle<ShaderData> loadFromMetadata(const std::string& jsonPath,
|
||||
const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 获取着色器
|
||||
* @param name 着色器名称
|
||||
* @return 着色器句柄
|
||||
*/
|
||||
ResourceHandle<ShaderData> get(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 检查着色器是否存在
|
||||
* @param name 着色器名称
|
||||
*/
|
||||
bool has(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 移除着色器
|
||||
* @param name 着色器名称
|
||||
*/
|
||||
void remove(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 清除所有着色器
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取着色器元数据
|
||||
* @param name 着色器名称
|
||||
* @return 元数据
|
||||
*/
|
||||
ShaderMetadata getMetadata(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 获取着色器目录
|
||||
*/
|
||||
const std::string& getShaderDir() const { return shaderDir_; }
|
||||
|
||||
/**
|
||||
* @brief 加载内置着色器
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool loadBuiltinShaders();
|
||||
|
||||
/**
|
||||
* @brief 获取内置着色器
|
||||
* @param name 内置着色器名称
|
||||
* @return 着色器句柄
|
||||
*/
|
||||
ResourceHandle<ShaderData> getBuiltin(const std::string& name);
|
||||
|
||||
private:
|
||||
ShaderManager() = default;
|
||||
~ShaderManager() = default;
|
||||
ShaderManager(const ShaderManager&) = delete;
|
||||
ShaderManager& operator=(const ShaderManager&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 读取文件内容
|
||||
*/
|
||||
static std::string readFile(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 处理 #include 指令
|
||||
*/
|
||||
std::string processIncludes(const std::string& source,
|
||||
const std::string& baseDir,
|
||||
std::vector<std::string>& dependencies);
|
||||
|
||||
/**
|
||||
* @brief 解析 JSON 元数据
|
||||
*/
|
||||
bool parseMetadata(const std::string& jsonContent, ShaderMetadata& metadata,
|
||||
std::string& vertPath, std::string& fragPath);
|
||||
|
||||
Ptr<rhi::RHIDevice> device_;
|
||||
std::string shaderDir_;
|
||||
bool initialized_ = false;
|
||||
|
||||
std::unordered_map<std::string, ShaderMetadata> metadataCache_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器句柄类型别名
|
||||
*/
|
||||
using ShaderHandle = ResourceHandle<ShaderData>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/render/rhi/rhi_device.h>
|
||||
#include <extra2d/render/rhi/rhi_texture.h>
|
||||
#include <extra2d/resources/resource_handle.h>
|
||||
#include <extra2d/resources/resource_system.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 纹理数据结构
|
||||
*/
|
||||
struct TextureData {
|
||||
std::string name;
|
||||
std::string path;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t channels = 0;
|
||||
rhi::Format format = rhi::Format::RGBA8_UNORM;
|
||||
Ptr<rhi::RHITexture> texture;
|
||||
Ptr<rhi::RHISampler> sampler;
|
||||
bool isRenderTarget = false;
|
||||
bool hasMipmaps = false;
|
||||
size_t memorySize = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理加载选项
|
||||
*/
|
||||
struct TextureLoadOptions {
|
||||
bool generateMipmaps = true;
|
||||
bool sRGB = true;
|
||||
rhi::FilterMode minFilter = rhi::FilterMode::LinearMipmapLinear;
|
||||
rhi::FilterMode magFilter = rhi::FilterMode::Linear;
|
||||
rhi::AddressMode addressU = rhi::AddressMode::ClampToEdge;
|
||||
rhi::AddressMode addressV = rhi::AddressMode::ClampToEdge;
|
||||
bool premultiplyAlpha = false;
|
||||
bool flipVertically = true;
|
||||
int desiredChannels = 4;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理管理器
|
||||
*
|
||||
* 使用 stb_image 加载图像,通过 RHI 创建 GPU 纹理
|
||||
* 支持同步/异步加载、缓存管理、引用计数
|
||||
*/
|
||||
class TextureManager : public BaseResourceManager<TextureData> {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static TextureManager& getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理管理器
|
||||
* @param device RHI 设备
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(Ptr<rhi::RHIDevice> device);
|
||||
|
||||
/**
|
||||
* @brief 关闭纹理管理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理
|
||||
* @param path 文件路径
|
||||
* @param options 加载选项
|
||||
* @return 纹理句柄
|
||||
*/
|
||||
ResourceHandle<TextureData> load(const std::string& path,
|
||||
const TextureLoadOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 从内存数据加载纹理
|
||||
* @param name 纹理名称
|
||||
* @param data 图像数据
|
||||
* @param dataSize 数据大小
|
||||
* @param options 加载选项
|
||||
* @return 纹理句柄
|
||||
*/
|
||||
ResourceHandle<TextureData> loadFromMemory(const std::string& name,
|
||||
const uint8_t* data,
|
||||
size_t dataSize,
|
||||
const TextureLoadOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 从像素数据创建纹理
|
||||
* @param name 纹理名称
|
||||
* @param pixels 像素数据
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param channels 通道数
|
||||
* @param options 加载选项
|
||||
* @return 纹理句柄
|
||||
*/
|
||||
ResourceHandle<TextureData> create(const std::string& name,
|
||||
const uint8_t* pixels,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t channels,
|
||||
const TextureLoadOptions& options = {});
|
||||
|
||||
/**
|
||||
* @brief 创建空纹理(用于渲染目标)
|
||||
* @param name 纹理名称
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param format 格式
|
||||
* @param generateMipmaps 是否生成 Mipmap
|
||||
* @return 纹理句柄
|
||||
*/
|
||||
ResourceHandle<TextureData> createEmpty(const std::string& name,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
rhi::Format format = rhi::Format::RGBA8_UNORM,
|
||||
bool generateMipmaps = false);
|
||||
|
||||
/**
|
||||
* @brief 获取纹理
|
||||
* @param path 纹理路径或名称
|
||||
* @return 纹理句柄
|
||||
*/
|
||||
ResourceHandle<TextureData> get(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 检查纹理是否存在
|
||||
* @param path 纹理路径
|
||||
*/
|
||||
bool has(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* @brief 移除纹理
|
||||
* @param path 纹理路径
|
||||
*/
|
||||
void remove(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 清除所有纹理
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取纹理内存使用量
|
||||
*/
|
||||
size_t getTextureMemory() const;
|
||||
|
||||
/**
|
||||
* @brief 获取纹理数量
|
||||
*/
|
||||
size_t getTextureCount() const;
|
||||
|
||||
/**
|
||||
* @brief 设置默认加载选项
|
||||
*/
|
||||
void setDefaultOptions(const TextureLoadOptions& options) { defaultOptions_ = options; }
|
||||
|
||||
/**
|
||||
* @brief 获取默认加载选项
|
||||
*/
|
||||
const TextureLoadOptions& getDefaultOptions() const { return defaultOptions_; }
|
||||
|
||||
private:
|
||||
TextureManager() = default;
|
||||
~TextureManager() = default;
|
||||
TextureManager(const TextureManager&) = delete;
|
||||
TextureManager& operator=(const TextureManager&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 创建采样器
|
||||
*/
|
||||
Ptr<rhi::RHISampler> createSampler(const TextureLoadOptions& options);
|
||||
|
||||
/**
|
||||
* @brief 计算纹理内存大小
|
||||
*/
|
||||
size_t calculateMemorySize(uint32_t width, uint32_t height,
|
||||
rhi::Format format, bool hasMipmaps);
|
||||
|
||||
Ptr<rhi::RHIDevice> device_;
|
||||
bool initialized_ = false;
|
||||
TextureLoadOptions defaultOptions_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理句柄类型别名
|
||||
*/
|
||||
using TextureHandle = ResourceHandle<TextureData>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Node;
|
||||
class Scene;
|
||||
class RenderQueue;
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* @brief 组件类型ID生成器
|
||||
*
|
||||
* 使用静态计数器为每种组件类型生成唯一ID
|
||||
*/
|
||||
inline uint32_t getNextComponentTypeId() {
|
||||
static uint32_t nextId = 0;
|
||||
return nextId++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取组件类型的唯一ID
|
||||
* @tparam T 组件类型
|
||||
* @return 组件类型的唯一ID
|
||||
*/
|
||||
template<typename T>
|
||||
uint32_t getComponentTypeId() {
|
||||
static uint32_t id = getNextComponentTypeId();
|
||||
return id;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief 组件类型声明宏
|
||||
*
|
||||
* 在组件类中使用此宏声明类型ID和类型名称
|
||||
*/
|
||||
#define E2D_COMPONENT_TYPE_DECL(ComponentClass) \
|
||||
public: \
|
||||
uint32_t getTypeId() const override { \
|
||||
return ::extra2d::detail::getComponentTypeId<ComponentClass>(); \
|
||||
} \
|
||||
const char* getTypeName() const override { return #ComponentClass; } \
|
||||
static uint32_t getStaticTypeId() { \
|
||||
return ::extra2d::detail::getComponentTypeId<ComponentClass>(); \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 组件基类
|
||||
*
|
||||
* 所有场景节点组件的基类,提供组件生命周期管理和节点关联
|
||||
* 组件可以添加到节点上以扩展节点的功能
|
||||
*/
|
||||
class Component : public std::enable_shared_from_this<Component> {
|
||||
public:
|
||||
virtual ~Component() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化组件
|
||||
*
|
||||
* 在组件添加到节点后调用,用于执行初始化逻辑
|
||||
*/
|
||||
virtual void init() {}
|
||||
|
||||
/**
|
||||
* @brief 销毁组件
|
||||
*
|
||||
* 在组件从节点移除前调用,用于清理资源
|
||||
*/
|
||||
virtual void destroy() {}
|
||||
|
||||
/**
|
||||
* @brief 更新组件
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*
|
||||
* 每帧调用,用于更新组件状态
|
||||
*/
|
||||
virtual void update(float dt) { (void)dt; }
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令
|
||||
* @param queue 渲染队列引用
|
||||
*
|
||||
* 将组件的渲染命令添加到渲染队列中
|
||||
*/
|
||||
virtual void collectRenderCommands(RenderQueue& queue) { (void)queue; }
|
||||
|
||||
/**
|
||||
* @brief 获取关联的节点
|
||||
* @return 关联的节点指针,未关联返回nullptr
|
||||
*/
|
||||
Node* getNode() const { return node_; }
|
||||
|
||||
/**
|
||||
* @brief 获取关联的场景
|
||||
* @return 关联的场景指针,未关联返回nullptr
|
||||
*/
|
||||
Scene* getScene() const;
|
||||
|
||||
/**
|
||||
* @brief 检查组件是否启用
|
||||
* @return 如果组件启用返回true
|
||||
*/
|
||||
bool isEnabled() const { return enabled_; }
|
||||
|
||||
/**
|
||||
* @brief 设置组件启用状态
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void setEnabled(bool enabled) { enabled_ = enabled; }
|
||||
|
||||
/**
|
||||
* @brief 获取组件类型ID
|
||||
* @return 组件类型的唯一ID
|
||||
*/
|
||||
virtual uint32_t getTypeId() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取组件类型名称
|
||||
* @return 组件类型的名称字符串
|
||||
*/
|
||||
virtual const char* getTypeName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查组件是否已初始化
|
||||
* @return 如果组件已初始化返回true
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
protected:
|
||||
friend class Node;
|
||||
|
||||
/**
|
||||
* @brief 设置关联的节点
|
||||
* @param node 节点指针
|
||||
*/
|
||||
void setNode(Node* node) { node_ = node; }
|
||||
|
||||
/**
|
||||
* @brief 设置初始化状态
|
||||
* @param initialized 是否已初始化
|
||||
*/
|
||||
void setInitialized(bool initialized) { initialized_ = initialized; }
|
||||
|
||||
Node* node_ = nullptr;
|
||||
bool enabled_ = true;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/render/core/render_command.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <extra2d/scene/component.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 形状渲染组件
|
||||
*
|
||||
* 负责渲染基本形状:线条、矩形、圆形、三角形、多边形等
|
||||
* 支持填充和轮廓两种渲染模式
|
||||
*/
|
||||
class ShapeRenderer : public Component {
|
||||
E2D_COMPONENT_TYPE_DECL(ShapeRenderer)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
ShapeRenderer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~ShapeRenderer() override;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 颜色和混合
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置颜色
|
||||
* @param color 颜色值
|
||||
*/
|
||||
void setColor(const Color& color);
|
||||
|
||||
/**
|
||||
* @brief 获取颜色
|
||||
* @return 颜色值
|
||||
*/
|
||||
Color getColor() const { return color_; }
|
||||
|
||||
/**
|
||||
* @brief 设置混合状态
|
||||
* @param blend 混合状态
|
||||
*/
|
||||
void setBlendState(const rhi::BlendState& blend);
|
||||
|
||||
/**
|
||||
* @brief 获取混合状态
|
||||
* @return 混合状态
|
||||
*/
|
||||
rhi::BlendState getBlendState() const { return blend_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 线条宽度
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置线条宽度
|
||||
* @param width 线条宽度
|
||||
*/
|
||||
void setLineWidth(float width);
|
||||
|
||||
/**
|
||||
* @brief 获取线条宽度
|
||||
* @return 线条宽度
|
||||
*/
|
||||
float getLineWidth() const { return lineWidth_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 渲染层级
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置渲染层级
|
||||
* @param layer 层级值
|
||||
*/
|
||||
void setLayer(int layer);
|
||||
|
||||
/**
|
||||
* @brief 获取渲染层级
|
||||
* @return 层级值
|
||||
*/
|
||||
int getLayer() const { return layer_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 形状绘制方法
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 绘制线条
|
||||
* @param start 起点坐标
|
||||
* @param end 终点坐标
|
||||
*/
|
||||
void drawLine(const Vec2& start, const Vec2& end);
|
||||
|
||||
/**
|
||||
* @brief 绘制多条线段
|
||||
* @param points 点列表
|
||||
*/
|
||||
void drawLines(const std::vector<Vec2>& points);
|
||||
|
||||
/**
|
||||
* @brief 绘制折线
|
||||
* @param points 点列表
|
||||
*/
|
||||
void drawPolyline(const std::vector<Vec2>& points);
|
||||
|
||||
/**
|
||||
* @brief 绘制矩形(轮廓)
|
||||
* @param rect 矩形区域
|
||||
*/
|
||||
void drawRect(const Rect& rect);
|
||||
|
||||
/**
|
||||
* @brief 绘制填充矩形
|
||||
* @param rect 矩形区域
|
||||
*/
|
||||
void drawFilledRect(const Rect& rect);
|
||||
|
||||
/**
|
||||
* @brief 绘制圆形(轮廓)
|
||||
* @param center 圆心坐标
|
||||
* @param radius 半径
|
||||
* @param segments 分段数(0表示使用默认值)
|
||||
*/
|
||||
void drawCircle(const Vec2& center, float radius, int segments = 0);
|
||||
|
||||
/**
|
||||
* @brief 绘制填充圆形
|
||||
* @param center 圆心坐标
|
||||
* @param radius 半径
|
||||
* @param segments 分段数(0表示使用默认值)
|
||||
*/
|
||||
void drawFilledCircle(const Vec2& center, float radius, int segments = 0);
|
||||
|
||||
/**
|
||||
* @brief 绘制椭圆(轮廓)
|
||||
* @param center 圆心坐标
|
||||
* @param radiusX X轴半径
|
||||
* @param radiusY Y轴半径
|
||||
* @param segments 分段数(0表示使用默认值)
|
||||
*/
|
||||
void drawEllipse(const Vec2& center, float radiusX, float radiusY, int segments = 0);
|
||||
|
||||
/**
|
||||
* @brief 绘制填充椭圆
|
||||
* @param center 圆心坐标
|
||||
* @param radiusX X轴半径
|
||||
* @param radiusY Y轴半径
|
||||
* @param segments 分段数(0表示使用默认值)
|
||||
*/
|
||||
void drawFilledEllipse(const Vec2& center, float radiusX, float radiusY, int segments = 0);
|
||||
|
||||
/**
|
||||
* @brief 绘制三角形(轮廓)
|
||||
* @param p1 第一个顶点
|
||||
* @param p2 第二个顶点
|
||||
* @param p3 第三个顶点
|
||||
*/
|
||||
void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3);
|
||||
|
||||
/**
|
||||
* @brief 绘制填充三角形
|
||||
* @param p1 第一个顶点
|
||||
* @param p2 第二个顶点
|
||||
* @param p3 第三个顶点
|
||||
*/
|
||||
void drawFilledTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3);
|
||||
|
||||
/**
|
||||
* @brief 绘制多边形(轮廓)
|
||||
* @param points 顶点列表
|
||||
*/
|
||||
void drawPolygon(const std::vector<Vec2>& points);
|
||||
|
||||
/**
|
||||
* @brief 绘制填充多边形
|
||||
* @param points 顶点列表
|
||||
*/
|
||||
void drawFilledPolygon(const std::vector<Vec2>& points);
|
||||
|
||||
/**
|
||||
* @brief 绘制圆弧
|
||||
* @param center 圆心坐标
|
||||
* @param radius 半径
|
||||
* @param startAngle 起始角度(度)
|
||||
* @param endAngle 结束角度(度)
|
||||
* @param segments 分段数(0表示使用默认值)
|
||||
*/
|
||||
void drawArc(const Vec2& center, float radius, float startAngle, float endAngle, int segments = 0);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 形状管理
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 清除所有形状
|
||||
*/
|
||||
void clearShapes();
|
||||
|
||||
/**
|
||||
* @brief 检查是否有形状
|
||||
* @return 如果有形状返回true
|
||||
*/
|
||||
bool hasShapes() const { return !shapes_.empty(); }
|
||||
|
||||
/**
|
||||
* @brief 获取形状数量
|
||||
* @return 形状数量
|
||||
*/
|
||||
size_t getShapeCount() const { return shapes_.size(); }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 组件生命周期
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 初始化组件
|
||||
*/
|
||||
void init() override;
|
||||
|
||||
/**
|
||||
* @brief 销毁组件
|
||||
*/
|
||||
void destroy() override;
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令
|
||||
* @param queue 渲染队列
|
||||
*/
|
||||
void collectRenderCommands(RenderQueue& queue) override;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 边界框
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取边界矩形
|
||||
* @return 边界矩形
|
||||
*/
|
||||
Rect getBounds() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 内部形状数据结构
|
||||
*/
|
||||
struct ShapeData {
|
||||
ShapeType type;
|
||||
std::vector<Vec2> points;
|
||||
Color color;
|
||||
float lineWidth;
|
||||
float radius;
|
||||
int segments;
|
||||
rhi::BlendState blend;
|
||||
bool filled;
|
||||
|
||||
ShapeData()
|
||||
: type(ShapeType::Rect), points(), color(Colors::White),
|
||||
lineWidth(1.0f), radius(0.0f), segments(32),
|
||||
blend(rhi::BlendState::alphaBlend()), filled(false) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 添加形状
|
||||
* @param shape 形状数据
|
||||
*/
|
||||
void addShape(const ShapeData& shape);
|
||||
|
||||
std::vector<ShapeData> shapes_;
|
||||
Color color_;
|
||||
rhi::BlendState blend_;
|
||||
float lineWidth_;
|
||||
int layer_;
|
||||
int defaultSegments_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/render/rhi/rhi_texture.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <extra2d/resources/texture_manager.h>
|
||||
#include <extra2d/scene/component.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 精灵渲染组件
|
||||
*
|
||||
* 负责渲染2D精灵图像,支持纹理、颜色调制、混合模式等功能
|
||||
* 附加到节点上时,会自动收集渲染命令到渲染队列
|
||||
*/
|
||||
class SpriteRenderer : public Component {
|
||||
E2D_COMPONENT_TYPE_DECL(SpriteRenderer)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
SpriteRenderer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~SpriteRenderer() override;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 纹理设置
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置纹理(使用纹理句柄)
|
||||
* @param handle 纹理句柄
|
||||
*/
|
||||
void setTexture(const TextureHandle& handle);
|
||||
|
||||
/**
|
||||
* @brief 设置纹理(通过路径加载)
|
||||
* @param path 纹理文件路径
|
||||
*/
|
||||
void setTexture(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 获取纹理句柄
|
||||
* @return 纹理句柄
|
||||
*/
|
||||
const TextureHandle& getTexture() const { return texture_; }
|
||||
|
||||
/**
|
||||
* @brief 获取纹理宽度
|
||||
* @return 纹理宽度,无纹理返回0
|
||||
*/
|
||||
uint32_t getTextureWidth() const;
|
||||
|
||||
/**
|
||||
* @brief 获取纹理高度
|
||||
* @return 纹理高度,无纹理返回0
|
||||
*/
|
||||
uint32_t getTextureHeight() const;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 源矩形设置(纹理区域)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置源矩形(纹理坐标区域)
|
||||
* @param rect 源矩形(像素坐标)
|
||||
*/
|
||||
void setSrcRect(const Rect& rect);
|
||||
|
||||
/**
|
||||
* @brief 设置源矩形
|
||||
* @param x 起始X坐标
|
||||
* @param y 起始Y坐标
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
*/
|
||||
void setSrcRect(float x, float y, float width, float height);
|
||||
|
||||
/**
|
||||
* @brief 获取源矩形
|
||||
* @return 源矩形
|
||||
*/
|
||||
Rect getSrcRect() const { return srcRect_; }
|
||||
|
||||
/**
|
||||
* @brief 重置源矩形为整个纹理
|
||||
*/
|
||||
void resetSrcRect();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 目标尺寸设置
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置内容尺寸
|
||||
* @param size 尺寸
|
||||
*/
|
||||
void setContentSize(const Size& size);
|
||||
|
||||
/**
|
||||
* @brief 设置内容尺寸
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
*/
|
||||
void setContentSize(float width, float height);
|
||||
|
||||
/**
|
||||
* @brief 获取内容尺寸
|
||||
* @return 内容尺寸
|
||||
*/
|
||||
Size getContentSize() const { return contentSize_; }
|
||||
|
||||
/**
|
||||
* @brief 重置内容尺寸为纹理原始尺寸
|
||||
*/
|
||||
void resetContentSize();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 颜色和混合
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置颜色调制
|
||||
* @param color 颜色值
|
||||
*/
|
||||
void setColor(const Color& color);
|
||||
|
||||
/**
|
||||
* @brief 获取颜色
|
||||
* @return 颜色值
|
||||
*/
|
||||
Color getColor() const { return color_; }
|
||||
|
||||
/**
|
||||
* @brief 设置混合状态
|
||||
* @param blend 混合状态
|
||||
*/
|
||||
void setBlendState(const rhi::BlendState& blend);
|
||||
|
||||
/**
|
||||
* @brief 获取混合状态
|
||||
* @return 混合状态
|
||||
*/
|
||||
rhi::BlendState getBlendState() const { return blend_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 翻转
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置X轴翻转
|
||||
* @param flip 是否翻转
|
||||
*/
|
||||
void setFlipX(bool flip);
|
||||
|
||||
/**
|
||||
* @brief 获取X轴翻转状态
|
||||
* @return 是否翻转
|
||||
*/
|
||||
bool isFlipX() const { return flipX_; }
|
||||
|
||||
/**
|
||||
* @brief 设置Y轴翻转
|
||||
* @param flip 是否翻转
|
||||
*/
|
||||
void setFlipY(bool flip);
|
||||
|
||||
/**
|
||||
* @brief 获取Y轴翻转状态
|
||||
* @return 是否翻转
|
||||
*/
|
||||
bool isFlipY() const { return flipY_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 渲染层级
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置渲染层级
|
||||
* @param layer 层级值
|
||||
*/
|
||||
void setLayer(int layer);
|
||||
|
||||
/**
|
||||
* @brief 获取渲染层级
|
||||
* @return 层级值
|
||||
*/
|
||||
int getLayer() const { return layer_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 组件生命周期
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 初始化组件
|
||||
*/
|
||||
void init() override;
|
||||
|
||||
/**
|
||||
* @brief 销毁组件
|
||||
*/
|
||||
void destroy() override;
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令
|
||||
* @param queue 渲染队列
|
||||
*/
|
||||
void collectRenderCommands(RenderQueue& queue) override;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 边界框
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取边界矩形
|
||||
* @return 边界矩形
|
||||
*/
|
||||
Rect getBounds() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 计算目标矩形
|
||||
* @return 目标矩形
|
||||
*/
|
||||
Rect calculateDestRect() const;
|
||||
|
||||
/**
|
||||
* @brief 计算翻转后的源矩形
|
||||
* @return 翻转后的源矩形
|
||||
*/
|
||||
Rect calculateFlippedSrcRect() const;
|
||||
|
||||
/**
|
||||
* @brief 更新内容尺寸
|
||||
*/
|
||||
void updateContentSizeFromTexture();
|
||||
|
||||
TextureHandle texture_;
|
||||
Rect srcRect_;
|
||||
Size contentSize_;
|
||||
Color color_;
|
||||
rhi::BlendState blend_;
|
||||
int layer_;
|
||||
bool flipX_;
|
||||
bool flipY_;
|
||||
bool useTextureSize_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/unicode_utils.h>
|
||||
#include <extra2d/render/batch/text_batcher.h>
|
||||
#include <extra2d/render/rhi/rhi_types.h>
|
||||
#include <extra2d/resources/font_manager.h>
|
||||
#include <extra2d/scene/component.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 文本渲染组件
|
||||
*
|
||||
* 负责渲染文本,支持多种编码、字体、对齐方式等
|
||||
* 使用UnicodeUtils处理编码转换,FontManager管理字体
|
||||
*/
|
||||
class TextRenderer : public Component {
|
||||
E2D_COMPONENT_TYPE_DECL(TextRenderer)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
TextRenderer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~TextRenderer() override;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 文本设置
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置文本(UTF-32)
|
||||
* @param text UTF-32编码的文本
|
||||
*/
|
||||
void setText(const std::u32string& text);
|
||||
|
||||
/**
|
||||
* @brief 设置文本(UTF-8)
|
||||
* @param text UTF-8编码的文本
|
||||
*/
|
||||
void setTextUTF8(const std::string& text);
|
||||
|
||||
/**
|
||||
* @brief 设置文本(指定编码)
|
||||
* @param text 文本数据
|
||||
* @param encoding 编码类型
|
||||
*/
|
||||
void setText(const std::string& text, TextEncoding encoding);
|
||||
|
||||
/**
|
||||
* @brief 获取文本(UTF-32)
|
||||
* @return UTF-32编码的文本
|
||||
*/
|
||||
const std::u32string& getText() const { return text_; }
|
||||
|
||||
/**
|
||||
* @brief 获取文本(UTF-8)
|
||||
* @return UTF-8编码的文本
|
||||
*/
|
||||
std::string getTextUTF8() const;
|
||||
|
||||
/**
|
||||
* @brief 清空文本
|
||||
*/
|
||||
void clearText();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 字体设置
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置字体(使用字体句柄)
|
||||
* @param handle 字体句柄
|
||||
*/
|
||||
void setFont(const FontHandle& handle);
|
||||
|
||||
/**
|
||||
* @brief 设置字体(通过路径加载)
|
||||
* @param path 字体文件路径
|
||||
*/
|
||||
void setFont(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 获取字体句柄
|
||||
* @return 字体句柄
|
||||
*/
|
||||
const FontHandle& getFont() const { return font_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 字体大小
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置字体大小
|
||||
* @param size 字体大小(像素)
|
||||
*/
|
||||
void setFontSize(int size);
|
||||
|
||||
/**
|
||||
* @brief 获取字体大小
|
||||
* @return 字体大小
|
||||
*/
|
||||
int getFontSize() const { return fontSize_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 颜色和混合
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置颜色
|
||||
* @param color 颜色值
|
||||
*/
|
||||
void setColor(const Color& color);
|
||||
|
||||
/**
|
||||
* @brief 获取颜色
|
||||
* @return 颜色值
|
||||
*/
|
||||
Color getColor() const { return color_; }
|
||||
|
||||
/**
|
||||
* @brief 设置混合状态
|
||||
* @param blend 混合状态
|
||||
*/
|
||||
void setBlendState(const rhi::BlendState& blend);
|
||||
|
||||
/**
|
||||
* @brief 获取混合状态
|
||||
* @return 混合状态
|
||||
*/
|
||||
rhi::BlendState getBlendState() const { return blend_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 对齐方式
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置水平对齐
|
||||
* @param alignment 水平对齐方式
|
||||
*/
|
||||
void setAlignment(TextAlignment alignment);
|
||||
|
||||
/**
|
||||
* @brief 获取水平对齐
|
||||
* @return 水平对齐方式
|
||||
*/
|
||||
TextAlignment getAlignment() const { return alignment_; }
|
||||
|
||||
/**
|
||||
* @brief 设置垂直对齐
|
||||
* @param alignment 垂直对齐方式
|
||||
*/
|
||||
void setVerticalAlignment(TextVerticalAlignment alignment);
|
||||
|
||||
/**
|
||||
* @brief 获取垂直对齐
|
||||
* @return 垂直对齐方式
|
||||
*/
|
||||
TextVerticalAlignment getVerticalAlignment() const { return verticalAlignment_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 布局设置
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置最大宽度(0表示无限制)
|
||||
* @param width 最大宽度
|
||||
*/
|
||||
void setMaxWidth(float width);
|
||||
|
||||
/**
|
||||
* @brief 获取最大宽度
|
||||
* @return 最大宽度
|
||||
*/
|
||||
float getMaxWidth() const { return maxWidth_; }
|
||||
|
||||
/**
|
||||
* @brief 设置行高(0表示使用字体默认行高)
|
||||
* @param height 行高
|
||||
*/
|
||||
void setLineHeight(float height);
|
||||
|
||||
/**
|
||||
* @brief 获取行高
|
||||
* @return 行高
|
||||
*/
|
||||
float getLineHeight() const { return lineHeight_; }
|
||||
|
||||
/**
|
||||
* @brief 设置是否自动换行
|
||||
* @param wrap 是否自动换行
|
||||
*/
|
||||
void setWordWrap(bool wrap);
|
||||
|
||||
/**
|
||||
* @brief 获取是否自动换行
|
||||
* @return 是否自动换行
|
||||
*/
|
||||
bool isWordWrap() const { return wordWrap_; }
|
||||
|
||||
/**
|
||||
* @brief 设置缩放比例
|
||||
* @param scale 缩放比例
|
||||
*/
|
||||
void setScale(float scale);
|
||||
|
||||
/**
|
||||
* @brief 获取缩放比例
|
||||
* @return 缩放比例
|
||||
*/
|
||||
float getScale() const { return scale_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 渲染层级
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置渲染层级
|
||||
* @param layer 层级值
|
||||
*/
|
||||
void setLayer(int layer);
|
||||
|
||||
/**
|
||||
* @brief 获取渲染层级
|
||||
* @return 层级值
|
||||
*/
|
||||
int getLayer() const { return layer_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 组件生命周期
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 初始化组件
|
||||
*/
|
||||
void init() override;
|
||||
|
||||
/**
|
||||
* @brief 销毁组件
|
||||
*/
|
||||
void destroy() override;
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令
|
||||
* @param queue 渲染队列
|
||||
*/
|
||||
void collectRenderCommands(RenderQueue& queue) override;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 文本测量
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 计算文本尺寸
|
||||
* @return 文本尺寸
|
||||
*/
|
||||
Size measureText() const;
|
||||
|
||||
/**
|
||||
* @brief 计算文本宽度
|
||||
* @return 文本宽度
|
||||
*/
|
||||
float measureTextWidth() const;
|
||||
|
||||
/**
|
||||
* @brief 获取边界矩形
|
||||
* @return 边界矩形
|
||||
*/
|
||||
Rect getBounds() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 计算文本起始位置(根据对齐方式)
|
||||
* @return 起始位置
|
||||
*/
|
||||
Vec2 calculateStartPosition() const;
|
||||
|
||||
std::u32string text_;
|
||||
FontHandle font_;
|
||||
int fontSize_;
|
||||
Color color_;
|
||||
rhi::BlendState blend_;
|
||||
TextAlignment alignment_;
|
||||
TextVerticalAlignment verticalAlignment_;
|
||||
float maxWidth_;
|
||||
float lineHeight_;
|
||||
float scale_;
|
||||
int layer_;
|
||||
bool wordWrap_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/scene/component.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 变换组件
|
||||
*
|
||||
* 管理节点的位置、旋转、缩放等变换属性
|
||||
* 支持本地坐标系和世界坐标系的变换计算
|
||||
*/
|
||||
class TransformComponent : public Component {
|
||||
E2D_COMPONENT_TYPE_DECL(TransformComponent)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
TransformComponent() = default;
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~TransformComponent() override = default;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 位置属性
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置本地位置
|
||||
* @param pos 新的位置坐标
|
||||
*/
|
||||
void setPosition(const Vec2& pos);
|
||||
|
||||
/**
|
||||
* @brief 设置本地位置
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
*/
|
||||
void setPosition(float x, float y) { setPosition(Vec2(x, y)); }
|
||||
|
||||
/**
|
||||
* @brief 获取本地位置
|
||||
* @return 本地位置坐标
|
||||
*/
|
||||
Vec2 getPosition() const { return position_; }
|
||||
|
||||
/**
|
||||
* @brief 设置世界位置
|
||||
* @param pos 世界坐标位置
|
||||
*/
|
||||
void setWorldPosition(const Vec2& pos);
|
||||
|
||||
/**
|
||||
* @brief 获取世界位置
|
||||
* @return 世界坐标位置
|
||||
*/
|
||||
Vec2 getWorldPosition() const;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 旋转属性
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置旋转角度
|
||||
* @param degrees 旋转角度(度数)
|
||||
*/
|
||||
void setRotation(float degrees);
|
||||
|
||||
/**
|
||||
* @brief 获取旋转角度
|
||||
* @return 旋转角度(度数)
|
||||
*/
|
||||
float getRotation() const { return rotation_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 缩放属性
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置缩放
|
||||
* @param scale 缩放向量
|
||||
*/
|
||||
void setScale(const Vec2& scale);
|
||||
|
||||
/**
|
||||
* @brief 设置统一缩放
|
||||
* @param scale 统一缩放值
|
||||
*/
|
||||
void setScale(float scale) { setScale(Vec2(scale, scale)); }
|
||||
|
||||
/**
|
||||
* @brief 设置缩放
|
||||
* @param x X轴缩放值
|
||||
* @param y Y轴缩放值
|
||||
*/
|
||||
void setScale(float x, float y) { setScale(Vec2(x, y)); }
|
||||
|
||||
/**
|
||||
* @brief 获取缩放
|
||||
* @return 缩放向量
|
||||
*/
|
||||
Vec2 getScale() const { return scale_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 锚点属性
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置锚点
|
||||
* @param anchor 锚点位置(0-1范围)
|
||||
*/
|
||||
void setAnchor(const Vec2& anchor);
|
||||
|
||||
/**
|
||||
* @brief 设置锚点
|
||||
* @param x 锚点X坐标(0-1范围)
|
||||
* @param y 锚点Y坐标(0-1范围)
|
||||
*/
|
||||
void setAnchor(float x, float y) { setAnchor(Vec2(x, y)); }
|
||||
|
||||
/**
|
||||
* @brief 获取锚点
|
||||
* @return 锚点位置
|
||||
*/
|
||||
Vec2 getAnchor() const { return anchor_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 斜切属性
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置斜切
|
||||
* @param skew 斜切角度向量
|
||||
*/
|
||||
void setSkew(const Vec2& skew);
|
||||
|
||||
/**
|
||||
* @brief 设置斜切
|
||||
* @param x X轴斜切角度
|
||||
* @param y Y轴斜切角度
|
||||
*/
|
||||
void setSkew(float x, float y) { setSkew(Vec2(x, y)); }
|
||||
|
||||
/**
|
||||
* @brief 获取斜切
|
||||
* @return 斜切角度向量
|
||||
*/
|
||||
Vec2 getSkew() const { return skew_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 变换矩阵
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取本地变换矩阵
|
||||
* @return 本地变换矩阵
|
||||
*/
|
||||
glm::mat4 getLocalTransform() const;
|
||||
|
||||
/**
|
||||
* @brief 获取世界变换矩阵
|
||||
* @return 世界变换矩阵
|
||||
*/
|
||||
glm::mat4 getWorldTransform() const;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 坐标转换
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 将本地坐标转换为世界坐标
|
||||
* @param localPos 本地坐标位置
|
||||
* @return 世界坐标位置
|
||||
*/
|
||||
Vec2 localToWorld(const Vec2& localPos) const;
|
||||
|
||||
/**
|
||||
* @brief 将世界坐标转换为本地坐标
|
||||
* @param worldPos 世界坐标位置
|
||||
* @return 本地坐标位置
|
||||
*/
|
||||
Vec2 worldToLocal(const Vec2& worldPos) const;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 脏标记
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 标记变换为脏
|
||||
*
|
||||
* 标记本地变换和世界变换需要重新计算
|
||||
*/
|
||||
void markTransformDirty();
|
||||
|
||||
/**
|
||||
* @brief 标记世界变换为脏
|
||||
*
|
||||
* 仅标记世界变换需要重新计算
|
||||
*/
|
||||
void markWorldTransformDirty();
|
||||
|
||||
/**
|
||||
* @brief 检查本地变换是否脏
|
||||
* @return 如果本地变换需要重新计算返回true
|
||||
*/
|
||||
bool isLocalTransformDirty() const { return localDirty_; }
|
||||
|
||||
/**
|
||||
* @brief 检查世界变换是否脏
|
||||
* @return 如果世界变换需要重新计算返回true
|
||||
*/
|
||||
bool isWorldTransformDirty() const { return worldDirty_; }
|
||||
|
||||
/**
|
||||
* @brief 批量更新变换
|
||||
*
|
||||
* 从父节点到子节点依次更新世界变换矩阵
|
||||
*/
|
||||
void batchTransforms();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 计算本地变换矩阵
|
||||
*/
|
||||
void computeLocalTransform() const;
|
||||
|
||||
Vec2 position_ = Vec2::Zero();
|
||||
Vec2 scale_ = Vec2::One();
|
||||
Vec2 anchor_ = Vec2(0.5f, 0.5f);
|
||||
Vec2 skew_ = Vec2::Zero();
|
||||
float rotation_ = 0.0f;
|
||||
|
||||
mutable glm::mat4 localTransform_ = glm::mat4(1.0f);
|
||||
mutable glm::mat4 worldTransform_ = glm::mat4(1.0f);
|
||||
mutable bool localDirty_ = true;
|
||||
mutable bool worldDirty_ = true;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -5,235 +5,614 @@
|
|||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/event/event_dispatcher.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/scene/component.h>
|
||||
#include <extra2d/scene/components/transform_component.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Scene;
|
||||
class RenderBackend;
|
||||
struct RenderCommand;
|
||||
class RenderQueue;
|
||||
|
||||
// ============================================================================
|
||||
// 节点基类 - 场景图的基础
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 节点基类 - 场景图的基础
|
||||
*
|
||||
* 节点是场景图的基本单元,支持层级管理、变换继承和组件系统
|
||||
*/
|
||||
class Node : public std::enable_shared_from_this<Node> {
|
||||
public:
|
||||
Node();
|
||||
virtual ~Node();
|
||||
/**
|
||||
* @brief 创建节点
|
||||
* @param name 节点名称
|
||||
* @return 创建的节点智能指针
|
||||
*/
|
||||
static Ptr<Node> create(const std::string& name = "");
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 层级管理
|
||||
// ------------------------------------------------------------------------
|
||||
void addChild(Ptr<Node> child);
|
||||
Node();
|
||||
virtual ~Node();
|
||||
|
||||
/**
|
||||
* @brief 批量添加子节点
|
||||
* @param children 子节点列表
|
||||
*/
|
||||
void addChildren(std::vector<Ptr<Node>> &&children);
|
||||
// -------------------------------------------------------------------------
|
||||
// 层级管理
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void removeChild(Ptr<Node> child);
|
||||
void removeChildByName(const std::string &name);
|
||||
void detach();
|
||||
void clearChildren();
|
||||
/**
|
||||
* @brief 添加子节点
|
||||
* @param child 要添加的子节点智能指针
|
||||
*/
|
||||
void addChild(Ptr<Node> child);
|
||||
|
||||
Ptr<Node> getParent() const { return parent_.lock(); }
|
||||
const std::vector<Ptr<Node>> &getChildren() const { return children_; }
|
||||
Ptr<Node> findChild(const std::string &name) const;
|
||||
Ptr<Node> findChildByTag(int tag) const;
|
||||
/**
|
||||
* @brief 批量添加子节点
|
||||
* @param children 子节点列表
|
||||
*/
|
||||
void addChildren(std::vector<Ptr<Node>>&& children);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 变换属性
|
||||
// ------------------------------------------------------------------------
|
||||
void setPos(const Vec2 &pos);
|
||||
void setPos(float x, float y);
|
||||
Vec2 getPosition() const { return position_; }
|
||||
/**
|
||||
* @brief 移除子节点
|
||||
* @param child 要移除的子节点智能指针
|
||||
*/
|
||||
void removeChild(Ptr<Node> child);
|
||||
|
||||
void setRotation(float degrees);
|
||||
float getRotation() const { return rotation_; }
|
||||
/**
|
||||
* @brief 通过名称移除子节点
|
||||
* @param name 子节点的名称
|
||||
*/
|
||||
void removeChildByName(const std::string& name);
|
||||
|
||||
void setScale(const Vec2 &scale);
|
||||
void setScale(float scale);
|
||||
void setScale(float x, float y);
|
||||
Vec2 getScale() const { return scale_; }
|
||||
/**
|
||||
* @brief 从父节点分离
|
||||
*/
|
||||
void detach();
|
||||
|
||||
void setAnchor(const Vec2 &anchor);
|
||||
void setAnchor(float x, float y);
|
||||
Vec2 getAnchor() const { return anchor_; }
|
||||
/**
|
||||
* @brief 清除所有子节点
|
||||
*/
|
||||
void clearChildren();
|
||||
|
||||
void setSkew(const Vec2 &skew);
|
||||
void setSkew(float x, float y);
|
||||
Vec2 getSkew() const { return skew_; }
|
||||
/**
|
||||
* @brief 获取父节点
|
||||
* @return 父节点智能指针
|
||||
*/
|
||||
Ptr<Node> getParent() const { return parent_.lock(); }
|
||||
|
||||
void setOpacity(float opacity);
|
||||
float getOpacity() const { return opacity_; }
|
||||
/**
|
||||
* @brief 获取所有子节点
|
||||
* @return 子节点列表的常量引用
|
||||
*/
|
||||
const std::vector<Ptr<Node>>& getChildren() const { return children_; }
|
||||
|
||||
void setVisible(bool visible);
|
||||
bool isVisible() const { return visible_; }
|
||||
/**
|
||||
* @brief 通过名称查找子节点
|
||||
* @param name 子节点的名称
|
||||
* @return 找到的子节点智能指针,未找到返回nullptr
|
||||
*/
|
||||
Ptr<Node> findChild(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 设置颜色
|
||||
* @param color RGB颜色
|
||||
*/
|
||||
void setColor(const Color3B &color);
|
||||
Color3B getColor() const { return color_; }
|
||||
/**
|
||||
* @brief 通过标签查找子节点
|
||||
* @param tag 子节点的标签值
|
||||
* @return 找到的子节点智能指针,未找到返回nullptr
|
||||
*/
|
||||
Ptr<Node> findChildByTag(int tag) const;
|
||||
|
||||
/**
|
||||
* @brief 设置X轴翻转
|
||||
*/
|
||||
void setFlipX(bool flipX);
|
||||
bool isFlipX() const { return flipX_; }
|
||||
// -------------------------------------------------------------------------
|
||||
// 组件系统
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置Y轴翻转
|
||||
*/
|
||||
void setFlipY(bool flipY);
|
||||
bool isFlipY() const { return flipY_; }
|
||||
/**
|
||||
* @brief 添加组件
|
||||
* @tparam T 组件类型
|
||||
* @tparam Args 构造函数参数类型
|
||||
* @param args 构造函数参数
|
||||
* @return 创建的组件指针
|
||||
*/
|
||||
template<typename T, typename... Args>
|
||||
T* addComponent(Args&&... args) {
|
||||
static_assert(std::is_base_of<Component, T>::value,
|
||||
"T must derive from Component");
|
||||
|
||||
uint32_t typeId = T::getStaticTypeId();
|
||||
|
||||
// 检查是否已存在相同类型的组件
|
||||
auto it = components_.find(typeId);
|
||||
if (it != components_.end()) {
|
||||
return static_cast<T*>(it->second.get());
|
||||
}
|
||||
|
||||
void setZOrder(int zOrder);
|
||||
int getZOrder() const { return zOrder_; }
|
||||
// 创建新组件
|
||||
auto component = makePtr<T>(std::forward<Args>(args)...);
|
||||
component->setNode(this);
|
||||
component->init();
|
||||
component->setInitialized(true);
|
||||
|
||||
components_[typeId] = component;
|
||||
|
||||
return component.get();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 世界变换
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 toWorld(const Vec2 &localPos) const;
|
||||
Vec2 toLocal(const Vec2 &worldPos) const;
|
||||
/**
|
||||
* @brief 获取组件
|
||||
* @tparam T 组件类型
|
||||
* @return 组件指针,不存在返回nullptr
|
||||
*/
|
||||
template<typename T>
|
||||
T* getComponent() const {
|
||||
static_assert(std::is_base_of<Component, T>::value,
|
||||
"T must derive from Component");
|
||||
|
||||
uint32_t typeId = T::getStaticTypeId();
|
||||
auto it = components_.find(typeId);
|
||||
if (it != components_.end()) {
|
||||
return static_cast<T*>(it->second.get());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glm::mat4 getLocalTransform() const;
|
||||
glm::mat4 getWorldTransform() const;
|
||||
/**
|
||||
* @brief 检查是否有组件
|
||||
* @tparam T 组件类型
|
||||
* @return 如果存在返回true
|
||||
*/
|
||||
template<typename T>
|
||||
bool hasComponent() const {
|
||||
return getComponent<T>() != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 标记变换矩阵为脏状态,并传播到所有子节点
|
||||
*/
|
||||
void markTransformDirty();
|
||||
/**
|
||||
* @brief 移除组件
|
||||
* @tparam T 组件类型
|
||||
*/
|
||||
template<typename T>
|
||||
void removeComponent() {
|
||||
static_assert(std::is_base_of<Component, T>::value,
|
||||
"T must derive from Component");
|
||||
|
||||
uint32_t typeId = T::getStaticTypeId();
|
||||
auto it = components_.find(typeId);
|
||||
if (it != components_.end()) {
|
||||
it->second->destroy();
|
||||
it->second->setInitialized(false);
|
||||
it->second->setNode(nullptr);
|
||||
components_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 批量更新变换矩阵
|
||||
* 在渲染前统一计算所有脏节点的变换矩阵,避免逐节点计算时的重复递归
|
||||
*/
|
||||
void batchTransforms();
|
||||
/**
|
||||
* @brief 获取变换组件
|
||||
* @return 变换组件指针
|
||||
*/
|
||||
TransformComponent* getTransform() const { return transform_; }
|
||||
|
||||
/**
|
||||
* @brief 获取变换脏标记状态
|
||||
*/
|
||||
bool isTransformDirty() const { return transformDirty_; }
|
||||
bool isWorldTransformDirty() const { return worldTransformDirty_; }
|
||||
// -------------------------------------------------------------------------
|
||||
// 变换属性(便捷方法,委托给TransformComponent)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 名称和标签
|
||||
// ------------------------------------------------------------------------
|
||||
void setName(const std::string &name) { name_ = name; }
|
||||
const std::string &getName() const { return name_; }
|
||||
/**
|
||||
* @brief 设置节点位置
|
||||
* @param pos 新的位置坐标
|
||||
*/
|
||||
void setPos(const Vec2& pos);
|
||||
|
||||
void setTag(int tag) { tag_ = tag; }
|
||||
int getTag() const { return tag_; }
|
||||
/**
|
||||
* @brief 设置节点位置
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
*/
|
||||
void setPos(float x, float y);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 生命周期回调
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void onEnter();
|
||||
virtual void onExit();
|
||||
virtual void onUpdate(float dt);
|
||||
virtual void onRender(RenderBackend &renderer);
|
||||
virtual void onAttachToScene(Scene *scene);
|
||||
virtual void onDetachFromScene();
|
||||
/**
|
||||
* @brief 获取节点位置
|
||||
* @return 位置坐标
|
||||
*/
|
||||
Vec2 getPosition() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 边界框
|
||||
// ------------------------------------------------------------------------
|
||||
virtual Rect getBounds() const;
|
||||
/**
|
||||
* @brief 设置节点旋转角度
|
||||
* @param degrees 旋转角度(度数)
|
||||
*/
|
||||
void setRotation(float degrees);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 事件系统
|
||||
// ------------------------------------------------------------------------
|
||||
EventDispatcher &getEventDispatcher() { return eventDispatcher_; }
|
||||
/**
|
||||
* @brief 获取节点旋转角度
|
||||
* @return 旋转角度(度数)
|
||||
*/
|
||||
float getRotation() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内部方法
|
||||
// ------------------------------------------------------------------------
|
||||
void update(float dt);
|
||||
void render(RenderBackend &renderer);
|
||||
void sortChildren();
|
||||
/**
|
||||
* @brief 设置节点缩放
|
||||
* @param scale 缩放向量
|
||||
*/
|
||||
void setScale(const Vec2& scale);
|
||||
|
||||
bool isRunning() const { return running_; }
|
||||
Scene *getScene() const { return scene_; }
|
||||
/**
|
||||
* @brief 设置节点统一缩放
|
||||
* @param scale 统一缩放值
|
||||
*/
|
||||
void setScale(float scale);
|
||||
|
||||
// 多线程渲染命令收集
|
||||
virtual void collectRenderCommands(std::vector<RenderCommand> &commands,
|
||||
int parentZOrder = 0);
|
||||
/**
|
||||
* @brief 设置节点缩放
|
||||
* @param x X轴缩放值
|
||||
* @param y Y轴缩放值
|
||||
*/
|
||||
void setScale(float x, float y);
|
||||
|
||||
/**
|
||||
* @brief 获取节点缩放
|
||||
* @return 缩放向量
|
||||
*/
|
||||
Vec2 getScale() const;
|
||||
|
||||
/**
|
||||
* @brief 设置节点锚点
|
||||
* @param anchor 锚点位置(0-1范围)
|
||||
*/
|
||||
void setAnchor(const Vec2& anchor);
|
||||
|
||||
/**
|
||||
* @brief 设置节点锚点
|
||||
* @param x 锚点X坐标(0-1范围)
|
||||
* @param y 锚点Y坐标(0-1范围)
|
||||
*/
|
||||
void setAnchor(float x, float y);
|
||||
|
||||
/**
|
||||
* @brief 获取节点锚点
|
||||
* @return 锚点位置
|
||||
*/
|
||||
Vec2 getAnchor() const;
|
||||
|
||||
/**
|
||||
* @brief 设置节点斜切
|
||||
* @param skew 斜切角度向量
|
||||
*/
|
||||
void setSkew(const Vec2& skew);
|
||||
|
||||
/**
|
||||
* @brief 设置节点斜切
|
||||
* @param x X轴斜切角度
|
||||
* @param y Y轴斜切角度
|
||||
*/
|
||||
void setSkew(float x, float y);
|
||||
|
||||
/**
|
||||
* @brief 获取节点斜切
|
||||
* @return 斜切角度向量
|
||||
*/
|
||||
Vec2 getSkew() const;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 世界变换
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 将本地坐标转换为世界坐标
|
||||
* @param localPos 本地坐标位置
|
||||
* @return 世界坐标位置
|
||||
*/
|
||||
Vec2 toWorld(const Vec2& localPos) const;
|
||||
|
||||
/**
|
||||
* @brief 将世界坐标转换为本地坐标
|
||||
* @param worldPos 世界坐标位置
|
||||
* @return 本地坐标位置
|
||||
*/
|
||||
Vec2 toLocal(const Vec2& worldPos) const;
|
||||
|
||||
/**
|
||||
* @brief 获取本地变换矩阵
|
||||
* @return 本地变换矩阵
|
||||
*/
|
||||
glm::mat4 getLocalTransform() const;
|
||||
|
||||
/**
|
||||
* @brief 获取世界变换矩阵
|
||||
* @return 世界变换矩阵
|
||||
*/
|
||||
glm::mat4 getWorldTransform() const;
|
||||
|
||||
/**
|
||||
* @brief 标记变换矩阵为脏状态,并传播到所有子节点
|
||||
*/
|
||||
void markTransformDirty();
|
||||
|
||||
/**
|
||||
* @brief 批量更新变换矩阵
|
||||
*/
|
||||
void batchTransforms();
|
||||
|
||||
/**
|
||||
* @brief 获取变换脏标记状态
|
||||
*/
|
||||
bool isTransformDirty() const;
|
||||
bool isWorldTransformDirty() const;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 可见性和外观
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置透明度
|
||||
* @param opacity 透明度值(0.0-1.0范围)
|
||||
*/
|
||||
void setOpacity(float opacity);
|
||||
|
||||
/**
|
||||
* @brief 获取透明度
|
||||
* @return 透明度值
|
||||
*/
|
||||
float getOpacity() const { return opacity_; }
|
||||
|
||||
/**
|
||||
* @brief 设置可见性
|
||||
* @param visible 是否可见
|
||||
*/
|
||||
void setVisible(bool visible);
|
||||
|
||||
/**
|
||||
* @brief 获取可见性
|
||||
* @return 是否可见
|
||||
*/
|
||||
bool isVisible() const { return visible_; }
|
||||
|
||||
/**
|
||||
* @brief 设置颜色
|
||||
* @param color RGB颜色
|
||||
*/
|
||||
void setColor(const Color3B& color);
|
||||
|
||||
/**
|
||||
* @brief 获取颜色
|
||||
* @return RGB颜色
|
||||
*/
|
||||
Color3B getColor() const { return color_; }
|
||||
|
||||
/**
|
||||
* @brief 设置X轴翻转
|
||||
*/
|
||||
void setFlipX(bool flipX);
|
||||
|
||||
/**
|
||||
* @brief 获取X轴翻转状态
|
||||
*/
|
||||
bool isFlipX() const { return flipX_; }
|
||||
|
||||
/**
|
||||
* @brief 设置Y轴翻转
|
||||
*/
|
||||
void setFlipY(bool flipY);
|
||||
|
||||
/**
|
||||
* @brief 获取Y轴翻转状态
|
||||
*/
|
||||
bool isFlipY() const { return flipY_; }
|
||||
|
||||
/**
|
||||
* @brief 设置Z序
|
||||
* @param zOrder 渲染层级顺序
|
||||
*/
|
||||
void setZOrder(int zOrder);
|
||||
|
||||
/**
|
||||
* @brief 获取Z序
|
||||
* @return 渲染层级顺序
|
||||
*/
|
||||
int getZOrder() const { return zOrder_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 名称和标签
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置名称
|
||||
* @param name 节点名称
|
||||
*/
|
||||
void setName(const std::string& name) { name_ = name; }
|
||||
|
||||
/**
|
||||
* @brief 获取名称
|
||||
* @return 节点名称
|
||||
*/
|
||||
const std::string& getName() const { return name_; }
|
||||
|
||||
/**
|
||||
* @brief 设置标签
|
||||
* @param tag 标签值
|
||||
*/
|
||||
void setTag(int tag) { tag_ = tag; }
|
||||
|
||||
/**
|
||||
* @brief 获取标签
|
||||
* @return 标签值
|
||||
*/
|
||||
int getTag() const { return tag_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 生命周期回调
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 节点进入时的回调
|
||||
*/
|
||||
virtual void onEnter();
|
||||
|
||||
/**
|
||||
* @brief 节点退出时的回调
|
||||
*/
|
||||
virtual void onExit();
|
||||
|
||||
/**
|
||||
* @brief 更新回调
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*/
|
||||
virtual void onUpdate(float dt);
|
||||
|
||||
/**
|
||||
* @brief 渲染回调
|
||||
* @param renderer 渲染后端引用
|
||||
*/
|
||||
virtual void onRender(RenderBackend& renderer);
|
||||
|
||||
/**
|
||||
* @brief 附加到场景时的回调
|
||||
* @param scene 所属场景指针
|
||||
*/
|
||||
virtual void onAttachToScene(Scene* scene);
|
||||
|
||||
/**
|
||||
* @brief 从场景分离时的回调
|
||||
*/
|
||||
virtual void onDetachFromScene();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 边界框
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取节点边界矩形
|
||||
* @return 节点的边界矩形
|
||||
*/
|
||||
virtual Rect getBounds() const;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 事件系统
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取事件分发器
|
||||
* @return 事件分发器引用
|
||||
*/
|
||||
EventDispatcher& getEventDispatcher() { return eventDispatcher_; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 内部方法
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 更新节点
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*/
|
||||
void update(float dt);
|
||||
|
||||
/**
|
||||
* @brief 渲染节点
|
||||
* @param renderer 渲染后端引用
|
||||
*/
|
||||
void render(RenderBackend& renderer);
|
||||
|
||||
/**
|
||||
* @brief 对子节点排序
|
||||
*/
|
||||
void sortChildren();
|
||||
|
||||
/**
|
||||
* @brief 检查是否正在运行
|
||||
* @return 是否正在运行
|
||||
*/
|
||||
bool isRunning() const { return running_; }
|
||||
|
||||
/**
|
||||
* @brief 获取所属场景
|
||||
* @return 所属场景指针
|
||||
*/
|
||||
Scene* getScene() const { return scene_; }
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令
|
||||
* @param commands 渲染命令输出向量
|
||||
* @param parentZOrder 父节点的Z序
|
||||
*/
|
||||
virtual void collectRenderCommands(std::vector<RenderCommand>& commands,
|
||||
int parentZOrder = 0);
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令到队列
|
||||
* @param queue 渲染队列引用
|
||||
*/
|
||||
virtual void collectRenderCommandsToQueue(RenderQueue& queue);
|
||||
|
||||
protected:
|
||||
// 子类重写
|
||||
virtual void onDraw(RenderBackend &renderer) {}
|
||||
virtual void onUpdateNode(float dt) {}
|
||||
virtual void generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||
int zOrder) {};
|
||||
/**
|
||||
* @brief 绘制回调(子类重写)
|
||||
* @param renderer 渲染后端引用
|
||||
*/
|
||||
virtual void onDraw(RenderBackend& renderer) {}
|
||||
|
||||
// 供子类访问的内部状态
|
||||
Vec2 &getPositionRef() { return position_; }
|
||||
Vec2 &getScaleRef() { return scale_; }
|
||||
Vec2 &getAnchorRef() { return anchor_; }
|
||||
float getRotationRef() { return rotation_; }
|
||||
float getOpacityRef() { return opacity_; }
|
||||
/**
|
||||
* @brief 节点更新回调(子类重写)
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*/
|
||||
virtual void onUpdateNode(float dt) {}
|
||||
|
||||
/**
|
||||
* @brief 生成渲染命令(子类重写)
|
||||
* @param commands 渲染命令输出向量
|
||||
* @param zOrder Z序
|
||||
*/
|
||||
virtual void generateRenderCommand(std::vector<RenderCommand>& commands,
|
||||
int zOrder) {}
|
||||
|
||||
/**
|
||||
* @brief 生成渲染命令到队列(子类重写)
|
||||
* @param queue 渲染队列引用
|
||||
*/
|
||||
virtual void generateRenderCommandToQueue(RenderQueue& queue) { (void)queue; }
|
||||
|
||||
private:
|
||||
// ==========================================================================
|
||||
// 成员变量按类型大小降序排列,减少内存对齐填充
|
||||
// 64位系统对齐:std::string(32) > glm::mat4(64) > std::vector(24) >
|
||||
// double(8) > float(4) > int(4) > bool(1)
|
||||
// ==========================================================================
|
||||
// ==========================================================================
|
||||
// 成员变量按类型大小降序排列,减少内存对齐填充
|
||||
// ==========================================================================
|
||||
|
||||
// 1. 大块内存(64字节)
|
||||
mutable glm::mat4 localTransform_; // 64 bytes
|
||||
mutable glm::mat4 worldTransform_; // 64 bytes
|
||||
// 1. 大块内存(64字节)
|
||||
mutable glm::mat4 localTransform_ = glm::mat4(1.0f);
|
||||
mutable glm::mat4 worldTransform_ = glm::mat4(1.0f);
|
||||
|
||||
// 2. 字符串和容器(24-32字节)
|
||||
std::string name_; // 32 bytes
|
||||
std::vector<Ptr<Node>> children_; // 24 bytes
|
||||
// 2. 字符串和容器(24-32字节)
|
||||
std::string name_;
|
||||
std::vector<Ptr<Node>> children_;
|
||||
std::unordered_map<uint32_t, Ptr<Component>> components_;
|
||||
|
||||
// 3. 子节点索引(加速查找)
|
||||
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
|
||||
std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
|
||||
// 3. 子节点索引(加速查找)
|
||||
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_;
|
||||
std::unordered_map<int, WeakPtr<Node>> tagIndex_;
|
||||
|
||||
// 4. 事件分发器
|
||||
EventDispatcher eventDispatcher_; // 大小取决于实现
|
||||
// 4. 事件分发器
|
||||
EventDispatcher eventDispatcher_;
|
||||
|
||||
// 5. 父节点引用
|
||||
WeakPtr<Node> parent_; // 16 bytes
|
||||
// 5. 父节点引用
|
||||
WeakPtr<Node> parent_;
|
||||
|
||||
// 7. 变换属性(按访问频率分组)
|
||||
Vec2 position_ = Vec2::Zero(); // 8 bytes
|
||||
Vec2 scale_ = Vec2(1.0f, 1.0f); // 8 bytes
|
||||
Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes
|
||||
Vec2 skew_ = Vec2::Zero(); // 8 bytes
|
||||
// 6. 变换组件(内置)
|
||||
TransformComponent* transform_ = nullptr;
|
||||
|
||||
// 8. 浮点属性
|
||||
float rotation_ = 0.0f; // 4 bytes
|
||||
float opacity_ = 1.0f; // 4 bytes
|
||||
// 7. 浮点属性
|
||||
float opacity_ = 1.0f;
|
||||
|
||||
// 10. 颜色属性
|
||||
Color3B color_ = Color3B(255, 255, 255); // 3 bytes
|
||||
// 8. 颜色属性
|
||||
Color3B color_ = Color3B(255, 255, 255);
|
||||
|
||||
// 11. 整数属性
|
||||
int zOrder_ = 0; // 4 bytes
|
||||
int tag_ = -1; // 4 bytes
|
||||
// 9. 整数属性
|
||||
int zOrder_ = 0;
|
||||
int tag_ = -1;
|
||||
|
||||
// 12. 布尔属性
|
||||
bool flipX_ = false; // 1 byte
|
||||
bool flipY_ = false; // 1 byte
|
||||
// 10. 布尔属性
|
||||
bool flipX_ = false;
|
||||
bool flipY_ = false;
|
||||
|
||||
// 13. 场景指针
|
||||
Scene *scene_ = nullptr; // 8 bytes
|
||||
// 11. 场景指针
|
||||
Scene* scene_ = nullptr;
|
||||
|
||||
// 14. 布尔标志(打包在一起)
|
||||
mutable bool transformDirty_ = true; // 1 byte
|
||||
mutable bool worldTransformDirty_ = true; // 1 byte
|
||||
bool childrenOrderDirty_ = false; // 1 byte
|
||||
bool visible_ = true; // 1 byte
|
||||
bool running_ = false; // 1 byte
|
||||
// 12. 布尔标志(打包在一起)
|
||||
mutable bool transformDirty_ = true;
|
||||
mutable bool worldTransformDirty_ = true;
|
||||
bool childrenOrderDirty_ = false;
|
||||
bool visible_ = true;
|
||||
bool running_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -7,83 +7,191 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
struct RenderCommand;
|
||||
class RenderQueue;
|
||||
|
||||
// ============================================================================
|
||||
// 场景类 - 节点容器,管理整个场景图
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 场景类 - 节点容器,管理整个场景图
|
||||
*
|
||||
* 场景是场景图的根节点,管理相机、视口和渲染流程
|
||||
*/
|
||||
class Scene : public Node {
|
||||
public:
|
||||
Scene();
|
||||
~Scene() override = default;
|
||||
Scene();
|
||||
~Scene() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 场景属性
|
||||
// ------------------------------------------------------------------------
|
||||
void setBackgroundColor(const Color &color) { backgroundColor_ = color; }
|
||||
Color getBackgroundColor() const { return backgroundColor_; }
|
||||
// -------------------------------------------------------------------------
|
||||
// 场景属性
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 摄像机
|
||||
// ------------------------------------------------------------------------
|
||||
void setCamera(Ptr<Camera> camera);
|
||||
Ptr<Camera> getCamera() const { return camera_; }
|
||||
/**
|
||||
* @brief 设置背景颜色
|
||||
* @param color 背景颜色
|
||||
*/
|
||||
void setBackgroundColor(const Color& color) { backgroundColor_ = color; }
|
||||
|
||||
Camera *getActiveCamera() const {
|
||||
return camera_ ? camera_.get() : defaultCamera_.get();
|
||||
}
|
||||
/**
|
||||
* @brief 获取背景颜色
|
||||
* @return 背景颜色
|
||||
*/
|
||||
Color getBackgroundColor() const { return backgroundColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 视口和尺寸
|
||||
// ------------------------------------------------------------------------
|
||||
void setViewportSize(float width, float height);
|
||||
void setViewportSize(const Size &size);
|
||||
Size getViewportSize() const { return viewportSize_; }
|
||||
// -------------------------------------------------------------------------
|
||||
// 摄像机
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
float getWidth() const { return viewportSize_.width; }
|
||||
float getHeight() const { return viewportSize_.height; }
|
||||
/**
|
||||
* @brief 设置场景相机
|
||||
* @param camera 相机智能指针
|
||||
*/
|
||||
void setCamera(Ptr<Camera> camera);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 场景状态
|
||||
// ------------------------------------------------------------------------
|
||||
bool isPaused() const { return paused_; }
|
||||
void pause() { paused_ = true; }
|
||||
void resume() { paused_ = false; }
|
||||
/**
|
||||
* @brief 获取场景相机
|
||||
* @return 相机智能指针
|
||||
*/
|
||||
Ptr<Camera> getCamera() const { return camera_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 渲染和更新
|
||||
// ------------------------------------------------------------------------
|
||||
void renderScene(RenderBackend &renderer);
|
||||
virtual void renderContent(RenderBackend &renderer);
|
||||
void updateScene(float dt);
|
||||
void collectRenderCommands(std::vector<RenderCommand> &commands,
|
||||
int parentZOrder = 0) override;
|
||||
/**
|
||||
* @brief 获取活动相机
|
||||
* @return 活动相机指针(优先返回设置的相机,否则返回默认相机)
|
||||
*/
|
||||
Camera* getActiveCamera() const {
|
||||
return camera_ ? camera_.get() : defaultCamera_.get();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Scene> create();
|
||||
// -------------------------------------------------------------------------
|
||||
// 视口和尺寸
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置视口大小
|
||||
* @param width 视口宽度
|
||||
* @param height 视口高度
|
||||
*/
|
||||
void setViewportSize(float width, float height);
|
||||
|
||||
/**
|
||||
* @brief 设置视口大小
|
||||
* @param size 视口尺寸结构体
|
||||
*/
|
||||
void setViewportSize(const Size& size);
|
||||
|
||||
/**
|
||||
* @brief 获取视口大小
|
||||
* @return 视口尺寸
|
||||
*/
|
||||
Size getViewportSize() const { return viewportSize_; }
|
||||
|
||||
/**
|
||||
* @brief 获取视口宽度
|
||||
* @return 视口宽度
|
||||
*/
|
||||
float getWidth() const { return viewportSize_.width; }
|
||||
|
||||
/**
|
||||
* @brief 获取视口高度
|
||||
* @return 视口高度
|
||||
*/
|
||||
float getHeight() const { return viewportSize_.height; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 场景状态
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 检查场景是否暂停
|
||||
* @return 如果场景暂停返回true
|
||||
*/
|
||||
bool isPaused() const { return paused_; }
|
||||
|
||||
/**
|
||||
* @brief 暂停场景
|
||||
*/
|
||||
void pause() { paused_ = true; }
|
||||
|
||||
/**
|
||||
* @brief 恢复场景
|
||||
*/
|
||||
void resume() { paused_ = false; }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 渲染和更新
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 渲染场景
|
||||
* @param renderer 渲染后端引用
|
||||
*/
|
||||
void renderScene(RenderBackend& renderer);
|
||||
|
||||
/**
|
||||
* @brief 渲染场景内容
|
||||
* @param renderer 渲染后端引用
|
||||
*/
|
||||
virtual void renderContent(RenderBackend& renderer);
|
||||
|
||||
/**
|
||||
* @brief 更新场景
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*/
|
||||
void updateScene(float dt);
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令
|
||||
* @param commands 渲染命令输出向量
|
||||
* @param parentZOrder 父节点的Z序
|
||||
*/
|
||||
void collectRenderCommands(std::vector<RenderCommand>& commands,
|
||||
int parentZOrder = 0) override;
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令到队列
|
||||
* @param queue 渲染队列引用
|
||||
*/
|
||||
void collectRenderCommandsToQueue(RenderQueue& queue) override;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 创建场景对象
|
||||
* @return 新创建的场景智能指针
|
||||
*/
|
||||
static Ptr<Scene> create();
|
||||
|
||||
protected:
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
/**
|
||||
* @brief 场景进入时的回调
|
||||
*/
|
||||
void onEnter() override;
|
||||
|
||||
// 过渡场景生命周期回调(供 TransitionScene 使用)
|
||||
virtual void onExitTransitionDidStart() {}
|
||||
virtual void onEnterTransitionDidFinish() {}
|
||||
/**
|
||||
* @brief 场景退出时的回调
|
||||
*/
|
||||
void onExit() override;
|
||||
|
||||
friend class SceneManager;
|
||||
friend class TransitionScene;
|
||||
/**
|
||||
* @brief 过渡场景开始时的回调(供 TransitionScene 使用)
|
||||
*/
|
||||
virtual void onExitTransitionDidStart() {}
|
||||
|
||||
/**
|
||||
* @brief 过渡场景结束时的回调(供 TransitionScene 使用)
|
||||
*/
|
||||
virtual void onEnterTransitionDidFinish() {}
|
||||
|
||||
friend class SceneManager;
|
||||
friend class TransitionScene;
|
||||
|
||||
private:
|
||||
Color backgroundColor_ = Colors::Black;
|
||||
Size viewportSize_ = Size::Zero();
|
||||
Color backgroundColor_ = Colors::Black;
|
||||
Size viewportSize_ = Size::Zero();
|
||||
|
||||
Ptr<Camera> camera_;
|
||||
Ptr<Camera> defaultCamera_;
|
||||
Ptr<Camera> camera_;
|
||||
Ptr<Camera> defaultCamera_;
|
||||
|
||||
bool paused_ = false;
|
||||
bool paused_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,141 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace graphics {
|
||||
|
||||
std::unordered_map<std::string, BackendFactory::BackendEntry> &
|
||||
BackendFactory::registry() {
|
||||
static std::unordered_map<std::string, BackendEntry> reg;
|
||||
return reg;
|
||||
}
|
||||
|
||||
void BackendFactory::reg(const std::string &name, BackendFn backend,
|
||||
const std::vector<std::string> &windowBackends) {
|
||||
registry()[name] = {backend, windowBackends};
|
||||
E2D_LOG_DEBUG("已注册图形后端: {} (窗口后端数量: {})", name,
|
||||
windowBackends.size());
|
||||
}
|
||||
|
||||
UniquePtr<RenderBackend>
|
||||
BackendFactory::createBackend(const std::string &name) {
|
||||
auto ® = registry();
|
||||
auto it = reg.find(name);
|
||||
if (it != reg.end() && it->second.createFn) {
|
||||
E2D_LOG_INFO("正在创建图形后端: {}", name);
|
||||
return it->second.createFn();
|
||||
}
|
||||
E2D_LOG_ERROR("未找到图形后端 '{}'", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UniquePtr<RenderBackend> BackendFactory::createDefaultBackend() {
|
||||
std::string recommended = getRecommendedBackend();
|
||||
if (recommended.empty()) {
|
||||
E2D_LOG_ERROR("无可用的图形后端");
|
||||
return nullptr;
|
||||
}
|
||||
return createBackend(recommended);
|
||||
}
|
||||
|
||||
UniquePtr<RenderBackend>
|
||||
BackendFactory::createBackendForWindow(const std::string &windowBackend) {
|
||||
std::string recommended = getRecommendedBackendForWindow(windowBackend);
|
||||
if (recommended.empty()) {
|
||||
E2D_LOG_ERROR("未找到与窗口后端 '{}' 兼容的图形后端", windowBackend);
|
||||
return nullptr;
|
||||
}
|
||||
return createBackend(recommended);
|
||||
}
|
||||
|
||||
std::vector<std::string> BackendFactory::backends() {
|
||||
std::vector<std::string> result;
|
||||
for (const auto &pair : registry()) {
|
||||
result.push_back(pair.first);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BackendFactory::has(const std::string &name) {
|
||||
return registry().find(name) != registry().end();
|
||||
}
|
||||
|
||||
std::string BackendFactory::getRecommendedBackend() {
|
||||
auto ® = registry();
|
||||
|
||||
static const std::vector<std::string> priority = {
|
||||
"vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"};
|
||||
|
||||
for (const auto &name : priority) {
|
||||
if (reg.find(name) != reg.end()) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!reg.empty()) {
|
||||
return reg.begin()->first;
|
||||
}
|
||||
|
||||
E2D_LOG_WARN("未注册任何图形后端");
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string BackendFactory::getRecommendedBackendForWindow(
|
||||
const std::string &windowBackend) {
|
||||
auto ® = registry();
|
||||
|
||||
static const std::vector<std::string> priority = {
|
||||
"vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"};
|
||||
|
||||
for (const auto &name : priority) {
|
||||
auto it = reg.find(name);
|
||||
if (it != reg.end() && isCompatible(name, windowBackend)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &pair : reg) {
|
||||
if (isCompatible(pair.first, windowBackend)) {
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
|
||||
E2D_LOG_WARN("未找到与窗口后端 '{}' 兼容的图形后端", windowBackend);
|
||||
return "";
|
||||
}
|
||||
|
||||
bool BackendFactory::isCompatible(const std::string &graphicsBackend,
|
||||
const std::string &windowBackend) {
|
||||
auto ® = registry();
|
||||
auto it = reg.find(graphicsBackend);
|
||||
if (it == reg.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &windowBackends = it->second.windowBackends;
|
||||
if (windowBackends.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto &wb : windowBackends) {
|
||||
if (wb == windowBackend) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
BackendFactory::getSupportedWindowBackends(const std::string &graphicsBackend) {
|
||||
auto ® = registry();
|
||||
auto it = reg.find(graphicsBackend);
|
||||
if (it != reg.end()) {
|
||||
return it->second.windowBackends;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#include <extra2d/graphics/backends/opengl/gl_renderer.h>
|
||||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace graphics {
|
||||
|
||||
namespace {
|
||||
static bool s_openglBackendRegistered = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化 OpenGL 后端注册
|
||||
*/
|
||||
void initOpenGLBackend() {
|
||||
if (s_openglBackendRegistered) {
|
||||
return;
|
||||
}
|
||||
s_openglBackendRegistered = true;
|
||||
|
||||
BackendFactory::reg(
|
||||
"opengl",
|
||||
[]() -> UniquePtr<RenderBackend> {
|
||||
return makeUnique<GLRenderer>();
|
||||
},
|
||||
{"sdl2", "glfw"}
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct OpenGLBackendAutoReg {
|
||||
OpenGLBackendAutoReg() {
|
||||
initOpenGLBackend();
|
||||
}
|
||||
};
|
||||
static OpenGLBackendAutoReg s_openglAutoReg;
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
#include <cstring>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_buffer.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// GLBuffer 实现
|
||||
// ============================================================================
|
||||
|
||||
GLBuffer::GLBuffer() = default;
|
||||
|
||||
GLBuffer::~GLBuffer() { shutdown(); }
|
||||
|
||||
bool GLBuffer::init(const BufferDesc &desc) {
|
||||
if (bufferID_ != 0) {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
type_ = desc.type;
|
||||
usage_ = desc.usage;
|
||||
size_ = desc.size;
|
||||
target_ = convertType(type_);
|
||||
glUsage_ = convertUsage(usage_);
|
||||
|
||||
// 生成缓冲区
|
||||
glGenBuffers(1, &bufferID_);
|
||||
if (bufferID_ == 0) {
|
||||
E2D_LOG_ERROR("生成 OpenGL 缓冲区失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 绑定并分配缓冲区
|
||||
glBindBuffer(target_, bufferID_);
|
||||
glBufferData(target_, static_cast<GLsizeiptr>(size_), desc.initialData,
|
||||
glUsage_);
|
||||
glBindBuffer(target_, 0);
|
||||
|
||||
// 追踪显存使用
|
||||
VRAMMgr::get().allocBuffer(size_);
|
||||
|
||||
E2D_LOG_DEBUG("GLBuffer 已创建: ID={}, 大小={}, 类型={}, 用途={}",
|
||||
bufferID_, size_, static_cast<int>(type_),
|
||||
static_cast<int>(usage_));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLBuffer::shutdown() {
|
||||
if (bufferID_ != 0) {
|
||||
if (mapped_) {
|
||||
unmap();
|
||||
}
|
||||
// 释放显存追踪
|
||||
VRAMMgr::get().freeBuffer(size_);
|
||||
glDeleteBuffers(1, &bufferID_);
|
||||
E2D_LOG_DEBUG("GLBuffer 已销毁: ID={}", bufferID_);
|
||||
bufferID_ = 0;
|
||||
}
|
||||
size_ = 0;
|
||||
mapped_ = false;
|
||||
mappedPtr_ = nullptr;
|
||||
}
|
||||
|
||||
void GLBuffer::bind() {
|
||||
if (bufferID_ != 0) {
|
||||
glBindBuffer(target_, bufferID_);
|
||||
}
|
||||
}
|
||||
|
||||
void GLBuffer::unbind() { glBindBuffer(target_, 0); }
|
||||
|
||||
void GLBuffer::setData(const void *data, size_t size) {
|
||||
if (bufferID_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
|
||||
// 如果大小相同,使用 glBufferSubData 更高效
|
||||
if (size == size_) {
|
||||
glBufferSubData(target_, 0, static_cast<GLsizeiptr>(size), data);
|
||||
} else {
|
||||
// 大小不同,重新分配
|
||||
size_ = size;
|
||||
glBufferData(target_, static_cast<GLsizeiptr>(size_), data, glUsage_);
|
||||
}
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
void GLBuffer::updateData(const void *data, size_t offset, size_t size) {
|
||||
if (bufferID_ == 0 || data == nullptr || size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset + size > size_) {
|
||||
E2D_LOG_WARN(
|
||||
"GLBuffer updateData out of bounds: offset={}, size={}, bufferSize={}",
|
||||
offset, size, size_);
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
glBufferSubData(target_, static_cast<GLintptr>(offset),
|
||||
static_cast<GLsizeiptr>(size), data);
|
||||
unbind();
|
||||
}
|
||||
|
||||
void *GLBuffer::map() {
|
||||
if (bufferID_ == 0 || mapped_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bind();
|
||||
|
||||
// 使用 glMapBufferRange 替代 glMapBuffer,更现代且安全
|
||||
GLbitfield access = GL_MAP_WRITE_BIT;
|
||||
if (usage_ == BufferUsage::Dynamic || usage_ == BufferUsage::Stream) {
|
||||
access |= GL_MAP_INVALIDATE_BUFFER_BIT; // 暗示驱动可以丢弃旧数据
|
||||
}
|
||||
|
||||
mappedPtr_ =
|
||||
glMapBufferRange(target_, 0, static_cast<GLsizeiptr>(size_), access);
|
||||
if (mappedPtr_) {
|
||||
mapped_ = true;
|
||||
} else {
|
||||
E2D_LOG_ERROR("映射 GLBuffer 失败");
|
||||
}
|
||||
|
||||
return mappedPtr_;
|
||||
}
|
||||
|
||||
void GLBuffer::unmap() {
|
||||
if (!mapped_ || bufferID_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
glUnmapBuffer(target_);
|
||||
mapped_ = false;
|
||||
mappedPtr_ = nullptr;
|
||||
unbind();
|
||||
}
|
||||
|
||||
GLenum GLBuffer::convertUsage(BufferUsage usage) {
|
||||
switch (usage) {
|
||||
case BufferUsage::Static:
|
||||
return GL_STATIC_DRAW;
|
||||
case BufferUsage::Dynamic:
|
||||
return GL_DYNAMIC_DRAW;
|
||||
case BufferUsage::Stream:
|
||||
return GL_STREAM_DRAW;
|
||||
default:
|
||||
return GL_STATIC_DRAW;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum GLBuffer::convertType(BufferType type) {
|
||||
switch (type) {
|
||||
case BufferType::Vertex:
|
||||
return GL_ARRAY_BUFFER;
|
||||
case BufferType::Index:
|
||||
return GL_ELEMENT_ARRAY_BUFFER;
|
||||
case BufferType::Uniform:
|
||||
return GL_UNIFORM_BUFFER;
|
||||
default:
|
||||
return GL_ARRAY_BUFFER;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_context.h>
|
||||
#include <extra2d/graphics/memory/gpu_context.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// GLContext 实现
|
||||
// ============================================================================
|
||||
|
||||
GLContext &GLContext::get() {
|
||||
static GLContext instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool GLContext::init() {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 解析 OpenGL 版本
|
||||
parseVersion();
|
||||
|
||||
// 加载扩展(GLAD 已在 glad.c 中完成)
|
||||
if (!loadExtensions()) {
|
||||
E2D_LOG_ERROR("加载 OpenGL 扩展失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
// 标记 GPU 上下文为有效
|
||||
GPUContext::get().markValid();
|
||||
|
||||
E2D_LOG_INFO("OpenGL 上下文已初始化");
|
||||
E2D_LOG_INFO(" 版本: {}", getVersionString());
|
||||
E2D_LOG_INFO(" 供应商: {}", getVendor());
|
||||
E2D_LOG_INFO(" 渲染器: {}", getRenderer());
|
||||
E2D_LOG_INFO(" 最大纹理大小: {}", getMaxTextureSize());
|
||||
E2D_LOG_INFO(" 最大纹理单元数: {}", getMaxTextureUnits());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLContext::shutdown() {
|
||||
// 标记 GPU 上下文为无效
|
||||
GPUContext::get().markInvalid();
|
||||
|
||||
initialized_ = false;
|
||||
version_ = GLVersion{};
|
||||
maxTextureSize_ = -1;
|
||||
maxTextureUnits_ = -1;
|
||||
maxVertexAttribs_ = -1;
|
||||
maxUniformBufferBindings_ = -1;
|
||||
}
|
||||
|
||||
std::string GLContext::getVersionString() const {
|
||||
const char *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
|
||||
return version ? version : "Unknown";
|
||||
}
|
||||
|
||||
std::string GLContext::getVendor() const {
|
||||
const char *vendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
|
||||
return vendor ? vendor : "Unknown";
|
||||
}
|
||||
|
||||
std::string GLContext::getRenderer() const {
|
||||
const char *renderer =
|
||||
reinterpret_cast<const char *>(glGetString(GL_RENDERER));
|
||||
return renderer ? renderer : "Unknown";
|
||||
}
|
||||
|
||||
bool GLContext::hasExtension(const std::string &extension) const {
|
||||
GLint numExtensions = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
|
||||
|
||||
for (GLint i = 0; i < numExtensions; ++i) {
|
||||
const char *ext =
|
||||
reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i));
|
||||
if (ext && extension == ext) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int GLContext::getMaxTextureSize() const {
|
||||
if (maxTextureSize_ < 0) {
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_);
|
||||
}
|
||||
return maxTextureSize_;
|
||||
}
|
||||
|
||||
int GLContext::getMaxTextureUnits() const {
|
||||
if (maxTextureUnits_ < 0) {
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits_);
|
||||
}
|
||||
return maxTextureUnits_;
|
||||
}
|
||||
|
||||
int GLContext::getMaxVertexAttribs() const {
|
||||
if (maxVertexAttribs_ < 0) {
|
||||
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs_);
|
||||
}
|
||||
return maxVertexAttribs_;
|
||||
}
|
||||
|
||||
int GLContext::getMaxUniformBufferBindings() const {
|
||||
if (maxUniformBufferBindings_ < 0) {
|
||||
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings_);
|
||||
}
|
||||
return maxUniformBufferBindings_;
|
||||
}
|
||||
|
||||
bool GLContext::hasVAO() const {
|
||||
// OpenGL 3.0+ 或 OpenGL ES 3.0+ 原生支持 VAO
|
||||
if (version_.es) {
|
||||
return version_.major >= 3;
|
||||
}
|
||||
return version_.major > 3 || (version_.major == 3 && version_.minor >= 0);
|
||||
}
|
||||
|
||||
bool GLContext::hasFBO() const {
|
||||
// OpenGL 3.0+ 或 OpenGL ES 2.0+ 原生支持 FBO
|
||||
if (version_.es) {
|
||||
return version_.major >= 2;
|
||||
}
|
||||
return version_.major >= 3;
|
||||
}
|
||||
|
||||
bool GLContext::hasShader() const {
|
||||
// OpenGL 2.0+ 或 OpenGL ES 2.0+ 原生支持 Shader
|
||||
if (version_.es) {
|
||||
return version_.major >= 2;
|
||||
}
|
||||
return version_.major >= 2;
|
||||
}
|
||||
|
||||
void GLContext::parseVersion() {
|
||||
const char *versionStr =
|
||||
reinterpret_cast<const char *>(glGetString(GL_VERSION));
|
||||
if (!versionStr) {
|
||||
version_ = GLVersion{0, 0, false};
|
||||
return;
|
||||
}
|
||||
|
||||
std::string version(versionStr);
|
||||
|
||||
// 检查是否为 OpenGL ES
|
||||
if (version.find("OpenGL ES") != std::string::npos) {
|
||||
version_.es = true;
|
||||
// 解析 ES 版本号,格式如 "OpenGL ES 3.0"
|
||||
std::sscanf(version.c_str(), "OpenGL ES %d.%d", &version_.major,
|
||||
&version_.minor);
|
||||
} else {
|
||||
version_.es = false;
|
||||
// 解析桌面版本号,格式如 "3.3.0 NVIDIA"
|
||||
std::sscanf(version.c_str(), "%d.%d", &version_.major, &version_.minor);
|
||||
}
|
||||
}
|
||||
|
||||
bool GLContext::loadExtensions() {
|
||||
// GLAD 已经在 glad.c 中加载了所有扩展
|
||||
// 这里可以添加额外的扩展检查
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_font_atlas.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <simdutf/simdutf.h>
|
||||
|
||||
// 在实现文件中定义 STB 实现
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include <stb/stb_truetype.h>
|
||||
|
||||
#define STB_RECT_PACK_IMPLEMENTATION
|
||||
#include <stb/stb_rect_pack.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// GLFontAtlas 构造函数
|
||||
// 加载字体文件并初始化图集
|
||||
// ============================================================================
|
||||
GLFontAtlas::GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF)
|
||||
: useSDF_(useSDF), fontSize_(fontSize), lineHeight_(0.0f), ascent_(0.0f),
|
||||
descent_(0.0f), lineGap_(0.0f), scale_(0.0f) {
|
||||
|
||||
// 加载字体文件
|
||||
if (!initFont(filepath)) {
|
||||
E2D_LOG_ERROR("初始化字体失败: {}", filepath);
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算字体缩放比例和度量
|
||||
scale_ = stbtt_ScaleForPixelHeight(&fontInfo_, static_cast<float>(fontSize_));
|
||||
|
||||
int ascent, descent, lineGap;
|
||||
stbtt_GetFontVMetrics(&fontInfo_, &ascent, &descent, &lineGap);
|
||||
|
||||
ascent_ = static_cast<float>(ascent) * scale_;
|
||||
descent_ = static_cast<float>(descent) * scale_;
|
||||
lineGap_ = static_cast<float>(lineGap) * scale_;
|
||||
lineHeight_ = ascent_ - descent_ + lineGap_;
|
||||
|
||||
// 创建图集纹理和打包上下文
|
||||
createAtlas();
|
||||
|
||||
// 预加载常用 ASCII 字符
|
||||
std::string asciiChars = " !\"#$%&'()*+,-./"
|
||||
"0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
|
||||
"abcdefghijklmnopqrstuvwxyz{|}~";
|
||||
for (char c : asciiChars) {
|
||||
char32_t codepoint = static_cast<char32_t>(static_cast<unsigned char>(c));
|
||||
cacheGlyph(codepoint);
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("字体图集已创建: {} ({}px, {}x{})", filepath, fontSize_,
|
||||
ATLAS_WIDTH, ATLAS_HEIGHT);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GLFontAtlas 析构函数
|
||||
// ============================================================================
|
||||
GLFontAtlas::~GLFontAtlas() {
|
||||
// 智能指针自动管理纹理资源
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 获取字形信息 - 如果字形不存在则动态缓存
|
||||
// ============================================================================
|
||||
const Glyph *GLFontAtlas::getGlyph(char32_t codepoint) const {
|
||||
auto it = glyphs_.find(codepoint);
|
||||
if (it == glyphs_.end()) {
|
||||
// 动态缓存新字形
|
||||
const_cast<GLFontAtlas *>(this)->cacheGlyph(codepoint);
|
||||
it = glyphs_.find(codepoint);
|
||||
if (it == glyphs_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回静态存储的 Glyph 数据
|
||||
static Glyph glyph;
|
||||
const auto &data = it->second;
|
||||
glyph.width = data.width;
|
||||
glyph.height = data.height;
|
||||
glyph.bearingX = data.bearingX;
|
||||
glyph.bearingY = data.bearingY;
|
||||
glyph.advance = data.advance;
|
||||
glyph.u0 = data.u0;
|
||||
glyph.v0 = data.v0;
|
||||
glyph.u1 = data.u1;
|
||||
glyph.v1 = data.v1;
|
||||
|
||||
return &glyph;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 测量文本尺寸 - 支持多行文本
|
||||
// ============================================================================
|
||||
Vec2 GLFontAtlas::measureText(const std::string &text) {
|
||||
float maxWidth = 0.0f;
|
||||
float height = lineHeight_;
|
||||
float currentWidth = 0.0f;
|
||||
|
||||
size_t utf32Length = simdutf::count_utf8(text.data(), text.size());
|
||||
std::vector<char32_t> utf32Buffer(utf32Length);
|
||||
size_t converted = simdutf::convert_utf8_to_utf32(text.data(), text.size(),
|
||||
utf32Buffer.data());
|
||||
|
||||
for (size_t i = 0; i < converted; ++i) {
|
||||
char32_t codepoint = utf32Buffer[i];
|
||||
|
||||
if (codepoint == '\n') {
|
||||
maxWidth = std::max(maxWidth, currentWidth);
|
||||
currentWidth = 0.0f;
|
||||
height += lineHeight_;
|
||||
continue;
|
||||
}
|
||||
|
||||
const Glyph *glyph = getGlyph(codepoint);
|
||||
if (glyph) {
|
||||
currentWidth += glyph->advance;
|
||||
}
|
||||
}
|
||||
|
||||
maxWidth = std::max(maxWidth, currentWidth);
|
||||
return Vec2(maxWidth, height);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 初始化字体 - 加载字体文件到内存
|
||||
// ============================================================================
|
||||
bool GLFontAtlas::initFont(const std::string &filepath) {
|
||||
// 读取字体文件到内存
|
||||
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("打开字体文件失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
fontData_.resize(static_cast<size_t>(size));
|
||||
if (!file.read(reinterpret_cast<char *>(fontData_.data()), size)) {
|
||||
E2D_LOG_ERROR("读取字体文件失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化 STB 字体
|
||||
if (!stbtt_InitFont(&fontInfo_, fontData_.data(), 0)) {
|
||||
E2D_LOG_ERROR("初始化 STB 字体失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 创建图集纹理 - 初始化空白纹理和矩形打包上下文
|
||||
// ============================================================================
|
||||
void GLFontAtlas::createAtlas() {
|
||||
// 统一使用 4 通道格式 (RGBA)
|
||||
int channels = 4;
|
||||
std::vector<uint8_t> emptyData(ATLAS_WIDTH * ATLAS_HEIGHT * channels, 0);
|
||||
texture_ = std::make_unique<GLTexture>(ATLAS_WIDTH, ATLAS_HEIGHT,
|
||||
emptyData.data(), channels);
|
||||
// 所有字体都使用线性过滤,SDF的抗锯齿由着色器处理
|
||||
texture_->setFilter(true);
|
||||
|
||||
// 初始化矩形打包上下文 - 持久化以支持增量打包
|
||||
packNodes_.resize(ATLAS_WIDTH);
|
||||
stbrp_init_target(&packContext_, ATLAS_WIDTH, ATLAS_HEIGHT, packNodes_.data(),
|
||||
ATLAS_WIDTH);
|
||||
|
||||
// 预分配字形缓冲区
|
||||
// 假设最大字形尺寸为 fontSize * fontSize * 4 (RGBA)
|
||||
size_t maxGlyphSize = static_cast<size_t>(fontSize_ * fontSize_ * 4 * 4);
|
||||
glyphBitmapCache_.reserve(maxGlyphSize);
|
||||
glyphRgbaCache_.reserve(maxGlyphSize);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 缓存字形 - 渲染字形到图集并存储信息
|
||||
// 使用 stb_rect_pack 进行矩形打包
|
||||
// ============================================================================
|
||||
void GLFontAtlas::cacheGlyph(char32_t codepoint) {
|
||||
// 检查是否已存在
|
||||
if (glyphs_.find(codepoint) != glyphs_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取字形水平度量
|
||||
int advance, leftSideBearing;
|
||||
stbtt_GetCodepointHMetrics(&fontInfo_, static_cast<int>(codepoint), &advance,
|
||||
&leftSideBearing);
|
||||
float advancePx = static_cast<float>(advance) * scale_;
|
||||
|
||||
// SDF 渲染模式
|
||||
if (useSDF_) {
|
||||
constexpr int SDF_PADDING = 8;
|
||||
constexpr unsigned char ONEDGE_VALUE = 128;
|
||||
constexpr float PIXEL_DIST_SCALE = 64.0f;
|
||||
|
||||
int w = 0, h = 0, xoff = 0, yoff = 0;
|
||||
unsigned char *sdf = stbtt_GetCodepointSDF(
|
||||
&fontInfo_, scale_, static_cast<int>(codepoint), SDF_PADDING,
|
||||
ONEDGE_VALUE, PIXEL_DIST_SCALE, &w, &h, &xoff, &yoff);
|
||||
|
||||
if (!sdf || w <= 0 || h <= 0) {
|
||||
if (sdf)
|
||||
stbtt_FreeSDF(sdf, nullptr);
|
||||
// 创建空白字形(如空格)
|
||||
GlyphData data{};
|
||||
data.advance = advancePx;
|
||||
glyphs_[codepoint] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用 stb_rect_pack 打包矩形
|
||||
stbrp_rect rect;
|
||||
rect.id = static_cast<int>(codepoint);
|
||||
rect.w = w + PADDING * 2;
|
||||
rect.h = h + PADDING * 2;
|
||||
|
||||
stbrp_pack_rects(&packContext_, &rect, 1);
|
||||
if (!rect.was_packed) {
|
||||
E2D_LOG_WARN("字体图集已满,无法缓存字符码点: {}", codepoint);
|
||||
stbtt_FreeSDF(sdf, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
int atlasX = rect.x + PADDING;
|
||||
int atlasY = rect.y + PADDING;
|
||||
|
||||
// 创建字形数据
|
||||
GlyphData data;
|
||||
data.width = static_cast<float>(w);
|
||||
data.height = static_cast<float>(h);
|
||||
data.bearingX = static_cast<float>(xoff);
|
||||
data.bearingY = static_cast<float>(yoff);
|
||||
data.advance = advancePx;
|
||||
|
||||
// 计算 UV 坐标
|
||||
// stb_rect_pack 使用左上角为原点,OpenGL纹理使用左下角为原点
|
||||
// 需要翻转V坐标
|
||||
float v0 = static_cast<float>(atlasY) / ATLAS_HEIGHT;
|
||||
float v1 = static_cast<float>(atlasY + h) / ATLAS_HEIGHT;
|
||||
data.u0 = static_cast<float>(atlasX) / ATLAS_WIDTH;
|
||||
data.v0 = 1.0f - v1; // 翻转V坐标
|
||||
data.u1 = static_cast<float>(atlasX + w) / ATLAS_WIDTH;
|
||||
data.v1 = 1.0f - v0; // 翻转V坐标
|
||||
|
||||
glyphs_[codepoint] = data;
|
||||
|
||||
// 将 SDF 单通道数据转换为 RGBA 格式(统一格式)
|
||||
size_t pixelCount = static_cast<size_t>(w) * static_cast<size_t>(h);
|
||||
glyphRgbaCache_.resize(pixelCount * 4);
|
||||
for (size_t i = 0; i < pixelCount; ++i) {
|
||||
uint8_t alpha = sdf[i];
|
||||
glyphRgbaCache_[i * 4 + 0] = 255; // R
|
||||
glyphRgbaCache_[i * 4 + 1] = 255; // G
|
||||
glyphRgbaCache_[i * 4 + 2] = 255; // B
|
||||
glyphRgbaCache_[i * 4 + 3] = alpha; // A - SDF 值存储在 Alpha 通道
|
||||
}
|
||||
|
||||
// 更新纹理 - OpenGL纹理坐标原点在左下角,需要将Y坐标翻转
|
||||
updateAtlas(atlasX, ATLAS_HEIGHT - atlasY - h, w, h, glyphRgbaCache_);
|
||||
|
||||
stbtt_FreeSDF(sdf, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// 普通位图渲染模式
|
||||
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
||||
stbtt_GetCodepointBitmapBox(&fontInfo_, static_cast<int>(codepoint), scale_,
|
||||
scale_, &x0, &y0, &x1, &y1);
|
||||
int w = x1 - x0;
|
||||
int h = y1 - y0;
|
||||
int xoff = x0;
|
||||
// y0 是相对于基线的偏移(通常为负值,表示在基线上方)
|
||||
// bearingY 应该是字形顶部相对于基线的偏移
|
||||
int yoff = y0;
|
||||
|
||||
if (w <= 0 || h <= 0) {
|
||||
// 空白字符(如空格)
|
||||
GlyphData data{};
|
||||
data.advance = advancePx;
|
||||
glyphs_[codepoint] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用预分配缓冲区渲染字形
|
||||
size_t pixelCount = static_cast<size_t>(w) * static_cast<size_t>(h);
|
||||
glyphBitmapCache_.resize(pixelCount);
|
||||
stbtt_MakeCodepointBitmap(&fontInfo_, glyphBitmapCache_.data(), w, h, w,
|
||||
scale_, scale_, static_cast<int>(codepoint));
|
||||
|
||||
// 使用 stb_rect_pack 打包矩形
|
||||
stbrp_rect rect;
|
||||
rect.id = static_cast<int>(codepoint);
|
||||
rect.w = w + PADDING * 2;
|
||||
rect.h = h + PADDING * 2;
|
||||
|
||||
stbrp_pack_rects(&packContext_, &rect, 1);
|
||||
|
||||
if (!rect.was_packed) {
|
||||
E2D_LOG_WARN("字体图集已满,无法缓存字符码点: {}", codepoint);
|
||||
return;
|
||||
}
|
||||
|
||||
int atlasX = rect.x + PADDING;
|
||||
int atlasY = rect.y + PADDING;
|
||||
|
||||
// 创建字形数据
|
||||
GlyphData data;
|
||||
data.width = static_cast<float>(w);
|
||||
data.height = static_cast<float>(h);
|
||||
data.bearingX = static_cast<float>(xoff);
|
||||
data.bearingY = static_cast<float>(yoff);
|
||||
data.advance = advancePx;
|
||||
|
||||
// 计算 UV 坐标
|
||||
// stb_rect_pack 使用左上角为原点,OpenGL纹理使用左下角为原点
|
||||
// 需要翻转V坐标
|
||||
float v0 = static_cast<float>(atlasY) / ATLAS_HEIGHT;
|
||||
float v1 = static_cast<float>(atlasY + h) / ATLAS_HEIGHT;
|
||||
data.u0 = static_cast<float>(atlasX) / ATLAS_WIDTH;
|
||||
data.v0 = 1.0f - v1; // 翻转V坐标
|
||||
data.u1 = static_cast<float>(atlasX + w) / ATLAS_WIDTH;
|
||||
data.v1 = 1.0f - v0; // 翻转V坐标
|
||||
|
||||
glyphs_[codepoint] = data;
|
||||
|
||||
// 将单通道字形数据转换为 RGBA 格式(白色字形,Alpha 通道存储灰度)
|
||||
glyphRgbaCache_.resize(pixelCount * 4);
|
||||
for (size_t i = 0; i < pixelCount; ++i) {
|
||||
uint8_t alpha = glyphBitmapCache_[i];
|
||||
glyphRgbaCache_[i * 4 + 0] = 255; // R
|
||||
glyphRgbaCache_[i * 4 + 1] = 255; // G
|
||||
glyphRgbaCache_[i * 4 + 2] = 255; // B
|
||||
glyphRgbaCache_[i * 4 + 3] = alpha; // A
|
||||
}
|
||||
|
||||
// 更新纹理 - OpenGL纹理坐标原点在左下角,需要将Y坐标翻转
|
||||
updateAtlas(atlasX, ATLAS_HEIGHT - atlasY - h, w, h, glyphRgbaCache_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 更新图集纹理区域
|
||||
// ============================================================================
|
||||
void GLFontAtlas::updateAtlas(int x, int y, int width, int height,
|
||||
const std::vector<uint8_t> &data) {
|
||||
if (texture_) {
|
||||
texture_->bind();
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, data.data());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// GLFramebuffer 实现
|
||||
// ============================================================================
|
||||
|
||||
GLFramebuffer::GLFramebuffer() = default;
|
||||
|
||||
GLFramebuffer::~GLFramebuffer() { shutdown(); }
|
||||
|
||||
bool GLFramebuffer::init(const FramebufferDesc &desc) {
|
||||
if (fboID_ != 0) {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
width_ = desc.width;
|
||||
height_ = desc.height;
|
||||
numColorAttachments_ = desc.colorAttachments;
|
||||
hasDepth_ = desc.hasDepth;
|
||||
hasStencil_ = desc.hasStencil;
|
||||
|
||||
// 限制颜色附件数
|
||||
if (numColorAttachments_ > MAX_COLOR_ATTACHMENTS) {
|
||||
numColorAttachments_ = MAX_COLOR_ATTACHMENTS;
|
||||
}
|
||||
|
||||
// 生成 FBO
|
||||
glGenFramebuffers(1, &fboID_);
|
||||
if (fboID_ == 0) {
|
||||
E2D_LOG_ERROR("生成 OpenGL 帧缓冲区失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("GLFramebuffer 已创建: ID={}, 大小={}x{}, 颜色附件={}",
|
||||
fboID_, width_, height_, numColorAttachments_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLFramebuffer::shutdown() {
|
||||
if (fboID_ != 0) {
|
||||
glDeleteFramebuffers(1, &fboID_);
|
||||
E2D_LOG_DEBUG("GLFramebuffer 已销毁: ID={}", fboID_);
|
||||
fboID_ = 0;
|
||||
}
|
||||
|
||||
// 清理纹理引用
|
||||
for (auto &tex : colorTextures_) {
|
||||
tex.reset();
|
||||
}
|
||||
depthTexture_.reset();
|
||||
depthStencilTexture_.reset();
|
||||
|
||||
hasInternalTextures_ = false;
|
||||
}
|
||||
|
||||
void GLFramebuffer::bind() {
|
||||
if (fboID_ != 0) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fboID_);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFramebuffer::unbind() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
|
||||
|
||||
void GLFramebuffer::attachColorTexture(Ptr<Texture> texture, int attachment) {
|
||||
if (fboID_ == 0 || !texture || attachment < 0 ||
|
||||
attachment >= MAX_COLOR_ATTACHMENTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
|
||||
// 获取 OpenGL 纹理 ID
|
||||
GLuint texID = static_cast<GLuint>(
|
||||
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, getColorAttachment(attachment),
|
||||
GL_TEXTURE_2D, texID, 0);
|
||||
|
||||
colorTextures_[attachment] = texture;
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
void GLFramebuffer::attachDepthTexture(Ptr<Texture> texture) {
|
||||
if (fboID_ == 0 || !texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
|
||||
// 获取 OpenGL 纹理 ID
|
||||
GLuint texID = static_cast<GLuint>(
|
||||
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
texID, 0);
|
||||
|
||||
depthTexture_ = texture;
|
||||
hasDepth_ = true;
|
||||
hasStencil_ = false;
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
void GLFramebuffer::attachDepthStencilTexture(Ptr<Texture> texture) {
|
||||
if (fboID_ == 0 || !texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
|
||||
// 获取 OpenGL 纹理 ID
|
||||
GLuint texID = static_cast<GLuint>(
|
||||
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D, texID, 0);
|
||||
|
||||
depthStencilTexture_ = texture;
|
||||
hasDepth_ = true;
|
||||
hasStencil_ = true;
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
bool GLFramebuffer::isComplete() {
|
||||
if (fboID_ == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bind();
|
||||
bool complete = checkStatus();
|
||||
unbind();
|
||||
|
||||
return complete;
|
||||
}
|
||||
|
||||
Ptr<Texture> GLFramebuffer::getColorTexture(int attachment) const {
|
||||
if (attachment >= 0 && attachment < MAX_COLOR_ATTACHMENTS) {
|
||||
return colorTextures_[attachment];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<Texture> GLFramebuffer::getDepthTexture() const { return depthTexture_; }
|
||||
|
||||
void GLFramebuffer::clear(const Color &color, bool clearColor, bool clearDepth,
|
||||
bool clearStencil) {
|
||||
if (fboID_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
|
||||
GLbitfield mask = 0;
|
||||
|
||||
if (clearColor) {
|
||||
mask |= GL_COLOR_BUFFER_BIT;
|
||||
glClearColor(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
if (clearDepth) {
|
||||
mask |= GL_DEPTH_BUFFER_BIT;
|
||||
glClearDepthf(1.0f);
|
||||
}
|
||||
|
||||
if (clearStencil) {
|
||||
mask |= GL_STENCIL_BUFFER_BIT;
|
||||
glClearStencil(0);
|
||||
}
|
||||
|
||||
if (mask != 0) {
|
||||
glClear(mask);
|
||||
}
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
void GLFramebuffer::setViewport(int x, int y, int width, int height) {
|
||||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
bool GLFramebuffer::readPixels(int x, int y, int width, int height,
|
||||
std::vector<uint8_t> &outData) {
|
||||
if (fboID_ == 0 || width <= 0 || height <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算需要的缓冲区大小 (RGBA8)
|
||||
size_t dataSize = width * height * 4;
|
||||
outData.resize(dataSize);
|
||||
|
||||
bind();
|
||||
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, outData.data());
|
||||
unbind();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLFramebuffer::createWithTextures(int width, int height,
|
||||
PixelFormat colorFormat,
|
||||
PixelFormat depthFormat) {
|
||||
FramebufferDesc desc;
|
||||
desc.width = width;
|
||||
desc.height = height;
|
||||
desc.colorAttachments = 1;
|
||||
desc.hasDepth = (depthFormat != PixelFormat::RGBA8);
|
||||
desc.hasStencil = (depthFormat == PixelFormat::Depth24Stencil8);
|
||||
|
||||
if (!init(desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hasInternalTextures_ = true;
|
||||
|
||||
// 创建颜色纹理
|
||||
// 注意:这里简化处理,实际应该通过纹理工厂创建
|
||||
// 暂时返回 true,实际纹理创建由调用者处理
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLFramebuffer::checkStatus() {
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
|
||||
switch (status) {
|
||||
case GL_FRAMEBUFFER_COMPLETE:
|
||||
return true;
|
||||
case GL_FRAMEBUFFER_UNDEFINED:
|
||||
E2D_LOG_ERROR("帧缓冲区不完整: GL_FRAMEBUFFER_UNDEFINED");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
||||
E2D_LOG_ERROR(
|
||||
"帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
||||
E2D_LOG_ERROR(
|
||||
"帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
|
||||
break;
|
||||
#ifndef GL_ES_VERSION_2_0
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
|
||||
E2D_LOG_ERROR(
|
||||
"帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
|
||||
E2D_LOG_ERROR(
|
||||
"帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
|
||||
break;
|
||||
#endif
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED:
|
||||
E2D_LOG_ERROR("帧缓冲区不完整: GL_FRAMEBUFFER_UNSUPPORTED");
|
||||
break;
|
||||
default:
|
||||
E2D_LOG_ERROR("帧缓冲区不完整: 未知错误 {}", status);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GLenum GLFramebuffer::getColorAttachment(int index) {
|
||||
return GL_COLOR_ATTACHMENT0 + index;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,235 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_pipeline.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// GLPipeline 实现
|
||||
// ============================================================================
|
||||
|
||||
GLPipeline::GLPipeline() = default;
|
||||
|
||||
GLPipeline::~GLPipeline() { shutdown(); }
|
||||
|
||||
bool GLPipeline::init(const PipelineDesc &desc) {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
blendMode_ = desc.blendMode;
|
||||
blendEnabled_ = desc.blendEnabled;
|
||||
depthTest_ = desc.depthTest;
|
||||
depthWrite_ = desc.depthWrite;
|
||||
depthFunc_ = desc.depthFunc;
|
||||
cullMode_ = desc.cullMode;
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
E2D_LOG_DEBUG(
|
||||
"GLPipeline initialized: blendMode={}, depthTest={}, cullMode={}",
|
||||
static_cast<int>(blendMode_), depthTest_, static_cast<int>(cullMode_));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLPipeline::shutdown() { initialized_ = false; }
|
||||
|
||||
void GLPipeline::bind() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyAllStates();
|
||||
}
|
||||
|
||||
void GLPipeline::unbind() {
|
||||
// OpenGL 不需要显式解绑管线
|
||||
}
|
||||
|
||||
void GLPipeline::setBlendMode(BlendMode mode) {
|
||||
blendMode_ = mode;
|
||||
applyBlendState();
|
||||
}
|
||||
|
||||
void GLPipeline::setDepthTest(bool enabled) {
|
||||
depthTest_ = enabled;
|
||||
applyDepthState();
|
||||
}
|
||||
|
||||
void GLPipeline::setDepthWrite(bool enabled) {
|
||||
depthWrite_ = enabled;
|
||||
applyDepthState();
|
||||
}
|
||||
|
||||
void GLPipeline::setDepthFunc(DepthFunc func) {
|
||||
depthFunc_ = func;
|
||||
applyDepthState();
|
||||
}
|
||||
|
||||
void GLPipeline::setCullMode(CullMode mode) {
|
||||
cullMode_ = mode;
|
||||
applyCullState();
|
||||
}
|
||||
|
||||
void GLPipeline::setViewport(int x, int y, int width, int height) {
|
||||
viewportX_ = x;
|
||||
viewportY_ = y;
|
||||
viewportWidth_ = width;
|
||||
viewportHeight_ = height;
|
||||
|
||||
// 检查缓存,避免冗余调用
|
||||
if (x != cachedViewportX_ || y != cachedViewportY_ ||
|
||||
width != cachedViewportWidth_ || height != cachedViewportHeight_) {
|
||||
glViewport(x, y, width, height);
|
||||
cachedViewportX_ = x;
|
||||
cachedViewportY_ = y;
|
||||
cachedViewportWidth_ = width;
|
||||
cachedViewportHeight_ = height;
|
||||
}
|
||||
}
|
||||
|
||||
void GLPipeline::getViewport(int &x, int &y, int &width, int &height) const {
|
||||
x = viewportX_;
|
||||
y = viewportY_;
|
||||
width = viewportWidth_;
|
||||
height = viewportHeight_;
|
||||
}
|
||||
|
||||
void GLPipeline::applyAllStates() {
|
||||
applyBlendState();
|
||||
applyDepthState();
|
||||
applyCullState();
|
||||
|
||||
// 应用视口
|
||||
if (viewportWidth_ > 0 && viewportHeight_ > 0) {
|
||||
setViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
|
||||
}
|
||||
}
|
||||
|
||||
void GLPipeline::applyBlendState() {
|
||||
// 检查是否需要启用/禁用混合
|
||||
if (blendEnabled_ != cachedBlendEnabled_) {
|
||||
if (blendEnabled_) {
|
||||
glEnable(GL_BLEND);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
cachedBlendEnabled_ = blendEnabled_;
|
||||
}
|
||||
|
||||
// 如果禁用了混合,不需要设置混合函数
|
||||
if (!blendEnabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查混合模式是否改变
|
||||
if (blendMode_ != cachedBlendMode_) {
|
||||
GLenum srcFactor, dstFactor;
|
||||
getBlendFactors(blendMode_, srcFactor, dstFactor);
|
||||
glBlendFunc(srcFactor, dstFactor);
|
||||
cachedBlendMode_ = blendMode_;
|
||||
}
|
||||
}
|
||||
|
||||
void GLPipeline::applyDepthState() {
|
||||
// 深度测试
|
||||
if (depthTest_ != cachedDepthTest_) {
|
||||
if (depthTest_) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
cachedDepthTest_ = depthTest_;
|
||||
}
|
||||
|
||||
// 深度写入
|
||||
if (depthWrite_ != cachedDepthWrite_) {
|
||||
glDepthMask(depthWrite_ ? GL_TRUE : GL_FALSE);
|
||||
cachedDepthWrite_ = depthWrite_;
|
||||
}
|
||||
|
||||
// 深度函数
|
||||
if (depthFunc_ != cachedDepthFunc_) {
|
||||
glDepthFunc(convertDepthFunc(depthFunc_));
|
||||
cachedDepthFunc_ = depthFunc_;
|
||||
}
|
||||
}
|
||||
|
||||
void GLPipeline::applyCullState() {
|
||||
// 检查裁剪模式是否改变
|
||||
if (cullMode_ != cachedCullMode_) {
|
||||
if (cullMode_ == CullMode::None) {
|
||||
glDisable(GL_CULL_FACE);
|
||||
} else {
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(convertCullMode(cullMode_));
|
||||
}
|
||||
cachedCullMode_ = cullMode_;
|
||||
}
|
||||
}
|
||||
|
||||
void GLPipeline::getBlendFactors(BlendMode mode, GLenum &srcFactor,
|
||||
GLenum &dstFactor) {
|
||||
switch (mode) {
|
||||
case BlendMode::None:
|
||||
srcFactor = GL_ONE;
|
||||
dstFactor = GL_ZERO;
|
||||
break;
|
||||
case BlendMode::Alpha:
|
||||
srcFactor = GL_SRC_ALPHA;
|
||||
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
case BlendMode::Additive:
|
||||
srcFactor = GL_SRC_ALPHA;
|
||||
dstFactor = GL_ONE;
|
||||
break;
|
||||
case BlendMode::Multiply:
|
||||
srcFactor = GL_DST_COLOR;
|
||||
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
default:
|
||||
srcFactor = GL_SRC_ALPHA;
|
||||
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum GLPipeline::convertDepthFunc(DepthFunc func) {
|
||||
switch (func) {
|
||||
case DepthFunc::Never:
|
||||
return GL_NEVER;
|
||||
case DepthFunc::Less:
|
||||
return GL_LESS;
|
||||
case DepthFunc::Equal:
|
||||
return GL_EQUAL;
|
||||
case DepthFunc::LessEqual:
|
||||
return GL_LEQUAL;
|
||||
case DepthFunc::Greater:
|
||||
return GL_GREATER;
|
||||
case DepthFunc::NotEqual:
|
||||
return GL_NOTEQUAL;
|
||||
case DepthFunc::GreaterEqual:
|
||||
return GL_GEQUAL;
|
||||
case DepthFunc::Always:
|
||||
return GL_ALWAYS;
|
||||
default:
|
||||
return GL_LESS;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum GLPipeline::convertCullMode(CullMode mode) {
|
||||
switch (mode) {
|
||||
case CullMode::Front:
|
||||
return GL_FRONT;
|
||||
case CullMode::Back:
|
||||
return GL_BACK;
|
||||
case CullMode::Both:
|
||||
return GL_FRONT_AND_BACK;
|
||||
default:
|
||||
return GL_BACK;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,350 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_shader.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 构造函数,初始化着色器程序ID为0
|
||||
*/
|
||||
GLShader::GLShader() : programID_(0) {}
|
||||
|
||||
/**
|
||||
* @brief 析构函数,删除OpenGL着色器程序
|
||||
*/
|
||||
GLShader::~GLShader() {
|
||||
if (programID_ != 0) {
|
||||
glDeleteProgram(programID_);
|
||||
programID_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绑定Shader程序
|
||||
*/
|
||||
void GLShader::bind() const { glUseProgram(programID_); }
|
||||
|
||||
/**
|
||||
* @brief 解绑Shader程序
|
||||
*/
|
||||
void GLShader::unbind() const { glUseProgram(0); }
|
||||
|
||||
/**
|
||||
* @brief 设置布尔类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 布尔值
|
||||
*/
|
||||
void GLShader::setBool(const std::string &name, bool value) {
|
||||
glUniform1i(getUniformLocation(name), value ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置整数类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 整数值
|
||||
*/
|
||||
void GLShader::setInt(const std::string &name, int value) {
|
||||
glUniform1i(getUniformLocation(name), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置浮点类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 浮点值
|
||||
*/
|
||||
void GLShader::setFloat(const std::string &name, float value) {
|
||||
glUniform1f(getUniformLocation(name), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 二维向量值
|
||||
*/
|
||||
void GLShader::setVec2(const std::string &name, const glm::vec2 &value) {
|
||||
glUniform2fv(getUniformLocation(name), 1, &value[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 三维向量值
|
||||
*/
|
||||
void GLShader::setVec3(const std::string &name, const glm::vec3 &value) {
|
||||
glUniform3fv(getUniformLocation(name), 1, &value[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 四维向量值
|
||||
*/
|
||||
void GLShader::setVec4(const std::string &name, const glm::vec4 &value) {
|
||||
glUniform4fv(getUniformLocation(name), 1, &value[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置4x4矩阵类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 4x4矩阵值
|
||||
*/
|
||||
void GLShader::setMat4(const std::string &name, const glm::mat4 &value) {
|
||||
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, &value[0][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置颜色类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param color 颜色值
|
||||
*/
|
||||
void GLShader::setColor(const std::string &name, const Color &color) {
|
||||
glUniform4f(getUniformLocation(name), color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从源码编译Shader
|
||||
* @param vertexSource 顶点着色器源码
|
||||
* @param fragmentSource 片段着色器源码
|
||||
* @return 编译成功返回true,失败返回false
|
||||
*/
|
||||
bool GLShader::compileFromSource(const char *vertexSource,
|
||||
const char *fragmentSource) {
|
||||
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource);
|
||||
if (vertexShader == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource);
|
||||
if (fragmentShader == 0) {
|
||||
glDeleteShader(vertexShader);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (programID_ != 0) {
|
||||
glDeleteProgram(programID_);
|
||||
uniformCache_.clear();
|
||||
}
|
||||
|
||||
programID_ = glCreateProgram();
|
||||
glAttachShader(programID_, vertexShader);
|
||||
glAttachShader(programID_, fragmentShader);
|
||||
glLinkProgram(programID_);
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
|
||||
E2D_LOG_ERROR("着色器程序链接失败: {}", infoLog);
|
||||
glDeleteProgram(programID_);
|
||||
programID_ = 0;
|
||||
}
|
||||
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
|
||||
return success == GL_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从二进制数据创建Shader
|
||||
* @param binary 二进制数据
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*/
|
||||
bool GLShader::compileFromBinary(const std::vector<uint8_t> &binary) {
|
||||
if (binary.empty()) {
|
||||
E2D_LOG_ERROR("二进制数据为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
GLint numFormats = 0;
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numFormats);
|
||||
if (numFormats == 0) {
|
||||
E2D_LOG_ERROR("不支持程序二进制格式");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (programID_ != 0) {
|
||||
glDeleteProgram(programID_);
|
||||
uniformCache_.clear();
|
||||
}
|
||||
|
||||
programID_ = glCreateProgram();
|
||||
|
||||
GLenum binaryFormat = 0;
|
||||
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS,
|
||||
reinterpret_cast<GLint *>(&binaryFormat));
|
||||
|
||||
glProgramBinary(programID_, binaryFormat, binary.data(),
|
||||
static_cast<GLsizei>(binary.size()));
|
||||
|
||||
GLint success = 0;
|
||||
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
|
||||
E2D_LOG_ERROR("从二进制加载着色器失败: {}", infoLog);
|
||||
glDeleteProgram(programID_);
|
||||
programID_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取Shader二进制数据
|
||||
* @param outBinary 输出的二进制数据
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool GLShader::getBinary(std::vector<uint8_t> &outBinary) {
|
||||
if (programID_ == 0) {
|
||||
E2D_LOG_WARN("无法获取二进制数据: 着色器程序为 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
GLint binaryLength = 0;
|
||||
glGetProgramiv(programID_, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
|
||||
|
||||
E2D_LOG_DEBUG("着色器二进制数据长度: {}", binaryLength);
|
||||
|
||||
if (binaryLength <= 0) {
|
||||
E2D_LOG_WARN("着色器二进制数据长度为 0 或负数");
|
||||
return false;
|
||||
}
|
||||
|
||||
outBinary.resize(binaryLength);
|
||||
|
||||
GLenum binaryFormat = 0;
|
||||
GLsizei actualLength = 0;
|
||||
glGetProgramBinary(programID_, binaryLength, &actualLength, &binaryFormat,
|
||||
outBinary.data());
|
||||
|
||||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
E2D_LOG_ERROR("glGetProgramBinary 失败,错误码: {}", err);
|
||||
outBinary.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualLength == 0) {
|
||||
E2D_LOG_WARN("glGetProgramBinary 返回 0 字节");
|
||||
outBinary.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualLength != binaryLength) {
|
||||
outBinary.resize(actualLength);
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("着色器二进制数据已获取: {} 字节, 格式: {}", actualLength,
|
||||
binaryFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 编译单个着色器
|
||||
* @param type 着色器类型
|
||||
* @param source 着色器源码
|
||||
* @return 着色器ID,失败返回0
|
||||
*/
|
||||
GLuint GLShader::compileShader(GLenum type, const char *source) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
|
||||
E2D_LOG_ERROR("着色器编译失败: {}", infoLog);
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取uniform位置
|
||||
* @param name uniform变量名
|
||||
* @return uniform位置
|
||||
*/
|
||||
GLint GLShader::getUniformLocation(const std::string &name) {
|
||||
auto it = uniformCache_.find(name);
|
||||
if (it != uniformCache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
GLint location = glGetUniformLocation(programID_, name.c_str());
|
||||
uniformCache_[name] = location;
|
||||
return location;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GLShaderFactory 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 从源码创建Shader
|
||||
* @param name Shader名称
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
Ptr<IShader> GLShaderFactory::createFromSource(const std::string &name,
|
||||
const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
shader->setName(name);
|
||||
|
||||
if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) {
|
||||
E2D_LOG_ERROR("从源码编译着色器失败: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从缓存二进制创建Shader
|
||||
* @param name Shader名称
|
||||
* @param binary 编译后的二进制数据
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
Ptr<IShader>
|
||||
GLShaderFactory::createFromBinary(const std::string &name,
|
||||
const std::vector<uint8_t> &binary) {
|
||||
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
shader->setName(name);
|
||||
|
||||
if (!shader->compileFromBinary(binary)) {
|
||||
E2D_LOG_ERROR("从二进制创建着色器失败: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取Shader的二进制数据
|
||||
* @param shader Shader实例
|
||||
* @param outBinary 输出的二进制数据
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool GLShaderFactory::getShaderBinary(const IShader &shader,
|
||||
std::vector<uint8_t> &outBinary) {
|
||||
const GLShader *glShader = dynamic_cast<const GLShader *>(&shader);
|
||||
if (!glShader) {
|
||||
E2D_LOG_ERROR("着色器不是 GLShader 实例");
|
||||
return false;
|
||||
}
|
||||
|
||||
return const_cast<GLShader *>(glShader)->getBinary(outBinary);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,230 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_sprite_batch.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
GLSpriteBatch::GLSpriteBatch()
|
||||
: vao_(0), currentTexture_(nullptr), drawCallCount_(0) {}
|
||||
|
||||
GLSpriteBatch::~GLSpriteBatch() { shutdown(); }
|
||||
|
||||
bool GLSpriteBatch::init() {
|
||||
// 从ShaderManager获取精灵着色器
|
||||
shader_ = ShaderManager::getInstance().getBuiltin("sprite");
|
||||
if (!shader_) {
|
||||
E2D_LOG_ERROR("获取内置精灵着色器失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建 VAO
|
||||
glGenVertexArrays(1, &vao_);
|
||||
glBindVertexArray(vao_);
|
||||
|
||||
// 初始化 VBO(顶点缓冲区)- 动态使用模式
|
||||
BufferDesc vboDesc;
|
||||
vboDesc.type = BufferType::Vertex;
|
||||
vboDesc.usage = BufferUsage::Dynamic;
|
||||
vboDesc.size = SpriteBatch::MAX_VERTICES * sizeof(SpriteVertex);
|
||||
vboDesc.initialData = nullptr;
|
||||
if (!vbo_.init(vboDesc)) {
|
||||
E2D_LOG_ERROR("初始化精灵批处理 VBO 失败");
|
||||
return false;
|
||||
}
|
||||
vbo_.bind();
|
||||
|
||||
// 设置顶点属性
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(
|
||||
0, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
||||
reinterpret_cast<void *>(offsetof(SpriteVertex, position)));
|
||||
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(
|
||||
1, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
||||
reinterpret_cast<void *>(offsetof(SpriteVertex, texCoord)));
|
||||
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(
|
||||
2, 4, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
||||
reinterpret_cast<void *>(offsetof(SpriteVertex, color)));
|
||||
|
||||
// 初始化 EBO(索引缓冲区)- 静态使用模式
|
||||
BufferDesc eboDesc;
|
||||
eboDesc.type = BufferType::Index;
|
||||
eboDesc.usage = BufferUsage::Static;
|
||||
eboDesc.size = batch_.getIndices().size() * sizeof(uint16_t);
|
||||
eboDesc.initialData = batch_.getIndices().data();
|
||||
if (!ebo_.init(eboDesc)) {
|
||||
E2D_LOG_ERROR("初始化精灵批处理 EBO 失败");
|
||||
return false;
|
||||
}
|
||||
ebo_.bind();
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLSpriteBatch::shutdown() {
|
||||
// 使用 GLBuffer::shutdown() 释放缓冲区资源
|
||||
vbo_.shutdown();
|
||||
ebo_.shutdown();
|
||||
|
||||
if (vao_ != 0) {
|
||||
glDeleteVertexArrays(1, &vao_);
|
||||
vao_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GLSpriteBatch::begin(const glm::mat4 &viewProjection) {
|
||||
batch_.begin(viewProjection);
|
||||
batches_.clear();
|
||||
currentTexture_ = nullptr;
|
||||
drawCallCount_ = 0;
|
||||
// 保存 viewProjection 矩阵供后续使用
|
||||
viewProjection_ = viewProjection;
|
||||
|
||||
// 绑定 VAO 和缓冲区
|
||||
glBindVertexArray(vao_);
|
||||
vbo_.bind();
|
||||
ebo_.bind();
|
||||
}
|
||||
|
||||
void GLSpriteBatch::begin(const glm::mat4 &viewProjection, Ptr<IShader> shader) {
|
||||
// 设置自定义着色器
|
||||
if (shader) {
|
||||
shader_ = shader;
|
||||
}
|
||||
begin(viewProjection);
|
||||
}
|
||||
|
||||
void GLSpriteBatch::setShader(Ptr<IShader> shader) {
|
||||
// 如果当前有未提交的批次,先提交
|
||||
if (batch_.getSpriteCount() > 0) {
|
||||
flush();
|
||||
}
|
||||
shader_ = shader;
|
||||
}
|
||||
|
||||
void GLSpriteBatch::end() {
|
||||
if (batch_.getSpriteCount() > 0) {
|
||||
flush();
|
||||
}
|
||||
|
||||
// 解绑缓冲区
|
||||
vbo_.unbind();
|
||||
ebo_.unbind();
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) {
|
||||
const GLTexture *glTex = dynamic_cast<const GLTexture *>(&texture);
|
||||
if (!glTex) {
|
||||
E2D_LOG_WARN("精灵批处理纹理类型无效");
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果纹理改变或批次已满,先提交当前批次
|
||||
if (currentTexture_ != glTex || batch_.needsFlush()) {
|
||||
if (batch_.getSpriteCount() > 0) {
|
||||
submitBatch();
|
||||
}
|
||||
currentTexture_ = glTex;
|
||||
}
|
||||
|
||||
// 使用 batch 层生成顶点
|
||||
batch_.draw(data);
|
||||
}
|
||||
|
||||
void GLSpriteBatch::drawBatch(const Texture &texture,
|
||||
const std::vector<SpriteData> &sprites) {
|
||||
const GLTexture *glTex = dynamic_cast<const GLTexture *>(&texture);
|
||||
if (!glTex) {
|
||||
E2D_LOG_WARN("精灵批处理纹理类型无效");
|
||||
return;
|
||||
}
|
||||
|
||||
// 批量处理精灵
|
||||
for (const auto &data : sprites) {
|
||||
// 如果纹理改变或批次已满,先提交当前批次
|
||||
if (currentTexture_ != glTex || batch_.needsFlush()) {
|
||||
if (batch_.getSpriteCount() > 0) {
|
||||
submitBatch();
|
||||
}
|
||||
currentTexture_ = glTex;
|
||||
}
|
||||
|
||||
// 使用 batch 层生成顶点
|
||||
batch_.draw(data);
|
||||
}
|
||||
}
|
||||
|
||||
void GLSpriteBatch::submitBatch() {
|
||||
if (batch_.getSpriteCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录批次信息
|
||||
Batch batchInfo;
|
||||
batchInfo.texture = currentTexture_;
|
||||
batchInfo.startVertex = 0; // 每次提交都是新的缓冲区
|
||||
batchInfo.vertexCount = batch_.getSpriteCount() * 4;
|
||||
batches_.push_back(batchInfo);
|
||||
|
||||
// 绑定着色器并设置uniform
|
||||
if (shader_) {
|
||||
shader_->bind();
|
||||
|
||||
// 只提供需要动态计算的值,其他值使用JSON中定义的默认值
|
||||
UniformValueMap uniformValues;
|
||||
uniformValues["u_viewProjection"] = viewProjection_;
|
||||
|
||||
// 合并额外的uniform值(如SDF字体的u_textureSize)
|
||||
for (const auto& [name, value] : extraUniforms_) {
|
||||
uniformValues[name] = value;
|
||||
}
|
||||
|
||||
// 使用ShaderManager自动应用uniform值(未提供的值使用JSON中的默认值)
|
||||
// 使用着色器自己的名称(从JSON中解析的name字段)
|
||||
ShaderManager::getInstance().applyUniforms(shader_, shader_->getName(),
|
||||
uniformValues);
|
||||
|
||||
// 设置纹理采样器
|
||||
shader_->setInt("u_texture", 0);
|
||||
}
|
||||
|
||||
// 上传顶点数据 - 使用 orphaning 策略优化动态缓冲区
|
||||
// 通过传入 nullptr 进行 orphaning,告诉驱动器可以丢弃旧缓冲区并分配新内存
|
||||
// 这样可以避免 GPU 等待,提高性能
|
||||
size_t vertexDataSize = batch_.getVertices().size() * sizeof(SpriteVertex);
|
||||
vbo_.setData(nullptr, vertexDataSize); // orphaning
|
||||
vbo_.updateData(batch_.getVertices().data(), 0, vertexDataSize);
|
||||
|
||||
// 绘制
|
||||
currentTexture_->bind(0);
|
||||
|
||||
size_t indexCount = batch_.getSpriteCount() * 6;
|
||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indexCount),
|
||||
GL_UNSIGNED_SHORT, nullptr);
|
||||
|
||||
drawCallCount_++;
|
||||
|
||||
// 清空 batch 层,准备下一批
|
||||
batch_.clear();
|
||||
}
|
||||
|
||||
void GLSpriteBatch::flush() {
|
||||
// 提交最后的批次
|
||||
if (batch_.getSpriteCount() > 0) {
|
||||
submitBatch();
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
batches_.clear();
|
||||
currentTexture_ = nullptr;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,506 +0,0 @@
|
|||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/memory/gpu_context.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <cstring>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <fstream>
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// KTX 文件头结构
|
||||
// ============================================================================
|
||||
#pragma pack(push, 1)
|
||||
struct KTXHeader {
|
||||
uint8_t identifier[12];
|
||||
uint32_t endianness;
|
||||
uint32_t glType;
|
||||
uint32_t glTypeSize;
|
||||
uint32_t glFormat;
|
||||
uint32_t glInternalFormat;
|
||||
uint32_t glBaseInternalFormat;
|
||||
uint32_t pixelWidth;
|
||||
uint32_t pixelHeight;
|
||||
uint32_t pixelDepth;
|
||||
uint32_t numberOfArrayElements;
|
||||
uint32_t numberOfFaces;
|
||||
uint32_t numberOfMipmapLevels;
|
||||
uint32_t bytesOfKeyValueData;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
// KTX 文件标识符
|
||||
static const uint8_t KTX_IDENTIFIER[12] = {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31,
|
||||
0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
|
||||
// ============================================================================
|
||||
// DDS 文件头结构
|
||||
// ============================================================================
|
||||
#pragma pack(push, 1)
|
||||
struct DDSPixelFormat {
|
||||
uint32_t size;
|
||||
uint32_t flags;
|
||||
uint32_t fourCC;
|
||||
uint32_t rgbBitCount;
|
||||
uint32_t rBitMask;
|
||||
uint32_t gBitMask;
|
||||
uint32_t bBitMask;
|
||||
uint32_t aBitMask;
|
||||
};
|
||||
|
||||
struct DDSHeader {
|
||||
uint32_t magic;
|
||||
uint32_t size;
|
||||
uint32_t flags;
|
||||
uint32_t height;
|
||||
uint32_t width;
|
||||
uint32_t pitchOrLinearSize;
|
||||
uint32_t depth;
|
||||
uint32_t mipMapCount;
|
||||
uint32_t reserved1[11];
|
||||
DDSPixelFormat pixelFormat;
|
||||
uint32_t caps;
|
||||
uint32_t caps2;
|
||||
uint32_t caps3;
|
||||
uint32_t caps4;
|
||||
uint32_t reserved2;
|
||||
};
|
||||
|
||||
struct DDSHeaderDXT10 {
|
||||
uint32_t dxgiFormat;
|
||||
uint32_t resourceDimension;
|
||||
uint32_t miscFlag;
|
||||
uint32_t arraySize;
|
||||
uint32_t miscFlags2;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static constexpr uint32_t DDS_MAGIC = 0x20534444; // "DDS "
|
||||
static constexpr uint32_t DDPF_FOURCC = 0x04;
|
||||
|
||||
/**
|
||||
* @brief 生成四字符代码(FourCC)
|
||||
* @param a 第一个字符
|
||||
* @param b 第二个字符
|
||||
* @param c 第三个字符
|
||||
* @param d 第四个字符
|
||||
* @return 组合后的32位无符号整数
|
||||
*/
|
||||
static uint32_t makeFourCC(char a, char b, char c, char d) {
|
||||
return static_cast<uint32_t>(a) | (static_cast<uint32_t>(b) << 8) |
|
||||
(static_cast<uint32_t>(c) << 16) | (static_cast<uint32_t>(d) << 24);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GLTexture 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 从像素数据构造纹理对象
|
||||
* @param width 纹理宽度(像素)
|
||||
* @param height 纹理高度(像素)
|
||||
* @param pixels 像素数据指针,可为nullptr创建空纹理
|
||||
* @param channels 颜色通道数(1=R, 3=RGB, 4=RGBA)
|
||||
*/
|
||||
GLTexture::GLTexture(int width, int height, const uint8_t *pixels, int channels)
|
||||
: textureID_(0), width_(width), height_(height), channels_(channels),
|
||||
format_(PixelFormat::RGBA8), dataSize_(0) {
|
||||
// 保存像素数据用于生成遮罩
|
||||
if (pixels) {
|
||||
pixelData_.resize(width * height * channels);
|
||||
std::memcpy(pixelData_.data(), pixels, pixelData_.size());
|
||||
}
|
||||
createTexture(pixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从文件路径构造纹理对象
|
||||
* @param filepath 纹理文件路径,支持普通图片格式和压缩格式(KTX/DDS)
|
||||
*/
|
||||
GLTexture::GLTexture(const std::string &filepath)
|
||||
: textureID_(0), width_(0), height_(0), channels_(0),
|
||||
format_(PixelFormat::RGBA8), dataSize_(0) {
|
||||
// 检查是否为压缩纹理格式
|
||||
std::string ext = filepath.substr(filepath.find_last_of('.') + 1);
|
||||
if (ext == "ktx" || ext == "KTX") {
|
||||
loadCompressed(filepath);
|
||||
return;
|
||||
}
|
||||
if (ext == "dds" || ext == "DDS") {
|
||||
loadCompressed(filepath);
|
||||
return;
|
||||
}
|
||||
|
||||
// 不翻转图片,保持原始方向
|
||||
stbi_set_flip_vertically_on_load(false);
|
||||
uint8_t *data = stbi_load(filepath.c_str(), &width_, &height_, &channels_, 0);
|
||||
if (data) {
|
||||
// 保存像素数据用于生成遮罩
|
||||
pixelData_.resize(width_ * height_ * channels_);
|
||||
std::memcpy(pixelData_.data(), data, pixelData_.size());
|
||||
|
||||
createTexture(data);
|
||||
stbi_image_free(data);
|
||||
} else {
|
||||
E2D_LOG_ERROR("加载纹理失败: {}", filepath);
|
||||
}
|
||||
}
|
||||
|
||||
GLTexture::~GLTexture() {
|
||||
if (textureID_ != 0) {
|
||||
// 检查 GPU 上下文是否仍然有效
|
||||
// 如果 OpenGL 上下文已销毁,则跳过 glDeleteTextures 调用
|
||||
if (GPUContext::get().isValid()) {
|
||||
glDeleteTextures(1, &textureID_);
|
||||
}
|
||||
// VRAM 跟踪: 释放纹理显存(无论上下文是否有效都需要更新统计)
|
||||
if (dataSize_ > 0) {
|
||||
VRAMMgr::get().freeTexture(dataSize_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLTexture::setFilter(bool linear) {
|
||||
bind();
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
linear ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
|
||||
linear ? GL_LINEAR : GL_NEAREST);
|
||||
}
|
||||
|
||||
void GLTexture::setWrap(bool repeat) {
|
||||
bind();
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
|
||||
repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
|
||||
repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
void GLTexture::bind(unsigned int slot) const {
|
||||
glActiveTexture(GL_TEXTURE0 + slot);
|
||||
glBindTexture(GL_TEXTURE_2D, textureID_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解绑当前纹理
|
||||
*/
|
||||
void GLTexture::unbind() const { glBindTexture(GL_TEXTURE_2D, 0); }
|
||||
|
||||
/**
|
||||
* @brief 创建OpenGL纹理对象并上传像素数据
|
||||
* @param pixels 像素数据指针
|
||||
*/
|
||||
void GLTexture::createTexture(const uint8_t *pixels) {
|
||||
GLenum format = GL_RGBA;
|
||||
GLenum internalFormat = GL_RGBA8;
|
||||
int unpackAlignment = 4;
|
||||
if (channels_ == 1) {
|
||||
format = GL_RED;
|
||||
internalFormat = GL_R8;
|
||||
unpackAlignment = 1;
|
||||
format_ = PixelFormat::R8;
|
||||
} else if (channels_ == 3) {
|
||||
format = GL_RGB;
|
||||
internalFormat = GL_RGB8;
|
||||
unpackAlignment = 1;
|
||||
format_ = PixelFormat::RGB8;
|
||||
} else if (channels_ == 4) {
|
||||
format = GL_RGBA;
|
||||
internalFormat = GL_RGBA8;
|
||||
unpackAlignment = 4;
|
||||
format_ = PixelFormat::RGBA8;
|
||||
}
|
||||
|
||||
glGenTextures(1, &textureID_);
|
||||
bind();
|
||||
|
||||
GLint prevUnpackAlignment = 4;
|
||||
glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevUnpackAlignment);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, unpackAlignment);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width_, height_, 0, format,
|
||||
GL_UNSIGNED_BYTE, pixels);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, prevUnpackAlignment);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
// 使用 NEAREST 过滤器,更适合像素艺术风格的精灵
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
// VRAM 跟踪
|
||||
dataSize_ = static_cast<size_t>(width_ * height_ * channels_);
|
||||
VRAMMgr::get().allocTexture(dataSize_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 压缩纹理加载
|
||||
// ============================================================================
|
||||
|
||||
bool GLTexture::loadCompressed(const std::string &filepath) {
|
||||
std::string ext = filepath.substr(filepath.find_last_of('.') + 1);
|
||||
if (ext == "ktx" || ext == "KTX") {
|
||||
return loadKTX(filepath);
|
||||
}
|
||||
if (ext == "dds" || ext == "DDS") {
|
||||
return loadDDS(filepath);
|
||||
}
|
||||
E2D_LOG_ERROR("不支持的压缩纹理格式: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加载KTX格式压缩纹理
|
||||
* @param filepath KTX文件路径
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool GLTexture::loadKTX(const std::string &filepath) {
|
||||
std::ifstream file(filepath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("打开 KTX 文件失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
KTXHeader header;
|
||||
file.read(reinterpret_cast<char *>(&header), sizeof(header));
|
||||
if (!file) {
|
||||
E2D_LOG_ERROR("读取 KTX 文件头失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证标识符
|
||||
if (std::memcmp(header.identifier, KTX_IDENTIFIER, 12) != 0) {
|
||||
E2D_LOG_ERROR("无效的 KTX 标识符: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
width_ = static_cast<int>(header.pixelWidth);
|
||||
height_ = static_cast<int>(header.pixelHeight);
|
||||
channels_ = 4; // 压缩纹理通常解压为 RGBA
|
||||
|
||||
// 确定压缩格式
|
||||
GLenum glInternalFormat = header.glInternalFormat;
|
||||
switch (glInternalFormat) {
|
||||
case GL_COMPRESSED_RGB8_ETC2:
|
||||
format_ = PixelFormat::ETC2_RGB8;
|
||||
channels_ = 3;
|
||||
break;
|
||||
case GL_COMPRESSED_RGBA8_ETC2_EAC:
|
||||
format_ = PixelFormat::ETC2_RGBA8;
|
||||
break;
|
||||
case GL_COMPRESSED_RGBA_ASTC_4x4:
|
||||
format_ = PixelFormat::ASTC_4x4;
|
||||
break;
|
||||
case GL_COMPRESSED_RGBA_ASTC_6x6:
|
||||
format_ = PixelFormat::ASTC_6x6;
|
||||
break;
|
||||
case GL_COMPRESSED_RGBA_ASTC_8x8:
|
||||
format_ = PixelFormat::ASTC_8x8;
|
||||
break;
|
||||
default:
|
||||
E2D_LOG_ERROR("不支持的 KTX 内部格式: {:#06x}", glInternalFormat);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 跳过 key-value 数据
|
||||
file.seekg(header.bytesOfKeyValueData, std::ios::cur);
|
||||
|
||||
// 读取第一个 mipmap level
|
||||
uint32_t imageSize = 0;
|
||||
file.read(reinterpret_cast<char *>(&imageSize), sizeof(imageSize));
|
||||
if (!file || imageSize == 0) {
|
||||
E2D_LOG_ERROR("读取 KTX 图像大小失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> compressedData(imageSize);
|
||||
file.read(reinterpret_cast<char *>(compressedData.data()), imageSize);
|
||||
if (!file) {
|
||||
E2D_LOG_ERROR("读取 KTX 图像数据失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建 GL 纹理
|
||||
glGenTextures(1, &textureID_);
|
||||
bind();
|
||||
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width_, height_, 0,
|
||||
static_cast<GLsizei>(imageSize),
|
||||
compressedData.data());
|
||||
|
||||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
E2D_LOG_ERROR("KTX 纹理上传失败: {:#06x}", err);
|
||||
glDeleteTextures(1, &textureID_);
|
||||
textureID_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// VRAM 跟踪
|
||||
dataSize_ = imageSize;
|
||||
VRAMMgr::get().allocTexture(dataSize_);
|
||||
|
||||
E2D_LOG_INFO("已加载 KTX 压缩纹理: {} ({}x{}, 格式={:#06x})",
|
||||
filepath, width_, height_, glInternalFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加载DDS格式压缩纹理
|
||||
* @param filepath DDS文件路径
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool GLTexture::loadDDS(const std::string &filepath) {
|
||||
std::ifstream file(filepath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("打开 DDS 文件失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
DDSHeader header;
|
||||
file.read(reinterpret_cast<char *>(&header), sizeof(header));
|
||||
if (!file) {
|
||||
E2D_LOG_ERROR("读取 DDS 文件头失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.magic != DDS_MAGIC) {
|
||||
E2D_LOG_ERROR("无效的 DDS 魔数: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
width_ = static_cast<int>(header.width);
|
||||
height_ = static_cast<int>(header.height);
|
||||
channels_ = 4;
|
||||
|
||||
GLenum glInternalFormat = 0;
|
||||
|
||||
// 检查 DX10 扩展头
|
||||
if ((header.pixelFormat.flags & DDPF_FOURCC) &&
|
||||
header.pixelFormat.fourCC == makeFourCC('D', 'X', '1', '0')) {
|
||||
DDSHeaderDXT10 dx10Header;
|
||||
file.read(reinterpret_cast<char *>(&dx10Header), sizeof(dx10Header));
|
||||
if (!file) {
|
||||
E2D_LOG_ERROR("读取 DDS DX10 文件头失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// DXGI_FORMAT 映射到 GL 格式
|
||||
switch (dx10Header.dxgiFormat) {
|
||||
case 147: // DXGI_FORMAT_ETC2_RGB8
|
||||
glInternalFormat = GL_COMPRESSED_RGB8_ETC2;
|
||||
format_ = PixelFormat::ETC2_RGB8;
|
||||
channels_ = 3;
|
||||
break;
|
||||
case 148: // DXGI_FORMAT_ETC2_RGBA8
|
||||
glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC;
|
||||
format_ = PixelFormat::ETC2_RGBA8;
|
||||
break;
|
||||
default:
|
||||
E2D_LOG_ERROR("不支持的 DDS DX10 格式: {}", dx10Header.dxgiFormat);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
E2D_LOG_ERROR("DDS 文件未使用 DX10 扩展,不支持: {}",
|
||||
filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算压缩数据大小
|
||||
size_t blockSize = (glInternalFormat == GL_COMPRESSED_RGB8_ETC2) ? 8 : 16;
|
||||
size_t blocksWide = (width_ + 3) / 4;
|
||||
size_t blocksHigh = (height_ + 3) / 4;
|
||||
size_t imageSize = blocksWide * blocksHigh * blockSize;
|
||||
|
||||
std::vector<uint8_t> compressedData(imageSize);
|
||||
file.read(reinterpret_cast<char *>(compressedData.data()), imageSize);
|
||||
if (!file) {
|
||||
E2D_LOG_ERROR("读取 DDS 图像数据失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建 GL 纹理
|
||||
glGenTextures(1, &textureID_);
|
||||
bind();
|
||||
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width_, height_, 0,
|
||||
static_cast<GLsizei>(imageSize),
|
||||
compressedData.data());
|
||||
|
||||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
E2D_LOG_ERROR("DDS 纹理上传失败: {:#06x}", err);
|
||||
glDeleteTextures(1, &textureID_);
|
||||
textureID_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// VRAM 跟踪
|
||||
dataSize_ = imageSize;
|
||||
VRAMMgr::get().allocTexture(dataSize_);
|
||||
|
||||
E2D_LOG_INFO("已加载 DDS 压缩纹理: {} ({}x{})", filepath, width_,
|
||||
height_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLTexture::generateAlphaMask() {
|
||||
if (pixelData_.empty() || width_ <= 0 || height_ <= 0) {
|
||||
E2D_LOG_WARN("无法生成透明遮罩: 没有可用的像素数据");
|
||||
return;
|
||||
}
|
||||
|
||||
alphaMask_ = std::make_unique<AlphaMask>(AlphaMask::createFromPixels(
|
||||
pixelData_.data(), width_, height_, channels_));
|
||||
|
||||
E2D_LOG_DEBUG("已为纹理生成透明遮罩: {}x{}", width_, height_);
|
||||
}
|
||||
|
||||
PixelFormat GLTexture::getFormat() const { return format_; }
|
||||
|
||||
/**
|
||||
* @brief 静态工厂方法,创建指定格式的空纹理
|
||||
* @param width 纹理宽度
|
||||
* @param height 纹理高度
|
||||
* @param format 像素格式
|
||||
* @return 创建的纹理智能指针
|
||||
*/
|
||||
Ptr<Texture> GLTexture::create(int width, int height, PixelFormat format) {
|
||||
int channels = 4;
|
||||
switch (format) {
|
||||
case PixelFormat::R8:
|
||||
channels = 1;
|
||||
break;
|
||||
case PixelFormat::RG8:
|
||||
channels = 2;
|
||||
break;
|
||||
case PixelFormat::RGB8:
|
||||
channels = 3;
|
||||
break;
|
||||
case PixelFormat::RGBA8:
|
||||
channels = 4;
|
||||
break;
|
||||
default:
|
||||
channels = 4;
|
||||
break;
|
||||
}
|
||||
return makePtr<GLTexture>(width, height, nullptr, channels);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#include <extra2d/graphics/backends/vulkan/vk_renderer.h>
|
||||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace graphics {
|
||||
|
||||
namespace {
|
||||
static bool s_vulkanBackendRegistered = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化 Vulkan 后端注册
|
||||
*/
|
||||
void initVulkanBackend() {
|
||||
if (s_vulkanBackendRegistered) {
|
||||
return;
|
||||
}
|
||||
s_vulkanBackendRegistered = true;
|
||||
|
||||
BackendFactory::reg(
|
||||
"vulkan",
|
||||
[]() -> UniquePtr<RenderBackend> {
|
||||
return makeUnique<VulkanRenderer>();
|
||||
},
|
||||
{"sdl2", "glfw"}
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct VulkanBackendAutoReg {
|
||||
VulkanBackendAutoReg() {
|
||||
initVulkanBackend();
|
||||
}
|
||||
};
|
||||
static VulkanBackendAutoReg s_vulkanAutoReg;
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/vulkan/vk_renderer.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
VulkanRenderer::VulkanRenderer() = default;
|
||||
|
||||
VulkanRenderer::~VulkanRenderer() { shutdown(); }
|
||||
|
||||
bool VulkanRenderer::init(IWindow *window) {
|
||||
E2D_LOG_WARN("Vulkan 渲染器尚未完全实现");
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanRenderer::shutdown() { initialized_ = false; }
|
||||
|
||||
void VulkanRenderer::beginFrame(const Color &clearColor) {
|
||||
// TODO: 实现Vulkan帧开始
|
||||
}
|
||||
|
||||
void VulkanRenderer::endFrame() {
|
||||
// TODO: 实现Vulkan帧结束
|
||||
}
|
||||
|
||||
void VulkanRenderer::setViewport(int x, int y, int width, int height) {
|
||||
// TODO: 实现视口设置
|
||||
}
|
||||
|
||||
void VulkanRenderer::setVSync(bool enabled) {
|
||||
// TODO: 实现垂直同步设置
|
||||
}
|
||||
|
||||
void VulkanRenderer::setBlendMode(BlendMode mode) {
|
||||
// TODO: 实现混合模式设置
|
||||
}
|
||||
|
||||
void VulkanRenderer::setViewProjection(const glm::mat4 &matrix) {
|
||||
// TODO: 实现视图投影矩阵设置
|
||||
}
|
||||
|
||||
void VulkanRenderer::pushTransform(const glm::mat4 &transform) {
|
||||
// TODO: 实现变换矩阵入栈
|
||||
}
|
||||
|
||||
void VulkanRenderer::popTransform() {
|
||||
// TODO: 实现变换矩阵出栈
|
||||
}
|
||||
|
||||
glm::mat4 VulkanRenderer::getCurrentTransform() const {
|
||||
return glm::mat4(1.0f);
|
||||
}
|
||||
|
||||
Ptr<Texture> VulkanRenderer::createTexture(int width, int height,
|
||||
const uint8_t *pixels,
|
||||
int channels) {
|
||||
// TODO: 实现Vulkan纹理创建
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<Texture> VulkanRenderer::loadTexture(const std::string &filepath) {
|
||||
// TODO: 实现Vulkan纹理加载
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VulkanRenderer::beginSpriteBatch() {
|
||||
// TODO: 实现精灵批处理开始
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawSprite(const Texture &texture, const Rect &destRect,
|
||||
const Rect &srcRect, const Color &tint,
|
||||
float rotation, const Vec2 &anchor) {
|
||||
// TODO: 实现精灵绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawSprite(const Texture &texture, const Vec2 &position,
|
||||
const Color &tint) {
|
||||
// TODO: 实现简化精灵绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::endSpriteBatch() {
|
||||
// TODO: 实现精灵批处理结束
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawLine(const Vec2 &start, const Vec2 &end,
|
||||
const Color &color, float width) {
|
||||
// TODO: 实现线条绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawRect(const Rect &rect, const Color &color,
|
||||
float width) {
|
||||
// TODO: 实现矩形边框绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::fillRect(const Rect &rect, const Color &color) {
|
||||
// TODO: 实现矩形填充
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawCircle(const Vec2 ¢er, float radius,
|
||||
const Color &color, int segments, float width) {
|
||||
// TODO: 实现圆形边框绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::fillCircle(const Vec2 ¢er, float radius,
|
||||
const Color &color, int segments) {
|
||||
// TODO: 实现圆形填充
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2,
|
||||
const Vec2 &p3, const Color &color,
|
||||
float width) {
|
||||
// TODO: 实现三角形边框绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2,
|
||||
const Vec2 &p3, const Color &color) {
|
||||
// TODO: 实现三角形填充
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color, float width) {
|
||||
// TODO: 实现多边形边框绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::fillPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color) {
|
||||
// TODO: 实现多边形填充
|
||||
}
|
||||
|
||||
Ptr<FontAtlas> VulkanRenderer::createFontAtlas(const std::string &filepath,
|
||||
int fontSize, bool useSDF) {
|
||||
// TODO: 实现字体图集创建
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text,
|
||||
const Vec2 &position, const Color &color) {
|
||||
// TODO: 实现文本绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text,
|
||||
float x, float y, const Color &color) {
|
||||
// TODO: 实现文本绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::resetStats() { stats_ = Stats{}; }
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
#include <extra2d/graphics/batch/shape_batch.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// ShapeBatch 基础实现(后端无关部分)
|
||||
// ============================================================================
|
||||
|
||||
// 这里可以添加后端无关的工具函数
|
||||
// 例如:计算圆形的顶点、三角化多边形等
|
||||
|
||||
// 计算圆形顶点
|
||||
void calculateCircleVertices(std::vector<Vec2>& outVertices,
|
||||
const Vec2& center, float radius,
|
||||
int segments, bool fill) {
|
||||
outVertices.clear();
|
||||
outVertices.reserve(fill ? segments + 1 : segments);
|
||||
|
||||
if (fill) {
|
||||
// 填充圆形:中心点 + 边缘点
|
||||
outVertices.push_back(center);
|
||||
for (int i = 0; i <= segments; ++i) {
|
||||
float angle = 2.0f * 3.14159265359f * static_cast<float>(i) / static_cast<float>(segments);
|
||||
outVertices.emplace_back(
|
||||
center.x + radius * cosf(angle),
|
||||
center.y + radius * sinf(angle)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 圆形边框:只保留边缘点
|
||||
for (int i = 0; i < segments; ++i) {
|
||||
float angle = 2.0f * 3.14159265359f * static_cast<float>(i) / static_cast<float>(segments);
|
||||
outVertices.emplace_back(
|
||||
center.x + radius * cosf(angle),
|
||||
center.y + radius * sinf(angle)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算矩形顶点
|
||||
void calculateRectVertices(std::vector<Vec2>& outVertices, const Rect& rect) {
|
||||
outVertices.clear();
|
||||
outVertices.reserve(4);
|
||||
|
||||
float x1 = rect.origin.x;
|
||||
float y1 = rect.origin.y;
|
||||
float x2 = rect.origin.x + rect.size.width;
|
||||
float y2 = rect.origin.y + rect.size.height;
|
||||
|
||||
outVertices.emplace_back(x1, y1); // 左上
|
||||
outVertices.emplace_back(x2, y1); // 右上
|
||||
outVertices.emplace_back(x2, y2); // 右下
|
||||
outVertices.emplace_back(x1, y2); // 左下
|
||||
}
|
||||
|
||||
// 简单的多边形三角化(扇形三角化,适用于凸多边形)
|
||||
void triangulatePolygon(std::vector<uint16_t>& outIndices, int vertexCount) {
|
||||
outIndices.clear();
|
||||
if (vertexCount < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 扇形三角化:以第一个顶点为扇形中心
|
||||
for (int i = 1; i < vertexCount - 1; ++i) {
|
||||
outIndices.push_back(0);
|
||||
outIndices.push_back(i);
|
||||
outIndices.push_back(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/batch/sprite_batch.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// TrigLookup 实现 - 三角函数查表
|
||||
// ============================================================================
|
||||
TrigLookup::TrigLookup() {
|
||||
constexpr float PI = 3.14159265359f;
|
||||
constexpr float DEG2RAD = PI / 180.0f;
|
||||
|
||||
for (int i = 0; i < TABLE_SIZE; ++i) {
|
||||
float angle = static_cast<float>(i) * (360.0f / TABLE_SIZE) * DEG2RAD;
|
||||
sinTable_[i] = std::sin(angle);
|
||||
cosTable_[i] = std::cos(angle);
|
||||
}
|
||||
}
|
||||
|
||||
float TrigLookup::sin(int angle) const {
|
||||
// 规范化角度到 0-360
|
||||
angle = ((angle % 360) + 360) % 360;
|
||||
return sinTable_[angle * 4];
|
||||
}
|
||||
|
||||
float TrigLookup::cos(int angle) const {
|
||||
// 规范化角度到 0-360
|
||||
angle = ((angle % 360) + 360) % 360;
|
||||
return cosTable_[angle * 4];
|
||||
}
|
||||
|
||||
float TrigLookup::sinRad(float rad) const {
|
||||
constexpr float RAD2DEG = 180.0f / 3.14159265359f;
|
||||
int angle = static_cast<int>(rad * RAD2DEG);
|
||||
return sin(angle);
|
||||
}
|
||||
|
||||
float TrigLookup::cosRad(float rad) const {
|
||||
constexpr float RAD2DEG = 180.0f / 3.14159265359f;
|
||||
int angle = static_cast<int>(rad * RAD2DEG);
|
||||
return cos(angle);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SpriteBatch 实现
|
||||
// ============================================================================
|
||||
SpriteBatch::SpriteBatch() : spriteCount_(0), vpDirty_(true) {
|
||||
// 预分配顶点缓冲区
|
||||
vertices_.reserve(MAX_VERTICES);
|
||||
indices_.reserve(MAX_INDICES);
|
||||
|
||||
// 生成静态索引缓冲区
|
||||
generateIndices();
|
||||
}
|
||||
|
||||
void SpriteBatch::generateIndices() {
|
||||
indices_.clear();
|
||||
for (size_t i = 0; i < MAX_SPRITES; ++i) {
|
||||
uint16_t base = static_cast<uint16_t>(i * 4);
|
||||
// 两个三角形: (0,1,2) 和 (0,2,3)
|
||||
indices_.push_back(base + 0);
|
||||
indices_.push_back(base + 1);
|
||||
indices_.push_back(base + 2);
|
||||
indices_.push_back(base + 0);
|
||||
indices_.push_back(base + 2);
|
||||
indices_.push_back(base + 3);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteBatch::begin(const glm::mat4 &viewProjection) {
|
||||
viewProjection_ = viewProjection;
|
||||
vpDirty_ = true;
|
||||
spriteCount_ = 0;
|
||||
vertices_.clear();
|
||||
}
|
||||
|
||||
void SpriteBatch::end() {
|
||||
// 批次结束,数据已准备好供后端使用
|
||||
}
|
||||
|
||||
void SpriteBatch::draw(const SpriteData &sprite) {
|
||||
if (spriteCount_ >= MAX_SPRITES) {
|
||||
// 缓冲区已满,需要刷新
|
||||
flush();
|
||||
}
|
||||
|
||||
generateVertices(sprite, spriteCount_ * VERTICES_PER_SPRITE);
|
||||
spriteCount_++;
|
||||
}
|
||||
|
||||
void SpriteBatch::drawBatch(const std::vector<SpriteData> &sprites) {
|
||||
size_t index = 0;
|
||||
while (index < sprites.size()) {
|
||||
// 计算剩余空间
|
||||
size_t remainingSpace = MAX_SPRITES - spriteCount_;
|
||||
size_t batchSize = std::min(remainingSpace, sprites.size() - index);
|
||||
|
||||
// 批量生成顶点
|
||||
for (size_t i = 0; i < batchSize; ++i) {
|
||||
generateVertices(sprites[index + i], spriteCount_ * VERTICES_PER_SPRITE);
|
||||
spriteCount_++;
|
||||
}
|
||||
|
||||
index += batchSize;
|
||||
|
||||
// 如果缓冲区已满,需要刷新(由后端处理)
|
||||
if (spriteCount_ >= MAX_SPRITES && index < sprites.size()) {
|
||||
// 通知后端刷新,然后继续
|
||||
// 注意:这里只是准备数据,实际 GPU 提交由后端决定
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteBatch::drawImmediate(const SpriteData &sprite) {
|
||||
// 立即绘制模式:清空当前批次,只绘制这一个精灵
|
||||
clear();
|
||||
draw(sprite);
|
||||
}
|
||||
|
||||
void SpriteBatch::generateVertices(const SpriteData &sprite,
|
||||
size_t vertexOffset) {
|
||||
// 确保顶点缓冲区足够
|
||||
if (vertices_.size() < vertexOffset + VERTICES_PER_SPRITE) {
|
||||
vertices_.resize(vertexOffset + VERTICES_PER_SPRITE);
|
||||
}
|
||||
|
||||
// 计算旋转(使用查表)
|
||||
float c = 1.0f;
|
||||
float s = 0.0f;
|
||||
if (sprite.rotation != 0.0f) {
|
||||
c = trigLookup_.cosRad(sprite.rotation);
|
||||
s = trigLookup_.sinRad(sprite.rotation);
|
||||
}
|
||||
|
||||
// 计算精灵的四个角(相对于中心点)
|
||||
float halfWidth = sprite.size.x * 0.5f;
|
||||
float halfHeight = sprite.size.y * 0.5f;
|
||||
|
||||
// 考虑 pivot 偏移
|
||||
float pivotOffsetX = (sprite.pivot.x - 0.5f) * sprite.size.x;
|
||||
float pivotOffsetY = (sprite.pivot.y - 0.5f) * sprite.size.y;
|
||||
|
||||
// 四个角的本地坐标
|
||||
Vec2 localCorners[4] = {
|
||||
Vec2(-halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 左下
|
||||
Vec2(halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 右下
|
||||
Vec2(halfWidth - pivotOffsetX, halfHeight - pivotOffsetY), // 右上
|
||||
Vec2(-halfWidth - pivotOffsetX, halfHeight - pivotOffsetY) // 左上
|
||||
};
|
||||
|
||||
// 应用旋转和平移
|
||||
Vec2 worldPos = sprite.position;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
Vec2 rotated;
|
||||
if (sprite.rotation != 0.0f) {
|
||||
rotated.x = localCorners[i].x * c - localCorners[i].y * s;
|
||||
rotated.y = localCorners[i].x * s + localCorners[i].y * c;
|
||||
} else {
|
||||
rotated = localCorners[i];
|
||||
}
|
||||
|
||||
vertices_[vertexOffset + i].position = worldPos + rotated;
|
||||
}
|
||||
|
||||
// 设置纹理坐标
|
||||
// uvRect.origin = (u0, v0) - 左下
|
||||
// uvRect.size = (width, height) - 从左上到右下的尺寸
|
||||
float u0 = sprite.uvRect.origin.x;
|
||||
float v0 = sprite.uvRect.origin.y;
|
||||
float u1 = u0 + sprite.uvRect.size.width;
|
||||
float v1 = v0 + sprite.uvRect.size.height;
|
||||
|
||||
// 顶点顺序: 左下, 右下, 右上, 左上
|
||||
// 注意: 在 gl_font_atlas 中 v0 > v1 (因为翻转了V坐标)
|
||||
// 所以 v0 对应底部,v1 对应顶部
|
||||
vertices_[vertexOffset + 0].texCoord = Vec2(u0, v0); // 左下
|
||||
vertices_[vertexOffset + 1].texCoord = Vec2(u1, v0); // 右下
|
||||
vertices_[vertexOffset + 2].texCoord = Vec2(u1, v1); // 右上
|
||||
vertices_[vertexOffset + 3].texCoord = Vec2(u0, v1); // 左上
|
||||
|
||||
// 设置颜色
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
vertices_[vertexOffset + i].color = sprite.color;
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteBatch::flush() {
|
||||
// 标记需要刷新 - 实际刷新由后端处理
|
||||
// 这里只是重置计数器,让后端知道需要提交当前批次
|
||||
spriteCount_ = 0;
|
||||
vertices_.clear();
|
||||
}
|
||||
|
||||
void SpriteBatch::clear() {
|
||||
spriteCount_ = 0;
|
||||
vertices_.clear();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,351 +0,0 @@
|
|||
#include <algorithm>
|
||||
#include <extra2d/graphics/camera/camera.h>
|
||||
#include <extra2d/graphics/camera/viewport_adapter.h>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个默认的正交相机,视口范围为 (-1, -1) 到 (1, 1)
|
||||
*/
|
||||
Camera::Camera() : left_(-1.0f), right_(1.0f), bottom_(-1.0f), top_(1.0f) {}
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param left 视口左边界
|
||||
* @param right 视口右边界
|
||||
* @param bottom 视口底边界
|
||||
* @param top 视口顶边界
|
||||
*
|
||||
* 创建一个指定视口范围的正交相机
|
||||
*/
|
||||
Camera::Camera(float left, float right, float bottom, float top)
|
||||
: left_(left), right_(right), bottom_(bottom), top_(top) {}
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param viewport 视口尺寸
|
||||
*
|
||||
* 根据视口尺寸创建相机,视口原点在左上角
|
||||
*/
|
||||
Camera::Camera(const Size &viewport)
|
||||
: left_(0.0f), right_(viewport.width), bottom_(viewport.height),
|
||||
top_(0.0f) {}
|
||||
|
||||
/**
|
||||
* @brief 设置相机位置
|
||||
* @param position 新的位置坐标
|
||||
*
|
||||
* 设置相机在世界空间中的位置,会标记视图矩阵为脏
|
||||
*/
|
||||
void Camera::setPos(const Vec2 &position) {
|
||||
position_ = position;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置相机位置
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
*
|
||||
* 设置相机在世界空间中的位置,会标记视图矩阵为脏
|
||||
*/
|
||||
void Camera::setPos(float x, float y) {
|
||||
position_.x = x;
|
||||
position_.y = y;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置相机旋转角度
|
||||
* @param degrees 旋转角度(度数)
|
||||
*
|
||||
* 设置相机的旋转角度,会标记视图矩阵为脏
|
||||
*/
|
||||
void Camera::setRotation(float degrees) {
|
||||
rotation_ = degrees;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置相机缩放级别
|
||||
* @param zoom 缩放值(1.0为正常大小)
|
||||
*
|
||||
* 设置相机的缩放级别,会同时标记视图矩阵和投影矩阵为脏
|
||||
*/
|
||||
void Camera::setZoom(float zoom) {
|
||||
zoom_ = zoom;
|
||||
viewDirty_ = true;
|
||||
projDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口范围
|
||||
* @param left 左边界
|
||||
* @param right 右边界
|
||||
* @param bottom 底边界
|
||||
* @param top 顶边界
|
||||
*
|
||||
* 设置相机的正交投影视口范围,会标记投影矩阵为脏
|
||||
*/
|
||||
void Camera::setViewport(float left, float right, float bottom, float top) {
|
||||
left_ = left;
|
||||
right_ = right;
|
||||
bottom_ = bottom;
|
||||
top_ = top;
|
||||
projDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口范围
|
||||
* @param rect 视口矩形
|
||||
*
|
||||
* 使用矩形设置相机的正交投影视口范围,会标记投影矩阵为脏
|
||||
*/
|
||||
void Camera::setViewport(const Rect &rect) {
|
||||
left_ = rect.left();
|
||||
right_ = rect.right();
|
||||
bottom_ = rect.bottom();
|
||||
top_ = rect.top();
|
||||
projDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取视口矩形
|
||||
* @return 当前视口的矩形表示
|
||||
*
|
||||
* 返回当前相机的视口范围
|
||||
*/
|
||||
Rect Camera::getViewport() const {
|
||||
return Rect(left_, top_, right_ - left_, bottom_ - top_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取视图矩阵
|
||||
* @return 视图矩阵
|
||||
*
|
||||
* 变换顺序:平移 -> 旋转 -> 缩放(逆序应用)
|
||||
* View = T(-position) × R(-rotation) × S(1/zoom)
|
||||
*/
|
||||
glm::mat4 Camera::getViewMatrix() const {
|
||||
if (viewDirty_) {
|
||||
viewMatrix_ = glm::mat4(1.0f);
|
||||
|
||||
// 1. 平移(最后应用)
|
||||
viewMatrix_ = glm::translate(viewMatrix_,
|
||||
glm::vec3(-position_.x, -position_.y, 0.0f));
|
||||
|
||||
// 2. 旋转(中间应用)
|
||||
if (rotation_ != 0.0f) {
|
||||
viewMatrix_ = glm::rotate(viewMatrix_, -rotation_ * DEG_TO_RAD,
|
||||
glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
// 3. 缩放(最先应用)
|
||||
if (zoom_ != 1.0f) {
|
||||
viewMatrix_ =
|
||||
glm::scale(viewMatrix_, glm::vec3(1.0f / zoom_, 1.0f / zoom_, 1.0f));
|
||||
}
|
||||
|
||||
viewDirty_ = false;
|
||||
}
|
||||
return viewMatrix_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取投影矩阵
|
||||
* @return 正交投影矩阵
|
||||
*
|
||||
* 对于2D游戏,Y轴向下增长(屏幕坐标系)
|
||||
* OpenGL默认Y轴向上,所以需要反转Y轴
|
||||
*/
|
||||
glm::mat4 Camera::getProjectionMatrix() const {
|
||||
if (projDirty_) {
|
||||
// 对于2D游戏,Y轴向下增长(屏幕坐标系)
|
||||
// OpenGL默认Y轴向上,所以需要反转Y轴
|
||||
// glm::ortho(left, right, bottom, top)
|
||||
// 为了Y轴向下:传入 (bottom=height, top=0),这样Y轴翻转
|
||||
projMatrix_ = glm::ortho(
|
||||
left_, right_, // X轴:从左到右
|
||||
bottom_, top_, // Y轴:从下到上(传入bottom>top,实现Y轴向下增长)
|
||||
-1.0f, 1.0f);
|
||||
projDirty_ = false;
|
||||
}
|
||||
return projMatrix_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取视图-投影矩阵
|
||||
* @return 视图-投影矩阵
|
||||
*/
|
||||
glm::mat4 Camera::getViewProjectionMatrix() const {
|
||||
return getProjectionMatrix() * getViewMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将屏幕坐标转换为世界坐标
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 世界坐标
|
||||
*/
|
||||
Vec2 Camera::screenToWorld(const Vec2 &screenPos) const {
|
||||
Vec2 logicPos = screenPos;
|
||||
|
||||
// 如果有视口适配器,先转换到逻辑坐标
|
||||
if (viewportAdapter_) {
|
||||
logicPos = viewportAdapter_->screenToLogic(screenPos);
|
||||
}
|
||||
|
||||
// 使用逆视图-投影矩阵转换
|
||||
glm::mat4 invVP = glm::inverse(getViewProjectionMatrix());
|
||||
glm::vec4 ndc(logicPos.x, logicPos.y, 0.0f, 1.0f);
|
||||
glm::vec4 world = invVP * ndc;
|
||||
return Vec2(world.x, world.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将世界坐标转换为屏幕坐标
|
||||
* @param worldPos 世界坐标
|
||||
* @return 屏幕坐标
|
||||
*/
|
||||
Vec2 Camera::worldToScreen(const Vec2 &worldPos) const {
|
||||
glm::vec4 world(worldPos.x, worldPos.y, 0.0f, 1.0f);
|
||||
glm::vec4 screen = getViewProjectionMatrix() * world;
|
||||
Vec2 logicPos(screen.x, screen.y);
|
||||
|
||||
// 如果有视口适配器,转换到屏幕坐标
|
||||
if (viewportAdapter_) {
|
||||
return viewportAdapter_->logicToScreen(logicPos);
|
||||
}
|
||||
return logicPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将屏幕坐标转换为世界坐标
|
||||
* @param x 屏幕X坐标
|
||||
* @param y 屏幕Y坐标
|
||||
* @return 世界坐标
|
||||
*/
|
||||
Vec2 Camera::screenToWorld(float x, float y) const {
|
||||
return screenToWorld(Vec2(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将世界坐标转换为屏幕坐标
|
||||
* @param x 世界X坐标
|
||||
* @param y 世界Y坐标
|
||||
* @return 屏幕坐标
|
||||
*/
|
||||
Vec2 Camera::worldToScreen(float x, float y) const {
|
||||
return worldToScreen(Vec2(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移动相机位置
|
||||
* @param offset 位置偏移量
|
||||
*
|
||||
* 按指定偏移量移动相机位置,会标记视图矩阵为脏
|
||||
*/
|
||||
void Camera::move(const Vec2 &offset) {
|
||||
position_ += offset;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移动相机位置
|
||||
* @param x X方向偏移量
|
||||
* @param y Y方向偏移量
|
||||
*
|
||||
* 按指定偏移量移动相机位置,会标记视图矩阵为脏
|
||||
*/
|
||||
void Camera::move(float x, float y) {
|
||||
position_.x += x;
|
||||
position_.y += y;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置相机边界限制
|
||||
* @param bounds 边界矩形
|
||||
*
|
||||
* 设置相机的移动边界,相机位置将被限制在此边界内
|
||||
*/
|
||||
void Camera::setBounds(const Rect &bounds) {
|
||||
bounds_ = bounds;
|
||||
hasBounds_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除相机边界限制
|
||||
*
|
||||
* 移除相机的移动边界限制
|
||||
*/
|
||||
void Camera::clearBounds() { hasBounds_ = false; }
|
||||
|
||||
/**
|
||||
* @brief 将相机位置限制在边界内
|
||||
*
|
||||
* 如果设置了边界,将相机位置限制在边界矩形内
|
||||
*/
|
||||
void Camera::clampToBounds() {
|
||||
if (!hasBounds_)
|
||||
return;
|
||||
|
||||
float viewportWidth = (right_ - left_) / zoom_;
|
||||
float viewportHeight = (bottom_ - top_) / zoom_;
|
||||
|
||||
float minX = bounds_.left() + viewportWidth * 0.5f;
|
||||
float maxX = bounds_.right() - viewportWidth * 0.5f;
|
||||
float minY = bounds_.top() + viewportHeight * 0.5f;
|
||||
float maxY = bounds_.bottom() - viewportHeight * 0.5f;
|
||||
|
||||
if (minX > maxX) {
|
||||
position_.x = bounds_.center().x;
|
||||
} else {
|
||||
position_.x = std::clamp(position_.x, minX, maxX);
|
||||
}
|
||||
|
||||
if (minY > maxY) {
|
||||
position_.y = bounds_.center().y;
|
||||
} else {
|
||||
position_.y = std::clamp(position_.y, minY, maxY);
|
||||
}
|
||||
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将相机移动到目标位置
|
||||
* @param target 目标位置
|
||||
*
|
||||
* 设置相机位置到指定的世界坐标
|
||||
*/
|
||||
void Camera::lookAt(const Vec2 &target) {
|
||||
position_ = target;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口适配器
|
||||
* @param adapter 视口适配器指针
|
||||
*/
|
||||
void Camera::setViewportAdapter(ViewportAdapter *adapter) {
|
||||
viewportAdapter_ = adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据视口适配器自动设置视口
|
||||
*
|
||||
* 如果设置了视口适配器,根据其配置自动设置相机的视口范围
|
||||
*/
|
||||
void Camera::applyViewportAdapter() {
|
||||
if (viewportAdapter_) {
|
||||
const auto &config = viewportAdapter_->getConfig();
|
||||
setViewport(0.0f, config.logicWidth, config.logicHeight, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,451 +0,0 @@
|
|||
#include <extra2d/graphics/camera/viewport_adapter.h>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个未配置的视口适配器
|
||||
*/
|
||||
ViewportAdapter::ViewportAdapter() = default;
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param logicWidth 逻辑宽度
|
||||
* @param logicHeight 逻辑高度
|
||||
*
|
||||
* 创建一个指定逻辑尺寸的视口适配器
|
||||
*/
|
||||
ViewportAdapter::ViewportAdapter(float logicWidth, float logicHeight) {
|
||||
config_.logicWidth = logicWidth;
|
||||
config_.logicHeight = logicHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口配置
|
||||
* @param config 视口配置
|
||||
*
|
||||
* 设置完整的视口配置并标记矩阵为脏
|
||||
*/
|
||||
void ViewportAdapter::setConfig(const ViewportConfig &config) {
|
||||
config_ = config;
|
||||
matrixDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置逻辑尺寸
|
||||
* @param width 逻辑宽度
|
||||
* @param height 逻辑高度
|
||||
*
|
||||
* 设置视口的逻辑尺寸并标记矩阵为脏
|
||||
*/
|
||||
void ViewportAdapter::setLogicSize(float width, float height) {
|
||||
config_.logicWidth = width;
|
||||
config_.logicHeight = height;
|
||||
matrixDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口模式
|
||||
* @param mode 视口模式
|
||||
*
|
||||
* 设置视口适配模式(宽高比、拉伸、居中等)
|
||||
*/
|
||||
void ViewportAdapter::setMode(ViewportMode mode) {
|
||||
config_.mode = mode;
|
||||
matrixDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置黑边位置
|
||||
* @param position 黑边位置
|
||||
*
|
||||
* 设置黑边相对于视口的位置
|
||||
*/
|
||||
void ViewportAdapter::setLetterboxPosition(LetterboxPosition position) {
|
||||
config_.letterboxPosition = position;
|
||||
matrixDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置黑边颜色
|
||||
* @param color 黑边颜色
|
||||
*
|
||||
* 设置黑边区域的填充颜色
|
||||
*/
|
||||
void ViewportAdapter::setLetterboxColor(const Color &color) {
|
||||
config_.letterboxColor = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新视口适配
|
||||
* @param screenWidth 屏幕宽度
|
||||
* @param screenHeight 屏幕高度
|
||||
*
|
||||
* 根据屏幕尺寸和配置计算视口参数
|
||||
*/
|
||||
void ViewportAdapter::update(int screenWidth, int screenHeight) {
|
||||
if (screenWidth_ == screenWidth && screenHeight_ == screenHeight &&
|
||||
!matrixDirty_) {
|
||||
return;
|
||||
}
|
||||
|
||||
screenWidth_ = screenWidth;
|
||||
screenHeight_ = screenHeight;
|
||||
matrixDirty_ = true;
|
||||
|
||||
result_.hasLetterbox = false;
|
||||
result_.letterbox.top = Rect::Zero();
|
||||
result_.letterbox.bottom = Rect::Zero();
|
||||
result_.letterbox.left = Rect::Zero();
|
||||
result_.letterbox.right = Rect::Zero();
|
||||
|
||||
switch (config_.mode) {
|
||||
case ViewportMode::AspectRatio:
|
||||
calculateAspectRatio();
|
||||
break;
|
||||
case ViewportMode::Stretch:
|
||||
calculateStretch();
|
||||
break;
|
||||
case ViewportMode::Center:
|
||||
calculateCenter();
|
||||
break;
|
||||
case ViewportMode::Custom:
|
||||
calculateCustom();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算宽高比适配模式
|
||||
*
|
||||
* 保持逻辑宽高比,根据屏幕尺寸计算缩放和偏移
|
||||
*/
|
||||
void ViewportAdapter::calculateAspectRatio() {
|
||||
if (config_.logicHeight <= 0.0f || screenHeight_ <= 0) {
|
||||
result_ = ViewportResult();
|
||||
return;
|
||||
}
|
||||
|
||||
float logicAspect = config_.logicWidth / config_.logicHeight;
|
||||
float screenAspect = static_cast<float>(screenWidth_) / screenHeight_;
|
||||
|
||||
if (screenAspect > logicAspect) {
|
||||
result_.uniformScale = static_cast<float>(screenHeight_) / config_.logicHeight;
|
||||
result_.scaleX = result_.uniformScale;
|
||||
result_.scaleY = result_.uniformScale;
|
||||
result_.viewport.size.width = config_.logicWidth * result_.uniformScale;
|
||||
result_.viewport.size.height = static_cast<float>(screenHeight_);
|
||||
result_.offset.x = (screenWidth_ - result_.viewport.size.width) / 2.0f;
|
||||
result_.offset.y = 0.0f;
|
||||
} else {
|
||||
result_.uniformScale = static_cast<float>(screenWidth_) / config_.logicWidth;
|
||||
result_.scaleX = result_.uniformScale;
|
||||
result_.scaleY = result_.uniformScale;
|
||||
result_.viewport.size.width = static_cast<float>(screenWidth_);
|
||||
result_.viewport.size.height = config_.logicHeight * result_.uniformScale;
|
||||
result_.offset.x = 0.0f;
|
||||
result_.offset.y = (screenHeight_ - result_.viewport.size.height) / 2.0f;
|
||||
}
|
||||
|
||||
result_.viewport.origin = result_.offset;
|
||||
|
||||
applyLetterboxPosition(
|
||||
static_cast<float>(screenWidth_) - result_.viewport.size.width,
|
||||
static_cast<float>(screenHeight_) - result_.viewport.size.height);
|
||||
|
||||
calculateLetterbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算拉伸模式
|
||||
*
|
||||
* 拉伸逻辑视口以填满整个屏幕
|
||||
*/
|
||||
void ViewportAdapter::calculateStretch() {
|
||||
result_.scaleX = static_cast<float>(screenWidth_) / config_.logicWidth;
|
||||
result_.scaleY = static_cast<float>(screenHeight_) / config_.logicHeight;
|
||||
result_.uniformScale = std::min(result_.scaleX, result_.scaleY);
|
||||
|
||||
result_.viewport.origin = Vec2::Zero();
|
||||
result_.viewport.size.width = static_cast<float>(screenWidth_);
|
||||
result_.viewport.size.height = static_cast<float>(screenHeight_);
|
||||
|
||||
result_.offset = Vec2::Zero();
|
||||
result_.hasLetterbox = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算居中模式
|
||||
*
|
||||
* 将逻辑视口居中显示,可选自动缩放
|
||||
*/
|
||||
void ViewportAdapter::calculateCenter() {
|
||||
float displayWidth = config_.logicWidth;
|
||||
float displayHeight = config_.logicHeight;
|
||||
|
||||
if (config_.autoScaleInCenterMode) {
|
||||
float scaleX = static_cast<float>(screenWidth_) / config_.logicWidth;
|
||||
float scaleY = static_cast<float>(screenHeight_) / config_.logicHeight;
|
||||
result_.uniformScale = std::min(scaleX, scaleY);
|
||||
|
||||
if (result_.uniformScale < 1.0f) {
|
||||
displayWidth = config_.logicWidth * result_.uniformScale;
|
||||
displayHeight = config_.logicHeight * result_.uniformScale;
|
||||
} else {
|
||||
result_.uniformScale = 1.0f;
|
||||
}
|
||||
|
||||
result_.scaleX = result_.uniformScale;
|
||||
result_.scaleY = result_.uniformScale;
|
||||
} else {
|
||||
result_.scaleX = 1.0f;
|
||||
result_.scaleY = 1.0f;
|
||||
result_.uniformScale = 1.0f;
|
||||
}
|
||||
|
||||
result_.offset.x = (screenWidth_ - displayWidth) / 2.0f;
|
||||
result_.offset.y = (screenHeight_ - displayHeight) / 2.0f;
|
||||
|
||||
result_.viewport.origin = result_.offset;
|
||||
result_.viewport.size.width = displayWidth;
|
||||
result_.viewport.size.height = displayHeight;
|
||||
|
||||
applyLetterboxPosition(
|
||||
static_cast<float>(screenWidth_) - displayWidth,
|
||||
static_cast<float>(screenHeight_) - displayHeight);
|
||||
|
||||
calculateLetterbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算自定义模式
|
||||
*
|
||||
* 使用自定义缩放和偏移参数
|
||||
*/
|
||||
void ViewportAdapter::calculateCustom() {
|
||||
result_.scaleX = config_.customScale;
|
||||
result_.scaleY = config_.customScale;
|
||||
result_.uniformScale = config_.customScale;
|
||||
|
||||
if (config_.customViewport.empty()) {
|
||||
float displayWidth = config_.logicWidth * config_.customScale;
|
||||
float displayHeight = config_.logicHeight * config_.customScale;
|
||||
|
||||
result_.offset = config_.customOffset;
|
||||
result_.viewport.origin = result_.offset;
|
||||
result_.viewport.size.width = displayWidth;
|
||||
result_.viewport.size.height = displayHeight;
|
||||
} else {
|
||||
result_.viewport = config_.customViewport;
|
||||
result_.offset = config_.customViewport.origin;
|
||||
}
|
||||
|
||||
calculateLetterbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算黑边区域
|
||||
*
|
||||
* 根据视口偏移计算上下左右黑边矩形
|
||||
*/
|
||||
void ViewportAdapter::calculateLetterbox() {
|
||||
result_.hasLetterbox = false;
|
||||
|
||||
float screenW = static_cast<float>(screenWidth_);
|
||||
float screenH = static_cast<float>(screenHeight_);
|
||||
|
||||
if (result_.offset.y > 0.0f) {
|
||||
result_.hasLetterbox = true;
|
||||
result_.letterbox.top =
|
||||
Rect(0.0f, 0.0f, screenW, result_.offset.y);
|
||||
result_.letterbox.bottom =
|
||||
Rect(0.0f, result_.offset.y + result_.viewport.size.height, screenW,
|
||||
result_.offset.y);
|
||||
}
|
||||
|
||||
if (result_.offset.x > 0.0f) {
|
||||
result_.hasLetterbox = true;
|
||||
result_.letterbox.left =
|
||||
Rect(0.0f, 0.0f, result_.offset.x, screenH);
|
||||
result_.letterbox.right =
|
||||
Rect(result_.offset.x + result_.viewport.size.width, 0.0f,
|
||||
result_.offset.x, screenH);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 应用黑边位置
|
||||
* @param extraWidth 额外宽度
|
||||
* @param extraHeight 额外高度
|
||||
*
|
||||
* 根据配置调整视口偏移以实现不同的黑边位置
|
||||
*/
|
||||
void ViewportAdapter::applyLetterboxPosition(float extraWidth,
|
||||
float extraHeight) {
|
||||
if (extraWidth <= 0.0f && extraHeight <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (config_.letterboxPosition) {
|
||||
case LetterboxPosition::Center:
|
||||
break;
|
||||
|
||||
case LetterboxPosition::LeftTop:
|
||||
if (extraWidth > 0.0f) {
|
||||
result_.offset.x = 0.0f;
|
||||
}
|
||||
if (extraHeight > 0.0f) {
|
||||
result_.offset.y = 0.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case LetterboxPosition::RightTop:
|
||||
if (extraWidth > 0.0f) {
|
||||
result_.offset.x = extraWidth;
|
||||
}
|
||||
if (extraHeight > 0.0f) {
|
||||
result_.offset.y = 0.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case LetterboxPosition::LeftBottom:
|
||||
if (extraWidth > 0.0f) {
|
||||
result_.offset.x = 0.0f;
|
||||
}
|
||||
if (extraHeight > 0.0f) {
|
||||
result_.offset.y = extraHeight;
|
||||
}
|
||||
break;
|
||||
|
||||
case LetterboxPosition::RightBottom:
|
||||
if (extraWidth > 0.0f) {
|
||||
result_.offset.x = extraWidth;
|
||||
}
|
||||
if (extraHeight > 0.0f) {
|
||||
result_.offset.y = extraHeight;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
result_.viewport.origin = result_.offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将屏幕坐标转换为逻辑坐标
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 逻辑坐标
|
||||
*
|
||||
* 根据当前缩放和偏移计算对应的逻辑坐标
|
||||
*/
|
||||
Vec2 ViewportAdapter::screenToLogic(const Vec2 &screenPos) const {
|
||||
return Vec2((screenPos.x - result_.offset.x) / result_.scaleX,
|
||||
(screenPos.y - result_.offset.y) / result_.scaleY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将逻辑坐标转换为屏幕坐标
|
||||
* @param logicPos 逻辑坐标
|
||||
* @return 屏幕坐标
|
||||
*
|
||||
* 根据当前缩放和偏移计算对应的屏幕坐标
|
||||
*/
|
||||
Vec2 ViewportAdapter::logicToScreen(const Vec2 &logicPos) const {
|
||||
return Vec2(logicPos.x * result_.scaleX + result_.offset.x,
|
||||
logicPos.y * result_.scaleY + result_.offset.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将屏幕坐标转换为逻辑坐标
|
||||
* @param x 屏幕X坐标
|
||||
* @param y 屏幕Y坐标
|
||||
* @return 逻辑坐标
|
||||
*/
|
||||
Vec2 ViewportAdapter::screenToLogic(float x, float y) const {
|
||||
return screenToLogic(Vec2(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将逻辑坐标转换为屏幕坐标
|
||||
* @param x 逻辑X坐标
|
||||
* @param y 逻辑Y坐标
|
||||
* @return 屏幕坐标
|
||||
*/
|
||||
Vec2 ViewportAdapter::logicToScreen(float x, float y) const {
|
||||
return logicToScreen(Vec2(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取视口变换矩阵
|
||||
* @return 4x4变换矩阵
|
||||
*
|
||||
* 返回用于将逻辑坐标转换为屏幕坐标的变换矩阵
|
||||
*/
|
||||
glm::mat4 ViewportAdapter::getMatrix() const {
|
||||
if (matrixDirty_) {
|
||||
viewportMatrix_ = glm::mat4(1.0f);
|
||||
viewportMatrix_ = glm::translate(viewportMatrix_,
|
||||
glm::vec3(result_.offset.x, result_.offset.y, 0.0f));
|
||||
viewportMatrix_ = glm::scale(viewportMatrix_,
|
||||
glm::vec3(result_.scaleX, result_.scaleY, 1.0f));
|
||||
matrixDirty_ = false;
|
||||
}
|
||||
return viewportMatrix_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取视口逆变换矩阵
|
||||
* @return 4x4逆变换矩阵
|
||||
*
|
||||
* 返回用于将屏幕坐标转换为逻辑坐标的逆变换矩阵
|
||||
*/
|
||||
glm::mat4 ViewportAdapter::getInvMatrix() const {
|
||||
if (matrixDirty_) {
|
||||
getMatrix();
|
||||
}
|
||||
inverseViewportMatrix_ = glm::inverse(viewportMatrix_);
|
||||
return inverseViewportMatrix_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查屏幕坐标是否在视口内
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 在视口内返回true,否则返回false
|
||||
*/
|
||||
bool ViewportAdapter::isInViewport(const Vec2 &screenPos) const {
|
||||
return result_.viewport.containsPoint(screenPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查屏幕坐标是否在黑边区域内
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 在黑边区域内返回true,否则返回false
|
||||
*/
|
||||
bool ViewportAdapter::isInLetterbox(const Vec2 &screenPos) const {
|
||||
if (!result_.hasLetterbox) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!result_.letterbox.top.empty() &&
|
||||
result_.letterbox.top.containsPoint(screenPos)) {
|
||||
return true;
|
||||
}
|
||||
if (!result_.letterbox.bottom.empty() &&
|
||||
result_.letterbox.bottom.containsPoint(screenPos)) {
|
||||
return true;
|
||||
}
|
||||
if (!result_.letterbox.left.empty() &&
|
||||
result_.letterbox.left.containsPoint(screenPos)) {
|
||||
return true;
|
||||
}
|
||||
if (!result_.letterbox.right.empty() &&
|
||||
result_.letterbox.right.containsPoint(screenPos)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
#include <extra2d/graphics/core/render_command.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// RenderCommand 便捷构造函数
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建精灵渲染命令
|
||||
*
|
||||
* 创建一个用于渲染2D精灵的RenderCommand对象,包含纹理、目标区域、源区域、
|
||||
* 着色、旋转、锚点和层级等信息
|
||||
*
|
||||
* @param tex 指向纹理对象的指针
|
||||
* @param dest 目标渲染区域
|
||||
* @param src 源纹理区域
|
||||
* @param tint 着色颜色
|
||||
* @param rot 旋转角度(弧度)
|
||||
* @param anc 锚点位置(0.0-1.0范围)
|
||||
* @param lyr 渲染层级
|
||||
* @return 配置好的RenderCommand对象
|
||||
*/
|
||||
RenderCommand RenderCommand::makeSprite(const Texture* tex, const Rect& dest,
|
||||
const Rect& src, const Color& tint,
|
||||
float rot, const Vec2& anc,
|
||||
uint32_t lyr) {
|
||||
RenderCommand cmd;
|
||||
cmd.type = RenderCommandType::Sprite;
|
||||
cmd.layer = lyr;
|
||||
cmd.transform = glm::mat4(1.0f);
|
||||
|
||||
SpriteCommandData data;
|
||||
data.texture = tex;
|
||||
data.destRect = dest;
|
||||
data.srcRect = src;
|
||||
data.tint = tint;
|
||||
data.rotation = rot;
|
||||
data.anchor = anc;
|
||||
// 生成排序键:纹理指针的高位 + 层级的低位
|
||||
data.sortKey = (reinterpret_cast<uintptr_t>(tex) >> 4) & 0xFFFFFFF0;
|
||||
data.sortKey |= (lyr & 0xF);
|
||||
|
||||
cmd.data = data;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建线段渲染命令
|
||||
*
|
||||
* 创建一个用于渲染线段的RenderCommand对象
|
||||
*
|
||||
* @param s 线段起点坐标
|
||||
* @param e 线段终点坐标
|
||||
* @param c 线段颜色
|
||||
* @param w 线段宽度
|
||||
* @param lyr 渲染层级
|
||||
* @return 配置好的RenderCommand对象
|
||||
*/
|
||||
RenderCommand RenderCommand::makeLine(const Vec2& s, const Vec2& e, const Color& c,
|
||||
float w, uint32_t lyr) {
|
||||
RenderCommand cmd;
|
||||
cmd.type = RenderCommandType::Line;
|
||||
cmd.layer = lyr;
|
||||
cmd.transform = glm::mat4(1.0f);
|
||||
|
||||
LineCommandData data;
|
||||
data.start = s;
|
||||
data.end = e;
|
||||
data.color = c;
|
||||
data.width = w;
|
||||
|
||||
cmd.data = data;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建矩形渲染命令
|
||||
*
|
||||
* 创建一个用于渲染矩形的RenderCommand对象,可选择填充或描边模式
|
||||
*
|
||||
* @param r 矩形区域
|
||||
* @param c 矩形颜色
|
||||
* @param w 线条宽度(仅描边模式有效)
|
||||
* @param fill 是否填充矩形
|
||||
* @param lyr 渲染层级
|
||||
* @return 配置好的RenderCommand对象
|
||||
*/
|
||||
RenderCommand RenderCommand::makeRect(const Rect& r, const Color& c,
|
||||
float w, bool fill, uint32_t lyr) {
|
||||
RenderCommand cmd;
|
||||
cmd.type = fill ? RenderCommandType::FilledRect : RenderCommandType::Rect;
|
||||
cmd.layer = lyr;
|
||||
cmd.transform = glm::mat4(1.0f);
|
||||
|
||||
RectCommandData data;
|
||||
data.rect = r;
|
||||
data.color = c;
|
||||
data.width = w;
|
||||
data.filled = fill;
|
||||
|
||||
cmd.data = data;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// RenderCommandBuffer 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化渲染命令缓冲区,预留初始容量
|
||||
*/
|
||||
RenderCommandBuffer::RenderCommandBuffer() : nextOrder_(0) {
|
||||
commands_.reserve(INITIAL_CAPACITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 释放渲染命令缓冲区资源
|
||||
*/
|
||||
RenderCommandBuffer::~RenderCommandBuffer() = default;
|
||||
|
||||
/**
|
||||
* @brief 添加渲染命令(左值引用版本)
|
||||
*
|
||||
* 将渲染命令以拷贝方式添加到缓冲区,自动分配顺序号
|
||||
*
|
||||
* @param cmd 要添加的渲染命令
|
||||
*/
|
||||
void RenderCommandBuffer::addCommand(const RenderCommand& cmd) {
|
||||
if (commands_.size() >= MAX_CAPACITY) {
|
||||
// 缓冲区已满,可能需要立即刷新
|
||||
return;
|
||||
}
|
||||
|
||||
RenderCommand copy = cmd;
|
||||
copy.order = nextOrder_++;
|
||||
commands_.push_back(std::move(copy));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加渲染命令(右值引用版本)
|
||||
*
|
||||
* 将渲染命令以移动方式添加到缓冲区,自动分配顺序号
|
||||
*
|
||||
* @param cmd 要添加的渲染命令(右值引用)
|
||||
*/
|
||||
void RenderCommandBuffer::addCommand(RenderCommand&& cmd) {
|
||||
if (commands_.size() >= MAX_CAPACITY) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd.order = nextOrder_++;
|
||||
commands_.push_back(std::move(cmd));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 原地构造渲染命令
|
||||
*
|
||||
* 在缓冲区中直接构造一个渲染命令对象,避免额外的拷贝或移动操作
|
||||
*
|
||||
* @return 新构造的渲染命令对象的引用
|
||||
*/
|
||||
RenderCommand& RenderCommandBuffer::emplaceCommand() {
|
||||
if (commands_.size() >= MAX_CAPACITY) {
|
||||
// 如果已满,返回一个虚拟命令(不应该发生)
|
||||
static RenderCommand dummy;
|
||||
return dummy;
|
||||
}
|
||||
|
||||
commands_.emplace_back();
|
||||
commands_.back().order = nextOrder_++;
|
||||
return commands_.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 对渲染命令进行排序
|
||||
*
|
||||
* 按层级、命令类型、纹理/材质和提交顺序对命令进行排序,
|
||||
* 以优化渲染性能和批处理效率
|
||||
*/
|
||||
void RenderCommandBuffer::sortCommands() {
|
||||
// 按以下优先级排序:
|
||||
// 1. 层级 (layer) - 低层级先渲染
|
||||
// 2. 命令类型 - 精灵类命令优先批处理
|
||||
// 3. 纹理/材质 - 相同纹理的精灵连续渲染
|
||||
// 4. 提交顺序 - 保证稳定性
|
||||
|
||||
std::sort(commands_.begin(), commands_.end(), compareCommands);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空缓冲区
|
||||
*
|
||||
* 移除所有渲染命令并重置顺序计数器
|
||||
*/
|
||||
void RenderCommandBuffer::clear() {
|
||||
commands_.clear();
|
||||
nextOrder_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 预留缓冲区容量
|
||||
*
|
||||
* 预先分配缓冲区内存以减少动态分配开销
|
||||
*
|
||||
* @param capacity 要预留的容量大小
|
||||
*/
|
||||
void RenderCommandBuffer::reserve(size_t capacity) {
|
||||
if (capacity <= MAX_CAPACITY) {
|
||||
commands_.reserve(capacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染命令比较函数
|
||||
*
|
||||
* 用于排序的比较函数,按层级、类型、纹理和顺序进行比较
|
||||
*
|
||||
* @param a 第一个渲染命令
|
||||
* @param b 第二个渲染命令
|
||||
* @return 如果a应排在b前面返回true,否则返回false
|
||||
*/
|
||||
bool RenderCommandBuffer::compareCommands(const RenderCommand& a, const RenderCommand& b) {
|
||||
// 首先按层级排序
|
||||
if (a.layer != b.layer) {
|
||||
return a.layer < b.layer;
|
||||
}
|
||||
|
||||
// 然后按类型排序(精灵类命令放在一起以便批处理)
|
||||
if (a.type != b.type) {
|
||||
// 精灵和文本命令优先(需要纹理)
|
||||
bool aIsSprite = (a.type == RenderCommandType::Sprite ||
|
||||
a.type == RenderCommandType::Text);
|
||||
bool bIsSprite = (b.type == RenderCommandType::Sprite ||
|
||||
b.type == RenderCommandType::Text);
|
||||
|
||||
if (aIsSprite != bIsSprite) {
|
||||
return aIsSprite > bIsSprite; // 精灵类命令在前
|
||||
}
|
||||
|
||||
return static_cast<uint8_t>(a.type) < static_cast<uint8_t>(b.type);
|
||||
}
|
||||
|
||||
// 对于精灵命令,按纹理排序
|
||||
if (a.type == RenderCommandType::Sprite && b.type == RenderCommandType::Sprite) {
|
||||
const auto& dataA = std::get<SpriteCommandData>(a.data);
|
||||
const auto& dataB = std::get<SpriteCommandData>(b.data);
|
||||
if (dataA.texture != dataB.texture) {
|
||||
return dataA.texture < dataB.texture;
|
||||
}
|
||||
// 相同纹理时按 sortKey 排序
|
||||
if (dataA.sortKey != dataB.sortKey) {
|
||||
return dataA.sortKey < dataB.sortKey;
|
||||
}
|
||||
}
|
||||
|
||||
// 最后按提交顺序排序(保证稳定性)
|
||||
return a.order < b.order;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明后端初始化函数
|
||||
namespace graphics {
|
||||
#ifdef E2D_BACKEND_OPENGL
|
||||
void initOpenGLBackend();
|
||||
#endif
|
||||
#ifdef E2D_BACKEND_VULKAN
|
||||
void initVulkanBackend();
|
||||
#endif
|
||||
} // namespace graphics
|
||||
|
||||
RenderModule::RenderModule(std::function<void(RenderCfg &)> configFn) {
|
||||
configFn(cfg_);
|
||||
}
|
||||
|
||||
RenderModule::~RenderModule() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderModule::init() {
|
||||
if (initialized_)
|
||||
return true;
|
||||
|
||||
auto *winMod = Registry::instance().get<WindowModule>();
|
||||
if (!winMod || !winMod->win()) {
|
||||
E2D_LOG_ERROR("窗口模块不可用");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化图形后端(注册到工厂)
|
||||
#ifdef E2D_BACKEND_OPENGL
|
||||
graphics::initOpenGLBackend();
|
||||
#endif
|
||||
#ifdef E2D_BACKEND_VULKAN
|
||||
graphics::initVulkanBackend();
|
||||
#endif
|
||||
|
||||
// 使用ShaderManager的默认路径初始化
|
||||
if (!ShaderManager::getInstance().isInitialized()) {
|
||||
auto factory = makeShared<GLShaderFactory>();
|
||||
if (!ShaderManager::getInstance().init(factory)) {
|
||||
E2D_LOG_WARN("使用默认路径初始化 ShaderManager 失败");
|
||||
}
|
||||
}
|
||||
|
||||
std::string windowBackend = winMod->getWindowBackend();
|
||||
|
||||
if (cfg_.backend.empty()) {
|
||||
E2D_LOG_INFO("未指定图形后端,正在为窗口后端自动选择:{}", windowBackend);
|
||||
renderer_ = graphics::BackendFactory::createBackendForWindow(windowBackend);
|
||||
} else {
|
||||
if (!graphics::BackendFactory::isCompatible(cfg_.backend, windowBackend)) {
|
||||
E2D_LOG_WARN("图形后端 '{}' 与窗口后端 '{}' 不兼容", cfg_.backend,
|
||||
windowBackend);
|
||||
}
|
||||
renderer_ = graphics::BackendFactory::createBackend(cfg_.backend);
|
||||
}
|
||||
|
||||
if (!renderer_) {
|
||||
E2D_LOG_ERROR("创建渲染后端失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!renderer_->init(winMod->win())) {
|
||||
E2D_LOG_ERROR("初始化渲染后端失败");
|
||||
renderer_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("渲染模块初始化成功");
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderModule::shutdown() {
|
||||
if (!initialized_)
|
||||
return;
|
||||
|
||||
if (renderer_) {
|
||||
renderer_->shutdown();
|
||||
renderer_.reset();
|
||||
}
|
||||
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,780 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/core/render_target.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <stb/stb_image_write.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// RenderTarget实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个空的渲染目标对象
|
||||
*/
|
||||
RenderTarget::RenderTarget() = default;
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 销毁渲染目标并释放相关资源
|
||||
*/
|
||||
RenderTarget::~RenderTarget() { destroy(); }
|
||||
|
||||
/**
|
||||
* @brief 移动构造函数
|
||||
* @param other 源渲染目标对象
|
||||
*
|
||||
* 将其他渲染目标的资源转移到新对象
|
||||
*/
|
||||
RenderTarget::RenderTarget(RenderTarget &&other) noexcept
|
||||
: fbo_(other.fbo_), rbo_(other.rbo_),
|
||||
colorTexture_(std::move(other.colorTexture_)),
|
||||
depthTexture_(std::move(other.depthTexture_)), width_(other.width_),
|
||||
height_(other.height_), colorFormat_(other.colorFormat_),
|
||||
hasDepth_(other.hasDepth_), hasStencil_(other.hasStencil_),
|
||||
samples_(other.samples_) {
|
||||
other.fbo_ = 0;
|
||||
other.rbo_ = 0;
|
||||
other.width_ = 0;
|
||||
other.height_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移动赋值运算符
|
||||
* @param other 源渲染目标对象
|
||||
* @return 当前对象引用
|
||||
*
|
||||
* 将其他渲染目标的资源转移到当前对象
|
||||
*/
|
||||
RenderTarget &RenderTarget::operator=(RenderTarget &&other) noexcept {
|
||||
if (this != &other) {
|
||||
destroy();
|
||||
|
||||
fbo_ = other.fbo_;
|
||||
rbo_ = other.rbo_;
|
||||
colorTexture_ = std::move(other.colorTexture_);
|
||||
depthTexture_ = std::move(other.depthTexture_);
|
||||
width_ = other.width_;
|
||||
height_ = other.height_;
|
||||
colorFormat_ = other.colorFormat_;
|
||||
hasDepth_ = other.hasDepth_;
|
||||
hasStencil_ = other.hasStencil_;
|
||||
samples_ = other.samples_;
|
||||
|
||||
other.fbo_ = 0;
|
||||
other.rbo_ = 0;
|
||||
other.width_ = 0;
|
||||
other.height_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据配置创建渲染目标
|
||||
* @param config 渲染目标配置
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*
|
||||
* 根据指定的配置参数创建帧缓冲对象
|
||||
*/
|
||||
bool RenderTarget::create(const RenderTargetConfig &config) {
|
||||
destroy();
|
||||
|
||||
width_ = config.width;
|
||||
height_ = config.height;
|
||||
colorFormat_ = config.colorFormat;
|
||||
hasDepth_ = config.hasDepth;
|
||||
hasStencil_ = config.hasStencil;
|
||||
samples_ = config.samples;
|
||||
|
||||
if (!createFBO()) {
|
||||
E2D_ERROR("创建渲染目标失败: {}x{}", width_, height_);
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_INFO("创建渲染目标: {}x{} (深度:{}, 模板:{}, 采样:{})", width_, height_,
|
||||
hasDepth_, hasStencil_, samples_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从现有纹理创建渲染目标
|
||||
* @param texture 颜色纹理
|
||||
* @param hasDepth 是否创建深度缓冲
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*
|
||||
* 使用现有纹理作为颜色附件创建帧缓冲对象
|
||||
*/
|
||||
bool RenderTarget::createFromTexture(Ptr<Texture> texture, bool hasDepth) {
|
||||
if (!texture || !texture->isValid()) {
|
||||
E2D_ERROR("无效的颜色纹理");
|
||||
return false;
|
||||
}
|
||||
|
||||
destroy();
|
||||
|
||||
width_ = texture->getWidth();
|
||||
height_ = texture->getHeight();
|
||||
colorFormat_ = texture->getFormat();
|
||||
hasDepth_ = hasDepth;
|
||||
hasStencil_ = false;
|
||||
samples_ = 1;
|
||||
|
||||
colorTexture_ = texture;
|
||||
|
||||
// 创建FBO
|
||||
glGenFramebuffers(1, &fbo_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
|
||||
// 附加颜色纹理
|
||||
GLuint texId = static_cast<GLuint>(
|
||||
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
texId, 0);
|
||||
|
||||
// 创建深度缓冲(如果需要)
|
||||
if (hasDepth_) {
|
||||
glGenRenderbuffers(1, &rbo_);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbo_);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width_,
|
||||
height_);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, rbo_);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
}
|
||||
|
||||
// 检查完整性
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
E2D_ERROR("FBO不完整: {:#x}", status);
|
||||
destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_INFO("从纹理创建渲染目标: {}x{}", width_, height_);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 销毁渲染目标
|
||||
*
|
||||
* 释放帧缓冲对象和相关资源
|
||||
*/
|
||||
void RenderTarget::destroy() {
|
||||
deleteFBO();
|
||||
|
||||
colorTexture_.reset();
|
||||
depthTexture_.reset();
|
||||
|
||||
width_ = 0;
|
||||
height_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绑定渲染目标
|
||||
*
|
||||
* 将此渲染目标绑定为当前渲染目标
|
||||
*/
|
||||
void RenderTarget::bind() {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
glViewport(0, 0, width_, height_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解绑渲染目标
|
||||
*
|
||||
* 恢复默认帧缓冲
|
||||
*/
|
||||
void RenderTarget::unbind() { bindDefault(); }
|
||||
|
||||
/**
|
||||
* @brief 清除渲染目标
|
||||
* @param color 清除颜色
|
||||
*
|
||||
* 使用指定颜色清除颜色缓冲,如有深度/模板缓冲也会清除
|
||||
*/
|
||||
void RenderTarget::clear(const Color &color) {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
|
||||
GLbitfield mask = GL_COLOR_BUFFER_BIT;
|
||||
if (hasDepth_) {
|
||||
mask |= GL_DEPTH_BUFFER_BIT;
|
||||
glClearDepthf(1.0f); // GLES 使用 glClearDepthf
|
||||
}
|
||||
if (hasStencil_) {
|
||||
mask |= GL_STENCIL_BUFFER_BIT;
|
||||
glClearStencil(0);
|
||||
}
|
||||
|
||||
glClearColor(color.r, color.g, color.b, color.a);
|
||||
glClear(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口区域
|
||||
* @param x 视口左下角X坐标
|
||||
* @param y 视口左下角Y坐标
|
||||
* @param width 视口宽度
|
||||
* @param height 视口高度
|
||||
*
|
||||
* 设置渲染目标的视口区域
|
||||
*/
|
||||
void RenderTarget::setViewport(int x, int y, int width, int height) {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取完整视口区域
|
||||
* @param[out] x 视口左下角X坐标
|
||||
* @param[out] y 视口左下角Y坐标
|
||||
* @param[out] width 视口宽度
|
||||
* @param[out] height 视口高度
|
||||
*
|
||||
* 获取渲染目标的完整视口区域
|
||||
*/
|
||||
void RenderTarget::getFullViewport(int &x, int &y, int &width,
|
||||
int &height) const {
|
||||
x = 0;
|
||||
y = 0;
|
||||
width = width_;
|
||||
height = height_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 调整渲染目标大小
|
||||
* @param width 新宽度
|
||||
* @param height 新高度
|
||||
* @return 调整成功返回true,失败返回false
|
||||
*
|
||||
* 重新创建指定大小的渲染目标
|
||||
*/
|
||||
bool RenderTarget::resize(int width, int height) {
|
||||
if (!isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RenderTargetConfig config;
|
||||
config.width = width;
|
||||
config.height = height;
|
||||
config.colorFormat = colorFormat_;
|
||||
config.hasDepth = hasDepth_;
|
||||
config.hasStencil = hasStencil_;
|
||||
config.samples = samples_;
|
||||
|
||||
return create(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 复制到另一个渲染目标
|
||||
* @param target 目标渲染目标
|
||||
*
|
||||
* 使用glBlitFramebuffer将内容复制到目标渲染目标
|
||||
*/
|
||||
void RenderTarget::copyTo(RenderTarget &target) {
|
||||
if (!isValid() || !target.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用glBlitFramebuffer复制
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target.fbo_);
|
||||
|
||||
GLbitfield mask = GL_COLOR_BUFFER_BIT;
|
||||
if (hasDepth_ && target.hasDepth_) {
|
||||
mask |= GL_DEPTH_BUFFER_BIT;
|
||||
}
|
||||
|
||||
glBlitFramebuffer(0, 0, width_, height_, 0, 0, target.width_, target.height_,
|
||||
mask, GL_LINEAR);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 复制到屏幕
|
||||
* @param screenWidth 屏幕宽度
|
||||
* @param screenHeight 屏幕高度
|
||||
*
|
||||
* 将渲染目标内容复制到默认帧缓冲(屏幕)
|
||||
*/
|
||||
void RenderTarget::copyToScreen(int screenWidth, int screenHeight) {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
||||
glBlitFramebuffer(0, 0, width_, height_, 0, 0, screenWidth, screenHeight,
|
||||
GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 保存渲染目标到文件
|
||||
* @param filepath 文件路径
|
||||
* @return 保存成功返回true,失败返回false
|
||||
*
|
||||
* 将渲染目标内容保存为PNG图片文件
|
||||
*/
|
||||
bool RenderTarget::saveToFile(const std::string &filepath) {
|
||||
if (!isValid() || !colorTexture_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 读取像素数据
|
||||
std::vector<uint8_t> pixels(width_ * height_ * 4);
|
||||
|
||||
bind();
|
||||
glReadPixels(0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
||||
unbind();
|
||||
|
||||
// 翻转Y轴(OpenGL坐标系原点在左下角,PNG需要左上角原点)
|
||||
std::vector<uint8_t> flipped(width_ * height_ * 4);
|
||||
for (int y = 0; y < height_; ++y) {
|
||||
for (int x = 0; x < width_; ++x) {
|
||||
int srcIdx = ((height_ - 1 - y) * width_ + x) * 4;
|
||||
int dstIdx = (y * width_ + x) * 4;
|
||||
for (int c = 0; c < 4; ++c) {
|
||||
flipped[dstIdx + c] = pixels[srcIdx + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用stb_image_write保存为PNG
|
||||
int result = stbi_write_png(filepath.c_str(), width_, height_, 4,
|
||||
flipped.data(), width_ * 4);
|
||||
|
||||
if (result == 0) {
|
||||
E2D_ERROR("保存渲染目标到PNG失败: {}", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_INFO("保存渲染目标到: {}", filepath);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据配置创建渲染目标
|
||||
* @param config 渲染目标配置
|
||||
* @return 创建成功返回渲染目标指针,失败返回nullptr
|
||||
*
|
||||
* 静态工厂方法,创建并初始化渲染目标
|
||||
*/
|
||||
Ptr<RenderTarget>
|
||||
RenderTarget::createFromConfig(const RenderTargetConfig &config) {
|
||||
auto rt = std::make_shared<RenderTarget>();
|
||||
if (rt->create(config)) {
|
||||
return rt;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前绑定的帧缓冲对象ID
|
||||
* @return 当前绑定的FBO ID
|
||||
*
|
||||
* 查询OpenGL当前绑定的帧缓冲对象
|
||||
*/
|
||||
GLuint RenderTarget::getCurrentFBO() {
|
||||
GLint fbo = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
|
||||
return static_cast<GLuint>(fbo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绑定默认帧缓冲
|
||||
*
|
||||
* 将默认帧缓冲(屏幕)绑定为当前渲染目标
|
||||
*/
|
||||
void RenderTarget::bindDefault() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
|
||||
|
||||
// ============================================================================
|
||||
// 内部方法
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建帧缓冲对象
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*
|
||||
* 内部方法,创建FBO及相关附件
|
||||
*/
|
||||
bool RenderTarget::createFBO() {
|
||||
// 创建颜色纹理
|
||||
colorTexture_ = GLTexture::create(width_, height_, colorFormat_);
|
||||
if (!colorTexture_ || !colorTexture_->isValid()) {
|
||||
E2D_ERROR("创建颜色纹理失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建FBO
|
||||
glGenFramebuffers(1, &fbo_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
|
||||
// 附加颜色纹理
|
||||
GLuint colorTexId =
|
||||
static_cast<GLTexture *>(colorTexture_.get())->getTextureID();
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
colorTexId, 0);
|
||||
|
||||
// 创建深度/模板缓冲
|
||||
if (hasDepth_ || hasStencil_) {
|
||||
glGenRenderbuffers(1, &rbo_);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbo_);
|
||||
|
||||
if (hasDepth_ && hasStencil_) {
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width_,
|
||||
height_);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, rbo_);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, rbo_);
|
||||
} else if (hasDepth_) {
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width_,
|
||||
height_);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, rbo_);
|
||||
}
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
}
|
||||
|
||||
// 检查完整性
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
E2D_ERROR("FBO创建失败,状态: {:#x}", status);
|
||||
deleteFBO();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 删除帧缓冲对象
|
||||
*
|
||||
* 内部方法,删除FBO和渲染缓冲对象
|
||||
*/
|
||||
void RenderTarget::deleteFBO() {
|
||||
if (rbo_ != 0) {
|
||||
glDeleteRenderbuffers(1, &rbo_);
|
||||
rbo_ = 0;
|
||||
}
|
||||
|
||||
if (fbo_ != 0) {
|
||||
glDeleteFramebuffers(1, &fbo_);
|
||||
fbo_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// MultisampleRenderTarget实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建多重采样渲染目标
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param samples 采样数
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*
|
||||
* 创建支持多重采样抗锯齿的渲染目标
|
||||
*/
|
||||
bool MultisampleRenderTarget::create(int width, int height, int samples) {
|
||||
// 先销毁现有的
|
||||
destroy();
|
||||
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
samples_ = samples;
|
||||
hasDepth_ = true;
|
||||
hasStencil_ = false;
|
||||
colorFormat_ = PixelFormat::RGBA8;
|
||||
|
||||
// 创建FBO
|
||||
glGenFramebuffers(1, &fbo_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
||||
|
||||
// 创建多重采样颜色渲染缓冲
|
||||
glGenRenderbuffers(1, &colorRBO_);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, colorRBO_);
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, width,
|
||||
height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_RENDERBUFFER, colorRBO_);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
// 创建多重采样深度渲染缓冲
|
||||
glGenRenderbuffers(1, &rbo_);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbo_);
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
|
||||
GL_DEPTH_COMPONENT24, width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, rbo_);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
// 检查完整性
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
E2D_ERROR("多重采样FBO创建失败,状态: {:#x}", status);
|
||||
destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_INFO("创建多重采样渲染目标: {}x{} (采样数: {})", width, height, samples);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 销毁多重采样渲染目标
|
||||
*
|
||||
* 释放多重采样渲染缓冲和帧缓冲对象
|
||||
*/
|
||||
void MultisampleRenderTarget::destroy() {
|
||||
// 删除颜色渲染缓冲
|
||||
if (colorRBO_ != 0) {
|
||||
glDeleteRenderbuffers(1, &colorRBO_);
|
||||
colorRBO_ = 0;
|
||||
}
|
||||
|
||||
// 调用基类destroy
|
||||
RenderTarget::destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析多重采样到目标渲染目标
|
||||
* @param target 目标渲染目标
|
||||
*
|
||||
* 将多重采样渲染目标解析到普通渲染目标
|
||||
*/
|
||||
void MultisampleRenderTarget::resolveTo(RenderTarget &target) {
|
||||
if (!isValid() || !target.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用glBlitFramebuffer解析多重采样
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target.getFBO());
|
||||
|
||||
glBlitFramebuffer(0, 0, width_, height_, 0, 0, target.getWidth(),
|
||||
target.getHeight(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// RenderTargetStack实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取RenderTargetStack单例实例
|
||||
* @return RenderTargetStack单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
RenderTargetStack &RenderTargetStack::get() {
|
||||
static RenderTargetStack instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 压入渲染目标到栈
|
||||
* @param target 渲染目标指针
|
||||
*
|
||||
* 将渲染目标压入栈并绑定为当前渲染目标
|
||||
*/
|
||||
void RenderTargetStack::push(RenderTarget *target) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (target) {
|
||||
stack_.push_back(target);
|
||||
target->bind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 弹出栈顶渲染目标
|
||||
*
|
||||
* 弹出当前渲染目标并恢复前一个渲染目标
|
||||
*/
|
||||
void RenderTargetStack::pop() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (!stack_.empty()) {
|
||||
stack_.pop_back();
|
||||
}
|
||||
|
||||
// 绑定新的当前渲染目标
|
||||
if (!stack_.empty()) {
|
||||
stack_.back()->bind();
|
||||
} else {
|
||||
RenderTarget::bindDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前渲染目标
|
||||
* @return 当前渲染目标指针,栈为空返回nullptr
|
||||
*/
|
||||
RenderTarget *RenderTargetStack::getCurrent() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (stack_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return stack_.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取栈大小
|
||||
* @return 栈中渲染目标的数量
|
||||
*/
|
||||
size_t RenderTargetStack::size() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return stack_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空渲染目标栈
|
||||
*
|
||||
* 清空栈并恢复默认帧缓冲
|
||||
*/
|
||||
void RenderTargetStack::clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
stack_.clear();
|
||||
RenderTarget::bindDefault();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// RenderTargetMgr实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取RenderTargetMgr单例实例
|
||||
* @return RenderTargetMgr单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
RenderTargetMgr &RenderTargetMgr::get() {
|
||||
static RenderTargetMgr instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化渲染目标管理器
|
||||
* @param width 默认渲染目标宽度
|
||||
* @param height 默认渲染目标高度
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*
|
||||
* 创建默认渲染目标
|
||||
*/
|
||||
bool RenderTargetMgr::init(int width, int height) {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 创建默认渲染目标
|
||||
RenderTargetConfig config;
|
||||
config.width = width;
|
||||
config.height = height;
|
||||
config.hasDepth = true;
|
||||
config.hasStencil = false;
|
||||
|
||||
defaultRenderTarget_ = RenderTarget::createFromConfig(config);
|
||||
if (!defaultRenderTarget_) {
|
||||
E2D_ERROR("创建默认渲染目标失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
E2D_INFO("渲染目标管理器初始化完成: {}x{}", width, height);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭渲染目标管理器
|
||||
*
|
||||
* 清理所有渲染目标资源
|
||||
*/
|
||||
void RenderTargetMgr::shutdown() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderTargets_.clear();
|
||||
defaultRenderTarget_.reset();
|
||||
initialized_ = false;
|
||||
|
||||
E2D_INFO("渲染目标管理器已关闭");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建渲染目标
|
||||
* @param config 渲染目标配置
|
||||
* @return 创建成功返回渲染目标指针,失败返回nullptr
|
||||
*
|
||||
* 创建新的渲染目标并由管理器管理
|
||||
*/
|
||||
Ptr<RenderTarget>
|
||||
RenderTargetMgr::createRenderTarget(const RenderTargetConfig &config) {
|
||||
if (!initialized_) {
|
||||
E2D_ERROR("渲染目标管理器未初始化");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto rt = RenderTarget::createFromConfig(config);
|
||||
if (rt) {
|
||||
renderTargets_.push_back(rt);
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 调整所有渲染目标大小
|
||||
* @param width 新宽度
|
||||
* @param height 新高度
|
||||
*
|
||||
* 调整默认渲染目标的大小
|
||||
*/
|
||||
void RenderTargetMgr::resize(int width, int height) {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 调整默认渲染目标大小
|
||||
if (defaultRenderTarget_) {
|
||||
defaultRenderTarget_->resize(width, height);
|
||||
}
|
||||
|
||||
E2D_INFO("渲染目标管理器调整大小: {}x{}", width, height);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#include <extra2d/graphics/memory/gpu_context.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 获取GPUContext单例实例
|
||||
* @return GPUContext单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
GPUContext& GPUContext::get() {
|
||||
static GPUContext instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 标记GPU上下文为有效状态
|
||||
*
|
||||
* 使用原子操作设置有效标志为true,使用release内存序
|
||||
*/
|
||||
void GPUContext::markValid() {
|
||||
valid_.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 标记GPU上下文为无效状态
|
||||
*
|
||||
* 使用原子操作设置有效标志为false,使用release内存序
|
||||
*/
|
||||
void GPUContext::markInvalid() {
|
||||
valid_.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查GPU上下文是否有效
|
||||
* @return 如果GPU上下文有效返回true,否则返回false
|
||||
*
|
||||
* 使用原子操作读取有效标志,使用acquire内存序
|
||||
*/
|
||||
bool GPUContext::isValid() const {
|
||||
return valid_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
#include <algorithm>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// Switch 推荐 VRAM 预算 ~400MB
|
||||
static constexpr size_t DEFAULT_VRAM_BUDGET = 400 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化VRAM管理器,设置默认预算为400MB
|
||||
*/
|
||||
VRAMMgr::VRAMMgr()
|
||||
: textureVRAM_(0), bufferVRAM_(0), vramBudget_(DEFAULT_VRAM_BUDGET),
|
||||
textureAllocCount_(0), textureFreeCount_(0), bufferAllocCount_(0),
|
||||
bufferFreeCount_(0), peakTextureVRAM_(0), peakBufferVRAM_(0) {}
|
||||
|
||||
/**
|
||||
* @brief 获取VRAMMgr单例实例
|
||||
* @return VRAMMgr单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
VRAMMgr &VRAMMgr::get() {
|
||||
static VRAMMgr instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 分配纹理VRAM
|
||||
* @param size 分配的字节数
|
||||
*
|
||||
* 增加纹理VRAM使用量并更新峰值,如果超出预算则输出警告
|
||||
*/
|
||||
void VRAMMgr::allocTexture(size_t size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
textureVRAM_ += size;
|
||||
textureAllocCount_++;
|
||||
peakTextureVRAM_ = std::max(peakTextureVRAM_, textureVRAM_);
|
||||
|
||||
if (isOverBudget()) {
|
||||
E2D_LOG_WARN("显存超出预算! 已使用: {} MB / 预算: {} MB",
|
||||
getUsedVRAM() / (1024 * 1024), vramBudget_ / (1024 * 1024));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放纹理VRAM
|
||||
* @param size 释放的字节数
|
||||
*
|
||||
* 减少纹理VRAM使用量
|
||||
*/
|
||||
void VRAMMgr::freeTexture(size_t size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (size <= textureVRAM_) {
|
||||
textureVRAM_ -= size;
|
||||
} else {
|
||||
textureVRAM_ = 0;
|
||||
}
|
||||
textureFreeCount_++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 分配缓冲VRAM
|
||||
* @param size 分配的字节数
|
||||
*
|
||||
* 增加缓冲VRAM使用量并更新峰值,如果超出预算则输出警告
|
||||
*/
|
||||
void VRAMMgr::allocBuffer(size_t size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
bufferVRAM_ += size;
|
||||
bufferAllocCount_++;
|
||||
peakBufferVRAM_ = std::max(peakBufferVRAM_, bufferVRAM_);
|
||||
|
||||
if (isOverBudget()) {
|
||||
E2D_LOG_WARN("显存超出预算! 已使用: {} MB / 预算: {} MB",
|
||||
getUsedVRAM() / (1024 * 1024), vramBudget_ / (1024 * 1024));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放缓冲VRAM
|
||||
* @param size 释放的字节数
|
||||
*
|
||||
* 减少缓冲VRAM使用量
|
||||
*/
|
||||
void VRAMMgr::freeBuffer(size_t size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (size <= bufferVRAM_) {
|
||||
bufferVRAM_ -= size;
|
||||
} else {
|
||||
bufferVRAM_ = 0;
|
||||
}
|
||||
bufferFreeCount_++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取总VRAM使用量
|
||||
* @return 总使用字节数
|
||||
*
|
||||
* 返回纹理和缓冲VRAM使用量的总和
|
||||
*/
|
||||
size_t VRAMMgr::getUsedVRAM() const { return textureVRAM_ + bufferVRAM_; }
|
||||
|
||||
/**
|
||||
* @brief 获取纹理VRAM使用量
|
||||
* @return 纹理使用字节数
|
||||
*/
|
||||
size_t VRAMMgr::getTextureVRAM() const { return textureVRAM_; }
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲VRAM使用量
|
||||
* @return 缓冲使用字节数
|
||||
*/
|
||||
size_t VRAMMgr::getBufferVRAM() const { return bufferVRAM_; }
|
||||
|
||||
/**
|
||||
* @brief 获取可用VRAM
|
||||
* @return 可用字节数
|
||||
*
|
||||
* 返回预算内剩余的VRAM空间
|
||||
*/
|
||||
size_t VRAMMgr::getAvailableVRAM() const {
|
||||
size_t used = getUsedVRAM();
|
||||
return (used < vramBudget_) ? (vramBudget_ - used) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置VRAM预算
|
||||
* @param budget 预算字节数
|
||||
*
|
||||
* 设置VRAM使用上限
|
||||
*/
|
||||
void VRAMMgr::setVRAMBudget(size_t budget) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
vramBudget_ = budget;
|
||||
E2D_LOG_INFO("显存预算设置为 {} MB", budget / (1024 * 1024));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取VRAM预算
|
||||
* @return 预算字节数
|
||||
*/
|
||||
size_t VRAMMgr::getVRAMBudget() const { return vramBudget_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否超出预算
|
||||
* @return 超出预算返回true,否则返回false
|
||||
*/
|
||||
bool VRAMMgr::isOverBudget() const { return getUsedVRAM() > vramBudget_; }
|
||||
|
||||
/**
|
||||
* @brief 打印VRAM统计信息
|
||||
*
|
||||
* 输出纹理和缓冲的VRAM使用情况、峰值和分配/释放次数
|
||||
*/
|
||||
void VRAMMgr::printStats() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
E2D_LOG_INFO("=== 显存统计 ===");
|
||||
E2D_LOG_INFO(" 纹理显存: {} MB (峰值: {} MB)",
|
||||
textureVRAM_ / (1024 * 1024), peakTextureVRAM_ / (1024 * 1024));
|
||||
E2D_LOG_INFO(" 缓冲显存: {} MB (峰值: {} MB)",
|
||||
bufferVRAM_ / (1024 * 1024), peakBufferVRAM_ / (1024 * 1024));
|
||||
E2D_LOG_INFO(" 总计使用: {} MB / {} MB 预算",
|
||||
(textureVRAM_ + bufferVRAM_) / (1024 * 1024),
|
||||
vramBudget_ / (1024 * 1024));
|
||||
E2D_LOG_INFO(" 纹理分配/释放次数: {} / {}", textureAllocCount_,
|
||||
textureFreeCount_);
|
||||
E2D_LOG_INFO(" 缓冲分配/释放次数: {} / {}", bufferAllocCount_,
|
||||
bufferFreeCount_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重置所有统计信息
|
||||
*
|
||||
* 清零所有VRAM计数器和峰值记录
|
||||
*/
|
||||
void VRAMMgr::reset() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
textureVRAM_ = 0;
|
||||
bufferVRAM_ = 0;
|
||||
textureAllocCount_ = 0;
|
||||
textureFreeCount_ = 0;
|
||||
bufferAllocCount_ = 0;
|
||||
bufferFreeCount_ = 0;
|
||||
peakTextureVRAM_ = 0;
|
||||
peakBufferVRAM_ = 0;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,293 +0,0 @@
|
|||
#include <chrono>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/shader/shader_cache.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 缓存管理器实例引用
|
||||
*/
|
||||
ShaderCache &ShaderCache::getInstance() {
|
||||
static ShaderCache instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化缓存系统
|
||||
* @param cacheDir 缓存目录路径
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderCache::init(const std::string &cacheDir) {
|
||||
cacheDir_ = cacheDir;
|
||||
|
||||
if (!ensureCacheDirectory()) {
|
||||
E2D_LOG_ERROR("创建缓存目录失败: {}", cacheDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loadCacheIndex()) {
|
||||
E2D_LOG_WARN("加载缓存索引失败,重新开始");
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("着色器缓存已初始化,位置: {}", cacheDir);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭缓存系统
|
||||
*/
|
||||
void ShaderCache::shutdown() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
saveCacheIndex();
|
||||
cacheMap_.clear();
|
||||
initialized_ = false;
|
||||
E2D_LOG_INFO("着色器缓存已关闭");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查缓存是否有效
|
||||
* @param name Shader名称
|
||||
* @param sourceHash 源码哈希值
|
||||
* @return 缓存有效返回true,否则返回false
|
||||
*/
|
||||
bool ShaderCache::hasValidCache(const std::string &name,
|
||||
const std::string &sourceHash) {
|
||||
auto it = cacheMap_.find(name);
|
||||
if (it == cacheMap_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return it->second.sourceHash == sourceHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加载缓存的二进制数据
|
||||
* @param name Shader名称
|
||||
* @return 缓存条目指针,不存在返回nullptr
|
||||
*/
|
||||
Ptr<ShaderCacheEntry> ShaderCache::loadCache(const std::string &name) {
|
||||
auto it = cacheMap_.find(name);
|
||||
if (it == cacheMap_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string cachePath = getCachePath(name);
|
||||
std::ifstream file(cachePath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_WARN("打开缓存文件失败: {}", cachePath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto entry = std::make_shared<ShaderCacheEntry>(it->second);
|
||||
entry->binary.clear();
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
size_t fileSize = static_cast<size_t>(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
entry->binary.resize(fileSize);
|
||||
file.read(reinterpret_cast<char *>(entry->binary.data()), fileSize);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 保存编译结果到缓存
|
||||
* @param entry 缓存条目
|
||||
* @return 保存成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderCache::saveCache(const ShaderCacheEntry &entry) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_WARN("着色器缓存未初始化,无法保存缓存");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.binary.empty()) {
|
||||
E2D_LOG_WARN("着色器二进制数据为空,跳过缓存保存: {}",
|
||||
entry.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string cachePath = getCachePath(entry.name);
|
||||
E2D_LOG_DEBUG("正在保存着色器缓存到: {} ({} 字节)", cachePath,
|
||||
entry.binary.size());
|
||||
|
||||
std::ofstream file(cachePath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("创建缓存文件失败: {}", cachePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
file.write(reinterpret_cast<const char *>(entry.binary.data()),
|
||||
entry.binary.size());
|
||||
file.close();
|
||||
|
||||
cacheMap_[entry.name] = entry;
|
||||
saveCacheIndex();
|
||||
|
||||
E2D_LOG_INFO("着色器缓存已保存: {} ({} 字节)", entry.name,
|
||||
entry.binary.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使缓存失效
|
||||
* @param name Shader名称
|
||||
*/
|
||||
void ShaderCache::invalidate(const std::string &name) {
|
||||
auto it = cacheMap_.find(name);
|
||||
if (it == cacheMap_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string cachePath = getCachePath(name);
|
||||
fs::remove(cachePath);
|
||||
|
||||
cacheMap_.erase(it);
|
||||
saveCacheIndex();
|
||||
|
||||
E2D_LOG_DEBUG("着色器缓存已失效: {}", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除所有缓存
|
||||
*/
|
||||
void ShaderCache::clearAll() {
|
||||
for (const auto &pair : cacheMap_) {
|
||||
std::string cachePath = getCachePath(pair.first);
|
||||
fs::remove(cachePath);
|
||||
}
|
||||
|
||||
cacheMap_.clear();
|
||||
saveCacheIndex();
|
||||
|
||||
E2D_LOG_INFO("所有着色器缓存已清除");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算源码哈希值
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 哈希值字符串
|
||||
*/
|
||||
std::string ShaderCache::computeHash(const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
std::string combined = vertSource + fragSource;
|
||||
|
||||
uint32_t hash = 5381;
|
||||
for (char c : combined) {
|
||||
hash = ((hash << 5) + hash) + static_cast<uint32_t>(c);
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::hex << hash;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加载缓存索引
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderCache::loadCacheIndex() {
|
||||
std::string indexPath = cacheDir_ + "/.cache_index";
|
||||
|
||||
if (!fs::exists(indexPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ifstream file(indexPath);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t pos = line.find('=');
|
||||
if (pos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string name = line.substr(0, pos);
|
||||
std::string hash = line.substr(pos + 1);
|
||||
|
||||
std::string cachePath = getCachePath(name);
|
||||
if (fs::exists(cachePath)) {
|
||||
ShaderCacheEntry entry;
|
||||
entry.name = name;
|
||||
entry.sourceHash = hash;
|
||||
entry.compileTime = static_cast<uint64_t>(
|
||||
std::chrono::system_clock::now().time_since_epoch().count());
|
||||
cacheMap_[name] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 保存缓存索引
|
||||
* @return 保存成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderCache::saveCacheIndex() {
|
||||
std::string indexPath = cacheDir_ + "/.cache_index";
|
||||
|
||||
std::ofstream file(indexPath);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file << "# Extra2D Shader Cache Index\n";
|
||||
file << "# Format: name=hash\n";
|
||||
|
||||
for (const auto &pair : cacheMap_) {
|
||||
file << pair.first << "=" << pair.second.sourceHash << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取缓存文件路径
|
||||
* @param name Shader名称
|
||||
* @return 缓存文件完整路径
|
||||
*/
|
||||
std::string ShaderCache::getCachePath(const std::string &name) const {
|
||||
return cacheDir_ + "/" + name + ".cache";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 确保缓存目录存在
|
||||
* @return 目录存在或创建成功返回true,否则返回false
|
||||
*/
|
||||
bool ShaderCache::ensureCacheDirectory() {
|
||||
if (cacheDir_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
if (!fs::exists(cacheDir_)) {
|
||||
if (!fs::create_directories(cacheDir_, ec)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
#include <chrono>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/shader/shader_hot_reloader.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <filesystem>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 热重载管理器实例引用
|
||||
*/
|
||||
ShaderHotReloader &ShaderHotReloader::getInstance() {
|
||||
static ShaderHotReloader instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化热重载系统
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderHotReloader::init() {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
buffer_.resize(4096);
|
||||
#endif
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("着色器热重载器已初始化");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭热重载系统
|
||||
*/
|
||||
void ShaderHotReloader::shutdown() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (watchHandle_ != nullptr) {
|
||||
FindCloseChangeNotification(watchHandle_);
|
||||
watchHandle_ = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
watchMap_.clear();
|
||||
initialized_ = false;
|
||||
enabled_ = false;
|
||||
E2D_LOG_INFO("着色器热重载器已关闭");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册Shader文件监视
|
||||
* @param shaderName Shader名称
|
||||
* @param filePaths 要监视的文件列表
|
||||
* @param callback 文件变化时的回调
|
||||
*/
|
||||
void ShaderHotReloader::watch(const std::string &shaderName,
|
||||
const std::vector<std::string> &filePaths,
|
||||
FileChangeCallback callback) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_WARN("热重载器未初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
WatchInfo info;
|
||||
info.filePaths = filePaths;
|
||||
info.callback = callback;
|
||||
|
||||
for (const auto &path : filePaths) {
|
||||
info.modifiedTimes[path] = getFileModifiedTime(path);
|
||||
}
|
||||
|
||||
watchMap_[shaderName] = std::move(info);
|
||||
E2D_LOG_DEBUG("正在监视着色器: {} ({} 个文件)", shaderName, filePaths.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 取消监视
|
||||
* @param shaderName Shader名称
|
||||
*/
|
||||
void ShaderHotReloader::unwatch(const std::string &shaderName) {
|
||||
auto it = watchMap_.find(shaderName);
|
||||
if (it != watchMap_.end()) {
|
||||
watchMap_.erase(it);
|
||||
E2D_LOG_DEBUG("停止监视着色器: {}", shaderName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新文件监视(在主循环中调用)
|
||||
*/
|
||||
void ShaderHotReloader::update() {
|
||||
if (!initialized_ || !enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
pollChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用热重载
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void ShaderHotReloader::setEnabled(bool enabled) {
|
||||
enabled_ = enabled;
|
||||
E2D_LOG_DEBUG("热重载已{}", enabled ? "Enable" : "Disable");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 轮询检查文件变化
|
||||
*/
|
||||
void ShaderHotReloader::pollChanges() {
|
||||
auto now = static_cast<uint64_t>(
|
||||
std::chrono::system_clock::now().time_since_epoch().count());
|
||||
|
||||
for (auto &pair : watchMap_) {
|
||||
WatchInfo &info = pair.second;
|
||||
|
||||
for (const auto &filePath : info.filePaths) {
|
||||
uint64_t currentModTime = getFileModifiedTime(filePath);
|
||||
uint64_t lastModTime = info.modifiedTimes[filePath];
|
||||
|
||||
if (currentModTime != 0 && lastModTime != 0 &&
|
||||
currentModTime != lastModTime) {
|
||||
info.modifiedTimes[filePath] = currentModTime;
|
||||
|
||||
FileChangeEvent event;
|
||||
event.filepath = filePath;
|
||||
event.type = FileChangeEvent::Type::Modified;
|
||||
event.timestamp = now;
|
||||
|
||||
E2D_LOG_DEBUG("着色器文件已更改: {}", filePath);
|
||||
|
||||
if (info.callback) {
|
||||
info.callback(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件修改时间
|
||||
* @param filepath 文件路径
|
||||
* @return 修改时间戳
|
||||
*/
|
||||
uint64_t ShaderHotReloader::getFileModifiedTime(const std::string &filepath) {
|
||||
try {
|
||||
auto ftime = fs::last_write_time(filepath);
|
||||
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||
ftime - fs::file_time_type::clock::now() +
|
||||
std::chrono::system_clock::now());
|
||||
return static_cast<uint64_t>(sctp.time_since_epoch().count());
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,452 +0,0 @@
|
|||
#include <algorithm>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/shader/shader_loader.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
/**
|
||||
* @brief 构造函数,初始化Shader加载器
|
||||
*/
|
||||
ShaderLoader::ShaderLoader() {}
|
||||
|
||||
/**
|
||||
* @brief 从分离文件加载Shader (.vert + .frag)
|
||||
* @param name Shader名称
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult
|
||||
ShaderLoader::loadFromSeparateFiles(const std::string &name,
|
||||
const std::string &vertPath,
|
||||
const std::string &fragPath) {
|
||||
|
||||
ShaderLoadResult result;
|
||||
|
||||
if (!fileExists(vertPath)) {
|
||||
result.errorMessage = "Vertex shader file not found: " + vertPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!fileExists(fragPath)) {
|
||||
result.errorMessage = "Fragment shader file not found: " + fragPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string vertSource = readFile(vertPath);
|
||||
std::string fragSource = readFile(fragPath);
|
||||
|
||||
if (vertSource.empty()) {
|
||||
result.errorMessage = "Failed to read vertex shader file: " + vertPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fragSource.empty()) {
|
||||
result.errorMessage = "Failed to read fragment shader file: " + fragPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::path vertDir = fs::path(vertPath).parent_path();
|
||||
fs::path fragDir = fs::path(fragPath).parent_path();
|
||||
|
||||
vertSource =
|
||||
processIncludes(vertSource, vertDir.string(), result.dependencies);
|
||||
fragSource =
|
||||
processIncludes(fragSource, fragDir.string(), result.dependencies);
|
||||
|
||||
result.vertSource = vertSource;
|
||||
result.fragSource = fragSource;
|
||||
result.success = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从组合文件加载Shader (.shader)
|
||||
* @param path 组合Shader文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult ShaderLoader::loadFromCombinedFile(const std::string &path) {
|
||||
ShaderLoadResult result;
|
||||
|
||||
if (!fileExists(path)) {
|
||||
result.errorMessage = "Shader file not found: " + path;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string content = readFile(path);
|
||||
if (content.empty()) {
|
||||
result.errorMessage = "Failed to read shader file: " + path;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
ShaderMetadata metadata;
|
||||
std::string vertSource, fragSource;
|
||||
|
||||
if (!parseCombinedFile(content, vertSource, fragSource, metadata)) {
|
||||
result.errorMessage = "Failed to parse combined shader file: " + path;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::path baseDir = fs::path(path).parent_path();
|
||||
vertSource =
|
||||
processIncludes(vertSource, baseDir.string(), result.dependencies);
|
||||
fragSource =
|
||||
processIncludes(fragSource, baseDir.string(), result.dependencies);
|
||||
|
||||
result.vertSource = vertSource;
|
||||
result.fragSource = fragSource;
|
||||
result.success = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从源码字符串加载Shader
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult ShaderLoader::loadFromSource(const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
|
||||
ShaderLoadResult result;
|
||||
result.vertSource = vertSource;
|
||||
result.fragSource = fragSource;
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理Shader源码中的#include指令
|
||||
* @param source 原始源码
|
||||
* @param baseDir 基础目录
|
||||
* @param outDependencies 输出依赖列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
std::string
|
||||
ShaderLoader::processIncludes(const std::string &source,
|
||||
const std::string &baseDir,
|
||||
std::vector<std::string> &outDependencies) {
|
||||
|
||||
std::string result;
|
||||
std::istringstream stream(source);
|
||||
std::string line;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
size_t includePos = line.find("#include");
|
||||
if (includePos != std::string::npos) {
|
||||
size_t startQuote = line.find('"', includePos);
|
||||
size_t endQuote = line.find('"', startQuote + 1);
|
||||
|
||||
if (startQuote != std::string::npos && endQuote != std::string::npos) {
|
||||
std::string includeName =
|
||||
line.substr(startQuote + 1, endQuote - startQuote - 1);
|
||||
std::string includePath = findIncludeFile(includeName, baseDir);
|
||||
|
||||
if (!includePath.empty()) {
|
||||
auto cacheIt = includeCache_.find(includePath);
|
||||
std::string includeContent;
|
||||
|
||||
if (cacheIt != includeCache_.end()) {
|
||||
includeContent = cacheIt->second;
|
||||
} else {
|
||||
includeContent = readFile(includePath);
|
||||
includeCache_[includePath] = includeContent;
|
||||
}
|
||||
|
||||
outDependencies.push_back(includePath);
|
||||
result += includeContent;
|
||||
result += "\n";
|
||||
continue;
|
||||
} else {
|
||||
E2D_LOG_WARN("未找到包含文件: {}", includeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result += line;
|
||||
result += "\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 应用预处理器定义
|
||||
* @param source 原始源码
|
||||
* @param defines 预处理器定义列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
std::string
|
||||
ShaderLoader::applyDefines(const std::string &source,
|
||||
const std::vector<std::string> &defines) {
|
||||
|
||||
if (defines.empty()) {
|
||||
return source;
|
||||
}
|
||||
|
||||
std::string defineBlock;
|
||||
for (const auto &def : defines) {
|
||||
defineBlock += "#define " + def + "\n";
|
||||
}
|
||||
|
||||
std::string result;
|
||||
std::istringstream stream(source);
|
||||
std::string line;
|
||||
bool inserted = false;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
if (!inserted && (line.find("#version") != std::string::npos ||
|
||||
line.find("precision") != std::string::npos)) {
|
||||
result += line + "\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inserted) {
|
||||
result += defineBlock;
|
||||
inserted = true;
|
||||
}
|
||||
|
||||
result += line + "\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取Shader元数据
|
||||
* @param path Shader文件路径
|
||||
* @return 元数据
|
||||
*/
|
||||
ShaderMetadata ShaderLoader::getMetadata(const std::string &path) {
|
||||
ShaderMetadata metadata;
|
||||
|
||||
if (!fileExists(path)) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
metadata.combinedPath = path;
|
||||
metadata.lastModified = getFileModifiedTime(path);
|
||||
|
||||
fs::path p(path);
|
||||
metadata.name = p.stem().string();
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加include搜索路径
|
||||
* @param path 搜索路径
|
||||
*/
|
||||
void ShaderLoader::addIncludePath(const std::string &path) {
|
||||
if (std::find(includePaths_.begin(), includePaths_.end(), path) ==
|
||||
includePaths_.end()) {
|
||||
includePaths_.push_back(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取文件内容
|
||||
* @param filepath 文件路径
|
||||
* @return 文件内容字符串
|
||||
*/
|
||||
std::string ShaderLoader::readFile(const std::string &filepath) {
|
||||
std::ifstream file(filepath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::ostringstream content;
|
||||
content << file.rdbuf();
|
||||
return content.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件修改时间
|
||||
* @param filepath 文件路径
|
||||
* @return 修改时间戳
|
||||
*/
|
||||
uint64_t ShaderLoader::getFileModifiedTime(const std::string &filepath) {
|
||||
#ifdef __SWITCH__
|
||||
(void)filepath;
|
||||
return 1;
|
||||
#else
|
||||
try {
|
||||
auto ftime = fs::last_write_time(filepath);
|
||||
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||
ftime - fs::file_time_type::clock::now() +
|
||||
std::chrono::system_clock::now());
|
||||
return static_cast<uint64_t>(sctp.time_since_epoch().count());
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查文件是否存在
|
||||
* @param filepath 文件路径
|
||||
* @return 存在返回true,否则返回false
|
||||
*/
|
||||
bool ShaderLoader::fileExists(const std::string &filepath) {
|
||||
return fs::exists(filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析组合Shader文件
|
||||
* @param content 文件内容
|
||||
* @param outVert 输出顶点着色器源码
|
||||
* @param outFrag 输出片段着色器源码
|
||||
* @param outMetadata 输出元数据
|
||||
* @return 解析成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderLoader::parseCombinedFile(const std::string &content,
|
||||
std::string &outVert, std::string &outFrag,
|
||||
ShaderMetadata &outMetadata) {
|
||||
enum class Section { None, Meta, Vertex, Fragment };
|
||||
|
||||
Section currentSection = Section::None;
|
||||
std::string metaContent;
|
||||
std::string vertContent;
|
||||
std::string fragContent;
|
||||
|
||||
std::istringstream stream(content);
|
||||
std::string line;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
std::string trimmedLine = line;
|
||||
size_t start = trimmedLine.find_first_not_of(" \t\r\n");
|
||||
if (start != std::string::npos) {
|
||||
trimmedLine = trimmedLine.substr(start);
|
||||
}
|
||||
size_t end = trimmedLine.find_last_not_of(" \t\r\n");
|
||||
if (end != std::string::npos) {
|
||||
trimmedLine = trimmedLine.substr(0, end + 1);
|
||||
}
|
||||
|
||||
if (trimmedLine == "#meta") {
|
||||
currentSection = Section::Meta;
|
||||
continue;
|
||||
} else if (trimmedLine == "#vertex") {
|
||||
currentSection = Section::Vertex;
|
||||
continue;
|
||||
} else if (trimmedLine == "#fragment") {
|
||||
currentSection = Section::Fragment;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (currentSection) {
|
||||
case Section::Meta:
|
||||
metaContent += line + "\n";
|
||||
break;
|
||||
case Section::Vertex:
|
||||
vertContent += line + "\n";
|
||||
break;
|
||||
case Section::Fragment:
|
||||
fragContent += line + "\n";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (vertContent.empty() || fragContent.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!metaContent.empty()) {
|
||||
parseMetadata(metaContent, outMetadata);
|
||||
}
|
||||
|
||||
outVert = vertContent;
|
||||
outFrag = fragContent;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析元数据JSON块
|
||||
* @param jsonContent JSON内容
|
||||
* @param outMetadata 输出元数据
|
||||
* @return 解析成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderLoader::parseMetadata(const std::string &jsonContent,
|
||||
ShaderMetadata &outMetadata) {
|
||||
std::string content = jsonContent;
|
||||
|
||||
size_t start = content.find('{');
|
||||
size_t end = content.rfind('}');
|
||||
if (start == std::string::npos || end == std::string::npos || end <= start) {
|
||||
return false;
|
||||
}
|
||||
|
||||
content = content.substr(start, end - start + 1);
|
||||
|
||||
auto extractString = [&content](const std::string &key) -> std::string {
|
||||
std::string searchKey = "\"" + key + "\"";
|
||||
size_t keyPos = content.find(searchKey);
|
||||
if (keyPos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t colonPos = content.find(':', keyPos);
|
||||
if (colonPos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t quoteStart = content.find('"', colonPos);
|
||||
if (quoteStart == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t quoteEnd = content.find('"', quoteStart + 1);
|
||||
if (quoteEnd == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return content.substr(quoteStart + 1, quoteEnd - quoteStart - 1);
|
||||
};
|
||||
|
||||
outMetadata.name = extractString("name");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查找include文件路径
|
||||
* @param includeName include文件名
|
||||
* @param baseDir 基础目录
|
||||
* @return 找到的完整路径,未找到返回空字符串
|
||||
*/
|
||||
std::string ShaderLoader::findIncludeFile(const std::string &includeName,
|
||||
const std::string &baseDir) {
|
||||
fs::path basePath(baseDir);
|
||||
fs::path includePath = basePath / includeName;
|
||||
|
||||
if (fs::exists(includePath)) {
|
||||
return includePath.string();
|
||||
}
|
||||
|
||||
for (const auto &searchPath : includePaths_) {
|
||||
includePath = fs::path(searchPath) / includeName;
|
||||
if (fs::exists(includePath)) {
|
||||
return includePath.string();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,855 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace nl = nlohmann;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return Shader管理器实例引用
|
||||
*/
|
||||
ShaderManager &ShaderManager::getInstance() {
|
||||
static ShaderManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使用平台默认路径初始化Shader系统
|
||||
* 自动检测平台并使用正确的路径(romfs/sdmc/相对路径)
|
||||
* @param factory 渲染后端Shader工厂
|
||||
* @param appName 应用名称(用于缓存目录)
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderManager::init(Ptr<IShaderFactory> factory,
|
||||
const std::string &appName) {
|
||||
// 使用相对路径作为Shader目录
|
||||
fs::path shaderDir = "shaders";
|
||||
fs::path cacheDir = "cache/shaders";
|
||||
|
||||
// 非Switch平台支持热重载
|
||||
#ifndef __SWITCH__
|
||||
hotReloadSupported_ = true;
|
||||
#else
|
||||
hotReloadSupported_ = false;
|
||||
#endif
|
||||
|
||||
E2D_LOG_INFO("ShaderManager 初始化 (热重载: {})",
|
||||
hotReloadSupported_ ? "supported" : "not supported");
|
||||
|
||||
return init(shaderDir.string(), cacheDir.string(), factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化Shader系统
|
||||
* @param shaderDir Shader文件目录
|
||||
* @param cacheDir 缓存目录
|
||||
* @param factory 渲染后端Shader工厂
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderManager::init(const std::string &shaderDir,
|
||||
const std::string &cacheDir,
|
||||
Ptr<IShaderFactory> factory) {
|
||||
if (initialized_) {
|
||||
E2D_LOG_WARN("ShaderManager 已初始化");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!factory) {
|
||||
E2D_LOG_ERROR("Shader 工厂为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
shaderDir_ = shaderDir;
|
||||
cacheDir_ = cacheDir;
|
||||
factory_ = factory;
|
||||
|
||||
// 非Switch平台支持热重载
|
||||
#ifndef __SWITCH__
|
||||
hotReloadSupported_ = true;
|
||||
#else
|
||||
hotReloadSupported_ = false;
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
||||
E2D_LOG_WARN("Switch 平台初始化着色器缓存失败");
|
||||
}
|
||||
#else
|
||||
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
||||
E2D_LOG_WARN("初始化着色器缓存失败,已禁用缓存");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (hotReloadSupported_) {
|
||||
if (!ShaderHotReloader::getInstance().init()) {
|
||||
E2D_LOG_WARN("初始化热重载器失败");
|
||||
}
|
||||
}
|
||||
|
||||
loader_.addIncludePath(shaderDir_ + "common");
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("ShaderManager 初始化成功");
|
||||
E2D_LOG_INFO(" 着色器目录: {}", shaderDir_);
|
||||
E2D_LOG_INFO(" 缓存目录: {}", cacheDir_);
|
||||
E2D_LOG_INFO(" 热重载: {}", hotReloadSupported_ ? "支持" : "不支持");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭Shader系统
|
||||
*/
|
||||
void ShaderManager::shutdown() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotReloadSupported_) {
|
||||
ShaderHotReloader::getInstance().shutdown();
|
||||
}
|
||||
ShaderCache::getInstance().shutdown();
|
||||
|
||||
shaders_.clear();
|
||||
factory_.reset();
|
||||
initialized_ = false;
|
||||
|
||||
E2D_LOG_INFO("ShaderManager 已关闭");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从分离文件加载Shader
|
||||
* @param name Shader名称
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::loadFromFiles(const std::string &name,
|
||||
const std::string &vertPath,
|
||||
const std::string &fragPath) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager 未初始化");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
|
||||
ShaderLoadResult result =
|
||||
loader_.loadFromSeparateFiles(name, vertPath, fragPath);
|
||||
if (!result.success) {
|
||||
E2D_LOG_ERROR("加载着色器文件失败: {} - {}", vertPath, fragPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string sourceHash =
|
||||
ShaderCache::computeHash(result.vertSource, result.fragSource);
|
||||
Ptr<IShader> shader =
|
||||
loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
|
||||
|
||||
if (!shader) {
|
||||
E2D_LOG_DEBUG("未找到有效缓存,从源码编译着色器: {}", name);
|
||||
shader =
|
||||
factory_->createFromSource(name, result.vertSource, result.fragSource);
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("从源码创建着色器失败: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> binary;
|
||||
if (factory_->getShaderBinary(*shader, binary)) {
|
||||
E2D_LOG_DEBUG("获取到着色器二进制数据,大小: {} 字节", binary.size());
|
||||
ShaderCacheEntry entry;
|
||||
entry.name = name;
|
||||
entry.sourceHash = sourceHash;
|
||||
entry.binary = binary;
|
||||
entry.dependencies = result.dependencies;
|
||||
ShaderCache::getInstance().saveCache(entry);
|
||||
} else {
|
||||
E2D_LOG_WARN("获取着色器二进制数据失败: {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderInfo info;
|
||||
info.shader = shader;
|
||||
info.vertSource = result.vertSource;
|
||||
info.fragSource = result.fragSource;
|
||||
info.filePaths = {vertPath, fragPath};
|
||||
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(),
|
||||
result.dependencies.end());
|
||||
|
||||
info.metadata.name = name;
|
||||
info.metadata.vertPath = vertPath;
|
||||
info.metadata.fragPath = fragPath;
|
||||
|
||||
shaders_[name] = std::move(info);
|
||||
|
||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
||||
auto callback = [this, name](const FileChangeEvent &event) {
|
||||
this->handleFileChange(name, event);
|
||||
};
|
||||
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths,
|
||||
callback);
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("着色器已加载: {}", name);
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从组合文件加载Shader
|
||||
* @param path 组合Shader文件路径
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::loadFromCombinedFile(const std::string &path) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager 未初始化");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ShaderMetadata metadata = loader_.getMetadata(path);
|
||||
std::string name = metadata.name.empty() ? path : metadata.name;
|
||||
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
|
||||
ShaderLoadResult result = loader_.loadFromCombinedFile(path);
|
||||
if (!result.success) {
|
||||
E2D_LOG_ERROR("加载组合着色器文件失败: {}", path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string sourceHash =
|
||||
ShaderCache::computeHash(result.vertSource, result.fragSource);
|
||||
Ptr<IShader> shader =
|
||||
loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
|
||||
|
||||
if (!shader) {
|
||||
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}",
|
||||
name);
|
||||
shader =
|
||||
factory_->createFromSource(name, result.vertSource, result.fragSource);
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("从源码创建着色器失败: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> binary;
|
||||
if (factory_->getShaderBinary(*shader, binary)) {
|
||||
E2D_LOG_DEBUG("获取到着色器二进制数据,大小: {} 字节", binary.size());
|
||||
ShaderCacheEntry entry;
|
||||
entry.name = name;
|
||||
entry.sourceHash = sourceHash;
|
||||
entry.binary = binary;
|
||||
entry.dependencies = result.dependencies;
|
||||
ShaderCache::getInstance().saveCache(entry);
|
||||
} else {
|
||||
E2D_LOG_WARN("获取着色器二进制数据失败: {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderInfo info;
|
||||
info.shader = shader;
|
||||
info.vertSource = result.vertSource;
|
||||
info.fragSource = result.fragSource;
|
||||
info.filePaths = {path};
|
||||
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(),
|
||||
result.dependencies.end());
|
||||
info.metadata = metadata;
|
||||
|
||||
shaders_[name] = std::move(info);
|
||||
|
||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
||||
auto callback = [this, name](const FileChangeEvent &event) {
|
||||
this->handleFileChange(name, event);
|
||||
};
|
||||
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths,
|
||||
callback);
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("从组合文件加载着色器成功: {}", name);
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从源码加载Shader
|
||||
* @param name Shader名称
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::loadFromSource(const std::string &name,
|
||||
const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager 未初始化");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
|
||||
Ptr<IShader> shader =
|
||||
factory_->createFromSource(name, vertSource, fragSource);
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("从源码创建着色器失败: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ShaderInfo info;
|
||||
info.shader = shader;
|
||||
info.vertSource = vertSource;
|
||||
info.fragSource = fragSource;
|
||||
info.metadata.name = name;
|
||||
|
||||
shaders_[name] = std::move(info);
|
||||
|
||||
E2D_LOG_DEBUG("从源码加载着色器成功: {}", name);
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取已加载的Shader
|
||||
* @param name Shader名称
|
||||
* @return Shader实例,不存在返回nullptr
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::get(const std::string &name) const {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查Shader是否存在
|
||||
* @param name Shader名称
|
||||
* @return 存在返回true,否则返回false
|
||||
*/
|
||||
bool ShaderManager::has(const std::string &name) const {
|
||||
return shaders_.find(name) != shaders_.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除Shader
|
||||
* @param name Shader名称
|
||||
*/
|
||||
void ShaderManager::remove(const std::string &name) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
ShaderHotReloader::getInstance().unwatch(name);
|
||||
shaders_.erase(it);
|
||||
E2D_LOG_DEBUG("着色器已移除: {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除所有Shader
|
||||
*/
|
||||
void ShaderManager::clear() {
|
||||
if (hotReloadSupported_) {
|
||||
for (const auto &pair : shaders_) {
|
||||
ShaderHotReloader::getInstance().unwatch(pair.first);
|
||||
}
|
||||
}
|
||||
shaders_.clear();
|
||||
E2D_LOG_DEBUG("所有着色器已清除");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册重载回调
|
||||
* @param name Shader名称
|
||||
* @param callback 重载回调函数
|
||||
*/
|
||||
void ShaderManager::setReloadCallback(const std::string &name,
|
||||
ShaderReloadCallback callback) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
it->second.reloadCallback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用热重载
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void ShaderManager::setHotReloadEnabled(bool enabled) {
|
||||
if (!hotReloadSupported_) {
|
||||
E2D_LOG_WARN("当前平台不支持热重载");
|
||||
return;
|
||||
}
|
||||
hotReloadEnabled_ = enabled;
|
||||
ShaderHotReloader::getInstance().setEnabled(enabled);
|
||||
E2D_LOG_INFO("热重载已{}", enabled ? "Enable" : "Disable");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查热重载是否启用
|
||||
* @return 启用返回true,否则返回false
|
||||
*/
|
||||
bool ShaderManager::isHotReloadEnabled() const {
|
||||
return hotReloadEnabled_ && hotReloadSupported_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新热重载系统(主循环调用)
|
||||
*/
|
||||
void ShaderManager::update() {
|
||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
||||
ShaderHotReloader::getInstance().update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 手动重载Shader
|
||||
* @param name Shader名称
|
||||
* @return 重载成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderManager::reload(const std::string &name) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it == shaders_.end()) {
|
||||
E2D_LOG_WARN("未找到要重载的着色器: {}", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
ShaderInfo &info = it->second;
|
||||
|
||||
std::string vertSource = info.vertSource;
|
||||
std::string fragSource = info.fragSource;
|
||||
|
||||
if (!info.metadata.vertPath.empty() && !info.metadata.fragPath.empty()) {
|
||||
ShaderLoadResult result = loader_.loadFromSeparateFiles(
|
||||
name, info.metadata.vertPath, info.metadata.fragPath);
|
||||
if (result.success) {
|
||||
vertSource = result.vertSource;
|
||||
fragSource = result.fragSource;
|
||||
}
|
||||
} else if (!info.metadata.combinedPath.empty()) {
|
||||
ShaderLoadResult result =
|
||||
loader_.loadFromCombinedFile(info.metadata.combinedPath);
|
||||
if (result.success) {
|
||||
vertSource = result.vertSource;
|
||||
fragSource = result.fragSource;
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<IShader> newShader =
|
||||
factory_->createFromSource(name, vertSource, fragSource);
|
||||
if (!newShader) {
|
||||
E2D_LOG_ERROR("重载着色器失败: {}", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
info.shader = newShader;
|
||||
info.vertSource = vertSource;
|
||||
info.fragSource = fragSource;
|
||||
|
||||
if (info.reloadCallback) {
|
||||
info.reloadCallback(newShader);
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("着色器重载成功: {}", name);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取内置Shader
|
||||
* @param name 内置Shader名称
|
||||
* @return Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::getBuiltin(const std::string &name) {
|
||||
Ptr<IShader> shader = get(name);
|
||||
if (shader) {
|
||||
return shader;
|
||||
}
|
||||
|
||||
// 从JSON元数据文件加载内置着色器
|
||||
fs::path jsonPath =
|
||||
fs::path(shaderDir_) / "shared" / "builtin" / (name + ".json");
|
||||
if (loader_.fileExists(jsonPath.string())) {
|
||||
return loadFromMetadata(jsonPath.string(), name);
|
||||
}
|
||||
|
||||
E2D_LOG_ERROR("未找到内置着色器: {}", jsonPath.string());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从JSON元数据文件加载Shader(多后端支持)
|
||||
* @param jsonPath JSON元数据文件路径
|
||||
* @param name Shader名称
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::loadFromMetadata(const std::string &jsonPath,
|
||||
const std::string &name) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager 未初始化");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 检查是否已加载
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
|
||||
// 读取JSON文件
|
||||
std::string jsonContent = loader_.readFile(jsonPath);
|
||||
if (jsonContent.empty()) {
|
||||
E2D_LOG_ERROR("读取着色器元数据失败: {}", jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用nlohmann/json解析
|
||||
nl::json j = nl::json::parse(jsonContent);
|
||||
|
||||
// 解析基本元数据
|
||||
ShaderMetadata metadata;
|
||||
metadata.name = name;
|
||||
|
||||
if (j.contains("category")) {
|
||||
metadata.category = j["category"].get<std::string>();
|
||||
}
|
||||
if (j.contains("version")) {
|
||||
metadata.version = j["version"].get<std::string>();
|
||||
}
|
||||
if (j.contains("description")) {
|
||||
metadata.description = j["description"].get<std::string>();
|
||||
}
|
||||
|
||||
// 解析 uniforms 详细定义
|
||||
if (j.contains("uniforms")) {
|
||||
for (auto &[key, value] : j["uniforms"].items()) {
|
||||
ShaderUniformDef def;
|
||||
if (value.contains("type")) {
|
||||
def.type = value["type"].get<std::string>();
|
||||
}
|
||||
if (value.contains("description")) {
|
||||
def.description = value["description"].get<std::string>();
|
||||
}
|
||||
// 解析默认值
|
||||
if (value.contains("default")) {
|
||||
def.hasDefault = true;
|
||||
if (def.type == "float") {
|
||||
def.defaultValue = value["default"].get<float>();
|
||||
} else if (def.type == "int") {
|
||||
def.defaultInt = value["default"].get<int>();
|
||||
} else if (def.type == "bool") {
|
||||
def.defaultBool = value["default"].get<bool>();
|
||||
} else if (def.type == "vec2" && value["default"].is_array()) {
|
||||
auto arr = value["default"].get<std::vector<float>>();
|
||||
for (size_t i = 0; i < arr.size() && i < 2; ++i) {
|
||||
def.defaultVec2[i] = arr[i];
|
||||
}
|
||||
} else if (def.type == "vec3" && value["default"].is_array()) {
|
||||
auto arr = value["default"].get<std::vector<float>>();
|
||||
for (size_t i = 0; i < arr.size() && i < 3; ++i) {
|
||||
def.defaultVec3[i] = arr[i];
|
||||
}
|
||||
} else if (def.type == "vec4" && value["default"].is_array()) {
|
||||
auto arr = value["default"].get<std::vector<float>>();
|
||||
for (size_t i = 0; i < arr.size() && i < 4; ++i) {
|
||||
def.defaultVec4[i] = arr[i];
|
||||
}
|
||||
} else if (def.type == "mat4" && value["default"].is_array()) {
|
||||
auto arr = value["default"].get<std::vector<float>>();
|
||||
for (size_t i = 0; i < arr.size() && i < 16; ++i) {
|
||||
def.defaultMat4[i] = arr[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
metadata.uniformDefs[key] = def;
|
||||
// 同时填充旧的uniforms映射以保持兼容性
|
||||
metadata.uniforms[key] = def.type;
|
||||
}
|
||||
}
|
||||
|
||||
// 解析 samplers 定义
|
||||
if (j.contains("samplers")) {
|
||||
for (auto &[key, value] : j["samplers"].items()) {
|
||||
ShaderSamplerDef def;
|
||||
if (value.contains("type")) {
|
||||
def.type = value["type"].get<std::string>();
|
||||
}
|
||||
if (value.contains("description")) {
|
||||
def.description = value["description"].get<std::string>();
|
||||
}
|
||||
metadata.samplerDefs[key] = def;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取OpenGL后端路径
|
||||
if (!j.contains("backends") || !j["backends"].contains("opengl")) {
|
||||
E2D_LOG_ERROR("着色器元数据中未找到 OpenGL 后端: {}", jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto &opengl = j["backends"]["opengl"];
|
||||
if (!opengl.contains("vertex") || !opengl.contains("fragment")) {
|
||||
E2D_LOG_ERROR("着色器元数据中缺少顶点或片段路径: {}", jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string vertRelativePath = opengl["vertex"].get<std::string>();
|
||||
std::string fragRelativePath = opengl["fragment"].get<std::string>();
|
||||
|
||||
// 使用C++17文件系统构建完整路径
|
||||
fs::path vertPath = fs::path(shaderDir_) / vertRelativePath;
|
||||
fs::path fragPath = fs::path(shaderDir_) / fragRelativePath;
|
||||
|
||||
E2D_LOG_DEBUG("从元数据加载着色器: {} -> 顶点: {}, 片段: {}", name,
|
||||
vertPath.string(), fragPath.string());
|
||||
|
||||
// 使用分离文件加载
|
||||
Ptr<IShader> shader =
|
||||
loadFromFiles(name, vertPath.string(), fragPath.string());
|
||||
|
||||
// 更新着色器元数据
|
||||
if (shader && shaders_.find(name) != shaders_.end()) {
|
||||
shaders_[name].metadata = metadata;
|
||||
}
|
||||
|
||||
return shader;
|
||||
|
||||
} catch (const nl::json::exception &e) {
|
||||
E2D_LOG_ERROR("解析着色器元数据 {} 失败: {}", jsonPath, e.what());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加载所有内置Shader
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderManager::loadBuiltinShaders() {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager 未初始化");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool allSuccess = true;
|
||||
|
||||
const char *builtinNames[] = {"sprite", "particle", "shape", "postprocess",
|
||||
"font"};
|
||||
|
||||
for (const char *name : builtinNames) {
|
||||
fs::path jsonPath = fs::path(shaderDir_) / "shared" / "builtin" /
|
||||
(std::string(name) + ".json");
|
||||
std::string shaderName = std::string("builtin_") + name;
|
||||
|
||||
Ptr<IShader> shader = nullptr;
|
||||
if (loader_.fileExists(jsonPath.string())) {
|
||||
shader = loadFromMetadata(jsonPath.string(), name);
|
||||
}
|
||||
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("加载内置 {} 着色器失败", name);
|
||||
allSuccess = false;
|
||||
} else {
|
||||
// 同时注册带 builtin_ 前缀的名称
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
shaders_[shaderName] = it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allSuccess) {
|
||||
E2D_LOG_INFO("所有内置着色器加载成功");
|
||||
}
|
||||
|
||||
return allSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从缓存加载Shader
|
||||
* @param name Shader名称
|
||||
* @param sourceHash 源码哈希值
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::loadFromCache(const std::string &name,
|
||||
const std::string &sourceHash,
|
||||
const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
if (!ShaderCache::getInstance().isInitialized()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ShaderCache::getInstance().hasValidCache(name, sourceHash)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<ShaderCacheEntry> entry = ShaderCache::getInstance().loadCache(name);
|
||||
if (!entry || entry->binary.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<IShader> shader = factory_->createFromBinary(name, entry->binary);
|
||||
if (shader) {
|
||||
E2D_LOG_DEBUG("从缓存加载着色器成功: {}", name);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理文件变化事件
|
||||
* @param shaderName Shader名称
|
||||
* @param event 文件变化事件
|
||||
*/
|
||||
void ShaderManager::handleFileChange(const std::string &shaderName,
|
||||
const FileChangeEvent &event) {
|
||||
E2D_LOG_DEBUG("着色器文件已更改: {} -> {}", shaderName, event.filepath);
|
||||
reload(shaderName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取Shader元数据
|
||||
* @param name Shader名称
|
||||
* @return Shader元数据,不存在返回空元数据
|
||||
*/
|
||||
ShaderMetadata ShaderManager::getMetadata(const std::string &name) const {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.metadata;
|
||||
}
|
||||
return ShaderMetadata{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取Shader的uniform定义
|
||||
* @param name Shader名称
|
||||
* @return uniform定义映射,不存在返回空映射
|
||||
*/
|
||||
std::unordered_map<std::string, ShaderUniformDef>
|
||||
ShaderManager::getUniformDefs(const std::string &name) const {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.metadata.uniformDefs;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取Shader的sampler定义
|
||||
* @param name Shader名称
|
||||
* @return sampler定义映射,不存在返回空映射
|
||||
*/
|
||||
std::unordered_map<std::string, ShaderSamplerDef>
|
||||
ShaderManager::getSamplerDefs(const std::string &name) const {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.metadata.samplerDefs;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 自动应用uniform值到着色器
|
||||
* 根据JSON元数据中的uniform定义,自动设置对应的uniform值
|
||||
* 如果值映射表中不存在该uniform,则使用JSON中定义的默认值
|
||||
* @param shader 目标着色器
|
||||
* @param shaderName Shader名称(用于查找元数据)
|
||||
* @param values uniform值映射表(可选,用于覆盖默认值)
|
||||
*/
|
||||
void ShaderManager::applyUniforms(Ptr<IShader> shader,
|
||||
const std::string &shaderName,
|
||||
const UniformValueMap &values) {
|
||||
if (!shader)
|
||||
return;
|
||||
|
||||
auto uniformDefs = getUniformDefs(shaderName);
|
||||
for (const auto &[name, def] : uniformDefs) {
|
||||
auto it = values.find(name);
|
||||
|
||||
if (it != values.end()) {
|
||||
// 使用提供的值
|
||||
const auto &value = it->second;
|
||||
if (def.type == "float" &&
|
||||
value.type == ShaderUniformValue::Type::Float) {
|
||||
shader->setFloat(name, value.data.f[0]);
|
||||
} else if (def.type == "int" &&
|
||||
value.type == ShaderUniformValue::Type::Int) {
|
||||
shader->setInt(name, value.data.i);
|
||||
} else if (def.type == "bool" &&
|
||||
value.type == ShaderUniformValue::Type::Bool) {
|
||||
shader->setBool(name, value.data.b);
|
||||
} else if (def.type == "vec2" &&
|
||||
value.type == ShaderUniformValue::Type::Vec2) {
|
||||
shader->setVec2(name, glm::vec2(value.data.f[0], value.data.f[1]));
|
||||
} else if (def.type == "vec3" &&
|
||||
value.type == ShaderUniformValue::Type::Vec3) {
|
||||
shader->setVec3(
|
||||
name, glm::vec3(value.data.f[0], value.data.f[1], value.data.f[2]));
|
||||
} else if (def.type == "vec4" &&
|
||||
value.type == ShaderUniformValue::Type::Vec4) {
|
||||
shader->setVec4(name, glm::vec4(value.data.f[0], value.data.f[1],
|
||||
value.data.f[2], value.data.f[3]));
|
||||
} else if (def.type == "mat4" &&
|
||||
value.type == ShaderUniformValue::Type::Mat4) {
|
||||
glm::mat4 mat;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (int j = 0; j < 4; ++j)
|
||||
mat[i][j] = value.data.f[i * 4 + j];
|
||||
shader->setMat4(name, mat);
|
||||
}
|
||||
} else if (def.hasDefault) {
|
||||
// 使用JSON中定义的默认值
|
||||
if (def.type == "float") {
|
||||
shader->setFloat(name, def.defaultValue);
|
||||
} else if (def.type == "int") {
|
||||
shader->setInt(name, def.defaultInt);
|
||||
} else if (def.type == "bool") {
|
||||
shader->setBool(name, def.defaultBool);
|
||||
} else if (def.type == "vec2") {
|
||||
shader->setVec2(name,
|
||||
glm::vec2(def.defaultVec2[0], def.defaultVec2[1]));
|
||||
} else if (def.type == "vec3") {
|
||||
shader->setVec3(name, glm::vec3(def.defaultVec3[0], def.defaultVec3[1],
|
||||
def.defaultVec3[2]));
|
||||
} else if (def.type == "vec4") {
|
||||
shader->setVec4(name,
|
||||
glm::vec4(def.defaultVec4[0], def.defaultVec4[1],
|
||||
def.defaultVec4[2], def.defaultVec4[3]));
|
||||
} else if (def.type == "mat4") {
|
||||
glm::mat4 mat;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (int j = 0; j < 4; ++j)
|
||||
mat[i][j] = def.defaultMat4[i * 4 + j];
|
||||
shader->setMat4(name, mat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 自动应用sampler绑定到着色器
|
||||
* 根据JSON元数据中的sampler定义,自动设置对应的纹理单元
|
||||
* @param shader 目标着色器
|
||||
* @param shaderName Shader名称(用于查找元数据)
|
||||
*/
|
||||
void ShaderManager::applySamplers(Ptr<IShader> shader,
|
||||
const std::string &shaderName) {
|
||||
if (!shader)
|
||||
return;
|
||||
|
||||
// TODO: 从JSON中解析binding并设置
|
||||
// 目前sampler绑定在submitBatch中通过setInt设置
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/graphics/shader/shader_preset.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 创建水波纹效果着色器
|
||||
* @param params 水波纹效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
Ptr<IShader> ShaderPreset::Water(const WaterParams ¶ms) {
|
||||
Ptr<IShader> shader = ShaderManager::getInstance().get("water");
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("获取水波纹着色器失败");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shader->setFloat("u_waveSpeed", params.waveSpeed);
|
||||
shader->setFloat("u_waveAmplitude", params.waveAmplitude);
|
||||
shader->setFloat("u_waveFrequency", params.waveFrequency);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建描边效果着色器
|
||||
* @param params 描边效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
Ptr<IShader> ShaderPreset::Outline(const OutlineParams ¶ms) {
|
||||
Ptr<IShader> shader = ShaderManager::getInstance().get("outline");
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("获取描边着色器失败");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shader->setVec4("u_outlineColor", glm::vec4(params.color.r, params.color.g,
|
||||
params.color.b, params.color.a));
|
||||
shader->setFloat("u_thickness", params.thickness);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建扭曲效果着色器
|
||||
* @param params 扭曲效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
Ptr<IShader> ShaderPreset::Distortion(const DistortionParams ¶ms) {
|
||||
Ptr<IShader> shader = ShaderManager::getInstance().get("distortion");
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("获取扭曲效果着色器失败");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shader->setFloat("u_distortionAmount", params.distortionAmount);
|
||||
shader->setFloat("u_timeScale", params.timeScale);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建像素化效果着色器
|
||||
* @param params 像素化效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
Ptr<IShader> ShaderPreset::Pixelate(const PixelateParams ¶ms) {
|
||||
Ptr<IShader> shader = ShaderManager::getInstance().get("pixelate");
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("获取像素化着色器失败");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shader->setFloat("u_pixelSize", params.pixelSize);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建反相效果着色器
|
||||
* @param params 反相效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
Ptr<IShader> ShaderPreset::Invert(const InvertParams ¶ms) {
|
||||
Ptr<IShader> shader = ShaderManager::getInstance().get("invert");
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("获取反相着色器失败");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shader->setFloat("u_strength", params.strength);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建灰度效果着色器
|
||||
* @param params 灰度效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
Ptr<IShader> ShaderPreset::Grayscale(const GrayscaleParams ¶ms) {
|
||||
Ptr<IShader> shader = ShaderManager::getInstance().get("grayscale");
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("获取灰度着色器失败");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shader->setFloat("u_intensity", params.intensity);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建模糊效果着色器
|
||||
* @param params 模糊效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
Ptr<IShader> ShaderPreset::Blur(const BlurParams ¶ms) {
|
||||
Ptr<IShader> shader = ShaderManager::getInstance().get("blur");
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("获取模糊着色器失败");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shader->setFloat("u_radius", params.radius);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建灰度+描边组合效果着色器
|
||||
* @param grayParams 灰度效果参数
|
||||
* @param outlineParams 描边效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
Ptr<IShader>
|
||||
ShaderPreset::GrayscaleOutline(const GrayscaleParams &grayParams,
|
||||
const OutlineParams &outlineParams) {
|
||||
std::string shaderDir = ShaderManager::getInstance().getShaderDir();
|
||||
std::string jsonPath = shaderDir + "effects/grayscale_outline.json";
|
||||
|
||||
Ptr<IShader> shader =
|
||||
ShaderManager::getInstance().loadFromMetadata(jsonPath, "grayscale_outline");
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("从 {} 加载灰度描边组合着色器失败",
|
||||
jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shader->setFloat("u_grayIntensity", grayParams.intensity);
|
||||
shader->setVec4("u_outlineColor",
|
||||
glm::vec4(outlineParams.color.r, outlineParams.color.g,
|
||||
outlineParams.color.b, outlineParams.color.a));
|
||||
shader->setFloat("u_thickness", outlineParams.thickness);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建像素化+反相组合效果着色器
|
||||
* @param pixParams 像素化效果参数
|
||||
* @param invParams 反相效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
Ptr<IShader> ShaderPreset::PixelateInvert(const PixelateParams &pixParams,
|
||||
const InvertParams &invParams) {
|
||||
std::string shaderDir = ShaderManager::getInstance().getShaderDir();
|
||||
std::string jsonPath = shaderDir + "effects/pixelate_invert.json";
|
||||
|
||||
Ptr<IShader> shader =
|
||||
ShaderManager::getInstance().loadFromMetadata(jsonPath, "pixelate_invert");
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("从 {} 加载像素化反相组合着色器失败", jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shader->setFloat("u_pixelSize", pixParams.pixelSize);
|
||||
shader->setFloat("u_invertStrength", invParams.strength);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
#include <extra2d/graphics/texture/alpha_mask.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*
|
||||
* 创建指定尺寸的Alpha遮罩,初始时所有像素均为不透明(值为255)
|
||||
*
|
||||
* @param width 遮罩宽度(像素)
|
||||
* @param height 遮罩高度(像素)
|
||||
*/
|
||||
AlphaMask::AlphaMask(int width, int height)
|
||||
: width_(width), height_(height), data_(width * height, 255) {}
|
||||
|
||||
/**
|
||||
* @brief 从像素数据创建Alpha遮罩
|
||||
*
|
||||
* 根据输入的像素数据提取Alpha通道创建遮罩,
|
||||
* 支持RGBA(4通道)、RGB(3通道)和灰度(1通道)格式
|
||||
*
|
||||
* @param pixels 像素数据指针
|
||||
* @param width 图像宽度
|
||||
* @param height 图像高度
|
||||
* @param channels 通道数量(1、3或4)
|
||||
* @return 创建的Alpha遮罩对象
|
||||
*/
|
||||
AlphaMask AlphaMask::createFromPixels(const uint8_t *pixels, int width,
|
||||
int height, int channels) {
|
||||
AlphaMask mask(width, height);
|
||||
|
||||
if (!pixels || width <= 0 || height <= 0) {
|
||||
return mask;
|
||||
}
|
||||
|
||||
// 根据通道数提取Alpha值
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
int pixelIndex = (y * width + x) * channels;
|
||||
uint8_t alpha = 255;
|
||||
|
||||
if (channels == 4) {
|
||||
// RGBA格式,Alpha在第四个通道
|
||||
alpha = pixels[pixelIndex + 3];
|
||||
} else if (channels == 1) {
|
||||
// 灰度图,直接作为Alpha
|
||||
alpha = pixels[pixelIndex];
|
||||
} else if (channels == 3) {
|
||||
// RGB格式,没有Alpha通道,视为不透明
|
||||
alpha = 255;
|
||||
}
|
||||
|
||||
mask.data_[y * width + x] = alpha;
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定位置的Alpha值
|
||||
*
|
||||
* 返回指定坐标处的Alpha值,如果坐标无效则返回0
|
||||
*
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @return Alpha值(0-255),坐标无效时返回0
|
||||
*/
|
||||
uint8_t AlphaMask::getAlpha(int x, int y) const {
|
||||
if (!isValid(x, y)) {
|
||||
return 0;
|
||||
}
|
||||
return data_[y * width_ + x];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查指定位置是否不透明
|
||||
*
|
||||
* 判断指定坐标处的Alpha值是否大于等于给定的阈值
|
||||
*
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @param threshold 不透明度阈值(默认为255,即完全不透明)
|
||||
* @return 如果Alpha值大于等于阈值返回true,否则返回false
|
||||
*/
|
||||
bool AlphaMask::isOpaque(int x, int y, uint8_t threshold) const {
|
||||
return getAlpha(x, y) >= threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查坐标是否有效
|
||||
*
|
||||
* 判断给定的坐标是否在遮罩的有效范围内
|
||||
*
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @return 如果坐标在有效范围内返回true,否则返回false
|
||||
*/
|
||||
bool AlphaMask::isValid(int x, int y) const {
|
||||
return x >= 0 && x < width_ && y >= 0 && y < height_;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,386 +0,0 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/texture/texture_atlas.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// TextureAtlasPage 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param width 页面宽度
|
||||
* @param height 页面高度
|
||||
*
|
||||
* 创建指定尺寸的纹理图集页面,初始化空白纹理和打包根节点
|
||||
*/
|
||||
TextureAtlasPage::TextureAtlasPage(int width, int height)
|
||||
: width_(width), height_(height), isFull_(false), usedArea_(0) {
|
||||
// 创建空白纹理
|
||||
std::vector<uint8_t> emptyData(width * height * 4, 0);
|
||||
texture_ = makePtr<GLTexture>(width, height, emptyData.data(), 4);
|
||||
|
||||
// 初始化矩形打包根节点
|
||||
root_ = std::make_unique<PackNode>(0, 0, width, height);
|
||||
|
||||
E2D_LOG_INFO("已创建纹理图集页面: {}x{}", width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 释放纹理图集页面资源
|
||||
*/
|
||||
TextureAtlasPage::~TextureAtlasPage() = default;
|
||||
|
||||
/**
|
||||
* @brief 尝试添加纹理到图集页面
|
||||
* @param name 纹理名称
|
||||
* @param texWidth 纹理宽度
|
||||
* @param texHeight 纹理高度
|
||||
* @param pixels 像素数据
|
||||
* @param[out] outUvRect 输出的UV坐标矩形
|
||||
* @return 添加成功返回true,失败返回false
|
||||
*
|
||||
* 尝试将纹理添加到图集页面中,使用矩形打包算法找到合适位置
|
||||
*/
|
||||
bool TextureAtlasPage::tryAddTexture(const std::string &name, int texWidth,
|
||||
int texHeight, const uint8_t *pixels,
|
||||
Rect &outUvRect) {
|
||||
if (isFull_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 添加边距
|
||||
int paddedWidth = texWidth + 2 * PADDING;
|
||||
int paddedHeight = texHeight + 2 * PADDING;
|
||||
|
||||
// 如果纹理太大,无法放入
|
||||
if (paddedWidth > width_ || paddedHeight > height_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 尝试插入
|
||||
PackNode *node = insert(root_.get(), paddedWidth, paddedHeight);
|
||||
if (node == nullptr) {
|
||||
// 无法放入,标记为满
|
||||
isFull_ = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 写入像素数据(跳过边距区域)
|
||||
writePixels(node->x + PADDING, node->y + PADDING, texWidth, texHeight,
|
||||
pixels);
|
||||
|
||||
// 创建条目
|
||||
AtlasEntry entry;
|
||||
entry.name = name;
|
||||
entry.originalSize =
|
||||
Vec2(static_cast<float>(texWidth), static_cast<float>(texHeight));
|
||||
entry.padding = PADDING;
|
||||
|
||||
// 计算 UV 坐标(考虑边距)
|
||||
float u1 = static_cast<float>(node->x + PADDING) / width_;
|
||||
float v1 = static_cast<float>(node->y + PADDING) / height_;
|
||||
float u2 = static_cast<float>(node->x + PADDING + texWidth) / width_;
|
||||
float v2 = static_cast<float>(node->y + PADDING + texHeight) / height_;
|
||||
|
||||
entry.uvRect = Rect(u1, v1, u2 - u1, v2 - v1);
|
||||
outUvRect = entry.uvRect;
|
||||
|
||||
entries_[name] = std::move(entry);
|
||||
usedArea_ += paddedWidth * paddedHeight;
|
||||
|
||||
E2D_LOG_DEBUG("已将纹理 '{}' 添加到图集: {}x{} 位置 ({}, {})", name,
|
||||
texWidth, texHeight, node->x, node->y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 插入纹理到打包树
|
||||
* @param node 当前节点
|
||||
* @param width 纹理宽度
|
||||
* @param height 纹理高度
|
||||
* @return 找到合适的节点返回节点指针,否则返回nullptr
|
||||
*
|
||||
* 使用二叉树算法递归查找合适的空间位置
|
||||
*/
|
||||
TextureAtlasPage::PackNode *TextureAtlasPage::insert(PackNode *node, int width,
|
||||
int height) {
|
||||
if (node == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 如果节点已被使用,尝试子节点
|
||||
if (node->used) {
|
||||
PackNode *result = insert(node->left.get(), width, height);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
return insert(node->right.get(), width, height);
|
||||
}
|
||||
|
||||
// 检查是否适合
|
||||
if (width > node->width || height > node->height) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 如果刚好合适,使用此节点
|
||||
if (width == node->width && height == node->height) {
|
||||
node->used = true;
|
||||
return node;
|
||||
}
|
||||
|
||||
// 需要分割节点
|
||||
int dw = node->width - width;
|
||||
int dh = node->height - height;
|
||||
|
||||
if (dw > dh) {
|
||||
// 水平分割
|
||||
node->left =
|
||||
std::make_unique<PackNode>(node->x, node->y, width, node->height);
|
||||
node->right =
|
||||
std::make_unique<PackNode>(node->x + width, node->y, dw, node->height);
|
||||
} else {
|
||||
// 垂直分割
|
||||
node->left =
|
||||
std::make_unique<PackNode>(node->x, node->y, node->width, height);
|
||||
node->right =
|
||||
std::make_unique<PackNode>(node->x, node->y + height, node->width, dh);
|
||||
}
|
||||
|
||||
// 递归插入到左子节点
|
||||
return insert(node->left.get(), width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 写入像素数据到纹理
|
||||
* @param x 起始X坐标
|
||||
* @param y 起始Y坐标
|
||||
* @param w 宽度
|
||||
* @param h 高度
|
||||
* @param pixels 像素数据
|
||||
*
|
||||
* 使用glTexSubImage2D更新纹理的指定区域
|
||||
*/
|
||||
void TextureAtlasPage::writePixels(int x, int y, int w, int h,
|
||||
const uint8_t *pixels) {
|
||||
if (texture_ == nullptr || pixels == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用 glTexSubImage2D 更新纹理数据
|
||||
GLuint texID = static_cast<GLuint>(
|
||||
reinterpret_cast<uintptr_t>(texture_->getNativeHandle()));
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texID);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
pixels);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取图集中的纹理条目信息
|
||||
* @param name 纹理名称
|
||||
* @return 找到返回条目指针,未找到返回nullptr
|
||||
*/
|
||||
const AtlasEntry *TextureAtlasPage::getEntry(const std::string &name) const {
|
||||
auto it = entries_.find(name);
|
||||
if (it != entries_.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取页面使用率
|
||||
* @return 使用率(0.0到1.0之间)
|
||||
*
|
||||
* 计算已使用面积占总面积的比例
|
||||
*/
|
||||
float TextureAtlasPage::getUsageRatio() const {
|
||||
return static_cast<float>(usedArea_) / (width_ * height_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TextureAtlas 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个使用默认页面大小的纹理图集
|
||||
*/
|
||||
TextureAtlas::TextureAtlas()
|
||||
: pageSize_(TextureAtlasPage::DEFAULT_SIZE), sizeThreshold_(256),
|
||||
enabled_(true), initialized_(false) {}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 释放纹理图集资源
|
||||
*/
|
||||
TextureAtlas::~TextureAtlas() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理图集
|
||||
* @param pageSize 页面大小
|
||||
*
|
||||
* 设置图集页面大小并标记为已初始化
|
||||
*/
|
||||
void TextureAtlas::init(int pageSize) {
|
||||
pageSize_ = pageSize;
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("纹理图集已初始化,页面大小: {}", pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加纹理到图集
|
||||
* @param name 纹理名称
|
||||
* @param width 纹理宽度
|
||||
* @param height 纹理高度
|
||||
* @param pixels 像素数据
|
||||
* @return 添加成功返回true,失败返回false
|
||||
*
|
||||
* 尝试将纹理添加到现有页面,如空间不足则创建新页面
|
||||
*/
|
||||
bool TextureAtlas::addTexture(const std::string &name, int width, int height,
|
||||
const uint8_t *pixels) {
|
||||
if (!enabled_ || !initialized_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
if (contains(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查纹理大小
|
||||
if (width > sizeThreshold_ || height > sizeThreshold_) {
|
||||
E2D_LOG_DEBUG("纹理 '{}' 太大无法加入图集 ({}x{} > {}),跳过",
|
||||
name, width, height, sizeThreshold_);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 尝试添加到现有页面
|
||||
Rect uvRect;
|
||||
for (auto &page : pages_) {
|
||||
if (page->tryAddTexture(name, width, height, pixels, uvRect)) {
|
||||
entryToPage_[name] = page.get();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新页面
|
||||
auto newPage = std::make_unique<TextureAtlasPage>(pageSize_, pageSize_);
|
||||
if (newPage->tryAddTexture(name, width, height, pixels, uvRect)) {
|
||||
entryToPage_[name] = newPage.get();
|
||||
pages_.push_back(std::move(newPage));
|
||||
return true;
|
||||
}
|
||||
|
||||
E2D_LOG_WARN("添加纹理 '{}' 到图集失败", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查纹理是否已存在于图集中
|
||||
* @param name 纹理名称
|
||||
* @return 存在返回true,不存在返回false
|
||||
*/
|
||||
bool TextureAtlas::contains(const std::string &name) const {
|
||||
return entryToPage_.find(name) != entryToPage_.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取纹理所在的图集纹理
|
||||
* @param name 纹理名称
|
||||
* @return 找到返回纹理指针,未找到返回nullptr
|
||||
*/
|
||||
const Texture *TextureAtlas::getAtlasTexture(const std::string &name) const {
|
||||
auto it = entryToPage_.find(name);
|
||||
if (it != entryToPage_.end()) {
|
||||
return it->second->getTexture().get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取纹理在图集中的UV坐标矩形
|
||||
* @param name 纹理名称
|
||||
* @return UV坐标矩形,未找到返回默认值
|
||||
*/
|
||||
Rect TextureAtlas::getUVRect(const std::string &name) const {
|
||||
auto it = entryToPage_.find(name);
|
||||
if (it != entryToPage_.end()) {
|
||||
const AtlasEntry *entry = it->second->getEntry(name);
|
||||
if (entry != nullptr) {
|
||||
return entry->uvRect;
|
||||
}
|
||||
}
|
||||
return Rect(0, 0, 1, 1); // 默认 UV
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取纹理的原始尺寸
|
||||
* @param name 纹理名称
|
||||
* @return 原始尺寸,未找到返回零向量
|
||||
*/
|
||||
Vec2 TextureAtlas::getOriginalSize(const std::string &name) const {
|
||||
auto it = entryToPage_.find(name);
|
||||
if (it != entryToPage_.end()) {
|
||||
const AtlasEntry *entry = it->second->getEntry(name);
|
||||
if (entry != nullptr) {
|
||||
return entry->originalSize;
|
||||
}
|
||||
}
|
||||
return Vec2(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取总使用率
|
||||
* @return 所有页面的平均使用率
|
||||
*
|
||||
* 计算所有页面的平均空间使用率
|
||||
*/
|
||||
float TextureAtlas::getTotalUsageRatio() const {
|
||||
if (pages_.empty()) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float total = 0.0f;
|
||||
for (const auto &page : pages_) {
|
||||
total += page->getUsageRatio();
|
||||
}
|
||||
return total / pages_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空图集
|
||||
*
|
||||
* 移除所有页面和条目映射
|
||||
*/
|
||||
void TextureAtlas::clear() {
|
||||
pages_.clear();
|
||||
entryToPage_.clear();
|
||||
E2D_LOG_INFO("纹理图集已清空");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TextureAtlasMgr 单例实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取TextureAtlasMgr单例实例
|
||||
* @return TextureAtlasMgr单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
TextureAtlasMgr &TextureAtlasMgr::get() {
|
||||
static TextureAtlasMgr instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,652 +0,0 @@
|
|||
#include <extra2d/graphics/texture/texture_pool.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// TexturePool 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个未初始化的纹理池
|
||||
*/
|
||||
TexturePool::TexturePool()
|
||||
: scene_(nullptr)
|
||||
, maxMemoryUsage_(0)
|
||||
, currentMemoryUsage_(0)
|
||||
, cacheHits_(0)
|
||||
, cacheMisses_(0)
|
||||
, evictionCount_(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param scene 场景指针
|
||||
* @param maxMemoryUsage 最大内存使用量(0 表示无限制)
|
||||
*
|
||||
* 创建一个指定场景和内存限制的纹理池
|
||||
*/
|
||||
TexturePool::TexturePool(Scene* scene, size_t maxMemoryUsage)
|
||||
: scene_(scene)
|
||||
, maxMemoryUsage_(maxMemoryUsage)
|
||||
, currentMemoryUsage_(0)
|
||||
, cacheHits_(0)
|
||||
, cacheMisses_(0)
|
||||
, evictionCount_(0) {
|
||||
E2D_LOG_INFO("TexturePool 已创建,最大内存: {} 字节", maxMemoryUsage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理池
|
||||
* @param scene 场景指针
|
||||
* @param maxMemoryUsage 最大内存使用量(0 表示无限制)
|
||||
*
|
||||
* 设置纹理池的场景和内存限制
|
||||
*/
|
||||
void TexturePool::init(Scene* scene, size_t maxMemoryUsage) {
|
||||
scene_ = scene;
|
||||
maxMemoryUsage_ = maxMemoryUsage;
|
||||
E2D_LOG_INFO("TexturePool 已初始化,最大内存: {} 字节", maxMemoryUsage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 清理纹理池并释放所有资源
|
||||
*/
|
||||
TexturePool::~TexturePool() {
|
||||
clear();
|
||||
E2D_LOG_INFO("TexturePool 已销毁");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 纹理加载
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理
|
||||
* @param path 文件路径
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*
|
||||
* 加载完整纹理文件到纹理池
|
||||
*/
|
||||
TextureRef TexturePool::load(const std::string& path, const TextureLoadOptions& options) {
|
||||
return load(path, Rect::Zero(), options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理区域
|
||||
* @param path 文件路径
|
||||
* @param region 纹理区域
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*
|
||||
* 加载纹理文件的指定区域到纹理池
|
||||
*/
|
||||
TextureRef TexturePool::load(const std::string& path, const Rect& region,
|
||||
const TextureLoadOptions& options) {
|
||||
TextureKey key(path, region);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
// 检查缓存
|
||||
auto it = cache_.find(key);
|
||||
if (it != cache_.end()) {
|
||||
// 缓存命中
|
||||
it->second.touch();
|
||||
it->second.refCount.fetch_add(1, std::memory_order_relaxed);
|
||||
cacheHits_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
E2D_LOG_DEBUG("纹理缓存命中: {}", path);
|
||||
return TextureRef(it->second.texture, &it->second, &mutex_);
|
||||
}
|
||||
|
||||
// 缓存未命中
|
||||
cacheMisses_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// 获取渲染后端
|
||||
RenderBackend* backend = nullptr;
|
||||
if (scene_) {
|
||||
// 假设 Scene 有获取 RenderBackend 的方法
|
||||
// 这里需要根据实际接口调整
|
||||
backend = nullptr; // TODO: 从 Scene 获取 RenderBackend
|
||||
}
|
||||
|
||||
if (!backend) {
|
||||
E2D_LOG_ERROR("TexturePool: 渲染后端不可用");
|
||||
return TextureRef();
|
||||
}
|
||||
|
||||
// 加载纹理
|
||||
Ptr<Texture> texture = backend->loadTexture(path);
|
||||
if (!texture) {
|
||||
E2D_LOG_ERROR("TexturePool: 加载纹理失败: {}", path);
|
||||
return TextureRef();
|
||||
}
|
||||
|
||||
// 计算内存大小
|
||||
size_t memorySize = calculateTextureMemory(texture.get());
|
||||
|
||||
// 检查内存限制
|
||||
if (maxMemoryUsage_ > 0 && currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
|
||||
// 尝试淘汰
|
||||
evictLRU(currentMemoryUsage_ + memorySize - maxMemoryUsage_);
|
||||
|
||||
// 再次检查
|
||||
if (currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
|
||||
E2D_LOG_WARN("TexturePool: 内存限制超出,无法加载纹理: {}", path);
|
||||
return TextureRef();
|
||||
}
|
||||
}
|
||||
|
||||
// 创建缓存条目
|
||||
auto result = cache_.emplace(key, TexturePoolEntry(nullptr, key, 0));
|
||||
if (result.second) {
|
||||
result.first->second.texture = texture;
|
||||
result.first->second.memorySize = memorySize;
|
||||
result.first->second.refCount.store(1, std::memory_order_relaxed);
|
||||
result.first->second.touch();
|
||||
currentMemoryUsage_ += memorySize;
|
||||
E2D_LOG_INFO("TexturePool: 已加载纹理: {} ({} 字节)", path, memorySize);
|
||||
return TextureRef(texture, &result.first->second, &mutex_);
|
||||
}
|
||||
|
||||
return TextureRef();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从内存加载纹理
|
||||
* @param data 像素数据
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param channels 通道数
|
||||
* @param key 缓存键
|
||||
* @return 纹理引用
|
||||
*
|
||||
* 从内存中的像素数据创建纹理并加入纹理池
|
||||
*/
|
||||
TextureRef TexturePool::loadFromMemory(const uint8_t* data, int width, int height,
|
||||
int channels, const std::string& key) {
|
||||
TextureKey textureKey(key);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
// 检查缓存
|
||||
auto it = cache_.find(textureKey);
|
||||
if (it != cache_.end()) {
|
||||
it->second.touch();
|
||||
it->second.refCount.fetch_add(1, std::memory_order_relaxed);
|
||||
cacheHits_.fetch_add(1, std::memory_order_relaxed);
|
||||
return TextureRef(it->second.texture, &it->second, &mutex_);
|
||||
}
|
||||
|
||||
cacheMisses_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// 获取渲染后端
|
||||
RenderBackend* backend = nullptr;
|
||||
if (scene_) {
|
||||
backend = nullptr; // TODO: 从 Scene 获取 RenderBackend
|
||||
}
|
||||
|
||||
if (!backend) {
|
||||
E2D_LOG_ERROR("TexturePool: 渲染后端不可用");
|
||||
return TextureRef();
|
||||
}
|
||||
|
||||
// 创建纹理
|
||||
Ptr<Texture> texture = backend->createTexture(width, height, data, channels);
|
||||
if (!texture) {
|
||||
E2D_LOG_ERROR("TexturePool: 从内存创建纹理失败");
|
||||
return TextureRef();
|
||||
}
|
||||
|
||||
// 计算内存大小
|
||||
size_t memorySize = calculateTextureMemory(texture.get());
|
||||
|
||||
// 检查内存限制
|
||||
if (maxMemoryUsage_ > 0 && currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
|
||||
evictLRU(currentMemoryUsage_ + memorySize - maxMemoryUsage_);
|
||||
|
||||
if (currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
|
||||
E2D_LOG_WARN("TexturePool: 内存限制超出");
|
||||
return TextureRef();
|
||||
}
|
||||
}
|
||||
|
||||
// 创建缓存条目
|
||||
auto result = cache_.emplace(textureKey, TexturePoolEntry(nullptr, textureKey, 0));
|
||||
if (result.second) {
|
||||
result.first->second.texture = texture;
|
||||
result.first->second.memorySize = memorySize;
|
||||
result.first->second.refCount.store(1, std::memory_order_relaxed);
|
||||
result.first->second.touch();
|
||||
currentMemoryUsage_ += memorySize;
|
||||
E2D_LOG_INFO("TexturePool: 已从内存创建纹理 ({} 字节)", memorySize);
|
||||
return TextureRef(texture, &result.first->second, &mutex_);
|
||||
}
|
||||
|
||||
return TextureRef();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取或加载纹理
|
||||
* @param path 文件路径
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*
|
||||
* 如果纹理已缓存则返回缓存,否则加载纹理
|
||||
*/
|
||||
TextureRef TexturePool::getOrLoad(const std::string& path, const TextureLoadOptions& options) {
|
||||
return getOrLoad(path, Rect::Zero(), options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取或加载纹理区域
|
||||
* @param path 文件路径
|
||||
* @param region 纹理区域
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*
|
||||
* 如果纹理区域已缓存则返回缓存,否则加载纹理区域
|
||||
*/
|
||||
TextureRef TexturePool::getOrLoad(const std::string& path, const Rect& region,
|
||||
const TextureLoadOptions& options) {
|
||||
TextureKey key(path, region);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
// 检查缓存
|
||||
auto it = cache_.find(key);
|
||||
if (it != cache_.end()) {
|
||||
it->second.touch();
|
||||
it->second.refCount.fetch_add(1, std::memory_order_relaxed);
|
||||
cacheHits_.fetch_add(1, std::memory_order_relaxed);
|
||||
return TextureRef(it->second.texture, &it->second, &mutex_);
|
||||
}
|
||||
|
||||
// 释放锁后调用 load
|
||||
// 注意:这里需要重新设计以避免死锁
|
||||
// 简化处理:直接在这里加载
|
||||
|
||||
cacheMisses_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
RenderBackend* backend = nullptr;
|
||||
if (scene_) {
|
||||
backend = nullptr; // TODO: 从 Scene 获取 RenderBackend
|
||||
}
|
||||
|
||||
if (!backend) {
|
||||
E2D_LOG_ERROR("TexturePool: 渲染后端不可用");
|
||||
return TextureRef();
|
||||
}
|
||||
|
||||
Ptr<Texture> texture = backend->loadTexture(path);
|
||||
if (!texture) {
|
||||
E2D_LOG_ERROR("TexturePool: 加载纹理失败: {}", path);
|
||||
return TextureRef();
|
||||
}
|
||||
|
||||
size_t memorySize = calculateTextureMemory(texture.get());
|
||||
|
||||
if (maxMemoryUsage_ > 0 && currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
|
||||
evictLRU(currentMemoryUsage_ + memorySize - maxMemoryUsage_);
|
||||
|
||||
if (currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
|
||||
E2D_LOG_WARN("TexturePool: 内存限制超出");
|
||||
return TextureRef();
|
||||
}
|
||||
}
|
||||
|
||||
auto result = cache_.emplace(key, TexturePoolEntry(nullptr, key, 0));
|
||||
if (result.second) {
|
||||
result.first->second.texture = texture;
|
||||
result.first->second.memorySize = memorySize;
|
||||
result.first->second.refCount.store(1, std::memory_order_relaxed);
|
||||
result.first->second.touch();
|
||||
currentMemoryUsage_ += memorySize;
|
||||
return TextureRef(texture, &result.first->second, &mutex_);
|
||||
}
|
||||
|
||||
return TextureRef();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 引用计数管理
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 增加引用计数
|
||||
* @param key 纹理键
|
||||
* @return 是否成功
|
||||
*
|
||||
* 增加指定纹理的引用计数
|
||||
*/
|
||||
bool TexturePool::addRef(const TextureKey& key) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto it = cache_.find(key);
|
||||
if (it != cache_.end()) {
|
||||
it->second.touch();
|
||||
it->second.refCount.fetch_add(1, std::memory_order_relaxed);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 减少引用计数
|
||||
* @param key 纹理键
|
||||
* @return 减少后的引用计数
|
||||
*
|
||||
* 减少指定纹理的引用计数并返回新值
|
||||
*/
|
||||
uint32_t TexturePool::release(const TextureKey& key) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto it = cache_.find(key);
|
||||
if (it != cache_.end()) {
|
||||
uint32_t count = it->second.refCount.fetch_sub(1, std::memory_order_relaxed);
|
||||
return count > 0 ? count - 1 : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取引用计数
|
||||
* @param key 纹理键
|
||||
* @return 引用计数
|
||||
*
|
||||
* 获取指定纹理的当前引用计数
|
||||
*/
|
||||
uint32_t TexturePool::getRefCount(const TextureKey& key) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto it = cache_.find(key);
|
||||
if (it != cache_.end()) {
|
||||
return it->second.refCount.load(std::memory_order_relaxed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 缓存管理
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 检查纹理是否已缓存
|
||||
* @param key 纹理键
|
||||
* @return 是否已缓存
|
||||
*
|
||||
* 检查指定纹理是否存在于缓存中
|
||||
*/
|
||||
bool TexturePool::isCached(const TextureKey& key) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return cache_.find(key) != cache_.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从缓存中移除纹理
|
||||
* @param key 纹理键
|
||||
* @return 是否成功
|
||||
*
|
||||
* 从缓存中移除指定的纹理
|
||||
*/
|
||||
bool TexturePool::removeFromCache(const TextureKey& key) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto it = cache_.find(key);
|
||||
if (it != cache_.end()) {
|
||||
currentMemoryUsage_ -= it->second.memorySize;
|
||||
cache_.erase(it);
|
||||
E2D_LOG_DEBUG("TexturePool: 已从缓存移除纹理");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 垃圾回收(移除引用计数为 0 的纹理)
|
||||
* @return 移除的纹理数量
|
||||
*
|
||||
* 清理所有引用计数为0的纹理,释放内存
|
||||
*/
|
||||
size_t TexturePool::collectGarbage() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
size_t removed = 0;
|
||||
for (auto it = cache_.begin(); it != cache_.end(); ) {
|
||||
if (it->second.refCount.load(std::memory_order_relaxed) == 0) {
|
||||
currentMemoryUsage_ -= it->second.memorySize;
|
||||
it = cache_.erase(it);
|
||||
++removed;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (removed > 0) {
|
||||
E2D_LOG_INFO("TexturePool: 垃圾回收 {} 个纹理", removed);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空所有缓存
|
||||
*
|
||||
* 移除纹理池中的所有纹理
|
||||
*/
|
||||
void TexturePool::clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
cache_.clear();
|
||||
currentMemoryUsage_ = 0;
|
||||
|
||||
E2D_LOG_INFO("TexturePool: 已清除所有纹理");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 内存管理
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取当前内存使用量
|
||||
* @return 内存使用量(字节)
|
||||
*
|
||||
* 返回纹理池当前的内存使用量
|
||||
*/
|
||||
size_t TexturePool::getMemoryUsage() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return currentMemoryUsage_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置最大内存使用量
|
||||
* @param maxMemory 最大内存使用量(0 表示无限制)
|
||||
*
|
||||
* 设置纹理池的内存上限,如果当前使用量超过新上限则执行淘汰
|
||||
*/
|
||||
void TexturePool::setMaxMemoryUsage(size_t maxMemory) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
maxMemoryUsage_ = maxMemory;
|
||||
|
||||
// 如果当前内存超过新的限制,执行淘汰
|
||||
if (maxMemoryUsage_ > 0 && currentMemoryUsage_ > maxMemoryUsage_) {
|
||||
evictLRU(maxMemoryUsage_);
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("TexturePool: 最大内存设置为 {} 字节", maxMemory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行 LRU 淘汰
|
||||
* @param targetMemory 目标内存使用量
|
||||
* @return 淘汰的纹理数量
|
||||
*
|
||||
* 根据LRU算法淘汰最少使用的纹理以达到目标内存使用量
|
||||
*/
|
||||
size_t TexturePool::evictLRU(size_t targetMemory) {
|
||||
// 注意:调用者应该已持有锁
|
||||
|
||||
if (cache_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 收集所有条目并按最后访问时间排序
|
||||
std::vector<std::pair<TextureKey, uint64_t>> entries;
|
||||
entries.reserve(cache_.size());
|
||||
|
||||
for (const auto& pair : cache_) {
|
||||
// 只淘汰引用计数为 0 的纹理
|
||||
if (pair.second.refCount.load(std::memory_order_relaxed) == 0) {
|
||||
entries.emplace_back(pair.first, pair.second.lastAccessTime);
|
||||
}
|
||||
}
|
||||
|
||||
// 按访问时间升序排序(最旧的在前)
|
||||
std::sort(entries.begin(), entries.end(),
|
||||
[](const auto& a, const auto& b) { return a.second < b.second; });
|
||||
|
||||
size_t evicted = 0;
|
||||
size_t target = targetMemory > 0 ? targetMemory : 0;
|
||||
|
||||
for (const auto& entry : entries) {
|
||||
if (targetMemory > 0 && currentMemoryUsage_ <= target) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto it = cache_.find(entry.first);
|
||||
if (it != cache_.end()) {
|
||||
currentMemoryUsage_ -= it->second.memorySize;
|
||||
cache_.erase(it);
|
||||
++evicted;
|
||||
}
|
||||
}
|
||||
|
||||
if (evicted > 0) {
|
||||
evictionCount_.fetch_add(evicted, std::memory_order_relaxed);
|
||||
E2D_LOG_INFO("TexturePool: LRU 淘汰 {} 个纹理", evicted);
|
||||
}
|
||||
|
||||
return evicted;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 统计信息
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
* @return 统计信息结构体
|
||||
*
|
||||
* 返回纹理池的统计信息,包括纹理数量、内存使用、缓存命中率等
|
||||
*/
|
||||
TexturePool::Stats TexturePool::getStats() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
Stats stats;
|
||||
stats.textureCount = cache_.size();
|
||||
stats.memoryUsage = currentMemoryUsage_;
|
||||
stats.maxMemoryUsage = maxMemoryUsage_;
|
||||
stats.cacheHits = cacheHits_.load(std::memory_order_relaxed);
|
||||
stats.cacheMisses = cacheMisses_.load(std::memory_order_relaxed);
|
||||
stats.evictionCount = evictionCount_.load(std::memory_order_relaxed);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重置统计信息
|
||||
*
|
||||
* 清零缓存命中、未命中和淘汰计数
|
||||
*/
|
||||
void TexturePool::resetStats() {
|
||||
cacheHits_.store(0, std::memory_order_relaxed);
|
||||
cacheMisses_.store(0, std::memory_order_relaxed);
|
||||
evictionCount_.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 私有方法
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 计算纹理内存大小
|
||||
* @param texture 纹理对象
|
||||
* @return 内存大小(字节)
|
||||
*
|
||||
* 根据纹理的尺寸、通道数和像素格式计算内存占用
|
||||
*/
|
||||
size_t TexturePool::calculateTextureMemory(const Texture* texture) {
|
||||
if (!texture) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int width = texture->getWidth();
|
||||
int height = texture->getHeight();
|
||||
int channels = texture->getChannels();
|
||||
|
||||
// 基础内存计算
|
||||
size_t baseSize = static_cast<size_t>(width) * height * channels;
|
||||
|
||||
// 根据像素格式调整
|
||||
PixelFormat format = texture->getFormat();
|
||||
switch (format) {
|
||||
case PixelFormat::RGB16F:
|
||||
case PixelFormat::RGBA16F:
|
||||
baseSize *= 2; // 半精度浮点
|
||||
break;
|
||||
case PixelFormat::RGB32F:
|
||||
case PixelFormat::RGBA32F:
|
||||
baseSize *= 4; // 全精度浮点
|
||||
break;
|
||||
case PixelFormat::Depth16:
|
||||
baseSize = static_cast<size_t>(width) * height * 2;
|
||||
break;
|
||||
case PixelFormat::Depth24:
|
||||
case PixelFormat::Depth24Stencil8:
|
||||
baseSize = static_cast<size_t>(width) * height * 4;
|
||||
break;
|
||||
case PixelFormat::Depth32F:
|
||||
baseSize = static_cast<size_t>(width) * height * 4;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 考虑 Mipmaps(大约增加 33% 内存)
|
||||
// 注意:这里假设生成了 mipmaps,实际应该根据 TextureLoadOptions 判断
|
||||
// baseSize = baseSize * 4 / 3;
|
||||
|
||||
return baseSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否需要淘汰
|
||||
* @return 是否需要淘汰
|
||||
*
|
||||
* 检查当前内存使用量是否超过限制
|
||||
*/
|
||||
bool TexturePool::needsEviction() const {
|
||||
return maxMemoryUsage_ > 0 && currentMemoryUsage_ > maxMemoryUsage_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 尝试自动淘汰
|
||||
*
|
||||
* 如果内存使用量超过限制,执行LRU淘汰
|
||||
*/
|
||||
void TexturePool::tryAutoEvict() {
|
||||
if (needsEviction()) {
|
||||
evictLRU(maxMemoryUsage_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue