refactor(render): 重构渲染模块结构,将图形相关代码迁移至render目录

- 将graphics目录重命名为render,调整相关头文件引用路径
- 添加RenderModule类作为应用模块集成渲染器
- 实现GLContext类管理OpenGL上下文状态
- 重构Camera和ViewportAdapter类,改进视口适配逻辑
- 统一渲染命令数据结构,优化形状节点渲染
- 更新构建脚本以适配新的目录结构
This commit is contained in:
ChestnutYueyue 2026-02-19 23:09:33 +08:00
parent 89d1612336
commit 1f2ed0942d
41 changed files with 5588 additions and 1862 deletions

View File

@ -13,8 +13,6 @@
#include <extra2d/core/compression.h>
#include <extra2d/core/aes_cipher.h>
// Config removed - app info now in Application class
// Platform
#include <extra2d/platform/iinput.h>
#include <extra2d/platform/iwindow.h>
@ -23,17 +21,26 @@
#include <extra2d/platform/backend_factory.h>
#include <extra2d/platform/window_module.h>
// Graphics
#include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/texture/font.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/graphics/core/render_target.h>
#include <extra2d/graphics/camera/viewport_adapter.h>
#include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/graphics/texture/texture_pool.h>
// Render Core
#include <extra2d/render/core/render_backend.h>
#include <extra2d/render/core/render_command.h>
#include <extra2d/render/core/render_context.h>
#include <extra2d/render/core/render_queue.h>
#include <extra2d/render/core/render_stats.h>
#include <extra2d/render/core/render_module.h>
#include <extra2d/render/core/backend_factory.h>
// Render Camera
#include <extra2d/render/camera/camera.h>
#include <extra2d/render/camera/viewport_adapter.h>
// Render Batch
#include <extra2d/render/batch/sprite_batcher.h>
#include <extra2d/render/batch/shape_batcher.h>
#include <extra2d/render/batch/text_batcher.h>
// Render Renderer
#include <extra2d/render/renderer.h>
// RHI (Render Hardware Interface)
#include <extra2d/render/rhi/rhi_types.h>

View File

@ -0,0 +1,144 @@
#pragma once
#include <glad/glad.h>
#include <extra2d/core/types.h>
#include <string>
#include <unordered_set>
namespace extra2d {
/**
* @brief OpenGL
*/
struct GLVersion {
int major = 0;
int minor = 0;
};
/**
* @brief OpenGL
*
* OpenGL
*/
class GLContext {
public:
/**
* @brief
*/
static GLContext& get();
/**
* @brief OpenGL
* @return true
*/
bool init();
/**
* @brief OpenGL
*/
void shutdown();
/**
* @brief
*/
bool isInitialized() const { return initialized_; }
// ========================================================================
// 版本信息
// ========================================================================
/**
* @brief OpenGL
*/
GLVersion getVersion() const { return version_; }
/**
* @brief
*/
std::string getVersionString() const;
/**
* @brief
*/
const std::string& getRenderer() const { return renderer_; }
/**
* @brief
*/
const std::string& getVendor() const { return vendor_; }
// ========================================================================
// 能力查询
// ========================================================================
/**
* @brief
*/
int getMaxTextureSize() const { return maxTextureSize_; }
/**
* @brief
*/
int getMaxTextureUnits() const { return maxTextureUnits_; }
/**
* @brief
*/
int getMaxVertexAttribs() const { return maxVertexAttribs_; }
/**
* @brief Uniform
*/
int getMaxUniformBufferBindings() const { return maxUniformBufferBindings_; }
/**
* @brief
*/
bool hasExtension(const std::string& extension) const;
// ========================================================================
// 状态管理
// ========================================================================
/**
* @brief OpenGL
*/
bool checkError(const char* operation = nullptr);
/**
* @brief
*/
void clearErrors();
private:
GLContext() = default;
~GLContext() = default;
GLContext(const GLContext&) = delete;
GLContext& operator=(const GLContext&) = delete;
/**
* @brief OpenGL
*/
void queryCapabilities();
/**
* @brief
*/
void parseVersion();
bool initialized_ = false;
GLVersion version_;
std::string renderer_;
std::string vendor_;
std::string glslVersion_;
int maxTextureSize_ = 0;
int maxTextureUnits_ = 0;
int maxVertexAttribs_ = 0;
int maxUniformBufferBindings_ = 0;
std::unordered_set<std::string> extensions_;
};
} // namespace extra2d

View File

@ -0,0 +1,302 @@
#pragma once
#include <extra2d/render/core/render_backend.h>
#include <extra2d/render/rhi/rhi_device.h>
#include <extra2d/render/backends/opengl/gl_rhi_device.h>
#include <glm/mat4x4.hpp>
#include <stack>
namespace extra2d {
class IWindow;
/**
* @brief OpenGL
*
* RenderBackend 使 OpenGL API
*/
class GLRenderBackend : public RenderBackend {
public:
/**
* @brief
*/
GLRenderBackend();
/**
* @brief
*/
~GLRenderBackend() override;
// ========================================================================
// 生命周期
// ========================================================================
/**
* @brief
* @param window
* @param config
* @return true
*/
bool init(IWindow *window, const RenderBackendConfig &config = {}) override;
/**
* @brief
*/
void shutdown() override;
/**
* @brief
*/
bool isValid() const override;
// ========================================================================
// 帧管理
// ========================================================================
/**
* @brief
* @param clearColor
*/
void beginFrame(const Color &clearColor = Colors::Black) override;
/**
* @brief
*/
void endFrame() override;
/**
* @brief
*/
void present() override;
// ========================================================================
// 渲染状态
// ========================================================================
/**
* @brief
*/
void setViewport(int x, int y, int width, int height) override;
/**
* @brief
*/
void setScissor(int x, int y, int width, int height) override;
/**
* @brief /
*/
void setScissorEnabled(bool enabled) override;
/**
* @brief
*/
void setProjectionMatrix(const glm::mat4 &matrix) override;
/**
* @brief
*/
void setViewMatrix(const glm::mat4 &matrix) override;
/**
* @brief
*/
void setVSync(bool enabled) override;
/**
* @brief
*/
bool isVSyncEnabled() const override;
// ========================================================================
// 精灵渲染
// ========================================================================
/**
* @brief
*/
void drawSprite(Ptr<rhi::RHITexture> texture, const Rect &destRect,
const Rect &srcRect = Rect(0, 0, 1, 1),
const Color &color = Colors::White,
float rotation = 0.0f, bool flipX = false,
bool flipY = false) override;
/**
* @brief 使
*/
void drawSprite(Ptr<rhi::RHITexture> texture,
const glm::mat4 &transform,
const Rect &srcRect = Rect(0, 0, 1, 1),
const Color &color = Colors::White) override;
/**
* @brief
*/
void draw9Slice(Ptr<rhi::RHITexture> texture, const Rect &destRect,
const Rect &srcRect, const Vec2 &borderSize,
const Color &color = Colors::White) override;
// ========================================================================
// 文本渲染
// ========================================================================
/**
* @brief
*/
void drawText(const std::string &text, const Vec2 &position,
float fontSize, const Color &color = Colors::White) override;
/**
* @brief UTF-32
*/
void drawText(const std::u32string &text, const Vec2 &position,
float fontSize, const Color &color = Colors::White) override;
/**
* @brief
*/
Vec2 measureText(const std::string &text, float fontSize) override;
/**
* @brief UTF-32
*/
Vec2 measureText(const std::u32string &text, float fontSize) override;
// ========================================================================
// 形状渲染
// ========================================================================
/**
* @brief
*/
void drawRect(const Rect &rect, const Color &color,
float lineWidth = 1.0f) override;
/**
* @brief
*/
void fillRect(const Rect &rect, const Color &color) override;
/**
* @brief
*/
void drawCircle(const Vec2 &center, float radius, const Color &color,
float lineWidth = 1.0f, uint32_t segments = 32) override;
/**
* @brief
*/
void fillCircle(const Vec2 &center, float radius, const Color &color,
uint32_t segments = 32) override;
/**
* @brief 线
*/
void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
float lineWidth = 1.0f) override;
/**
* @brief
*/
void drawPolygon(const Vec2 *points, size_t count, const Color &color,
float lineWidth = 1.0f) override;
/**
* @brief
*/
void fillPolygon(const Vec2 *points, size_t count,
const Color &color) override;
// ========================================================================
// 批处理控制
// ========================================================================
/**
* @brief
*/
void beginSpriteBatch() override;
/**
* @brief
*/
void endSpriteBatch() override;
/**
* @brief
*/
void flush() override;
/**
* @brief
*/
void setSortKey(uint64_t key) override;
/**
* @brief
*/
void setLayer(int layer) override;
/**
* @brief
*/
void setBlendMode(rhi::BlendState blend) override;
// ========================================================================
// 访问器
// ========================================================================
/**
* @brief RHI
*/
rhi::RHIDevice *getDevice() const override;
/**
* @brief
*/
int getWidth() const override;
/**
* @brief
*/
int getHeight() const override;
/**
* @brief
*/
const RenderStats &getStats() const override;
/**
* @brief
*/
void resetStats() override;
/**
* @brief API
*/
rhi::GraphicsAPI getAPI() const override;
/**
* @brief
*/
const rhi::DeviceCaps &getCaps() const override;
private:
bool initialized_ = false;
IWindow *window_ = nullptr;
RenderBackendConfig config_;
RenderStats stats_;
Ptr<rhi::RHIDevice> device_;
glm::mat4 projectionMatrix_ = glm::mat4(1.0f);
glm::mat4 viewMatrix_ = glm::mat4(1.0f);
int width_ = 0;
int height_ = 0;
uint64_t sortKey_ = 0;
int layer_ = 0;
rhi::BlendState blendState_ = rhi::BlendState::alphaBlend();
bool inBatch_ = false;
};
} // namespace extra2d

View File

@ -3,9 +3,7 @@
#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 {
@ -20,180 +18,193 @@ namespace rhi {
*/
class GLRHIDevice : public RHIDevice {
public:
/**
* @brief
*/
GLRHIDevice();
/**
* @brief
*/
GLRHIDevice();
/**
* @brief
*/
~GLRHIDevice() override;
/**
* @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_; }
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;
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<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<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<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<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;
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 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;
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 设置
// ========================================================================
// ========================================================================
// 渲染命令 - 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 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 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;
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; }
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_;
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;
// 当前绑定的资源
Ptr<RHIPipeline> currentPipeline_;
Ptr<RHIShader> currentShader_;
Ptr<RHIFramebuffer> currentFramebuffer_;
IndexFormat currentIndexFormat_ = IndexFormat::UInt16;
// 默认帧缓冲尺寸
uint32_t defaultFBWidth_ = 0;
uint32_t defaultFBHeight_ = 0;
// 默认帧缓冲尺寸
uint32_t defaultFBWidth_ = 0;
uint32_t defaultFBHeight_ = 0;
// 裁剪测试状态
bool scissorEnabled_ = false;
// 裁剪测试状态
bool scissorEnabled_ = false;
/**
* @brief
*/
void initCaps();
/**
* @brief
*/
void initCaps();
/**
* @brief
*/
void setDefaultState();
/**
* @brief
*/
void setDefaultState();
/**
* @brief 线
*/
void applyPipelineState(const BlendState& blend,
const DepthStencilState& depthStencil,
const RasterizerState& rasterizer);
/**
* @brief 线
*/
void applyPipelineState(const BlendState &blend,
const DepthStencilState &depthStencil,
const RasterizerState &rasterizer);
};
} // namespace rhi

View File

