refactor(logger): 替换日志系统为spdlog实现

重构日志系统,使用spdlog替代原有实现,提供更强大的格式化功能和性能优化
移除自定义日志格式化和级别处理代码,统一使用spdlog接口
更新所有日志调用点,移除E2D_LOG_前缀宏,简化为E2D_前缀
添加spdlog为第三方依赖,更新构建配置
This commit is contained in:
ChestnutYueyue 2026-03-03 21:23:54 +08:00
parent 8864637459
commit 579aa2dd0d
25 changed files with 1500 additions and 1433 deletions

View File

@ -1,7 +0,0 @@
{
"permissions": {
"allow": [
"Bash(git diff:*)"
]
}
}

View File

@ -1,7 +1,7 @@
#pragma once
#include <renderer/rhi/rhi.h>
#include <glad/glad.h>
#include <renderer/rhi/rhi.h>
#include <utils/logger.h>
namespace extra2d {
@ -9,299 +9,485 @@ namespace extra2d {
/**
* @brief OpenGL
*/
#define GL_CHECK(call) do { \
call; \
GLenum err = glGetError(); \
if (err != GL_NO_ERROR) { \
E2D_LOG_ERROR("OpenGL 错误在 {}: {} ({})", #call, getGLErrorString(err), err); \
} \
} while(0)
#define GL_CHECK(call) \
do { \
call; \
GLenum err = glGetError(); \
if (err != GL_NO_ERROR) { \
E2D_ERROR("OpenGL 错误在 {}: {} ({})", #call, getGLErrorString(err), \
err); \
} \
} while (0)
/**
* @brief OpenGL
* @param error OpenGL
* @return
*/
inline const char* getGLErrorString(GLenum error) {
switch (error) {
case GL_NO_ERROR: return "GL_NO_ERROR";
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW";
case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW";
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
default: return "Unknown Error";
}
inline const char *getGLErrorString(GLenum error) {
switch (error) {
case GL_NO_ERROR:
return "GL_NO_ERROR";
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
case GL_STACK_OVERFLOW:
return "GL_STACK_OVERFLOW";
case GL_STACK_UNDERFLOW:
return "GL_STACK_UNDERFLOW";
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
case GL_INVALID_FRAMEBUFFER_OPERATION:
return "GL_INVALID_FRAMEBUFFER_OPERATION";
default:
return "Unknown Error";
}
}
/**
* @brief BufferType OpenGL
*/
inline GLenum bufferTypeToGL(BufferType type) {
switch (type) {
case BufferType::Vertex: return GL_ARRAY_BUFFER;
case BufferType::Index: return GL_ELEMENT_ARRAY_BUFFER;
case BufferType::Uniform: return GL_UNIFORM_BUFFER;
case BufferType::Storage: return GL_SHADER_STORAGE_BUFFER;
case BufferType::Staging: return GL_ARRAY_BUFFER;
default: return GL_ARRAY_BUFFER;
}
switch (type) {
case BufferType::Vertex:
return GL_ARRAY_BUFFER;
case BufferType::Index:
return GL_ELEMENT_ARRAY_BUFFER;
case BufferType::Uniform:
return GL_UNIFORM_BUFFER;
case BufferType::Storage:
return GL_SHADER_STORAGE_BUFFER;
case BufferType::Staging:
return GL_ARRAY_BUFFER;
default:
return GL_ARRAY_BUFFER;
}
}
/**
* @brief BufferUsage OpenGL 使
*/
inline GLenum bufferUsageToGL(BufferUsage usage) {
switch (usage) {
case BufferUsage::Static: return GL_STATIC_DRAW;
case BufferUsage::Dynamic: return GL_DYNAMIC_DRAW;
case BufferUsage::Stream: return GL_STREAM_DRAW;
default: return GL_STATIC_DRAW;
}
switch (usage) {
case BufferUsage::Static:
return GL_STATIC_DRAW;
case BufferUsage::Dynamic:
return GL_DYNAMIC_DRAW;
case BufferUsage::Stream:
return GL_STREAM_DRAW;
default:
return GL_STATIC_DRAW;
}
}
/**
* @brief TextureFormat OpenGL
*/
inline GLenum textureFormatToGLInternal(TextureFormat format) {
switch (format) {
case TextureFormat::R8: return GL_R8;
case TextureFormat::RG8: return GL_RG8;
case TextureFormat::RGB8: return GL_RGB8;
case TextureFormat::RGBA8: return GL_RGBA8;
case TextureFormat::RGBA8_SRGB: return GL_SRGB8_ALPHA8;
case TextureFormat::Depth16: return GL_DEPTH_COMPONENT16;
case TextureFormat::Depth24: return GL_DEPTH_COMPONENT24;
case TextureFormat::Depth32F: return GL_DEPTH_COMPONENT32F;
case TextureFormat::Depth24Stencil8: return GL_DEPTH24_STENCIL8;
case TextureFormat::Depth32FStencil8:return GL_DEPTH32F_STENCIL8;
case TextureFormat::BC1: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
case TextureFormat::BC3: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case TextureFormat::BC5: return 0x8DBD; // GL_COMPRESSED_RG_RGTC2
default: return GL_RGBA8;
}
switch (format) {
case TextureFormat::R8:
return GL_R8;
case TextureFormat::RG8:
return GL_RG8;
case TextureFormat::RGB8:
return GL_RGB8;
case TextureFormat::RGBA8:
return GL_RGBA8;
case TextureFormat::RGBA8_SRGB:
return GL_SRGB8_ALPHA8;
case TextureFormat::Depth16:
return GL_DEPTH_COMPONENT16;
case TextureFormat::Depth24:
return GL_DEPTH_COMPONENT24;
case TextureFormat::Depth32F:
return GL_DEPTH_COMPONENT32F;
case TextureFormat::Depth24Stencil8:
return GL_DEPTH24_STENCIL8;
case TextureFormat::Depth32FStencil8:
return GL_DEPTH32F_STENCIL8;
case TextureFormat::BC1:
return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
case TextureFormat::BC3:
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case TextureFormat::BC5:
return 0x8DBD; // GL_COMPRESSED_RG_RGTC2
default:
return GL_RGBA8;
}
}
/**
* @brief TextureFormat OpenGL
*/
inline GLenum textureFormatToGLFormat(TextureFormat format) {
switch (format) {
case TextureFormat::R8: return GL_RED;
case TextureFormat::RG8: return GL_RG;
case TextureFormat::RGB8: return GL_RGB;
case TextureFormat::RGBA8:
case TextureFormat::RGBA8_SRGB: return GL_RGBA;
case TextureFormat::Depth16:
case TextureFormat::Depth24:
case TextureFormat::Depth32F: return GL_DEPTH_COMPONENT;
case TextureFormat::Depth24Stencil8:
case TextureFormat::Depth32FStencil8:return GL_DEPTH_STENCIL;
default: return GL_RGBA;
}
switch (format) {
case TextureFormat::R8:
return GL_RED;
case TextureFormat::RG8:
return GL_RG;
case TextureFormat::RGB8:
return GL_RGB;
case TextureFormat::RGBA8:
case TextureFormat::RGBA8_SRGB:
return GL_RGBA;
case TextureFormat::Depth16:
case TextureFormat::Depth24:
case TextureFormat::Depth32F:
return GL_DEPTH_COMPONENT;
case TextureFormat::Depth24Stencil8:
case TextureFormat::Depth32FStencil8:
return GL_DEPTH_STENCIL;
default:
return GL_RGBA;
}
}
/**
* @brief TextureFormat OpenGL
*/
inline GLenum textureFormatToGLType(TextureFormat format) {
switch (format) {
case TextureFormat::R8:
case TextureFormat::RG8:
case TextureFormat::RGB8:
case TextureFormat::RGBA8:
case TextureFormat::RGBA8_SRGB: return GL_UNSIGNED_BYTE;
case TextureFormat::Depth16: return GL_UNSIGNED_SHORT;
case TextureFormat::Depth24: return GL_UNSIGNED_INT;
case TextureFormat::Depth32F: return GL_FLOAT;
case TextureFormat::Depth24Stencil8: return GL_UNSIGNED_INT_24_8;
case TextureFormat::Depth32FStencil8:return GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
default: return GL_UNSIGNED_BYTE;
}
switch (format) {
case TextureFormat::R8:
case TextureFormat::RG8:
case TextureFormat::RGB8:
case TextureFormat::RGBA8:
case TextureFormat::RGBA8_SRGB:
return GL_UNSIGNED_BYTE;
case TextureFormat::Depth16:
return GL_UNSIGNED_SHORT;
case TextureFormat::Depth24:
return GL_UNSIGNED_INT;
case TextureFormat::Depth32F:
return GL_FLOAT;
case TextureFormat::Depth24Stencil8:
return GL_UNSIGNED_INT_24_8;
case TextureFormat::Depth32FStencil8:
return GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
default:
return GL_UNSIGNED_BYTE;
}
}
/**
* @brief TextureFilter OpenGL
*/
inline GLenum textureFilterToGLMin(TextureFilter filter) {
switch (filter) {
case TextureFilter::Nearest: return GL_NEAREST;
case TextureFilter::Linear: return GL_LINEAR;
case TextureFilter::NearestMipmapNearest: return GL_NEAREST_MIPMAP_NEAREST;
case TextureFilter::LinearMipmapNearest: return GL_LINEAR_MIPMAP_NEAREST;
case TextureFilter::NearestMipmapLinear: return GL_NEAREST_MIPMAP_LINEAR;
case TextureFilter::LinearMipmapLinear: return GL_LINEAR_MIPMAP_LINEAR;
default: return GL_LINEAR;
}
switch (filter) {
case TextureFilter::Nearest:
return GL_NEAREST;
case TextureFilter::Linear:
return GL_LINEAR;
case TextureFilter::NearestMipmapNearest:
return GL_NEAREST_MIPMAP_NEAREST;
case TextureFilter::LinearMipmapNearest:
return GL_LINEAR_MIPMAP_NEAREST;
case TextureFilter::NearestMipmapLinear:
return GL_NEAREST_MIPMAP_LINEAR;
case TextureFilter::LinearMipmapLinear:
return GL_LINEAR_MIPMAP_LINEAR;
default:
return GL_LINEAR;
}
}
inline GLenum textureFilterToGLMag(TextureFilter filter) {
// Mag filter doesn't support mipmaps
switch (filter) {
case TextureFilter::Nearest:
case TextureFilter::NearestMipmapNearest:
case TextureFilter::NearestMipmapLinear:
return GL_NEAREST;
case TextureFilter::Linear:
case TextureFilter::LinearMipmapNearest:
case TextureFilter::LinearMipmapLinear:
default:
return GL_LINEAR;
}
// Mag filter doesn't support mipmaps
switch (filter) {
case TextureFilter::Nearest:
case TextureFilter::NearestMipmapNearest:
case TextureFilter::NearestMipmapLinear:
return GL_NEAREST;
case TextureFilter::Linear:
case TextureFilter::LinearMipmapNearest:
case TextureFilter::LinearMipmapLinear:
default:
return GL_LINEAR;
}
}
/**
* @brief TextureWrap OpenGL
*/
inline GLenum textureWrapToGL(TextureWrap wrap) {
switch (wrap) {
case TextureWrap::Repeat: return GL_REPEAT;
case TextureWrap::ClampToEdge: return GL_CLAMP_TO_EDGE;
case TextureWrap::ClampToBorder: return GL_CLAMP_TO_BORDER;
case TextureWrap::MirroredRepeat: return GL_MIRRORED_REPEAT;
default: return GL_REPEAT;
}
switch (wrap) {
case TextureWrap::Repeat:
return GL_REPEAT;
case TextureWrap::ClampToEdge:
return GL_CLAMP_TO_EDGE;
case TextureWrap::ClampToBorder:
return GL_CLAMP_TO_BORDER;
case TextureWrap::MirroredRepeat:
return GL_MIRRORED_REPEAT;
default:
return GL_REPEAT;
}
}
/**
* @brief VertexFormat OpenGL
*/
inline void vertexFormatToGL(VertexFormat format, GLint& components, GLenum& type, GLboolean& normalized) {
switch (format) {
case VertexFormat::Float1: components = 1; type = GL_FLOAT; normalized = GL_FALSE; break;
case VertexFormat::Float2: components = 2; type = GL_FLOAT; normalized = GL_FALSE; break;
case VertexFormat::Float3: components = 3; type = GL_FLOAT; normalized = GL_FALSE; break;
case VertexFormat::Float4: components = 4; type = GL_FLOAT; normalized = GL_FALSE; break;
case VertexFormat::Int1: components = 1; type = GL_INT; normalized = GL_FALSE; break;
case VertexFormat::Int2: components = 2; type = GL_INT; normalized = GL_FALSE; break;
case VertexFormat::Int3: components = 3; type = GL_INT; normalized = GL_FALSE; break;
case VertexFormat::Int4: components = 4; type = GL_INT; normalized = GL_FALSE; break;
case VertexFormat::UInt1: components = 1; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
case VertexFormat::UInt2: components = 2; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
case VertexFormat::UInt3: components = 3; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
case VertexFormat::UInt4: components = 4; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
case VertexFormat::Byte4: components = 4; type = GL_BYTE; normalized = GL_FALSE; break;
case VertexFormat::Byte4Normalized: components = 4; type = GL_BYTE; normalized = GL_TRUE; break;
case VertexFormat::UByte4: components = 4; type = GL_UNSIGNED_BYTE; normalized = GL_FALSE; break;
case VertexFormat::UByte4Normalized:components = 4; type = GL_UNSIGNED_BYTE; normalized = GL_TRUE; break;
default: components = 4; type = GL_FLOAT; normalized = GL_FALSE; break;
}
inline void vertexFormatToGL(VertexFormat format, GLint &components,
GLenum &type, GLboolean &normalized) {
switch (format) {
case VertexFormat::Float1:
components = 1;
type = GL_FLOAT;
normalized = GL_FALSE;
break;
case VertexFormat::Float2:
components = 2;
type = GL_FLOAT;
normalized = GL_FALSE;
break;
case VertexFormat::Float3:
components = 3;
type = GL_FLOAT;
normalized = GL_FALSE;
break;
case VertexFormat::Float4:
components = 4;
type = GL_FLOAT;
normalized = GL_FALSE;
break;
case VertexFormat::Int1:
components = 1;
type = GL_INT;
normalized = GL_FALSE;
break;
case VertexFormat::Int2:
components = 2;
type = GL_INT;
normalized = GL_FALSE;
break;
case VertexFormat::Int3:
components = 3;
type = GL_INT;
normalized = GL_FALSE;
break;
case VertexFormat::Int4:
components = 4;
type = GL_INT;
normalized = GL_FALSE;
break;
case VertexFormat::UInt1:
components = 1;
type = GL_UNSIGNED_INT;
normalized = GL_FALSE;
break;
case VertexFormat::UInt2:
components = 2;
type = GL_UNSIGNED_INT;
normalized = GL_FALSE;
break;
case VertexFormat::UInt3:
components = 3;
type = GL_UNSIGNED_INT;
normalized = GL_FALSE;
break;
case VertexFormat::UInt4:
components = 4;
type = GL_UNSIGNED_INT;
normalized = GL_FALSE;
break;
case VertexFormat::Byte4:
components = 4;
type = GL_BYTE;
normalized = GL_FALSE;
break;
case VertexFormat::Byte4Normalized:
components = 4;
type = GL_BYTE;
normalized = GL_TRUE;
break;
case VertexFormat::UByte4:
components = 4;
type = GL_UNSIGNED_BYTE;
normalized = GL_FALSE;
break;
case VertexFormat::UByte4Normalized:
components = 4;
type = GL_UNSIGNED_BYTE;
normalized = GL_TRUE;
break;
default:
components = 4;
type = GL_FLOAT;
normalized = GL_FALSE;
break;
}
}
/**
* @brief IndexType OpenGL
*/
inline GLenum indexTypeToGL(IndexType type) {
switch (type) {
case IndexType::UInt16: return GL_UNSIGNED_SHORT;
case IndexType::UInt32: return GL_UNSIGNED_INT;
default: return GL_UNSIGNED_SHORT;
}
switch (type) {
case IndexType::UInt16:
return GL_UNSIGNED_SHORT;
case IndexType::UInt32:
return GL_UNSIGNED_INT;
default:
return GL_UNSIGNED_SHORT;
}
}
/**
* @brief PrimitiveType OpenGL
*/
inline GLenum primitiveTypeToGL(PrimitiveType type) {
switch (type) {
case PrimitiveType::Points: return GL_POINTS;
case PrimitiveType::Lines: return GL_LINES;
case PrimitiveType::LineStrip: return GL_LINE_STRIP;
case PrimitiveType::Triangles: return GL_TRIANGLES;
case PrimitiveType::TriangleStrip: return GL_TRIANGLE_STRIP;
case PrimitiveType::TriangleFan: return GL_TRIANGLE_FAN;
default: return GL_TRIANGLES;
}
switch (type) {
case PrimitiveType::Points:
return GL_POINTS;
case PrimitiveType::Lines:
return GL_LINES;
case PrimitiveType::LineStrip:
return GL_LINE_STRIP;
case PrimitiveType::Triangles:
return GL_TRIANGLES;
case PrimitiveType::TriangleStrip:
return GL_TRIANGLE_STRIP;
case PrimitiveType::TriangleFan:
return GL_TRIANGLE_FAN;
default:
return GL_TRIANGLES;
}
}
/**
* @brief BlendFactor OpenGL
*/
inline GLenum blendFactorToGL(BlendFactor factor) {
switch (factor) {
case BlendFactor::Zero: return GL_ZERO;
case BlendFactor::One: return GL_ONE;
case BlendFactor::SrcColor: return GL_SRC_COLOR;
case BlendFactor::OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR;
case BlendFactor::DstColor: return GL_DST_COLOR;
case BlendFactor::OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR;
case BlendFactor::SrcAlpha: return GL_SRC_ALPHA;
case BlendFactor::OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA;
case BlendFactor::DstAlpha: return GL_DST_ALPHA;
case BlendFactor::OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA;
default: return GL_ONE;
}
switch (factor) {
case BlendFactor::Zero:
return GL_ZERO;
case BlendFactor::One:
return GL_ONE;
case BlendFactor::SrcColor:
return GL_SRC_COLOR;
case BlendFactor::OneMinusSrcColor:
return GL_ONE_MINUS_SRC_COLOR;
case BlendFactor::DstColor:
return GL_DST_COLOR;
case BlendFactor::OneMinusDstColor:
return GL_ONE_MINUS_DST_COLOR;
case BlendFactor::SrcAlpha:
return GL_SRC_ALPHA;
case BlendFactor::OneMinusSrcAlpha:
return GL_ONE_MINUS_SRC_ALPHA;
case BlendFactor::DstAlpha:
return GL_DST_ALPHA;
case BlendFactor::OneMinusDstAlpha:
return GL_ONE_MINUS_DST_ALPHA;
default:
return GL_ONE;
}
}
/**
* @brief BlendOp OpenGL
*/
inline GLenum blendOpToGL(BlendOp op) {
switch (op) {
case BlendOp::Add: return GL_FUNC_ADD;
case BlendOp::Subtract: return GL_FUNC_SUBTRACT;
case BlendOp::ReverseSubtract: return GL_FUNC_REVERSE_SUBTRACT;
case BlendOp::Min: return GL_MIN;
case BlendOp::Max: return GL_MAX;
default: return GL_FUNC_ADD;
}
switch (op) {
case BlendOp::Add:
return GL_FUNC_ADD;
case BlendOp::Subtract:
return GL_FUNC_SUBTRACT;
case BlendOp::ReverseSubtract:
return GL_FUNC_REVERSE_SUBTRACT;
case BlendOp::Min:
return GL_MIN;
case BlendOp::Max:
return GL_MAX;
default:
return GL_FUNC_ADD;
}
}
/**
* @brief CompareFunc OpenGL
*/
inline GLenum compareFuncToGL(CompareFunc func) {
switch (func) {
case CompareFunc::Never: return GL_NEVER;
case CompareFunc::Less: return GL_LESS;
case CompareFunc::Equal: return GL_EQUAL;
case CompareFunc::LessEqual: return GL_LEQUAL;
case CompareFunc::Greater: return GL_GREATER;
case CompareFunc::NotEqual: return GL_NOTEQUAL;
case CompareFunc::GreaterEqual: return GL_GEQUAL;
case CompareFunc::Always: return GL_ALWAYS;
default: return GL_LESS;
}
switch (func) {
case CompareFunc::Never:
return GL_NEVER;
case CompareFunc::Less:
return GL_LESS;
case CompareFunc::Equal:
return GL_EQUAL;
case CompareFunc::LessEqual:
return GL_LEQUAL;
case CompareFunc::Greater:
return GL_GREATER;
case CompareFunc::NotEqual:
return GL_NOTEQUAL;
case CompareFunc::GreaterEqual:
return GL_GEQUAL;
case CompareFunc::Always:
return GL_ALWAYS;
default:
return GL_LESS;
}
}
/**
* @brief ShaderType OpenGL
*/
inline GLenum shaderTypeToGL(ShaderType type) {
switch (type) {
case ShaderType::Vertex: return GL_VERTEX_SHADER;
case ShaderType::Fragment: return GL_FRAGMENT_SHADER;
case ShaderType::Geometry: return GL_GEOMETRY_SHADER;
case ShaderType::Compute: return GL_COMPUTE_SHADER;
default: return GL_VERTEX_SHADER;
}
switch (type) {
case ShaderType::Vertex:
return GL_VERTEX_SHADER;
case ShaderType::Fragment:
return GL_FRAGMENT_SHADER;
case ShaderType::Geometry:
return GL_GEOMETRY_SHADER;
case ShaderType::Compute:
return GL_COMPUTE_SHADER;
default:
return GL_VERTEX_SHADER;
}
}
/**
* @brief VertexFormat
*/
inline uint32_t getVertexFormatSize(VertexFormat format) {
switch (format) {
case VertexFormat::Float1: return sizeof(float) * 1;
case VertexFormat::Float2: return sizeof(float) * 2;
case VertexFormat::Float3: return sizeof(float) * 3;
case VertexFormat::Float4: return sizeof(float) * 4;
case VertexFormat::Int1: return sizeof(int32_t) * 1;
case VertexFormat::Int2: return sizeof(int32_t) * 2;
case VertexFormat::Int3: return sizeof(int32_t) * 3;
case VertexFormat::Int4: return sizeof(int32_t) * 4;
case VertexFormat::UInt1: return sizeof(uint32_t) * 1;
case VertexFormat::UInt2: return sizeof(uint32_t) * 2;
case VertexFormat::UInt3: return sizeof(uint32_t) * 3;
case VertexFormat::UInt4: return sizeof(uint32_t) * 4;
case VertexFormat::Byte4:
case VertexFormat::Byte4Normalized: return sizeof(int8_t) * 4;
case VertexFormat::UByte4:
case VertexFormat::UByte4Normalized: return sizeof(uint8_t) * 4;
default: return sizeof(float) * 4;
}
switch (format) {
case VertexFormat::Float1:
return sizeof(float) * 1;
case VertexFormat::Float2:
return sizeof(float) * 2;
case VertexFormat::Float3:
return sizeof(float) * 3;
case VertexFormat::Float4:
return sizeof(float) * 4;
case VertexFormat::Int1:
return sizeof(int32_t) * 1;
case VertexFormat::Int2:
return sizeof(int32_t) * 2;
case VertexFormat::Int3:
return sizeof(int32_t) * 3;
case VertexFormat::Int4:
return sizeof(int32_t) * 4;
case VertexFormat::UInt1:
return sizeof(uint32_t) * 1;
case VertexFormat::UInt2:
return sizeof(uint32_t) * 2;
case VertexFormat::UInt3:
return sizeof(uint32_t) * 3;
case VertexFormat::UInt4:
return sizeof(uint32_t) * 4;
case VertexFormat::Byte4:
case VertexFormat::Byte4Normalized:
return sizeof(int8_t) * 4;
case VertexFormat::UByte4:
case VertexFormat::UByte4Normalized:
return sizeof(uint8_t) * 4;
default:
return sizeof(float) * 4;
}
}
} // namespace extra2d

View File

@ -1,172 +1,37 @@
#pragma once
#include <cstdio>
#include <sstream>
#include <memory>
#include <string>
#include <type_traits>
#include <types/base/types.h>
#include <utility>
#include <spdlog/fmt/fmt.h>
#include <spdlog/spdlog.h>
// Switch 平台支持
#ifdef __SWITCH__
#include <switch.h>
#endif
#ifndef TEST_BUILD
// SDL2 日志头文件(测试构建时不使用)
#include <SDL.h>
#include <switch.h>
#endif
namespace extra2d {
// ============================================================================
// 日志级别枚举
// ============================================================================
/**
* @brief
*/
enum class LogLevel {
#ifdef TEST_BUILD
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
Fatal = 5,
Off = 6
#else
Trace = SDL_LOG_PRIORITY_VERBOSE, // SDL 详细日志
Debug = SDL_LOG_PRIORITY_DEBUG, // SDL 调试日志
Info = SDL_LOG_PRIORITY_INFO, // SDL 信息日志
Warn = SDL_LOG_PRIORITY_WARN, // SDL 警告日志
Error = SDL_LOG_PRIORITY_ERROR, // SDL 错误日志
Fatal = SDL_LOG_PRIORITY_CRITICAL, // SDL 严重日志
Off = SDL_LOG_PRIORITY_CRITICAL + 1 // 关闭日志
#endif
Trace = spdlog::level::trace,
Debug = spdlog::level::debug,
Info = spdlog::level::info,
Warn = spdlog::level::warn,
Error = spdlog::level::err,
Fatal = spdlog::level::critical,
Off = spdlog::level::off
};
// ============================================================================
// 简单的 fmt-style {} 格式化器
// ============================================================================
namespace detail {
// 将单个参数转为字符串
template <typename T> inline std::string to_string_arg(const T &value) {
if constexpr (std::is_same_v<T, std::string>) {
return value;
} else if constexpr (std::is_same_v<T, const char *> ||
std::is_same_v<T, char *>) {
return value ? std::string(value) : std::string("(null)");
} else if constexpr (std::is_same_v<T, bool>) {
return value ? "true" : "false";
} else if constexpr (std::is_arithmetic_v<T>) {
// 对浮点数使用特殊格式
if constexpr (std::is_floating_point_v<T>) {
char buf[64];
snprintf(buf, sizeof(buf), "%.2f", static_cast<double>(value));
return buf;
} else {
return std::to_string(value);
}
} else {
std::ostringstream oss;
oss << value;
return oss.str();
}
}
// 格式化基础情况:没有更多参数
inline std::string format_impl(const char *fmt) {
std::string result;
while (*fmt) {
if (*fmt == '{' && *(fmt + 1) == '}') {
result += "{}"; // 无参数可替换,保留原样
fmt += 2;
} else {
result += *fmt;
++fmt;
}
}
return result;
}
// 格式化递归:替换第一个 {} 并递归处理剩余
template <typename T, typename... Args>
inline std::string format_impl(const char *fmt, const T &first,
const Args &...rest) {
std::string result;
while (*fmt) {
if (*fmt == '{') {
// 检查 {:#x} 等格式说明符
if (*(fmt + 1) == '}') {
result += to_string_arg(first);
fmt += 2;
result += format_impl(fmt, rest...);
return result;
} else if (*(fmt + 1) == ':') {
// 跳过格式说明符直到 }
const char *end = fmt + 2;
while (*end && *end != '}')
++end;
if (*end == '}') {
// 检查是否是十六进制格式
std::string spec(fmt + 2, end);
if (spec.find('x') != std::string::npos ||
spec.find('X') != std::string::npos) {
if constexpr (std::is_integral_v<T>) {
char buf[32];
snprintf(buf, sizeof(buf), "0x%x",
static_cast<unsigned int>(first));
result += buf;
} else {
result += to_string_arg(first);
}
} else if (spec.find('f') != std::string::npos ||
spec.find('.') != std::string::npos) {
if constexpr (std::is_arithmetic_v<T>) {
// 解析精度
int precision = 2;
auto dot = spec.find('.');
if (dot != std::string::npos) {
precision = 0;
for (size_t i = dot + 1;
i < spec.size() && spec[i] >= '0' && spec[i] <= '9'; ++i) {
precision = precision * 10 + (spec[i] - '0');
}
}
char fmtbuf[16];
snprintf(fmtbuf, sizeof(fmtbuf), "%%.%df", precision);
char buf[64];
snprintf(buf, sizeof(buf), fmtbuf, static_cast<double>(first));
result += buf;
} else {
result += to_string_arg(first);
}
} else {
result += to_string_arg(first);
}
fmt = end + 1;
result += format_impl(fmt, rest...);
return result;
}
}
}
result += *fmt;
++fmt;
}
return result;
}
} // namespace detail
// 顶层格式化函数
template <typename... Args>
inline std::string e2d_format(const char *fmt, const Args &...args) {
return detail::format_impl(fmt, args...);
}
// 无参数版本
inline std::string e2d_format(const char *fmt) { return std::string(fmt); }
// ============================================================================
// Logger 类 - 使用 SDL2 日志系统
// ============================================================================
/**
* @brief
*
*
* setLevel()
*/
class Logger {
public:
/**
@ -186,14 +51,14 @@ public:
static void setLevel(LogLevel level);
/**
* @brief
* @brief /
* @param enable
*/
static void setConsoleOutput(bool enable);
/**
* @brief
* @param filename
* @brief
* @param filename
*/
static void setFileOutput(const std::string &filename);
@ -201,94 +66,84 @@ public:
* @brief
* @return
*/
static LogLevel getLevel() { return level_; }
static LogLevel getLevel();
/**
* @brief
* @brief
* @tparam Args
* @param level
* @param fmt
* @param args
* @param fmt
* @param args
*/
template <typename... Args>
static void log(LogLevel level, const char *fmt, const Args &...args) {
if (static_cast<int>(level) < static_cast<int>(level_))
static void log(LogLevel level, fmt::format_string<Args...> fmt,
Args &&...args) {
if (!logger_) {
return;
std::string msg = e2d_format(fmt, args...);
#ifdef TEST_BUILD
printf("[%s] %s\n", getLevelString(level), msg.c_str());
#else
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level), "[%s] %s",
getLevelString(level), msg.c_str());
#endif
}
auto spdLevel = static_cast<spdlog::level::level_enum>(level);
logger_->log(spdLevel, fmt, std::forward<Args>(args)...);
}
/**
* @brief
* @brief
* @param level
* @param msg
*/
static void log(LogLevel level, const char *msg) {
if (static_cast<int>(level) < static_cast<int>(level_))
return;
#ifdef TEST_BUILD
printf("[%s] %s\n", getLevelString(level), msg);
#else
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level), "[%s] %s",
getLevelString(level), msg);
#endif
}
private:
static LogLevel level_; // 当前日志级别
static bool initialized_; // 是否已初始化
static bool consoleOutput_; // 是否输出到控制台
static bool fileOutput_; // 是否输出到文件
static std::string logFile_; // 日志文件路径
static void log(LogLevel level, const char *msg);
/**
* @brief
* @param level
* @return
* @brief spdlog
* @return spdlog
*/
static const char *getLevelString(LogLevel level);
static std::shared_ptr<spdlog::logger> getLogger() { return logger_; }
private:
static std::shared_ptr<spdlog::logger> logger_;
static bool initialized_;
static bool consoleOutput_;
static bool fileOutput_;
static std::string logFile_;
};
// ============================================================================
// 日志宏
// ============================================================================
#ifdef E2D_DEBUG
#define E2D_LOG_TRACE(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Trace, __VA_ARGS__)
#define E2D_LOG_DEBUG(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Debug, __VA_ARGS__)
#else
#define E2D_LOG_TRACE(...)
#define E2D_LOG_DEBUG(...)
#endif
#define E2D_LOG_INFO(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Info, __VA_ARGS__)
#define E2D_LOG_WARN(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Warn, __VA_ARGS__)
#define E2D_LOG_ERROR(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Error, __VA_ARGS__)
#define E2D_LOG_FATAL(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Fatal, __VA_ARGS__)
// 简化的日志宏
#define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__)
#define E2D_WARN(...) E2D_LOG_WARN(__VA_ARGS__)
#define E2D_ERROR(...) E2D_LOG_ERROR(__VA_ARGS__)
#define E2D_FATAL(...) E2D_LOG_FATAL(__VA_ARGS__)
#ifdef E2D_DEBUG
#define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__)
#define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__)
#else
#define E2D_DEBUG_LOG(...)
#define E2D_TRACE(...)
#endif
} // namespace extra2d
// ============================================
// 日志宏定义 - 简化统一版本
// ============================================
/**
* @brief -
*/
#define E2D_TRACE(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Trace, __VA_ARGS__)
/**
* @brief -
*/
#define E2D_DEBUG(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Debug, __VA_ARGS__)
/**
* @brief -
*/
#define E2D_INFO(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Info, __VA_ARGS__)
/**
* @brief -
*/
#define E2D_WARN(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Warn, __VA_ARGS__)
/**
* @brief -
*/
#define E2D_ERROR(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Error, __VA_ARGS__)
/**
* @brief -
*/
#define E2D_FATAL(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Fatal, __VA_ARGS__)

