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
|
#pragma once
|
||||||
|
|
||||||
#include <renderer/rhi/rhi.h>
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
#include <renderer/rhi/rhi.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -9,30 +9,41 @@ namespace extra2d {
|
||||||
/**
|
/**
|
||||||
* @brief OpenGL 错误检查宏
|
* @brief OpenGL 错误检查宏
|
||||||
*/
|
*/
|
||||||
#define GL_CHECK(call) do { \
|
#define GL_CHECK(call) \
|
||||||
|
do { \
|
||||||
call; \
|
call; \
|
||||||
GLenum err = glGetError(); \
|
GLenum err = glGetError(); \
|
||||||
if (err != GL_NO_ERROR) { \
|
if (err != GL_NO_ERROR) { \
|
||||||
E2D_LOG_ERROR("OpenGL 错误在 {}: {} ({})", #call, getGLErrorString(err), err); \
|
E2D_ERROR("OpenGL 错误在 {}: {} ({})", #call, getGLErrorString(err), \
|
||||||
|
err); \
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while (0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取 OpenGL 错误字符串
|
* @brief 获取 OpenGL 错误字符串
|
||||||
* @param error OpenGL 错误码
|
* @param error OpenGL 错误码
|
||||||
* @return 错误描述字符串
|
* @return 错误描述字符串
|
||||||
*/
|
*/
|
||||||
inline const char* getGLErrorString(GLenum error) {
|
inline const char *getGLErrorString(GLenum error) {
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case GL_NO_ERROR: return "GL_NO_ERROR";
|
case GL_NO_ERROR:
|
||||||
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
|
return "GL_NO_ERROR";
|
||||||
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
|
case GL_INVALID_ENUM:
|
||||||
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
|
return "GL_INVALID_ENUM";
|
||||||
case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW";
|
case GL_INVALID_VALUE:
|
||||||
case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW";
|
return "GL_INVALID_VALUE";
|
||||||
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
|
case GL_INVALID_OPERATION:
|
||||||
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
|
return "GL_INVALID_OPERATION";
|
||||||
default: return "Unknown Error";
|
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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,12 +52,18 @@ inline const char* getGLErrorString(GLenum error) {
|
||||||
*/
|
*/
|
||||||
inline GLenum bufferTypeToGL(BufferType type) {
|
inline GLenum bufferTypeToGL(BufferType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BufferType::Vertex: return GL_ARRAY_BUFFER;
|
case BufferType::Vertex:
|
||||||
case BufferType::Index: return GL_ELEMENT_ARRAY_BUFFER;
|
return GL_ARRAY_BUFFER;
|
||||||
case BufferType::Uniform: return GL_UNIFORM_BUFFER;
|
case BufferType::Index:
|
||||||
case BufferType::Storage: return GL_SHADER_STORAGE_BUFFER;
|
return GL_ELEMENT_ARRAY_BUFFER;
|
||||||
case BufferType::Staging: return GL_ARRAY_BUFFER;
|
case BufferType::Uniform:
|
||||||
default: return GL_ARRAY_BUFFER;
|
return GL_UNIFORM_BUFFER;
|
||||||
|
case BufferType::Storage:
|
||||||
|
return GL_SHADER_STORAGE_BUFFER;
|
||||||
|
case BufferType::Staging:
|
||||||
|
return GL_ARRAY_BUFFER;
|
||||||
|
default:
|
||||||
|
return GL_ARRAY_BUFFER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,10 +72,14 @@ inline GLenum bufferTypeToGL(BufferType type) {
|
||||||
*/
|
*/
|
||||||
inline GLenum bufferUsageToGL(BufferUsage usage) {
|
inline GLenum bufferUsageToGL(BufferUsage usage) {
|
||||||
switch (usage) {
|
switch (usage) {
|
||||||
case BufferUsage::Static: return GL_STATIC_DRAW;
|
case BufferUsage::Static:
|
||||||
case BufferUsage::Dynamic: return GL_DYNAMIC_DRAW;
|
return GL_STATIC_DRAW;
|
||||||
case BufferUsage::Stream: return GL_STREAM_DRAW;
|
case BufferUsage::Dynamic:
|
||||||
default: return GL_STATIC_DRAW;
|
return GL_DYNAMIC_DRAW;
|
||||||
|
case BufferUsage::Stream:
|
||||||
|
return GL_STREAM_DRAW;
|
||||||
|
default:
|
||||||
|
return GL_STATIC_DRAW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,20 +88,34 @@ inline GLenum bufferUsageToGL(BufferUsage usage) {
|
||||||
*/
|
*/
|
||||||
inline GLenum textureFormatToGLInternal(TextureFormat format) {
|
inline GLenum textureFormatToGLInternal(TextureFormat format) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case TextureFormat::R8: return GL_R8;
|
case TextureFormat::R8:
|
||||||
case TextureFormat::RG8: return GL_RG8;
|
return GL_R8;
|
||||||
case TextureFormat::RGB8: return GL_RGB8;
|
case TextureFormat::RG8:
|
||||||
case TextureFormat::RGBA8: return GL_RGBA8;
|
return GL_RG8;
|
||||||
case TextureFormat::RGBA8_SRGB: return GL_SRGB8_ALPHA8;
|
case TextureFormat::RGB8:
|
||||||
case TextureFormat::Depth16: return GL_DEPTH_COMPONENT16;
|
return GL_RGB8;
|
||||||
case TextureFormat::Depth24: return GL_DEPTH_COMPONENT24;
|
case TextureFormat::RGBA8:
|
||||||
case TextureFormat::Depth32F: return GL_DEPTH_COMPONENT32F;
|
return GL_RGBA8;
|
||||||
case TextureFormat::Depth24Stencil8: return GL_DEPTH24_STENCIL8;
|
case TextureFormat::RGBA8_SRGB:
|
||||||
case TextureFormat::Depth32FStencil8:return GL_DEPTH32F_STENCIL8;
|
return GL_SRGB8_ALPHA8;
|
||||||
case TextureFormat::BC1: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
case TextureFormat::Depth16:
|
||||||
case TextureFormat::BC3: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
return GL_DEPTH_COMPONENT16;
|
||||||
case TextureFormat::BC5: return 0x8DBD; // GL_COMPRESSED_RG_RGTC2
|
case TextureFormat::Depth24:
|
||||||
default: return GL_RGBA8;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,17 +124,24 @@ inline GLenum textureFormatToGLInternal(TextureFormat format) {
|
||||||
*/
|
*/
|
||||||
inline GLenum textureFormatToGLFormat(TextureFormat format) {
|
inline GLenum textureFormatToGLFormat(TextureFormat format) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case TextureFormat::R8: return GL_RED;
|
case TextureFormat::R8:
|
||||||
case TextureFormat::RG8: return GL_RG;
|
return GL_RED;
|
||||||
case TextureFormat::RGB8: return GL_RGB;
|
case TextureFormat::RG8:
|
||||||
|
return GL_RG;
|
||||||
|
case TextureFormat::RGB8:
|
||||||
|
return GL_RGB;
|
||||||
case TextureFormat::RGBA8:
|
case TextureFormat::RGBA8:
|
||||||
case TextureFormat::RGBA8_SRGB: return GL_RGBA;
|
case TextureFormat::RGBA8_SRGB:
|
||||||
|
return GL_RGBA;
|
||||||
case TextureFormat::Depth16:
|
case TextureFormat::Depth16:
|
||||||
case TextureFormat::Depth24:
|
case TextureFormat::Depth24:
|
||||||
case TextureFormat::Depth32F: return GL_DEPTH_COMPONENT;
|
case TextureFormat::Depth32F:
|
||||||
|
return GL_DEPTH_COMPONENT;
|
||||||
case TextureFormat::Depth24Stencil8:
|
case TextureFormat::Depth24Stencil8:
|
||||||
case TextureFormat::Depth32FStencil8:return GL_DEPTH_STENCIL;
|
case TextureFormat::Depth32FStencil8:
|
||||||
default: return GL_RGBA;
|
return GL_DEPTH_STENCIL;
|
||||||
|
default:
|
||||||
|
return GL_RGBA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,13 +154,20 @@ inline GLenum textureFormatToGLType(TextureFormat format) {
|
||||||
case TextureFormat::RG8:
|
case TextureFormat::RG8:
|
||||||
case TextureFormat::RGB8:
|
case TextureFormat::RGB8:
|
||||||
case TextureFormat::RGBA8:
|
case TextureFormat::RGBA8:
|
||||||
case TextureFormat::RGBA8_SRGB: return GL_UNSIGNED_BYTE;
|
case TextureFormat::RGBA8_SRGB:
|
||||||
case TextureFormat::Depth16: return GL_UNSIGNED_SHORT;
|
return GL_UNSIGNED_BYTE;
|
||||||
case TextureFormat::Depth24: return GL_UNSIGNED_INT;
|
case TextureFormat::Depth16:
|
||||||
case TextureFormat::Depth32F: return GL_FLOAT;
|
return GL_UNSIGNED_SHORT;
|
||||||
case TextureFormat::Depth24Stencil8: return GL_UNSIGNED_INT_24_8;
|
case TextureFormat::Depth24:
|
||||||
case TextureFormat::Depth32FStencil8:return GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
|
return GL_UNSIGNED_INT;
|
||||||
default: return GL_UNSIGNED_BYTE;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,13 +176,20 @@ inline GLenum textureFormatToGLType(TextureFormat format) {
|
||||||
*/
|
*/
|
||||||
inline GLenum textureFilterToGLMin(TextureFilter filter) {
|
inline GLenum textureFilterToGLMin(TextureFilter filter) {
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
case TextureFilter::Nearest: return GL_NEAREST;
|
case TextureFilter::Nearest:
|
||||||
case TextureFilter::Linear: return GL_LINEAR;
|
return GL_NEAREST;
|
||||||
case TextureFilter::NearestMipmapNearest: return GL_NEAREST_MIPMAP_NEAREST;
|
case TextureFilter::Linear:
|
||||||
case TextureFilter::LinearMipmapNearest: return GL_LINEAR_MIPMAP_NEAREST;
|
return GL_LINEAR;
|
||||||
case TextureFilter::NearestMipmapLinear: return GL_NEAREST_MIPMAP_LINEAR;
|
case TextureFilter::NearestMipmapNearest:
|
||||||
case TextureFilter::LinearMipmapLinear: return GL_LINEAR_MIPMAP_LINEAR;
|
return GL_NEAREST_MIPMAP_NEAREST;
|
||||||
default: return GL_LINEAR;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,36 +213,110 @@ inline GLenum textureFilterToGLMag(TextureFilter filter) {
|
||||||
*/
|
*/
|
||||||
inline GLenum textureWrapToGL(TextureWrap wrap) {
|
inline GLenum textureWrapToGL(TextureWrap wrap) {
|
||||||
switch (wrap) {
|
switch (wrap) {
|
||||||
case TextureWrap::Repeat: return GL_REPEAT;
|
case TextureWrap::Repeat:
|
||||||
case TextureWrap::ClampToEdge: return GL_CLAMP_TO_EDGE;
|
return GL_REPEAT;
|
||||||
case TextureWrap::ClampToBorder: return GL_CLAMP_TO_BORDER;
|
case TextureWrap::ClampToEdge:
|
||||||
case TextureWrap::MirroredRepeat: return GL_MIRRORED_REPEAT;
|
return GL_CLAMP_TO_EDGE;
|
||||||
default: return GL_REPEAT;
|
case TextureWrap::ClampToBorder:
|
||||||
|
return GL_CLAMP_TO_BORDER;
|
||||||
|
case TextureWrap::MirroredRepeat:
|
||||||
|
return GL_MIRRORED_REPEAT;
|
||||||
|
default:
|
||||||
|
return GL_REPEAT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 将 VertexFormat 转换为 OpenGL 格式
|
* @brief 将 VertexFormat 转换为 OpenGL 格式
|
||||||
*/
|
*/
|
||||||
inline void vertexFormatToGL(VertexFormat format, GLint& components, GLenum& type, GLboolean& normalized) {
|
inline void vertexFormatToGL(VertexFormat format, GLint &components,
|
||||||
|
GLenum &type, GLboolean &normalized) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case VertexFormat::Float1: components = 1; type = GL_FLOAT; normalized = GL_FALSE; break;
|
case VertexFormat::Float1:
|
||||||
case VertexFormat::Float2: components = 2; type = GL_FLOAT; normalized = GL_FALSE; break;
|
components = 1;
|
||||||
case VertexFormat::Float3: components = 3; type = GL_FLOAT; normalized = GL_FALSE; break;
|
type = GL_FLOAT;
|
||||||
case VertexFormat::Float4: components = 4; type = GL_FLOAT; normalized = GL_FALSE; break;
|
normalized = GL_FALSE;
|
||||||
case VertexFormat::Int1: components = 1; type = GL_INT; normalized = GL_FALSE; break;
|
break;
|
||||||
case VertexFormat::Int2: components = 2; type = GL_INT; normalized = GL_FALSE; break;
|
case VertexFormat::Float2:
|
||||||
case VertexFormat::Int3: components = 3; type = GL_INT; normalized = GL_FALSE; break;
|
components = 2;
|
||||||
case VertexFormat::Int4: components = 4; type = GL_INT; normalized = GL_FALSE; break;
|
type = GL_FLOAT;
|
||||||
case VertexFormat::UInt1: components = 1; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
|
normalized = GL_FALSE;
|
||||||
case VertexFormat::UInt2: components = 2; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
|
break;
|
||||||
case VertexFormat::UInt3: components = 3; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
|
case VertexFormat::Float3:
|
||||||
case VertexFormat::UInt4: components = 4; type = GL_UNSIGNED_INT; normalized = GL_FALSE; break;
|
components = 3;
|
||||||
case VertexFormat::Byte4: components = 4; type = GL_BYTE; normalized = GL_FALSE; break;
|
type = GL_FLOAT;
|
||||||
case VertexFormat::Byte4Normalized: components = 4; type = GL_BYTE; normalized = GL_TRUE; break;
|
normalized = GL_FALSE;
|
||||||
case VertexFormat::UByte4: components = 4; type = GL_UNSIGNED_BYTE; normalized = GL_FALSE; break;
|
break;
|
||||||
case VertexFormat::UByte4Normalized:components = 4; type = GL_UNSIGNED_BYTE; normalized = GL_TRUE; break;
|
case VertexFormat::Float4:
|
||||||
default: components = 4; type = GL_FLOAT; normalized = GL_FALSE; break;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,9 +325,12 @@ inline void vertexFormatToGL(VertexFormat format, GLint& components, GLenum& typ
|
||||||
*/
|
*/
|
||||||
inline GLenum indexTypeToGL(IndexType type) {
|
inline GLenum indexTypeToGL(IndexType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case IndexType::UInt16: return GL_UNSIGNED_SHORT;
|
case IndexType::UInt16:
|
||||||
case IndexType::UInt32: return GL_UNSIGNED_INT;
|
return GL_UNSIGNED_SHORT;
|
||||||
default: return GL_UNSIGNED_SHORT;
|
case IndexType::UInt32:
|
||||||
|
return GL_UNSIGNED_INT;
|
||||||
|
default:
|
||||||
|
return GL_UNSIGNED_SHORT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,13 +339,20 @@ inline GLenum indexTypeToGL(IndexType type) {
|
||||||
*/
|
*/
|
||||||
inline GLenum primitiveTypeToGL(PrimitiveType type) {
|
inline GLenum primitiveTypeToGL(PrimitiveType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case PrimitiveType::Points: return GL_POINTS;
|
case PrimitiveType::Points:
|
||||||
case PrimitiveType::Lines: return GL_LINES;
|
return GL_POINTS;
|
||||||
case PrimitiveType::LineStrip: return GL_LINE_STRIP;
|
case PrimitiveType::Lines:
|
||||||
case PrimitiveType::Triangles: return GL_TRIANGLES;
|
return GL_LINES;
|
||||||
case PrimitiveType::TriangleStrip: return GL_TRIANGLE_STRIP;
|
case PrimitiveType::LineStrip:
|
||||||
case PrimitiveType::TriangleFan: return GL_TRIANGLE_FAN;
|
return GL_LINE_STRIP;
|
||||||
default: return GL_TRIANGLES;
|
case PrimitiveType::Triangles:
|
||||||
|
return GL_TRIANGLES;
|
||||||
|
case PrimitiveType::TriangleStrip:
|
||||||
|
return GL_TRIANGLE_STRIP;
|
||||||
|
case PrimitiveType::TriangleFan:
|
||||||
|
return GL_TRIANGLE_FAN;
|
||||||
|
default:
|
||||||
|
return GL_TRIANGLES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,17 +361,28 @@ inline GLenum primitiveTypeToGL(PrimitiveType type) {
|
||||||
*/
|
*/
|
||||||
inline GLenum blendFactorToGL(BlendFactor factor) {
|
inline GLenum blendFactorToGL(BlendFactor factor) {
|
||||||
switch (factor) {
|
switch (factor) {
|
||||||
case BlendFactor::Zero: return GL_ZERO;
|
case BlendFactor::Zero:
|
||||||
case BlendFactor::One: return GL_ONE;
|
return GL_ZERO;
|
||||||
case BlendFactor::SrcColor: return GL_SRC_COLOR;
|
case BlendFactor::One:
|
||||||
case BlendFactor::OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR;
|
return GL_ONE;
|
||||||
case BlendFactor::DstColor: return GL_DST_COLOR;
|
case BlendFactor::SrcColor:
|
||||||
case BlendFactor::OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR;
|
return GL_SRC_COLOR;
|
||||||
case BlendFactor::SrcAlpha: return GL_SRC_ALPHA;
|
case BlendFactor::OneMinusSrcColor:
|
||||||
case BlendFactor::OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA;
|
return GL_ONE_MINUS_SRC_COLOR;
|
||||||
case BlendFactor::DstAlpha: return GL_DST_ALPHA;
|
case BlendFactor::DstColor:
|
||||||
case BlendFactor::OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA;
|
return GL_DST_COLOR;
|
||||||
default: return GL_ONE;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,12 +391,18 @@ inline GLenum blendFactorToGL(BlendFactor factor) {
|
||||||
*/
|
*/
|
||||||
inline GLenum blendOpToGL(BlendOp op) {
|
inline GLenum blendOpToGL(BlendOp op) {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case BlendOp::Add: return GL_FUNC_ADD;
|
case BlendOp::Add:
|
||||||
case BlendOp::Subtract: return GL_FUNC_SUBTRACT;
|
return GL_FUNC_ADD;
|
||||||
case BlendOp::ReverseSubtract: return GL_FUNC_REVERSE_SUBTRACT;
|
case BlendOp::Subtract:
|
||||||
case BlendOp::Min: return GL_MIN;
|
return GL_FUNC_SUBTRACT;
|
||||||
case BlendOp::Max: return GL_MAX;
|
case BlendOp::ReverseSubtract:
|
||||||
default: return GL_FUNC_ADD;
|
return GL_FUNC_REVERSE_SUBTRACT;
|
||||||
|
case BlendOp::Min:
|
||||||
|
return GL_MIN;
|
||||||
|
case BlendOp::Max:
|
||||||
|
return GL_MAX;
|
||||||
|
default:
|
||||||
|
return GL_FUNC_ADD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,15 +411,24 @@ inline GLenum blendOpToGL(BlendOp op) {
|
||||||
*/
|
*/
|
||||||
inline GLenum compareFuncToGL(CompareFunc func) {
|
inline GLenum compareFuncToGL(CompareFunc func) {
|
||||||
switch (func) {
|
switch (func) {
|
||||||
case CompareFunc::Never: return GL_NEVER;
|
case CompareFunc::Never:
|
||||||
case CompareFunc::Less: return GL_LESS;
|
return GL_NEVER;
|
||||||
case CompareFunc::Equal: return GL_EQUAL;
|
case CompareFunc::Less:
|
||||||
case CompareFunc::LessEqual: return GL_LEQUAL;
|
return GL_LESS;
|
||||||
case CompareFunc::Greater: return GL_GREATER;
|
case CompareFunc::Equal:
|
||||||
case CompareFunc::NotEqual: return GL_NOTEQUAL;
|
return GL_EQUAL;
|
||||||
case CompareFunc::GreaterEqual: return GL_GEQUAL;
|
case CompareFunc::LessEqual:
|
||||||
case CompareFunc::Always: return GL_ALWAYS;
|
return GL_LEQUAL;
|
||||||
default: return GL_LESS;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,11 +437,16 @@ inline GLenum compareFuncToGL(CompareFunc func) {
|
||||||
*/
|
*/
|
||||||
inline GLenum shaderTypeToGL(ShaderType type) {
|
inline GLenum shaderTypeToGL(ShaderType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ShaderType::Vertex: return GL_VERTEX_SHADER;
|
case ShaderType::Vertex:
|
||||||
case ShaderType::Fragment: return GL_FRAGMENT_SHADER;
|
return GL_VERTEX_SHADER;
|
||||||
case ShaderType::Geometry: return GL_GEOMETRY_SHADER;
|
case ShaderType::Fragment:
|
||||||
case ShaderType::Compute: return GL_COMPUTE_SHADER;
|
return GL_FRAGMENT_SHADER;
|
||||||
default: return GL_VERTEX_SHADER;
|
case ShaderType::Geometry:
|
||||||
|
return GL_GEOMETRY_SHADER;
|
||||||
|
case ShaderType::Compute:
|
||||||
|
return GL_COMPUTE_SHADER;
|
||||||
|
default:
|
||||||
|
return GL_VERTEX_SHADER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -284,23 +455,38 @@ inline GLenum shaderTypeToGL(ShaderType type) {
|
||||||
*/
|
*/
|
||||||
inline uint32_t getVertexFormatSize(VertexFormat format) {
|
inline uint32_t getVertexFormatSize(VertexFormat format) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case VertexFormat::Float1: return sizeof(float) * 1;
|
case VertexFormat::Float1:
|
||||||
case VertexFormat::Float2: return sizeof(float) * 2;
|
return sizeof(float) * 1;
|
||||||
case VertexFormat::Float3: return sizeof(float) * 3;
|
case VertexFormat::Float2:
|
||||||
case VertexFormat::Float4: return sizeof(float) * 4;
|
return sizeof(float) * 2;
|
||||||
case VertexFormat::Int1: return sizeof(int32_t) * 1;
|
case VertexFormat::Float3:
|
||||||
case VertexFormat::Int2: return sizeof(int32_t) * 2;
|
return sizeof(float) * 3;
|
||||||
case VertexFormat::Int3: return sizeof(int32_t) * 3;
|
case VertexFormat::Float4:
|
||||||
case VertexFormat::Int4: return sizeof(int32_t) * 4;
|
return sizeof(float) * 4;
|
||||||
case VertexFormat::UInt1: return sizeof(uint32_t) * 1;
|
case VertexFormat::Int1:
|
||||||
case VertexFormat::UInt2: return sizeof(uint32_t) * 2;
|
return sizeof(int32_t) * 1;
|
||||||
case VertexFormat::UInt3: return sizeof(uint32_t) * 3;
|
case VertexFormat::Int2:
|
||||||
case VertexFormat::UInt4: return sizeof(uint32_t) * 4;
|
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::Byte4:
|
||||||
case VertexFormat::Byte4Normalized: return sizeof(int8_t) * 4;
|
case VertexFormat::Byte4Normalized:
|
||||||
|
return sizeof(int8_t) * 4;
|
||||||
case VertexFormat::UByte4:
|
case VertexFormat::UByte4:
|
||||||
case VertexFormat::UByte4Normalized: return sizeof(uint8_t) * 4;
|
case VertexFormat::UByte4Normalized:
|
||||||
default: return sizeof(float) * 4;
|
return sizeof(uint8_t) * 4;
|
||||||
|
default:
|
||||||
|
return sizeof(float) * 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,172 +1,37 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdio>
|
#include <memory>
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <utility>
|
||||||
#include <types/base/types.h>
|
|
||||||
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
// Switch 平台支持
|
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef TEST_BUILD
|
|
||||||
// SDL2 日志头文件(测试构建时不使用)
|
|
||||||
#include <SDL.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
// ============================================================================
|
/**
|
||||||
// 日志级别枚举
|
* @brief 日志级别枚举
|
||||||
// ============================================================================
|
*/
|
||||||
enum class LogLevel {
|
enum class LogLevel {
|
||||||
#ifdef TEST_BUILD
|
Trace = spdlog::level::trace,
|
||||||
Trace = 0,
|
Debug = spdlog::level::debug,
|
||||||
Debug = 1,
|
Info = spdlog::level::info,
|
||||||
Info = 2,
|
Warn = spdlog::level::warn,
|
||||||
Warn = 3,
|
Error = spdlog::level::err,
|
||||||
Error = 4,
|
Fatal = spdlog::level::critical,
|
||||||
Fatal = 5,
|
Off = spdlog::level::off
|
||||||
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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
/**
|
||||||
// 简单的 fmt-style {} 格式化器
|
* @brief 日志系统管理类
|
||||||
// ============================================================================
|
*
|
||||||
namespace detail {
|
* 提供统一的日志记录接口,支持控制台和文件输出
|
||||||
|
* 通过 setLevel() 在运行时控制日志级别
|
||||||
// 将单个参数转为字符串
|
*/
|
||||||
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 日志系统
|
|
||||||
// ============================================================================
|
|
||||||
class Logger {
|
class Logger {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
@ -186,14 +51,14 @@ public:
|
||||||
static void setLevel(LogLevel level);
|
static void setLevel(LogLevel level);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否输出到控制台
|
* @brief 启用/禁用控制台输出
|
||||||
* @param enable 是否启用
|
* @param enable 是否启用
|
||||||
*/
|
*/
|
||||||
static void setConsoleOutput(bool enable);
|
static void setConsoleOutput(bool enable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置日志输出到文件
|
* @brief 设置文件输出
|
||||||
* @param filename 日志文件名
|
* @param filename 日志文件路径
|
||||||
*/
|
*/
|
||||||
static void setFileOutput(const std::string &filename);
|
static void setFileOutput(const std::string &filename);
|
||||||
|
|
||||||
|
|
@ -201,94 +66,84 @@ public:
|
||||||
* @brief 获取当前日志级别
|
* @brief 获取当前日志级别
|
||||||
* @return 当前日志级别
|
* @return 当前日志级别
|
||||||
*/
|
*/
|
||||||
static LogLevel getLevel() { return level_; }
|
static LogLevel getLevel();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 日志记录模板函数
|
* @brief 记录日志(格式化版本)
|
||||||
|
* @tparam Args 参数类型
|
||||||
* @param level 日志级别
|
* @param level 日志级别
|
||||||
* @param fmt 格式化字符串
|
* @param fmt 格式字符串
|
||||||
* @param args 可变参数
|
* @param args 格式化参数
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static void log(LogLevel level, const char *fmt, const Args &...args) {
|
static void log(LogLevel level, fmt::format_string<Args...> fmt,
|
||||||
if (static_cast<int>(level) < static_cast<int>(level_))
|
Args &&...args) {
|
||||||
|
if (!logger_) {
|
||||||
return;
|
return;
|
||||||
std::string msg = e2d_format(fmt, args...);
|
}
|
||||||
#ifdef TEST_BUILD
|
auto spdLevel = static_cast<spdlog::level::level_enum>(level);
|
||||||
printf("[%s] %s\n", getLevelString(level), msg.c_str());
|
logger_->log(spdLevel, fmt, std::forward<Args>(args)...);
|
||||||
#else
|
|
||||||
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
static_cast<SDL_LogPriority>(level), "[%s] %s",
|
|
||||||
getLevelString(level), msg.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 日志记录无参数版本
|
* @brief 记录日志(字符串版本)
|
||||||
* @param level 日志级别
|
* @param level 日志级别
|
||||||
* @param msg 日志消息
|
* @param msg 日志消息
|
||||||
*/
|
*/
|
||||||
static void log(LogLevel level, const char *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_; // 日志文件路径
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取日志级别字符串
|
* @brief 获取内部 spdlog 日志器
|
||||||
* @param level 日志级别
|
* @return spdlog 日志器实例
|
||||||
* @return 级别字符串
|
|
||||||
*/
|
*/
|
||||||
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
|
} // 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) {
|
bool Application::init(const AppConfig &config) {
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
E2D_LOG_WARN("应用程序已经初始化");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
config_ = config;
|
config_ = config;
|
||||||
|
|
||||||
|
// 初始化日志系统(必须在所有日志调用之前)
|
||||||
|
Logger::init();
|
||||||
|
|
||||||
// 创建引擎上下文
|
// 创建引擎上下文
|
||||||
context_ = Context::create();
|
context_ = Context::create();
|
||||||
if (!context_) {
|
if (!context_) {
|
||||||
E2D_LOG_ERROR("创建上下文失败");
|
E2D_ERROR("创建上下文失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化引擎
|
// 初始化引擎
|
||||||
if (!context_->init()) {
|
if (!context_->init()) {
|
||||||
E2D_LOG_ERROR("初始化上下文失败");
|
E2D_ERROR("初始化上下文失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,9 +55,9 @@ bool Application::init(const AppConfig &config) {
|
||||||
|
|
||||||
events::OnInit::emit();
|
events::OnInit::emit();
|
||||||
|
|
||||||
E2D_LOG_INFO("应用程序初始化成功");
|
E2D_INFO("应用程序初始化成功");
|
||||||
E2D_LOG_INFO("窗口: {}x{}, 全屏: {}", config.width,
|
E2D_INFO("窗口: {}x{}, 全屏: {}", config.width, config.height,
|
||||||
config.height, config.fullscreen);
|
config.fullscreen);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,9 +73,9 @@ void Application::initModules() {
|
||||||
// 初始化所有模块
|
// 初始化所有模块
|
||||||
for (auto &module : modules_) {
|
for (auto &module : modules_) {
|
||||||
if (!module->init()) {
|
if (!module->init()) {
|
||||||
E2D_LOG_ERROR("初始化模块失败: {}", module->getName());
|
E2D_ERROR("初始化模块失败: {}", module->getName());
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_INFO("模块已初始化: {} (优先级: {})", module->getName(),
|
E2D_INFO("模块已初始化: {} (优先级: {})", module->getName(),
|
||||||
module->getPriority());
|
module->getPriority());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +87,7 @@ void Application::shutdown() {
|
||||||
|
|
||||||
events::OnShutdown::emit();
|
events::OnShutdown::emit();
|
||||||
|
|
||||||
E2D_LOG_INFO("正在关闭应用程序...");
|
E2D_INFO("正在关闭应用程序...");
|
||||||
|
|
||||||
// 按相反顺序销毁模块
|
// 按相反顺序销毁模块
|
||||||
for (auto it = modules_.rbegin(); it != modules_.rend(); ++it) {
|
for (auto it = modules_.rbegin(); it != modules_.rend(); ++it) {
|
||||||
|
|
@ -99,12 +101,15 @@ void Application::shutdown() {
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
running_ = false;
|
running_ = false;
|
||||||
|
|
||||||
E2D_LOG_INFO("应用程序关闭完成");
|
E2D_INFO("应用程序关闭完成");
|
||||||
|
|
||||||
|
// 关闭日志系统(在所有日志调用之后)
|
||||||
|
Logger::shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::run() {
|
void Application::run() {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
E2D_LOG_ERROR("应用程序未初始化");
|
E2D_ERROR("应用程序未初始化");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,7 +164,7 @@ void Application::pause() {
|
||||||
if (!paused_) {
|
if (!paused_) {
|
||||||
paused_ = true;
|
paused_ = true;
|
||||||
events::OnPause::emit();
|
events::OnPause::emit();
|
||||||
E2D_LOG_INFO("应用程序已暂停");
|
E2D_INFO("应用程序已暂停");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,7 +172,7 @@ void Application::resume() {
|
||||||
if (paused_) {
|
if (paused_) {
|
||||||
paused_ = false;
|
paused_ = false;
|
||||||
events::OnResume::emit();
|
events::OnResume::emit();
|
||||||
E2D_LOG_INFO("应用程序已恢复");
|
E2D_INFO("应用程序已恢复");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ AssetsModule::~AssetsModule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AssetsModule::init() {
|
bool AssetsModule::init() {
|
||||||
E2D_LOG_INFO("资源模块正在初始化...");
|
E2D_INFO("资源模块正在初始化...");
|
||||||
|
|
||||||
textureLoader_ = std::make_unique<TextureLoader>();
|
textureLoader_ = std::make_unique<TextureLoader>();
|
||||||
shaderLoader_ = std::make_unique<ShaderLoader>();
|
shaderLoader_ = std::make_unique<ShaderLoader>();
|
||||||
|
|
@ -84,8 +84,7 @@ bool AssetsModule::init() {
|
||||||
onShowListener_ = std::make_unique<events::OnShow::Listener>();
|
onShowListener_ = std::make_unique<events::OnShow::Listener>();
|
||||||
onShowListener_->bind([this]() { this->onGLContextReady(); });
|
onShowListener_->bind([this]() { this->onGLContextReady(); });
|
||||||
|
|
||||||
E2D_LOG_INFO(
|
E2D_INFO("资源模块初始化成功 (等待 GL 上下文)");
|
||||||
"资源模块初始化成功 (等待 GL 上下文)");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,19 +93,19 @@ void AssetsModule::onGLContextReady() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("OpenGL 上下文就绪,正在创建默认资源...");
|
E2D_INFO("OpenGL 上下文就绪,正在创建默认资源...");
|
||||||
|
|
||||||
if (!createDefaultResources()) {
|
if (!createDefaultResources()) {
|
||||||
E2D_LOG_ERROR("创建默认资源失败");
|
E2D_ERROR("创建默认资源失败");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultResourcesCreated_ = true;
|
defaultResourcesCreated_ = true;
|
||||||
E2D_LOG_INFO("默认资源创建成功");
|
E2D_INFO("默认资源创建成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::shutdown() {
|
void AssetsModule::shutdown() {
|
||||||
E2D_LOG_INFO("资源模块正在关闭...");
|
E2D_INFO("资源模块正在关闭...");
|
||||||
|
|
||||||
// 关闭异步加载系统
|
// 关闭异步加载系统
|
||||||
shutdownAsyncLoader();
|
shutdownAsyncLoader();
|
||||||
|
|
@ -141,7 +140,7 @@ void AssetsModule::shutdown() {
|
||||||
|
|
||||||
defaultResourcesCreated_ = false;
|
defaultResourcesCreated_ = false;
|
||||||
|
|
||||||
E2D_LOG_INFO("资源模块关闭完成");
|
E2D_INFO("资源模块关闭完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetsModule *getAssets() { return g_assetsModule; }
|
AssetsModule *getAssets() { return g_assetsModule; }
|
||||||
|
|
@ -164,13 +163,13 @@ Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!textureLoader_) {
|
if (!textureLoader_) {
|
||||||
E2D_LOG_ERROR("纹理加载器未注册");
|
E2D_ERROR("纹理加载器未注册");
|
||||||
return Handle<Texture>::invalid();
|
return Handle<Texture>::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr<Texture> texture = textureLoader_->load(path);
|
Ptr<Texture> texture = textureLoader_->load(path);
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
E2D_LOG_ERROR("加载纹理失败: {}", path);
|
E2D_ERROR("加载纹理失败: {}", path);
|
||||||
return Handle<Texture>::invalid();
|
return Handle<Texture>::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,7 +188,7 @@ Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
|
||||||
texturePathCache_[path] = handle;
|
texturePathCache_[path] = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_DEBUG("已加载纹理: {} -> 句柄索引 {}", path, handle.index());
|
E2D_DEBUG("已加载纹理: {} -> 句柄索引 {}", path, handle.index());
|
||||||
|
|
||||||
// 如果启用了热重载,添加文件监控
|
// 如果启用了热重载,添加文件监控
|
||||||
if (hotReloadEnabled_) {
|
if (hotReloadEnabled_) {
|
||||||
|
|
@ -212,13 +211,13 @@ Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!textureLoader_) {
|
if (!textureLoader_) {
|
||||||
E2D_LOG_ERROR("纹理加载器未注册");
|
E2D_ERROR("纹理加载器未注册");
|
||||||
return Handle<Texture>::invalid();
|
return Handle<Texture>::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr<Texture> texture = textureLoader_->loadFromMemory(data, size);
|
Ptr<Texture> texture = textureLoader_->loadFromMemory(data, size);
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
E2D_LOG_ERROR("从内存加载纹理失败: {}", key);
|
E2D_ERROR("从内存加载纹理失败: {}", key);
|
||||||
return Handle<Texture>::invalid();
|
return Handle<Texture>::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,13 +244,13 @@ template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shaderLoader_) {
|
if (!shaderLoader_) {
|
||||||
E2D_LOG_ERROR("着色器加载器未注册");
|
E2D_ERROR("着色器加载器未注册");
|
||||||
return Handle<Shader>::invalid();
|
return Handle<Shader>::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr<Shader> shader = shaderLoader_->load(path);
|
Ptr<Shader> shader = shaderLoader_->load(path);
|
||||||
if (!shader) {
|
if (!shader) {
|
||||||
E2D_LOG_ERROR("加载着色器失败: {}", path);
|
E2D_ERROR("加载着色器失败: {}", path);
|
||||||
return Handle<Shader>::invalid();
|
return Handle<Shader>::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,7 +269,7 @@ template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
|
||||||
shaderPathCache_[path] = handle;
|
shaderPathCache_[path] = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_DEBUG("已加载着色器: {} -> 句柄索引 {}", path, handle.index());
|
E2D_DEBUG("已加载着色器: {} -> 句柄索引 {}", path, handle.index());
|
||||||
|
|
||||||
// 如果启用了热重载,添加文件监控
|
// 如果启用了热重载,添加文件监控
|
||||||
if (hotReloadEnabled_) {
|
if (hotReloadEnabled_) {
|
||||||
|
|
@ -297,14 +296,14 @@ Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shaderLoader_) {
|
if (!shaderLoader_) {
|
||||||
E2D_LOG_ERROR("着色器加载器未注册");
|
E2D_ERROR("着色器加载器未注册");
|
||||||
return Handle<Shader>::invalid();
|
return Handle<Shader>::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderLoader *shaderLoader = static_cast<ShaderLoader *>(shaderLoader_.get());
|
ShaderLoader *shaderLoader = static_cast<ShaderLoader *>(shaderLoader_.get());
|
||||||
Ptr<Shader> shader = shaderLoader->load(vertPath, fragPath);
|
Ptr<Shader> shader = shaderLoader->load(vertPath, fragPath);
|
||||||
if (!shader) {
|
if (!shader) {
|
||||||
E2D_LOG_ERROR("加载着色器失败: {} + {}", vertPath, fragPath);
|
E2D_ERROR("加载着色器失败: {} + {}", vertPath, fragPath);
|
||||||
return Handle<Shader>::invalid();
|
return Handle<Shader>::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,7 +322,7 @@ Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
||||||
shaderPathCache_[cacheKey] = handle;
|
shaderPathCache_[cacheKey] = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_DEBUG("已加载着色器: {} + {} -> 句柄索引 {}", vertPath, fragPath,
|
E2D_DEBUG("已加载着色器: {} + {} -> 句柄索引 {}", vertPath, fragPath,
|
||||||
handle.index());
|
handle.index());
|
||||||
|
|
||||||
// 如果启用了热重载,添加文件监控
|
// 如果启用了热重载,添加文件监控
|
||||||
|
|
@ -348,7 +347,7 @@ AssetsModule::loadDir<Texture>(const std::string &directory,
|
||||||
try {
|
try {
|
||||||
std::filesystem::path dirPath(directory);
|
std::filesystem::path dirPath(directory);
|
||||||
if (!fileExists(directory)) {
|
if (!fileExists(directory)) {
|
||||||
E2D_LOG_WARN("目录未找到: {}", directory);
|
E2D_WARN("目录未找到: {}", directory);
|
||||||
return handles;
|
return handles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -382,10 +381,10 @@ AssetsModule::loadDir<Texture>(const std::string &directory,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (const std::exception &e) {
|
} 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;
|
return handles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -455,11 +454,11 @@ bool AssetsModule::createDefaultResources() {
|
||||||
Ptr<Texture> texture = makePtr<Texture>();
|
Ptr<Texture> texture = makePtr<Texture>();
|
||||||
uint8_t whitePixel[] = {255, 255, 255, 255};
|
uint8_t whitePixel[] = {255, 255, 255, 255};
|
||||||
if (!texture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8)) {
|
if (!texture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8)) {
|
||||||
E2D_LOG_ERROR("创建默认纹理失败");
|
E2D_ERROR("创建默认纹理失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
defaultTexture_ = textures_.insert(texture);
|
defaultTexture_ = textures_.insert(texture);
|
||||||
E2D_LOG_DEBUG("已创建默认纹理");
|
E2D_DEBUG("已创建默认纹理");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -474,8 +473,7 @@ bool AssetsModule::createDefaultResources() {
|
||||||
: "shader/default.frag";
|
: "shader/default.frag";
|
||||||
|
|
||||||
if (!fileExists(vertPath) || !fileExists(fragPath)) {
|
if (!fileExists(vertPath) || !fileExists(fragPath)) {
|
||||||
E2D_LOG_ERROR("默认着色器文件未找到: {}, {}", vertPath,
|
E2D_ERROR("默认着色器文件未找到: {}, {}", vertPath, fragPath);
|
||||||
fragPath);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -483,22 +481,19 @@ bool AssetsModule::createDefaultResources() {
|
||||||
std::string vsSource = readFileToString(vertPath);
|
std::string vsSource = readFileToString(vertPath);
|
||||||
std::string fsSource = readFileToString(fragPath);
|
std::string fsSource = readFileToString(fragPath);
|
||||||
if (vsSource.empty() || fsSource.empty()) {
|
if (vsSource.empty() || fsSource.empty()) {
|
||||||
E2D_LOG_ERROR("读取默认着色器文件失败: {}, {}", vertPath,
|
E2D_ERROR("读取默认着色器文件失败: {}, {}", vertPath, fragPath);
|
||||||
fragPath);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从源码加载着色器
|
// 从源码加载着色器
|
||||||
Ptr<Shader> shader = makePtr<Shader>();
|
Ptr<Shader> shader = makePtr<Shader>();
|
||||||
if (!shader->loadFromSource(vsSource, fsSource)) {
|
if (!shader->loadFromSource(vsSource, fsSource)) {
|
||||||
E2D_LOG_ERROR("编译默认着色器失败: {}, {}", vertPath,
|
E2D_ERROR("编译默认着色器失败: {}, {}", vertPath, fragPath);
|
||||||
fragPath);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultShader_ = shaders_.insert(shader);
|
defaultShader_ = shaders_.insert(shader);
|
||||||
E2D_LOG_DEBUG("已从文件加载默认着色器: {}, {}", vertPath,
|
E2D_DEBUG("已从文件加载默认着色器: {}, {}", vertPath, fragPath);
|
||||||
fragPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -527,17 +522,17 @@ bool AssetsModule::createDefaultResources() {
|
||||||
// 添加默认纹理到材质
|
// 添加默认纹理到材质
|
||||||
material->setTexture("uTexture", getPtr(defaultTexture_), 0);
|
material->setTexture("uTexture", getPtr(defaultTexture_), 0);
|
||||||
defaultMaterial_ = materials_.insert(material);
|
defaultMaterial_ = materials_.insert(material);
|
||||||
E2D_LOG_DEBUG("已创建默认材质,使用默认纹理和布局");
|
E2D_DEBUG("已创建默认材质,使用默认纹理和布局");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Ptr<Mesh> mesh = Mesh::createQuad(Vec2(1.0f, 1.0f));
|
Ptr<Mesh> mesh = Mesh::createQuad(Vec2(1.0f, 1.0f));
|
||||||
if (!mesh) {
|
if (!mesh) {
|
||||||
E2D_LOG_ERROR("创建默认四边形网格失败");
|
E2D_ERROR("创建默认四边形网格失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
defaultQuad_ = meshes_.insert(mesh);
|
defaultQuad_ = meshes_.insert(mesh);
|
||||||
E2D_LOG_DEBUG("已创建默认四边形网格");
|
E2D_DEBUG("已创建默认四边形网格");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -584,9 +579,9 @@ Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(defaultQuad_); }
|
||||||
void AssetsModule::enableHotReload(bool enable) {
|
void AssetsModule::enableHotReload(bool enable) {
|
||||||
hotReloadEnabled_ = enable;
|
hotReloadEnabled_ = enable;
|
||||||
if (enable) {
|
if (enable) {
|
||||||
E2D_LOG_INFO("热重载已启用");
|
E2D_INFO("热重载已启用");
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_INFO("热重载已禁用");
|
E2D_INFO("热重载已禁用");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -594,7 +589,8 @@ void AssetsModule::setHotReloadInterval(float interval) {
|
||||||
hotReloadInterval_ = interval;
|
hotReloadInterval_ = interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::addFileWatchInternal(const std::string &path, FileWatchInfo &&info) {
|
void AssetsModule::addFileWatchInternal(const std::string &path,
|
||||||
|
FileWatchInfo &&info) {
|
||||||
try {
|
try {
|
||||||
if (!fileExists(path)) {
|
if (!fileExists(path)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -606,20 +602,22 @@ void AssetsModule::addFileWatchInternal(const std::string &path, FileWatchInfo &
|
||||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||||
fileWatchList_.push_back(std::move(info));
|
fileWatchList_.push_back(std::move(info));
|
||||||
const char *type = fileWatchList_.back().isTexture ? "纹理" : "着色器";
|
const char *type = fileWatchList_.back().isTexture ? "纹理" : "着色器";
|
||||||
E2D_LOG_DEBUG("正在监控 {} 文件: {}", type, path);
|
E2D_DEBUG("正在监控 {} 文件: {}", type, path);
|
||||||
} catch (const std::exception &e) {
|
} 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;
|
FileWatchInfo info;
|
||||||
info.textureHandle = handle;
|
info.textureHandle = handle;
|
||||||
info.isTexture = true;
|
info.isTexture = true;
|
||||||
addFileWatchInternal(path, std::move(info));
|
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;
|
FileWatchInfo info;
|
||||||
info.shaderHandle = handle;
|
info.shaderHandle = handle;
|
||||||
info.isTexture = false;
|
info.isTexture = false;
|
||||||
|
|
@ -631,22 +629,22 @@ void AssetsModule::reloadTexture(const FileWatchInfo &info) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("正在重载纹理: {}", info.path);
|
E2D_INFO("正在重载纹理: {}", info.path);
|
||||||
|
|
||||||
// 获取旧纹理指针
|
// 获取旧纹理指针
|
||||||
Texture *oldTexture = textures_.get(info.textureHandle);
|
Texture *oldTexture = textures_.get(info.textureHandle);
|
||||||
if (!oldTexture) {
|
if (!oldTexture) {
|
||||||
E2D_LOG_ERROR("未找到要重载的旧纹理: {}", info.path);
|
E2D_ERROR("未找到要重载的旧纹理: {}", info.path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接在原有纹理对象上重新加载
|
// 直接在原有纹理对象上重新加载
|
||||||
if (!oldTexture->reloadFromFile(info.path)) {
|
if (!oldTexture->reloadFromFile(info.path)) {
|
||||||
E2D_LOG_ERROR("重载纹理失败: {}", info.path);
|
E2D_ERROR("重载纹理失败: {}", info.path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("纹理重载成功: {}", info.path);
|
E2D_INFO("纹理重载成功: {}", info.path);
|
||||||
notifyTextureReloaded(info.textureHandle);
|
notifyTextureReloaded(info.textureHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -655,12 +653,12 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("正在重载着色器: {}", info.path);
|
E2D_INFO("正在重载着色器: {}", info.path);
|
||||||
|
|
||||||
// 获取旧着色器指针
|
// 获取旧着色器指针
|
||||||
Shader *oldShader = shaders_.get(info.shaderHandle);
|
Shader *oldShader = shaders_.get(info.shaderHandle);
|
||||||
if (!oldShader) {
|
if (!oldShader) {
|
||||||
E2D_LOG_ERROR("未找到要重载的旧着色器: {}", info.path);
|
E2D_ERROR("未找到要重载的旧着色器: {}", info.path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -677,7 +675,7 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cacheKey.empty()) {
|
if (cacheKey.empty()) {
|
||||||
E2D_LOG_WARN("未找到着色器缓存键: {}", info.path);
|
E2D_WARN("未找到着色器缓存键: {}", info.path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -685,7 +683,7 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
|
||||||
size_t sepPos = cacheKey.find('|');
|
size_t sepPos = cacheKey.find('|');
|
||||||
if (sepPos == std::string::npos) {
|
if (sepPos == std::string::npos) {
|
||||||
// 单文件模式 - 这里暂不支持,因为 ShaderLoader 目前只支持双文件
|
// 单文件模式 - 这里暂不支持,因为 ShaderLoader 目前只支持双文件
|
||||||
E2D_LOG_WARN("不支持单文件着色器重载: {}", info.path);
|
E2D_WARN("不支持单文件着色器重载: {}", info.path);
|
||||||
} else {
|
} else {
|
||||||
// 双文件模式
|
// 双文件模式
|
||||||
std::string vertPath = cacheKey.substr(0, sepPos);
|
std::string vertPath = cacheKey.substr(0, sepPos);
|
||||||
|
|
@ -695,18 +693,18 @@ void AssetsModule::reloadShader(const FileWatchInfo &info) {
|
||||||
std::string vsSource = readFileToString(vertPath);
|
std::string vsSource = readFileToString(vertPath);
|
||||||
std::string fsSource = readFileToString(fragPath);
|
std::string fsSource = readFileToString(fragPath);
|
||||||
if (vsSource.empty() || fsSource.empty()) {
|
if (vsSource.empty() || fsSource.empty()) {
|
||||||
E2D_LOG_ERROR("读取着色器文件失败: {}, {}", vertPath, fragPath);
|
E2D_ERROR("读取着色器文件失败: {}, {}", vertPath, fragPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接在原有着色器对象上重新加载
|
// 直接在原有着色器对象上重新加载
|
||||||
if (!oldShader->reloadFromSource(vsSource, fsSource)) {
|
if (!oldShader->reloadFromSource(vsSource, fsSource)) {
|
||||||
E2D_LOG_ERROR("重载着色器失败: {} + {}", vertPath, fragPath);
|
E2D_ERROR("重载着色器失败: {} + {}", vertPath, fragPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("着色器重载成功: {}", info.path);
|
E2D_INFO("着色器重载成功: {}", info.path);
|
||||||
notifyShaderReloaded(info.shaderHandle);
|
notifyShaderReloaded(info.shaderHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -744,7 +742,7 @@ void AssetsModule::checkForChanges() {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
}
|
}
|
||||||
} catch (const std::exception &e) {
|
} 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);
|
workerThreads_.emplace_back(&AssetsModule::workerThreadLoop, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("异步加载器已初始化,使用 {} 个线程", threadCount);
|
E2D_INFO("异步加载器已初始化,使用 {} 个线程", threadCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::shutdownAsyncLoader() {
|
void AssetsModule::shutdownAsyncLoader() {
|
||||||
|
|
@ -790,7 +788,7 @@ void AssetsModule::shutdownAsyncLoader() {
|
||||||
std::lock_guard<std::mutex> lock(queueMutex_);
|
std::lock_guard<std::mutex> lock(queueMutex_);
|
||||||
loadQueue_.clear();
|
loadQueue_.clear();
|
||||||
|
|
||||||
E2D_LOG_INFO("异步加载器已关闭");
|
E2D_INFO("异步加载器已关闭");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsModule::submitLoadTask(const LoadTask &task) {
|
void AssetsModule::submitLoadTask(const LoadTask &task) {
|
||||||
|
|
@ -890,8 +888,7 @@ void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
||||||
info.dependentMaterials.end(), material);
|
info.dependentMaterials.end(), material);
|
||||||
if (it == info.dependentMaterials.end()) {
|
if (it == info.dependentMaterials.end()) {
|
||||||
info.dependentMaterials.push_back(material);
|
info.dependentMaterials.push_back(material);
|
||||||
E2D_LOG_DEBUG("已注册材质 {} 对纹理 {} 的依赖",
|
E2D_DEBUG("已注册材质 {} 对纹理 {} 的依赖", material.index(), textureIndex);
|
||||||
material.index(), textureIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -912,8 +909,8 @@ void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
||||||
info.dependentMaterials.end(), material);
|
info.dependentMaterials.end(), material);
|
||||||
if (it == info.dependentMaterials.end()) {
|
if (it == info.dependentMaterials.end()) {
|
||||||
info.dependentMaterials.push_back(material);
|
info.dependentMaterials.push_back(material);
|
||||||
E2D_LOG_DEBUG("已注册材质 {} 对着色器 {} 的依赖",
|
E2D_DEBUG("已注册材质 {} 对着色器 {} 的依赖", material.index(),
|
||||||
material.index(), shaderIndex);
|
shaderIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -927,8 +924,7 @@ void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) {
|
||||||
uint32_t textureIndex = texture.index();
|
uint32_t textureIndex = texture.index();
|
||||||
auto it = textureDependencies_.find(textureIndex);
|
auto it = textureDependencies_.find(textureIndex);
|
||||||
if (it != textureDependencies_.end()) {
|
if (it != textureDependencies_.end()) {
|
||||||
E2D_LOG_INFO("通知 {} 个材质纹理已重载",
|
E2D_INFO("通知 {} 个材质纹理已重载", it->second.dependentMaterials.size());
|
||||||
it->second.dependentMaterials.size());
|
|
||||||
|
|
||||||
// 材质需要更新纹理引用
|
// 材质需要更新纹理引用
|
||||||
// 这里可以触发材质更新事件
|
// 这里可以触发材质更新事件
|
||||||
|
|
@ -936,8 +932,7 @@ void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) {
|
||||||
Material *material = materials_.get(materialHandle);
|
Material *material = materials_.get(materialHandle);
|
||||||
if (material) {
|
if (material) {
|
||||||
// 材质自动使用新的纹理数据
|
// 材质自动使用新的纹理数据
|
||||||
E2D_LOG_DEBUG("材质 {} 已更新为重载后的纹理",
|
E2D_DEBUG("材质 {} 已更新为重载后的纹理", materialHandle.index());
|
||||||
materialHandle.index());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -953,7 +948,7 @@ void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) {
|
||||||
uint32_t shaderIndex = shader.index();
|
uint32_t shaderIndex = shader.index();
|
||||||
auto it = shaderDependencies_.find(shaderIndex);
|
auto it = shaderDependencies_.find(shaderIndex);
|
||||||
if (it != shaderDependencies_.end()) {
|
if (it != shaderDependencies_.end()) {
|
||||||
E2D_LOG_INFO("通知 {} 个材质着色器已重载",
|
E2D_INFO("通知 {} 个材质着色器已重载",
|
||||||
it->second.dependentMaterials.size());
|
it->second.dependentMaterials.size());
|
||||||
|
|
||||||
for (const auto &materialHandle : it->second.dependentMaterials) {
|
for (const auto &materialHandle : it->second.dependentMaterials) {
|
||||||
|
|
@ -961,8 +956,7 @@ void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) {
|
||||||
if (material) {
|
if (material) {
|
||||||
// 更新材质的着色器引用
|
// 更新材质的着色器引用
|
||||||
material->setShader(getPtr(shader));
|
material->setShader(getPtr(shader));
|
||||||
E2D_LOG_DEBUG("材质 {} 已更新为重载后的着色器",
|
E2D_DEBUG("材质 {} 已更新为重载后的着色器", materialHandle.index());
|
||||||
materialHandle.index());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,7 @@ namespace {
|
||||||
/**
|
/**
|
||||||
* @brief 获取 FileModule 实例
|
* @brief 获取 FileModule 实例
|
||||||
*/
|
*/
|
||||||
FileModule *getFileModule() {
|
FileModule *getFileModule() { return getModule<FileModule>(); }
|
||||||
return getModule<FileModule>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查文件是否存在(使用 FileModule)
|
* @brief 检查文件是否存在(使用 FileModule)
|
||||||
|
|
@ -65,31 +63,30 @@ Ptr<Shader> ShaderLoader::load(const std::string &vertPath,
|
||||||
// 读取顶点着色器文件
|
// 读取顶点着色器文件
|
||||||
std::string vsSource = readFileToString(vertPath);
|
std::string vsSource = readFileToString(vertPath);
|
||||||
if (vsSource.empty()) {
|
if (vsSource.empty()) {
|
||||||
E2D_LOG_ERROR("ShaderLoader: 读取顶点着色器失败: {}", vertPath);
|
E2D_ERROR("ShaderLoader: 读取顶点着色器失败: {}", vertPath);
|
||||||
return Ptr<Shader>();
|
return Ptr<Shader>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取片段着色器文件
|
// 读取片段着色器文件
|
||||||
std::string fsSource = readFileToString(fragPath);
|
std::string fsSource = readFileToString(fragPath);
|
||||||
if (fsSource.empty()) {
|
if (fsSource.empty()) {
|
||||||
E2D_LOG_ERROR("ShaderLoader: 读取片段着色器失败: {}", fragPath);
|
E2D_ERROR("ShaderLoader: 读取片段着色器失败: {}", fragPath);
|
||||||
return Ptr<Shader>();
|
return Ptr<Shader>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从源码加载着色器
|
// 从源码加载着色器
|
||||||
Ptr<Shader> shader = makePtr<Shader>();
|
Ptr<Shader> shader = makePtr<Shader>();
|
||||||
if (!shader->loadFromSource(vsSource, fsSource)) {
|
if (!shader->loadFromSource(vsSource, fsSource)) {
|
||||||
E2D_LOG_ERROR("ShaderLoader: 编译着色器失败 {} + {}", vertPath,
|
E2D_ERROR("ShaderLoader: 编译着色器失败 {} + {}", vertPath, fragPath);
|
||||||
fragPath);
|
|
||||||
return Ptr<Shader>();
|
return Ptr<Shader>();
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_DEBUG("ShaderLoader: 已加载着色器 {} + {}", vertPath, fragPath);
|
E2D_DEBUG("ShaderLoader: 已加载着色器 {} + {}", vertPath, fragPath);
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr<Shader> ShaderLoader::loadFromMemory(const uint8_t *data, size_t size) {
|
Ptr<Shader> ShaderLoader::loadFromMemory(const uint8_t *data, size_t size) {
|
||||||
E2D_LOG_ERROR("ShaderLoader: loadFromMemory 不支持着色器");
|
E2D_ERROR("ShaderLoader: loadFromMemory 不支持着色器");
|
||||||
return Ptr<Shader>();
|
return Ptr<Shader>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,11 +95,11 @@ Ptr<Shader> ShaderLoader::loadFromSource(const std::string &vsSource,
|
||||||
Ptr<Shader> shader = makePtr<Shader>();
|
Ptr<Shader> shader = makePtr<Shader>();
|
||||||
|
|
||||||
if (!shader->loadFromSource(vsSource, fsSource)) {
|
if (!shader->loadFromSource(vsSource, fsSource)) {
|
||||||
E2D_LOG_ERROR("ShaderLoader: 从源码编译着色器失败");
|
E2D_ERROR("ShaderLoader: 从源码编译着色器失败");
|
||||||
return Ptr<Shader>();
|
return Ptr<Shader>();
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_DEBUG("ShaderLoader: 已从源码编译着色器");
|
E2D_DEBUG("ShaderLoader: 已从源码编译着色器");
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ Ptr<Texture> TextureLoader::load(const std::string &path) {
|
||||||
Ptr<Texture> texture = makePtr<Texture>();
|
Ptr<Texture> texture = makePtr<Texture>();
|
||||||
|
|
||||||
if (!texture->loadFromFile(path)) {
|
if (!texture->loadFromFile(path)) {
|
||||||
E2D_LOG_ERROR("TextureLoader: 从 {} 加载纹理失败", path);
|
E2D_ERROR("TextureLoader: 从 {} 加载纹理失败", path);
|
||||||
return Ptr<Texture>();
|
return Ptr<Texture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_DEBUG("TextureLoader: 已加载纹理 {}", path);
|
E2D_DEBUG("TextureLoader: 已加载纹理 {}", path);
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ Ptr<Texture> TextureLoader::loadFromMemory(const uint8_t *data, size_t size) {
|
||||||
&width, &height, &channels, 0);
|
&width, &height, &channels, 0);
|
||||||
|
|
||||||
if (!imageData) {
|
if (!imageData) {
|
||||||
E2D_LOG_ERROR("TextureLoader: 从内存解码图像失败");
|
E2D_ERROR("TextureLoader: 从内存解码图像失败");
|
||||||
return Ptr<Texture>();
|
return Ptr<Texture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,12 +54,11 @@ Ptr<Texture> TextureLoader::loadFromMemory(const uint8_t *data, size_t size) {
|
||||||
stbi_image_free(imageData);
|
stbi_image_free(imageData);
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
E2D_LOG_ERROR("TextureLoader: 从内存创建纹理失败");
|
E2D_ERROR("TextureLoader: 从内存创建纹理失败");
|
||||||
return Ptr<Texture>();
|
return Ptr<Texture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_DEBUG("TextureLoader: 已从内存创建纹理 ({}x{})", width,
|
E2D_DEBUG("TextureLoader: 已从内存创建纹理 ({}x{})", width, height);
|
||||||
height);
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,11 @@ bool FileModule::init() {
|
||||||
// 初始化 Switch 的 RomFS
|
// 初始化 Switch 的 RomFS
|
||||||
Result rc = romfsInit();
|
Result rc = romfsInit();
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
E2D_LOG_INFO("RomFS 初始化成功");
|
E2D_INFO("RomFS 初始化成功");
|
||||||
// 在 Switch 上设置资源根目录为 RomFS
|
// 在 Switch 上设置资源根目录为 RomFS
|
||||||
assetRoot_ = "romfs:/";
|
assetRoot_ = "romfs:/";
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_WARN("RomFS 初始化失败: {:#08X}", rc);
|
E2D_WARN("RomFS 初始化失败: {:#08X}", rc);
|
||||||
// RomFS 初始化失败,使用当前目录
|
// RomFS 初始化失败,使用当前目录
|
||||||
assetRoot_ = "./";
|
assetRoot_ = "./";
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +43,7 @@ bool FileModule::init() {
|
||||||
assetRoot_ = "./";
|
assetRoot_ = "./";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
E2D_LOG_INFO("资源根目录设置为: {}", assetRoot_);
|
E2D_INFO("资源根目录设置为: {}", assetRoot_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,7 +102,7 @@ bool FileModule::write(const std::string &path, const void *data,
|
||||||
size_t size) const {
|
size_t size) const {
|
||||||
// RomFS 是只读的,不允许写入
|
// RomFS 是只读的,不允许写入
|
||||||
if (isRomfsPath(path)) {
|
if (isRomfsPath(path)) {
|
||||||
E2D_LOG_WARN("无法写入 RomFS 路径: {}", path);
|
E2D_WARN("无法写入 RomFS 路径: {}", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,7 +124,7 @@ bool FileModule::append(const std::string &path, const void *data,
|
||||||
size_t size) const {
|
size_t size) const {
|
||||||
// RomFS 是只读的,不允许写入
|
// RomFS 是只读的,不允许写入
|
||||||
if (isRomfsPath(path)) {
|
if (isRomfsPath(path)) {
|
||||||
E2D_LOG_WARN("无法追加到 RomFS 路径: {}", path);
|
E2D_WARN("无法追加到 RomFS 路径: {}", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,7 +140,7 @@ bool FileModule::append(const std::string &path, const void *data,
|
||||||
bool FileModule::remove(const std::string &path) const {
|
bool FileModule::remove(const std::string &path) const {
|
||||||
// RomFS 是只读的,不允许删除
|
// RomFS 是只读的,不允许删除
|
||||||
if (isRomfsPath(path)) {
|
if (isRomfsPath(path)) {
|
||||||
E2D_LOG_WARN("无法删除 RomFS 路径: {}", path);
|
E2D_WARN("无法删除 RomFS 路径: {}", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return fs::remove(path);
|
return fs::remove(path);
|
||||||
|
|
@ -149,7 +149,7 @@ bool FileModule::remove(const std::string &path) const {
|
||||||
bool FileModule::mkdir(const std::string &path) const {
|
bool FileModule::mkdir(const std::string &path) const {
|
||||||
// RomFS 是只读的,不允许创建目录
|
// RomFS 是只读的,不允许创建目录
|
||||||
if (isRomfsPath(path)) {
|
if (isRomfsPath(path)) {
|
||||||
E2D_LOG_WARN("无法在 RomFS 中创建目录: {}", path);
|
E2D_WARN("无法在 RomFS 中创建目录: {}", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ WindowModule::~WindowModule() { shutdown(); }
|
||||||
|
|
||||||
bool WindowModule::init() {
|
bool WindowModule::init() {
|
||||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
||||||
E2D_LOG_ERROR("初始化 SDL 视频子系统失败: {}", SDL_GetError());
|
E2D_ERROR("初始化 SDL 视频子系统失败: {}", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,11 +58,11 @@ bool WindowModule::create(const WindowCfg &cfg) {
|
||||||
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
|
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
|
||||||
|
|
||||||
if (!window_) {
|
if (!window_) {
|
||||||
E2D_LOG_ERROR("创建窗口失败: {}", SDL_GetError());
|
E2D_ERROR("创建窗口失败: {}", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("窗口已创建: {}x{}", cfg.width, cfg.height);
|
E2D_INFO("窗口已创建: {}x{}", cfg.width, cfg.height);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,7 +71,7 @@ bool WindowModule::pollEvents() {
|
||||||
while (SDL_PollEvent(&evt)) {
|
while (SDL_PollEvent(&evt)) {
|
||||||
switch (evt.type) {
|
switch (evt.type) {
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
E2D_LOG_INFO("收到 SDL_QUIT 事件");
|
E2D_INFO("收到 SDL_QUIT 事件");
|
||||||
shouldClose_ = true;
|
shouldClose_ = true;
|
||||||
// 发送窗口关闭事件
|
// 发送窗口关闭事件
|
||||||
events::OnClose::emit();
|
events::OnClose::emit();
|
||||||
|
|
@ -139,10 +139,8 @@ void WindowModule::onModuleConfig(const AppConfig &config) {
|
||||||
cfg.resizable = config.resizable;
|
cfg.resizable = config.resizable;
|
||||||
|
|
||||||
if (create(cfg)) {
|
if (create(cfg)) {
|
||||||
// 发送窗口显示事件
|
// 先显示窗口,setVisible 会触发 OnShow 事件
|
||||||
// RHI 模块会监听此事件并创建 OpenGL 上下文
|
// RHI 模块会监听此事件并创建 OpenGL 上下文
|
||||||
events::OnShow::emit();
|
|
||||||
|
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -193,12 +191,10 @@ void WindowModule::setVisible(bool visible) {
|
||||||
if (window_) {
|
if (window_) {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
SDL_ShowWindow(window_);
|
SDL_ShowWindow(window_);
|
||||||
// 发送窗口显示事件
|
// SDL_WINDOWEVENT_SHOWN 事件会触发 OnShow,这里不需要手动触发
|
||||||
events::OnShow::emit();
|
|
||||||
} else {
|
} else {
|
||||||
SDL_HideWindow(window_);
|
SDL_HideWindow(window_);
|
||||||
// 发送窗口隐藏事件
|
// SDL_WINDOWEVENT_HIDDEN 事件会触发 OnHide,这里不需要手动触发
|
||||||
events::OnHide::emit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <vector>
|
|
||||||
#include <renderer/command_queue.h>
|
#include <renderer/command_queue.h>
|
||||||
#include <renderer/material.h>
|
#include <renderer/material.h>
|
||||||
#include <renderer/mesh.h>
|
#include <renderer/mesh.h>
|
||||||
#include <renderer/rhi_module.h>
|
|
||||||
#include <renderer/rhi/opengl/gl_command_list.h>
|
#include <renderer/rhi/opengl/gl_command_list.h>
|
||||||
#include <renderer/rhi/opengl/gl_texture.h>
|
#include <renderer/rhi/opengl/gl_texture.h>
|
||||||
|
#include <renderer/rhi_module.h>
|
||||||
#include <types/math/transform.h>
|
#include <types/math/transform.h>
|
||||||
#include <types/ptr/intrusive_ptr.h>
|
#include <types/ptr/intrusive_ptr.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
@ -28,14 +28,14 @@ CommandSorter::CommandSorter() {
|
||||||
|
|
||||||
CommandSorter::~CommandSorter() = default;
|
CommandSorter::~CommandSorter() = default;
|
||||||
|
|
||||||
CommandSorter::CommandSorter(CommandSorter&& other) noexcept
|
CommandSorter::CommandSorter(CommandSorter &&other) noexcept
|
||||||
: commands_(std::move(other.commands_)),
|
: commands_(std::move(other.commands_)),
|
||||||
sortedIndices_(std::move(other.sortedIndices_)),
|
sortedIndices_(std::move(other.sortedIndices_)),
|
||||||
commandCount_(other.commandCount_) {
|
commandCount_(other.commandCount_) {
|
||||||
other.commandCount_ = 0;
|
other.commandCount_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandSorter& CommandSorter::operator=(CommandSorter&& other) noexcept {
|
CommandSorter &CommandSorter::operator=(CommandSorter &&other) noexcept {
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
commands_ = std::move(other.commands_);
|
commands_ = std::move(other.commands_);
|
||||||
sortedIndices_ = std::move(other.sortedIndices_);
|
sortedIndices_ = std::move(other.sortedIndices_);
|
||||||
|
|
@ -62,15 +62,15 @@ uint32_t CommandSorter::addCommand(const DrawCommand &cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandSorter::sort() {
|
void CommandSorter::sort() {
|
||||||
if (commandCount_ == 0) return;
|
if (commandCount_ == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
// 只排序有效范围的索引
|
// 只排序有效范围的索引
|
||||||
auto indicesBegin = sortedIndices_.begin();
|
auto indicesBegin = sortedIndices_.begin();
|
||||||
auto indicesEnd = sortedIndices_.begin() + commandCount_;
|
auto indicesEnd = sortedIndices_.begin() + commandCount_;
|
||||||
|
|
||||||
// 使用稳定排序保持相同键的命令顺序
|
// 使用稳定排序保持相同键的命令顺序
|
||||||
std::stable_sort(indicesBegin, indicesEnd,
|
std::stable_sort(indicesBegin, indicesEnd, [this](uint32_t a, uint32_t b) {
|
||||||
[this](uint32_t a, uint32_t b) {
|
|
||||||
return commands_[a].key < commands_[b].key;
|
return commands_[a].key < commands_[b].key;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -198,40 +198,40 @@ bool CommandQueue::initialize() {
|
||||||
// 获取 RHI 模块
|
// 获取 RHI 模块
|
||||||
auto *rhiModule = RHIModule::get();
|
auto *rhiModule = RHIModule::get();
|
||||||
if (!rhiModule) {
|
if (!rhiModule) {
|
||||||
E2D_LOG_ERROR("RHIModule not available");
|
E2D_ERROR("RHIModule not available");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
auto *device = rhiModule->getDevice();
|
||||||
if (!device) {
|
if (!device) {
|
||||||
E2D_LOG_ERROR("RHIDevice not available");
|
E2D_ERROR("RHIDevice not available");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
context_ = rhiModule->getContext();
|
context_ = rhiModule->getContext();
|
||||||
if (!context_) {
|
if (!context_) {
|
||||||
E2D_LOG_ERROR("RHIContext not available");
|
E2D_ERROR("RHIContext not available");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建命令列表
|
// 创建命令列表
|
||||||
commandList_ = device->createCommandList();
|
commandList_ = device->createCommandList();
|
||||||
if (!commandList_) {
|
if (!commandList_) {
|
||||||
E2D_LOG_ERROR("Failed to create command list");
|
E2D_ERROR("Failed to create command list");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化 UBO 管理器
|
// 初始化 UBO 管理器
|
||||||
uboManager_ = std::make_unique<UniformBufferManager>();
|
uboManager_ = std::make_unique<UniformBufferManager>();
|
||||||
if (!uboManager_->initialize()) {
|
if (!uboManager_->initialize()) {
|
||||||
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
|
E2D_ERROR("Failed to initialize UniformBufferManager");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预分配材质 UBO 数据缓冲区
|
// 预分配材质 UBO 数据缓冲区
|
||||||
materialUBOData_.reserve(1024 * 1024); // 1MB
|
materialUBOData_.reserve(1024 * 1024); // 1MB
|
||||||
|
|
||||||
E2D_LOG_INFO("命令队列已初始化");
|
E2D_INFO("命令队列已初始化");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,7 +240,7 @@ void CommandQueue::shutdown() {
|
||||||
commandList_.reset();
|
commandList_.reset();
|
||||||
context_ = nullptr;
|
context_ = nullptr;
|
||||||
materialUBOData_.clear();
|
materialUBOData_.clear();
|
||||||
E2D_LOG_INFO("命令队列已关闭");
|
E2D_INFO("命令队列已关闭");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandQueue::beginFrame() {
|
void CommandQueue::beginFrame() {
|
||||||
|
|
@ -285,7 +285,8 @@ uint32_t CommandQueue::getMaterialId(Material *material) {
|
||||||
return id;
|
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) {
|
if (!uboManager_ || size == 0) {
|
||||||
return {nullptr, 0};
|
return {nullptr, 0};
|
||||||
}
|
}
|
||||||
|
|
@ -314,7 +315,8 @@ std::pair<UniformBuffer*, uint32_t> CommandQueue::allocateMaterialUBO(uint32_t s
|
||||||
|
|
||||||
materialUBOBufferOffset_ = offset + alignedSize;
|
materialUBOBufferOffset_ = offset + alignedSize;
|
||||||
|
|
||||||
return {nullptr, offset}; // 返回 nullptr 表示使用 CPU 缓冲区,offset 是 CPU 缓冲区偏移
|
return {nullptr,
|
||||||
|
offset}; // 返回 nullptr 表示使用 CPU 缓冲区,offset 是 CPU 缓冲区偏移
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandQueue::flushMaterialUBOToGPU() {
|
void CommandQueue::flushMaterialUBOToGPU() {
|
||||||
|
|
@ -323,20 +325,22 @@ void CommandQueue::flushMaterialUBOToGPU() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取一个足够大的 UBO
|
// 获取一个足够大的 UBO
|
||||||
currentMaterialUBO_ = uboManager_->acquireMaterialUBO(materialUBOBufferOffset_);
|
currentMaterialUBO_ =
|
||||||
|
uboManager_->acquireMaterialUBO(materialUBOBufferOffset_);
|
||||||
if (!currentMaterialUBO_) {
|
if (!currentMaterialUBO_) {
|
||||||
E2D_LOG_ERROR("获取材质 UBO 失败");
|
E2D_ERROR("获取材质 UBO 失败");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量更新到 GPU
|
// 批量更新到 GPU
|
||||||
currentMaterialUBO_->update(materialUBOData_.data(), materialUBOBufferOffset_, 0);
|
currentMaterialUBO_->update(materialUBOData_.data(), materialUBOBufferOffset_,
|
||||||
|
0);
|
||||||
|
|
||||||
// 重置 CPU 缓冲区偏移
|
// 重置 CPU 缓冲区偏移
|
||||||
materialUBOBufferOffset_ = 0;
|
materialUBOBufferOffset_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
UniformBuffer* CommandQueue::getCurrentMaterialUBO() const {
|
UniformBuffer *CommandQueue::getCurrentMaterialUBO() const {
|
||||||
return currentMaterialUBO_;
|
return currentMaterialUBO_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -386,14 +390,16 @@ void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
|
||||||
|
|
||||||
// 复制材质数据到 CPU 缓冲区,并修改颜色
|
// 复制材质数据到 CPU 缓冲区,并修改颜色
|
||||||
if (offset + materialDataSize <= materialUBOData_.size()) {
|
if (offset + materialDataSize <= materialUBOData_.size()) {
|
||||||
std::memcpy(materialUBOData_.data() + offset, material->getData(), materialDataSize);
|
std::memcpy(materialUBOData_.data() + offset, material->getData(),
|
||||||
|
materialDataSize);
|
||||||
|
|
||||||
// 将实例颜色应用到 UBO 数据中的 uColor 参数
|
// 将实例颜色应用到 UBO 数据中的 uColor 参数
|
||||||
auto layout = material->getLayout();
|
auto layout = material->getLayout();
|
||||||
if (layout) {
|
if (layout) {
|
||||||
const auto* param = layout->getParam("uColor");
|
const auto *param = layout->getParam("uColor");
|
||||||
if (param && param->type == MaterialParamType::Color) {
|
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,15 +437,17 @@ void CommandQueue::setViewport(int32_t x, int32_t y, int32_t width,
|
||||||
commandList_->setViewport(viewport);
|
commandList_->setViewport(viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandQueue::updateGlobalUBO(const Mat4& viewProjection, float deltaTime,
|
void CommandQueue::updateGlobalUBO(const Mat4 &viewProjection, float deltaTime,
|
||||||
uint32_t screenWidth, uint32_t screenHeight, uint32_t frameIndex) {
|
uint32_t screenWidth, uint32_t screenHeight,
|
||||||
|
uint32_t frameIndex) {
|
||||||
if (!uboManager_) {
|
if (!uboManager_) {
|
||||||
E2D_LOG_WARN("CommandQueue::updateGlobalUBO: uboManager 为空");
|
E2D_WARN("CommandQueue::updateGlobalUBO: uboManager 为空");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 填充全局 UBO 数据
|
// 填充全局 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[0] = 0.0f;
|
||||||
globalUBOData_.cameraPosition[1] = 0.0f;
|
globalUBOData_.cameraPosition[1] = 0.0f;
|
||||||
globalUBOData_.cameraPosition[2] = 0.0f;
|
globalUBOData_.cameraPosition[2] = 0.0f;
|
||||||
|
|
@ -450,12 +458,13 @@ void CommandQueue::updateGlobalUBO(const Mat4& viewProjection, float deltaTime,
|
||||||
globalUBOData_.screenSize[1] = static_cast<float>(screenHeight);
|
globalUBOData_.screenSize[1] = static_cast<float>(screenHeight);
|
||||||
|
|
||||||
// 使用双缓冲更新 UBO
|
// 使用双缓冲更新 UBO
|
||||||
uboManager_->updateGlobalUBO(&globalUBOData_, sizeof(globalUBOData_), frameIndex);
|
uboManager_->updateGlobalUBO(&globalUBOData_, sizeof(globalUBOData_),
|
||||||
|
frameIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandQueue::execute(uint32_t frameIndex) {
|
void CommandQueue::execute(uint32_t frameIndex) {
|
||||||
if (!commandList_) {
|
if (!commandList_) {
|
||||||
E2D_LOG_ERROR("CommandQueue::execute: commandList 为空");
|
E2D_ERROR("CommandQueue::execute: commandList 为空");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -477,7 +486,8 @@ void CommandQueue::execute(uint32_t frameIndex) {
|
||||||
commandList_->submit();
|
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_) {
|
if (!commandList_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -487,12 +497,12 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
|
||||||
commandList_->setPipeline(batch.pipeline.get());
|
commandList_->setPipeline(batch.pipeline.get());
|
||||||
stats_.pipelineBinds++;
|
stats_.pipelineBinds++;
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_WARN("批次没有有效的管线!");
|
E2D_WARN("批次没有有效的管线!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定全局 UBO (binding = 0) - 使用双缓冲
|
// 绑定全局 UBO (binding = 0) - 使用双缓冲
|
||||||
if (uboManager_) {
|
if (uboManager_) {
|
||||||
UniformBuffer* globalUBO = uboManager_->getGlobalUBO(frameIndex);
|
UniformBuffer *globalUBO = uboManager_->getGlobalUBO(frameIndex);
|
||||||
if (globalUBO) {
|
if (globalUBO) {
|
||||||
commandList_->setUniformBuffer(0, globalUBO->getRHIBuffer());
|
commandList_->setUniformBuffer(0, globalUBO->getRHIBuffer());
|
||||||
stats_.bufferBinds++;
|
stats_.bufferBinds++;
|
||||||
|
|
@ -518,7 +528,7 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
|
||||||
commandList_->setVertexBuffer(0, cmd.vertexBuffer.get(), 0);
|
commandList_->setVertexBuffer(0, cmd.vertexBuffer.get(), 0);
|
||||||
stats_.bufferBinds++;
|
stats_.bufferBinds++;
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_WARN("绘制命令没有有效的顶点缓冲区!");
|
E2D_WARN("绘制命令没有有效的顶点缓冲区!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定索引缓冲区(如果有)
|
// 绑定索引缓冲区(如果有)
|
||||||
|
|
@ -529,7 +539,9 @@ void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch,
|
||||||
|
|
||||||
// 绑定材质 UBO (binding = 1)
|
// 绑定材质 UBO (binding = 1)
|
||||||
if (cmd.materialUBOSize > 0 && currentMaterialUBO_) {
|
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++;
|
stats_.bufferBinds++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
#include <cstring>
|
||||||
#include <renderer/material.h>
|
#include <renderer/material.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -15,15 +15,15 @@ MaterialLayout::MaterialLayout() = default;
|
||||||
* @param name 参数名称
|
* @param name 参数名称
|
||||||
* @param type 参数类型
|
* @param type 参数类型
|
||||||
*/
|
*/
|
||||||
void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
|
void MaterialLayout::addParam(const std::string &name, MaterialParamType type) {
|
||||||
if (finalized_) {
|
if (finalized_) {
|
||||||
E2D_LOG_WARN("无法向已完成的材质布局添加参数");
|
E2D_WARN("无法向已完成的材质布局添加参数");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否已存在
|
// 检查是否已存在
|
||||||
if (paramIndexMap_.find(name) != paramIndexMap_.end()) {
|
if (paramIndexMap_.find(name) != paramIndexMap_.end()) {
|
||||||
E2D_LOG_WARN("参数 '{}' 已存在于材质布局中", name);
|
E2D_WARN("参数 '{}' 已存在于材质布局中", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,11 +47,12 @@ void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
|
||||||
* - 整个结构体大小必须是16的倍数
|
* - 整个结构体大小必须是16的倍数
|
||||||
*/
|
*/
|
||||||
void MaterialLayout::finalize() {
|
void MaterialLayout::finalize() {
|
||||||
if (finalized_) return;
|
if (finalized_)
|
||||||
|
return;
|
||||||
|
|
||||||
uint32_t offset = 0;
|
uint32_t offset = 0;
|
||||||
for (auto& pair : params_) {
|
for (auto &pair : params_) {
|
||||||
auto& info = pair.second;
|
auto &info = pair.second;
|
||||||
|
|
||||||
// 计算对齐要求 - std140规则
|
// 计算对齐要求 - std140规则
|
||||||
uint32_t alignment = 4; // 默认 4 字节对齐
|
uint32_t alignment = 4; // 默认 4 字节对齐
|
||||||
|
|
@ -93,7 +94,8 @@ void MaterialLayout::finalize() {
|
||||||
* @param name 参数名称
|
* @param name 参数名称
|
||||||
* @return 参数信息指针,不存在返回 nullptr
|
* @return 参数信息指针,不存在返回 nullptr
|
||||||
*/
|
*/
|
||||||
const MaterialParamInfo* MaterialLayout::getParam(const std::string& name) const {
|
const MaterialParamInfo *
|
||||||
|
MaterialLayout::getParam(const std::string &name) const {
|
||||||
auto mapIt = paramIndexMap_.find(name);
|
auto mapIt = paramIndexMap_.find(name);
|
||||||
if (mapIt != paramIndexMap_.end()) {
|
if (mapIt != paramIndexMap_.end()) {
|
||||||
return ¶ms_[mapIt->second].second;
|
return ¶ms_[mapIt->second].second;
|
||||||
|
|
@ -122,9 +124,7 @@ void Material::setLayout(Ptr<MaterialLayout> layout) {
|
||||||
* @brief 设置着色器
|
* @brief 设置着色器
|
||||||
* @param shader 着色器
|
* @param shader 着色器
|
||||||
*/
|
*/
|
||||||
void Material::setShader(Ptr<Shader> shader) {
|
void Material::setShader(Ptr<Shader> shader) { shader_ = shader; }
|
||||||
shader_ = shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取 RHI 管线句柄
|
* @brief 获取 RHI 管线句柄
|
||||||
|
|
@ -142,10 +142,11 @@ PipelineHandle Material::getPipeline() const {
|
||||||
* @param name 参数名称
|
* @param name 参数名称
|
||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
void Material::setFloat(const std::string& name, float value) {
|
void Material::setFloat(const std::string &name, float value) {
|
||||||
if (!layout_) return;
|
if (!layout_)
|
||||||
|
return;
|
||||||
|
|
||||||
const auto* param = layout_->getParam(name);
|
const auto *param = layout_->getParam(name);
|
||||||
if (param && param->type == MaterialParamType::Float) {
|
if (param && param->type == MaterialParamType::Float) {
|
||||||
std::memcpy(data_.data() + param->offset, &value, sizeof(float));
|
std::memcpy(data_.data() + param->offset, &value, sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
@ -156,10 +157,11 @@ void Material::setFloat(const std::string& name, float value) {
|
||||||
* @param name 参数名称
|
* @param name 参数名称
|
||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
void Material::setVec2(const std::string& name, const Vec2& value) {
|
void Material::setVec2(const std::string &name, const Vec2 &value) {
|
||||||
if (!layout_) return;
|
if (!layout_)
|
||||||
|
return;
|
||||||
|
|
||||||
const auto* param = layout_->getParam(name);
|
const auto *param = layout_->getParam(name);
|
||||||
if (param && param->type == MaterialParamType::Vec2) {
|
if (param && param->type == MaterialParamType::Vec2) {
|
||||||
std::memcpy(data_.data() + param->offset, &value.x, sizeof(float) * 2);
|
std::memcpy(data_.data() + param->offset, &value.x, sizeof(float) * 2);
|
||||||
}
|
}
|
||||||
|
|
@ -173,10 +175,12 @@ void Material::setVec2(const std::string& name, const Vec2& value) {
|
||||||
* @param z Z 分量
|
* @param z Z 分量
|
||||||
* @param w W 分量
|
* @param w W 分量
|
||||||
*/
|
*/
|
||||||
void Material::setVec4(const std::string& name, float x, float y, float z, float w) {
|
void Material::setVec4(const std::string &name, float x, float y, float z,
|
||||||
if (!layout_) return;
|
float w) {
|
||||||
|
if (!layout_)
|
||||||
|
return;
|
||||||
|
|
||||||
const auto* param = layout_->getParam(name);
|
const auto *param = layout_->getParam(name);
|
||||||
if (param && param->type == MaterialParamType::Vec4) {
|
if (param && param->type == MaterialParamType::Vec4) {
|
||||||
float values[4] = {x, y, z, w};
|
float values[4] = {x, y, z, w};
|
||||||
std::memcpy(data_.data() + param->offset, values, sizeof(float) * 4);
|
std::memcpy(data_.data() + param->offset, values, sizeof(float) * 4);
|
||||||
|
|
@ -188,10 +192,11 @@ void Material::setVec4(const std::string& name, float x, float y, float z, float
|
||||||
* @param name 参数名称
|
* @param name 参数名称
|
||||||
* @param value 颜色值
|
* @param value 颜色值
|
||||||
*/
|
*/
|
||||||
void Material::setColor(const std::string& name, const Color& value) {
|
void Material::setColor(const std::string &name, const Color &value) {
|
||||||
if (!layout_) return;
|
if (!layout_)
|
||||||
|
return;
|
||||||
|
|
||||||
const auto* param = layout_->getParam(name);
|
const auto *param = layout_->getParam(name);
|
||||||
if (param && param->type == MaterialParamType::Color) {
|
if (param && param->type == MaterialParamType::Color) {
|
||||||
std::memcpy(data_.data() + param->offset, &value.r, sizeof(float) * 4);
|
std::memcpy(data_.data() + param->offset, &value.r, sizeof(float) * 4);
|
||||||
}
|
}
|
||||||
|
|
@ -202,10 +207,11 @@ void Material::setColor(const std::string& name, const Color& value) {
|
||||||
* @param name 参数名称
|
* @param name 参数名称
|
||||||
* @param value 矩阵数据指针
|
* @param value 矩阵数据指针
|
||||||
*/
|
*/
|
||||||
void Material::setMat4(const std::string& name, const float* value) {
|
void Material::setMat4(const std::string &name, const float *value) {
|
||||||
if (!layout_) return;
|
if (!layout_)
|
||||||
|
return;
|
||||||
|
|
||||||
const auto* param = layout_->getParam(name);
|
const auto *param = layout_->getParam(name);
|
||||||
if (param && param->type == MaterialParamType::Mat4) {
|
if (param && param->type == MaterialParamType::Mat4) {
|
||||||
std::memcpy(data_.data() + param->offset, value, sizeof(float) * 16);
|
std::memcpy(data_.data() + param->offset, value, sizeof(float) * 16);
|
||||||
}
|
}
|
||||||
|
|
@ -217,9 +223,10 @@ void Material::setMat4(const std::string& name, const float* value) {
|
||||||
* @param texture 纹理
|
* @param texture 纹理
|
||||||
* @param slot 纹理槽位(0-15)
|
* @param slot 纹理槽位(0-15)
|
||||||
*/
|
*/
|
||||||
void Material::setTexture(const std::string& uniformName, Ptr<Texture> texture, uint32_t slot) {
|
void Material::setTexture(const std::string &uniformName, Ptr<Texture> texture,
|
||||||
|
uint32_t slot) {
|
||||||
// 查找是否已存在相同名称的纹理
|
// 查找是否已存在相同名称的纹理
|
||||||
for (auto& texSlot : textures_) {
|
for (auto &texSlot : textures_) {
|
||||||
if (texSlot.uniformName == uniformName) {
|
if (texSlot.uniformName == uniformName) {
|
||||||
texSlot.texture = texture;
|
texSlot.texture = texture;
|
||||||
texSlot.slot = slot;
|
texSlot.slot = slot;
|
||||||
|
|
@ -234,8 +241,6 @@ void Material::setTexture(const std::string& uniformName, Ptr<Texture> texture,
|
||||||
/**
|
/**
|
||||||
* @brief 清除所有纹理
|
* @brief 清除所有纹理
|
||||||
*/
|
*/
|
||||||
void Material::clearTextures() {
|
void Material::clearTextures() { textures_.clear(); }
|
||||||
textures_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,13 @@ void Mesh::setVertices(const Vertex *vertices, uint32_t count) {
|
||||||
// 获取 RHI 模块
|
// 获取 RHI 模块
|
||||||
auto *rhiModule = RHIModule::get();
|
auto *rhiModule = RHIModule::get();
|
||||||
if (!rhiModule) {
|
if (!rhiModule) {
|
||||||
E2D_LOG_ERROR("RHI 模块不可用");
|
E2D_ERROR("RHI 模块不可用");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
auto *device = rhiModule->getDevice();
|
||||||
if (!device) {
|
if (!device) {
|
||||||
E2D_LOG_ERROR("RHI 设备不可用");
|
E2D_ERROR("RHI 设备不可用");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ void Mesh::setVertices(const Vertex *vertices, uint32_t count) {
|
||||||
// 通过句柄获取缓冲区指针并更新数据
|
// 通过句柄获取缓冲区指针并更新数据
|
||||||
// 注意:这里假设可以通过某种方式获取缓冲区指针
|
// 注意:这里假设可以通过某种方式获取缓冲区指针
|
||||||
// 实际实现可能需要通过 RHI 命令列表来更新
|
// 实际实现可能需要通过 RHI 命令列表来更新
|
||||||
E2D_LOG_WARN("顶点缓冲区更新尚未实现");
|
E2D_WARN("顶点缓冲区更新尚未实现");
|
||||||
} else {
|
} else {
|
||||||
// 需要创建新缓冲区
|
// 需要创建新缓冲区
|
||||||
vertexBuffer_ = BufferHandle();
|
vertexBuffer_ = BufferHandle();
|
||||||
|
|
@ -49,7 +49,7 @@ void Mesh::setVertices(const Vertex *vertices, uint32_t count) {
|
||||||
// 创建缓冲区
|
// 创建缓冲区
|
||||||
auto buffer = device->createBuffer(desc);
|
auto buffer = device->createBuffer(desc);
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
E2D_LOG_ERROR("创建顶点缓冲区失败");
|
E2D_ERROR("创建顶点缓冲区失败");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +62,7 @@ void Mesh::setVertices(const Vertex *vertices, uint32_t count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
vertexCount_ = count;
|
vertexCount_ = count;
|
||||||
E2D_LOG_INFO("设置了 {} 个顶点", count);
|
E2D_INFO("设置了 {} 个顶点", count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::setIndices(const uint16_t *indices, uint32_t 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 模块
|
// 获取 RHI 模块
|
||||||
auto *rhiModule = RHIModule::get();
|
auto *rhiModule = RHIModule::get();
|
||||||
if (!rhiModule) {
|
if (!rhiModule) {
|
||||||
E2D_LOG_ERROR("RHI 模块不可用");
|
E2D_ERROR("RHI 模块不可用");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
auto *device = rhiModule->getDevice();
|
||||||
if (!device) {
|
if (!device) {
|
||||||
E2D_LOG_ERROR("RHI 设备不可用");
|
E2D_ERROR("RHI 设备不可用");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ void Mesh::setIndices(const uint16_t *indices, uint32_t count) {
|
||||||
|
|
||||||
// 如果缓冲区已存在且容量足够,更新数据
|
// 如果缓冲区已存在且容量足够,更新数据
|
||||||
if (indexBuffer_.isValid() && indexCapacity_ >= count) {
|
if (indexBuffer_.isValid() && indexCapacity_ >= count) {
|
||||||
E2D_LOG_WARN("索引缓冲区更新尚未实现");
|
E2D_WARN("索引缓冲区更新尚未实现");
|
||||||
} else {
|
} else {
|
||||||
// 需要创建新缓冲区
|
// 需要创建新缓冲区
|
||||||
indexBuffer_ = BufferHandle();
|
indexBuffer_ = BufferHandle();
|
||||||
|
|
@ -98,7 +98,7 @@ void Mesh::setIndices(const uint16_t *indices, uint32_t count) {
|
||||||
// 创建缓冲区
|
// 创建缓冲区
|
||||||
auto buffer = device->createBuffer(desc);
|
auto buffer = device->createBuffer(desc);
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
E2D_LOG_ERROR("创建索引缓冲区失败");
|
E2D_ERROR("创建索引缓冲区失败");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,7 +109,7 @@ void Mesh::setIndices(const uint16_t *indices, uint32_t count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
indexCount_ = count;
|
indexCount_ = count;
|
||||||
E2D_LOG_INFO("设置了 {} 个索引", count);
|
E2D_INFO("设置了 {} 个索引", count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::updateVertices(const Vertex *vertices, uint32_t count,
|
void Mesh::updateVertices(const Vertex *vertices, uint32_t count,
|
||||||
|
|
@ -118,7 +118,7 @@ void Mesh::updateVertices(const Vertex *vertices, uint32_t count,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (offset + count > vertexCount_) {
|
if (offset + count > vertexCount_) {
|
||||||
E2D_LOG_WARN("顶点更新超出边界");
|
E2D_WARN("顶点更新超出边界");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,7 +134,7 @@ void Mesh::updateVertices(const Vertex *vertices, uint32_t count,
|
||||||
// 通过句柄获取缓冲区并更新数据
|
// 通过句柄获取缓冲区并更新数据
|
||||||
// 注意:这里需要 RHI 提供更新缓冲区的方法
|
// 注意:这里需要 RHI 提供更新缓冲区的方法
|
||||||
// 暂时记录为未实现
|
// 暂时记录为未实现
|
||||||
E2D_LOG_WARN("updateVertices 尚未完全实现");
|
E2D_WARN("updateVertices 尚未完全实现");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr<Mesh> Mesh::createQuad(const Vec2 &size, const Rect &uv) {
|
Ptr<Mesh> Mesh::createQuad(const Vec2 &size, const Rect &uv) {
|
||||||
|
|
|
||||||
|
|
@ -95,11 +95,11 @@ RenderGraph &RenderGraph::operator=(RenderGraph &&other) noexcept {
|
||||||
|
|
||||||
bool RenderGraph::initialize() {
|
bool RenderGraph::initialize() {
|
||||||
if (!commandQueue_.initialize()) {
|
if (!commandQueue_.initialize()) {
|
||||||
E2D_LOG_ERROR("初始化命令队列失败");
|
E2D_ERROR("初始化命令队列失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("渲染图已初始化");
|
E2D_INFO("渲染图已初始化");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,7 +110,7 @@ void RenderGraph::shutdown() {
|
||||||
commandQueue_.shutdown();
|
commandQueue_.shutdown();
|
||||||
compiled_ = false;
|
compiled_ = false;
|
||||||
|
|
||||||
E2D_LOG_INFO("渲染图已关闭");
|
E2D_INFO("渲染图已关闭");
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderPass *RenderGraph::addPass(std::unique_ptr<RenderPass> pass) {
|
RenderPass *RenderGraph::addPass(std::unique_ptr<RenderPass> pass) {
|
||||||
|
|
@ -174,15 +174,15 @@ bool RenderGraph::compile() {
|
||||||
createResources();
|
createResources();
|
||||||
|
|
||||||
compiled_ = true;
|
compiled_ = true;
|
||||||
E2D_LOG_INFO("渲染图编译完成,共 {} 个通道", executionOrder_.size());
|
E2D_INFO("渲染图编译完成,共 {} 个通道", executionOrder_.size());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderGraph::execute(float deltaTime, const Mat4& viewProjection) {
|
void RenderGraph::execute(float deltaTime, const Mat4 &viewProjection) {
|
||||||
if (!compiled_) {
|
if (!compiled_) {
|
||||||
if (!compile()) {
|
if (!compile()) {
|
||||||
E2D_LOG_ERROR("编译渲染图失败");
|
E2D_ERROR("编译渲染图失败");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ RendererModule::RendererModule() = default;
|
||||||
RendererModule::~RendererModule() = default;
|
RendererModule::~RendererModule() = default;
|
||||||
|
|
||||||
bool RendererModule::init() {
|
bool RendererModule::init() {
|
||||||
E2D_LOG_INFO("正在初始化渲染模块...");
|
E2D_INFO("正在初始化渲染模块...");
|
||||||
|
|
||||||
// 绑定事件监听器
|
// 绑定事件监听器
|
||||||
onRenderBeginListener_.bind([this]() { onRenderBegin(); });
|
onRenderBeginListener_.bind([this]() { onRenderBegin(); });
|
||||||
|
|
@ -24,7 +24,7 @@ bool RendererModule::init() {
|
||||||
onResizeListener_.bind([this](int32 w, int32 h) { onResize(w, h); });
|
onResizeListener_.bind([this](int32 w, int32 h) { onResize(w, h); });
|
||||||
onShowListener_.bind([this]() { onWindowShow(); });
|
onShowListener_.bind([this]() { onWindowShow(); });
|
||||||
|
|
||||||
E2D_LOG_INFO("渲染模块已初始化 (等待窗口显示)");
|
E2D_INFO("渲染模块已初始化 (等待窗口显示)");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,25 +33,25 @@ void RendererModule::onWindowShow() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("正在初始化渲染模块上下文...");
|
E2D_INFO("正在初始化渲染模块上下文...");
|
||||||
|
|
||||||
// 初始化 RHI 模块
|
// 初始化 RHI 模块
|
||||||
auto *rhiModule = RHIModule::get();
|
auto *rhiModule = RHIModule::get();
|
||||||
if (!rhiModule) {
|
if (!rhiModule) {
|
||||||
E2D_LOG_ERROR("RHI 模块不可用");
|
E2D_ERROR("RHI 模块不可用");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rhiModule->getDevice()) {
|
if (!rhiModule->getDevice()) {
|
||||||
// RHI 设备尚未初始化,这是正常的时序问题
|
// RHI 设备尚未初始化,这是正常的时序问题
|
||||||
// RHIModule 会在窗口显示后初始化设备,然后再次触发此函数
|
// RHIModule 会在窗口显示后初始化设备,然后再次触发此函数
|
||||||
E2D_LOG_INFO("RHI 设备尚未就绪,等待 RHI 初始化...");
|
E2D_INFO("RHI 设备尚未就绪,等待 RHI 初始化...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化渲染图
|
// 初始化渲染图
|
||||||
if (!renderGraph_.initialize()) {
|
if (!renderGraph_.initialize()) {
|
||||||
E2D_LOG_ERROR("初始化渲染图失败");
|
E2D_ERROR("初始化渲染图失败");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,22 +61,22 @@ void RendererModule::onWindowShow() {
|
||||||
auto geometryPass = std::make_unique<GeometryRenderPass>("Geometry");
|
auto geometryPass = std::make_unique<GeometryRenderPass>("Geometry");
|
||||||
renderGraph_.addPass(std::move(geometryPass));
|
renderGraph_.addPass(std::move(geometryPass));
|
||||||
|
|
||||||
E2D_LOG_INFO("已添加默认几何渲染通道");
|
E2D_INFO("已添加默认几何渲染通道");
|
||||||
|
|
||||||
auto windowModule = getModule<WindowModule>();
|
auto windowModule = getModule<WindowModule>();
|
||||||
if (!windowModule) {
|
if (!windowModule) {
|
||||||
E2D_LOG_ERROR("窗口模块不可用");
|
E2D_ERROR("窗口模块不可用");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setViewport(0, 0, static_cast<int32>(windowModule->getSize().w),
|
setViewport(0, 0, static_cast<int32>(windowModule->getSize().w),
|
||||||
static_cast<int32>(windowModule->getSize().h));
|
static_cast<int32>(windowModule->getSize().h));
|
||||||
|
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
E2D_LOG_INFO("渲染模块上下文初始化成功");
|
E2D_INFO("渲染模块上下文初始化成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::shutdown() {
|
void RendererModule::shutdown() {
|
||||||
E2D_LOG_INFO("正在关闭渲染模块...");
|
E2D_INFO("正在关闭渲染模块...");
|
||||||
|
|
||||||
renderGraph_.shutdown();
|
renderGraph_.shutdown();
|
||||||
commandQueue_ = nullptr;
|
commandQueue_ = nullptr;
|
||||||
|
|
@ -84,7 +84,7 @@ void RendererModule::shutdown() {
|
||||||
|
|
||||||
// 注意:事件监听器是值类型,会在析构时自动清理
|
// 注意:事件监听器是值类型,会在析构时自动清理
|
||||||
|
|
||||||
E2D_LOG_INFO("渲染模块关闭完成");
|
E2D_INFO("渲染模块关闭完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
RHIContext *RendererModule::getRHIContext() const {
|
RHIContext *RendererModule::getRHIContext() const {
|
||||||
|
|
@ -162,7 +162,7 @@ void RendererModule::onRenderBegin() {
|
||||||
|
|
||||||
void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
||||||
if (!initialized_ || !commandQueue_) {
|
if (!initialized_ || !commandQueue_) {
|
||||||
E2D_LOG_WARN("onRenderSubmit: 渲染模块未初始化或没有命令队列");
|
E2D_WARN("onRenderSubmit: 渲染模块未初始化或没有命令队列");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,8 +194,8 @@ void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
||||||
transform, cmd.drawMesh.color);
|
transform, cmd.drawMesh.color);
|
||||||
stats_.commandsSubmitted++;
|
stats_.commandsSubmitted++;
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_WARN("提交绘制命令失败: 材质={}, 网格={}",
|
E2D_WARN("提交绘制命令失败: 材质={}, 网格={}", material ? "有效" : "空",
|
||||||
material ? "有效" : "空", mesh ? "有效" : "空");
|
mesh ? "有效" : "空");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -226,7 +226,7 @@ void RendererModule::onRenderSetCamera(const Mat4 &viewProj) {
|
||||||
|
|
||||||
void RendererModule::onRenderEnd() {
|
void RendererModule::onRenderEnd() {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
E2D_LOG_WARN("onRenderEnd: 渲染模块未初始化");
|
E2D_WARN("onRenderEnd: 渲染模块未初始化");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,7 +241,7 @@ void RendererModule::onRenderEnd() {
|
||||||
// 每60帧输出一次统计信息
|
// 每60帧输出一次统计信息
|
||||||
static uint32_t frameCount = 0;
|
static uint32_t frameCount = 0;
|
||||||
if (++frameCount % 60 == 0) {
|
if (++frameCount % 60 == 0) {
|
||||||
E2D_LOG_INFO("渲染统计: 绘制调用={}, 三角形数={}, 顶点数={}, "
|
E2D_INFO("渲染统计: 绘制调用={}, 三角形数={}, 顶点数={}, "
|
||||||
"管线绑定={}, 纹理绑定={}, 缓冲区绑定={}",
|
"管线绑定={}, 纹理绑定={}, 缓冲区绑定={}",
|
||||||
stats.drawCalls, stats.triangles, stats.vertices,
|
stats.drawCalls, stats.triangles, stats.vertices,
|
||||||
stats.pipelineBinds, stats.textureBinds, stats.bufferBinds);
|
stats.pipelineBinds, stats.textureBinds, stats.bufferBinds);
|
||||||
|
|
|
||||||
|
|
@ -12,22 +12,22 @@ GLContext::GLContext(GLDevice *device)
|
||||||
GLContext::~GLContext() = default;
|
GLContext::~GLContext() = default;
|
||||||
|
|
||||||
bool GLContext::initialize() {
|
bool GLContext::initialize() {
|
||||||
E2D_LOG_INFO("正在初始化 OpenGL 上下文...");
|
E2D_INFO("正在初始化 OpenGL 上下文...");
|
||||||
|
|
||||||
// 创建默认帧缓冲(窗口帧缓冲)
|
// 创建默认帧缓冲(窗口帧缓冲)
|
||||||
defaultFramebuffer_ = std::make_unique<GLFramebuffer>();
|
defaultFramebuffer_ = std::make_unique<GLFramebuffer>();
|
||||||
defaultFramebuffer_->setSize(800, 600); // 默认大小,会被更新
|
defaultFramebuffer_->setSize(800, 600); // 默认大小,会被更新
|
||||||
|
|
||||||
E2D_LOG_INFO("OpenGL 上下文初始化完成");
|
E2D_INFO("OpenGL 上下文初始化完成");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLContext::shutdown() {
|
void GLContext::shutdown() {
|
||||||
E2D_LOG_INFO("正在关闭 OpenGL 上下文...");
|
E2D_INFO("正在关闭 OpenGL 上下文...");
|
||||||
|
|
||||||
defaultFramebuffer_.reset();
|
defaultFramebuffer_.reset();
|
||||||
|
|
||||||
E2D_LOG_INFO("OpenGL 上下文关闭完成");
|
E2D_INFO("OpenGL 上下文关闭完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLContext::beginFrame() {
|
void GLContext::beginFrame() {
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
#include <renderer/rhi/opengl/gl_texture.h>
|
#include <renderer/rhi/opengl/gl_texture.h>
|
||||||
#include <renderer/rhi/opengl/gl_utils.h>
|
#include <renderer/rhi/opengl/gl_utils.h>
|
||||||
|
|
||||||
#include <glad/glad.h>
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -21,19 +21,20 @@ class GLDevice;
|
||||||
*/
|
*/
|
||||||
class GLDevice : public RHIDevice {
|
class GLDevice : public RHIDevice {
|
||||||
public:
|
public:
|
||||||
GLDevice() : window_(nullptr), glContext_(nullptr), context_(nullptr), stats_() {}
|
GLDevice()
|
||||||
|
: window_(nullptr), glContext_(nullptr), context_(nullptr), stats_() {}
|
||||||
|
|
||||||
~GLDevice() override { shutdown(); }
|
~GLDevice() override { shutdown(); }
|
||||||
|
|
||||||
bool initialize(void* nativeWindow) override {
|
bool initialize(void *nativeWindow) override {
|
||||||
E2D_LOG_INFO("正在初始化 OpenGL 设备...");
|
E2D_INFO("正在初始化 OpenGL 设备...");
|
||||||
|
|
||||||
if (!nativeWindow) {
|
if (!nativeWindow) {
|
||||||
E2D_LOG_ERROR("原生窗口句柄为空");
|
E2D_ERROR("原生窗口句柄为空");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_ = static_cast<SDL_Window*>(nativeWindow);
|
window_ = static_cast<SDL_Window *>(nativeWindow);
|
||||||
|
|
||||||
// 创建 OpenGL ES 3.2 上下文
|
// 创建 OpenGL ES 3.2 上下文
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||||
|
|
@ -45,38 +46,39 @@ public:
|
||||||
|
|
||||||
glContext_ = SDL_GL_CreateContext(window_);
|
glContext_ = SDL_GL_CreateContext(window_);
|
||||||
if (!glContext_) {
|
if (!glContext_) {
|
||||||
E2D_LOG_ERROR("创建 OpenGL ES 上下文失败: {}", SDL_GetError());
|
E2D_ERROR("创建 OpenGL ES 上下文失败: {}", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化 GLAD
|
// 初始化 GLAD
|
||||||
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
|
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
|
||||||
E2D_LOG_ERROR("初始化 GLAD 失败");
|
E2D_ERROR("初始化 GLAD 失败");
|
||||||
SDL_GL_DeleteContext(glContext_);
|
SDL_GL_DeleteContext(glContext_);
|
||||||
glContext_ = nullptr;
|
glContext_ = nullptr;
|
||||||
return false;
|
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) {
|
if (!version) {
|
||||||
E2D_LOG_ERROR("获取 OpenGL 版本失败");
|
E2D_ERROR("获取 OpenGL 版本失败");
|
||||||
SDL_GL_DeleteContext(glContext_);
|
SDL_GL_DeleteContext(glContext_);
|
||||||
glContext_ = nullptr;
|
glContext_ = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("OpenGL 版本: {}", version);
|
E2D_INFO("OpenGL 版本: {}", version);
|
||||||
E2D_LOG_INFO("OpenGL 供应商: {}",
|
E2D_INFO("OpenGL 供应商: {}",
|
||||||
reinterpret_cast<const char *>(glGetString(GL_VENDOR)));
|
reinterpret_cast<const char *>(glGetString(GL_VENDOR)));
|
||||||
E2D_LOG_INFO("OpenGL 渲染器: {}",
|
E2D_INFO("OpenGL 渲染器: {}",
|
||||||
reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
|
reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
|
||||||
|
|
||||||
// 创建上下文管理器
|
// 创建上下文管理器
|
||||||
context_ = std::make_unique<GLContext>(this);
|
context_ = std::make_unique<GLContext>(this);
|
||||||
if (!context_->initialize()) {
|
if (!context_->initialize()) {
|
||||||
E2D_LOG_ERROR("初始化 GL 上下文失败");
|
E2D_ERROR("初始化 GL 上下文失败");
|
||||||
SDL_GL_DeleteContext(glContext_);
|
SDL_GL_DeleteContext(glContext_);
|
||||||
glContext_ = nullptr;
|
glContext_ = nullptr;
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -86,12 +88,12 @@ public:
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
E2D_LOG_INFO("OpenGL 设备初始化成功");
|
E2D_INFO("OpenGL 设备初始化成功");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void shutdown() override {
|
void shutdown() override {
|
||||||
E2D_LOG_INFO("正在关闭 OpenGL 设备...");
|
E2D_INFO("正在关闭 OpenGL 设备...");
|
||||||
|
|
||||||
if (context_) {
|
if (context_) {
|
||||||
context_->shutdown();
|
context_->shutdown();
|
||||||
|
|
@ -105,7 +107,7 @@ public:
|
||||||
|
|
||||||
window_ = nullptr;
|
window_ = nullptr;
|
||||||
|
|
||||||
E2D_LOG_INFO("OpenGL 设备关闭完成");
|
E2D_INFO("OpenGL 设备关闭完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<RHIBuffer> createBuffer(const BufferDesc &desc) override {
|
std::unique_ptr<RHIBuffer> createBuffer(const BufferDesc &desc) override {
|
||||||
|
|
@ -127,7 +129,7 @@ public:
|
||||||
std::unique_ptr<RHIShader> createShader(const ShaderDesc &desc) override {
|
std::unique_ptr<RHIShader> createShader(const ShaderDesc &desc) override {
|
||||||
auto shader = std::make_unique<GLShader>(desc);
|
auto shader = std::make_unique<GLShader>(desc);
|
||||||
if (!shader->compile()) {
|
if (!shader->compile()) {
|
||||||
E2D_LOG_ERROR("着色器编译失败: {}", shader->getCompileLog());
|
E2D_ERROR("着色器编译失败: {}", shader->getCompileLog());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return shader;
|
return shader;
|
||||||
|
|
@ -142,9 +144,9 @@ public:
|
||||||
|
|
||||||
// 获取 vertex shader 的 program ID
|
// 获取 vertex shader 的 program ID
|
||||||
if (desc.vertexShader.isValid()) {
|
if (desc.vertexShader.isValid()) {
|
||||||
RHIShader* shader = desc.vertexShader.get();
|
RHIShader *shader = desc.vertexShader.get();
|
||||||
if (shader) {
|
if (shader) {
|
||||||
auto* glShader = static_cast<GLShader*>(shader);
|
auto *glShader = static_cast<GLShader *>(shader);
|
||||||
pipeline->setGLProgram(glShader->getGLProgram());
|
pipeline->setGLProgram(glShader->getGLProgram());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -186,7 +188,7 @@ public:
|
||||||
void incrementRenderPassSwitches() { stats_.renderPassSwitches++; }
|
void incrementRenderPassSwitches() { stats_.renderPassSwitches++; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SDL_Window* window_;
|
SDL_Window *window_;
|
||||||
SDL_GLContext glContext_;
|
SDL_GLContext glContext_;
|
||||||
std::unique_ptr<GLContext> context_;
|
std::unique_ptr<GLContext> context_;
|
||||||
RenderStats stats_;
|
RenderStats stats_;
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,13 @@ RHIModule::~RHIModule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RHIModule::init() {
|
bool RHIModule::init() {
|
||||||
E2D_LOG_INFO("正在初始化 RHI 模块...");
|
E2D_INFO("正在初始化 RHI 模块...");
|
||||||
|
|
||||||
// 注册窗口显示事件监听,在窗口显示时创建 OpenGL 上下文
|
// 注册窗口显示事件监听,在窗口显示时创建 OpenGL 上下文
|
||||||
onShowListener_ = std::make_unique<events::OnShow::Listener>();
|
onShowListener_ = std::make_unique<events::OnShow::Listener>();
|
||||||
onShowListener_->bind([this]() { this->onWindowShow(); });
|
onShowListener_->bind([this]() { this->onWindowShow(); });
|
||||||
|
|
||||||
E2D_LOG_INFO("RHI 模块已初始化 (等待窗口显示)");
|
E2D_INFO("RHI 模块已初始化 (等待窗口显示)");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,54 +41,45 @@ void RHIModule::onWindowShow() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("RHI 模块: 正在创建 OpenGL 设备...");
|
E2D_INFO("RHI 模块: 正在创建 OpenGL 设备...");
|
||||||
|
|
||||||
// 获取窗口模块
|
// 获取窗口模块
|
||||||
auto *windowModule = getModule<WindowModule>();
|
auto *windowModule = getModule<WindowModule>();
|
||||||
if (!windowModule) {
|
if (!windowModule) {
|
||||||
E2D_LOG_ERROR("窗口模块不可用");
|
E2D_ERROR("窗口模块不可用");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待窗口创建完成
|
// 等待窗口创建完成
|
||||||
if (!windowModule->handle()) {
|
if (!windowModule->handle()) {
|
||||||
E2D_LOG_ERROR("窗口尚未创建");
|
E2D_ERROR("窗口尚未创建");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 OpenGL 设备并传入窗口句柄
|
// 创建 OpenGL 设备并传入窗口句柄
|
||||||
device_ = CreateGLDevice();
|
device_ = CreateGLDevice();
|
||||||
if (!device_) {
|
if (!device_) {
|
||||||
E2D_LOG_ERROR("创建 RHI 设备失败");
|
E2D_ERROR("创建 RHI 设备失败");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!device_->initialize(windowModule->handle())) {
|
if (!device_->initialize(windowModule->handle())) {
|
||||||
E2D_LOG_ERROR("初始化 RHI 设备失败");
|
E2D_ERROR("初始化 RHI 设备失败");
|
||||||
device_.reset();
|
device_.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context_ = device_->getContext();
|
context_ = device_->getContext();
|
||||||
if (!context_) {
|
if (!context_) {
|
||||||
E2D_LOG_ERROR("获取 RHI 上下文失败");
|
E2D_ERROR("获取 RHI 上下文失败");
|
||||||
device_->shutdown();
|
device_->shutdown();
|
||||||
device_.reset();
|
device_.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 注意:context 已在 GLDevice::initialize() 中初始化,这里不需要再次初始化
|
||||||
// 初始化上下文
|
|
||||||
if (!context_->initialize()) {
|
|
||||||
E2D_LOG_ERROR("初始化 RHI 上下文失败");
|
|
||||||
device_->shutdown();
|
|
||||||
device_.reset();
|
|
||||||
context_ = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
E2D_LOG_INFO("RHIModule initialized successfully (backend: {})",
|
E2D_INFO("RHI模块 初始化成功 (后端:{})", device_->getBackendName());
|
||||||
device_->getBackendName());
|
|
||||||
|
|
||||||
// RHI 初始化完成,再次触发 OnShow 事件通知其他模块
|
// RHI 初始化完成,再次触发 OnShow 事件通知其他模块
|
||||||
// 这样 RendererModule 等依赖 RHI 的模块可以完成初始化
|
// 这样 RendererModule 等依赖 RHI 的模块可以完成初始化
|
||||||
|
|
@ -96,7 +87,7 @@ void RHIModule::onWindowShow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RHIModule::shutdown() {
|
void RHIModule::shutdown() {
|
||||||
E2D_LOG_INFO("Shutting down RHIModule...");
|
E2D_INFO("正在关闭 RHI模块...");
|
||||||
|
|
||||||
// 清理事件监听器
|
// 清理事件监听器
|
||||||
onShowListener_.reset();
|
onShowListener_.reset();
|
||||||
|
|
@ -113,14 +104,14 @@ void RHIModule::shutdown() {
|
||||||
|
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
|
|
||||||
E2D_LOG_INFO("RHIModule shutdown complete");
|
E2D_INFO("RHI模块 关闭完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
RHIModule *RHIModule::get() { return g_rhiModule; }
|
RHIModule *RHIModule::get() { return g_rhiModule; }
|
||||||
|
|
||||||
std::unique_ptr<RHIBuffer> RHIModule::createBuffer(const BufferDesc &desc) {
|
std::unique_ptr<RHIBuffer> RHIModule::createBuffer(const BufferDesc &desc) {
|
||||||
if (!device_) {
|
if (!device_) {
|
||||||
E2D_LOG_ERROR("Cannot create buffer: RHI device not initialized");
|
E2D_ERROR("无法创建缓冲区:RHI 设备未初始化");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return device_->createBuffer(desc);
|
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) {
|
std::unique_ptr<RHITexture> RHIModule::createTexture(const TextureDesc &desc) {
|
||||||
if (!device_) {
|
if (!device_) {
|
||||||
E2D_LOG_ERROR("无法创建纹理: RHI 设备未初始化");
|
E2D_ERROR("无法创建纹理: RHI 设备未初始化");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return device_->createTexture(desc);
|
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) {
|
std::unique_ptr<RHIShader> RHIModule::createShader(const ShaderDesc &desc) {
|
||||||
if (!device_) {
|
if (!device_) {
|
||||||
E2D_LOG_ERROR("无法创建着色器: RHI 设备未初始化");
|
E2D_ERROR("无法创建着色器: RHI 设备未初始化");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return device_->createShader(desc);
|
return device_->createShader(desc);
|
||||||
|
|
@ -145,7 +136,7 @@ std::unique_ptr<RHIShader> RHIModule::createShader(const ShaderDesc &desc) {
|
||||||
std::unique_ptr<RHIPipeline>
|
std::unique_ptr<RHIPipeline>
|
||||||
RHIModule::createPipeline(const PipelineDesc &desc) {
|
RHIModule::createPipeline(const PipelineDesc &desc) {
|
||||||
if (!device_) {
|
if (!device_) {
|
||||||
E2D_LOG_ERROR("无法创建管线: RHI 设备未初始化");
|
E2D_ERROR("无法创建管线: RHI 设备未初始化");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return device_->createPipeline(desc);
|
return device_->createPipeline(desc);
|
||||||
|
|
@ -154,7 +145,7 @@ RHIModule::createPipeline(const PipelineDesc &desc) {
|
||||||
std::unique_ptr<RHIFramebuffer>
|
std::unique_ptr<RHIFramebuffer>
|
||||||
RHIModule::createFramebuffer(const RenderPassDesc &desc) {
|
RHIModule::createFramebuffer(const RenderPassDesc &desc) {
|
||||||
if (!device_) {
|
if (!device_) {
|
||||||
E2D_LOG_ERROR("无法创建帧缓冲区: RHI 设备未初始化");
|
E2D_ERROR("无法创建帧缓冲区: RHI 设备未初始化");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return device_->createFramebuffer(desc);
|
return device_->createFramebuffer(desc);
|
||||||
|
|
@ -162,7 +153,7 @@ RHIModule::createFramebuffer(const RenderPassDesc &desc) {
|
||||||
|
|
||||||
std::unique_ptr<RHICommandList> RHIModule::createCommandList() {
|
std::unique_ptr<RHICommandList> RHIModule::createCommandList() {
|
||||||
if (!device_) {
|
if (!device_) {
|
||||||
E2D_LOG_ERROR("无法创建命令列表: RHI 设备未初始化");
|
E2D_ERROR("无法创建命令列表: RHI 设备未初始化");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return device_->createCommandList();
|
return device_->createCommandList();
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@ bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
|
||||||
// 获取 RHI 设备
|
// 获取 RHI 设备
|
||||||
auto *rhiModule = RHIModule::get();
|
auto *rhiModule = RHIModule::get();
|
||||||
if (!rhiModule) {
|
if (!rhiModule) {
|
||||||
E2D_LOG_ERROR("RHI 模块不可用");
|
E2D_ERROR("RHI 模块不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
auto *device = rhiModule->getDevice();
|
||||||
if (!device) {
|
if (!device) {
|
||||||
E2D_LOG_ERROR("RHI 设备不可用");
|
E2D_ERROR("RHI 设备不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
|
||||||
// 创建着色器
|
// 创建着色器
|
||||||
auto shader = device->createShader(shaderDesc);
|
auto shader = device->createShader(shaderDesc);
|
||||||
if (!shader) {
|
if (!shader) {
|
||||||
E2D_LOG_ERROR("创建着色器失败");
|
E2D_ERROR("创建着色器失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,7 +76,7 @@ bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
|
||||||
// 创建管线
|
// 创建管线
|
||||||
auto pipeline = device->createPipeline(pipelineDesc);
|
auto pipeline = device->createPipeline(pipelineDesc);
|
||||||
if (!pipeline) {
|
if (!pipeline) {
|
||||||
E2D_LOG_ERROR("创建管线失败");
|
E2D_ERROR("创建管线失败");
|
||||||
handle_ = ShaderHandle();
|
handle_ = ShaderHandle();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +84,7 @@ bool Shader::loadFromSourceWithLayout(const std::string &vsSource,
|
||||||
// 获取管线句柄
|
// 获取管线句柄
|
||||||
pipeline_ = PipelineHandle(pipeline.release());
|
pipeline_ = PipelineHandle(pipeline.release());
|
||||||
|
|
||||||
E2D_LOG_INFO("着色器创建成功");
|
E2D_INFO("着色器创建成功");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,13 +127,13 @@ bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
|
||||||
// 获取 RHI 设备
|
// 获取 RHI 设备
|
||||||
auto *rhiModule = RHIModule::get();
|
auto *rhiModule = RHIModule::get();
|
||||||
if (!rhiModule) {
|
if (!rhiModule) {
|
||||||
E2D_LOG_ERROR("RHI 模块不可用");
|
E2D_ERROR("RHI 模块不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
auto *device = rhiModule->getDevice();
|
||||||
if (!device) {
|
if (!device) {
|
||||||
E2D_LOG_ERROR("RHI 设备不可用");
|
E2D_ERROR("RHI 设备不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,7 +149,7 @@ bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
|
||||||
// 创建着色器
|
// 创建着色器
|
||||||
auto shader = device->createShader(shaderDesc);
|
auto shader = device->createShader(shaderDesc);
|
||||||
if (!shader) {
|
if (!shader) {
|
||||||
E2D_LOG_ERROR("重载时创建着色器失败");
|
E2D_ERROR("重载时创建着色器失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,7 +168,7 @@ bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
|
||||||
// 创建管线
|
// 创建管线
|
||||||
auto pipeline = device->createPipeline(pipelineDesc);
|
auto pipeline = device->createPipeline(pipelineDesc);
|
||||||
if (!pipeline) {
|
if (!pipeline) {
|
||||||
E2D_LOG_ERROR("重载时创建管线失败");
|
E2D_ERROR("重载时创建管线失败");
|
||||||
handle_ = ShaderHandle();
|
handle_ = ShaderHandle();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -176,7 +176,7 @@ bool Shader::reloadFromSourceWithLayout(const std::string &vsSource,
|
||||||
// 获取管线句柄
|
// 获取管线句柄
|
||||||
pipeline_ = PipelineHandle(pipeline.release());
|
pipeline_ = PipelineHandle(pipeline.release());
|
||||||
|
|
||||||
E2D_LOG_INFO("着色器重载成功");
|
E2D_INFO("着色器重载成功");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#include <renderer/texture.h>
|
|
||||||
#include <renderer/rhi_module.h>
|
#include <renderer/rhi_module.h>
|
||||||
|
#include <renderer/texture.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <stb/stb_image.h>
|
#include <stb/stb_image.h>
|
||||||
|
|
@ -14,38 +14,50 @@ Texture::~Texture() {
|
||||||
handle_ = TextureHandle();
|
handle_ = TextureHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::loadFromFile(const std::string& path) {
|
bool Texture::loadFromFile(const std::string &path) {
|
||||||
// 加载图片
|
// 加载图片
|
||||||
int channels;
|
int channels;
|
||||||
stbi_set_flip_vertically_on_load(true);
|
stbi_set_flip_vertically_on_load(true);
|
||||||
unsigned char* data = stbi_load(path.c_str(), &width_, &height_, &channels, 0);
|
unsigned char *data =
|
||||||
|
stbi_load(path.c_str(), &width_, &height_, &channels, 0);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
E2D_LOG_ERROR("加载纹理失败: {}", path);
|
E2D_ERROR("加载纹理失败: {}", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据通道数确定格式
|
// 根据通道数确定格式
|
||||||
TextureFormat format;
|
TextureFormat format;
|
||||||
switch (channels) {
|
switch (channels) {
|
||||||
case 1: format = TextureFormat::R8; break;
|
case 1:
|
||||||
case 2: format = TextureFormat::RG8; break;
|
format = TextureFormat::R8;
|
||||||
case 3: format = TextureFormat::RGB8; break;
|
break;
|
||||||
case 4: format = TextureFormat::RGBA8; break;
|
case 2:
|
||||||
default: format = TextureFormat::RGBA8; break;
|
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);
|
bool result = loadFromMemory(data, width_, height_, format);
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
E2D_LOG_DEBUG("纹理已加载: {} ({}x{})", path, width_, height_);
|
E2D_DEBUG("纹理已加载: {} ({}x{})", path, width_, height_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::loadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
|
bool Texture::loadFromMemory(const uint8_t *data, int width, int height,
|
||||||
|
TextureFormat format) {
|
||||||
// 释放旧纹理
|
// 释放旧纹理
|
||||||
handle_ = TextureHandle();
|
handle_ = TextureHandle();
|
||||||
|
|
||||||
|
|
@ -54,15 +66,15 @@ bool Texture::loadFromMemory(const uint8_t* data, int width, int height, Texture
|
||||||
format_ = format;
|
format_ = format;
|
||||||
|
|
||||||
// 获取 RHI 设备
|
// 获取 RHI 设备
|
||||||
auto* rhiModule = RHIModule::get();
|
auto *rhiModule = RHIModule::get();
|
||||||
if (!rhiModule) {
|
if (!rhiModule) {
|
||||||
E2D_LOG_ERROR("RHI 模块不可用");
|
E2D_ERROR("RHI 模块不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* device = rhiModule->getDevice();
|
auto *device = rhiModule->getDevice();
|
||||||
if (!device) {
|
if (!device) {
|
||||||
E2D_LOG_ERROR("RHI 设备不可用");
|
E2D_ERROR("RHI 设备不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,7 +88,7 @@ bool Texture::loadFromMemory(const uint8_t* data, int width, int height, Texture
|
||||||
// 创建 RHI 纹理
|
// 创建 RHI 纹理
|
||||||
auto texture = device->createTexture(desc);
|
auto texture = device->createTexture(desc);
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
E2D_LOG_ERROR("创建 RHI 纹理失败");
|
E2D_ERROR("创建 RHI 纹理失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +96,8 @@ bool Texture::loadFromMemory(const uint8_t* data, int width, int height, Texture
|
||||||
if (data) {
|
if (data) {
|
||||||
// 注意:update 方法的参数需要根据实际 RHI 接口调整
|
// 注意:update 方法的参数需要根据实际 RHI 接口调整
|
||||||
// 这里假设 update 接受数据指针
|
// 这里假设 update 接受数据指针
|
||||||
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
|
texture->update(
|
||||||
|
data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
|
||||||
// 生成 mipmap
|
// 生成 mipmap
|
||||||
// 注意:generateMipmap 方法需要在 RHI 接口中实现
|
// 注意:generateMipmap 方法需要在 RHI 接口中实现
|
||||||
}
|
}
|
||||||
|
|
@ -104,15 +117,15 @@ bool Texture::create(int width, int height, TextureFormat format) {
|
||||||
format_ = format;
|
format_ = format;
|
||||||
|
|
||||||
// 获取 RHI 设备
|
// 获取 RHI 设备
|
||||||
auto* rhiModule = RHIModule::get();
|
auto *rhiModule = RHIModule::get();
|
||||||
if (!rhiModule) {
|
if (!rhiModule) {
|
||||||
E2D_LOG_ERROR("RHI 模块不可用");
|
E2D_ERROR("RHI 模块不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* device = rhiModule->getDevice();
|
auto *device = rhiModule->getDevice();
|
||||||
if (!device) {
|
if (!device) {
|
||||||
E2D_LOG_ERROR("RHI 设备不可用");
|
E2D_ERROR("RHI 设备不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,7 +139,7 @@ bool Texture::create(int width, int height, TextureFormat format) {
|
||||||
// 创建 RHI 纹理
|
// 创建 RHI 纹理
|
||||||
auto texture = device->createTexture(desc);
|
auto texture = device->createTexture(desc);
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
E2D_LOG_ERROR("创建 RHI 纹理失败");
|
E2D_ERROR("创建 RHI 纹理失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,53 +149,65 @@ bool Texture::create(int width, int height, TextureFormat format) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::reloadFromFile(const std::string& path) {
|
bool Texture::reloadFromFile(const std::string &path) {
|
||||||
// 加载图片
|
// 加载图片
|
||||||
int channels;
|
int channels;
|
||||||
stbi_set_flip_vertically_on_load(true);
|
stbi_set_flip_vertically_on_load(true);
|
||||||
unsigned char* data = stbi_load(path.c_str(), &width_, &height_, &channels, 0);
|
unsigned char *data =
|
||||||
|
stbi_load(path.c_str(), &width_, &height_, &channels, 0);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
E2D_LOG_ERROR("重载纹理失败: {}", path);
|
E2D_ERROR("重载纹理失败: {}", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据通道数确定格式
|
// 根据通道数确定格式
|
||||||
TextureFormat format;
|
TextureFormat format;
|
||||||
switch (channels) {
|
switch (channels) {
|
||||||
case 1: format = TextureFormat::R8; break;
|
case 1:
|
||||||
case 2: format = TextureFormat::RG8; break;
|
format = TextureFormat::R8;
|
||||||
case 3: format = TextureFormat::RGB8; break;
|
break;
|
||||||
case 4: format = TextureFormat::RGBA8; break;
|
case 2:
|
||||||
default: format = TextureFormat::RGBA8; break;
|
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);
|
bool result = reloadFromMemory(data, width_, height_, format);
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
E2D_LOG_INFO("纹理已重载: {} ({}x{})", path, width_, height_);
|
E2D_INFO("纹理已重载: {} ({}x{})", path, width_, height_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::reloadFromMemory(const uint8_t* data, int width, int height, TextureFormat format) {
|
bool Texture::reloadFromMemory(const uint8_t *data, int width, int height,
|
||||||
|
TextureFormat format) {
|
||||||
// 更新尺寸和格式
|
// 更新尺寸和格式
|
||||||
width_ = width;
|
width_ = width;
|
||||||
height_ = height;
|
height_ = height;
|
||||||
format_ = format;
|
format_ = format;
|
||||||
|
|
||||||
// 获取 RHI 设备
|
// 获取 RHI 设备
|
||||||
auto* rhiModule = RHIModule::get();
|
auto *rhiModule = RHIModule::get();
|
||||||
if (!rhiModule) {
|
if (!rhiModule) {
|
||||||
E2D_LOG_ERROR("RHI 模块不可用");
|
E2D_ERROR("RHI 模块不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* device = rhiModule->getDevice();
|
auto *device = rhiModule->getDevice();
|
||||||
if (!device) {
|
if (!device) {
|
||||||
E2D_LOG_ERROR("RHI 设备不可用");
|
E2D_ERROR("RHI 设备不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,13 +221,14 @@ bool Texture::reloadFromMemory(const uint8_t* data, int width, int height, Textu
|
||||||
// 创建新的 RHI 纹理
|
// 创建新的 RHI 纹理
|
||||||
auto texture = device->createTexture(desc);
|
auto texture = device->createTexture(desc);
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
E2D_LOG_ERROR("重载时创建 RHI 纹理失败");
|
E2D_ERROR("重载时创建 RHI 纹理失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传纹理数据
|
// 上传纹理数据
|
||||||
if (data) {
|
if (data) {
|
||||||
texture->update(data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
|
texture->update(
|
||||||
|
data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 替换旧的纹理句柄
|
// 替换旧的纹理句柄
|
||||||
|
|
@ -214,17 +240,27 @@ bool Texture::reloadFromMemory(const uint8_t* data, int width, int height, Textu
|
||||||
// 辅助函数:获取每个像素的字节数
|
// 辅助函数:获取每个像素的字节数
|
||||||
uint32_t Texture::getBytesPerPixel(TextureFormat format) {
|
uint32_t Texture::getBytesPerPixel(TextureFormat format) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case TextureFormat::R8: return 1;
|
case TextureFormat::R8:
|
||||||
case TextureFormat::RG8: return 2;
|
return 1;
|
||||||
case TextureFormat::RGB8: return 3;
|
case TextureFormat::RG8:
|
||||||
|
return 2;
|
||||||
|
case TextureFormat::RGB8:
|
||||||
|
return 3;
|
||||||
case TextureFormat::RGBA8:
|
case TextureFormat::RGBA8:
|
||||||
case TextureFormat::RGBA8_SRGB: return 4;
|
case TextureFormat::RGBA8_SRGB:
|
||||||
case TextureFormat::Depth16: return 2;
|
return 4;
|
||||||
case TextureFormat::Depth24: return 3;
|
case TextureFormat::Depth16:
|
||||||
case TextureFormat::Depth32F: return 4;
|
return 2;
|
||||||
case TextureFormat::Depth24Stencil8: return 4;
|
case TextureFormat::Depth24:
|
||||||
case TextureFormat::Depth32FStencil8: return 5;
|
return 3;
|
||||||
default: return 4;
|
case TextureFormat::Depth32F:
|
||||||
|
return 4;
|
||||||
|
case TextureFormat::Depth24Stencil8:
|
||||||
|
return 4;
|
||||||
|
case TextureFormat::Depth32FStencil8:
|
||||||
|
return 5;
|
||||||
|
default:
|
||||||
|
return 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include <renderer/texture_atlas.h>
|
|
||||||
#include <utils/logger.h>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <renderer/texture_atlas.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -11,25 +11,21 @@ namespace extra2d {
|
||||||
|
|
||||||
TextureAtlas::TextureAtlas() = default;
|
TextureAtlas::TextureAtlas() = default;
|
||||||
|
|
||||||
TextureAtlas::~TextureAtlas() {
|
TextureAtlas::~TextureAtlas() { shutdown(); }
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureAtlas::TextureAtlas(TextureAtlas&& other) noexcept
|
TextureAtlas::TextureAtlas(TextureAtlas &&other) noexcept
|
||||||
: width_(other.width_)
|
: width_(other.width_), height_(other.height_),
|
||||||
, height_(other.height_)
|
atlasTexture_(std::move(other.atlasTexture_)),
|
||||||
, atlasTexture_(std::move(other.atlasTexture_))
|
regions_(std::move(other.regions_)),
|
||||||
, regions_(std::move(other.regions_))
|
pendingTextures_(std::move(other.pendingTextures_)),
|
||||||
, pendingTextures_(std::move(other.pendingTextures_))
|
packContext_(std::move(other.packContext_)),
|
||||||
, packContext_(std::move(other.packContext_))
|
packNodes_(std::move(other.packNodes_)), finalized_(other.finalized_) {
|
||||||
, packNodes_(std::move(other.packNodes_))
|
|
||||||
, finalized_(other.finalized_) {
|
|
||||||
other.width_ = 0;
|
other.width_ = 0;
|
||||||
other.height_ = 0;
|
other.height_ = 0;
|
||||||
other.finalized_ = false;
|
other.finalized_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureAtlas& TextureAtlas::operator=(TextureAtlas&& other) noexcept {
|
TextureAtlas &TextureAtlas::operator=(TextureAtlas &&other) noexcept {
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
shutdown();
|
shutdown();
|
||||||
|
|
||||||
|
|
@ -53,7 +49,7 @@ bool TextureAtlas::initialize(int width, int height) {
|
||||||
shutdown();
|
shutdown();
|
||||||
|
|
||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
E2D_LOG_ERROR("TextureAtlas::initialize: 无效的尺寸 {}x{}", width, height);
|
E2D_ERROR("TextureAtlas::initialize: 无效的尺寸 {}x{}", width, height);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,9 +59,10 @@ bool TextureAtlas::initialize(int width, int height) {
|
||||||
// 初始化 stb_rect_pack
|
// 初始化 stb_rect_pack
|
||||||
packContext_ = std::make_unique<stbrp_context>();
|
packContext_ = std::make_unique<stbrp_context>();
|
||||||
packNodes_.resize(width);
|
packNodes_.resize(width);
|
||||||
stbrp_init_target(packContext_.get(), width, height, packNodes_.data(), width);
|
stbrp_init_target(packContext_.get(), width, height, packNodes_.data(),
|
||||||
|
width);
|
||||||
|
|
||||||
E2D_LOG_DEBUG("纹理图集已初始化: {}x{}", width, height);
|
E2D_DEBUG("纹理图集已初始化: {}x{}", width, height);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,19 +77,19 @@ void TextureAtlas::shutdown() {
|
||||||
finalized_ = false;
|
finalized_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureAtlas::addTexture(const std::string& name, Ptr<Texture> texture) {
|
bool TextureAtlas::addTexture(const std::string &name, Ptr<Texture> texture) {
|
||||||
if (finalized_) {
|
if (finalized_) {
|
||||||
E2D_LOG_WARN("TextureAtlas::addTexture: 无法向已完成的图集添加纹理");
|
E2D_WARN("TextureAtlas::addTexture: 无法向已完成的图集添加纹理");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
E2D_LOG_WARN("TextureAtlas::addTexture: 纹理为空");
|
E2D_WARN("TextureAtlas::addTexture: 纹理为空");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (regions_.find(name) != regions_.end()) {
|
if (regions_.find(name) != regions_.end()) {
|
||||||
E2D_LOG_WARN("TextureAtlas::addTexture: 纹理 '{}' 已存在", name);
|
E2D_WARN("TextureAtlas::addTexture: 纹理 '{}' 已存在", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,13 +98,14 @@ bool TextureAtlas::addTexture(const std::string& name, Ptr<Texture> texture) {
|
||||||
int texHeight = static_cast<int>(texture->getHeight());
|
int texHeight = static_cast<int>(texture->getHeight());
|
||||||
|
|
||||||
if (texWidth <= 0 || texHeight <= 0) {
|
if (texWidth <= 0 || texHeight <= 0) {
|
||||||
E2D_LOG_WARN("TextureAtlas::addTexture: 无效的纹理尺寸 {}x{}", texWidth, texHeight);
|
E2D_WARN("TextureAtlas::addTexture: 无效的纹理尺寸 {}x{}", texWidth,
|
||||||
|
texHeight);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查纹理是否适合图集
|
// 检查纹理是否适合图集
|
||||||
if (texWidth > width_ || texHeight > height_) {
|
if (texWidth > width_ || texHeight > height_) {
|
||||||
E2D_LOG_WARN("TextureAtlas::addTexture: 纹理 {}x{} 太大,无法放入图集 {}x{}",
|
E2D_WARN("TextureAtlas::addTexture: 纹理 {}x{} 太大,无法放入图集 {}x{}",
|
||||||
texWidth, texHeight, width_, height_);
|
texWidth, texHeight, width_, height_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -129,29 +127,30 @@ bool TextureAtlas::addTexture(const std::string& name, Ptr<Texture> texture) {
|
||||||
|
|
||||||
pendingTextures_.push_back(std::move(pending));
|
pendingTextures_.push_back(std::move(pending));
|
||||||
|
|
||||||
E2D_LOG_DEBUG("纹理图集: 已添加纹理 '{}' ({}x{})", name, texWidth, texHeight);
|
E2D_DEBUG("纹理图集: 已添加纹理 '{}' ({}x{})", name, texWidth, texHeight);
|
||||||
return true;
|
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) {
|
int width, int height, TextureFormat format) {
|
||||||
if (finalized_) {
|
if (finalized_) {
|
||||||
E2D_LOG_WARN("TextureAtlas::addTextureData: 无法向已完成的图集添加纹理");
|
E2D_WARN("TextureAtlas::addTextureData: 无法向已完成的图集添加纹理");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data || width <= 0 || height <= 0) {
|
if (!data || width <= 0 || height <= 0) {
|
||||||
E2D_LOG_WARN("TextureAtlas::addTextureData: 无效参数");
|
E2D_WARN("TextureAtlas::addTextureData: 无效参数");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (regions_.find(name) != regions_.end()) {
|
if (regions_.find(name) != regions_.end()) {
|
||||||
E2D_LOG_WARN("TextureAtlas::addTextureData: 纹理 '{}' 已存在", name);
|
E2D_WARN("TextureAtlas::addTextureData: 纹理 '{}' 已存在", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width > width_ || height > height_) {
|
if (width > width_ || height > height_) {
|
||||||
E2D_LOG_WARN("TextureAtlas::addTextureData: 纹理 {}x{} 太大,无法放入图集 {}x{}",
|
E2D_WARN(
|
||||||
|
"TextureAtlas::addTextureData: 纹理 {}x{} 太大,无法放入图集 {}x{}",
|
||||||
width, height, width_, height_);
|
width, height, width_, height_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +169,7 @@ bool TextureAtlas::addTextureData(const std::string& name, const uint8_t* data,
|
||||||
|
|
||||||
pendingTextures_.push_back(std::move(pending));
|
pendingTextures_.push_back(std::move(pending));
|
||||||
|
|
||||||
E2D_LOG_DEBUG("纹理图集: 已添加纹理数据 '{}' ({}x{})", name, width, height);
|
E2D_DEBUG("纹理图集: 已添加纹理数据 '{}' ({}x{})", name, width, height);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,7 +179,7 @@ bool TextureAtlas::finalize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pendingTextures_.empty()) {
|
if (pendingTextures_.empty()) {
|
||||||
E2D_LOG_WARN("TextureAtlas::finalize: 没有要打包的纹理");
|
E2D_WARN("TextureAtlas::finalize: 没有要打包的纹理");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,10 +199,11 @@ bool TextureAtlas::finalize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行打包
|
// 执行打包
|
||||||
int result = stbrp_pack_rects(packContext_.get(), rects.data(), static_cast<int>(rects.size()));
|
int result = stbrp_pack_rects(packContext_.get(), rects.data(),
|
||||||
|
static_cast<int>(rects.size()));
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
E2D_LOG_ERROR("TextureAtlas::finalize: 打包所有纹理失败");
|
E2D_ERROR("TextureAtlas::finalize: 打包所有纹理失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,13 +211,13 @@ bool TextureAtlas::finalize() {
|
||||||
std::vector<uint8_t> atlasData(width_ * height_ * 4, 0); // RGBA 黑色背景
|
std::vector<uint8_t> atlasData(width_ * height_ * 4, 0); // RGBA 黑色背景
|
||||||
|
|
||||||
// 处理打包结果
|
// 处理打包结果
|
||||||
for (const auto& rect : rects) {
|
for (const auto &rect : rects) {
|
||||||
if (!rect.was_packed) {
|
if (!rect.was_packed) {
|
||||||
E2D_LOG_WARN("TextureAtlas::finalize: 纹理 {} 未被打包", rect.id);
|
E2D_WARN("TextureAtlas::finalize: 纹理 {} 未被打包", rect.id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& pending = pendingTextures_[rect.id];
|
const auto &pending = pendingTextures_[rect.id];
|
||||||
|
|
||||||
// 创建区域信息
|
// 创建区域信息
|
||||||
AtlasRegion region;
|
AtlasRegion region;
|
||||||
|
|
@ -246,14 +246,15 @@ bool TextureAtlas::finalize() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_DEBUG("纹理图集: 已打包 '{}' 在 ({}, {}) 尺寸 {}x{}",
|
E2D_DEBUG("纹理图集: 已打包 '{}' 在 ({}, {}) 尺寸 {}x{}", pending.name,
|
||||||
pending.name, rect.x, rect.y, rect.w, rect.h);
|
rect.x, rect.y, rect.w, rect.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建图集纹理
|
// 创建图集纹理
|
||||||
atlasTexture_ = makePtr<Texture>();
|
atlasTexture_ = makePtr<Texture>();
|
||||||
if (!atlasTexture_->loadFromMemory(atlasData.data(), width_, height_, TextureFormat::RGBA8)) {
|
if (!atlasTexture_->loadFromMemory(atlasData.data(), width_, height_,
|
||||||
E2D_LOG_ERROR("TextureAtlas::finalize: 创建图集纹理失败");
|
TextureFormat::RGBA8)) {
|
||||||
|
E2D_ERROR("TextureAtlas::finalize: 创建图集纹理失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,14 +262,14 @@ bool TextureAtlas::finalize() {
|
||||||
pendingTextures_.clear();
|
pendingTextures_.clear();
|
||||||
finalized_ = true;
|
finalized_ = true;
|
||||||
|
|
||||||
E2D_LOG_INFO("纹理图集完成: {} 个纹理打包到 {}x{} 图集 (使用率 {}%)",
|
E2D_INFO("纹理图集完成: {} 个纹理打包到 {}x{} 图集 (使用率 {}%)",
|
||||||
regions_.size(), width_, height_,
|
regions_.size(), width_, height_,
|
||||||
static_cast<int>(getUsageRatio() * 100));
|
static_cast<int>(getUsageRatio() * 100));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AtlasRegion* TextureAtlas::getRegion(const std::string& name) const {
|
const AtlasRegion *TextureAtlas::getRegion(const std::string &name) const {
|
||||||
auto it = regions_.find(name);
|
auto it = regions_.find(name);
|
||||||
if (it != regions_.end()) {
|
if (it != regions_.end()) {
|
||||||
return &it->second;
|
return &it->second;
|
||||||
|
|
@ -276,15 +277,15 @@ const AtlasRegion* TextureAtlas::getRegion(const std::string& name) const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect TextureAtlas::getUVRect(const std::string& name) const {
|
Rect TextureAtlas::getUVRect(const std::string &name) const {
|
||||||
const AtlasRegion* region = getRegion(name);
|
const AtlasRegion *region = getRegion(name);
|
||||||
if (region) {
|
if (region) {
|
||||||
return region->getUVRect(width_, height_);
|
return region->getUVRect(width_, height_);
|
||||||
}
|
}
|
||||||
return Rect(0.0f, 0.0f, 1.0f, 1.0f);
|
return Rect(0.0f, 0.0f, 1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureAtlas::hasTexture(const std::string& name) const {
|
bool TextureAtlas::hasTexture(const std::string &name) const {
|
||||||
return regions_.find(name) != regions_.end();
|
return regions_.find(name) != regions_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,7 +295,7 @@ float TextureAtlas::getUsageRatio() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int totalArea = 0;
|
int totalArea = 0;
|
||||||
for (const auto& pair : regions_) {
|
for (const auto &pair : regions_) {
|
||||||
totalArea += pair.second.width * pair.second.height;
|
totalArea += pair.second.width * pair.second.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,7 +308,7 @@ float TextureAtlas::getUsageRatio() const {
|
||||||
|
|
||||||
Ptr<TextureAtlas> AtlasBuilder::build() {
|
Ptr<TextureAtlas> AtlasBuilder::build() {
|
||||||
if (textures_.empty()) {
|
if (textures_.empty()) {
|
||||||
E2D_LOG_WARN("AtlasBuilder::build: No textures to build");
|
E2D_WARN("AtlasBuilder::build: No textures to build");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,7 +317,7 @@ Ptr<TextureAtlas> AtlasBuilder::build() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& pair : textures_) {
|
for (const auto &pair : textures_) {
|
||||||
atlas->addTexture(pair.first, pair.second);
|
atlas->addTexture(pair.first, pair.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -329,7 +330,7 @@ Ptr<TextureAtlas> AtlasBuilder::build() {
|
||||||
|
|
||||||
Ptr<TextureAtlas> AtlasBuilder::buildAuto() {
|
Ptr<TextureAtlas> AtlasBuilder::buildAuto() {
|
||||||
if (textures_.empty()) {
|
if (textures_.empty()) {
|
||||||
E2D_LOG_WARN("AtlasBuilder::buildAuto: No textures to build");
|
E2D_WARN("AtlasBuilder::buildAuto: No textures to build");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,7 +339,7 @@ Ptr<TextureAtlas> AtlasBuilder::buildAuto() {
|
||||||
int maxWidth = 0;
|
int maxWidth = 0;
|
||||||
int maxHeight = 0;
|
int maxHeight = 0;
|
||||||
|
|
||||||
for (const auto& pair : textures_) {
|
for (const auto &pair : textures_) {
|
||||||
if (pair.second) {
|
if (pair.second) {
|
||||||
int w = static_cast<int>(pair.second->getWidth());
|
int w = static_cast<int>(pair.second->getWidth());
|
||||||
int h = static_cast<int>(pair.second->getHeight());
|
int h = static_cast<int>(pair.second->getHeight());
|
||||||
|
|
@ -350,10 +351,11 @@ Ptr<TextureAtlas> AtlasBuilder::buildAuto() {
|
||||||
|
|
||||||
// 选择合适的大小(2 的幂)
|
// 选择合适的大小(2 的幂)
|
||||||
int atlasSize = 64;
|
int atlasSize = 64;
|
||||||
while (atlasSize < maxWidth || atlasSize < maxHeight || atlasSize * atlasSize < totalArea * 1.5f) {
|
while (atlasSize < maxWidth || atlasSize < maxHeight ||
|
||||||
|
atlasSize * atlasSize < totalArea * 1.5f) {
|
||||||
atlasSize *= 2;
|
atlasSize *= 2;
|
||||||
if (atlasSize > 8192) {
|
if (atlasSize > 8192) {
|
||||||
E2D_LOG_ERROR("AtlasBuilder::buildAuto: Textures too large for atlas");
|
E2D_ERROR("AtlasBuilder::buildAuto: Textures too large for atlas");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,13 @@ bool UniformBuffer::create(uint32_t size, uint32_t binding) {
|
||||||
// 获取 RHI 设备
|
// 获取 RHI 设备
|
||||||
auto *rhiModule = RHIModule::get();
|
auto *rhiModule = RHIModule::get();
|
||||||
if (!rhiModule) {
|
if (!rhiModule) {
|
||||||
E2D_LOG_ERROR("RHI 模块不可用");
|
E2D_ERROR("RHI 模块不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *device = rhiModule->getDevice();
|
auto *device = rhiModule->getDevice();
|
||||||
if (!device) {
|
if (!device) {
|
||||||
E2D_LOG_ERROR("RHI 设备不可用");
|
E2D_ERROR("RHI 设备不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,14 +34,14 @@ bool UniformBuffer::create(uint32_t size, uint32_t binding) {
|
||||||
// 创建缓冲区
|
// 创建缓冲区
|
||||||
auto buffer = device->createBuffer(desc);
|
auto buffer = device->createBuffer(desc);
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
E2D_LOG_ERROR("创建 uniform 缓冲区失败");
|
E2D_ERROR("创建 uniform 缓冲区失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取缓冲区句柄
|
// 获取缓冲区句柄
|
||||||
handle_ = BufferHandle(buffer.release());
|
handle_ = BufferHandle(buffer.release());
|
||||||
|
|
||||||
E2D_LOG_DEBUG("UBO 已创建: 大小={}, 绑定槽={}", size, binding);
|
E2D_DEBUG("UBO 已创建: 大小={}, 绑定槽={}", size, binding);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,8 +57,8 @@ void UniformBuffer::update(const void *data, uint32_t size, uint32_t offset) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (offset + size > size_) {
|
if (offset + size > size_) {
|
||||||
E2D_LOG_WARN("UBO 更新超出边界: 偏移={}, 大小={}, 缓冲区大小={}",
|
E2D_WARN("UBO 更新超出边界: 偏移={}, 大小={}, 缓冲区大小={}", offset, size,
|
||||||
offset, size, size_);
|
size_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,7 +123,7 @@ bool UniformBufferManager::initialize() {
|
||||||
globalUBOs_[i] = std::make_unique<UniformBuffer>();
|
globalUBOs_[i] = std::make_unique<UniformBuffer>();
|
||||||
if (!globalUBOs_[i]->create(UniformBufferManager::GLOBAL_UBO_SIZE,
|
if (!globalUBOs_[i]->create(UniformBufferManager::GLOBAL_UBO_SIZE,
|
||||||
UniformBufferManager::GLOBAL_UBO_BINDING)) {
|
UniformBufferManager::GLOBAL_UBO_BINDING)) {
|
||||||
E2D_LOG_ERROR("创建全局 UBO {} 失败", i);
|
E2D_ERROR("创建全局 UBO {} 失败", i);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -134,13 +134,13 @@ bool UniformBufferManager::initialize() {
|
||||||
// 预分配材质 UBO CPU 缓冲区
|
// 预分配材质 UBO CPU 缓冲区
|
||||||
materialUBOBuffer_.resize(MATERIAL_UBO_BUFFER_SIZE);
|
materialUBOBuffer_.resize(MATERIAL_UBO_BUFFER_SIZE);
|
||||||
|
|
||||||
E2D_LOG_INFO("UniformBufferManager 已初始化,使用双缓冲");
|
E2D_INFO("UniformBufferManager 已初始化,使用双缓冲");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UniformBufferManager::shutdown() {
|
void UniformBufferManager::shutdown() {
|
||||||
materialUBOPool_.clear();
|
materialUBOPool_.clear();
|
||||||
for (auto& ubo : globalUBOs_) {
|
for (auto &ubo : globalUBOs_) {
|
||||||
ubo.reset();
|
ubo.reset();
|
||||||
}
|
}
|
||||||
currentUBOIndex_ = 0;
|
currentUBOIndex_ = 0;
|
||||||
|
|
@ -148,7 +148,7 @@ void UniformBufferManager::shutdown() {
|
||||||
materialUBOBufferOffset_ = 0;
|
materialUBOBufferOffset_ = 0;
|
||||||
currentMaterialUBO_ = nullptr;
|
currentMaterialUBO_ = nullptr;
|
||||||
|
|
||||||
E2D_LOG_INFO("UniformBufferManager 已关闭");
|
E2D_INFO("UniformBufferManager 已关闭");
|
||||||
}
|
}
|
||||||
|
|
||||||
UniformBuffer *UniformBufferManager::getGlobalUBO(uint32_t frameIndex) {
|
UniformBuffer *UniformBufferManager::getGlobalUBO(uint32_t frameIndex) {
|
||||||
|
|
@ -169,7 +169,7 @@ UniformBuffer *UniformBufferManager::acquireMaterialUBO(uint32_t size) {
|
||||||
// 需要创建新的 UBO
|
// 需要创建新的 UBO
|
||||||
auto ubo = std::make_unique<UniformBuffer>();
|
auto ubo = std::make_unique<UniformBuffer>();
|
||||||
if (!ubo->create(size, UniformBufferManager::MATERIAL_UBO_BINDING)) {
|
if (!ubo->create(size, UniformBufferManager::MATERIAL_UBO_BINDING)) {
|
||||||
E2D_LOG_ERROR("创建材质 UBO 失败");
|
E2D_ERROR("创建材质 UBO 失败");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,7 +186,8 @@ void UniformBufferManager::resetMaterialUBOs() {
|
||||||
currentMaterialUBO_ = nullptr;
|
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
|
// 使用双缓冲更新全局 UBO
|
||||||
size_t index = frameIndex % 2;
|
size_t index = frameIndex % 2;
|
||||||
if (globalUBOs_[index]) {
|
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()) {
|
if (!data || size == 0 || offset + size > materialUBOBuffer_.size()) {
|
||||||
E2D_LOG_WARN("无效的批量更新参数");
|
E2D_WARN("无效的批量更新参数");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,7 +214,8 @@ void UniformBufferManager::flushMaterialUBO() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 一次性将 CPU 缓冲区数据更新到 GPU
|
// 一次性将 CPU 缓冲区数据更新到 GPU
|
||||||
currentMaterialUBO_->update(materialUBOBuffer_.data(), materialUBOBufferOffset_, 0);
|
currentMaterialUBO_->update(materialUBOBuffer_.data(),
|
||||||
|
materialUBOBufferOffset_, 0);
|
||||||
materialUBOBufferOffset_ = 0;
|
materialUBOBufferOffset_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ void CameraComponent::setOrtho(float left, float right, float bottom, float top,
|
||||||
far_ = far;
|
far_ = far;
|
||||||
markProjDirty();
|
markProjDirty();
|
||||||
|
|
||||||
E2D_LOG_DEBUG("正交投影已设置: 左={}, 右={}, 下={}, 上={}, 近={}, 远={}",
|
E2D_DEBUG("正交投影已设置: 左={}, 右={}, 下={}, 上={}, 近={}, 远={}", left,
|
||||||
left, right, bottom, top, near, far);
|
right, bottom, top, near, far);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ void Director::render() {
|
||||||
Mat4 viewProj = camera->getViewProjectionMatrix();
|
Mat4 viewProj = camera->getViewProjectionMatrix();
|
||||||
events::OnRenderSetCamera::emit(viewProj);
|
events::OnRenderSetCamera::emit(viewProj);
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_WARN("Director::render: 未设置主相机!");
|
E2D_WARN("Director::render: 未设置主相机!");
|
||||||
}
|
}
|
||||||
|
|
||||||
runningScene_->render();
|
runningScene_->render();
|
||||||
|
|
|
||||||
|
|
@ -1,65 +1,25 @@
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
#include <spdlog/sinks/basic_file_sink.h>
|
||||||
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <fcntl.h>
|
|
||||||
#include <io.h>
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
// 静态成员定义
|
std::shared_ptr<spdlog::logger> Logger::logger_ = nullptr;
|
||||||
LogLevel Logger::level_ = LogLevel::Info;
|
|
||||||
bool Logger::initialized_ = false;
|
bool Logger::initialized_ = false;
|
||||||
bool Logger::consoleOutput_ = true;
|
bool Logger::consoleOutput_ = true;
|
||||||
bool Logger::fileOutput_ = false;
|
bool Logger::fileOutput_ = false;
|
||||||
std::string Logger::logFile_;
|
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 初始化日志系统
|
* @brief 初始化日志系统
|
||||||
|
*
|
||||||
|
* 设置控制台输出编码(Windows),初始化 Nintendo Switch 控制台(Switch 平台)
|
||||||
|
* 创建日志输出目标(控制台和/或文件),注册日志器到 spdlog
|
||||||
*/
|
*/
|
||||||
void Logger::init() {
|
void Logger::init() {
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
|
|
@ -67,41 +27,59 @@ void Logger::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// 获取标准输出句柄
|
|
||||||
HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
if (hConsoleOut == INVALID_HANDLE_VALUE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置控制台输出代码页为UTF-8 (65001是UTF-8的代码页编号)
|
|
||||||
SetConsoleOutputCP(CP_UTF8);
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
|
||||||
// 设置控制台模式,确保支持UTF-8输出(可选,但能提升兼容性)
|
|
||||||
DWORD consoleMode;
|
|
||||||
GetConsoleMode(hConsoleOut, &consoleMode);
|
|
||||||
consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; // 启用ANSI转义序列(可选)
|
|
||||||
SetConsoleMode(hConsoleOut, consoleMode);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
// Switch 平台:初始化控制台并设置 SDL 日志重定向
|
|
||||||
consoleInit(NULL);
|
consoleInit(NULL);
|
||||||
SDL_LogSetOutputFunction(SwitchLogOutput, nullptr);
|
|
||||||
#else
|
|
||||||
// 其他平台:设置 SDL 日志级别为详细模式
|
|
||||||
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE);
|
|
||||||
#endif
|
#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;
|
initialized_ = true;
|
||||||
log(LogLevel::Info, "日志系统已初始化");
|
log(LogLevel::Info, "日志系统已初始化");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 关闭日志系统
|
* @brief 关闭日志系统
|
||||||
|
*
|
||||||
|
* 记录关闭日志,释放 spdlog 资源,清理 Nintendo Switch 控制台
|
||||||
*/
|
*/
|
||||||
void Logger::shutdown() {
|
void Logger::shutdown() {
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
log(LogLevel::Info, "日志系统正在关闭");
|
log(LogLevel::Info, "日志系统正在关闭");
|
||||||
|
spdlog::shutdown();
|
||||||
|
logger_.reset();
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
consoleExit(NULL);
|
consoleExit(NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -111,45 +89,53 @@ void Logger::shutdown() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置日志级别
|
* @brief 设置日志级别
|
||||||
* @param level 日志级别
|
* @param level 要设置的日志级别
|
||||||
|
*
|
||||||
|
* 低于此级别的日志将不会被输出
|
||||||
*/
|
*/
|
||||||
void Logger::setLevel(LogLevel level) {
|
void Logger::setLevel(LogLevel level) {
|
||||||
level_ = level;
|
if (logger_) {
|
||||||
// 同时设置 SDL 的日志级别
|
logger_->set_level(static_cast<spdlog::level::level_enum>(level));
|
||||||
if (level != LogLevel::Off) {
|
|
||||||
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
static_cast<SDL_LogPriority>(level));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否输出到控制台
|
* @brief 启用或禁用控制台输出
|
||||||
* @param enable 是否启用
|
* @param enable true 启用控制台输出,false 禁用
|
||||||
*/
|
*/
|
||||||
void Logger::setConsoleOutput(bool enable) {
|
void Logger::setConsoleOutput(bool enable) { consoleOutput_ = 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_));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置日志输出到文件
|
* @brief 设置文件输出路径
|
||||||
* @param filename 日志文件名
|
* @param filename 日志文件路径,为空字符串则禁用文件输出
|
||||||
*/
|
*/
|
||||||
void Logger::setFileOutput(const std::string &filename) {
|
void Logger::setFileOutput(const std::string &filename) {
|
||||||
logFile_ = filename;
|
logFile_ = filename;
|
||||||
fileOutput_ = !filename.empty();
|
fileOutput_ = !filename.empty();
|
||||||
|
}
|
||||||
|
|
||||||
if (fileOutput_) {
|
/**
|
||||||
// SDL2 使用 SDL_LogSetOutputFunction 可以重定向日志输出
|
* @brief 获取当前日志级别
|
||||||
// 这里我们记录文件路径,实际文件输出可以通过自定义回调实现
|
* @return 当前设置的日志级别,如果日志器未初始化则返回 Info
|
||||||
log(LogLevel::Info, "文件输出已配置: {}", filename);
|
*/
|
||||||
|
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
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,14 @@ function define_extra2d_engine()
|
||||||
|
|
||||||
add_files("src/**.cpp")
|
add_files("src/**.cpp")
|
||||||
add_files("third_party/glad/src/glad.c")
|
add_files("third_party/glad/src/glad.c")
|
||||||
|
add_files("third_party/spdlog/src/*.cpp")
|
||||||
|
|
||||||
add_includedirs("include", {public = true})
|
add_includedirs("include", {public = true})
|
||||||
add_includedirs("third_party", {public = true})
|
add_includedirs("third_party", {public = true})
|
||||||
add_includedirs("third_party/glad/include", {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()
|
local plat = get_current_plat()
|
||||||
if plat == "mingw" then
|
if plat == "mingw" then
|
||||||
|
|
@ -47,7 +51,7 @@ function define_extra2d_engine()
|
||||||
add_cxxflags("-Wno-deprecated-copy", "-Wno-class-memaccess", {force = true})
|
add_cxxflags("-Wno-deprecated-copy", "-Wno-class-memaccess", {force = true})
|
||||||
|
|
||||||
if is_mode("debug") then
|
if is_mode("debug") then
|
||||||
add_defines("E2D_DEBUG", "_DEBUG", {public = true})
|
add_defines("_DEBUG", {public = true})
|
||||||
add_cxxflags("-O0", "-g", {force = true})
|
add_cxxflags("-O0", "-g", {force = true})
|
||||||
else
|
else
|
||||||
add_defines("NDEBUG", {public = true})
|
add_defines("NDEBUG", {public = true})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue