refactor(logger): 替换日志系统为spdlog实现
重构日志系统,使用spdlog替代原有实现,提供更强大的格式化功能和性能优化 移除自定义日志格式化和级别处理代码,统一使用spdlog接口 更新所有日志调用点,移除E2D_LOG_前缀宏,简化为E2D_前缀 添加spdlog为第三方依赖,更新构建配置
This commit is contained in:
parent
8864637459
commit
579aa2dd0d
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git diff:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
|
|
|||
|
|
@ -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("应用程序已恢复");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,这里不需要手动触发
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ¶ms_[mapIt->second].second;
|
||||
}
|
||||
return nullptr;
|
||||
const MaterialParamInfo *
|
||||
MaterialLayout::getParam(const std::string &name) const {
|
||||
auto mapIt = paramIndexMap_.find(name);
|
||||
if (mapIt != paramIndexMap_.end()) {
|
||||
return ¶ms_[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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
Loading…
Reference in New Issue