View File

@ -23,22 +23,24 @@ Application &Application::operator=(Application &&) noexcept = default;
bool Application::init(const AppConfig &config) {
if (initialized_) {
E2D_LOG_WARN("应用程序已经初始化");
return true;
}
config_ = config;
// 初始化日志系统(必须在所有日志调用之前)
Logger::init();
// 创建引擎上下文
context_ = Context::create();
if (!context_) {
E2D_LOG_ERROR("创建上下文失败");
E2D_ERROR("创建上下文失败");
return false;
}
// 初始化引擎
if (!context_->init()) {
E2D_LOG_ERROR("初始化上下文失败");
E2D_ERROR("初始化上下文失败");
return false;
}
@ -53,9 +55,9 @@ bool Application::init(const AppConfig &config) {
events::OnInit::emit();
E2D_LOG_INFO("应用程序初始化成功");
E2D_LOG_INFO("窗口: {}x{}, 全屏: {}", config.width,
config.height, config.fullscreen);
E2D_INFO("应用程序初始化成功");
E2D_INFO("窗口: {}x{}, 全屏: {}", config.width, config.height,
config.fullscreen);
return true;
}
@ -71,10 +73,10 @@ void Application::initModules() {
// 初始化所有模块
for (auto &module : modules_) {
if (!module->init()) {
E2D_LOG_ERROR("初始化模块失败: {}", module->getName());
E2D_ERROR("初始化模块失败: {}", module->getName());
} else {
E2D_LOG_INFO("模块已初始化: {} (优先级: {})", module->getName(),
module->getPriority());
E2D_INFO("模块已初始化: {} (优先级: {})", module->getName(),
module->getPriority());
}
}
}
@ -85,7 +87,7 @@ void Application::shutdown() {
events::OnShutdown::emit();
E2D_LOG_INFO("正在关闭应用程序...");
E2D_INFO("正在关闭应用程序...");
// 按相反顺序销毁模块
for (auto it = modules_.rbegin(); it != modules_.rend(); ++it) {
@ -99,12 +101,15 @@ void Application::shutdown() {
initialized_ = false;
running_ = false;
E2D_LOG_INFO("应用程序关闭完成");
E2D_INFO("应用程序关闭完成");
// 关闭日志系统(在所有日志调用之后)
Logger::shutdown();
}
void Application::run() {
if (!initialized_) {
E2D_LOG_ERROR("应用程序未初始化");
E2D_ERROR("应用程序未初始化");
return;
}
@ -159,7 +164,7 @@ void Application::pause() {
if (!paused_) {
paused_ = true;
events::OnPause::emit();
E2D_LOG_INFO("应用程序已暂停");
E2D_INFO("应用程序已暂停");
}
}
@ -167,7 +172,7 @@ void Application::resume() {
if (paused_) {
paused_ = false;
events::OnResume::emit();
E2D_LOG_INFO("应用程序已恢复");
E2D_INFO("应用程序已恢复");
}
}

View File

@ -75,7 +75,7 @@ AssetsModule::~AssetsModule() {
}
bool AssetsModule::init() {
E2D_LOG_INFO("资源模块正在初始化...");
E2D_INFO("资源模块正在初始化...");
textureLoader_ = std::make_unique<TextureLoader>();
shaderLoader_ = std::make_unique<ShaderLoader>();
@ -84,8 +84,7 @@ bool AssetsModule::init() {
onShowListener_ = std::make_unique<events::OnShow::Listener>();
onShowListener_->bind([this]() { this->onGLContextReady(); });
E2D_LOG_INFO(
"资源模块初始化成功 (等待 GL 上下文)");
E2D_INFO("资源模块初始化成功 (等待 GL 上下文)");
return true;
}
@ -94,19 +93,19 @@ void AssetsModule::onGLContextReady() {
return;
}
E2D_LOG_INFO("OpenGL 上下文就绪,正在创建默认资源...");
E2D_INFO("OpenGL 上下文就绪,正在创建默认资源...");
if (!createDefaultResources()) {
E2D_LOG_ERROR("创建默认资源失败");
E2D_ERROR("创建默认资源失败");
return;
}
defaultResourcesCreated_ = true;
E2D_LOG_INFO("默认资源创建成功");
E2D_INFO("默认资源创建成功");
}
void AssetsModule::shutdown() {
E2D_LOG_INFO("资源模块正在关闭...");
E2D_INFO("资源模块正在关闭...");
// 关闭异步加载系统
shutdownAsyncLoader();
@ -141,7 +140,7 @@ void AssetsModule::shutdown() {
defaultResourcesCreated_ = false;
E2D_LOG_INFO("资源模块关闭完成");
E2D_INFO("资源模块关闭完成");
}
AssetsModule *getAssets() { return g_assetsModule; }
@ -164,13 +163,13 @@ Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
}
if (!textureLoader_) {
E2D_LOG_ERROR("纹理加载器未注册");
E2D_ERROR("纹理加载器未注册");
return Handle<Texture>::invalid();
}
Ptr<Texture> texture = textureLoader_->load(path);
if (!texture) {
E2D_LOG_ERROR("加载纹理失败: {}", path);
E2D_ERROR("加载纹理失败: {}", path);
return Handle<Texture>::invalid();
}
@ -189,7 +188,7 @@ Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
texturePathCache_[path] = handle;
}
E2D_LOG_DEBUG("已加载纹理: {} -> 句柄索引 {}", path, handle.index());
E2D_DEBUG("已加载纹理: {} -> 句柄索引 {}", path, handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) {
@ -212,13 +211,13 @@ Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
}
if (!textureLoader_) {
E2D_LOG_ERROR("纹理加载器未注册");
E2D_ERROR("纹理加载器未注册");
return Handle<Texture>::invalid();
}
Ptr<Texture> texture = textureLoader_->loadFromMemory(data, size);
if (!texture) {
E2D_LOG_ERROR("从内存加载纹理失败: {}", key);
E2D_ERROR("从内存加载纹理失败: {}", key);
return Handle<Texture>::invalid();
}
@ -245,13 +244,13 @@ template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
}
if (!shaderLoader_) {
E2D_LOG_ERROR("着色器加载器未注册");
E2D_ERROR("着色器加载器未注册");
return Handle<Shader>::invalid();
}
Ptr<Shader> shader = shaderLoader_->load(path);
if (!shader) {
E2D_LOG_ERROR("加载着色器失败: {}", path);
E2D_ERROR("加载着色器失败: {}", path);
return Handle<Shader>::invalid();
}
@ -270,7 +269,7 @@ template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
shaderPathCache_[path] = handle;
}
E2D_LOG_DEBUG("已加载着色器: {} -> 句柄索引 {}", path, handle.index());
E2D_DEBUG("已加载着色器: {} -> 句柄索引 {}", path, handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) {
@ -297,14 +296,14 @@ Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
}
if (!shaderLoader_) {
E2D_LOG_ERROR("着色器加载器未注册");
E2D_ERROR("着色器加载器未注册");
return Handle<Shader>::invalid();
}
ShaderLoader *shaderLoader = static_cast<ShaderLoader *>(shaderLoader_.get());
Ptr<Shader> shader = shaderLoader->load(vertPath, fragPath);
if (!shader) {
E2D_LOG_ERROR("加载着色器失败: {} + {}", vertPath, fragPath);
E2D_ERROR("加载着色器失败: {} + {}", vertPath, fragPath);
return Handle<Shader>::invalid();
}
@ -323,8 +322,8 @@ Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
shaderPathCache_[cacheKey] = handle;
}
E2D_LOG_DEBUG("已加载着色器: {} + {} -> 句柄索引 {}", vertPath, fragPath,
handle.index());
E2D_DEBUG("已加载着色器: {} + {} -> 句柄索引 {}", vertPath, fragPath,
handle.index());
// 如果启用了热重载,添加文件监控
if (hotReloadEnabled_) {
@ -348,7 +347,7 @@ AssetsModule::loadDir<Texture>(const std::string &directory,
try {
std::filesystem::path dirPath(directory);
if (!fileExists(directory)) {
E2D_LOG_WARN("目录未找到: {}", directory);
E2D_WARN("目录未找到: {}", directory);
return handles;
}
@ -382,10 +381,10 @@ AssetsModule::loadDir<Texture>(const std::string &directory,
}
}
} catch (const std::exception &e) {
E2D_LOG_ERROR("加载目录 {} 时出错: {}", directory, e.what());
E2D_ERROR("加载目录 {} 时出错: {}", directory, e.what());
}
E2D_LOG_DEBUG("已从 {} 加载 {} 个纹理", directory, handles.size());
E2D_DEBUG("已从 {} 加载 {} 个纹理", directory, handles.size());
return handles;
}
@ -455,11 +454,11 @@ bool AssetsModule::createDefaultResources() {
Ptr<Texture> texture = makePtr<Texture>();
uint8_t whitePixel[] = {255, 255, 255, 255};
if (!texture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8)) {
E2D_LOG_ERROR("创建默认纹理失败");
E2D_ERROR("创建默认纹理失败");
return false;
}
defaultTexture_ = textures_.insert(texture);
E2D_LOG_DEBUG("已创建默认纹理");
E2D_DEBUG("已创建默认纹理");
}
{
@ -474,8 +473,7 @@ bool AssetsModule::createDefaultResources() {
: "shader/default.frag";
if (!fileExists(vertPath) || !fileExists(fragPath)) {
E2D_LOG_ERROR("默认着色器文件未找到: {}, {}", vertPath,
fragPath);
E2D_ERROR("默认着色器文件未找到: {}, {}", vertPath, fragPath);
return false;
}
@ -483,22 +481,19 @@ bool AssetsModule::createDefaultResources() {
std::string vsSource = readFileToString(vertPath);
std::string fsSource = readFileToString(fragPath);
if (vsSource.empty() || fsSource.empty()) {
E2D_LOG_ERROR("读取默认着色器文件失败: {}, {}", vertPath,
fragPath);
E2D_ERROR("读取默认着色器文件失败: {}, {}", vertPath, fragPath);
return false;
}
// 从源码加载着色器
Ptr<Shader> shader = makePtr<Shader>();
if (!shader->loadFromSource(vsSource, fsSource)) {
E2D_LOG_ERROR("编译默认着色器失败: {}, {}", vertPath,
fragPath);
E2D_ERROR("编译默认着色器失败: {}, {}", vertPath, fragPath);
return false;
}
defaultShader_ = shaders_.insert(shader);
E2D_LOG_DEBUG("已从文件加载默认着色器: {}, {}", vertPath,
fragPath);
E2D_DEBUG("已从文件加载默认着色器: {}, {}", vertPath, fragPath);
}
{
@ -527,17 +522,17 @@ bool AssetsModule::createDefaultResources() {
// 添加默认纹理到材质
material->setTexture("uTexture", getPtr(defaultTexture_), 0);
defaultMaterial_ = materials_.insert(material);
E2D_LOG_DEBUG("已创建默认材质,使用默认纹理和布局");
E2D_DEBUG("已创建默认材质,使用默认纹理和布局");
}
{
Ptr<Mesh> mesh = Mesh::createQuad(Vec2(1.0f, 1.0f));
if (!mesh) {
E2D_LOG_ERROR("创建默认四边形网格失败");
E2D_ERROR("创建默认四边形网格失败");
return false;
}
defaultQuad_ = meshes_.insert(mesh);
E2D_LOG_DEBUG("已创建默认四边形网格");
E2D_DEBUG("已创建默认四边形网格");
}
return true;
@ -584,9 +579,9 @@ Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(defaultQuad_); }
void AssetsModule::enableHotReload(bool enable) {
hotReloadEnabled_ = enable;
if (enable) {
E2D_LOG_INFO("热重载已启用");
E2D_INFO("热重载已启用");
} else {
E2D_LOG_INFO("热重载已禁用");
E2D_INFO("热重载已禁用");
}
}
@ -594,7 +589,8 @@ void AssetsModule::setHotReloadInterval(float interval) {
hotReloadInterval_ = interval;
}
void AssetsModule::addFileWatchInternal(const std::string &path, FileWatchInfo &&info) {
void AssetsModule::addFileWatchInternal(const std::string &path,
FileWatchInfo &&info) {
try {
if (!fileExists(path)) {
return;
@ -606,20 +602,22 @@ void AssetsModule::addFileWatchInternal(const std::string &path, FileWatchInfo &
std::unique_lock<std::shared_mutex> lock(mutex_);
fileWatchList_.push_back(std::move(info));
const char *type = fileWatchList_.back().isTexture ? "纹理" : "着色器";
E2D_LOG_DEBUG("正在监控 {} 文件: {}", type, path);
E2D_DEBUG("正在监控 {} 文件: {}", type, path);
} catch (const std::exception &e) {
E2D_LOG_ERROR("添加文件监控失败 {}: {}", path, e.what());
E2D_ERROR("添加文件监控失败 {}: {}", path, e.what());
}
}
void AssetsModule::addFileWatch(const std::string &path, Handle<Texture> handle) {
void AssetsModule::addFileWatch(const std::string &path,
Handle<Texture> handle) {
FileWatchInfo info;
info.textureHandle = handle;
info.isTexture = true;
addFileWatchInternal(path, std::move(info));
}
void AssetsModule::addFileWatch(const std::string &path, Handle<Shader> handle) {
void AssetsModule::addFileWatch(const std::string &path,
Handle<Shader> handle) {
FileWatchInfo info;
info.shaderHandle = handle;
info.isTexture = false;
@ -631,22 +629,22 @@ void AssetsModule::reloadTexture(const FileWatchInfo &info) {
return;
}
E2D_LOG_INFO("正在重载纹理: {}", info.path);
E2D_INFO("正在重载纹理: {}", info.path);
// 获取旧纹理指针
Texture *oldTexture = textures_.get(info.textureHandle);
if (!oldTexture) {
E2D_LOG_ERROR("未找到要重载的旧纹理: {}", info.path);
E2D_ERROR("未找到要重载的旧纹理: {}", info.path);
return;
}
// 直接在原有纹理对象上重新加载
if (!oldTexture->reloadFromFile(info.path)) {
E2D_LOG_ERROR("重载纹理失败: {}", info.path);
E2D_ERROR("重载纹理失败: {}", info.path);
return;
}
E2D_LOG_INFO("纹理重载成功: {}", info.path);
E2D_INFO("纹理重载成功: {}", info.path);
notifyTextureReloaded(info.textureHandle);
}
@ -655,12 +653,12 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
return;
}
E2D_LOG_INFO("正在重载着色器: {}", info.path);
E2D_INFO("正在重载着色器: {}", info.path);
// 获取旧着色器指针
Shader *oldShader = shaders_.get(info.shaderHandle);
if (!oldShader) {
E2D_LOG_ERROR("未找到要重载的旧着色器: {}", info.path);
E2D_ERROR("未找到要重载的旧着色器: {}", info.path);
return;
}
@ -677,7 +675,7 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
}
if (cacheKey.empty()) {
E2D_LOG_WARN("未找到着色器缓存键: {}", info.path);
E2D_WARN("未找到着色器缓存键: {}", info.path);
return;
}
@ -685,7 +683,7 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
size_t sepPos = cacheKey.find('|');
if (sepPos == std::string::npos) {
// 单文件模式 - 这里暂不支持,因为 ShaderLoader 目前只支持双文件
E2D_LOG_WARN("不支持单文件着色器重载: {}", info.path);
E2D_WARN("不支持单文件着色器重载: {}", info.path);
} else {
// 双文件模式
std::string vertPath = cacheKey.substr(0, sepPos);
@ -695,18 +693,18 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
std::string vsSource = readFileToString(vertPath);
std::string fsSource = readFileToString(fragPath);
if (vsSource.empty() || fsSource.empty()) {
E2D_LOG_ERROR("读取着色器文件失败: {}, {}", vertPath, fragPath);
E2D_ERROR("读取着色器文件失败: {}, {}", vertPath, fragPath);
return;
}
// 直接在原有着色器对象上重新加载
if (!oldShader->reloadFromSource(vsSource, fsSource)) {
E2D_LOG_ERROR("重载着色器失败: {} + {}", vertPath, fragPath);
E2D_ERROR("重载着色器失败: {} + {}", vertPath, fragPath);
return;
}
}
E2D_LOG_INFO("着色器重载成功: {}", info.path);
E2D_INFO("着色器重载成功: {}", info.path);
notifyShaderReloaded(info.shaderHandle);
}
@ -744,7 +742,7 @@ void AssetsModule::checkForChanges() {
lock.lock();
}
} catch (const std::exception &e) {
E2D_LOG_ERROR("检查文件 {} 时出错: {}", info.path, e.what());
E2D_ERROR("检查文件 {} 时出错: {}", info.path, e.what());
}
}
}
@ -768,7 +766,7 @@ void AssetsModule::initAsyncLoader(uint32_t threadCount) {
workerThreads_.emplace_back(&AssetsModule::workerThreadLoop, this);
}
E2D_LOG_INFO("异步加载器已初始化,使用 {} 个线程", threadCount);
E2D_INFO("异步加载器已初始化,使用 {} 个线程", threadCount);
}
void AssetsModule::shutdownAsyncLoader() {
@ -790,7 +788,7 @@ void AssetsModule::shutdownAsyncLoader() {
std::lock_guard<std::mutex> lock(queueMutex_);
loadQueue_.clear();
E2D_LOG_INFO("异步加载器已关闭");
E2D_INFO("异步加载器已关闭");
}
void AssetsModule::submitLoadTask(const LoadTask &task) {
@ -890,8 +888,7 @@ void AssetsModule::registerMaterialDependency(Handle<Material> material,
info.dependentMaterials.end(), material);
if (it == info.dependentMaterials.end()) {
info.dependentMaterials.push_back(material);
E2D_LOG_DEBUG("已注册材质 {} 对纹理 {} 的依赖",
material.index(), textureIndex);
E2D_DEBUG("已注册材质 {} 对纹理 {} 的依赖", material.index(), textureIndex);
}
}
@ -912,8 +909,8 @@ void AssetsModule::registerMaterialDependency(Handle<Material> material,
info.dependentMaterials.end(), material);
if (it == info.dependentMaterials.end()) {
info.dependentMaterials.push_back(material);
E2D_LOG_DEBUG("已注册材质 {} 对着色器 {} 的依赖",
material.index(), shaderIndex);
E2D_DEBUG("已注册材质 {} 对着色器 {} 的依赖", material.index(),
shaderIndex);
}
}
@ -927,8 +924,7 @@ void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) {
uint32_t textureIndex = texture.index();
auto it = textureDependencies_.find(textureIndex);
if (it != textureDependencies_.end()) {
E2D_LOG_INFO("通知 {} 个材质纹理已重载",
it->second.dependentMaterials.size());
E2D_INFO("通知 {} 个材质纹理已重载", it->second.dependentMaterials.size());
// 材质需要更新纹理引用
// 这里可以触发材质更新事件
@ -936,8 +932,7 @@ void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) {
Material *material = materials_.get(materialHandle);
if (material) {
// 材质自动使用新的纹理数据
E2D_LOG_DEBUG("材质 {} 已更新为重载后的纹理",
materialHandle.index());
E2D_DEBUG("材质 {} 已更新为重载后的纹理", materialHandle.index());
}
}
}
@ -953,16 +948,15 @@ void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) {
uint32_t shaderIndex = shader.index();
auto it = shaderDependencies_.find(shaderIndex);
if (it != shaderDependencies_.end()) {
E2D_LOG_INFO("通知 {} 个材质着色器已重载",
it->second.dependentMaterials.size());
E2D_INFO("通知 {} 个材质着色器已重载",
it->second.dependentMaterials.size());
for (const auto &materialHandle : it->second.dependentMaterials) {
Material *material = materials_.get(materialHandle);
if (material) {
// 更新材质的着色器引用
material->setShader(getPtr(shader));
E2D_LOG_DEBUG("材质 {} 已更新为重载后的着色器",
materialHandle.index());
E2D_DEBUG("材质 {} 已更新为重载后的着色器", materialHandle.index());
}
}
}

View File

@ -11,9 +11,7 @@ namespace {
/**
* @brief FileModule
*/
FileModule *getFileModule() {
return getModule<FileModule>();
}
FileModule *getFileModule() { return getModule<FileModule>(); }
/**
* @brief 使 FileModule
@ -65,31 +63,30 @@ Ptr<Shader> ShaderLoader::load(const std::string &vertPath,
// 读取顶点着色器文件
std::string vsSource = readFileToString(vertPath);
if (vsSource.empty()) {
E2D_LOG_ERROR("ShaderLoader: 读取顶点着色器失败: {}", vertPath);
E2D_ERROR("ShaderLoader: 读取顶点着色器失败: {}", vertPath);
return Ptr<Shader>();
}
// 读取片段着色器文件
std::string fsSource = readFileToString(fragPath);
if (fsSource.empty()) {
E2D_LOG_ERROR("ShaderLoader: 读取片段着色器失败: {}", fragPath);
E2D_ERROR("ShaderLoader: 读取片段着色器失败: {}", fragPath);
return Ptr<Shader>();
}
// 从源码加载着色器
Ptr<Shader> shader = makePtr<Shader>();
if (!shader->loadFromSource(vsSource, fsSource)) {
E2D_LOG_ERROR("ShaderLoader: 编译着色器失败 {} + {}", vertPath,
fragPath);
E2D_ERROR("ShaderLoader: 编译着色器失败 {} + {}", vertPath, fragPath);
return Ptr<Shader>();
}
E2D_LOG_DEBUG("ShaderLoader: 已加载着色器 {} + {}", vertPath, fragPath);
E2D_DEBUG("ShaderLoader: 已加载着色器 {} + {}", vertPath, fragPath);
return shader;
}
Ptr<Shader> ShaderLoader::loadFromMemory(const uint8_t *data, size_t size) {
E2D_LOG_ERROR("ShaderLoader: loadFromMemory 不支持着色器");
E2D_ERROR("ShaderLoader: loadFromMemory 不支持着色器");
return Ptr<Shader>();
}
@ -98,11 +95,11 @@ Ptr<Shader> ShaderLoader::loadFromSource(const std::string &vsSource,
Ptr<Shader> shader = makePtr<Shader>();
if (!shader->loadFromSource(vsSource, fsSource)) {
E2D_LOG_ERROR("ShaderLoader: 从源码编译着色器失败");
E2D_ERROR("ShaderLoader: 从源码编译着色器失败");
return Ptr<Shader>();
}
E2D_LOG_DEBUG("ShaderLoader: 已从源码编译着色器");
E2D_DEBUG("ShaderLoader: 已从源码编译着色器");
return shader;
}

View File

@ -10,11 +10,11 @@ Ptr<Texture> TextureLoader::load(const std::string &path) {
Ptr<Texture> texture = makePtr<Texture>();
if (!texture->loadFromFile(path)) {
E2D_LOG_ERROR("TextureLoader: 从 {} 加载纹理失败", path);
E2D_ERROR("TextureLoader: 从 {} 加载纹理失败", path);
return Ptr<Texture>();
}
E2D_LOG_DEBUG("TextureLoader: 已加载纹理 {}", path);
E2D_DEBUG("TextureLoader: 已加载纹理 {}", path);
return texture;
}
@ -26,7 +26,7 @@ Ptr<Texture> TextureLoader::loadFromMemory(const uint8_t *data, size_t size) {
&width, &height, &channels, 0);
if (!imageData) {
E2D_LOG_ERROR("TextureLoader: 从内存解码图像失败");
E2D_ERROR("TextureLoader: 从内存解码图像失败");
return Ptr<Texture>();
}
@ -54,12 +54,11 @@ Ptr<Texture> TextureLoader::loadFromMemory(const uint8_t *data, size_t size) {
stbi_image_free(imageData);
if (!success) {
E2D_LOG_ERROR("TextureLoader: 从内存创建纹理失败");
E2D_ERROR("TextureLoader: 从内存创建纹理失败");
return Ptr<Texture>();
}
E2D_LOG_DEBUG("TextureLoader: 已从内存创建纹理 ({}x{})", width,
height);
E2D_DEBUG("TextureLoader: 已从内存创建纹理 ({}x{})", width, height);
return texture;
}

View File

@ -30,11 +30,11 @@ bool FileModule::init() {
// 初始化 Switch 的 RomFS
Result rc = romfsInit();
if (R_SUCCEEDED(rc)) {
E2D_LOG_INFO("RomFS 初始化成功");
E2D_INFO("RomFS 初始化成功");
// 在 Switch 上设置资源根目录为 RomFS
assetRoot_ = "romfs:/";
} else {
E2D_LOG_WARN("RomFS 初始化失败: {:#08X}", rc);
E2D_WARN("RomFS 初始化失败: {:#08X}", rc);
// RomFS 初始化失败,使用当前目录
assetRoot_ = "./";
}
@ -43,7 +43,7 @@ bool FileModule::init() {
assetRoot_ = "./";
#endif
E2D_LOG_INFO("资源根目录设置为: {}", assetRoot_);
E2D_INFO("资源根目录设置为: {}", assetRoot_);
return true;
}
@ -102,7 +102,7 @@ bool FileModule::write(const std::string &path, const void *data,
size_t size) const {
// RomFS 是只读的,不允许写入
if (isRomfsPath(path)) {
E2D_LOG_WARN("无法写入 RomFS 路径: {}", path);
E2D_WARN("无法写入 RomFS 路径: {}", path);
return false;
}
@ -124,7 +124,7 @@ bool FileModule::append(const std::string &path, const void *data,
size_t size) const {
// RomFS 是只读的,不允许写入
if (isRomfsPath(path)) {
E2D_LOG_WARN("无法追加到 RomFS 路径: {}", path);
E2D_WARN("无法追加到 RomFS 路径: {}", path);
return false;
}
@ -140,7 +140,7 @@ bool FileModule::append(const std::string &path, const void *data,
bool FileModule::remove(const std::string &path) const {
// RomFS 是只读的,不允许删除
if (isRomfsPath(path)) {
E2D_LOG_WARN("无法删除 RomFS 路径: {}", path);
E2D_WARN("无法删除 RomFS 路径: {}", path);
return false;
}
return fs::remove(path);
@ -149,7 +149,7 @@ bool FileModule::remove(const std::string &path) const {
bool FileModule::mkdir(const std::string &path) const {
// RomFS 是只读的,不允许创建目录
if (isRomfsPath(path)) {
E2D_LOG_WARN("无法在 RomFS 中创建目录: {}", path);
E2D_WARN("无法在 RomFS 中创建目录: {}", path);
return false;
}

View File

@ -12,7 +12,7 @@ WindowModule::~WindowModule() { shutdown(); }
bool WindowModule::init() {
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
E2D_LOG_ERROR("初始化 SDL 视频子系统失败: {}", SDL_GetError());
E2D_ERROR("初始化 SDL 视频子系统失败: {}", SDL_GetError());
return false;
}
@ -58,11 +58,11 @@ bool WindowModule::create(const WindowCfg &cfg) {
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
if (!window_) {
E2D_LOG_ERROR("创建窗口失败: {}", SDL_GetError());
E2D_ERROR("创建窗口失败: {}", SDL_GetError());
return false;
}
E2D_LOG_INFO("窗口已创建: {}x{}", cfg.width, cfg.height);
E2D_INFO("窗口已创建: {}x{}", cfg.width, cfg.height);
return true;
}
@ -71,7 +71,7 @@ bool WindowModule::pollEvents() {
while (SDL_PollEvent(&evt)) {
switch (evt.type) {
case SDL_QUIT:
E2D_LOG_INFO("收到 SDL_QUIT 事件");
E2D_INFO("收到 SDL_QUIT 事件");
shouldClose_ = true;
// 发送窗口关闭事件
events::OnClose::emit();
@ -139,10 +139,8 @@ void WindowModule::onModuleConfig(const AppConfig &config) {
cfg.resizable = config.resizable;
if (create(cfg)) {
// 发送窗口显示事件
// 先显示窗口setVisible 会触发 OnShow 事件
// RHI 模块会监听此事件并创建 OpenGL 上下文
events::OnShow::emit();
setVisible(true);
}
}
@ -193,12 +191,10 @@ void WindowModule::setVisible(bool visible) {
if (window_) {
if (visible) {
SDL_ShowWindow(window_);
// 发送窗口显示事件
events::OnShow::emit();
// SDL_WINDOWEVENT_SHOWN 事件会触发 OnShow这里不需要手动触发
} else {
SDL_HideWindow(window_);
// 发送窗口隐藏事件
events::OnHide::emit();
// SDL_WINDOWEVENT_HIDDEN 事件会触发 OnHide这里不需要手动触发
}
}
}

View File

@ -1,15 +1,15 @@
#include <algorithm>
#include <cstring>
#include <vector>
#include <renderer/command_queue.h>
#include <renderer/material.h>
#include <renderer/mesh.h>
#include <renderer/rhi_module.h>
#include <renderer/rhi/opengl/gl_command_list.h>
#include <renderer/rhi/opengl/gl_texture.h>
#include <renderer/rhi_module.h>
#include <types/math/transform.h>
#include <types/ptr/intrusive_ptr.h>
#include <utils/logger.h>
#include <vector>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
@ -28,14 +28,14 @@ CommandSorter::CommandSorter() {
CommandSorter::~CommandSorter() = default;
CommandSorter::CommandSorter(CommandSorter&& other) noexcept
CommandSorter::CommandSorter(CommandSorter &&other) noexcept
: commands_(std::move(other.commands_)),
sortedIndices_(std::move(other.sortedIndices_)),
commandCount_(other.commandCount_) {
other.commandCount_ = 0;
}
CommandSorter& CommandSorter::operator=(CommandSorter&& other) noexcept {
CommandSorter &CommandSorter::operator=(CommandSorter &&other) noexcept {
if (this != &other) {
commands_ = std::move(other.commands_);
sortedIndices_ = std::move(other.sortedIndices_);
@ -62,17 +62,17 @@ uint32_t CommandSorter::addCommand(const DrawCommand &cmd) {
}
void CommandSorter::sort() {
if (commandCount_ == 0) return;
if (commandCount_ == 0)
return;
// 只排序有效范围的索引
auto indicesBegin = sortedIndices_.begin();
auto indicesEnd = sortedIndices_.begin() + commandCount_;
// 使用稳定排序保持相同键的命令顺序
std::stable_sort(indicesBegin, indicesEnd,
[this](uint32_t a, uint32_t b) {
return commands_[a].key < commands_[b].key;
});
std::stable_sort(indicesBegin, indicesEnd, [this](uint32_t a, uint32_t b) {
return commands_[a].key < commands_[b].key;
});
}
void CommandSorter::clear() {
@ -198,40 +198,40 @@ bool CommandQueue::initialize() {
// 获取 RHI 模块
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
E2D_ERROR("RHIModule not available");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
E2D_ERROR("RHIDevice not available");
return false;
}
context_ = rhiModule->getContext();
if (!context_) {
E2D_LOG_ERROR("RHIContext not available");
E2D_ERROR("RHIContext not available");
return false;
}
// 创建命令列表
commandList_ = device->createCommandList();
if (!commandList_) {
E2D_LOG_ERROR("Failed to create command list");
E2D_ERROR("Failed to create command list");
return false;
}
// 初始化 UBO 管理器
uboManager_ = std::make_unique<UniformBufferManager>();
if (!uboManager_->initialize()) {
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
E2D_ERROR("Failed to initialize UniformBufferManager");
return false;
}
// 预分配材质 UBO 数据缓冲区
materialUBOData_.reserve(1024 * 1024); // 1MB
E2D_LOG_INFO("命令队列已初始化");
E2D_INFO("命令队列已初始化");
return true;
}
@ -240,7 +240,7 @@ void CommandQueue::shutdown() {
commandList_.reset();
context_ = nullptr;
materialUBOData_.clear();
E2D_LOG_INFO("命令队列已关闭");
E2D_INFO("命令队列已关闭");
}
void CommandQueue::beginFrame() {
@ -285,7 +285,8 @@ uint32_t CommandQueue::getMaterialId(Material *material) {
return id;
}
std::pair<UniformBuffer*, uint32_t> CommandQueue::allocateMaterialUBO(uint32_t size) {
std::pair<UniformBuffer *, uint32_t>
CommandQueue::allocateMaterialUBO(uint32_t size) {
if (!uboManager_ || size == 0) {
return {nullptr, 0};
}
@ -314,7 +315,8 @@ std::pair<UniformBuffer*, uint32_t> CommandQueue::allocateMaterialUBO(uint32_t s
materialUBOBufferOffset_ = offset + alignedSize;
return {nullptr, offset}; // 返回 nullptr 表示使用 CPU 缓冲区offset 是 CPU 缓冲区偏移
return {nullptr,
offset}; // 返回 nullptr 表示使用 CPU 缓冲区offset 是 CPU 缓冲区偏移
}
void CommandQueue::flushMaterialUBOToGPU() {
@ -323,20 +325,22 @@ void CommandQueue::flushMaterialUBOToGPU() {
}
// 获取一个足够大的 UBO
currentMaterialUBO_ = uboManager_->acquireMaterialUBO(materialUBOBufferOffset_);
currentMaterialUBO_ =
uboManager_->acquireMaterialUBO(materialUBOBufferOffset_);
if (!currentMaterialUBO_) {
E2D_LOG_ERROR("获取材质 UBO 失败");
E2D_ERROR("获取材质 UBO 失败");
return;
}
// 批量更新到 GPU
currentMaterialUBO_->update(materialUBOData_.data(), materialUBOBufferOffset_, 0);
currentMaterialUBO_->update(materialUBOData_.data(), materialUBOBufferOffset_,
0);
// 重置 CPU 缓冲区偏移
materialUBOBufferOffset_ = 0;
}
UniformBuffer* CommandQueue::getCurrentMaterialUBO() const {
UniformBuffer *CommandQueue::getCurrentMaterialUBO() const {
return currentMaterialUBO_;
}
@ -386,14 +390,16 @@ void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
// 复制材质数据到 CPU 缓冲区,并修改颜色
if (offset + materialDataSize <= materialUBOData_.size()) {
std::memcpy(materialUBOData_.data() + offset, material->getData(), materialDataSize);
std::memcpy(materialUBOData_.data() + offset, material->getData(),
materialDataSize);
// 将实例颜色应用到 UBO 数据中的 uColor 参数
auto layout = material->getLayout();
if (layout) {
const auto* param = layout->getParam("uColor");
const auto *param = layout->getParam("uColor");
if (param && param->type == MaterialParamType::Color) {
std::memcpy(materialUBOData_.data() + offset + param->offset, &color.r, sizeof(float) * 4);
std::memcpy(materialUBOData_.data() + offset + param->offset,
&color.r, sizeof(float) * 4);
}
}
@ -431,31 +437,34 @@ void CommandQueue::setViewport(int32_t x, int32_t y, int32_t width,
commandList_->setViewport(viewport);
}
void CommandQueue::updateGlobalUBO(const Mat4& viewProjection, float deltaTime,
uint32_t screenWidth, uint32_t screenHeight, uint32_t frameIndex) {
void CommandQueue::updateGlobalUBO(const Mat4 &viewProjection, float deltaTime,
uint32_t screenWidth, uint32_t screenHeight,
uint32_t frameIndex) {
if (!uboManager_) {
E2D_LOG_WARN("CommandQueue::updateGlobalUBO: uboManager 为空");
E2D_WARN("CommandQueue::updateGlobalUBO: uboManager 为空");
return;
}
// 填充全局 UBO 数据
std::memcpy(globalUBOData_.viewProjection, glm::value_ptr(viewProjection), sizeof(float) * 16);
std::memcpy(globalUBOData_.viewProjection, glm::value_ptr(viewProjection),
sizeof(float) * 16);
globalUBOData_.cameraPosition[0] = 0.0f;
globalUBOData_.cameraPosition[1] = 0.0f;
globalUBOData_.cameraPosition[2] = 0.0f;
globalUBOData_.cameraPosition[3] = 1.0f;
globalUBOData_.time = 0.0f; // TODO: 传递实际时间
globalUBOData_.time = 0.0f; // TODO: 传递实际时间
globalUBOData_.deltaTime = deltaTime;
globalUBOData_.screenSize[0] = static_cast<float>(screenWidth);
globalUBOData_.screenSize[1] = static_cast<float>(screenHeight);
// 使用双缓冲更新 UBO
uboManager_->updateGlobalUBO(&globalUBOData_, sizeof(globalUBOData_), frameIndex);
uboManager_->updateGlobalUBO(&globalUBOData_, sizeof(globalUBOData_),
frameIndex);
}
void CommandQueue::execute(uint32_t frameIndex) {
if (!commandList_) {
E2D_LOG_ERROR("CommandQueue::execute: commandList 为空");
E2D_ERROR("CommandQueue::execute: commandList 为空");
return;
}
@ -477,7 +486,8 @@ void CommandQueue::execute(uint32_t frameIndex) {
commandList_->submit();
}
void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch, uint32_t frameIndex) {
void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
uint32_t frameIndex) {
if (!commandList_) {
return;
}
@ -487,12 +497,12 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
commandList_->setPipeline(batch.pipeline.get());
stats_.pipelineBinds++;
} else {
E2D_LOG_WARN("批次没有有效的管线!");
E2D_WARN("批次没有有效的管线!");
}
// 绑定全局 UBO (binding = 0) - 使用双缓冲
if (uboManager_) {
UniformBuffer* globalUBO = uboManager_->getGlobalUBO(frameIndex);
UniformBuffer *globalUBO = uboManager_->getGlobalUBO(frameIndex);
if (globalUBO) {
commandList_->setUniformBuffer(0, globalUBO->getRHIBuffer());
stats_.bufferBinds++;
@ -518,7 +528,7 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
commandList_->setVertexBuffer(0, cmd.vertexBuffer.get(), 0);
stats_.bufferBinds++;
} else {
E2D_LOG_WARN("绘制命令没有有效的顶点缓冲区!");
E2D_WARN("绘制命令没有有效的顶点缓冲区!");
}
// 绑定索引缓冲区(如果有)
@ -529,7 +539,9 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
// 绑定材质 UBO (binding = 1)
if (cmd.materialUBOSize > 0 && currentMaterialUBO_) {
commandList_->setUniformBuffer(1, currentMaterialUBO_->getRHIBuffer(), cmd.materialUBOOffset, cmd.materialUBOSize);
commandList_->setUniformBuffer(1, currentMaterialUBO_->getRHIBuffer(),
cmd.materialUBOOffset,
cmd.materialUBOSize);
stats_.bufferBinds++;
}

View File

@ -1,6 +1,6 @@
#include <cstring>
#include <renderer/material.h>
#include <utils/logger.h>
#include <cstring>
namespace extra2d {
@ -15,31 +15,31 @@ MaterialLayout::MaterialLayout() = default;
* @param name
* @param type
*/
void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
if (finalized_) {
E2D_LOG_WARN("无法向已完成的材质布局添加参数");
return;
}
void MaterialLayout::addParam(const std::string &name, MaterialParamType type) {
if (finalized_) {
E2D_WARN("无法向已完成的材质布局添加参数");
return;
}
// 检查是否已存在
if (paramIndexMap_.find(name) != paramIndexMap_.end()) {
E2D_LOG_WARN("参数 '{}' 已存在于材质布局中", name);
return;
}
MaterialParamInfo info;
info.type = type;
info.size = getMaterialParamSize(type);
info.offset = 0; // 将在 finalize 时计算
size_t index = params_.size();
params_.emplace_back(name, info);
paramIndexMap_[name] = index;
// 检查是否已存在
if (paramIndexMap_.find(name) != paramIndexMap_.end()) {
E2D_WARN("参数 '{}' 已存在于材质布局中", name);
return;
}
MaterialParamInfo info;
info.type = type;
info.size = getMaterialParamSize(type);
info.offset = 0; // 将在 finalize 时计算
size_t index = params_.size();
params_.emplace_back(name, info);
paramIndexMap_[name] = index;
}
/**
* @brief std140
*
*
* std140
* -
* - 16
@ -47,45 +47,46 @@ void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
* - 16
*/
void MaterialLayout::finalize() {
if (finalized_) return;
uint32_t offset = 0;
for (auto& pair : params_) {
auto& info = pair.second;
// 计算对齐要求 - std140规则
uint32_t alignment = 4; // 默认 4 字节对齐
switch (info.type) {
case MaterialParamType::Float:
alignment = 4; // float: 4 字节对齐
break;
case MaterialParamType::Vec2:
alignment = 8; // vec2: 8 字节对齐
break;
case MaterialParamType::Vec3:
case MaterialParamType::Vec4:
case MaterialParamType::Color:
alignment = 16; // vec3/vec4/color: 16 字节对齐std140 规则)
break;
case MaterialParamType::Mat4:
alignment = 16; // mat4: 16 字节对齐每列vec4对齐到16字节
break;
default:
alignment = 4;
break;
}
// 对齐偏移到alignment的倍数
offset = (offset + alignment - 1) & ~(alignment - 1);
info.offset = offset;
offset += info.size;
if (finalized_)
return;
uint32_t offset = 0;
for (auto &pair : params_) {
auto &info = pair.second;
// 计算对齐要求 - std140规则
uint32_t alignment = 4; // 默认 4 字节对齐
switch (info.type) {
case MaterialParamType::Float:
alignment = 4; // float: 4 字节对齐
break;
case MaterialParamType::Vec2:
alignment = 8; // vec2: 8 字节对齐
break;
case MaterialParamType::Vec3:
case MaterialParamType::Vec4:
case MaterialParamType::Color:
alignment = 16; // vec3/vec4/color: 16 字节对齐std140 规则)
break;
case MaterialParamType::Mat4:
alignment = 16; // mat4: 16 字节对齐每列vec4对齐到16字节
break;
default:
alignment = 4;
break;
}
// 最终大小对齐到 16 字节std140要求结构体总大小是16的倍数
bufferSize_ = (offset + 15) & ~15;
finalized_ = true;
// 对齐偏移到alignment的倍数
offset = (offset + alignment - 1) & ~(alignment - 1);
info.offset = offset;
offset += info.size;
}
// 最终大小对齐到 16 字节std140要求结构体总大小是16的倍数
bufferSize_ = (offset + 15) & ~15;
finalized_ = true;
}
/**
@ -93,12 +94,13 @@ void MaterialLayout::finalize() {
* @param name
* @return nullptr
*/
const MaterialParamInfo* MaterialLayout::getParam(const std::string& name) const {
auto mapIt = paramIndexMap_.find(name);
if (mapIt != paramIndexMap_.end()) {
return &params_[mapIt->second].second;
}
return nullptr;
const MaterialParamInfo *
MaterialLayout::getParam(const std::string &name) const {
auto mapIt = paramIndexMap_.find(name);
if (mapIt != paramIndexMap_.end()) {
return &params_[mapIt->second].second;
}
return nullptr;
}
// ========================================
@ -112,29 +114,27 @@ Material::Material() = default;
* @param layout
*/
void Material::setLayout(Ptr<MaterialLayout> layout) {
layout_ = layout;
if (layout_ && layout_->isFinalized()) {
data_.resize(layout_->getBufferSize(), 0);
}
layout_ = layout;
if (layout_ && layout_->isFinalized()) {
data_.resize(layout_->getBufferSize(), 0);
}
}
/**
* @brief
* @param shader
*/
void Material::setShader(Ptr<Shader> shader) {
shader_ = shader;
}
void Material::setShader(Ptr<Shader> shader) { shader_ = shader; }
/**
* @brief RHI 线
* @return RHI 线
*/
PipelineHandle Material::getPipeline() const {
if (shader_) {
return shader_->getPipeline();
}
return PipelineHandle{};
if (shader_) {
return shader_->getPipeline();
}
return PipelineHandle{};
}
/**
@ -142,13 +142,14 @@ PipelineHandle Material::getPipeline() const {
* @param name
* @param value
*/
void Material::setFloat(const std::string& name, float value) {
if (!layout_) return;
const auto* param = layout_->getParam(name);
if (param && param->type == MaterialParamType::Float) {
std::memcpy(data_.data() + param->offset, &value, sizeof(float));
}
void Material::setFloat(const std::string &name, float value) {
if (!layout_)
return;
const auto *param = layout_->getParam(name);
if (param && param->type == MaterialParamType::Float) {
std::memcpy(data_.data() + param->offset, &value, sizeof(float));
}
}
/**
@ -156,13 +157,14 @@ void Material::setFloat(const std::string& name, float value) {
* @param name
* @param value
*/
void Material::setVec2(const std::string& name, const Vec2& value) {
if (!layout_) return;
const auto* param = layout_->getParam(name);
if (param && param->type == MaterialParamType::Vec2) {
std::memcpy(data_.data() + param->offset, &value.x, sizeof(float) * 2);
}
void Material::setVec2(const std::string &name, const Vec2 &value) {
if (!layout_)
return;
const auto *param = layout_->getParam(name);
if (param && param->type == MaterialParamType::Vec2) {
std::memcpy(data_.data() + param->offset, &value.x, sizeof(float) * 2);
}
}
/**
@ -173,14 +175,16 @@ void Material::setVec2(const std::string& name, const Vec2& value) {
* @param z Z
* @param w W
*/
void Material::setVec4(const std::string& name, float x, float y, float z, float w) {
if (!layout_) return;
const auto* param = layout_->getParam(name);
if (param && param->type == MaterialParamType::Vec4) {
float values[4] = {x, y, z, w};
std::memcpy(data_.data() + param->offset, values, sizeof(float) * 4);
}
void Material::setVec4(const std::string &name, float x, float y, float z,
float w) {
if (!layout_)
return;
const auto *param = layout_->getParam(name);
if (param && param->type == MaterialParamType::Vec4) {
float values[4] = {x, y, z, w};
std::memcpy(data_.data() + param->offset, values, sizeof(float) * 4);
}
}
/**
@ -188,13 +192,14 @@ void Material::setVec4(const std::string& name, float x, float y, float z, float
* @param name
* @param value
*/
void Material::setColor(const std::string& name, const Color& value) {
if (!layout_) return;
const auto* param = layout_->getParam(name);
if (param && param->type == MaterialParamType::Color) {
std::memcpy(data_.data() + param->offset, &value.r, sizeof(float) * 4);
}
void Material::setColor(const std::string &name, const Color &value) {
if (!layout_)
return;
const auto *param = layout_->getParam(name);
if (param && param->type == MaterialParamType::Color) {
std::memcpy(data_.data() + param->offset, &value.r, sizeof(float) * 4);
}
}
/**
@ -202,13 +207,14 @@ void Material::setColor(const std::string& name, const Color& value) {
* @param name
* @param value
*/
void Material::setMat4(const std::string& name, const float* value) {
if (!layout_) return;
const auto* param = layout_->getParam(name);
if (param && param->type == MaterialParamType::Mat4) {
std::memcpy(data_.data() + param->offset, value, sizeof(float) * 16);
}
void Material::setMat4(const std::string &name, const float *value) {
if (!layout_)
return;
const auto *param = layout_->getParam(name);
if (param && param->type == MaterialParamType::Mat4) {
std::memcpy(data_.data() + param->offset, value, sizeof(float) * 16);
}
}
/**
@ -217,25 +223,24 @@ void Material::setMat4(const std::string& name, const float* value) {
* @param texture
* @param slot 0-15
*/
void Material::setTexture(const std::string& uniformName, Ptr<Texture> texture, uint32_t slot) {
// 查找是否已存在相同名称的纹理
for (auto& texSlot : textures_) {
if (texSlot.uniformName == uniformName) {
texSlot.texture = texture;
texSlot.slot = slot;
return;
}
void Material::setTexture(const std::string &uniformName, Ptr<Texture> texture,
uint32_t slot) {
// 查找是否已存在相同名称的纹理
for (auto &texSlot : textures_) {
if (texSlot.uniformName == uniformName) {
texSlot.texture = texture;
texSlot.slot = slot;
return;
}
// 添加新的纹理槽位
textures_.push_back({texture, slot, uniformName});
}
// 添加新的纹理槽位
textures_.push_back({texture, slot, uniformName});
}
/**
* @brief
*/
void Material::clearTextures() {
textures_.clear();
}
void Material::clearTextures() { textures_.clear(); }
} // namespace extra2d

View File

@ -20,13 +20,13 @@ void Mesh::setVertices(const Vertex *vertices, uint32_t count) {
// 获取 RHI 模块
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHI 模块不可用");
E2D_ERROR("RHI 模块不可用");
return;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHI 设备不可用");
E2D_ERROR("RHI 设备不可用");
return;
}
@ -37,7 +37,7 @@ void Mesh::setVertices(const Vertex *vertices, uint32_t count) {
// 通过句柄获取缓冲区指针并更新数据
// 注意:这里假设可以通过某种方式获取缓冲区指针
// 实际实现可能需要通过 RHI 命令列表来更新
E2D_LOG_WARN("顶点缓冲区更新尚未实现");
E2D_WARN("顶点缓冲区更新尚未实现");
} else {
// 需要创建新缓冲区
vertexBuffer_ = BufferHandle();
@ -49,7 +49,7 @@ void Mesh::setVertices(const Vertex *vertices, uint32_t count) {
// 创建缓冲区
auto buffer = device->createBuffer(desc);
if (!buffer) {
E2D_LOG_ERROR("创建顶点缓冲区失败");
E2D_ERROR("创建顶点缓冲区失败");
return;
}
@ -62,7 +62,7 @@ void Mesh::setVertices(const Vertex *vertices, uint32_t count) {
}
vertexCount_ = count;
E2D_LOG_INFO("设置了 {} 个顶点", count);
E2D_INFO("设置了 {} 个顶点", count);
}
void Mesh::setIndices(const uint16_t *indices, uint32_t count) {
@ -72,13 +72,13 @@ void Mesh::setIndices(const uint16_t *indices, uint32_t count) {
// 获取 RHI 模块
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHI 模块不可用");
E2D_ERROR("RHI 模块不可用");
return;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHI 设备不可用");
E2D_ERROR("RHI 设备不可用");
return;
}
@ -86,7 +86,7 @@ void Mesh::setIndices(const uint16_t *indices, uint32_t count) {
// 如果缓冲区已存在且容量足够,更新数据
if (indexBuffer_.isValid() && indexCapacity_ >= count) {
E2D_LOG_WARN("索引缓冲区更新尚未实现");
E2D_WARN("索引缓冲区更新尚未实现");
} else {
// 需要创建新缓冲区
indexBuffer_ = BufferHandle();
@ -98,7 +98,7 @@ void Mesh::setIndices(const uint16_t *indices, uint32_t count) {
// 创建缓冲区
auto buffer = device->createBuffer(desc);
if (!buffer) {
E2D_LOG_ERROR("创建索引缓冲区失败");
E2D_ERROR("创建索引缓冲区失败");
return;
}
@ -109,7 +109,7 @@ void Mesh::setIndices(const uint16_t *indices, uint32_t count) {
}
indexCount_ = count;
E2D_LOG_INFO("设置了 {} 个索引", count);
E2D_INFO("设置了 {} 个索引", count);
}
void Mesh::updateVertices(const Vertex *vertices, uint32_t count,
@ -118,7 +118,7 @@ void Mesh::updateVertices(const Vertex *vertices, uint32_t count,
return;
if (offset + count > vertexCount_) {
E2D_LOG_WARN("顶点更新超出边界");
E2D_WARN("顶点更新超出边界");
return;
}
@ -134,7 +134,7 @@ void Mesh::updateVertices(const Vertex *vertices, uint32_t count,
// 通过句柄获取缓冲区并更新数据
// 注意:这里需要 RHI 提供更新缓冲区的方法
// 暂时记录为未实现
E2D_LOG_WARN("updateVertices 尚未完全实现");
E2D_WARN("updateVertices 尚未完全实现");
}
Ptr<Mesh> Mesh::createQuad(const Vec2 &size, const Rect &uv) {

View File

@ -95,11 +95,11 @@ RenderGraph &RenderGraph::operator=(RenderGraph &&other) noexcept {
bool RenderGraph::initialize() {
if (!commandQueue_.initialize()) {
E2D_LOG_ERROR("初始化命令队列失败");
E2D_ERROR("初始化命令队列失败");
return false;
}
E2D_LOG_INFO("渲染图已初始化");
E2D_INFO("渲染图已初始化");
return true;
}
@ -110,7 +110,7 @@ void RenderGraph::shutdown() {
commandQueue_.shutdown();
compiled_ = false;
E2D_LOG_INFO("渲染图已关闭");
E2D_INFO("渲染图已关闭");
}
RenderPass *RenderGraph::addPass(std::unique_ptr<RenderPass> pass) {
@ -174,15 +174,15 @@ bool RenderGraph::compile() {
createResources();
compiled_ = true;
E2D_LOG_INFO("渲染图编译完成,共 {} 个通道", executionOrder_.size());
E2D_INFO("渲染图编译完成,共 {} 个通道", executionOrder_.size());
return true;
}
void RenderGraph::execute(float deltaTime, const Mat4& viewProjection) {
void RenderGraph::execute(float deltaTime, const Mat4 &viewProjection) {
if (!compiled_) {
if (!compile()) {
E2D_LOG_ERROR("编译渲染图失败");
E2D_ERROR("编译渲染图失败");
return;
}
}

View File

@ -12,7 +12,7 @@ RendererModule::RendererModule() = default;
RendererModule::~RendererModule() = default;
bool RendererModule::init() {
E2D_LOG_INFO("正在初始化渲染模块...");
E2D_INFO("正在初始化渲染模块...");
// 绑定事件监听器
onRenderBeginListener_.bind([this]() { onRenderBegin(); });
@ -24,7 +24,7 @@ bool RendererModule::init() {
onResizeListener_.bind([this](int32 w, int32 h) { onResize(w, h); });
onShowListener_.bind([this]() { onWindowShow(); });
E2D_LOG_INFO("渲染模块已初始化 (等待窗口显示)");
E2D_INFO("渲染模块已初始化 (等待窗口显示)");
return true;
}
@ -33,25 +33,25 @@ void RendererModule::onWindowShow() {
return;
}
E2D_LOG_INFO("正在初始化渲染模块上下文...");
E2D_INFO("正在初始化渲染模块上下文...");
// 初始化 RHI 模块
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHI 模块不可用");
E2D_ERROR("RHI 模块不可用");
return;
}
if (!rhiModule->getDevice()) {
// RHI 设备尚未初始化,这是正常的时序问题
// RHIModule 会在窗口显示后初始化设备,然后再次触发此函数
E2D_LOG_INFO("RHI 设备尚未就绪,等待 RHI 初始化...");
E2D_INFO("RHI 设备尚未就绪,等待 RHI 初始化...");
return;
}
// 初始化渲染图
if (!renderGraph_.initialize()) {
E2D_LOG_ERROR("初始化渲染图失败");
E2D_ERROR("初始化渲染图失败");
return;
}
@ -61,22 +61,22 @@ void RendererModule::onWindowShow() {
auto geometryPass = std::make_unique<GeometryRenderPass>("Geometry");
renderGraph_.addPass(std::move(geometryPass));
E2D_LOG_INFO("已添加默认几何渲染通道");
E2D_INFO("已添加默认几何渲染通道");
auto windowModule = getModule<WindowModule>();
if (!windowModule) {
E2D_LOG_ERROR("窗口模块不可用");
E2D_ERROR("窗口模块不可用");
return;
}
setViewport(0, 0, static_cast<int32>(windowModule->getSize().w),
static_cast<int32>(windowModule->getSize().h));
initialized_ = true;
E2D_LOG_INFO("渲染模块上下文初始化成功");
E2D_INFO("渲染模块上下文初始化成功");
}
void RendererModule::shutdown() {
E2D_LOG_INFO("正在关闭渲染模块...");
E2D_INFO("正在关闭渲染模块...");
renderGraph_.shutdown();
commandQueue_ = nullptr;
@ -84,7 +84,7 @@ void RendererModule::shutdown() {
// 注意:事件监听器是值类型,会在析构时自动清理
E2D_LOG_INFO("渲染模块关闭完成");
E2D_INFO("渲染模块关闭完成");
}
RHIContext *RendererModule::getRHIContext() const {
@ -162,7 +162,7 @@ void RendererModule::onRenderBegin() {
void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
if (!initialized_ || !commandQueue_) {
E2D_LOG_WARN("onRenderSubmit: 渲染模块未初始化或没有命令队列");
E2D_WARN("onRenderSubmit: 渲染模块未初始化或没有命令队列");
return;
}
@ -194,8 +194,8 @@ void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
transform, cmd.drawMesh.color);
stats_.commandsSubmitted++;
} else {
E2D_LOG_WARN("提交绘制命令失败: 材质={}, 网格={}",
material ? "有效" : "", mesh ? "有效" : "");
E2D_WARN("提交绘制命令失败: 材质={}, 网格={}", material ? "有效" : "",
mesh ? "有效" : "");
}
break;
}
@ -226,7 +226,7 @@ void RendererModule::onRenderSetCamera(const Mat4 &viewProj) {
void RendererModule::onRenderEnd() {
if (!initialized_) {
E2D_LOG_WARN("onRenderEnd: 渲染模块未初始化");
E2D_WARN("onRenderEnd: 渲染模块未初始化");
return;
}
@ -241,10 +241,10 @@ void RendererModule::onRenderEnd() {
// 每60帧输出一次统计信息
static uint32_t frameCount = 0;
if (++frameCount % 60 == 0) {
E2D_LOG_INFO("渲染统计: 绘制调用={}, 三角形数={}, 顶点数={}, "
"管线绑定={}, 纹理绑定={}, 缓冲区绑定={}",
stats.drawCalls, stats.triangles, stats.vertices,
stats.pipelineBinds, stats.textureBinds, stats.bufferBinds);
E2D_INFO("渲染统计: 绘制调用={}, 三角形数={}, 顶点数={}, "
"管线绑定={}, 纹理绑定={}, 缓冲区绑定={}",
stats.drawCalls, stats.triangles, stats.vertices,
stats.pipelineBinds, stats.textureBinds, stats.bufferBinds);
}
}

View File

@ -12,22 +12,22 @@ GLContext::GLContext(GLDevice *device)
GLContext::~GLContext() = default;
bool GLContext::initialize() {
E2D_LOG_INFO("正在初始化 OpenGL 上下文...");
E2D_INFO("正在初始化 OpenGL 上下文...");
// 创建默认帧缓冲(窗口帧缓冲)
defaultFramebuffer_ = std::make_unique<GLFramebuffer>();
defaultFramebuffer_->setSize(800, 600); // 默认大小,会被更新
E2D_LOG_INFO("OpenGL 上下文初始化完成");
E2D_INFO("OpenGL 上下文初始化完成");
return true;
}
void GLContext::shutdown() {
E2D_LOG_INFO("正在关闭 OpenGL 上下文...");
E2D_INFO("正在关闭 OpenGL 上下文...");
defaultFramebuffer_.reset();
E2D_LOG_INFO("OpenGL 上下文关闭完成");
E2D_INFO("OpenGL 上下文关闭完成");
}
void GLContext::beginFrame() {

View File

@ -7,8 +7,8 @@
#include <renderer/rhi/opengl/gl_texture.h>
#include <renderer/rhi/opengl/gl_utils.h>
#include <glad/glad.h>
#include <SDL.h>
#include <glad/glad.h>
#include <utils/logger.h>
namespace extra2d {
@ -21,19 +21,20 @@ class GLDevice;
*/
class GLDevice : public RHIDevice {
public:
GLDevice() : window_(nullptr), glContext_(nullptr), context_(nullptr), stats_() {}
GLDevice()
: window_(nullptr), glContext_(nullptr), context_(nullptr), stats_() {}
~GLDevice() override { shutdown(); }
bool initialize(void* nativeWindow) override {
E2D_LOG_INFO("正在初始化 OpenGL 设备...");
bool initialize(void *nativeWindow) override {
E2D_INFO("正在初始化 OpenGL 设备...");
if (!nativeWindow) {
E2D_LOG_ERROR("原生窗口句柄为空");
E2D_ERROR("原生窗口句柄为空");
return false;
}
window_ = static_cast<SDL_Window*>(nativeWindow);
window_ = static_cast<SDL_Window *>(nativeWindow);
// 创建 OpenGL ES 3.2 上下文
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
@ -45,38 +46,39 @@ public:
glContext_ = SDL_GL_CreateContext(window_);
if (!glContext_) {
E2D_LOG_ERROR("创建 OpenGL ES 上下文失败: {}", SDL_GetError());
E2D_ERROR("创建 OpenGL ES 上下文失败: {}", SDL_GetError());
return false;
}
// 初始化 GLAD
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
E2D_LOG_ERROR("初始化 GLAD 失败");
E2D_ERROR("初始化 GLAD 失败");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
}
E2D_LOG_INFO("OpenGL ES 上下文已创建: {}.{}", GLVersion.major, GLVersion.minor);
E2D_INFO("OpenGL ES 上下文已创建: {}.{}", GLVersion.major, GLVersion.minor);
const char* version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
const char *version =
reinterpret_cast<const char *>(glGetString(GL_VERSION));
if (!version) {
E2D_LOG_ERROR("获取 OpenGL 版本失败");
E2D_ERROR("获取 OpenGL 版本失败");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
}
E2D_LOG_INFO("OpenGL 版本: {}", version);
E2D_LOG_INFO("OpenGL 供应商: {}",
reinterpret_cast<const char *>(glGetString(GL_VENDOR)));
E2D_LOG_INFO("OpenGL 渲染器: {}",
reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
E2D_INFO("OpenGL 版本: {}", version);
E2D_INFO("OpenGL 供应商: {}",
reinterpret_cast<const char *>(glGetString(GL_VENDOR)));
E2D_INFO("OpenGL 渲染器: {}",
reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
// 创建上下文管理器
context_ = std::make_unique<GLContext>(this);
if (!context_->initialize()) {
E2D_LOG_ERROR("初始化 GL 上下文失败");
E2D_ERROR("初始化 GL 上下文失败");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
@ -86,12 +88,12 @@ public:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
E2D_LOG_INFO("OpenGL 设备初始化成功");
E2D_INFO("OpenGL 设备初始化成功");
return true;
}
void shutdown() override {
E2D_LOG_INFO("正在关闭 OpenGL 设备...");
E2D_INFO("正在关闭 OpenGL 设备...");
if (context_) {
context_->shutdown();
@ -105,7 +107,7 @@ public:
window_ = nullptr;
E2D_LOG_INFO("OpenGL 设备关闭完成");
E2D_INFO("OpenGL 设备关闭完成");
}
std::unique_ptr<RHIBuffer> createBuffer(const BufferDesc &desc) override {
@ -127,7 +129,7 @@ public:
std::unique_ptr<RHIShader> createShader(const ShaderDesc &desc) override {
auto shader = std::make_unique<GLShader>(desc);
if (!shader->compile()) {
E2D_LOG_ERROR("着色器编译失败: {}", shader->getCompileLog());
E2D_ERROR("着色器编译失败: {}", shader->getCompileLog());
return nullptr;
}
return shader;
@ -139,16 +141,16 @@ public:
if (!pipeline->create()) {
return nullptr;
}
// 获取 vertex shader 的 program ID
if (desc.vertexShader.isValid()) {
RHIShader* shader = desc.vertexShader.get();
RHIShader *shader = desc.vertexShader.get();
if (shader) {
auto* glShader = static_cast<GLShader*>(shader);
auto *glShader = static_cast<GLShader *>(shader);
pipeline->setGLProgram(glShader->getGLProgram());
}
}
return pipeline;
}
@ -186,7 +188,7 @@ public:
void incrementRenderPassSwitches() { stats_.renderPassSwitches++; }
private:
SDL_Window* window_;
SDL_Window *window_;
SDL_GLContext glContext_;
std::unique_ptr<GLContext> context_;
RenderStats stats_;

View File

@ -26,13 +26,13 @@ RHIModule::~RHIModule() {
}
bool RHIModule::init() {
E2D_LOG_INFO("正在初始化 RHI 模块...");
E2D_INFO("正在初始化 RHI 模块...");
// 注册窗口显示事件监听,在窗口显示时创建 OpenGL 上下文
onShowListener_ = std::make_unique<events::OnShow::Listener>();
onShowListener_->bind([this]() { this->onWindowShow(); });
E2D_LOG_INFO("RHI 模块已初始化 (等待窗口显示)");
E2D_INFO("RHI 模块已初始化 (等待窗口显示)");
return true;
}
@ -41,54 +41,45 @@ void RHIModule::onWindowShow() {
return;
}
E2D_LOG_INFO("RHI 模块: 正在创建 OpenGL 设备...");
E2D_INFO("RHI 模块: 正在创建 OpenGL 设备...");
// 获取窗口模块
auto *windowModule = getModule<WindowModule>();
if (!windowModule) {
E2D_LOG_ERROR("窗口模块不可用");
E2D_ERROR("窗口模块不可用");
return;
}
// 等待窗口创建完成
if (!windowModule->handle()) {
E2D_LOG_ERROR("窗口尚未创建");
E2D_ERROR("窗口尚未创建");
return;
}
// 创建 OpenGL 设备并传入窗口句柄
device_ = CreateGLDevice();
if (!device_) {
E2D_LOG_ERROR("创建 RHI 设备失败");
E2D_ERROR("创建 RHI 设备失败");
return;
}
if (!device_->initialize(windowModule->handle())) {
E2D_LOG_ERROR("初始化 RHI 设备失败");
E2D_ERROR("初始化 RHI 设备失败");
device_.reset();
return;
}
context_ = device_->getContext();
if (!context_) {
E2D_LOG_ERROR("获取 RHI 上下文失败");
E2D_ERROR("获取 RHI 上下文失败");
device_->shutdown();
device_.reset();
return;
}
// 初始化上下文
if (!context_->initialize()) {
E2D_LOG_ERROR("初始化 RHI 上下文失败");
device_->shutdown();
device_.reset();
context_ = nullptr;
return;
}
// 注意context 已在 GLDevice::initialize() 中初始化,这里不需要再次初始化
initialized_ = true;
E2D_LOG_INFO("RHIModule initialized successfully (backend: {})",
device_->getBackendName());
E2D_INFO("RHI模块 初始化成功 (后端:{})", device_->getBackendName());
// RHI 初始化完成,再次触发 OnShow 事件通知其他模块
// 这样 RendererModule 等依赖 RHI 的模块可以完成初始化
@ -96,7 +87,7 @@ void RHIModule::onWindowShow() {
}
void RHIModule::shutdown() {
E2D_LOG_INFO("Shutting down RHIModule...");
E2D_INFO("正在关闭 RHI模块...");
// 清理事件监听器
onShowListener_.reset();
@ -113,14 +104,14 @@ void RHIModule::shutdown() {
initialized_ = false;
E2D_LOG_INFO("RHIModule shutdown complete");
E2D_INFO("RHI模块 关闭完成");
}
RHIModule *RHIModule::get() { return g_rhiModule; }
std::unique_ptr<RHIBuffer> RHIModule::createBuffer(const BufferDesc &desc) {
if (!device_) {
E2D_LOG_ERROR("Cannot create buffer: RHI device not initialized");
E2D_ERROR("无法创建缓冲区RHI 设备未初始化");
return nullptr;
}
return device_->createBuffer(desc);
@ -128,7 +119,7 @@ std::unique_ptr<RHIBuffer> RHIModule::createBuffer(const BufferDesc &desc) {
std::unique_ptr<RHITexture> RHIModule::createTexture(const TextureDesc &desc) {
if (!device_) {
E2D_LOG_ERROR("无法创建纹理: RHI 设备未初始化");
E2D_ERROR("无法创建纹理: RHI 设备未初始化");
return nullptr;
}
return device_->createTexture(desc);
@ -136,7 +127,7 @@ std::unique_ptr<RHITexture> RHIModule::createTexture(const TextureDesc &desc) {
std::unique_ptr<RHIShader> RHIModule::createShader(const ShaderDesc &desc) {
if (!device_) {
E2D_LOG_ERROR("无法创建着色器: RHI 设备未初始化");
E2D_ERROR("无法创建着色器: RHI 设备未初始化");
return nullptr;
}
return device_->createShader(desc);
@ -145,7 +136,7 @@ std::unique_ptr<RHIShader> RHIModule::createShader(const ShaderDesc &desc) {
std::unique_ptr<RHIPipeline>
RHIModule::createPipeline(const PipelineDesc &desc) {
if (!device_) {
E2D_LOG_ERROR("无法创建管线: RHI 设备未初始化");
E2D_ERROR("无法创建管线: RHI 设备未初始化");
return nullptr;
}
return device_->createPipeline(desc);
@ -154,7 +145,7 @@ RHIModule::createPipeline(const PipelineDesc &desc) {
std::unique_ptr<RHIFramebuffer>
RHIModule::createFramebuffer(const RenderPassDesc &desc) {
if (!device_) {
E2D_LOG_ERROR("无法创建帧缓冲区: RHI 设备未初始化");
E2D_ERROR("无法创建帧缓冲区: RHI 设备未初始化");
return nullptr;
}
return device_->createFramebuffer(desc);
@ -162,7 +153,7 @@ RHIModule::createFramebuffer(const RenderPassDesc &desc) {
std::unique_ptr<RHICommandList> RHIModule::createCommandList() {
if (!device_) {
E2D_LOG_ERROR("无法创建命令列表: RHI 设备未初始化");
E2D_ERROR("无法创建命令列表: RHI 设备未初始化");
return nullptr;
}
return device_->createCommandList();

View File

@ -35,13 +35,13 @@ bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
// 获取 RHI 设备
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHI 模块不可用");
E2D_ERROR("RHI 模块不可用");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHI 设备不可用");
E2D_ERROR("RHI 设备不可用");
return false;
}
@ -57,7 +57,7 @@ bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
// 创建着色器
auto shader = device->createShader(shaderDesc);
if (!shader) {
E2D_LOG_ERROR("创建着色器失败");
E2D_ERROR("创建着色器失败");
return false;
}
@ -76,7 +76,7 @@ bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
// 创建管线
auto pipeline = device->createPipeline(pipelineDesc);
if (!pipeline) {
E2D_LOG_ERROR("创建管线失败");
E2D_ERROR("创建管线失败");
handle_ = ShaderHandle();
return false;
}
@ -84,7 +84,7 @@ bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
// 获取管线句柄
pipeline_ = PipelineHandle(pipeline.release());
E2D_LOG_INFO("着色器创建成功");
E2D_INFO("着色器创建成功");
return true;
}
@ -127,13 +127,13 @@ bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
// 获取 RHI 设备
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHI 模块不可用");
E2D_ERROR("RHI 模块不可用");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHI 设备不可用");
E2D_ERROR("RHI 设备不可用");
return false;
}
@ -149,7 +149,7 @@ bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
// 创建着色器
auto shader = device->createShader(shaderDesc);
if (!shader) {
E2D_LOG_ERROR("重载时创建着色器失败");
E2D_ERROR("重载时创建着色器失败");
return false;
}
@ -168,7 +168,7 @@ bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
// 创建管线
auto pipeline = device->createPipeline(pipelineDesc);
if (!pipeline) {
E2D_LOG_ERROR("重载时创建管线失败");
E2D_ERROR("重载时创建管线失败");
handle_ = ShaderHandle();
return false;
}
@ -176,7 +176,7 @@ bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
// 获取管线句柄
pipeline_ = PipelineHandle(pipeline.release());
E2D_LOG_INFO("着色器重载成功");
E2D_INFO("着色器重载成功");
return true;
}

View File

@ -1,5 +1,5 @@
#include <renderer/texture.h>
#include <renderer/rhi_module.h>
#include <renderer/texture.h>
#include <utils/logger.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
@ -9,223 +9,259 @@ namespace extra2d {
Texture::Texture() = default;
Texture::~Texture() {
// RHI 纹理句柄是轻量级句柄,不需要显式释放
// 实际的纹理资源由 RHI 设备管理
handle_ = TextureHandle();
// RHI 纹理句柄是轻量级句柄,不需要显式释放
// 实际的纹理资源由 RHI 设备管理
handle_ = TextureHandle();
}
bool Texture::loadFromFile(const std::string& path) {
// 加载图片
int channels;
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load(path.c_str(), &width_, &height_, &channels, 0);
if (!data) {
E2D_LOG_ERROR("加载纹理失败: {}", path);
return false;
}
// 根据通道数确定格式
TextureFormat format;
switch (channels) {
case 1: format = TextureFormat::R8; break;
case 2: format = TextureFormat::RG8; break;
case 3: format = TextureFormat::RGB8; break;
case 4: format = TextureFormat::RGBA8; break;
default: format = TextureFormat::RGBA8; break;
}
bool result = loadFromMemory(data, width_, height_, format);
stbi_image_free(data);
if (result) {
E2D_LOG_DEBUG("纹理已加载: {} ({}x{})", path, width_, height_);
}
return result;
bool Texture::loadFromFile(const std::string &path) {
// 加载图片
int channels;
stbi_set_flip_vertically_on_load(true);
unsigned char *data =
stbi_load(path.c_str(), &width_, &height_, &channels, 0);
if (!data) {
E2D_ERROR("加载纹理失败: {}", path);
return false;
}
// 根据通道数确定格式
TextureFormat format;
switch (channels) {
case 1:
format = TextureFormat::R8;
break;
case 2:
format = TextureFormat::RG8;
break;
case 3:
format = TextureFormat::RGB8;
break;
case 4:
format = TextureFormat::RGBA8;
break;
default:
format = TextureFormat::RGBA8;
break;
}
bool result = loadFromMemory(data, width_, height_, format);
stbi_image_free(data);
if (result) {
E2D_DEBUG("纹理已加载: {} ({}x{})", path, width_, height_);
}
return result;
}
bool Texture::loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
// 释放旧纹理
handle_ = TextureHandle();
width_ = width;
height_ = height;
format_ = format;
// 获取 RHI 设备
auto* rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHI 模块不可用");
return false;
}
bool Texture::loadFromMemory(const uint8_t *data, int width, int height,
TextureFormat format) {
// 释放旧纹理
handle_ = TextureHandle();
auto* device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHI 设备不可用");
return false;
}
// 创建纹理描述
TextureDesc desc;
desc.width = static_cast<uint32_t>(width);
desc.height = static_cast<uint32_t>(height);
desc.format = format;
desc.mipLevels = 1; // 初始创建 1 层,后续生成 mipmap
// 创建 RHI 纹理
auto texture = device->createTexture(desc);
if (!texture) {
E2D_LOG_ERROR("创建 RHI 纹理失败");
return false;
}
// 上传纹理数据
if (data) {
// 注意update 方法的参数需要根据实际 RHI 接口调整
// 这里假设 update 接受数据指针
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
// 生成 mipmap
// 注意generateMipmap 方法需要在 RHI 接口中实现
}
// 获取纹理句柄
handle_ = TextureHandle(texture.release());
return true;
width_ = width;
height_ = height;
format_ = format;
// 获取 RHI 设备
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_ERROR("RHI 模块不可用");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_ERROR("RHI 设备不可用");
return false;
}
// 创建纹理描述
TextureDesc desc;
desc.width = static_cast<uint32_t>(width);
desc.height = static_cast<uint32_t>(height);
desc.format = format;
desc.mipLevels = 1; // 初始创建 1 层,后续生成 mipmap
// 创建 RHI 纹理
auto texture = device->createTexture(desc);
if (!texture) {
E2D_ERROR("创建 RHI 纹理失败");
return false;
}
// 上传纹理数据
if (data) {
// 注意update 方法的参数需要根据实际 RHI 接口调整
// 这里假设 update 接受数据指针
texture->update(
data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
// 生成 mipmap
// 注意generateMipmap 方法需要在 RHI 接口中实现
}
// 获取纹理句柄
handle_ = TextureHandle(texture.release());
return true;
}
bool Texture::create(int width, int height, TextureFormat format) {
// 释放旧纹理
handle_ = TextureHandle();
width_ = width;
height_ = height;
format_ = format;
// 获取 RHI 设备
auto* rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHI 模块不可用");
return false;
}
// 释放旧纹理
handle_ = TextureHandle();
auto* device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHI 设备不可用");
return false;
}
// 创建纹理描述
TextureDesc desc;
desc.width = static_cast<uint32_t>(width);
desc.height = static_cast<uint32_t>(height);
desc.format = format;
desc.mipLevels = 1; // 不生成 mipmap
// 创建 RHI 纹理
auto texture = device->createTexture(desc);
if (!texture) {
E2D_LOG_ERROR("创建 RHI 纹理失败");
return false;
}
// 获取纹理句柄
handle_ = TextureHandle(texture.release());
return true;
width_ = width;
height_ = height;
format_ = format;
// 获取 RHI 设备
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_ERROR("RHI 模块不可用");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_ERROR("RHI 设备不可用");
return false;
}
// 创建纹理描述
TextureDesc desc;
desc.width = static_cast<uint32_t>(width);
desc.height = static_cast<uint32_t>(height);
desc.format = format;
desc.mipLevels = 1; // 不生成 mipmap
// 创建 RHI 纹理
auto texture = device->createTexture(desc);
if (!texture) {
E2D_ERROR("创建 RHI 纹理失败");
return false;
}
// 获取纹理句柄
handle_ = TextureHandle(texture.release());
return true;
}
bool Texture::reloadFromFile(const std::string& path) {
// 加载图片
int channels;
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load(path.c_str(), &width_, &height_, &channels, 0);
if (!data) {
E2D_LOG_ERROR("重载纹理失败: {}", path);
return false;
}
// 根据通道数确定格式
TextureFormat format;
switch (channels) {
case 1: format = TextureFormat::R8; break;
case 2: format = TextureFormat::RG8; break;
case 3: format = TextureFormat::RGB8; break;
case 4: format = TextureFormat::RGBA8; break;
default: format = TextureFormat::RGBA8; break;
}
bool result = reloadFromMemory(data, width_, height_, format);
stbi_image_free(data);
if (result) {
E2D_LOG_INFO("纹理已重载: {} ({}x{})", path, width_, height_);
}
return result;
bool Texture::reloadFromFile(const std::string &path) {
// 加载图片
int channels;
stbi_set_flip_vertically_on_load(true);
unsigned char *data =
stbi_load(path.c_str(), &width_, &height_, &channels, 0);
if (!data) {
E2D_ERROR("重载纹理失败: {}", path);
return false;
}
// 根据通道数确定格式
TextureFormat format;
switch (channels) {
case 1:
format = TextureFormat::R8;
break;
case 2:
format = TextureFormat::RG8;
break;
case 3:
format = TextureFormat::RGB8;
break;
case 4:
format = TextureFormat::RGBA8;
break;
default:
format = TextureFormat::RGBA8;
break;
}
bool result = reloadFromMemory(data, width_, height_, format);
stbi_image_free(data);
if (result) {
E2D_INFO("纹理已重载: {} ({}x{})", path, width_, height_);
}
return result;
}
bool Texture::reloadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
// 更新尺寸和格式
width_ = width;
height_ = height;
format_ = format;
// 获取 RHI 设备
auto* rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHI 模块不可用");
return false;
}
bool Texture::reloadFromMemory(const uint8_t *data, int width, int height,
TextureFormat format) {
// 更新尺寸和格式
width_ = width;
height_ = height;
format_ = format;
auto* device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHI 设备不可用");
return false;
}
// 创建新的纹理描述
TextureDesc desc;
desc.width = static_cast<uint32_t>(width);
desc.height = static_cast<uint32_t>(height);
desc.format = format;
desc.mipLevels = 1;
// 创建新的 RHI 纹理
auto texture = device->createTexture(desc);
if (!texture) {
E2D_LOG_ERROR("重载时创建 RHI 纹理失败");
return false;
}
// 上传纹理数据
if (data) {
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
}
// 替换旧的纹理句柄
handle_ = TextureHandle(texture.release());
return true;
// 获取 RHI 设备
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_ERROR("RHI 模块不可用");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_ERROR("RHI 设备不可用");
return false;
}
// 创建新的纹理描述
TextureDesc desc;
desc.width = static_cast<uint32_t>(width);
desc.height = static_cast<uint32_t>(height);
desc.format = format;
desc.mipLevels = 1;
// 创建新的 RHI 纹理
auto texture = device->createTexture(desc);
if (!texture) {
E2D_ERROR("重载时创建 RHI 纹理失败");
return false;
}
// 上传纹理数据
if (data) {
texture->update(
data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
}
// 替换旧的纹理句柄
handle_ = TextureHandle(texture.release());
return true;
}
// 辅助函数:获取每个像素的字节数
uint32_t Texture::getBytesPerPixel(TextureFormat format) {
switch (format) {
case TextureFormat::R8: return 1;
case TextureFormat::RG8: return 2;
case TextureFormat::RGB8: return 3;
case TextureFormat::RGBA8:
case TextureFormat::RGBA8_SRGB: return 4;
case TextureFormat::Depth16: return 2;
case TextureFormat::Depth24: return 3;
case TextureFormat::Depth32F: return 4;
case TextureFormat::Depth24Stencil8: return 4;
case TextureFormat::Depth32FStencil8: return 5;
default: return 4;
}
switch (format) {
case TextureFormat::R8:
return 1;
case TextureFormat::RG8:
return 2;
case TextureFormat::RGB8:
return 3;
case TextureFormat::RGBA8:
case TextureFormat::RGBA8_SRGB:
return 4;
case TextureFormat::Depth16:
return 2;
case TextureFormat::Depth24:
return 3;
case TextureFormat::Depth32F:
return 4;
case TextureFormat::Depth24Stencil8:
return 4;
case TextureFormat::Depth32FStencil8:
return 5;
default:
return 4;
}
}
} // namespace extra2d

View File

@ -1,7 +1,7 @@
#include <renderer/texture_atlas.h>
#include <utils/logger.h>
#include <algorithm>
#include <cstring>
#include <renderer/texture_atlas.h>
#include <utils/logger.h>
namespace extra2d {
@ -11,294 +11,295 @@ namespace extra2d {
TextureAtlas::TextureAtlas() = default;
TextureAtlas::~TextureAtlas() {
shutdown();
TextureAtlas::~TextureAtlas() { shutdown(); }
TextureAtlas::TextureAtlas(TextureAtlas &&other) noexcept
: width_(other.width_), height_(other.height_),
atlasTexture_(std::move(other.atlasTexture_)),
regions_(std::move(other.regions_)),
pendingTextures_(std::move(other.pendingTextures_)),
packContext_(std::move(other.packContext_)),
packNodes_(std::move(other.packNodes_)), finalized_(other.finalized_) {
other.width_ = 0;
other.height_ = 0;
other.finalized_ = false;
}
TextureAtlas::TextureAtlas(TextureAtlas&& other) noexcept
: width_(other.width_)
, height_(other.height_)
, atlasTexture_(std::move(other.atlasTexture_))
, regions_(std::move(other.regions_))
, pendingTextures_(std::move(other.pendingTextures_))
, packContext_(std::move(other.packContext_))
, packNodes_(std::move(other.packNodes_))
, finalized_(other.finalized_) {
TextureAtlas &TextureAtlas::operator=(TextureAtlas &&other) noexcept {
if (this != &other) {
shutdown();
width_ = other.width_;
height_ = other.height_;
atlasTexture_ = std::move(other.atlasTexture_);
regions_ = std::move(other.regions_);
pendingTextures_ = std::move(other.pendingTextures_);
packContext_ = std::move(other.packContext_);
packNodes_ = std::move(other.packNodes_);
finalized_ = other.finalized_;
other.width_ = 0;
other.height_ = 0;
other.finalized_ = false;
}
TextureAtlas& TextureAtlas::operator=(TextureAtlas&& other) noexcept {
if (this != &other) {
shutdown();
width_ = other.width_;
height_ = other.height_;
atlasTexture_ = std::move(other.atlasTexture_);
regions_ = std::move(other.regions_);
pendingTextures_ = std::move(other.pendingTextures_);
packContext_ = std::move(other.packContext_);
packNodes_ = std::move(other.packNodes_);
finalized_ = other.finalized_;
other.width_ = 0;
other.height_ = 0;
other.finalized_ = false;
}
return *this;
}
return *this;
}
bool TextureAtlas::initialize(int width, int height) {
shutdown();
if (width <= 0 || height <= 0) {
E2D_LOG_ERROR("TextureAtlas::initialize: 无效的尺寸 {}x{}", width, height);
return false;
}
width_ = width;
height_ = height;
// 初始化 stb_rect_pack
packContext_ = std::make_unique<stbrp_context>();
packNodes_.resize(width);
stbrp_init_target(packContext_.get(), width, height, packNodes_.data(), width);
E2D_LOG_DEBUG("纹理图集已初始化: {}x{}", width, height);
return true;
shutdown();
if (width <= 0 || height <= 0) {
E2D_ERROR("TextureAtlas::initialize: 无效的尺寸 {}x{}", width, height);
return false;
}
width_ = width;
height_ = height;
// 初始化 stb_rect_pack
packContext_ = std::make_unique<stbrp_context>();
packNodes_.resize(width);
stbrp_init_target(packContext_.get(), width, height, packNodes_.data(),
width);
E2D_DEBUG("纹理图集已初始化: {}x{}", width, height);
return true;
}
void TextureAtlas::shutdown() {
atlasTexture_.reset();
regions_.clear();
pendingTextures_.clear();
packContext_.reset();
packNodes_.clear();
width_ = 0;
height_ = 0;
finalized_ = false;
atlasTexture_.reset();
regions_.clear();
pendingTextures_.clear();
packContext_.reset();
packNodes_.clear();
width_ = 0;
height_ = 0;
finalized_ = false;
}
bool TextureAtlas::addTexture(const std::string& name, Ptr<Texture> texture) {
if (finalized_) {
E2D_LOG_WARN("TextureAtlas::addTexture: 无法向已完成的图集添加纹理");
return false;
}
bool TextureAtlas::addTexture(const std::string &name, Ptr<Texture> texture) {
if (finalized_) {
E2D_WARN("TextureAtlas::addTexture: 无法向已完成的图集添加纹理");
return false;
}
if (!texture) {
E2D_LOG_WARN("TextureAtlas::addTexture: 纹理为空");
return false;
}
if (!texture) {
E2D_WARN("TextureAtlas::addTexture: 纹理为空");
return false;
}
if (regions_.find(name) != regions_.end()) {
E2D_LOG_WARN("TextureAtlas::addTexture: 纹理 '{}' 已存在", name);
return false;
}
if (regions_.find(name) != regions_.end()) {
E2D_WARN("TextureAtlas::addTexture: 纹理 '{}' 已存在", name);
return false;
}
// 获取纹理尺寸
int texWidth = static_cast<int>(texture->getWidth());
int texHeight = static_cast<int>(texture->getHeight());
// 获取纹理尺寸
int texWidth = static_cast<int>(texture->getWidth());
int texHeight = static_cast<int>(texture->getHeight());
if (texWidth <= 0 || texHeight <= 0) {
E2D_LOG_WARN("TextureAtlas::addTexture: 无效的纹理尺寸 {}x{}", texWidth, texHeight);
return false;
}
if (texWidth <= 0 || texHeight <= 0) {
E2D_WARN("TextureAtlas::addTexture: 无效的纹理尺寸 {}x{}", texWidth,
texHeight);
return false;
}
// 检查纹理是否适合图集
if (texWidth > width_ || texHeight > height_) {
E2D_LOG_WARN("TextureAtlas::addTexture: 纹理 {}x{} 太大,无法放入图集 {}x{}",
texWidth, texHeight, width_, height_);
return false;
}
// 创建待处理纹理信息
PendingTexture pending;
pending.name = name;
pending.width = texWidth;
pending.height = texHeight;
pending.format = texture->getFormat();
// 获取纹理数据
size_t dataSize = texWidth * texHeight * 4; // 假设 RGBA
pending.data.resize(dataSize);
// TODO: 实现 Texture::getData() 方法来读取像素数据
// 暂时使用占位数据
std::fill(pending.data.begin(), pending.data.end(), 255);
pendingTextures_.push_back(std::move(pending));
E2D_LOG_DEBUG("纹理图集: 已添加纹理 '{}' ({}x{})", name, texWidth, texHeight);
return true;
// 检查纹理是否适合图集
if (texWidth > width_ || texHeight > height_) {
E2D_WARN("TextureAtlas::addTexture: 纹理 {}x{} 太大,无法放入图集 {}x{}",
texWidth, texHeight, width_, height_);
return false;
}
// 创建待处理纹理信息
PendingTexture pending;
pending.name = name;
pending.width = texWidth;
pending.height = texHeight;
pending.format = texture->getFormat();
// 获取纹理数据
size_t dataSize = texWidth * texHeight * 4; // 假设 RGBA
pending.data.resize(dataSize);
// TODO: 实现 Texture::getData() 方法来读取像素数据
// 暂时使用占位数据
std::fill(pending.data.begin(), pending.data.end(), 255);
pendingTextures_.push_back(std::move(pending));
E2D_DEBUG("纹理图集: 已添加纹理 '{}' ({}x{})", name, texWidth, texHeight);
return true;
}
bool TextureAtlas::addTextureData(const std::string& name, const uint8_t* data,
bool TextureAtlas::addTextureData(const std::string &name, const uint8_t *data,
int width, int height, TextureFormat format) {
if (finalized_) {
E2D_LOG_WARN("TextureAtlas::addTextureData: 无法向已完成的图集添加纹理");
return false;
}
if (finalized_) {
E2D_WARN("TextureAtlas::addTextureData: 无法向已完成的图集添加纹理");
return false;
}
if (!data || width <= 0 || height <= 0) {
E2D_LOG_WARN("TextureAtlas::addTextureData: 无效参数");
return false;
}
if (!data || width <= 0 || height <= 0) {
E2D_WARN("TextureAtlas::addTextureData: 无效参数");
return false;
}
if (regions_.find(name) != regions_.end()) {
E2D_LOG_WARN("TextureAtlas::addTextureData: 纹理 '{}' 已存在", name);
return false;
}
if (regions_.find(name) != regions_.end()) {
E2D_WARN("TextureAtlas::addTextureData: 纹理 '{}' 已存在", name);
return false;
}
if (width > width_ || height > height_) {
E2D_LOG_WARN("TextureAtlas::addTextureData: 纹理 {}x{} 太大,无法放入图集 {}x{}",
width, height, width_, height_);
return false;
}
PendingTexture pending;
pending.name = name;
pending.width = width;
pending.height = height;
pending.format = format;
// 复制像素数据
int channels = (format == TextureFormat::RGBA8) ? 4 : 3;
size_t dataSize = width * height * channels;
pending.data.resize(dataSize);
std::memcpy(pending.data.data(), data, dataSize);
pendingTextures_.push_back(std::move(pending));
E2D_LOG_DEBUG("纹理图集: 已添加纹理数据 '{}' ({}x{})", name, width, height);
return true;
if (width > width_ || height > height_) {
E2D_WARN(
"TextureAtlas::addTextureData: 纹理 {}x{} 太大,无法放入图集 {}x{}",
width, height, width_, height_);
return false;
}
PendingTexture pending;
pending.name = name;
pending.width = width;
pending.height = height;
pending.format = format;
// 复制像素数据
int channels = (format == TextureFormat::RGBA8) ? 4 : 3;
size_t dataSize = width * height * channels;
pending.data.resize(dataSize);
std::memcpy(pending.data.data(), data, dataSize);
pendingTextures_.push_back(std::move(pending));
E2D_DEBUG("纹理图集: 已添加纹理数据 '{}' ({}x{})", name, width, height);
return true;
}
bool TextureAtlas::finalize() {
if (finalized_) {
return true;
}
if (pendingTextures_.empty()) {
E2D_LOG_WARN("TextureAtlas::finalize: 没有要打包的纹理");
return false;
}
// 准备矩形数组
std::vector<stbrp_rect> rects;
rects.reserve(pendingTextures_.size());
for (size_t i = 0; i < pendingTextures_.size(); ++i) {
stbrp_rect rect;
rect.id = static_cast<int>(i);
rect.w = pendingTextures_[i].width;
rect.h = pendingTextures_[i].height;
rect.x = 0;
rect.y = 0;
rect.was_packed = 0;
rects.push_back(rect);
}
// 执行打包
int result = stbrp_pack_rects(packContext_.get(), rects.data(), static_cast<int>(rects.size()));
if (!result) {
E2D_LOG_ERROR("TextureAtlas::finalize: 打包所有纹理失败");
return false;
}
// 创建图集纹理数据
std::vector<uint8_t> atlasData(width_ * height_ * 4, 0); // RGBA 黑色背景
// 处理打包结果
for (const auto& rect : rects) {
if (!rect.was_packed) {
E2D_LOG_WARN("TextureAtlas::finalize: 纹理 {} 未被打包", rect.id);
continue;
}
const auto& pending = pendingTextures_[rect.id];
// 创建区域信息
AtlasRegion region;
region.x = rect.x;
region.y = rect.y;
region.width = rect.w;
region.height = rect.h;
regions_[pending.name] = region;
// 复制像素数据到图集
// TODO: 实现正确的像素格式转换
// 目前假设所有纹理都是 RGBA8
for (int y = 0; y < pending.height; ++y) {
for (int x = 0; x < pending.width; ++x) {
int srcIdx = (y * pending.width + x) * 4;
int dstIdx = ((rect.y + y) * width_ + (rect.x + x)) * 4;
if (srcIdx + 3 < static_cast<int>(pending.data.size()) &&
dstIdx + 3 < static_cast<int>(atlasData.size())) {
atlasData[dstIdx + 0] = pending.data[srcIdx + 0];
atlasData[dstIdx + 1] = pending.data[srcIdx + 1];
atlasData[dstIdx + 2] = pending.data[srcIdx + 2];
atlasData[dstIdx + 3] = pending.data[srcIdx + 3];
}
}
}
E2D_LOG_DEBUG("纹理图集: 已打包 '{}' 在 ({}, {}) 尺寸 {}x{}",
pending.name, rect.x, rect.y, rect.w, rect.h);
}
// 创建图集纹理
atlasTexture_ = makePtr<Texture>();
if (!atlasTexture_->loadFromMemory(atlasData.data(), width_, height_, TextureFormat::RGBA8)) {
E2D_LOG_ERROR("TextureAtlas::finalize: 创建图集纹理失败");
return false;
}
// 清理待处理列表
pendingTextures_.clear();
finalized_ = true;
E2D_LOG_INFO("纹理图集完成: {} 个纹理打包到 {}x{} 图集 (使用率 {}%)",
regions_.size(), width_, height_,
static_cast<int>(getUsageRatio() * 100));
if (finalized_) {
return true;
}
}
const AtlasRegion* TextureAtlas::getRegion(const std::string& name) const {
auto it = regions_.find(name);
if (it != regions_.end()) {
return &it->second;
if (pendingTextures_.empty()) {
E2D_WARN("TextureAtlas::finalize: 没有要打包的纹理");
return false;
}
// 准备矩形数组
std::vector<stbrp_rect> rects;
rects.reserve(pendingTextures_.size());
for (size_t i = 0; i < pendingTextures_.size(); ++i) {
stbrp_rect rect;
rect.id = static_cast<int>(i);
rect.w = pendingTextures_[i].width;
rect.h = pendingTextures_[i].height;
rect.x = 0;
rect.y = 0;
rect.was_packed = 0;
rects.push_back(rect);
}
// 执行打包
int result = stbrp_pack_rects(packContext_.get(), rects.data(),
static_cast<int>(rects.size()));
if (!result) {
E2D_ERROR("TextureAtlas::finalize: 打包所有纹理失败");
return false;
}
// 创建图集纹理数据
std::vector<uint8_t> atlasData(width_ * height_ * 4, 0); // RGBA 黑色背景
// 处理打包结果
for (const auto &rect : rects) {
if (!rect.was_packed) {
E2D_WARN("TextureAtlas::finalize: 纹理 {} 未被打包", rect.id);
continue;
}
return nullptr;
}
Rect TextureAtlas::getUVRect(const std::string& name) const {
const AtlasRegion* region = getRegion(name);
if (region) {
return region->getUVRect(width_, height_);
const auto &pending = pendingTextures_[rect.id];
// 创建区域信息
AtlasRegion region;
region.x = rect.x;
region.y = rect.y;
region.width = rect.w;
region.height = rect.h;
regions_[pending.name] = region;
// 复制像素数据到图集
// TODO: 实现正确的像素格式转换
// 目前假设所有纹理都是 RGBA8
for (int y = 0; y < pending.height; ++y) {
for (int x = 0; x < pending.width; ++x) {
int srcIdx = (y * pending.width + x) * 4;
int dstIdx = ((rect.y + y) * width_ + (rect.x + x)) * 4;
if (srcIdx + 3 < static_cast<int>(pending.data.size()) &&
dstIdx + 3 < static_cast<int>(atlasData.size())) {
atlasData[dstIdx + 0] = pending.data[srcIdx + 0];
atlasData[dstIdx + 1] = pending.data[srcIdx + 1];
atlasData[dstIdx + 2] = pending.data[srcIdx + 2];
atlasData[dstIdx + 3] = pending.data[srcIdx + 3];
}
}
}
return Rect(0.0f, 0.0f, 1.0f, 1.0f);
E2D_DEBUG("纹理图集: 已打包 '{}' 在 ({}, {}) 尺寸 {}x{}", pending.name,
rect.x, rect.y, rect.w, rect.h);
}
// 创建图集纹理
atlasTexture_ = makePtr<Texture>();
if (!atlasTexture_->loadFromMemory(atlasData.data(), width_, height_,
TextureFormat::RGBA8)) {
E2D_ERROR("TextureAtlas::finalize: 创建图集纹理失败");
return false;
}
// 清理待处理列表
pendingTextures_.clear();
finalized_ = true;
E2D_INFO("纹理图集完成: {} 个纹理打包到 {}x{} 图集 (使用率 {}%)",
regions_.size(), width_, height_,
static_cast<int>(getUsageRatio() * 100));
return true;
}
bool TextureAtlas::hasTexture(const std::string& name) const {
return regions_.find(name) != regions_.end();
const AtlasRegion *TextureAtlas::getRegion(const std::string &name) const {
auto it = regions_.find(name);
if (it != regions_.end()) {
return &it->second;
}
return nullptr;
}
Rect TextureAtlas::getUVRect(const std::string &name) const {
const AtlasRegion *region = getRegion(name);
if (region) {
return region->getUVRect(width_, height_);
}
return Rect(0.0f, 0.0f, 1.0f, 1.0f);
}
bool TextureAtlas::hasTexture(const std::string &name) const {
return regions_.find(name) != regions_.end();
}
float TextureAtlas::getUsageRatio() const {
if (!finalized_ || regions_.empty()) {
return 0.0f;
}
int totalArea = 0;
for (const auto& pair : regions_) {
totalArea += pair.second.width * pair.second.height;
}
return static_cast<float>(totalArea) / (width_ * height_);
if (!finalized_ || regions_.empty()) {
return 0.0f;
}
int totalArea = 0;
for (const auto &pair : regions_) {
totalArea += pair.second.width * pair.second.height;
}
return static_cast<float>(totalArea) / (width_ * height_);
}
// ========================================
@ -306,62 +307,63 @@ float TextureAtlas::getUsageRatio() const {
// ========================================
Ptr<TextureAtlas> AtlasBuilder::build() {
if (textures_.empty()) {
E2D_LOG_WARN("AtlasBuilder::build: No textures to build");
return nullptr;
}
auto atlas = makePtr<TextureAtlas>();
if (!atlas->initialize(width_, height_)) {
return nullptr;
}
for (const auto& pair : textures_) {
atlas->addTexture(pair.first, pair.second);
}
if (!atlas->finalize()) {
return nullptr;
}
return atlas;
if (textures_.empty()) {
E2D_WARN("AtlasBuilder::build: No textures to build");
return nullptr;
}
auto atlas = makePtr<TextureAtlas>();
if (!atlas->initialize(width_, height_)) {
return nullptr;
}
for (const auto &pair : textures_) {
atlas->addTexture(pair.first, pair.second);
}
if (!atlas->finalize()) {
return nullptr;
}
return atlas;
}
Ptr<TextureAtlas> AtlasBuilder::buildAuto() {
if (textures_.empty()) {
E2D_LOG_WARN("AtlasBuilder::buildAuto: No textures to build");
return nullptr;
if (textures_.empty()) {
E2D_WARN("AtlasBuilder::buildAuto: No textures to build");
return nullptr;
}
// 计算总面积
int totalArea = 0;
int maxWidth = 0;
int maxHeight = 0;
for (const auto &pair : textures_) {
if (pair.second) {
int w = static_cast<int>(pair.second->getWidth());
int h = static_cast<int>(pair.second->getHeight());
totalArea += w * h;
maxWidth = std::max(maxWidth, w);
maxHeight = std::max(maxHeight, h);
}
// 计算总面积
int totalArea = 0;
int maxWidth = 0;
int maxHeight = 0;
for (const auto& pair : textures_) {
if (pair.second) {
int w = static_cast<int>(pair.second->getWidth());
int h = static_cast<int>(pair.second->getHeight());
totalArea += w * h;
maxWidth = std::max(maxWidth, w);
maxHeight = std::max(maxHeight, h);
}
}
// 选择合适的大小2 的幂)
int atlasSize = 64;
while (atlasSize < maxWidth || atlasSize < maxHeight ||
atlasSize * atlasSize < totalArea * 1.5f) {
atlasSize *= 2;
if (atlasSize > 8192) {
E2D_ERROR("AtlasBuilder::buildAuto: Textures too large for atlas");
return nullptr;
}
// 选择合适的大小2 的幂)
int atlasSize = 64;
while (atlasSize < maxWidth || atlasSize < maxHeight || atlasSize * atlasSize < totalArea * 1.5f) {
atlasSize *= 2;
if (atlasSize > 8192) {
E2D_LOG_ERROR("AtlasBuilder::buildAuto: Textures too large for atlas");
return nullptr;
}
}
width_ = atlasSize;
height_ = atlasSize;
return build();
}
width_ = atlasSize;
height_ = atlasSize;
return build();
}
} // namespace extra2d

View File

@ -15,13 +15,13 @@ bool UniformBuffer::create(uint32_t size, uint32_t binding) {
// 获取 RHI 设备
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHI 模块不可用");
E2D_ERROR("RHI 模块不可用");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHI 设备不可用");
E2D_ERROR("RHI 设备不可用");
return false;
}
@ -34,14 +34,14 @@ bool UniformBuffer::create(uint32_t size, uint32_t binding) {
// 创建缓冲区
auto buffer = device->createBuffer(desc);
if (!buffer) {
E2D_LOG_ERROR("创建 uniform 缓冲区失败");
E2D_ERROR("创建 uniform 缓冲区失败");
return false;
}
// 获取缓冲区句柄
handle_ = BufferHandle(buffer.release());
E2D_LOG_DEBUG("UBO 已创建: 大小={}, 绑定槽={}", size, binding);
E2D_DEBUG("UBO 已创建: 大小={}, 绑定槽={}", size, binding);
return true;
}
@ -57,8 +57,8 @@ void UniformBuffer::update(const void *data, uint32_t size, uint32_t offset) {
return;
if (offset + size > size_) {
E2D_LOG_WARN("UBO 更新超出边界: 偏移={}, 大小={}, 缓冲区大小={}",
offset, size, size_);
E2D_WARN("UBO 更新超出边界: 偏移={}, 大小={}, 缓冲区大小={}", offset, size,
size_);
return;
}
@ -123,7 +123,7 @@ bool UniformBufferManager::initialize() {
globalUBOs_[i] = std::make_unique<UniformBuffer>();
if (!globalUBOs_[i]->create(UniformBufferManager::GLOBAL_UBO_SIZE,
UniformBufferManager::GLOBAL_UBO_BINDING)) {
E2D_LOG_ERROR("创建全局 UBO {} 失败", i);
E2D_ERROR("创建全局 UBO {} 失败", i);
return false;
}
}
@ -134,13 +134,13 @@ bool UniformBufferManager::initialize() {
// 预分配材质 UBO CPU 缓冲区
materialUBOBuffer_.resize(MATERIAL_UBO_BUFFER_SIZE);
E2D_LOG_INFO("UniformBufferManager 已初始化,使用双缓冲");
E2D_INFO("UniformBufferManager 已初始化,使用双缓冲");
return true;
}
void UniformBufferManager::shutdown() {
materialUBOPool_.clear();
for (auto& ubo : globalUBOs_) {
for (auto &ubo : globalUBOs_) {
ubo.reset();
}
currentUBOIndex_ = 0;
@ -148,7 +148,7 @@ void UniformBufferManager::shutdown() {
materialUBOBufferOffset_ = 0;
currentMaterialUBO_ = nullptr;
E2D_LOG_INFO("UniformBufferManager 已关闭");
E2D_INFO("UniformBufferManager 已关闭");
}
UniformBuffer *UniformBufferManager::getGlobalUBO(uint32_t frameIndex) {
@ -169,7 +169,7 @@ UniformBuffer *UniformBufferManager::acquireMaterialUBO(uint32_t size) {
// 需要创建新的 UBO
auto ubo = std::make_unique<UniformBuffer>();
if (!ubo->create(size, UniformBufferManager::MATERIAL_UBO_BINDING)) {
E2D_LOG_ERROR("创建材质 UBO 失败");
E2D_ERROR("创建材质 UBO 失败");
return nullptr;
}
@ -186,7 +186,8 @@ void UniformBufferManager::resetMaterialUBOs() {
currentMaterialUBO_ = nullptr;
}
void UniformBufferManager::updateGlobalUBO(const void *data, uint32_t size, uint32_t frameIndex) {
void UniformBufferManager::updateGlobalUBO(const void *data, uint32_t size,
uint32_t frameIndex) {
// 使用双缓冲更新全局 UBO
size_t index = frameIndex % 2;
if (globalUBOs_[index]) {
@ -195,9 +196,11 @@ void UniformBufferManager::updateGlobalUBO(const void *data, uint32_t size, uint
}
}
void UniformBufferManager::batchUpdateMaterialUBO(const void *data, uint32_t size, uint32_t offset) {
void UniformBufferManager::batchUpdateMaterialUBO(const void *data,
uint32_t size,
uint32_t offset) {
if (!data || size == 0 || offset + size > materialUBOBuffer_.size()) {
E2D_LOG_WARN("无效的批量更新参数");
E2D_WARN("无效的批量更新参数");
return;
}
@ -211,7 +214,8 @@ void UniformBufferManager::flushMaterialUBO() {
}
// 一次性将 CPU 缓冲区数据更新到 GPU
currentMaterialUBO_->update(materialUBOBuffer_.data(), materialUBOBufferOffset_, 0);
currentMaterialUBO_->update(materialUBOBuffer_.data(),
materialUBOBufferOffset_, 0);
materialUBOBufferOffset_ = 0;
}

View File

@ -47,8 +47,8 @@ void CameraComponent::setOrtho(float left, float right, float bottom, float top,
far_ = far;
markProjDirty();
E2D_LOG_DEBUG("正交投影已设置: 左={}, 右={}, 下={}, 上={}, 近={}, 远={}",
left, right, bottom, top, near, far);
E2D_DEBUG("正交投影已设置: 左={}, 右={}, 下={}, 上={}, 近={}, 远={}", left,
right, bottom, top, near, far);
}
/**

View File

@ -132,7 +132,7 @@ void Director::render() {
Mat4 viewProj = camera->getViewProjectionMatrix();
events::OnRenderSetCamera::emit(viewProj);
} else {
E2D_LOG_WARN("Director::render: 未设置主相机!");
E2D_WARN("Director::render: 未设置主相机!");
}
runningScene_->render();

View File

@ -1,65 +1,25 @@
#include <utils/logger.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#include <windows.h>
#endif
namespace extra2d {
// 静态成员定义
LogLevel Logger::level_ = LogLevel::Info;
std::shared_ptr<spdlog::logger> Logger::logger_ = nullptr;
bool Logger::initialized_ = false;
bool Logger::consoleOutput_ = true;
bool Logger::fileOutput_ = false;
std::string Logger::logFile_;
// Switch 平台:自定义 SDL 日志输出函数
#ifdef __SWITCH__
/**
* @brief Switch
* @param userdata
* @param category
* @param priority
* @param message
*/
static void SwitchLogOutput(void *userdata, int category,
SDL_LogPriority priority, const char *message) {
(void)userdata;
(void)category;
(void)priority;
// 输出到 Switch 控制台
printf("%s\n", message);
}
#endif
/**
* @brief
* @param level
* @return
*/
const char *Logger::getLevelString(LogLevel level) {
switch (level) {
case LogLevel::Trace:
return "跟踪";
case LogLevel::Debug:
return "调试";
case LogLevel::Info:
return "信息";
case LogLevel::Warn:
return "警告";
case LogLevel::Error:
return "错误";
case LogLevel::Fatal:
return "致命";
default:
return "未知";
}
}
/**
* @brief
*
* Windows Nintendo Switch Switch
* / spdlog
*/
void Logger::init() {
if (initialized_) {
@ -67,41 +27,59 @@ void Logger::init() {
}
#ifdef _WIN32
// 获取标准输出句柄
HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsoleOut == INVALID_HANDLE_VALUE) {
return;
}
// 设置控制台输出代码页为UTF-8 (65001是UTF-8的代码页编号)
SetConsoleOutputCP(CP_UTF8);
// 设置控制台模式确保支持UTF-8输出可选但能提升兼容性
DWORD consoleMode;
GetConsoleMode(hConsoleOut, &consoleMode);
consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; // 启用ANSI转义序列可选
SetConsoleMode(hConsoleOut, consoleMode);
#endif
#ifdef __SWITCH__
// Switch 平台:初始化控制台并设置 SDL 日志重定向
consoleInit(NULL);
SDL_LogSetOutputFunction(SwitchLogOutput, nullptr);
#else
// 其他平台:设置 SDL 日志级别为详细模式
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE);
#endif
std::vector<spdlog::sink_ptr> sinks;
if (consoleOutput_) {
auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
consoleSink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");
sinks.push_back(consoleSink);
}
if (fileOutput_ && !logFile_.empty()) {
auto fileSink =
std::make_shared<spdlog::sinks::basic_file_sink_mt>(logFile_, true);
fileSink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
sinks.push_back(fileSink);
}
if (sinks.empty()) {
auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
consoleSink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");
sinks.push_back(consoleSink);
}
logger_ =
std::make_shared<spdlog::logger>("extra2d", sinks.begin(), sinks.end());
logger_->set_level(spdlog::level::trace);
logger_->flush_on(spdlog::level::warn);
spdlog::register_logger(logger_);
spdlog::set_default_logger(logger_);
// 注册后重新设置日志级别,防止被 registry 的全局级别覆盖
logger_->set_level(spdlog::level::trace);
initialized_ = true;
log(LogLevel::Info, "日志系统已初始化");
}
/**
* @brief
*
* spdlog Nintendo Switch
*/
void Logger::shutdown() {
if (initialized_) {
log(LogLevel::Info, "日志系统正在关闭");
spdlog::shutdown();
logger_.reset();
#ifdef __SWITCH__
consoleExit(NULL);
#endif
@ -111,45 +89,53 @@ void Logger::shutdown() {
/**
* @brief
* @param level
* @param level
*
*
*/
void Logger::setLevel(LogLevel level) {
level_ = level;
// 同时设置 SDL 的日志级别
if (level != LogLevel::Off) {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level));
if (logger_) {
logger_->set_level(static_cast<spdlog::level::level_enum>(level));
}
}
/**
* @brief
* @param enable
* @brief
* @param enable true false
*/
void Logger::setConsoleOutput(bool enable) {
consoleOutput_ = enable;
// SDL2 日志默认输出到控制台,通过设置日志优先级控制
if (!enable) {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_CRITICAL);
} else {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION,
static_cast<SDL_LogPriority>(level_));
}
}
void Logger::setConsoleOutput(bool enable) { consoleOutput_ = enable; }
/**
* @brief
* @param filename
* @brief
* @param filename
*/
void Logger::setFileOutput(const std::string &filename) {
logFile_ = filename;
fileOutput_ = !filename.empty();
}
if (fileOutput_) {
// SDL2 使用 SDL_LogSetOutputFunction 可以重定向日志输出
// 这里我们记录文件路径,实际文件输出可以通过自定义回调实现
log(LogLevel::Info, "文件输出已配置: {}", filename);
/**
* @brief
* @return Info
*/
LogLevel Logger::getLevel() {
if (logger_) {
return static_cast<LogLevel>(logger_->level());
}
return LogLevel::Info;
}
/**
* @brief
* @param level
* @param msg
*/
void Logger::log(LogLevel level, const char *msg) {
if (!logger_) {
return;
}
auto spdLevel = static_cast<spdlog::level::level_enum>(level);
logger_->log(spdLevel, msg);
}
} // namespace extra2d

View File

@ -15,10 +15,14 @@ function define_extra2d_engine()
add_files("src/**.cpp")
add_files("third_party/glad/src/glad.c")
add_files("third_party/spdlog/src/*.cpp")
add_includedirs("include", {public = true})
add_includedirs("third_party", {public = true})
add_includedirs("third_party/glad/include", {public = true})
add_includedirs("third_party/spdlog/include", {public = true})
add_defines("SPDLOG_COMPILED_LIB", {public = true})
local plat = get_current_plat()
if plat == "mingw" then
@ -47,7 +51,7 @@ function define_extra2d_engine()
add_cxxflags("-Wno-deprecated-copy", "-Wno-class-memaccess", {force = true})
if is_mode("debug") then
add_defines("E2D_DEBUG", "_DEBUG", {public = true})
add_defines("_DEBUG", {public = true})
add_cxxflags("-O0", "-g", {force = true})
else
add_defines("NDEBUG", {public = true})