refactor(render): 重构渲染系统,移除旧图形模块并添加新渲染核心组件

- 移除旧的图形模块,包括GPU上下文、纹理、字体、帧缓冲等
- 添加新的渲染核心组件:RenderStats和Component
- 将渲染命令相关代码迁移到render/core目录
- 引入新的渲染队列系统替代旧批处理机制
- 清理不再使用的OpenGL和Vulkan后端实现
This commit is contained in:
ChestnutYueyue 2026-02-19 21:23:13 +08:00
parent a705a3d300
commit 89d1612336
125 changed files with 16451 additions and 15463 deletions

View File

@ -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>

View File

@ -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> &registry();
};
/**
* @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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 &center, float radius, const Color &color,
int segments, float width) override;
void fillCircle(const Vec2 &center, 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

View File

@ -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 truefalse
*/
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 truefalse
*/
bool compileFromSource(const char* vertexSource, const char* fragmentSource);
/**
* @brief Shader
* @param binary
* @return truefalse
*/
bool compileFromBinary(const std::vector<uint8_t>& binary);
/**
* @brief Shader二进制数据
* @param outBinary
* @return truefalse
*/
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 ID0
*/
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 truefalse
*/
bool getShaderBinary(const IShader& shader,
std::vector<uint8_t>& outBinary) override;
};
} // namespace extra2d

View File

@ -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

View File

@ -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

View File

@ -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 &center, float radius, const Color &color,
int segments, float width) override;
void fillCircle(const Vec2 &center, 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 &center, float radius, const Color &color,
int segments = 32, float width = 1.0f) = 0;
virtual void fillCircle(const Vec2 &center, 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 truefalse
*/
bool init(const std::string& cacheDir);
/**
* @brief
*/
void shutdown();
/**
* @brief
* @param name Shader名称
* @param sourceHash
* @return truefalse
*/
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 truefalse
*/
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 truefalse
*/
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 truefalse
*/
bool loadCacheIndex();
/**
* @brief
* @return truefalse
*/
bool saveCacheIndex();
/**
* @brief
* @param name Shader名称
* @return
*/
std::string getCachePath(const std::string& name) const;
/**
* @brief
* @return truefalse
*/
bool ensureCacheDirectory();
};
// 便捷宏
#define E2D_SHADER_CACHE() ::extra2d::ShaderCache::getInstance()
} // namespace extra2d

View File

@ -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 truefalse
*/
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 truefalse
*/
bool isEnabled() const { return enabled_; }
/**
* @brief
* @return truefalse
*/
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

View File

@ -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 truefalse
*/
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 truefalse
*/
virtual bool getShaderBinary(const IShader& shader,
std::vector<uint8_t>& outBinary) = 0;
};
} // namespace extra2d

View File

@ -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 truefalse
*/
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 truefalse
*/
bool parseCombinedFile(const std::string &content, std::string &outVert,
std::string &outFrag, ShaderMetadata &outMetadata);
/**
* @brief JSON块
* @param jsonContent JSON内容
* @param outMetadata
* @return truefalse
*/
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

View File

@ -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 truefalse
*/
bool init(Ptr<IShaderFactory> factory, const std::string& appName = "extra2d");
/**
* @brief Shader系统
* @param shaderDir Shader文件目录
* @param cacheDir
* @param factory Shader工厂
* @return truefalse
*/
bool init(const std::string& shaderDir,
const std::string& cacheDir,
Ptr<IShaderFactory> factory);
/**
* @brief Shader系统
*/
void shutdown();
/**
* @brief
* @return truefalse
*/
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 truefalse
*/
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 truefalse
*/
bool isHotReloadEnabled() const;
/**
* @brief
*/
void update();
/**
* @brief Shader
* @param name Shader名称
* @return truefalse
*/
bool reload(const std::string& name);
// ------------------------------------------------------------------------
// 内置Shader
// ------------------------------------------------------------------------
/**
* @brief Shader
* @param name Shader名称
* @return Shader实例
*/
Ptr<IShader> getBuiltin(const std::string& name);
/**
* @brief Shader
* @return truefalse
*/
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 &region,
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 &region,
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 2D3D
*/
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 &reg = 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 &reg = 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 &reg = 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 &reg = 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 &reg = registry();
auto it = reg.find(graphicsBackend);
if (it != reg.end()) {
return it->second.windowBackends;
}
return {};
}
} // namespace graphics
} // namespace extra2d

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 truefalse
*/
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 truefalse
*/
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 truefalse
*/
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 ID0
*/
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 truefalse
*/
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

View File

@ -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

View File

@ -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 truefalse
*/
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 truefalse
*/
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

View File

@ -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

View File

@ -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 &center, float radius,
const Color &color, int segments, float width) {
// TODO: 实现圆形边框绘制
}
void VulkanRenderer::fillCircle(const Vec2 &center, 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 truefalse
*/
bool ViewportAdapter::isInViewport(const Vec2 &screenPos) const {
return result_.viewport.containsPoint(screenPos);
}
/**
* @brief
* @param screenPos
* @return truefalse
*/
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

View File

@ -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前面返回truefalse
*/
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

View File

@ -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

View File

@ -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 truefalse
*
*
*/
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 truefalse
*
* 使
*/
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 truefalse
*
*
*/
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 truefalse
*
* 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 truefalse
*
* 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 truefalse
*
* 齿
*/
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 truefalse
*
*
*/
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

View File

@ -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上下文有效返回truefalse
*
* 使使acquire内存序
*/
bool GPUContext::isValid() const {
return valid_.load(std::memory_order_acquire);
}
} // namespace extra2d

View File

@ -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 truefalse
*/
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

View File

@ -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 truefalse
*/
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 truefalse
*/
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 truefalse
*/
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 truefalse
*/
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 truefalse
*/
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 truefalse
*/
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

View File

@ -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 truefalse
*/
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

View File

@ -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 truefalse
*/
bool ShaderLoader::fileExists(const std::string &filepath) {
return fs::exists(filepath);
}
/**
* @brief Shader文件
* @param content
* @param outVert
* @param outFrag
* @param outMetadata
* @return truefalse
*/
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 truefalse
*/
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

View File

@ -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 truefalse
*/
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 truefalse
*/
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 truefalse
*/
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 truefalse
*/
bool ShaderManager::isHotReloadEnabled() const {
return hotReloadEnabled_ && hotReloadSupported_;
}
/**
* @brief
*/
void ShaderManager::update() {
if (hotReloadEnabled_ && hotReloadSupported_) {
ShaderHotReloader::getInstance().update();
}
}
/**
* @brief Shader
* @param name Shader名称
* @return truefalse
*/
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 truefalse
*/
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

View File

@ -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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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

View File

@ -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通道创建遮罩
* RGBA4RGB31
*
* @param pixels
* @param width
* @param height
* @param channels 134
* @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-2550
*/
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值大于等于阈值返回truefalse
*/
bool AlphaMask::isOpaque(int x, int y, uint8_t threshold) const {
return getAlpha(x, y) >= threshold;
}
/**
* @brief
*
*
*
* @param x X坐标
* @param y Y坐标
* @return truefalse
*/
bool AlphaMask::isValid(int x, int y) const {
return x >= 0 && x < width_ && y >= 0 && y < height_;
}
} // namespace extra2d

View File

@ -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 truefalse
*
* 使
*/
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.01.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 truefalse
*
*
*/
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 truefalse
*/
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

View File

@ -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