@ -0,0 +1,230 @@
#pragma once
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <glm/mat4x4.hpp>
#include <string>
namespace extra2d {
class ViewportAdapter;
/**
* @brief
*
* 2D
*/
class Camera {
public:
Camera();
Camera(float left, float right, float bottom, float top);
~Camera() = default;
// ========================================================================
// 视口设置
// ========================================================================
/**
* @brief
*/
void setViewport(float left, float right, float bottom, float top);
/**
* @brief
*/
void setViewportSize(float width, float height);
/**
* @brief
*/
Rect getViewport() const;
/**
* @brief
*/
float getViewportWidth() const { return right_ - left_; }
/**
* @brief
*/
float getViewportHeight() const { return top_ - bottom_; }
// ========================================================================
// 位置和变换
// ========================================================================
/**
* @brief
*/
void setPos(const Vec2& position);
void setPos(float x, float y);
/**
* @brief
*/
Vec2 getPosition() const { return position_; }
/**
* @brief
*/
void move(const Vec2& offset);
void move(float x, float y);
/**
* @brief
*/
void setRotation(float degrees);
/**
* @brief
*/
float getRotation() const { return rotation_; }
/**
* @brief
*/
void rotate(float degrees);
/**
* @brief
*/
void setZoom(float zoom);
/**
* @brief
*/
float getZoom() const { return zoom_; }
/**
* @brief
*/
void zoomBy(float factor);
// ========================================================================
// 边界限制
// ========================================================================
/**
* @brief
*/
void setBounds(const Rect& bounds);
/**
* @brief
*/
void clearBounds();
/**
* @brief
*/
Rect getBounds() const { return bounds_; }
/**
* @brief
*/
bool hasBounds() const { return hasBounds_; }
// ========================================================================
// 矩阵
// ========================================================================
/**
* @brief
*/
glm::mat4 getViewMatrix() const;
/**
* @brief
*/
glm::mat4 getProjectionMatrix() const;
/**
* @brief
*/
glm::mat4 getViewProjectionMatrix() const;
/**
* @brief
*/
void updateMatrices();
// ========================================================================
// 坐标转换
// ========================================================================
/**
* @brief
*/
Vec2 screenToWorld(const Vec2& screenPos) const;
/**
* @brief
*/
Vec2 worldToScreen(const Vec2& worldPos) const;
/**
* @brief
*/
void lookAt(const Vec2& target);
// ========================================================================
// 视口适配器
// ========================================================================
/**
* @brief
*/
void setViewportAdapter(ViewportAdapter* adapter) { viewportAdapter_ = adapter; }
/**
* @brief
*/
ViewportAdapter* getViewportAdapter() const { return viewportAdapter_; }
/**
* @brief
*/
void applyViewportAdapter();
// ========================================================================
// 调试
// ========================================================================
/**
* @brief
*/
const std::string& getDebugName() const { return debugName_; }
/**
* @brief
*/
void setDebugName(const std::string& name) { debugName_ = name; }
private:
/**
* @brief
*/
void applyBounds();
Vec2 position_;
float rotation_;
float zoom_;
float left_;
float right_;
float bottom_;
float top_;
Rect bounds_;
bool hasBounds_;
glm::mat4 viewMatrix_;
glm::mat4 projectionMatrix_;
glm::mat4 viewProjectionMatrix_;
bool matricesDirty_;
ViewportAdapter* viewportAdapter_;
std::string debugName_;
};
} // namespace extra2d

View File

@ -0,0 +1,254 @@
#pragma once
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <string>
namespace extra2d {
/**
* @brief
*/
enum class ViewportScaleMode {
None, // 无缩放
Letterbox, // 保持宽高比,黑边
Crop, // 保持宽高比,裁剪
Stretch, // 拉伸填充
IntegerScale // 整数缩放
};
/**
* @brief
*/
enum class ViewportAlign {
Center,
TopLeft,
TopCenter,
TopRight,
CenterLeft,
CenterRight,
BottomLeft,
BottomCenter,
BottomRight
};
/**
* @brief
*/
struct ViewportConfig {
float designWidth = 1280.0f;
float designHeight = 720.0f;
ViewportScaleMode scaleMode = ViewportScaleMode::Letterbox;
ViewportAlign align = ViewportAlign::Center;
bool integerScaleOnly = false;
float minScale = 0.5f;
float maxScale = 4.0f;
ViewportConfig() = default;
ViewportConfig(float width, float height,
ViewportScaleMode mode = ViewportScaleMode::Letterbox)
: designWidth(width), designHeight(height), scaleMode(mode) {}
/**
* @brief HD (1280x720)
*/
static ViewportConfig hd() {
return ViewportConfig(1280.0f, 720.0f);
}
/**
* @brief Full HD (1920x1080)
*/
static ViewportConfig fullHd() {
return ViewportConfig(1920.0f, 1080.0f);
}
/**
* @brief 4K (3840x2160)
*/
static ViewportConfig uhd4k() {
return ViewportConfig(3840.0f, 2160.0f);
}
};
/**
* @brief
*/
struct ViewportResult {
float x = 0.0f;
float y = 0.0f;
float width = 0.0f;
float height = 0.0f;
float scale = 1.0f;
int offsetX = 0;
int offsetY = 0;
int screenWidth = 0;
int screenHeight = 0;
ViewportResult() = default;
/**
* @brief
*/
Rect getRect() const { return Rect(x, y, width, height); }
/**
* @brief
*/
Size getScaledDesignSize() const { return Size(width / scale, height / scale); }
};
/**
* @brief
*
*
*/
class ViewportAdapter {
public:
ViewportAdapter();
explicit ViewportAdapter(const ViewportConfig& config);
~ViewportAdapter() = default;
// ========================================================================
// 配置
// ========================================================================
/**
* @brief
*/
void setConfig(const ViewportConfig& config);
/**
* @brief
*/
const ViewportConfig& getConfig() const { return config_; }
/**
* @brief
*/
void setDesignSize(float width, float height);
/**
* @brief
*/
void setScaleMode(ViewportScaleMode mode);
/**
* @brief
*/
void setAlign(ViewportAlign align);
// ========================================================================
// 更新
// ========================================================================
/**
* @brief
*/
void update(int screenWidth, int screenHeight);
/**
* @brief
*/
const ViewportResult& getResult() const { return result_; }
// ========================================================================
// 查询
// ========================================================================
/**
* @brief
*/
float getDesignWidth() const { return config_.designWidth; }
/**
* @brief
*/
float getDesignHeight() const { return config_.designHeight; }
/**
* @brief
*/
float getDesignAspectRatio() const;
/**
* @brief
*/
int getScreenWidth() const { return result_.screenWidth; }
/**
* @brief
*/
int getScreenHeight() const { return result_.screenHeight; }
/**
* @brief
*/
float getScreenAspectRatio() const;
/**
* @brief
*/
float getScale() const { return result_.scale; }
/**
* @brief X
*/
int getOffsetX() const { return result_.offsetX; }
/**
* @brief Y
*/
int getOffsetY() const { return result_.offsetY; }
// ========================================================================
// 坐标转换
// ========================================================================
/**
* @brief
*/
Vec2 screenToDesign(const Vec2& screenPos) const;
/**
* @brief
*/
Vec2 designToScreen(const Vec2& designPos) const;
// ========================================================================
// 调试
// ========================================================================
/**
* @brief
*/
const std::string& getDebugName() const { return debugName_; }
/**
* @brief
*/
void setDebugName(const std::string& name) { debugName_ = name; }
private:
/**
* @brief
*/
void calculateViewport();
/**
* @brief
*/
float calculateScale() const;
/**
* @brief
*/
void calculateOffset();
ViewportConfig config_;
ViewportResult result_;
std::string debugName_;
};
} // namespace extra2d

View File

@ -0,0 +1,182 @@
#pragma once
#include <extra2d/core/types.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/render/rhi/rhi_types.h>
#include <functional>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
/**
* @brief
*
*
* OpenGLVulkanDirectX
*/
class BackendFactory {
public:
/**
* @brief
*/
using BackendFn = std::function<UniquePtr<RenderBackend>()>;
/**
* @brief
*/
struct BackendInfo {
std::string name;
BackendFn createFn;
std::vector<std::string> compatibleWindowBackends;
rhi::GraphicsAPI api;
std::string description;
int priority;
};
/**
* @brief
* @param name "opengl", "vulkan"
* @param backend
* @param windowBackends
* @param api API
* @param description
* @param priority
*/
static void reg(const std::string& name,
BackendFn backend,
const std::vector<std::string>& windowBackends = {},
rhi::GraphicsAPI api = rhi::GraphicsAPI::OpenGL,
const std::string& description = "",
int priority = 0);
/**
* @brief
* @param name
*/
static void unreg(const std::string& name);
/**
* @brief
* @param name
* @return nullptr
*/
static UniquePtr<RenderBackend> createBackend(const std::string& name);
/**
* @brief
*
*
* @return nullptr
*/
static UniquePtr<RenderBackend> createDefaultBackend();
/**
* @brief
* @param windowBackend
* @return nullptr
*/
static UniquePtr<RenderBackend> createBackendForWindow(const std::string& windowBackend);
/**
* @brief API
* @param api API
* @return nullptr
*/
static UniquePtr<RenderBackend> createBackendForAPI(rhi::GraphicsAPI api);
/**
* @brief
* @param name
*/
static bool hasBackend(const std::string& name);
/**
* @brief
*/
static std::vector<std::string> getBackendNames();
/**
* @brief
* @param name
* @return nullptr
*/
static const BackendInfo* getBackendInfo(const std::string& name);
/**
* @brief
*/
static std::vector<BackendInfo> getAllBackendInfos();
/**
* @brief
* @param windowBackend
*/
static std::vector<std::string> getCompatibleBackends(const std::string& windowBackend);
/**
* @brief
*/
static void clear();
/**
* @brief
*/
static std::string getDefaultBackendName();
/**
* @brief
*/
static void setDefaultBackendName(const std::string& name);
private:
static std::unordered_map<std::string, BackendInfo>& getRegistry();
static std::mutex& getMutex();
static std::string& getDefaultName();
};
/**
* @brief
*
*
*/
class BackendRegistrar {
public:
BackendRegistrar(const std::string& name,
BackendFactory::BackendFn backend,
const std::vector<std::string>& windowBackends = {},
rhi::GraphicsAPI api = rhi::GraphicsAPI::OpenGL,
const std::string& description = "",
int priority = 0) {
BackendFactory::reg(name, backend, windowBackends, api, description, priority);
}
};
/**
* @brief
*
* 使
* E2D_REG_RENDER_BACKEND(opengl, GLRenderBackend, {"sdl2", "glfw"})
*/
#define E2D_REG_RENDER_BACKEND(name, BackendClass, windowBackends) \
static ::extra2d::BackendRegistrar E2D_CONCAT(backend_reg_, name)( \
#name, \
[]() -> ::extra2d::UniquePtr<::extra2d::RenderBackend> { \
return ::extra2d::makeUnique<BackendClass>(); \
}, \
windowBackends)
/**
* @brief
*/
#define E2D_REG_RENDER_BACKEND_EX(name, BackendClass, windowBackends, api, desc, prio) \
static ::extra2d::BackendRegistrar E2D_CONCAT(backend_reg_, name)( \
#name, \
[]() -> ::extra2d::UniquePtr<::extra2d::RenderBackend> { \
return ::extra2d::makeUnique<BackendClass>(); \
}, \
windowBackends, api, desc, prio)
} // namespace extra2d

View File

@ -0,0 +1,324 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <extra2d/render/rhi/rhi_device.h>
#include <extra2d/render/rhi/rhi_types.h>
#include <glm/mat4x4.hpp>
#include <string>
namespace extra2d {
class IWindow;
/**
* @brief
*/
struct RenderStats {
uint32_t drawCalls = 0;
uint32_t spriteCount = 0;
uint32_t textCharCount = 0;
uint32_t shapeCount = 0;
uint32_t batchCount = 0;
uint32_t textureBinds = 0;
uint32_t shaderBinds = 0;
void reset() {
drawCalls = 0;
spriteCount = 0;
textCharCount = 0;
shapeCount = 0;
batchCount = 0;
textureBinds = 0;
shaderBinds = 0;
}
};
/**
* @brief
*/
struct RenderBackendConfig {
rhi::GraphicsAPI api = rhi::GraphicsAPI::OpenGL;
bool vsync = true;
uint32_t maxBatchSize = 10000;
bool enableDebug = false;
};
/**
* @brief
*
* RHI 使
*
*/
class RenderBackend {
public:
virtual ~RenderBackend() = default;
// ========================================================================
// 生命周期
// ========================================================================
/**
* @brief
* @param window
* @param config
* @return true
*/
virtual bool init(IWindow *window,
const RenderBackendConfig &config = {}) = 0;
/**
* @brief
*/
virtual void shutdown() = 0;
/**
* @brief
*/
virtual bool isValid() const = 0;
// ========================================================================
// 帧管理
// ========================================================================
/**
* @brief
* @param clearColor
*/
virtual void beginFrame(const Color &clearColor = Colors::Black) = 0;
/**
* @brief
*/
virtual void endFrame() = 0;
/**
* @brief
*/
virtual void present() = 0;
// ========================================================================
// 渲染状态
// ========================================================================
/**
* @brief
*/
virtual void setViewport(int x, int y, int width, int height) = 0;
/**
* @brief
*/
virtual void setScissor(int x, int y, int width, int height) = 0;
/**
* @brief /
*/
virtual void setScissorEnabled(bool enabled) = 0;
/**
* @brief
*/
virtual void setProjectionMatrix(const glm::mat4 &matrix) = 0;
/**
* @brief
*/
virtual void setViewMatrix(const glm::mat4 &matrix) = 0;
/**
* @brief
*/
virtual void setVSync(bool enabled) = 0;
/**
* @brief
*/
virtual bool isVSyncEnabled() const = 0;
// ========================================================================
// 精灵渲染
// ========================================================================
/**
* @brief
* @param texture
* @param destRect
* @param srcRect
* @param color
* @param rotation
* @param flipX
* @param flipY
*/
virtual void drawSprite(Ptr<rhi::RHITexture> texture, const Rect &destRect,
const Rect &srcRect = Rect(0, 0, 1, 1),
const Color &color = Colors::White,
float rotation = 0.0f, bool flipX = false,
bool flipY = false) = 0;
/**
* @brief 使
*/
virtual void drawSprite(Ptr<rhi::RHITexture> texture,
const glm::mat4 &transform,
const Rect &srcRect = Rect(0, 0, 1, 1),
const Color &color = Colors::White) = 0;
/**
* @brief
*/
virtual void draw9Slice(Ptr<rhi::RHITexture> texture, const Rect &destRect,
const Rect &srcRect, const Vec2 &borderSize,
const Color &color = Colors::White) = 0;
// ========================================================================
// 文本渲染
// ========================================================================
/**
* @brief
* @param text UTF-8
* @param position
* @param fontSize
* @param color
*/
virtual void drawText(const std::string &text, const Vec2 &position,
float fontSize, const Color &color = Colors::White) = 0;
/**
* @brief UTF-32
*/
virtual void drawText(const std::u32string &text, const Vec2 &position,
float fontSize, const Color &color = Colors::White) = 0;
/**
* @brief
*/
virtual Vec2 measureText(const std::string &text, float fontSize) = 0;
/**
* @brief UTF-32
*/
virtual Vec2 measureText(const std::u32string &text, float fontSize) = 0;
// ========================================================================
// 形状渲染
// ========================================================================
/**
* @brief
*/
virtual void drawRect(const Rect &rect, const Color &color,
float lineWidth = 1.0f) = 0;
/**
* @brief
*/
virtual void fillRect(const Rect &rect, const Color &color) = 0;
/**
* @brief
*/
virtual void drawCircle(const Vec2 &center, float radius, const Color &color,
float lineWidth = 1.0f, uint32_t segments = 32) = 0;
/**
* @brief
*/
virtual void fillCircle(const Vec2 &center, float radius, const Color &color,
uint32_t segments = 32) = 0;
/**
* @brief 线
*/
virtual void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
float lineWidth = 1.0f) = 0;
/**
* @brief
*/
virtual void drawPolygon(const Vec2 *points, size_t count, const Color &color,
float lineWidth = 1.0f) = 0;
/**
* @brief
*/
virtual void fillPolygon(const Vec2 *points, size_t count,
const Color &color) = 0;
// ========================================================================
// 批处理控制
// ========================================================================
/**
* @brief
*/
virtual void beginSpriteBatch() = 0;
/**
* @brief
*/
virtual void endSpriteBatch() = 0;
/**
* @brief
*/
virtual void flush() = 0;
/**
* @brief
*/
virtual void setSortKey(uint64_t key) = 0;
/**
* @brief
*/
virtual void setLayer(int layer) = 0;
/**
* @brief
*/
virtual void setBlendMode(rhi::BlendState blend) = 0;
// ========================================================================
// 访问器
// ========================================================================
/**
* @brief RHI
*/
virtual rhi::RHIDevice *getDevice() const = 0;
/**
* @brief
*/
virtual int getWidth() const = 0;
/**
* @brief
*/
virtual int getHeight() const = 0;
/**
* @brief
*/
virtual const RenderStats &getStats() const = 0;
/**
* @brief
*/
virtual void resetStats() = 0;
/**
* @brief API
*/
virtual rhi::GraphicsAPI getAPI() const = 0;
/**
* @brief
*/
virtual const rhi::DeviceCaps &getCaps() const = 0;
};
} // namespace extra2d

View File

@ -30,6 +30,7 @@ enum class RenderCommandType : uint8_t {
* @brief
*/
enum class ShapeType : uint8_t {
Point,
Line,
Rect,
FilledRect,

View File

@ -3,64 +3,30 @@
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <extra2d/render/core/render_backend.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;
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) {}
RenderContextConfig()
: maxBatchSize(10000), enableBatching(true), enableScissor(true),
enableStats(true), preferredAPI(rhi::GraphicsAPI::OpenGL) {}
};
/**
@ -70,150 +36,150 @@ struct RenderContextConfig {
*/
class RenderContext {
public:
static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 10000;
static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 10000;
RenderContext();
explicit RenderContext(const RenderContextConfig& config);
~RenderContext();
RenderContext();
explicit RenderContext(const RenderContextConfig &config);
~RenderContext();
/**
* @brief
*/
bool init(IWindow* window);
bool init(IWindow* window, rhi::GraphicsAPI api);
/**
* @brief
*/
bool init(IWindow *window);
bool init(IWindow *window, rhi::GraphicsAPI api);
/**
* @brief
*/
void shutdown();
/**
* @brief
*/
void shutdown();
/**
* @brief
*/
bool isValid() const;
/**
* @brief
*/
bool isValid() const;
/**
* @brief
*/
void beginFrame();
/**
* @brief
*/
void beginFrame();
/**
* @brief
*/
void endFrame();
/**
* @brief
*/
void endFrame();
/**
* @brief
*/
void present();
/**
* @brief
*/
void present();
/**
* @brief
*/
void clear(const Color& color = Colors::Black);
void clear(const Color& color, float depth, uint8_t stencil = 0);
/**
* @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 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 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 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
*/
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
*/
RenderQueue &getQueue() { return queue_; }
const RenderQueue &getQueue() const { return queue_; }
void clearQueue();
/**
* @brief
*/
void flushQueue();
/**
* @brief
*/
void flushQueue();
/**
* @brief RHI
*/
Ptr<rhi::RHIDevice> getDevice() const { return device_; }
/**
* @brief RHI
*/
Ptr<rhi::RHIDevice> getDevice() const { return device_; }
/**
* @brief
*/
IWindow* getWindow() const { return window_; }
/**
* @brief
*/
IWindow *getWindow() const { return window_; }
/**
* @brief
*/
const RenderStats& getStats() const { return stats_; }
void resetStats();
/**
* @brief
*/
const RenderStats &getStats() const { return stats_; }
void resetStats();
/**
* @brief
*/
const RenderContextConfig& getConfig() const { return config_; }
void setConfig(const RenderContextConfig& config);
/**
* @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
*/
uint32_t getFramebufferWidth() const { return fbWidth_; }
uint32_t getFramebufferHeight() const { return fbHeight_; }
/**
* @brief
*/
void setVSync(bool enabled);
bool isVSyncEnabled() const;
/**
* @brief
*/
void setVSync(bool enabled);
bool isVSyncEnabled() const;
/**
* @brief
*/
void setDefaultFramebufferSize(uint32_t width, uint32_t height);
/**
* @brief
*/
void setDefaultFramebufferSize(uint32_t width, uint32_t height);
private:
void initDefaultState();
void initDefaultState();
Ptr<rhi::RHIDevice> device_;
IWindow* window_;
RenderQueue queue_;
RenderStats stats_;
RenderContextConfig config_;
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_;
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_;
uint32_t fbWidth_;
uint32_t fbHeight_;
bool initialized_;
};
} // namespace extra2d

View File

@ -0,0 +1,145 @@
#pragma once
#include <extra2d/core/module.h>
#include <extra2d/core/types.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/render/rhi/rhi_types.h>
namespace extra2d {
class IWindow;
class Renderer;
/**
* @brief
*/
struct RenderModuleConfig {
rhi::GraphicsAPI api = rhi::GraphicsAPI::OpenGL;
bool vsync = true;
uint32_t maxBatchSize = 10000;
bool enableDebug = false;
float designWidth = 1280.0f;
float designHeight = 720.0f;
RenderModuleConfig() = default;
/**
* @brief OpenGL
*/
static RenderModuleConfig opengl() {
RenderModuleConfig config;
config.api = rhi::GraphicsAPI::OpenGL;
return config;
}
/**
* @brief HD (1280x720)
*/
static RenderModuleConfig hd() {
RenderModuleConfig config;
config.designWidth = 1280.0f;
config.designHeight = 720.0f;
return config;
}
/**
* @brief Full HD (1920x1080)
*/
static RenderModuleConfig fullHd() {
RenderModuleConfig config;
config.designWidth = 1920.0f;
config.designHeight = 1080.0f;
return config;
}
};
/**
* @brief
*
* Module Application
*
*/
class RenderModule : public Module {
public:
RenderModule();
explicit RenderModule(const RenderModuleConfig& config);
~RenderModule() override;
// ========================================================================
// Module 接口实现
// ========================================================================
/**
* @brief
*/
bool init() override;
/**
* @brief
*/
void shutdown() override;
/**
* @brief
*/
bool ok() const override { return initialized_; }
/**
* @brief
*/
const char* name() const override { return "RenderModule"; }
/**
* @brief
*/
int priority() const override { return 10; }
/**
* @brief
*/
std::vector<std::type_index> deps() const override;
// ========================================================================
// 渲染器访问
// ========================================================================
/**
* @brief
*/
class Renderer* getRenderer() const { return renderer_.get(); }
/**
* @brief
*/
Ptr<Renderer> getRendererPtr() const { return renderer_; }
// ========================================================================
// 配置
// ========================================================================
/**
* @brief
*/
const RenderModuleConfig& getConfig() const { return config_; }
/**
* @brief
*/
void setConfig(const RenderModuleConfig& config);
// ========================================================================
// 静态工厂
// ========================================================================
/**
* @brief
*/
static Ptr<RenderModule> create(const RenderModuleConfig& config = {});
private:
RenderModuleConfig config_;
Ptr<Renderer> renderer_;
bool initialized_;
};
} // namespace extra2d

View File

@ -0,0 +1,326 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/module.h>
#include <extra2d/core/types.h>
#include <extra2d/render/batch/shape_batcher.h>
#include <extra2d/render/batch/sprite_batcher.h>
#include <extra2d/render/batch/text_batcher.h>
#include <extra2d/render/camera/camera.h>
#include <extra2d/render/camera/viewport_adapter.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/render/core/render_context.h>
#include <extra2d/render/rhi/rhi_types.h>
namespace extra2d {
class IWindow;
class Scene;
/**
* @brief
*/
struct RendererConfig {
rhi::GraphicsAPI api = rhi::GraphicsAPI::OpenGL;
bool vsync = true;
uint32_t maxBatchSize = 10000;
bool enableDebug = false;
ViewportConfig viewportConfig;
RendererConfig() = default;
/**
* @brief OpenGL
*/
static RendererConfig opengl() {
RendererConfig config;
config.api = rhi::GraphicsAPI::OpenGL;
return config;
}
};
/**
* @brief
*
*
* 使
*/
class Renderer : public Module {
public:
Renderer();
explicit Renderer(const RendererConfig &config);
~Renderer() override;
// ========================================================================
// Module 接口实现
// ========================================================================
/**
* @brief
*/
bool init() override;
/**
* @brief
*/
void shutdown() override;
/**
* @brief
*/
bool ok() const override { return initialized_; }
/**
* @brief
*/
const char *name() const override { return "Renderer"; }
/**
* @brief
*/
int priority() const override { return 10; }
// ========================================================================
// 初始化
// ========================================================================
/**
* @brief 使
*/
bool init(IWindow *window);
/**
* @brief 使
*/
bool init(IWindow *window, const RendererConfig &config);
/**
* @brief
*/
bool isInitialized() const { return initialized_; }
// ========================================================================
// 帧管理
// ========================================================================
/**
* @brief
*/
void beginFrame(const Color &clearColor = Colors::Black);
/**
* @brief
*/
void endFrame();
/**
* @brief
*/
void present();
// ========================================================================
// 场景渲染
// ========================================================================
/**
* @brief
*/
void renderScene(Scene *scene);
/**
* @brief
*/
void renderScene(Scene *scene, Camera *camera);
// ========================================================================
// 相机系统
// ========================================================================
/**
* @brief
*/
void setActiveCamera(Ptr<Camera> camera);
/**
* @brief
*/
Ptr<Camera> getActiveCamera() const;
/**
* @brief
*/
Ptr<Camera> getDefaultCamera() const { return defaultCamera_; }
/**
* @brief
*/
ViewportAdapter &getViewportAdapter() { return viewportAdapter_; }
/**
* @brief const
*/
const ViewportAdapter &getViewportAdapter() const { return viewportAdapter_; }
// ========================================================================
// 渲染状态
// ========================================================================
/**
* @brief
*/
void setViewport(int x, int y, int width, int height);
/**
* @brief
*/
void setScissor(int x, int y, int width, int height);
/**
* @brief /
*/
void setScissorEnabled(bool enabled);
/**
* @brief
*/
void setVSync(bool enabled);
/**
* @brief
*/
bool isVSyncEnabled() const;
// ========================================================================
// 批处理器访问
// ========================================================================
/**
* @brief
*/
SpriteBatcher *getSpriteBatcher() const { return spriteBatcher_.get(); }
/**
* @brief
*/
ShapeBatcher *getShapeBatcher() const { return shapeBatcher_.get(); }
/**
* @brief
*/
TextBatcher *getTextBatcher() const { return textBatcher_.get(); }
// ========================================================================
// 渲染上下文
// ========================================================================
/**
* @brief
*/
RenderContext *getContext() const { return context_.get(); }
/**
* @brief
*/
RenderBackend *getBackend() const { return backend_.get(); }
/**
* @brief RHI
*/
rhi::RHIDevice *getDevice() const;
// ========================================================================
// 统计信息
// ========================================================================
/**
* @brief
*/
const RenderStats &getStats() const;
/**
* @brief
*/
void resetStats();
// ========================================================================
// 配置
// ========================================================================
/**
* @brief
*/
const RendererConfig &getConfig() const { return config_; }
/**
* @brief
*/
void setConfig(const RendererConfig &config);
// ========================================================================
// 窗口信息
// ========================================================================
/**
* @brief
*/
int getWidth() const { return width_; }
/**
* @brief
*/
int getHeight() const { return height_; }
/**
* @brief
*/
IWindow *getWindow() const { return window_; }
// ========================================================================
// 静态工厂
// ========================================================================
/**
* @brief
*/
static Ptr<Renderer> create(const RendererConfig &config = {});
private:
/**
* @brief
*/
bool createBatchers();
/**
* @brief
*/
void createDefaultCamera();
/**
* @brief
*/
void updateViewportAdapter();
/**
* @brief
*/
void applyCameraTransform(Camera *camera);
IWindow *window_;
RendererConfig config_;
UniquePtr<RenderContext> context_;
UniquePtr<RenderBackend> backend_;
UniquePtr<SpriteBatcher> spriteBatcher_;
UniquePtr<ShapeBatcher> shapeBatcher_;
UniquePtr<TextBatcher> textBatcher_;
ViewportAdapter viewportAdapter_;
Ptr<Camera> activeCamera_;
Ptr<Camera> defaultCamera_;
int width_;
int height_;
bool initialized_;
};
} // namespace extra2d

View File

@ -0,0 +1,186 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <extra2d/render/rhi/rhi_texture.h>
#include <extra2d/render/rhi/rhi_types.h>
#include <string>
namespace extra2d {
/**
* @brief
*/
enum class TextureFilter {
Nearest,
Linear,
NearestMipmapNearest,
LinearMipmapNearest,
NearestMipmapLinear,
LinearMipmapLinear
};
/**
* @brief
*/
enum class TextureWrap {
Repeat,
MirroredRepeat,
ClampToEdge,
ClampToBorder
};
/**
* @brief
*/
struct TextureConfig {
TextureFilter minFilter = TextureFilter::Linear;
TextureFilter magFilter = TextureFilter::Linear;
TextureWrap wrapU = TextureWrap::ClampToEdge;
TextureWrap wrapV = TextureWrap::ClampToEdge;
bool generateMipmaps = true;
bool premultipliedAlpha = false;
TextureConfig() = default;
};
/**
* @brief
*
* RHI
*/
class Texture {
public:
Texture();
explicit Texture(Ptr<rhi::RHITexture> rhiTexture);
~Texture() = default;
// ========================================================================
// 工厂方法
// ========================================================================
/**
* @brief
*/
static Ptr<Texture> load(const std::string& path, const TextureConfig& config = {});
/**
* @brief
*/
static Ptr<Texture> create(uint32_t width, uint32_t height,
const void* data = nullptr,
rhi::Format format = rhi::Format::RGBA8_UNORM,
const TextureConfig& config = {});
/**
* @brief
*/
static Ptr<Texture> createSolidColor(uint32_t width, uint32_t height, const Color& color);
// ========================================================================
// 属性
// ========================================================================
/**
* @brief
*/
uint32_t getWidth() const { return width_; }
/**
* @brief
*/
uint32_t getHeight() const { return height_; }
/**
* @brief
*/
Size getSize() const { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
/**
* @brief
*/
rhi::Format getFormat() const { return format_; }
/**
* @brief
*/
bool isValid() const { return rhiTexture_ != nullptr; }
/**
* @brief RHI
*/
Ptr<rhi::RHITexture> getRHITexture() const { return rhiTexture_; }
// ========================================================================
// 数据操作
// ========================================================================
/**
* @brief
*/
bool updateData(const void* data, uint32_t x = 0, uint32_t y = 0,
uint32_t width = 0, uint32_t height = 0);
/**
* @brief
*/
std::vector<uint8_t> getData() const;
// ========================================================================
// 配置
// ========================================================================
/**
* @brief
*/
void setFilter(TextureFilter minFilter, TextureFilter magFilter);
/**
* @brief
*/
void setWrap(TextureWrap wrapU, TextureWrap wrapV);
/**
* @brief Mipmap
*/
void generateMipmaps();
// ========================================================================
// 路径
// ========================================================================
/**
* @brief
*/
const std::string& getPath() const { return path_; }
/**
* @brief
*/
void setPath(const std::string& path) { path_ = path; }
// ========================================================================
// 调试
// ========================================================================
/**
* @brief
*/
const std::string& getDebugName() const { return debugName_; }
/**
* @brief
*/
void setDebugName(const std::string& name) { debugName_ = name; }
private:
Ptr<rhi::RHITexture> rhiTexture_;
uint32_t width_ = 0;
uint32_t height_ = 0;
rhi::Format format_ = rhi::Format::RGBA8_UNORM;
std::string path_;
std::string debugName_;
};
} // namespace extra2d

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/graphics/camera/camera.h>
#include <extra2d/render/camera/camera.h>
#include <extra2d/scene/node.h>
#include <vector>
@ -16,182 +16,182 @@ class RenderQueue;
*/
class Scene : public Node {
public:
Scene();
~Scene() override = default;
Scene();
~Scene() override = default;
// -------------------------------------------------------------------------
// 场景属性
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// 场景属性
// -------------------------------------------------------------------------
/**
* @brief
* @param color
*/
void setBackgroundColor(const Color& color) { backgroundColor_ = color; }
/**
* @brief
* @param color
*/
void setBackgroundColor(const Color &color) { backgroundColor_ = color; }
/**
* @brief
* @return
*/
Color getBackgroundColor() const { return backgroundColor_; }
/**
* @brief
* @return
*/
Color getBackgroundColor() const { return backgroundColor_; }
// -------------------------------------------------------------------------
// 摄像机
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// 摄像机
// -------------------------------------------------------------------------
/**
* @brief
* @param camera
*/
void setCamera(Ptr<Camera> camera);
/**
* @brief
* @param camera
*/
void setCamera(Ptr<Camera> camera);
/**
* @brief
* @return
*/
Ptr<Camera> getCamera() const { return camera_; }
/**
* @brief
* @return
*/
Ptr<Camera> getCamera() const { return camera_; }
/**
* @brief
* @return
*/
Camera* getActiveCamera() const {
return camera_ ? camera_.get() : defaultCamera_.get();
}
/**
* @brief
* @return
*/
Camera *getActiveCamera() const {
return camera_ ? camera_.get() : defaultCamera_.get();
}
// -------------------------------------------------------------------------
// 视口和尺寸
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// 视口和尺寸
// -------------------------------------------------------------------------
/**
* @brief
* @param width
* @param height
*/
void setViewportSize(float width, float height);
/**
* @brief
* @param width
* @param height
*/
void setViewportSize(float width, float height);
/**
* @brief
* @param size
*/
void setViewportSize(const Size& size);
/**
* @brief
* @param size
*/
void setViewportSize(const Size &size);
/**
* @brief
* @return
*/
Size getViewportSize() const { return viewportSize_; }
/**
* @brief
* @return
*/
Size getViewportSize() const { return viewportSize_; }
/**
* @brief
* @return
*/
float getWidth() const { return viewportSize_.width; }
/**
* @brief
* @return
*/
float getWidth() const { return viewportSize_.width; }
/**
* @brief
* @return
*/
float getHeight() const { return viewportSize_.height; }
/**
* @brief
* @return
*/
float getHeight() const { return viewportSize_.height; }
// -------------------------------------------------------------------------
// 场景状态
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// 场景状态
// -------------------------------------------------------------------------
/**
* @brief
* @return true
*/
bool isPaused() const { return paused_; }
/**
* @brief
* @return true
*/
bool isPaused() const { return paused_; }
/**
* @brief
*/
void pause() { paused_ = true; }
/**
* @brief
*/
void pause() { paused_ = true; }
/**
* @brief
*/
void resume() { paused_ = false; }
/**
* @brief
*/
void resume() { paused_ = false; }
// -------------------------------------------------------------------------
// 渲染和更新
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// 渲染和更新
// -------------------------------------------------------------------------
/**
* @brief
* @param renderer
*/
void renderScene(RenderBackend& renderer);
/**
* @brief
* @param renderer
*/
void renderScene(RenderBackend &renderer);
/**
* @brief
* @param renderer
*/
virtual void renderContent(RenderBackend& renderer);
/**
* @brief
* @param renderer
*/
virtual void renderContent(RenderBackend &renderer);
/**
* @brief
* @param dt
*/
void updateScene(float dt);
/**
* @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 commands
* @param parentZOrder Z序
*/
void collectRenderCommands(std::vector<RenderCommand> &commands,
int parentZOrder = 0) override;
/**
* @brief
* @param queue
*/
void collectRenderCommandsToQueue(RenderQueue& queue) override;
/**
* @brief
* @param queue
*/
void collectRenderCommandsToQueue(RenderQueue &queue) override;
// -------------------------------------------------------------------------
// 静态创建方法
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// 静态创建方法
// -------------------------------------------------------------------------
/**
* @brief
* @return
*/
static Ptr<Scene> create();
/**
* @brief
* @return
*/
static Ptr<Scene> create();
protected:
/**
* @brief
*/
void onEnter() override;
/**
* @brief
*/
void onEnter() override;
/**
* @brief 退
*/
void onExit() override;
/**
* @brief 退
*/
void onExit() override;
/**
* @brief TransitionScene 使
*/
virtual void onExitTransitionDidStart() {}
/**
* @brief TransitionScene 使
*/
virtual void onExitTransitionDidStart() {}
/**
* @brief TransitionScene 使
*/
virtual void onEnterTransitionDidFinish() {}
/**
* @brief TransitionScene 使
*/
virtual void onEnterTransitionDidFinish() {}
friend class SceneManager;
friend class TransitionScene;
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

@ -2,16 +2,12 @@
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/render/core/render_command.h>
#include <extra2d/scene/node.h>
#include <vector>
namespace extra2d {
// ============================================================================
// 形状类型
// ============================================================================
enum class ShapeType { Point, Line, Rect, Circle, Triangle, Polygon };
// ============================================================================
// 形状节点 - 用于绘制几何形状
// ============================================================================

View File

@ -1,6 +1,6 @@
#pragma once
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/resources/texture.h>
#include <extra2d/scene/node.h>
namespace extra2d {

View File

@ -2,8 +2,8 @@
#include <extra2d/core/service_interface.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/camera/viewport_adapter.h>
#include <extra2d/render/camera/camera.h>
#include <extra2d/render/camera/viewport_adapter.h>
#include <functional>
namespace extra2d {

View File

@ -1,19 +1,19 @@
#include <extra2d/app/application.h>
#include <extra2d/core/registry.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/platform/iinput.h>
#include <extra2d/platform/input_module.h>
#include <extra2d/platform/iwindow.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/render/camera/viewport_adapter.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/render/core/render_module.h>
#include <extra2d/render/renderer.h>
#include <extra2d/services/camera_service.h>
#include <extra2d/services/event_service.h>
#include <extra2d/services/logger_service.h>
#include <extra2d/services/scene_service.h>
#include <extra2d/services/timer_service.h>
namespace extra2d {
static double getTimeSeconds() {
@ -81,9 +81,9 @@ void Application::configureCameraService() {
static_cast<float>(win->height()), 0);
ViewportConfig vpConfig;
vpConfig.logicWidth = static_cast<float>(win->width());
vpConfig.logicHeight = static_cast<float>(win->height());
vpConfig.mode = ViewportMode::AspectRatio;
vpConfig.designWidth = static_cast<float>(win->width());
vpConfig.designHeight = static_cast<float>(win->height());
vpConfig.scaleMode = ViewportScaleMode::Letterbox;
cameraService->setViewportConfig(vpConfig);
cameraService->updateViewport(win->width(), win->height());
@ -97,8 +97,6 @@ void Application::shutdown() {
if (!initialized_)
return;
VRAMMgr::get().printStats();
ServiceLocator::instance().shutdownAll();
ServiceLocator::instance().clear();
Registry::instance().shutdown();
@ -176,8 +174,7 @@ void Application::mainLoop() {
// 帧率限制
auto *renderMod = get<RenderModule>();
if (renderMod && renderMod->renderer()) {
// 这里可以添加帧率限制逻辑
if (renderMod && renderMod->getRenderer()) {
}
}
@ -192,29 +189,28 @@ void Application::update() {
void Application::render() {
auto *renderMod = get<RenderModule>();
if (!renderMod || !renderMod->renderer())
if (!renderMod || !renderMod->getRenderer())
return;
auto *renderer = renderMod->renderer();
auto *renderer = renderMod->getRenderer();
auto *winMod = get<WindowModule>();
if (!winMod || !winMod->win())
return;
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
if (cameraService) {
const auto &vp = cameraService->getViewportResult().viewport;
const auto &vpResult = cameraService->getViewportResult();
renderer->setViewport(
static_cast<int>(vp.origin.x), static_cast<int>(vp.origin.y),
static_cast<int>(vp.size.width), static_cast<int>(vp.size.height));
renderer->setViewProjection(cameraService->getViewProjectionMatrix());
vpResult.offsetX, vpResult.offsetY,
static_cast<int>(vpResult.width), static_cast<int>(vpResult.height));
} else {
renderer->setViewport(0, 0, winMod->win()->width(),
winMod->win()->height());
}
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
if (sceneService) {
sceneService->render(*renderer);
if (sceneService && renderer->getBackend()) {
sceneService->render(*renderer->getBackend());
}
winMod->win()->swap();
@ -227,7 +223,7 @@ IWindow *Application::window() {
RenderBackend *Application::renderer() {
auto *renderMod = get<RenderModule>();
return renderMod ? renderMod->renderer() : nullptr;
return renderMod ? (renderMod->getRenderer() ? renderMod->getRenderer()->getBackend() : nullptr) : nullptr;
}
IInput *Application::input() {

View File

@ -0,0 +1,128 @@
#include <extra2d/render/backends/opengl/gl_context.h>
#include <extra2d/services/logger_service.h>
#include <sstream>
namespace extra2d {
GLContext& GLContext::get() {
static GLContext instance;
return instance;
}
bool GLContext::init() {
if (initialized_) {
return true;
}
if (!gladLoadGL()) {
E2D_LOG_ERROR("Failed to load OpenGL functions");
return false;
}
parseVersion();
queryCapabilities();
initialized_ = true;
E2D_LOG_INFO("OpenGL Context initialized");
E2D_LOG_INFO(" Version: {}.{}", version_.major, version_.minor);
E2D_LOG_INFO(" Renderer: {}", renderer_);
E2D_LOG_INFO(" Vendor: {}", vendor_);
E2D_LOG_INFO(" GLSL: {}", glslVersion_);
return true;
}
void GLContext::shutdown() {
if (!initialized_) {
return;
}
extensions_.clear();
initialized_ = false;
E2D_LOG_INFO("OpenGL Context shutdown");
}
std::string GLContext::getVersionString() const {
std::ostringstream oss;
oss << version_.major << "." << version_.minor;
return oss.str();
}
bool GLContext::hasExtension(const std::string& extension) const {
return extensions_.find(extension) != extensions_.end();
}
bool GLContext::checkError(const char* operation) {
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
if (operation) {
E2D_LOG_ERROR("OpenGL error during {}: 0x{:X}", operation, error);
} else {
E2D_LOG_ERROR("OpenGL error: 0x{:X}", error);
}
return false;
}
return true;
}
void GLContext::clearErrors() {
while (glGetError() != GL_NO_ERROR) {
}
}
void GLContext::queryCapabilities() {
const char* rendererStr = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
const char* vendorStr = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
const char* glslStr = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
renderer_ = rendererStr ? rendererStr : "Unknown";
vendor_ = vendorStr ? vendorStr : "Unknown";
glslVersion_ = glslStr ? glslStr : "Unknown";
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits_);
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs_);
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings_);
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) {
extensions_.insert(ext);
}
}
}
void GLContext::parseVersion() {
const char* versionStr = reinterpret_cast<const char*>(glGetString(GL_VERSION));
if (!versionStr) {
version_ = {4, 5};
return;
}
int major = 0, minor = 0;
const char* ptr = versionStr;
while (*ptr && (*ptr < '0' || *ptr > '9')) {
ptr++;
}
if (*ptr) {
major = std::atoi(ptr);
while (*ptr && *ptr != '.') {
ptr++;
}
if (*ptr == '.') {
ptr++;
minor = std::atoi(ptr);
}
}
version_ = {major, minor};
}
} // namespace extra2d

View File

@ -0,0 +1,521 @@
#include <extra2d/render/backends/opengl/gl_render_backend.h>
#include <extra2d/platform/iwindow.h>
#include <glm/gtc/matrix_transform.hpp>
#include <cmath>
namespace extra2d {
/**
* @brief
*/
GLRenderBackend::GLRenderBackend()
: initialized_(false)
, window_(nullptr)
, config_()
, stats_()
, device_(nullptr)
, projectionMatrix_(1.0f)
, viewMatrix_(1.0f)
, width_(0)
, height_(0)
, sortKey_(0)
, layer_(0)
, blendState_(rhi::BlendState::alphaBlend())
, inBatch_(false) {
}
/**
* @brief
*/
GLRenderBackend::~GLRenderBackend() {
shutdown();
}
// ============================================================================
// 生命周期
// ============================================================================
/**
* @brief
*/
bool GLRenderBackend::init(IWindow *window, const RenderBackendConfig &config) {
if (initialized_) {
return true;
}
if (!window) {
return false;
}
window_ = window;
config_ = config;
width_ = window->width();
height_ = window->height();
device_ = rhi::RHIDevice::create(rhi::GraphicsAPI::OpenGL);
if (!device_) {
return false;
}
if (!device_->init(window)) {
device_.reset();
return false;
}
projectionMatrix_ = glm::ortho(0.0f, static_cast<float>(width_),
static_cast<float>(height_), 0.0f,
-1.0f, 1.0f);
viewMatrix_ = glm::mat4(1.0f);
initialized_ = true;
return true;
}
/**
* @brief
*/
void GLRenderBackend::shutdown() {
if (!initialized_) {
return;
}
if (device_) {
device_->shutdown();
device_.reset();
}
window_ = nullptr;
width_ = 0;
height_ = 0;
initialized_ = false;
}
/**
* @brief
*/
bool GLRenderBackend::isValid() const {
return initialized_ && device_ && device_->isValid();
}
// ============================================================================
// 帧管理
// ============================================================================
/**
* @brief
*/
void GLRenderBackend::beginFrame(const Color &clearColor) {
if (!isValid()) {
return;
}
stats_.reset();
device_->beginFrame();
device_->resetStats();
rhi::ColorValue cv(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
device_->clearColor(cv);
device_->setViewport(rhi::Viewport(0.0f, 0.0f,
static_cast<float>(width_),
static_cast<float>(height_)));
}
/**
* @brief
*/
void GLRenderBackend::endFrame() {
if (!isValid()) {
return;
}
if (inBatch_) {
endSpriteBatch();
}
device_->endFrame();
}
/**
* @brief
*/
void GLRenderBackend::present() {
if (!isValid()) {
return;
}
device_->present();
}
// ============================================================================
// 渲染状态
// ============================================================================
/**
* @brief
*/
void GLRenderBackend::setViewport(int x, int y, int width, int height) {
if (!isValid()) {
return;
}
device_->setViewport(rhi::Viewport(
static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(width),
static_cast<float>(height)));
}
/**
* @brief
*/
void GLRenderBackend::setScissor(int x, int y, int width, int height) {
if (!isValid()) {
return;
}
device_->setScissorRect(rhi::ScissorRect(x, y,
static_cast<uint32_t>(width),
static_cast<uint32_t>(height)));
}
/**
* @brief /
*/
void GLRenderBackend::setScissorEnabled(bool enabled) {
if (!isValid()) {
return;
}
device_->setScissorEnabled(enabled);
}
/**
* @brief
*/
void GLRenderBackend::setProjectionMatrix(const glm::mat4 &matrix) {
projectionMatrix_ = matrix;
}
/**
* @brief
*/
void GLRenderBackend::setViewMatrix(const glm::mat4 &matrix) {
viewMatrix_ = matrix;
}
/**
* @brief
*/
void GLRenderBackend::setVSync(bool enabled) {
if (!isValid()) {
return;
}
device_->setVSync(enabled);
}
/**
* @brief
*/
bool GLRenderBackend::isVSyncEnabled() const {
return isValid() ? device_->isVSyncEnabled() : false;
}
// ============================================================================
// 精灵渲染
// ============================================================================
/**
* @brief
*/
void GLRenderBackend::drawSprite(Ptr<rhi::RHITexture> texture, const Rect &destRect,
const Rect &srcRect, const Color &color,
float rotation, bool flipX, bool flipY) {
if (!isValid() || !texture) {
return;
}
stats_.spriteCount++;
stats_.drawCalls++;
}
/**
* @brief 使
*/
void GLRenderBackend::drawSprite(Ptr<rhi::RHITexture> texture,
const glm::mat4 &transform,
const Rect &srcRect, const Color &color) {
if (!isValid() || !texture) {
return;
}
stats_.spriteCount++;
stats_.drawCalls++;
}
/**
* @brief
*/
void GLRenderBackend::draw9Slice(Ptr<rhi::RHITexture> texture, const Rect &destRect,
const Rect &srcRect, const Vec2 &borderSize,
const Color &color) {
if (!isValid() || !texture) {
return;
}
stats_.spriteCount += 9;
stats_.drawCalls++;
}
// ============================================================================
// 文本渲染
// ============================================================================
/**
* @brief
*/
void GLRenderBackend::drawText(const std::string &text, const Vec2 &position,
float fontSize, const Color &color) {
if (!isValid() || text.empty()) {
return;
}
stats_.textCharCount += static_cast<uint32_t>(text.length());
stats_.drawCalls++;
}
/**
* @brief UTF-32
*/
void GLRenderBackend::drawText(const std::u32string &text, const Vec2 &position,
float fontSize, const Color &color) {
if (!isValid() || text.empty()) {
return;
}
stats_.textCharCount += static_cast<uint32_t>(text.length());
stats_.drawCalls++;
}
/**
* @brief
*/
Vec2 GLRenderBackend::measureText(const std::string &text, float fontSize) {
float width = static_cast<float>(text.length()) * fontSize * 0.6f;
return Vec2(width, fontSize);
}
/**
* @brief UTF-32
*/
Vec2 GLRenderBackend::measureText(const std::u32string &text, float fontSize) {
float width = static_cast<float>(text.length()) * fontSize * 0.6f;
return Vec2(width, fontSize);
}
// ============================================================================
// 形状渲染
// ============================================================================
/**
* @brief
*/
void GLRenderBackend::drawRect(const Rect &rect, const Color &color, float lineWidth) {
if (!isValid()) {
return;
}
stats_.shapeCount++;
stats_.drawCalls++;
}
/**
* @brief
*/
void GLRenderBackend::fillRect(const Rect &rect, const Color &color) {
if (!isValid()) {
return;
}
stats_.shapeCount++;
stats_.drawCalls++;
}
/**
* @brief
*/
void GLRenderBackend::drawCircle(const Vec2 &center, float radius, const Color &color,
float lineWidth, uint32_t segments) {
if (!isValid()) {
return;
}
stats_.shapeCount++;
stats_.drawCalls++;
}
/**
* @brief
*/
void GLRenderBackend::fillCircle(const Vec2 &center, float radius, const Color &color,
uint32_t segments) {
if (!isValid()) {
return;
}
stats_.shapeCount++;
stats_.drawCalls++;
}
/**
* @brief 线
*/
void GLRenderBackend::drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
float lineWidth) {
if (!isValid()) {
return;
}
stats_.shapeCount++;
stats_.drawCalls++;
}
/**
* @brief
*/
void GLRenderBackend::drawPolygon(const Vec2 *points, size_t count, const Color &color,
float lineWidth) {
if (!isValid() || !points || count < 3) {
return;
}
stats_.shapeCount++;
stats_.drawCalls++;
}
/**
* @brief
*/
void GLRenderBackend::fillPolygon(const Vec2 *points, size_t count, const Color &color) {
if (!isValid() || !points || count < 3) {
return;
}
stats_.shapeCount++;
stats_.drawCalls++;
}
// ============================================================================
// 批处理控制
// ============================================================================
/**
* @brief
*/
void GLRenderBackend::beginSpriteBatch() {
inBatch_ = true;
}
/**
* @brief
*/
void GLRenderBackend::endSpriteBatch() {
if (!inBatch_) {
return;
}
flush();
inBatch_ = false;
}
/**
* @brief
*/
void GLRenderBackend::flush() {
if (!isValid()) {
return;
}
stats_.batchCount++;
}
/**
* @brief
*/
void GLRenderBackend::setSortKey(uint64_t key) {
sortKey_ = key;
}
/**
* @brief
*/
void GLRenderBackend::setLayer(int layer) {
layer_ = layer;
}
/**
* @brief
*/
void GLRenderBackend::setBlendMode(rhi::BlendState blend) {
blendState_ = blend;
}
// ============================================================================
// 访问器
// ============================================================================
/**
* @brief RHI
*/
rhi::RHIDevice *GLRenderBackend::getDevice() const {
return device_.get();
}
/**
* @brief
*/
int GLRenderBackend::getWidth() const {
return width_;
}
/**
* @brief
*/
int GLRenderBackend::getHeight() const {
return height_;
}
/**
* @brief
*/
const RenderStats &GLRenderBackend::getStats() const {
return stats_;
}
/**
* @brief
*/
void GLRenderBackend::resetStats() {
stats_.reset();
if (device_) {
device_->resetStats();
}
}
/**
* @brief API
*/
rhi::GraphicsAPI GLRenderBackend::getAPI() const {
return rhi::GraphicsAPI::OpenGL;
}
/**
* @brief
*/
const rhi::DeviceCaps &GLRenderBackend::getCaps() const {
static rhi::DeviceCaps empty;
return device_ ? device_->getCaps() : empty;
}
} // namespace extra2d

View File

@ -3,7 +3,7 @@
#include <extra2d/render/backends/opengl/gl_rhi_texture.h>
#include <extra2d/render/backends/opengl/gl_rhi_shader.h>
#include <extra2d/render/backends/opengl/gl_rhi_pipeline.h>
#include <extra2d/graphics/backends/opengl/gl_context.h>
#include <extra2d/render/backends/opengl/gl_context.h>
#include <extra2d/services/logger_service.h>
#include <extra2d/platform/iwindow.h>
#include <fstream>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,203 @@
#include <extra2d/render/camera/camera.h>
#include <extra2d/render/camera/viewport_adapter.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace extra2d {
Camera::Camera()
: position_(0.0f, 0.0f), rotation_(0.0f), zoom_(1.0f), left_(-640.0f),
right_(640.0f), bottom_(-360.0f), top_(360.0f), hasBounds_(false),
matricesDirty_(true), viewportAdapter_(nullptr) {
updateMatrices();
}
Camera::Camera(float left, float right, float bottom, float top)
: position_(0.0f, 0.0f), rotation_(0.0f), zoom_(1.0f), left_(left),
right_(right), bottom_(bottom), top_(top), hasBounds_(false),
matricesDirty_(true), viewportAdapter_(nullptr) {
updateMatrices();
}
void Camera::setViewport(float left, float right, float bottom, float top) {
left_ = left;
right_ = right;
bottom_ = bottom;
top_ = top;
matricesDirty_ = true;
}
void Camera::setViewportSize(float width, float height) {
float halfWidth = width / 2.0f;
float halfHeight = height / 2.0f;
left_ = -halfWidth;
right_ = halfWidth;
bottom_ = -halfHeight;
top_ = halfHeight;
matricesDirty_ = true;
}
Rect Camera::getViewport() const {
return Rect(left_, bottom_, right_ - left_, top_ - bottom_);
}
void Camera::setPos(const Vec2 &position) {
position_ = position;
applyBounds();
matricesDirty_ = true;
}
void Camera::setPos(float x, float y) { setPos(Vec2(x, y)); }
void Camera::move(const Vec2 &offset) {
position_ += offset;
applyBounds();
matricesDirty_ = true;
}
void Camera::move(float x, float y) { move(Vec2(x, y)); }
void Camera::setRotation(float degrees) {
rotation_ = degrees;
matricesDirty_ = true;
}
void Camera::rotate(float degrees) {
rotation_ += degrees;
matricesDirty_ = true;
}
void Camera::setZoom(float zoom) {
zoom_ = std::max(0.001f, zoom);
matricesDirty_ = true;
}
void Camera::zoomBy(float factor) {
zoom_ *= factor;
zoom_ = std::max(0.001f, zoom_);
matricesDirty_ = true;
}
void Camera::setBounds(const Rect &bounds) {
bounds_ = bounds;
hasBounds_ = true;
applyBounds();
matricesDirty_ = true;
}
void Camera::clearBounds() {
hasBounds_ = false;
matricesDirty_ = true;
}
glm::mat4 Camera::getViewMatrix() const {
if (matricesDirty_) {
const_cast<Camera *>(this)->updateMatrices();
}
return viewMatrix_;
}
glm::mat4 Camera::getProjectionMatrix() const {
if (matricesDirty_) {
const_cast<Camera *>(this)->updateMatrices();
}
return projectionMatrix_;
}
glm::mat4 Camera::getViewProjectionMatrix() const {
if (matricesDirty_) {
const_cast<Camera *>(this)->updateMatrices();
}
return viewProjectionMatrix_;
}
void Camera::updateMatrices() {
glm::mat4 transform = glm::mat4(1.0f);
transform =
glm::translate(transform, glm::vec3(position_.x, position_.y, 0.0f));
transform = glm::rotate(transform, rotation_ * DEG_TO_RAD,
glm::vec3(0.0f, 0.0f, 1.0f));
transform = glm::scale(transform, glm::vec3(zoom_, zoom_, 1.0f));
viewMatrix_ = glm::inverse(transform);
projectionMatrix_ = glm::ortho(left_, right_, bottom_, top_, -1.0f, 1.0f);
viewProjectionMatrix_ = projectionMatrix_ * viewMatrix_;
matricesDirty_ = false;
}
Vec2 Camera::screenToWorld(const Vec2 &screenPos) const {
if (matricesDirty_) {
const_cast<Camera *>(this)->updateMatrices();
}
glm::vec4 ndc(screenPos.x, screenPos.y, 0.0f, 1.0f);
glm::mat4 invVP = glm::inverse(viewProjectionMatrix_);
glm::vec4 world = invVP * ndc;
return Vec2(world.x, world.y);
}
Vec2 Camera::worldToScreen(const Vec2 &worldPos) const {
if (matricesDirty_) {
const_cast<Camera *>(this)->updateMatrices();
}
glm::vec4 world(worldPos.x, worldPos.y, 0.0f, 1.0f);
glm::vec4 screen = viewProjectionMatrix_ * world;
return Vec2(screen.x, screen.y);
}
void Camera::lookAt(const Vec2 &target) {
position_ = -target;
applyBounds();
matricesDirty_ = true;
}
void Camera::applyViewportAdapter() {
if (!viewportAdapter_) {
return;
}
const ViewportResult &result = viewportAdapter_->getResult();
float designWidth = viewportAdapter_->getDesignWidth();
float designHeight = viewportAdapter_->getDesignHeight();
float halfWidth = designWidth / 2.0f;
float halfHeight = designHeight / 2.0f;
setViewport(-halfWidth, halfWidth, -halfHeight, halfHeight);
setZoom(result.scale);
}
void Camera::applyBounds() {
if (!hasBounds_) {
return;
}
float viewWidth = (right_ - left_) / zoom_;
float viewHeight = (top_ - bottom_) / zoom_;
float minX = bounds_.left() + viewWidth / 2.0f;
float maxX = bounds_.right() - viewWidth / 2.0f;
float minY = bounds_.top() + viewHeight / 2.0f;
float maxY = bounds_.bottom() - viewHeight / 2.0f;
if (minX > maxX) {
position_.x = (bounds_.left() + bounds_.right()) / 2.0f;
} else {
position_.x = std::clamp(position_.x, minX, maxX);
}
if (minY > maxY) {
position_.y = (bounds_.top() + bounds_.bottom()) / 2.0f;
} else {
position_.y = std::clamp(position_.y, minY, maxY);
}
}
} // namespace extra2d

View File

@ -0,0 +1,168 @@
#include <extra2d/render/camera/viewport_adapter.h>
#include <algorithm>
#include <cmath>
namespace extra2d {
ViewportAdapter::ViewportAdapter() : config_(ViewportConfig::hd()) {}
ViewportAdapter::ViewportAdapter(const ViewportConfig& config) : config_(config) {}
void ViewportAdapter::setConfig(const ViewportConfig& config) {
config_ = config;
calculateViewport();
}
void ViewportAdapter::setDesignSize(float width, float height) {
config_.designWidth = width;
config_.designHeight = height;
calculateViewport();
}
void ViewportAdapter::setScaleMode(ViewportScaleMode mode) {
config_.scaleMode = mode;
calculateViewport();
}
void ViewportAdapter::setAlign(ViewportAlign align) {
config_.align = align;
calculateViewport();
}
void ViewportAdapter::update(int screenWidth, int screenHeight) {
result_.screenWidth = screenWidth;
result_.screenHeight = screenHeight;
calculateViewport();
}
float ViewportAdapter::getDesignAspectRatio() const {
if (config_.designHeight <= 0.0f) {
return 1.0f;
}
return config_.designWidth / config_.designHeight;
}
float ViewportAdapter::getScreenAspectRatio() const {
if (result_.screenHeight <= 0) {
return 1.0f;
}
return static_cast<float>(result_.screenWidth) / static_cast<float>(result_.screenHeight);
}
Vec2 ViewportAdapter::screenToDesign(const Vec2& screenPos) const {
float x = (screenPos.x - static_cast<float>(result_.offsetX)) / result_.scale;
float y = (screenPos.y - static_cast<float>(result_.offsetY)) / result_.scale;
return Vec2(x, y);
}
Vec2 ViewportAdapter::designToScreen(const Vec2& designPos) const {
float x = designPos.x * result_.scale + static_cast<float>(result_.offsetX);
float y = designPos.y * result_.scale + static_cast<float>(result_.offsetY);
return Vec2(x, y);
}
void ViewportAdapter::calculateViewport() {
if (result_.screenWidth <= 0 || result_.screenHeight <= 0) {
return;
}
result_.scale = calculateScale();
result_.width = config_.designWidth * result_.scale;
result_.height = config_.designHeight * result_.scale;
calculateOffset();
}
float ViewportAdapter::calculateScale() const {
float scaleX = static_cast<float>(result_.screenWidth) / config_.designWidth;
float scaleY = static_cast<float>(result_.screenHeight) / config_.designHeight;
float scale = 1.0f;
switch (config_.scaleMode) {
case ViewportScaleMode::None:
scale = 1.0f;
break;
case ViewportScaleMode::Letterbox:
scale = std::min(scaleX, scaleY);
break;
case ViewportScaleMode::Crop:
scale = std::max(scaleX, scaleY);
break;
case ViewportScaleMode::Stretch:
scale = 1.0f;
break;
case ViewportScaleMode::IntegerScale:
scale = std::min(scaleX, scaleY);
scale = std::floor(scale);
if (scale < 1.0f) {
scale = 1.0f;
}
break;
}
scale = std::clamp(scale, config_.minScale, config_.maxScale);
return scale;
}
void ViewportAdapter::calculateOffset() {
switch (config_.align) {
case ViewportAlign::TopLeft:
result_.offsetX = 0;
result_.offsetY = 0;
break;
case ViewportAlign::TopCenter:
result_.offsetX = (result_.screenWidth - static_cast<int>(result_.width)) / 2;
result_.offsetY = 0;
break;
case ViewportAlign::TopRight:
result_.offsetX = result_.screenWidth - static_cast<int>(result_.width);
result_.offsetY = 0;
break;
case ViewportAlign::CenterLeft:
result_.offsetX = 0;
result_.offsetY = (result_.screenHeight - static_cast<int>(result_.height)) / 2;
break;
case ViewportAlign::Center:
default:
result_.offsetX = (result_.screenWidth - static_cast<int>(result_.width)) / 2;
result_.offsetY = (result_.screenHeight - static_cast<int>(result_.height)) / 2;
break;
case ViewportAlign::CenterRight:
result_.offsetX = result_.screenWidth - static_cast<int>(result_.width);
result_.offsetY = (result_.screenHeight - static_cast<int>(result_.height)) / 2;
break;
case ViewportAlign::BottomLeft:
result_.offsetX = 0;
result_.offsetY = result_.screenHeight - static_cast<int>(result_.height);
break;
case ViewportAlign::BottomCenter:
result_.offsetX = (result_.screenWidth - static_cast<int>(result_.width)) / 2;
result_.offsetY = result_.screenHeight - static_cast<int>(result_.height);
break;
case ViewportAlign::BottomRight:
result_.offsetX = result_.screenWidth - static_cast<int>(result_.width);
result_.offsetY = result_.screenHeight - static_cast<int>(result_.height);
break;
}
result_.x = static_cast<float>(result_.offsetX);
result_.y = static_cast<float>(result_.offsetY);
}
} // namespace extra2d

View File

@ -0,0 +1,262 @@
#include <extra2d/render/core/backend_factory.h>
#include <extra2d/render/backends/opengl/gl_render_backend.h>
#include <algorithm>
namespace extra2d {
std::unordered_map<std::string, BackendFactory::BackendInfo>& BackendFactory::getRegistry() {
static std::unordered_map<std::string, BackendInfo> registry;
return registry;
}
std::mutex& BackendFactory::getMutex() {
static std::mutex mutex;
return mutex;
}
std::string& BackendFactory::getDefaultName() {
static std::string defaultName = "opengl";
return defaultName;
}
/**
* @brief
*/
void BackendFactory::reg(const std::string& name,
BackendFn backend,
const std::vector<std::string>& windowBackends,
rhi::GraphicsAPI api,
const std::string& description,
int priority) {
std::lock_guard<std::mutex> lock(getMutex());
BackendInfo info;
info.name = name;
info.createFn = backend;
info.compatibleWindowBackends = windowBackends;
info.api = api;
info.description = description;
info.priority = priority;
getRegistry()[name] = info;
}
/**
* @brief
*/
void BackendFactory::unreg(const std::string& name) {
std::lock_guard<std::mutex> lock(getMutex());
getRegistry().erase(name);
}
/**
* @brief
*/
UniquePtr<RenderBackend> BackendFactory::createBackend(const std::string& name) {
std::lock_guard<std::mutex> lock(getMutex());
auto& registry = getRegistry();
auto it = registry.find(name);
if (it != registry.end() && it->second.createFn) {
return it->second.createFn();
}
return nullptr;
}
/**
* @brief
*/
UniquePtr<RenderBackend> BackendFactory::createDefaultBackend() {
std::lock_guard<std::mutex> lock(getMutex());
auto& registry = getRegistry();
if (registry.empty()) {
return makeUnique<GLRenderBackend>();
}
std::string defaultName = getDefaultName();
auto it = registry.find(defaultName);
if (it != registry.end() && it->second.createFn) {
return it->second.createFn();
}
std::vector<BackendInfo*> sorted;
for (auto& pair : registry) {
sorted.push_back(&pair.second);
}
std::sort(sorted.begin(), sorted.end(),
[](const BackendInfo* a, const BackendInfo* b) {
return a->priority > b->priority;
});
if (!sorted.empty() && sorted.front()->createFn) {
return sorted.front()->createFn();
}
return makeUnique<GLRenderBackend>();
}
/**
* @brief
*/
UniquePtr<RenderBackend> BackendFactory::createBackendForWindow(const std::string& windowBackend) {
std::lock_guard<std::mutex> lock(getMutex());
auto& registry = getRegistry();
std::vector<BackendInfo*> compatible;
for (auto& pair : registry) {
const auto& windowBackends = pair.second.compatibleWindowBackends;
if (windowBackends.empty() ||
std::find(windowBackends.begin(), windowBackends.end(), windowBackend) != windowBackends.end()) {
compatible.push_back(&pair.second);
}
}
std::sort(compatible.begin(), compatible.end(),
[](const BackendInfo* a, const BackendInfo* b) {
return a->priority > b->priority;
});
if (!compatible.empty() && compatible.front()->createFn) {
return compatible.front()->createFn();
}
return makeUnique<GLRenderBackend>();
}
/**
* @brief API
*/
UniquePtr<RenderBackend> BackendFactory::createBackendForAPI(rhi::GraphicsAPI api) {
std::lock_guard<std::mutex> lock(getMutex());
auto& registry = getRegistry();
std::vector<BackendInfo*> matching;
for (auto& pair : registry) {
if (pair.second.api == api) {
matching.push_back(&pair.second);
}
}
std::sort(matching.begin(), matching.end(),
[](const BackendInfo* a, const BackendInfo* b) {
return a->priority > b->priority;
});
if (!matching.empty() && matching.front()->createFn) {
return matching.front()->createFn();
}
if (api == rhi::GraphicsAPI::OpenGL) {
return makeUnique<GLRenderBackend>();
}
return nullptr;
}
/**
* @brief
*/
bool BackendFactory::hasBackend(const std::string& name) {
std::lock_guard<std::mutex> lock(getMutex());
return getRegistry().find(name) != getRegistry().end();
}
/**
* @brief
*/
std::vector<std::string> BackendFactory::getBackendNames() {
std::lock_guard<std::mutex> lock(getMutex());
std::vector<std::string> names;
for (const auto& pair : getRegistry()) {
names.push_back(pair.first);
}
return names;
}
/**
* @brief
*/
const BackendFactory::BackendInfo* BackendFactory::getBackendInfo(const std::string& name) {
std::lock_guard<std::mutex> lock(getMutex());
auto& registry = getRegistry();
auto it = registry.find(name);
if (it != registry.end()) {
return &it->second;
}
return nullptr;
}
/**
* @brief
*/
std::vector<BackendFactory::BackendInfo> BackendFactory::getAllBackendInfos() {
std::lock_guard<std::mutex> lock(getMutex());
std::vector<BackendInfo> infos;
for (const auto& pair : getRegistry()) {
infos.push_back(pair.second);
}
return infos;
}
/**
* @brief
*/
std::vector<std::string> BackendFactory::getCompatibleBackends(const std::string& windowBackend) {
std::lock_guard<std::mutex> lock(getMutex());
std::vector<std::string> names;
for (const auto& pair : getRegistry()) {
const auto& windowBackends = pair.second.compatibleWindowBackends;
if (windowBackends.empty() ||
std::find(windowBackends.begin(), windowBackends.end(), windowBackend) != windowBackends.end()) {
names.push_back(pair.first);
}
}
return names;
}
/**
* @brief
*/
void BackendFactory::clear() {
std::lock_guard<std::mutex> lock(getMutex());
getRegistry().clear();
}
/**
* @brief
*/
std::string BackendFactory::getDefaultBackendName() {
return getDefaultName();
}
/**
* @brief
*/
void BackendFactory::setDefaultBackendName(const std::string& name) {
getDefaultName() = name;
}
namespace {
static BackendRegistrar s_openglBackendReg(
"opengl",
[]() -> UniquePtr<RenderBackend> {
return makeUnique<GLRenderBackend>();
},
{"sdl2", "glfw"},
rhi::GraphicsAPI::OpenGL,
"OpenGL 4.5 Core Profile",
100
);
}
} // namespace extra2d

View File

@ -245,7 +245,7 @@ void RenderContext::flushQueue() {
const RenderQueueStats& queueStats = queue_.getStats();
stats_.spriteCount = queueStats.spriteCommands;
stats_.textCount = queueStats.textCommands;
stats_.textCharCount = queueStats.textCommands;
stats_.shapeCount = queueStats.shapeCommands;
stats_.batchCount = queueStats.batchCount;

View File

@ -0,0 +1,81 @@
#include <extra2d/platform/window_module.h>
#include <extra2d/render/core/render_module.h>
#include <extra2d/render/renderer.h>
#include <extra2d/app/application.h>
#include <extra2d/platform/iwindow.h>
#include <typeindex>
namespace extra2d {
RenderModule::RenderModule() : initialized_(false) {}
RenderModule::RenderModule(const RenderModuleConfig &config)
: config_(config), initialized_(false) {}
RenderModule::~RenderModule() { shutdown(); }
bool RenderModule::init() {
if (initialized_) {
return true;
}
if (!app_) {
return false;
}
IWindow *window = nullptr;
renderer_ = Renderer::create();
if (!renderer_) {
return false;
}
RendererConfig rendererConfig;
rendererConfig.api = config_.api;
rendererConfig.vsync = config_.vsync;
rendererConfig.maxBatchSize = config_.maxBatchSize;
rendererConfig.enableDebug = config_.enableDebug;
rendererConfig.viewportConfig =
ViewportConfig(config_.designWidth, config_.designHeight);
renderer_->setConfig(rendererConfig);
initialized_ = true;
return true;
}
void RenderModule::shutdown() {
if (!initialized_) {
return;
}
renderer_.reset();
initialized_ = false;
}
std::vector<std::type_index> RenderModule::deps() const {
return {std::type_index(typeid(class WindowModule))};
}
void RenderModule::setConfig(const RenderModuleConfig &config) {
config_ = config;
if (renderer_ && initialized_) {
RendererConfig rendererConfig;
rendererConfig.api = config_.api;
rendererConfig.vsync = config_.vsync;
rendererConfig.maxBatchSize = config_.maxBatchSize;
rendererConfig.enableDebug = config_.enableDebug;
rendererConfig.viewportConfig =
ViewportConfig(config_.designWidth, config_.designHeight);
renderer_->setConfig(rendererConfig);
}
}
Ptr<RenderModule> RenderModule::create(const RenderModuleConfig &config) {
return makePtr<RenderModule>(config);
}
} // namespace extra2d

View File

@ -0,0 +1,349 @@
#include <extra2d/render/renderer.h>
#include <extra2d/platform/iwindow.h>
#include <extra2d/render/core/backend_factory.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/scene/scene.h>
namespace extra2d {
Renderer::Renderer()
: window_(nullptr), width_(0), height_(0), initialized_(false) {}
Renderer::Renderer(const RendererConfig &config)
: window_(nullptr), config_(config), width_(0), height_(0),
initialized_(false) {}
Renderer::~Renderer() { shutdown(); }
bool Renderer::init() { return initialized_; }
bool Renderer::init(IWindow *window) { return init(window, config_); }
bool Renderer::init(IWindow *window, const RendererConfig &config) {
if (initialized_) {
return true;
}
if (!window) {
return false;
}
window_ = window;
config_ = config;
width_ = window->width();
height_ = window->height();
viewportAdapter_.setConfig(config.viewportConfig);
viewportAdapter_.update(width_, height_);
RenderBackendConfig backendConfig;
backendConfig.api = config.api;
backendConfig.vsync = config.vsync;
backendConfig.maxBatchSize = config.maxBatchSize;
backendConfig.enableDebug = config.enableDebug;
backend_ = BackendFactory::createBackendForAPI(config.api);
if (!backend_ || !backend_->init(window, backendConfig)) {
return false;
}
RenderContextConfig contextConfig;
contextConfig.maxBatchSize = config.maxBatchSize;
contextConfig.preferredAPI = config.api;
context_ = makeUnique<RenderContext>(contextConfig);
if (!context_->init(window, config.api)) {
return false;
}
if (!createBatchers()) {
return false;
}
createDefaultCamera();
initialized_ = true;
return true;
}
void Renderer::shutdown() {
if (!initialized_) {
return;
}
textBatcher_.reset();
shapeBatcher_.reset();
spriteBatcher_.reset();
context_.reset();
backend_.reset();
activeCamera_.reset();
defaultCamera_.reset();
window_ = nullptr;
width_ = 0;
height_ = 0;
initialized_ = false;
}
void Renderer::beginFrame(const Color &clearColor) {
if (!initialized_) {
return;
}
if (backend_) {
backend_->beginFrame(clearColor);
}
if (context_) {
context_->beginFrame();
context_->clear(clearColor);
}
}
void Renderer::endFrame() {
if (!initialized_) {
return;
}
if (spriteBatcher_) {
spriteBatcher_->end();
}
if (shapeBatcher_) {
shapeBatcher_->end();
}
if (textBatcher_) {
textBatcher_->end();
}
if (context_) {
context_->endFrame();
}
if (backend_) {
backend_->endFrame();
}
}
void Renderer::present() {
if (!initialized_) {
return;
}
if (backend_) {
backend_->present();
}
if (context_) {
context_->present();
}
}
void Renderer::renderScene(Scene *scene) {
if (!initialized_ || !scene) {
return;
}
Camera *camera = activeCamera_ ? activeCamera_.get() : defaultCamera_.get();
if (!camera) {
camera = scene->getActiveCamera();
}
renderScene(scene, camera);
}
void Renderer::renderScene(Scene *scene, Camera *camera) {
if (!initialized_ || !scene) {
return;
}
if (!camera) {
camera = defaultCamera_.get();
}
if (camera) {
camera->setViewportAdapter(&viewportAdapter_);
camera->applyViewportAdapter();
}
applyCameraTransform(camera);
scene->renderScene(*backend_);
}
void Renderer::setActiveCamera(Ptr<Camera> camera) { activeCamera_ = camera; }
Ptr<Camera> Renderer::getActiveCamera() const {
return activeCamera_ ? activeCamera_ : defaultCamera_;
}
void Renderer::setViewport(int x, int y, int width, int height) {
if (context_) {
context_->setViewport(x, y, width, height);
}
if (backend_) {
backend_->setViewport(x, y, width, height);
}
}
void Renderer::setScissor(int x, int y, int width, int height) {
if (context_) {
context_->setScissorRect(x, y, width, height);
}
if (backend_) {
backend_->setScissor(x, y, width, height);
}
}
void Renderer::setScissorEnabled(bool enabled) {
if (context_) {
context_->setScissorEnabled(enabled);
}
if (backend_) {
backend_->setScissorEnabled(enabled);
}
}
void Renderer::setVSync(bool enabled) {
if (context_) {
context_->setVSync(enabled);
}
if (backend_) {
backend_->setVSync(enabled);
}
}
bool Renderer::isVSyncEnabled() const {
if (backend_) {
return backend_->isVSyncEnabled();
}
if (context_) {
return context_->isVSyncEnabled();
}
return false;
}
rhi::RHIDevice *Renderer::getDevice() const {
if (backend_) {
return backend_->getDevice();
}
if (context_) {
return context_->getDevice().get();
}
return nullptr;
}
const RenderStats &Renderer::getStats() const {
if (backend_) {
return backend_->getStats();
}
if (context_) {
return context_->getStats();
}
static RenderStats empty;
return empty;
}
void Renderer::resetStats() {
if (backend_) {
backend_->resetStats();
}
if (context_) {
context_->resetStats();
}
if (spriteBatcher_) {
spriteBatcher_->resetStats();
}
if (shapeBatcher_) {
shapeBatcher_->resetStats();
}
if (textBatcher_) {
textBatcher_->resetStats();
}
}
void Renderer::setConfig(const RendererConfig &config) {
config_ = config;
viewportAdapter_.setConfig(config.viewportConfig);
updateViewportAdapter();
}
bool Renderer::createBatchers() {
auto device = getDevice();
if (!device) {
return false;
}
auto devicePtr = context_ ? context_->getDevice() : nullptr;
if (!devicePtr) {
return false;
}
SpriteBatcherConfig spriteConfig;
spriteConfig.maxBatchSize = config_.maxBatchSize;
spriteBatcher_ = makeUnique<SpriteBatcher>(spriteConfig);
if (!spriteBatcher_->init(devicePtr)) {
return false;
}
ShapeBatcherConfig shapeConfig;
shapeConfig.maxBatchSize = config_.maxBatchSize / 2;
shapeBatcher_ = makeUnique<ShapeBatcher>(shapeConfig);
if (!shapeBatcher_->init(devicePtr)) {
return false;
}
TextBatcherConfig textConfig;
textConfig.maxBatchSize = config_.maxBatchSize / 2;
textBatcher_ = makeUnique<TextBatcher>(textConfig);
if (!textBatcher_->init(devicePtr)) {
return false;
}
return true;
}
void Renderer::createDefaultCamera() {
float designWidth = viewportAdapter_.getDesignWidth();
float designHeight = viewportAdapter_.getDesignHeight();
defaultCamera_ = makePtr<Camera>();
defaultCamera_->setViewport(0, designWidth, 0, designHeight);
defaultCamera_->setViewportAdapter(&viewportAdapter_);
defaultCamera_->setDebugName("DefaultCamera");
}
void Renderer::updateViewportAdapter() {
viewportAdapter_.update(width_, height_);
}
void Renderer::applyCameraTransform(Camera *camera) {
if (!camera) {
return;
}
camera->updateMatrices();
glm::mat4 viewProj = camera->getViewProjectionMatrix();
if (spriteBatcher_) {
spriteBatcher_->setViewProjection(viewProj);
}
if (shapeBatcher_) {
shapeBatcher_->setViewProjection(viewProj);
}
if (textBatcher_) {
textBatcher_->setViewProjection(viewProj);
}
if (backend_) {
backend_->setProjectionMatrix(camera->getProjectionMatrix());
backend_->setViewMatrix(camera->getViewMatrix());
}
}
Ptr<Renderer> Renderer::create(const RendererConfig &config) {
return makePtr<Renderer>(config);
}
} // namespace extra2d

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/render/core/render_command.h>
#include <extra2d/render/core/render_queue.h>
#include <extra2d/scene/scene.h>

View File

@ -1,8 +1,8 @@
#include <algorithm>
#include <extra2d/app/application.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/core/render_command.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/render/core/render_command.h>
#include <extra2d/platform/iinput.h>
#include <extra2d/scene/scene_manager.h>
#include <extra2d/scene/transition_box_scene.h>

View File

@ -1,7 +1,7 @@
#include <algorithm>
#include <cmath>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/core/render_command.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/render/core/render_command.h>
#include <extra2d/scene/shape_node.h>
#include <limits>
@ -196,24 +196,18 @@ Ptr<ShapeNode> ShapeNode::createFilledPolygon(const std::vector<Vec2> &points,
* @brief
* @param points
*/
void ShapeNode::setPoints(const std::vector<Vec2> &points) {
points_ = points;
}
void ShapeNode::setPoints(const std::vector<Vec2> &points) { points_ = points; }
/**
* @brief
* @param point
*/
void ShapeNode::addPoint(const Vec2 &point) {
points_.push_back(point);
}
void ShapeNode::addPoint(const Vec2 &point) { points_.push_back(point); }
/**
* @brief
*/
void ShapeNode::clearPoints() {
points_.clear();
}
void ShapeNode::clearPoints() { points_.clear(); }
/**
* @brief
@ -319,7 +313,7 @@ void ShapeNode::onDraw(RenderBackend &renderer) {
case ShapeType::Triangle:
if (points_.size() >= 3) {
if (filled_) {
renderer.fillTriangle(points_[0], points_[1], points_[2], color_);
renderer.fillPolygon(points_.data(), 3, color_);
} else {
renderer.drawLine(points_[0], points_[1], color_, lineWidth_);
renderer.drawLine(points_[1], points_[2], color_, lineWidth_);
@ -331,9 +325,9 @@ void ShapeNode::onDraw(RenderBackend &renderer) {
case ShapeType::Polygon:
if (!points_.empty()) {
if (filled_) {
renderer.fillPolygon(points_, color_);
renderer.fillPolygon(points_.data(), points_.size(), color_);
} else {
renderer.drawPolygon(points_, color_, lineWidth_);
renderer.drawPolygon(points_.data(), points_.size(), color_, lineWidth_);
}
}
break;
@ -354,71 +348,44 @@ void ShapeNode::generateRenderCommand(std::vector<RenderCommand> &commands,
}
Vec2 offset = getPosition();
RenderCommand cmd;
cmd.layer = zOrder;
switch (shapeType_) {
case ShapeType::Point:
if (!points_.empty()) {
cmd.type = RenderCommandType::FilledCircle;
cmd.data =
CircleCommandData{points_[0] + offset, lineWidth_ * 0.5f, color_, 8, 0.0f, true};
commands.push_back(RenderCommand::makeCircle(
points_[0] + offset, lineWidth_ * 0.5f, color_, 8, true, zOrder));
}
break;
case ShapeType::Line:
if (points_.size() >= 2) {
cmd.type = RenderCommandType::Line;
cmd.data = LineCommandData{points_[0] + offset, points_[1] + offset, color_,
lineWidth_};
commands.push_back(RenderCommand::makeLine(
points_[0] + offset, points_[1] + offset, color_, lineWidth_, zOrder));
}
break;
case ShapeType::Rect:
if (points_.size() >= 4) {
if (filled_) {
cmd.type = RenderCommandType::FilledRect;
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
points_[2].y - points_[0].y);
cmd.data =
RectCommandData{Rect(rect.origin + offset, rect.size), color_, 0.0f, true};
} else {
cmd.type = RenderCommandType::Rect;
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
points_[2].y - points_[0].y);
cmd.data =
RectCommandData{Rect(rect.origin + offset, rect.size), color_, lineWidth_, false};
}
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
points_[2].y - points_[0].y);
commands.push_back(RenderCommand::makeRect(
Rect(rect.origin + offset, rect.size), color_, lineWidth_, filled_, zOrder));
}
break;
case ShapeType::Circle:
if (points_.size() >= 2) {
float radius = points_[1].x;
if (filled_) {
cmd.type = RenderCommandType::FilledCircle;
cmd.data =
CircleCommandData{points_[0] + offset, radius, color_, segments_, 0.0f, true};
} else {
cmd.type = RenderCommandType::Circle;
cmd.data = CircleCommandData{points_[0] + offset, radius, color_, segments_,
lineWidth_, false};
}
commands.push_back(RenderCommand::makeCircle(
points_[0] + offset, radius, color_, segments_, filled_, zOrder));
}
break;
case ShapeType::Triangle:
if (points_.size() >= 3) {
Vec2 p1 = points_[0] + offset;
Vec2 p2 = points_[1] + offset;
Vec2 p3 = points_[2] + offset;
if (filled_) {
cmd.type = RenderCommandType::FilledTriangle;
cmd.data = TriangleCommandData{p1, p2, p3, color_, 0.0f, true};
} else {
cmd.type = RenderCommandType::Triangle;
cmd.data = TriangleCommandData{p1, p2, p3, color_, lineWidth_, false};
}
commands.push_back(RenderCommand::makeTriangle(
points_[0] + offset, points_[1] + offset, points_[2] + offset,
color_, filled_, zOrder));
}
break;
@ -429,19 +396,11 @@ void ShapeNode::generateRenderCommand(std::vector<RenderCommand> &commands,
for (const auto &p : points_) {
transformedPoints.push_back(p + offset);
}
if (filled_) {
cmd.type = RenderCommandType::FilledPolygon;
cmd.data = PolygonCommandData{transformedPoints, color_, 0.0f, true};
} else {
cmd.type = RenderCommandType::Polygon;
cmd.data = PolygonCommandData{transformedPoints, color_, lineWidth_, false};
}
commands.push_back(RenderCommand::makePolygon(
transformedPoints, color_, filled_, zOrder));
}
break;
}
commands.push_back(std::move(cmd));
}
} // namespace extra2d

View File

@ -1,8 +1,7 @@
#include <algorithm>
#include <cmath>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/core/render_command.h>
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/render/core/render_command.h>
#include <extra2d/scene/sprite.h>
namespace extra2d {
@ -168,8 +167,8 @@ void Sprite::onDraw(RenderBackend &renderer) {
// 从世界变换矩阵中提取旋转角度
float worldRotation = std::atan2(worldTransform[0][1], worldTransform[0][0]);
renderer.drawSprite(*texture_, destRect, srcRect, color_, worldRotation,
anchor);
renderer.drawSprite(texture_->getRHITexture(), destRect, srcRect, color_, worldRotation,
flipX_, flipY_);
}
/**
@ -225,8 +224,8 @@ void Sprite::generateRenderCommand(std::vector<RenderCommand> &commands,
RenderCommand cmd;
cmd.type = RenderCommandType::Sprite;
cmd.layer = zOrder;
cmd.data = SpriteCommandData{texture_.get(), destRect, srcRect, color_,
worldRotation, anchor, 0};
cmd.data = SpriteRenderData{texture_->getRHITexture(), destRect, srcRect, color_,
worldRotation, anchor, rhi::BlendState::alphaBlend(), 0};
commands.push_back(std::move(cmd));
}

View File

@ -1,7 +1,7 @@
#include <extra2d/scene/transition_box_scene.h>
#include <extra2d/app/application.h>
#include <extra2d/core/color.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/platform/iwindow.h>
#include <algorithm>
#include <glm/gtc/matrix_transform.hpp>
@ -76,7 +76,8 @@ void TransitionBoxScene::renderContent(RenderBackend &renderer) {
glm::mat4 overlayVP =
glm::ortho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f);
renderer.setViewProjection(overlayVP);
renderer.setProjectionMatrix(overlayVP);
renderer.setViewMatrix(glm::mat4(1.0f));
for (int idx = visible; idx < total; ++idx) {
int x = idx % div;

View File

@ -1,6 +1,6 @@
#include <extra2d/app/application.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/platform/iwindow.h>
#include <extra2d/scene/transition_fade_scene.h>
#include <extra2d/services/logger_service.h>
@ -75,7 +75,8 @@ void TransitionFadeScene::renderContent(RenderBackend &renderer) {
glm::mat4 overlayVP =
glm::ortho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f);
renderer.setViewProjection(overlayVP);
renderer.setProjectionMatrix(overlayVP);
renderer.setViewMatrix(glm::mat4(1.0f));
Color maskColor = maskColor_;
maskColor.a = maskAlpha;

View File

@ -1,7 +1,7 @@
#include <extra2d/scene/transition_flip_scene.h>
#include <extra2d/core/math_types.h>
#include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/render/camera/camera.h>
#include <extra2d/render/core/render_backend.h>
namespace extra2d {

View File

@ -1,6 +1,6 @@
#include <extra2d/scene/transition_scale_scene.h>
#include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/render/camera/camera.h>
#include <extra2d/render/core/render_backend.h>
#include <algorithm>
namespace extra2d {

View File

@ -1,5 +1,5 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/render/core/render_backend.h>
#include <extra2d/scene/transition_scene.h>
#include <extra2d/services/logger_service.h>

View File

@ -1,6 +1,6 @@
#include <extra2d/scene/transition_slide_scene.h>
#include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/render/camera/camera.h>
#include <extra2d/render/core/render_backend.h>
#include <algorithm>
namespace extra2d {

View File

@ -34,13 +34,13 @@ function define_extra2d_engine()
-- 渲染后端源文件
local render_backend = get_render_backend()
-- 图形后端工厂(始终编译)
add_files("Extra2D/src/graphics/backends/backend_factory.cpp")
-- 渲染后端工厂
add_files("Extra2D/src/render/core/backend_factory.cpp")
if render_backend == "vulkan" then
add_files("Extra2D/src/graphics/backends/vulkan/*.cpp")
add_files("Extra2D/src/render/backends/vulkan/*.cpp")
add_defines("E2D_BACKEND_VULKAN")
else
add_files("Extra2D/src/graphics/backends/opengl/*.cpp")
add_files("Extra2D/src/render/backends/opengl/*.cpp")
add_files("Extra2D/src/glad/glad.c")
add_defines("E2D_BACKEND_OPENGL")
end