refactor: 移除logger.h头文件并统一使用logger_service.h

将项目中所有对logger.h的引用替换为logger_service.h,并删除logger.h文件。同时调整了部分文件的include顺序和格式,保持代码风格一致。
This commit is contained in:
ChestnutYueyue 2026-02-18 19:10:31 +08:00
parent 6008331fc5
commit 65b143573c
35 changed files with 3781 additions and 3666 deletions

View File

@ -1,18 +1,19 @@
#pragma once #pragma once
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/utils/logger.h>
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <extra2d/core/math_types.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/services/logger_service.h>
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
namespace extra2d { namespace extra2d {
// 前向声明 // 前向声明

View File

@ -3,7 +3,6 @@
#include <extra2d/core/service_interface.h> #include <extra2d/core/service_interface.h>
#include <extra2d/core/service_locator.h> #include <extra2d/core/service_locator.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <cstdarg>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
@ -13,13 +12,13 @@ namespace extra2d {
* @brief * @brief
*/ */
enum class LogLevel { enum class LogLevel {
Trace = 0, Trace = 0,
Debug = 1, Debug = 1,
Info = 2, Info = 2,
Warn = 3, Warn = 3,
Error = 4, Error = 4,
Fatal = 5, Fatal = 5,
Off = 6 Off = 6
}; };
/** /**
@ -27,70 +26,70 @@ enum class LogLevel {
*/ */
class ILogger : public IService { class ILogger : public IService {
public: public:
virtual ~ILogger() = default; virtual ~ILogger() = default;
/** /**
* @brief * @brief
*/ */
virtual void setLevel(LogLevel level) = 0; virtual void setLevel(LogLevel level) = 0;
/** /**
* @brief * @brief
*/ */
virtual LogLevel getLevel() const = 0; virtual LogLevel getLevel() const = 0;
/** /**
* @brief * @brief
*/ */
virtual bool isEnabled(LogLevel level) const = 0; virtual bool isEnabled(LogLevel level) const = 0;
/** /**
* @brief * @brief
*/ */
virtual void log(LogLevel level, const char* fmt, ...) = 0; virtual void log(LogLevel level, const char *fmt, ...) = 0;
/** /**
* @brief * @brief
*/ */
virtual void log(LogLevel level, const std::string& msg) = 0; virtual void log(LogLevel level, const std::string &msg) = 0;
/** /**
* @brief Trace级别日志 * @brief Trace级别日志
*/ */
virtual void trace(const char* fmt, ...) = 0; virtual void trace(const char *fmt, ...) = 0;
/** /**
* @brief Debug级别日志 * @brief Debug级别日志
*/ */
virtual void debug(const char* fmt, ...) = 0; virtual void debug(const char *fmt, ...) = 0;
/** /**
* @brief Info级别日志 * @brief Info级别日志
*/ */
virtual void info(const char* fmt, ...) = 0; virtual void info(const char *fmt, ...) = 0;
/** /**
* @brief Warn级别日志 * @brief Warn级别日志
*/ */
virtual void warn(const char* fmt, ...) = 0; virtual void warn(const char *fmt, ...) = 0;
/** /**
* @brief Error级别日志 * @brief Error级别日志
*/ */
virtual void error(const char* fmt, ...) = 0; virtual void error(const char *fmt, ...) = 0;
/** /**
* @brief Fatal级别日志 * @brief Fatal级别日志
*/ */
virtual void fatal(const char* fmt, ...) = 0; virtual void fatal(const char *fmt, ...) = 0;
ServiceInfo getServiceInfo() const override { ServiceInfo getServiceInfo() const override {
ServiceInfo info; ServiceInfo info;
info.name = "Logger"; info.name = "Logger";
info.priority = ServicePriority::Core; info.priority = ServicePriority::Core;
info.enabled = true; info.enabled = true;
return info; return info;
} }
}; };
/** /**
@ -98,36 +97,36 @@ public:
*/ */
class ConsoleLogger : public ILogger { class ConsoleLogger : public ILogger {
public: public:
ConsoleLogger(); ConsoleLogger();
~ConsoleLogger() override; ~ConsoleLogger() override;
bool initialize() override; bool initialize() override;
void shutdown() override; void shutdown() override;
void setLevel(LogLevel level) override; void setLevel(LogLevel level) override;
LogLevel getLevel() const override; LogLevel getLevel() const override;
bool isEnabled(LogLevel level) const override; bool isEnabled(LogLevel level) const override;
void log(LogLevel level, const char* fmt, ...) override; void log(LogLevel level, const char *fmt, ...) override;
void log(LogLevel level, const std::string& msg) override; void log(LogLevel level, const std::string &msg) override;
void trace(const char* fmt, ...) override; void trace(const char *fmt, ...) override;
void debug(const char* fmt, ...) override; void debug(const char *fmt, ...) override;
void info(const char* fmt, ...) override; void info(const char *fmt, ...) override;
void warn(const char* fmt, ...) override; void warn(const char *fmt, ...) override;
void error(const char* fmt, ...) override; void error(const char *fmt, ...) override;
void fatal(const char* fmt, ...) override; void fatal(const char *fmt, ...) override;
private: private:
void output(LogLevel level, const char* msg); void output(LogLevel level, const char *msg);
const char* getLevelString(LogLevel level); const char *getLevelString(LogLevel level);
LogLevel level_; LogLevel level_;
class Impl; class Impl;
UniquePtr<Impl> impl_; UniquePtr<Impl> impl_;
// 服务注册元数据 // 服务注册元数据
E2D_AUTO_REGISTER_SERVICE(ILogger, ConsoleLogger); E2D_AUTO_REGISTER_SERVICE(ILogger, ConsoleLogger);
}; };
} // namespace extra2d } // namespace extra2d
@ -135,87 +134,80 @@ private:
// 格式化辅助函数 - 将参数转换为字符串 // 格式化辅助函数 - 将参数转换为字符串
namespace extra2d { namespace extra2d {
namespace detail { namespace detail {
template<typename T> template <typename T> std::string to_string(T &&value) {
std::string to_string(T&& value) { using Decayed = std::decay_t<T>;
using Decayed = std::decay_t<T>; if constexpr (std::is_same_v<Decayed, std::string>) {
if constexpr (std::is_same_v<Decayed, std::string>) { return value;
return value; } else if constexpr (std::is_same_v<Decayed, const char *>) {
} else if constexpr (std::is_same_v<Decayed, const char*>) { return value ? value : "(null)";
return value ? value : "(null)"; } else if constexpr (std::is_arithmetic_v<Decayed>) {
} else if constexpr (std::is_arithmetic_v<Decayed>) { if constexpr (std::is_same_v<Decayed, bool>) {
if constexpr (std::is_same_v<Decayed, bool>) { return value ? "true" : "false";
return value ? "true" : "false"; } else if constexpr (std::is_floating_point_v<Decayed>) {
} else if constexpr (std::is_floating_point_v<Decayed>) { return std::to_string(value);
return std::to_string(value);
} else {
return std::to_string(value);
}
} else {
return "<?>";
}
}
inline void format_impl(std::string& result, const char* fmt) {
result += fmt;
}
template<typename T, typename... Args>
void format_impl(std::string& result, const char* fmt, T&& value, Args&&... args) {
const char* p = fmt;
while (*p) {
if (*p == '{' && *(p + 1) == '}') {
result += to_string(std::forward<T>(value));
format_impl(result, p + 2, std::forward<Args>(args)...);
return;
}
result += *p++;
}
// 没有更多的 {},追加剩余参数(不应该发生)
result += " ";
result += to_string(std::forward<T>(value));
format_impl(result, p, std::forward<Args>(args)...);
}
}
template<typename... Args>
std::string format_str(const char* fmt, Args&&... args) {
if constexpr (sizeof...(args) == 0) {
return std::string(fmt);
} else { } else {
std::string result; return std::to_string(value);
detail::format_impl(result, fmt, std::forward<Args>(args)...);
return result;
} }
} else {
return "<?>";
}
} }
inline void format_impl(std::string &result, const char *fmt) { result += fmt; }
template <typename T, typename... Args>
void format_impl(std::string &result, const char *fmt, T &&value,
Args &&...args) {
const char *p = fmt;
while (*p) {
if (*p == '{' && *(p + 1) == '}') {
result += to_string(std::forward<T>(value));
format_impl(result, p + 2, std::forward<Args>(args)...);
return;
}
result += *p++;
}
// 没有更多的 {},追加剩余参数(不应该发生)
result += " ";
result += to_string(std::forward<T>(value));
format_impl(result, p, std::forward<Args>(args)...);
} }
} // namespace detail
template <typename... Args>
std::string format_str(const char *fmt, Args &&...args) {
if constexpr (sizeof...(args) == 0) {
return std::string(fmt);
} else {
std::string result;
detail::format_impl(result, fmt, std::forward<Args>(args)...);
return result;
}
}
} // namespace extra2d
// 便捷宏 - 自动获取日志服务 // 便捷宏 - 自动获取日志服务
#define E2D_LOG(level, ...) \ #define E2D_LOG(level, ...) \
do { \ do { \
if (auto logService = ::extra2d::ServiceLocator::instance().tryGetService<::extra2d::ILogger>()) { \ if (auto logService = ::extra2d::ServiceLocator::instance() \
if (logService->isEnabled(level)) { \ .tryGetService<::extra2d::ILogger>()) { \
logService->log(level, ::extra2d::format_str(__VA_ARGS__)); \ if (logService->isEnabled(level)) { \
} \ logService->log(level, ::extra2d::format_str(__VA_ARGS__)); \
} \ } \
} while(0) } \
} while (0)
#define E2D_LOG_TRACE(...) \ #define E2D_LOG_TRACE(...) E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__)
E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__)
#define E2D_LOG_DEBUG(...) \ #define E2D_LOG_DEBUG(...) E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__)
E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__)
#define E2D_LOG_INFO(...) \ #define E2D_LOG_INFO(...) E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__)
E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__)
#define E2D_LOG_WARN(...) \ #define E2D_LOG_WARN(...) E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__)
E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__)
#define E2D_LOG_ERROR(...) \ #define E2D_LOG_ERROR(...) E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__)
E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__)
#define E2D_LOG_FATAL(...) \ #define E2D_LOG_FATAL(...) E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
// 简写宏 // 简写宏
#define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__) #define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__)
@ -224,9 +216,9 @@ std::string format_str(const char* fmt, Args&&... args) {
#define E2D_FATAL(...) E2D_LOG_FATAL(__VA_ARGS__) #define E2D_FATAL(...) E2D_LOG_FATAL(__VA_ARGS__)
#ifdef E2D_DEBUG #ifdef E2D_DEBUG
#define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__) #define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__)
#define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__) #define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__)
#else #else
#define E2D_DEBUG_LOG(...) #define E2D_DEBUG_LOG(...)
#define E2D_TRACE(...) #define E2D_TRACE(...)
#endif #endif

View File

@ -1,12 +0,0 @@
#pragma once
/**
* @file logger.h
* @brief
*
* 便 logger_service.h
*/
#include <extra2d/services/logger_service.h>
#include <extra2d/core/service_locator.h>

View File

@ -1,96 +1,97 @@
#include <extra2d/core/registry.h> #include <extra2d/core/registry.h>
#include <extra2d/utils/logger.h> #include <extra2d/core/service_locator.h>
#include <algorithm> #include <extra2d/services/logger_service.h>
#include <queue>
#include <unordered_set>
#include <iostream> #include <iostream>
#include <queue>
namespace extra2d { namespace extra2d {
Registry& Registry::instance() { Registry &Registry::instance() {
static Registry instance; static Registry instance;
return instance; return instance;
} }
bool Registry::init() { bool Registry::init() {
auto sorted = topologicalSort(); auto sorted = topologicalSort();
std::cout << "[Registry] Initializing " << sorted.size() << " modules..." << std::endl; std::cout << "[Registry] Initializing " << sorted.size() << " modules..."
<< std::endl;
for (auto* module : sorted) { for (auto *module : sorted) {
std::cout << "[Registry] Initializing module: " << module->name() << std::endl; std::cout << "[Registry] Initializing module: " << module->name()
if (!module->init()) { << std::endl;
std::cerr << "[Registry] Failed to initialize module: " << module->name() << std::endl; if (!module->init()) {
return false; std::cerr << "[Registry] Failed to initialize module: " << module->name()
} << std::endl;
std::cout << "[Registry] Module " << module->name() << " initialized successfully" << std::endl; return false;
} }
std::cout << "[Registry] Module " << module->name()
<< " initialized successfully" << std::endl;
}
std::cout << "[Registry] All modules initialized" << std::endl; std::cout << "[Registry] All modules initialized" << std::endl;
return true; return true;
} }
void Registry::shutdown() { void Registry::shutdown() {
auto sorted = topologicalSort(); auto sorted = topologicalSort();
// 反向关闭 // 反向关闭
for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) { for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) {
(*it)->shutdown(); (*it)->shutdown();
} }
} }
void Registry::clear() { void Registry::clear() {
shutdown(); shutdown();
modules_.clear(); modules_.clear();
} }
std::vector<Module*> Registry::topologicalSort() { std::vector<Module *> Registry::topologicalSort() {
std::vector<Module*> result; std::vector<Module *> result;
std::unordered_map<Module*, int> inDegree; std::unordered_map<Module *, int> inDegree;
std::unordered_map<Module*, std::vector<Module*>> adj; std::unordered_map<Module *, std::vector<Module *>> adj;
// 构建图 // 构建图
for (auto& [typeIdx, module] : modules_) { for (auto &[typeIdx, module] : modules_) {
inDegree[module.get()] = 0; inDegree[module.get()] = 0;
}
for (auto &[typeIdx, module] : modules_) {
for (auto &depType : module->deps()) {
Module *dep = get(depType);
if (dep) {
adj[dep].push_back(module.get());
inDegree[module.get()]++;
}
} }
}
for (auto& [typeIdx, module] : modules_) { // 优先级队列(优先级小的先处理)
for (auto& depType : module->deps()) { auto cmp = [](Module *a, Module *b) { return a->priority() > b->priority(); };
Module* dep = get(depType); std::priority_queue<Module *, std::vector<Module *>, decltype(cmp)> pq(cmp);
if (dep) {
adj[dep].push_back(module.get()); for (auto &[mod, degree] : inDegree) {
inDegree[module.get()]++; if (degree == 0) {
} pq.push(mod);
}
} }
}
// 优先级队列(优先级小的先处理) // 拓扑排序
auto cmp = [](Module* a, Module* b) { while (!pq.empty()) {
return a->priority() > b->priority(); Module *curr = pq.top();
}; pq.pop();
std::priority_queue<Module*, std::vector<Module*>, decltype(cmp)> pq(cmp); result.push_back(curr);
for (auto& [mod, degree] : inDegree) { for (Module *next : adj[curr]) {
if (degree == 0) { inDegree[next]--;
pq.push(mod); if (inDegree[next] == 0) {
} pq.push(next);
}
} }
}
// 拓扑排序 return result;
while (!pq.empty()) {
Module* curr = pq.top();
pq.pop();
result.push_back(curr);
for (Module* next : adj[curr]) {
inDegree[next]--;
if (inDegree[next] == 0) {
pq.push(next);
}
}
}
return result;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,135 +1,142 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/backend_factory.h> #include <extra2d/graphics/backends/backend_factory.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
namespace graphics { namespace graphics {
std::unordered_map<std::string, BackendFactory::BackendEntry>& BackendFactory::registry() { std::unordered_map<std::string, BackendFactory::BackendEntry> &
static std::unordered_map<std::string, BackendEntry> reg; BackendFactory::registry() {
return reg; static std::unordered_map<std::string, BackendEntry> reg;
return reg;
} }
void BackendFactory::reg(const std::string& name, BackendFn backend, void BackendFactory::reg(const std::string &name, BackendFn backend,
const std::vector<std::string>& windowBackends) { const std::vector<std::string> &windowBackends) {
registry()[name] = {backend, windowBackends}; registry()[name] = {backend, windowBackends};
E2D_LOG_DEBUG("Registered graphics backend: {} (window backends: {})", E2D_LOG_DEBUG("Registered graphics backend: {} (window backends: {})", name,
name, windowBackends.size()); windowBackends.size());
} }
UniquePtr<RenderBackend> BackendFactory::createBackend(const std::string& name) { UniquePtr<RenderBackend>
auto& reg = registry(); BackendFactory::createBackend(const std::string &name) {
auto it = reg.find(name); auto &reg = registry();
if (it != reg.end() && it->second.createFn) { auto it = reg.find(name);
E2D_LOG_INFO("Creating graphics backend: {}", name); if (it != reg.end() && it->second.createFn) {
return it->second.createFn(); E2D_LOG_INFO("Creating graphics backend: {}", name);
} return it->second.createFn();
E2D_LOG_ERROR("Graphics backend '{}' not found", name); }
return nullptr; E2D_LOG_ERROR("Graphics backend '{}' not found", name);
return nullptr;
} }
UniquePtr<RenderBackend> BackendFactory::createDefaultBackend() { UniquePtr<RenderBackend> BackendFactory::createDefaultBackend() {
std::string recommended = getRecommendedBackend(); std::string recommended = getRecommendedBackend();
if (recommended.empty()) { if (recommended.empty()) {
E2D_LOG_ERROR("No graphics backend available"); E2D_LOG_ERROR("No graphics backend available");
return nullptr; return nullptr;
} }
return createBackend(recommended); return createBackend(recommended);
} }
UniquePtr<RenderBackend> BackendFactory::createBackendForWindow(const std::string& windowBackend) { UniquePtr<RenderBackend>
std::string recommended = getRecommendedBackendForWindow(windowBackend); BackendFactory::createBackendForWindow(const std::string &windowBackend) {
if (recommended.empty()) { std::string recommended = getRecommendedBackendForWindow(windowBackend);
E2D_LOG_ERROR("No compatible graphics backend for window backend: {}", windowBackend); if (recommended.empty()) {
return nullptr; E2D_LOG_ERROR("No compatible graphics backend for window backend: {}",
} windowBackend);
return createBackend(recommended); return nullptr;
}
return createBackend(recommended);
} }
std::vector<std::string> BackendFactory::backends() { std::vector<std::string> BackendFactory::backends() {
std::vector<std::string> result; std::vector<std::string> result;
for (const auto& pair : registry()) { for (const auto &pair : registry()) {
result.push_back(pair.first); result.push_back(pair.first);
} }
return result; return result;
} }
bool BackendFactory::has(const std::string& name) { bool BackendFactory::has(const std::string &name) {
return registry().find(name) != registry().end(); return registry().find(name) != registry().end();
} }
std::string BackendFactory::getRecommendedBackend() { std::string BackendFactory::getRecommendedBackend() {
auto& reg = registry(); auto &reg = registry();
static const std::vector<std::string> priority = { static const std::vector<std::string> priority = {
"vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles" "vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"};
};
for (const auto& name : priority) { for (const auto &name : priority) {
if (reg.find(name) != reg.end()) { if (reg.find(name) != reg.end()) {
return name; return name;
}
} }
}
if (!reg.empty()) { if (!reg.empty()) {
return reg.begin()->first; return reg.begin()->first;
} }
E2D_LOG_WARN("No graphics backend registered"); E2D_LOG_WARN("No graphics backend registered");
return ""; return "";
} }
std::string BackendFactory::getRecommendedBackendForWindow(const std::string& windowBackend) { std::string BackendFactory::getRecommendedBackendForWindow(
auto& reg = registry(); const std::string &windowBackend) {
auto &reg = registry();
static const std::vector<std::string> priority = { static const std::vector<std::string> priority = {
"vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles" "vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"};
};
for (const auto& name : priority) { for (const auto &name : priority) {
auto it = reg.find(name); auto it = reg.find(name);
if (it != reg.end() && isCompatible(name, windowBackend)) { if (it != reg.end() && isCompatible(name, windowBackend)) {
return name; return name;
}
} }
}
for (const auto& pair : reg) { for (const auto &pair : reg) {
if (isCompatible(pair.first, windowBackend)) { if (isCompatible(pair.first, windowBackend)) {
return pair.first; return pair.first;
}
} }
}
E2D_LOG_WARN("No compatible graphics backend for window backend: {}", windowBackend); E2D_LOG_WARN("No compatible graphics backend for window backend: {}",
return ""; windowBackend);
return "";
} }
bool BackendFactory::isCompatible(const std::string& graphicsBackend, const std::string& windowBackend) { bool BackendFactory::isCompatible(const std::string &graphicsBackend,
auto& reg = registry(); const std::string &windowBackend) {
auto it = reg.find(graphicsBackend); auto &reg = registry();
if (it == reg.end()) { auto it = reg.find(graphicsBackend);
return false; if (it == reg.end()) {
}
const auto& windowBackends = it->second.windowBackends;
if (windowBackends.empty()) {
return true;
}
for (const auto& wb : windowBackends) {
if (wb == windowBackend) {
return true;
}
}
return false; return false;
}
const auto &windowBackends = it->second.windowBackends;
if (windowBackends.empty()) {
return true;
}
for (const auto &wb : windowBackends) {
if (wb == windowBackend) {
return true;
}
}
return false;
} }
std::vector<std::string> BackendFactory::getSupportedWindowBackends(const std::string& graphicsBackend) { std::vector<std::string>
auto& reg = registry(); BackendFactory::getSupportedWindowBackends(const std::string &graphicsBackend) {
auto it = reg.find(graphicsBackend); auto &reg = registry();
if (it != reg.end()) { auto it = reg.find(graphicsBackend);
return it->second.windowBackends; if (it != reg.end()) {
} return it->second.windowBackends;
return {}; }
return {};
} }
} // namespace graphics } // namespace graphics

View File

@ -1,7 +1,9 @@
#include <cstring>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/opengl/gl_buffer.h> #include <extra2d/graphics/backends/opengl/gl_buffer.h>
#include <extra2d/graphics/memory/vram_manager.h> #include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
#include <cstring>
namespace extra2d { namespace extra2d {
@ -11,161 +13,162 @@ namespace extra2d {
GLBuffer::GLBuffer() = default; GLBuffer::GLBuffer() = default;
GLBuffer::~GLBuffer() { GLBuffer::~GLBuffer() { shutdown(); }
bool GLBuffer::init(const BufferDesc &desc) {
if (bufferID_ != 0) {
shutdown(); shutdown();
} }
bool GLBuffer::init(const BufferDesc& desc) { type_ = desc.type;
if (bufferID_ != 0) { usage_ = desc.usage;
shutdown(); size_ = desc.size;
} target_ = convertType(type_);
glUsage_ = convertUsage(usage_);
type_ = desc.type; // 生成缓冲区
usage_ = desc.usage; glGenBuffers(1, &bufferID_);
size_ = desc.size; if (bufferID_ == 0) {
target_ = convertType(type_); E2D_LOG_ERROR("Failed to generate OpenGL buffer");
glUsage_ = convertUsage(usage_); return false;
}
// 生成缓冲区 // 绑定并分配缓冲区
glGenBuffers(1, &bufferID_); glBindBuffer(target_, bufferID_);
if (bufferID_ == 0) { glBufferData(target_, static_cast<GLsizeiptr>(size_), desc.initialData,
E2D_LOG_ERROR("Failed to generate OpenGL buffer"); glUsage_);
return false; glBindBuffer(target_, 0);
}
// 绑定并分配缓冲区 // 追踪显存使用
glBindBuffer(target_, bufferID_); VRAMMgr::get().allocBuffer(size_);
glBufferData(target_, static_cast<GLsizeiptr>(size_), desc.initialData, glUsage_);
glBindBuffer(target_, 0);
// 追踪显存使用 E2D_LOG_DEBUG("GLBuffer created: ID={}, Size={}, Type={}, Usage={}",
VRAMMgr::get().allocBuffer(size_); bufferID_, size_, static_cast<int>(type_),
static_cast<int>(usage_));
E2D_LOG_DEBUG("GLBuffer created: ID={}, Size={}, Type={}, Usage={}", return true;
bufferID_, size_, static_cast<int>(type_), static_cast<int>(usage_));
return true;
} }
void GLBuffer::shutdown() { void GLBuffer::shutdown() {
if (bufferID_ != 0) { if (bufferID_ != 0) {
if (mapped_) { if (mapped_) {
unmap(); unmap();
}
// 释放显存追踪
VRAMMgr::get().freeBuffer(size_);
glDeleteBuffers(1, &bufferID_);
E2D_LOG_DEBUG("GLBuffer destroyed: ID={}", bufferID_);
bufferID_ = 0;
} }
size_ = 0; // 释放显存追踪
mapped_ = false; VRAMMgr::get().freeBuffer(size_);
mappedPtr_ = nullptr; glDeleteBuffers(1, &bufferID_);
E2D_LOG_DEBUG("GLBuffer destroyed: ID={}", bufferID_);
bufferID_ = 0;
}
size_ = 0;
mapped_ = false;
mappedPtr_ = nullptr;
} }
void GLBuffer::bind() { void GLBuffer::bind() {
if (bufferID_ != 0) { if (bufferID_ != 0) {
glBindBuffer(target_, bufferID_); glBindBuffer(target_, bufferID_);
} }
} }
void GLBuffer::unbind() { void GLBuffer::unbind() { glBindBuffer(target_, 0); }
glBindBuffer(target_, 0);
void GLBuffer::setData(const void *data, size_t size) {
if (bufferID_ == 0) {
return;
}
bind();
// 如果大小相同,使用 glBufferSubData 更高效
if (size == size_) {
glBufferSubData(target_, 0, static_cast<GLsizeiptr>(size), data);
} else {
// 大小不同,重新分配
size_ = size;
glBufferData(target_, static_cast<GLsizeiptr>(size_), data, glUsage_);
}
unbind();
} }
void GLBuffer::setData(const void* data, size_t size) { void GLBuffer::updateData(const void *data, size_t offset, size_t size) {
if (bufferID_ == 0) { if (bufferID_ == 0 || data == nullptr || size == 0) {
return; return;
} }
bind(); if (offset + size > size_) {
E2D_LOG_WARN(
"GLBuffer updateData out of bounds: offset={}, size={}, bufferSize={}",
offset, size, size_);
return;
}
// 如果大小相同,使用 glBufferSubData 更高效 bind();
if (size == size_) { glBufferSubData(target_, static_cast<GLintptr>(offset),
glBufferSubData(target_, 0, static_cast<GLsizeiptr>(size), data); static_cast<GLsizeiptr>(size), data);
} else { unbind();
// 大小不同,重新分配
size_ = size;
glBufferData(target_, static_cast<GLsizeiptr>(size_), data, glUsage_);
}
unbind();
} }
void GLBuffer::updateData(const void* data, size_t offset, size_t size) { void *GLBuffer::map() {
if (bufferID_ == 0 || data == nullptr || size == 0) { if (bufferID_ == 0 || mapped_) {
return; return nullptr;
} }
if (offset + size > size_) { bind();
E2D_LOG_WARN("GLBuffer updateData out of bounds: offset={}, size={}, bufferSize={}",
offset, size, size_);
return;
}
bind(); // 使用 glMapBufferRange 替代 glMapBuffer更现代且安全
glBufferSubData(target_, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), data); GLbitfield access = GL_MAP_WRITE_BIT;
unbind(); if (usage_ == BufferUsage::Dynamic || usage_ == BufferUsage::Stream) {
} access |= GL_MAP_INVALIDATE_BUFFER_BIT; // 暗示驱动可以丢弃旧数据
}
void* GLBuffer::map() { mappedPtr_ =
if (bufferID_ == 0 || mapped_) { glMapBufferRange(target_, 0, static_cast<GLsizeiptr>(size_), access);
return nullptr; if (mappedPtr_) {
} mapped_ = true;
} else {
E2D_LOG_ERROR("Failed to map GLBuffer");
}
bind(); return mappedPtr_;
// 使用 glMapBufferRange 替代 glMapBuffer更现代且安全
GLbitfield access = GL_MAP_WRITE_BIT;
if (usage_ == BufferUsage::Dynamic || usage_ == BufferUsage::Stream) {
access |= GL_MAP_INVALIDATE_BUFFER_BIT; // 暗示驱动可以丢弃旧数据
}
mappedPtr_ = glMapBufferRange(target_, 0, static_cast<GLsizeiptr>(size_), access);
if (mappedPtr_) {
mapped_ = true;
} else {
E2D_LOG_ERROR("Failed to map GLBuffer");
}
return mappedPtr_;
} }
void GLBuffer::unmap() { void GLBuffer::unmap() {
if (!mapped_ || bufferID_ == 0) { if (!mapped_ || bufferID_ == 0) {
return; return;
} }
glUnmapBuffer(target_); glUnmapBuffer(target_);
mapped_ = false; mapped_ = false;
mappedPtr_ = nullptr; mappedPtr_ = nullptr;
unbind(); unbind();
} }
GLenum GLBuffer::convertUsage(BufferUsage usage) { GLenum GLBuffer::convertUsage(BufferUsage usage) {
switch (usage) { switch (usage) {
case BufferUsage::Static: case BufferUsage::Static:
return GL_STATIC_DRAW; return GL_STATIC_DRAW;
case BufferUsage::Dynamic: case BufferUsage::Dynamic:
return GL_DYNAMIC_DRAW; return GL_DYNAMIC_DRAW;
case BufferUsage::Stream: case BufferUsage::Stream:
return GL_STREAM_DRAW; return GL_STREAM_DRAW;
default: default:
return GL_STATIC_DRAW; return GL_STATIC_DRAW;
} }
} }
GLenum GLBuffer::convertType(BufferType type) { GLenum GLBuffer::convertType(BufferType type) {
switch (type) { switch (type) {
case BufferType::Vertex: case BufferType::Vertex:
return GL_ARRAY_BUFFER; return GL_ARRAY_BUFFER;
case BufferType::Index: case BufferType::Index:
return GL_ELEMENT_ARRAY_BUFFER; return GL_ELEMENT_ARRAY_BUFFER;
case BufferType::Uniform: case BufferType::Uniform:
return GL_UNIFORM_BUFFER; return GL_UNIFORM_BUFFER;
default: default:
return GL_ARRAY_BUFFER; return GL_ARRAY_BUFFER;
} }
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,8 +1,7 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/opengl/gl_context.h> #include <extra2d/graphics/backends/opengl/gl_context.h>
#include <extra2d/graphics/memory/gpu_context.h> #include <extra2d/graphics/memory/gpu_context.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
#include <cstring>
#include <sstream>
namespace extra2d { namespace extra2d {
@ -10,158 +9,162 @@ namespace extra2d {
// GLContext 实现 // GLContext 实现
// ============================================================================ // ============================================================================
GLContext& GLContext::get() { GLContext &GLContext::get() {
static GLContext instance; static GLContext instance;
return instance; return instance;
} }
bool GLContext::init() { bool GLContext::init() {
if (initialized_) { if (initialized_) {
return true;
}
// 解析 OpenGL 版本
parseVersion();
// 加载扩展GLAD 已在 glad.c 中完成)
if (!loadExtensions()) {
E2D_LOG_ERROR("Failed to load OpenGL extensions");
return false;
}
initialized_ = true;
// 标记 GPU 上下文为有效
GPUContext::get().markValid();
E2D_LOG_INFO("OpenGL Context initialized");
E2D_LOG_INFO(" Version: {}", getVersionString());
E2D_LOG_INFO(" Vendor: {}", getVendor());
E2D_LOG_INFO(" Renderer: {}", getRenderer());
E2D_LOG_INFO(" Max Texture Size: {}", getMaxTextureSize());
E2D_LOG_INFO(" Max Texture Units: {}", getMaxTextureUnits());
return true; return true;
}
// 解析 OpenGL 版本
parseVersion();
// 加载扩展GLAD 已在 glad.c 中完成)
if (!loadExtensions()) {
E2D_LOG_ERROR("Failed to load OpenGL extensions");
return false;
}
initialized_ = true;
// 标记 GPU 上下文为有效
GPUContext::get().markValid();
E2D_LOG_INFO("OpenGL Context initialized");
E2D_LOG_INFO(" Version: {}", getVersionString());
E2D_LOG_INFO(" Vendor: {}", getVendor());
E2D_LOG_INFO(" Renderer: {}", getRenderer());
E2D_LOG_INFO(" Max Texture Size: {}", getMaxTextureSize());
E2D_LOG_INFO(" Max Texture Units: {}", getMaxTextureUnits());
return true;
} }
void GLContext::shutdown() { void GLContext::shutdown() {
// 标记 GPU 上下文为无效 // 标记 GPU 上下文为无效
GPUContext::get().markInvalid(); GPUContext::get().markInvalid();
initialized_ = false; initialized_ = false;
version_ = GLVersion{}; version_ = GLVersion{};
maxTextureSize_ = -1; maxTextureSize_ = -1;
maxTextureUnits_ = -1; maxTextureUnits_ = -1;
maxVertexAttribs_ = -1; maxVertexAttribs_ = -1;
maxUniformBufferBindings_ = -1; maxUniformBufferBindings_ = -1;
} }
std::string GLContext::getVersionString() const { std::string GLContext::getVersionString() const {
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); const char *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
return version ? version : "Unknown"; return version ? version : "Unknown";
} }
std::string GLContext::getVendor() const { std::string GLContext::getVendor() const {
const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); const char *vendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
return vendor ? vendor : "Unknown"; return vendor ? vendor : "Unknown";
} }
std::string GLContext::getRenderer() const { std::string GLContext::getRenderer() const {
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); const char *renderer =
return renderer ? renderer : "Unknown"; reinterpret_cast<const char *>(glGetString(GL_RENDERER));
return renderer ? renderer : "Unknown";
} }
bool GLContext::hasExtension(const std::string& extension) const { bool GLContext::hasExtension(const std::string &extension) const {
GLint numExtensions = 0; GLint numExtensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
for (GLint i = 0; i < numExtensions; ++i) { for (GLint i = 0; i < numExtensions; ++i) {
const char* ext = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i)); const char *ext =
if (ext && extension == ext) { reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i));
return true; if (ext && extension == ext) {
} return true;
} }
}
return false; return false;
} }
int GLContext::getMaxTextureSize() const { int GLContext::getMaxTextureSize() const {
if (maxTextureSize_ < 0) { if (maxTextureSize_ < 0) {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_);
} }
return maxTextureSize_; return maxTextureSize_;
} }
int GLContext::getMaxTextureUnits() const { int GLContext::getMaxTextureUnits() const {
if (maxTextureUnits_ < 0) { if (maxTextureUnits_ < 0) {
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits_); glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits_);
} }
return maxTextureUnits_; return maxTextureUnits_;
} }
int GLContext::getMaxVertexAttribs() const { int GLContext::getMaxVertexAttribs() const {
if (maxVertexAttribs_ < 0) { if (maxVertexAttribs_ < 0) {
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs_); glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs_);
} }
return maxVertexAttribs_; return maxVertexAttribs_;
} }
int GLContext::getMaxUniformBufferBindings() const { int GLContext::getMaxUniformBufferBindings() const {
if (maxUniformBufferBindings_ < 0) { if (maxUniformBufferBindings_ < 0) {
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings_); glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings_);
} }
return maxUniformBufferBindings_; return maxUniformBufferBindings_;
} }
bool GLContext::hasVAO() const { bool GLContext::hasVAO() const {
// OpenGL 3.0+ 或 OpenGL ES 3.0+ 原生支持 VAO // OpenGL 3.0+ 或 OpenGL ES 3.0+ 原生支持 VAO
if (version_.es) { if (version_.es) {
return version_.major >= 3; return version_.major >= 3;
} }
return version_.major > 3 || (version_.major == 3 && version_.minor >= 0); return version_.major > 3 || (version_.major == 3 && version_.minor >= 0);
} }
bool GLContext::hasFBO() const { bool GLContext::hasFBO() const {
// OpenGL 3.0+ 或 OpenGL ES 2.0+ 原生支持 FBO // OpenGL 3.0+ 或 OpenGL ES 2.0+ 原生支持 FBO
if (version_.es) { if (version_.es) {
return version_.major >= 2; return version_.major >= 2;
} }
return version_.major >= 3; return version_.major >= 3;
} }
bool GLContext::hasShader() const { bool GLContext::hasShader() const {
// OpenGL 2.0+ 或 OpenGL ES 2.0+ 原生支持 Shader // OpenGL 2.0+ 或 OpenGL ES 2.0+ 原生支持 Shader
if (version_.es) { if (version_.es) {
return version_.major >= 2;
}
return version_.major >= 2; return version_.major >= 2;
}
return version_.major >= 2;
} }
void GLContext::parseVersion() { void GLContext::parseVersion() {
const char* versionStr = reinterpret_cast<const char*>(glGetString(GL_VERSION)); const char *versionStr =
if (!versionStr) { reinterpret_cast<const char *>(glGetString(GL_VERSION));
version_ = GLVersion{0, 0, false}; if (!versionStr) {
return; version_ = GLVersion{0, 0, false};
} return;
}
std::string version(versionStr); std::string version(versionStr);
// 检查是否为 OpenGL ES // 检查是否为 OpenGL ES
if (version.find("OpenGL ES") != std::string::npos) { if (version.find("OpenGL ES") != std::string::npos) {
version_.es = true; version_.es = true;
// 解析 ES 版本号,格式如 "OpenGL ES 3.0" // 解析 ES 版本号,格式如 "OpenGL ES 3.0"
std::sscanf(version.c_str(), "OpenGL ES %d.%d", &version_.major, &version_.minor); std::sscanf(version.c_str(), "OpenGL ES %d.%d", &version_.major,
} else { &version_.minor);
version_.es = false; } else {
// 解析桌面版本号,格式如 "3.3.0 NVIDIA" version_.es = false;
std::sscanf(version.c_str(), "%d.%d", &version_.major, &version_.minor); // 解析桌面版本号,格式如 "3.3.0 NVIDIA"
} std::sscanf(version.c_str(), "%d.%d", &version_.major, &version_.minor);
}
} }
bool GLContext::loadExtensions() { bool GLContext::loadExtensions() {
// GLAD 已经在 glad.c 中加载了所有扩展 // GLAD 已经在 glad.c 中加载了所有扩展
// 这里可以添加额外的扩展检查 // 这里可以添加额外的扩展检查
return true; return true;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,8 +1,8 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/opengl/gl_font_atlas.h> #include <extra2d/graphics/backends/opengl/gl_font_atlas.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <cstring> #include <cstring>
#include <fstream> #include <fstream>

View File

@ -1,6 +1,8 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h> #include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
#include <extra2d/graphics/backends/opengl/gl_texture.h> #include <extra2d/graphics/backends/opengl/gl_texture.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
@ -10,259 +12,261 @@ namespace extra2d {
GLFramebuffer::GLFramebuffer() = default; GLFramebuffer::GLFramebuffer() = default;
GLFramebuffer::~GLFramebuffer() { GLFramebuffer::~GLFramebuffer() { shutdown(); }
bool GLFramebuffer::init(const FramebufferDesc &desc) {
if (fboID_ != 0) {
shutdown(); shutdown();
} }
bool GLFramebuffer::init(const FramebufferDesc& desc) { width_ = desc.width;
if (fboID_ != 0) { height_ = desc.height;
shutdown(); numColorAttachments_ = desc.colorAttachments;
} hasDepth_ = desc.hasDepth;
hasStencil_ = desc.hasStencil;
width_ = desc.width; // 限制颜色附件数
height_ = desc.height; if (numColorAttachments_ > MAX_COLOR_ATTACHMENTS) {
numColorAttachments_ = desc.colorAttachments; numColorAttachments_ = MAX_COLOR_ATTACHMENTS;
hasDepth_ = desc.hasDepth; }
hasStencil_ = desc.hasStencil;
// 限制颜色附件数 // 生成 FBO
if (numColorAttachments_ > MAX_COLOR_ATTACHMENTS) { glGenFramebuffers(1, &fboID_);
numColorAttachments_ = MAX_COLOR_ATTACHMENTS; if (fboID_ == 0) {
} E2D_LOG_ERROR("Failed to generate OpenGL framebuffer");
return false;
}
// 生成 FBO E2D_LOG_DEBUG("GLFramebuffer created: ID={}, Size={}x{}, ColorAttachments={}",
glGenFramebuffers(1, &fboID_); fboID_, width_, height_, numColorAttachments_);
if (fboID_ == 0) {
E2D_LOG_ERROR("Failed to generate OpenGL framebuffer");
return false;
}
E2D_LOG_DEBUG("GLFramebuffer created: ID={}, Size={}x{}, ColorAttachments={}", return true;
fboID_, width_, height_, numColorAttachments_);
return true;
} }
void GLFramebuffer::shutdown() { void GLFramebuffer::shutdown() {
if (fboID_ != 0) { if (fboID_ != 0) {
glDeleteFramebuffers(1, &fboID_); glDeleteFramebuffers(1, &fboID_);
E2D_LOG_DEBUG("GLFramebuffer destroyed: ID={}", fboID_); E2D_LOG_DEBUG("GLFramebuffer destroyed: ID={}", fboID_);
fboID_ = 0; fboID_ = 0;
} }
// 清理纹理引用 // 清理纹理引用
for (auto& tex : colorTextures_) { for (auto &tex : colorTextures_) {
tex.reset(); tex.reset();
} }
depthTexture_.reset(); depthTexture_.reset();
depthStencilTexture_.reset(); depthStencilTexture_.reset();
hasInternalTextures_ = false; hasInternalTextures_ = false;
} }
void GLFramebuffer::bind() { void GLFramebuffer::bind() {
if (fboID_ != 0) { if (fboID_ != 0) {
glBindFramebuffer(GL_FRAMEBUFFER, fboID_); glBindFramebuffer(GL_FRAMEBUFFER, fboID_);
} }
} }
void GLFramebuffer::unbind() { void GLFramebuffer::unbind() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void GLFramebuffer::attachColorTexture(Ptr<Texture> texture, int attachment) { void GLFramebuffer::attachColorTexture(Ptr<Texture> texture, int attachment) {
if (fboID_ == 0 || !texture || attachment < 0 || attachment >= MAX_COLOR_ATTACHMENTS) { if (fboID_ == 0 || !texture || attachment < 0 ||
return; attachment >= MAX_COLOR_ATTACHMENTS) {
} return;
}
bind(); bind();
// 获取 OpenGL 纹理 ID // 获取 OpenGL 纹理 ID
GLuint texID = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture->getNativeHandle())); GLuint texID = static_cast<GLuint>(
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
glFramebufferTexture2D(GL_FRAMEBUFFER, getColorAttachment(attachment), glFramebufferTexture2D(GL_FRAMEBUFFER, getColorAttachment(attachment),
GL_TEXTURE_2D, texID, 0); GL_TEXTURE_2D, texID, 0);
colorTextures_[attachment] = texture; colorTextures_[attachment] = texture;
unbind(); unbind();
} }
void GLFramebuffer::attachDepthTexture(Ptr<Texture> texture) { void GLFramebuffer::attachDepthTexture(Ptr<Texture> texture) {
if (fboID_ == 0 || !texture) { if (fboID_ == 0 || !texture) {
return; return;
} }
bind(); bind();
// 获取 OpenGL 纹理 ID // 获取 OpenGL 纹理 ID
GLuint texID = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture->getNativeHandle())); GLuint texID = static_cast<GLuint>(
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
GL_TEXTURE_2D, texID, 0); texID, 0);
depthTexture_ = texture; depthTexture_ = texture;
hasDepth_ = true; hasDepth_ = true;
hasStencil_ = false; hasStencil_ = false;
unbind(); unbind();
} }
void GLFramebuffer::attachDepthStencilTexture(Ptr<Texture> texture) { void GLFramebuffer::attachDepthStencilTexture(Ptr<Texture> texture) {
if (fboID_ == 0 || !texture) { if (fboID_ == 0 || !texture) {
return; return;
} }
bind(); bind();
// 获取 OpenGL 纹理 ID // 获取 OpenGL 纹理 ID
GLuint texID = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture->getNativeHandle())); GLuint texID = static_cast<GLuint>(
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_TEXTURE_2D, texID, 0); GL_TEXTURE_2D, texID, 0);
depthStencilTexture_ = texture; depthStencilTexture_ = texture;
hasDepth_ = true; hasDepth_ = true;
hasStencil_ = true; hasStencil_ = true;
unbind(); unbind();
} }
bool GLFramebuffer::isComplete() { bool GLFramebuffer::isComplete() {
if (fboID_ == 0) { if (fboID_ == 0) {
return false; return false;
} }
bind(); bind();
bool complete = checkStatus(); bool complete = checkStatus();
unbind(); unbind();
return complete; return complete;
} }
Ptr<Texture> GLFramebuffer::getColorTexture(int attachment) const { Ptr<Texture> GLFramebuffer::getColorTexture(int attachment) const {
if (attachment >= 0 && attachment < MAX_COLOR_ATTACHMENTS) { if (attachment >= 0 && attachment < MAX_COLOR_ATTACHMENTS) {
return colorTextures_[attachment]; return colorTextures_[attachment];
} }
return nullptr; return nullptr;
} }
Ptr<Texture> GLFramebuffer::getDepthTexture() const { Ptr<Texture> GLFramebuffer::getDepthTexture() const { return depthTexture_; }
return depthTexture_;
}
void GLFramebuffer::clear(const Color& color, bool clearColor, void GLFramebuffer::clear(const Color &color, bool clearColor, bool clearDepth,
bool clearDepth, bool clearStencil) { bool clearStencil) {
if (fboID_ == 0) { if (fboID_ == 0) {
return; return;
} }
bind(); bind();
GLbitfield mask = 0; GLbitfield mask = 0;
if (clearColor) { if (clearColor) {
mask |= GL_COLOR_BUFFER_BIT; mask |= GL_COLOR_BUFFER_BIT;
glClearColor(color.r, color.g, color.b, color.a); glClearColor(color.r, color.g, color.b, color.a);
} }
if (clearDepth) { if (clearDepth) {
mask |= GL_DEPTH_BUFFER_BIT; mask |= GL_DEPTH_BUFFER_BIT;
glClearDepthf(1.0f); glClearDepthf(1.0f);
} }
if (clearStencil) { if (clearStencil) {
mask |= GL_STENCIL_BUFFER_BIT; mask |= GL_STENCIL_BUFFER_BIT;
glClearStencil(0); glClearStencil(0);
} }
if (mask != 0) { if (mask != 0) {
glClear(mask); glClear(mask);
} }
unbind(); unbind();
} }
void GLFramebuffer::setViewport(int x, int y, int width, int height) { void GLFramebuffer::setViewport(int x, int y, int width, int height) {
glViewport(x, y, width, height); glViewport(x, y, width, height);
} }
bool GLFramebuffer::readPixels(int x, int y, int width, int height, bool GLFramebuffer::readPixels(int x, int y, int width, int height,
std::vector<uint8_t>& outData) { std::vector<uint8_t> &outData) {
if (fboID_ == 0 || width <= 0 || height <= 0) { if (fboID_ == 0 || width <= 0 || height <= 0) {
return false; return false;
} }
// 计算需要的缓冲区大小 (RGBA8) // 计算需要的缓冲区大小 (RGBA8)
size_t dataSize = width * height * 4; size_t dataSize = width * height * 4;
outData.resize(dataSize); outData.resize(dataSize);
bind(); bind();
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, outData.data()); glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, outData.data());
unbind(); unbind();
return true; return true;
} }
bool GLFramebuffer::createWithTextures(int width, int height, bool GLFramebuffer::createWithTextures(int width, int height,
PixelFormat colorFormat, PixelFormat colorFormat,
PixelFormat depthFormat) { PixelFormat depthFormat) {
FramebufferDesc desc; FramebufferDesc desc;
desc.width = width; desc.width = width;
desc.height = height; desc.height = height;
desc.colorAttachments = 1; desc.colorAttachments = 1;
desc.hasDepth = (depthFormat != PixelFormat::RGBA8); desc.hasDepth = (depthFormat != PixelFormat::RGBA8);
desc.hasStencil = (depthFormat == PixelFormat::Depth24Stencil8); desc.hasStencil = (depthFormat == PixelFormat::Depth24Stencil8);
if (!init(desc)) { if (!init(desc)) {
return false; return false;
} }
hasInternalTextures_ = true; hasInternalTextures_ = true;
// 创建颜色纹理 // 创建颜色纹理
// 注意:这里简化处理,实际应该通过纹理工厂创建 // 注意:这里简化处理,实际应该通过纹理工厂创建
// 暂时返回 true实际纹理创建由调用者处理 // 暂时返回 true实际纹理创建由调用者处理
return true; return true;
} }
bool GLFramebuffer::checkStatus() { bool GLFramebuffer::checkStatus() {
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch (status) { switch (status) {
case GL_FRAMEBUFFER_COMPLETE: case GL_FRAMEBUFFER_COMPLETE:
return true; return true;
case GL_FRAMEBUFFER_UNDEFINED: case GL_FRAMEBUFFER_UNDEFINED:
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_UNDEFINED"); E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_UNDEFINED");
break; break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); E2D_LOG_ERROR(
break; "Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: break;
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
break; E2D_LOG_ERROR(
"Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
break;
#ifndef GL_ES_VERSION_2_0 #ifndef GL_ES_VERSION_2_0
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"); E2D_LOG_ERROR(
break; "Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: break;
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"); case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
break; E2D_LOG_ERROR(
"Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
break;
#endif #endif
case GL_FRAMEBUFFER_UNSUPPORTED: case GL_FRAMEBUFFER_UNSUPPORTED:
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_UNSUPPORTED"); E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_UNSUPPORTED");
break; break;
default: default:
E2D_LOG_ERROR("Framebuffer incomplete: Unknown error {}", status); E2D_LOG_ERROR("Framebuffer incomplete: Unknown error {}", status);
break; break;
} }
return false; return false;
} }
GLenum GLFramebuffer::getColorAttachment(int index) { GLenum GLFramebuffer::getColorAttachment(int index) {
return GL_COLOR_ATTACHMENT0 + index; return GL_COLOR_ATTACHMENT0 + index;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,5 +1,7 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/opengl/gl_pipeline.h> #include <extra2d/graphics/backends/opengl/gl_pipeline.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
@ -9,214 +11,225 @@ namespace extra2d {
GLPipeline::GLPipeline() = default; GLPipeline::GLPipeline() = default;
GLPipeline::~GLPipeline() { GLPipeline::~GLPipeline() { shutdown(); }
bool GLPipeline::init(const PipelineDesc &desc) {
if (initialized_) {
shutdown(); shutdown();
}
blendMode_ = desc.blendMode;
blendEnabled_ = desc.blendEnabled;
depthTest_ = desc.depthTest;
depthWrite_ = desc.depthWrite;
depthFunc_ = desc.depthFunc;
cullMode_ = desc.cullMode;
initialized_ = true;
E2D_LOG_DEBUG(
"GLPipeline initialized: blendMode={}, depthTest={}, cullMode={}",
static_cast<int>(blendMode_), depthTest_, static_cast<int>(cullMode_));
return true;
} }
bool GLPipeline::init(const PipelineDesc& desc) { void GLPipeline::shutdown() { initialized_ = false; }
if (initialized_) {
shutdown();
}
blendMode_ = desc.blendMode;
blendEnabled_ = desc.blendEnabled;
depthTest_ = desc.depthTest;
depthWrite_ = desc.depthWrite;
depthFunc_ = desc.depthFunc;
cullMode_ = desc.cullMode;
initialized_ = true;
E2D_LOG_DEBUG("GLPipeline initialized: blendMode={}, depthTest={}, cullMode={}",
static_cast<int>(blendMode_), depthTest_, static_cast<int>(cullMode_));
return true;
}
void GLPipeline::shutdown() {
initialized_ = false;
}
void GLPipeline::bind() { void GLPipeline::bind() {
if (!initialized_) { if (!initialized_) {
return; return;
} }
applyAllStates(); applyAllStates();
} }
void GLPipeline::unbind() { void GLPipeline::unbind() {
// OpenGL 不需要显式解绑管线 // OpenGL 不需要显式解绑管线
} }
void GLPipeline::setBlendMode(BlendMode mode) { void GLPipeline::setBlendMode(BlendMode mode) {
blendMode_ = mode; blendMode_ = mode;
applyBlendState(); applyBlendState();
} }
void GLPipeline::setDepthTest(bool enabled) { void GLPipeline::setDepthTest(bool enabled) {
depthTest_ = enabled; depthTest_ = enabled;
applyDepthState(); applyDepthState();
} }
void GLPipeline::setDepthWrite(bool enabled) { void GLPipeline::setDepthWrite(bool enabled) {
depthWrite_ = enabled; depthWrite_ = enabled;
applyDepthState(); applyDepthState();
} }
void GLPipeline::setDepthFunc(DepthFunc func) { void GLPipeline::setDepthFunc(DepthFunc func) {
depthFunc_ = func; depthFunc_ = func;
applyDepthState(); applyDepthState();
} }
void GLPipeline::setCullMode(CullMode mode) { void GLPipeline::setCullMode(CullMode mode) {
cullMode_ = mode; cullMode_ = mode;
applyCullState(); applyCullState();
} }
void GLPipeline::setViewport(int x, int y, int width, int height) { void GLPipeline::setViewport(int x, int y, int width, int height) {
viewportX_ = x; viewportX_ = x;
viewportY_ = y; viewportY_ = y;
viewportWidth_ = width; viewportWidth_ = width;
viewportHeight_ = height; viewportHeight_ = height;
// 检查缓存,避免冗余调用 // 检查缓存,避免冗余调用
if (x != cachedViewportX_ || y != cachedViewportY_ || if (x != cachedViewportX_ || y != cachedViewportY_ ||
width != cachedViewportWidth_ || height != cachedViewportHeight_) { width != cachedViewportWidth_ || height != cachedViewportHeight_) {
glViewport(x, y, width, height); glViewport(x, y, width, height);
cachedViewportX_ = x; cachedViewportX_ = x;
cachedViewportY_ = y; cachedViewportY_ = y;
cachedViewportWidth_ = width; cachedViewportWidth_ = width;
cachedViewportHeight_ = height; cachedViewportHeight_ = height;
} }
} }
void GLPipeline::getViewport(int& x, int& y, int& width, int& height) const { void GLPipeline::getViewport(int &x, int &y, int &width, int &height) const {
x = viewportX_; x = viewportX_;
y = viewportY_; y = viewportY_;
width = viewportWidth_; width = viewportWidth_;
height = viewportHeight_; height = viewportHeight_;
} }
void GLPipeline::applyAllStates() { void GLPipeline::applyAllStates() {
applyBlendState(); applyBlendState();
applyDepthState(); applyDepthState();
applyCullState(); applyCullState();
// 应用视口 // 应用视口
if (viewportWidth_ > 0 && viewportHeight_ > 0) { if (viewportWidth_ > 0 && viewportHeight_ > 0) {
setViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_); setViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
} }
} }
void GLPipeline::applyBlendState() { void GLPipeline::applyBlendState() {
// 检查是否需要启用/禁用混合 // 检查是否需要启用/禁用混合
if (blendEnabled_ != cachedBlendEnabled_) { if (blendEnabled_ != cachedBlendEnabled_) {
if (blendEnabled_) { if (blendEnabled_) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
} else { } else {
glDisable(GL_BLEND); glDisable(GL_BLEND);
}
cachedBlendEnabled_ = blendEnabled_;
} }
cachedBlendEnabled_ = blendEnabled_;
}
// 如果禁用了混合,不需要设置混合函数 // 如果禁用了混合,不需要设置混合函数
if (!blendEnabled_) { if (!blendEnabled_) {
return; return;
} }
// 检查混合模式是否改变 // 检查混合模式是否改变
if (blendMode_ != cachedBlendMode_) { if (blendMode_ != cachedBlendMode_) {
GLenum srcFactor, dstFactor; GLenum srcFactor, dstFactor;
getBlendFactors(blendMode_, srcFactor, dstFactor); getBlendFactors(blendMode_, srcFactor, dstFactor);
glBlendFunc(srcFactor, dstFactor); glBlendFunc(srcFactor, dstFactor);
cachedBlendMode_ = blendMode_; cachedBlendMode_ = blendMode_;
} }
} }
void GLPipeline::applyDepthState() { void GLPipeline::applyDepthState() {
// 深度测试 // 深度测试
if (depthTest_ != cachedDepthTest_) { if (depthTest_ != cachedDepthTest_) {
if (depthTest_) { if (depthTest_) {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} else { } else {
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
}
cachedDepthTest_ = depthTest_;
} }
cachedDepthTest_ = depthTest_;
}
// 深度写入 // 深度写入
if (depthWrite_ != cachedDepthWrite_) { if (depthWrite_ != cachedDepthWrite_) {
glDepthMask(depthWrite_ ? GL_TRUE : GL_FALSE); glDepthMask(depthWrite_ ? GL_TRUE : GL_FALSE);
cachedDepthWrite_ = depthWrite_; cachedDepthWrite_ = depthWrite_;
} }
// 深度函数 // 深度函数
if (depthFunc_ != cachedDepthFunc_) { if (depthFunc_ != cachedDepthFunc_) {
glDepthFunc(convertDepthFunc(depthFunc_)); glDepthFunc(convertDepthFunc(depthFunc_));
cachedDepthFunc_ = depthFunc_; cachedDepthFunc_ = depthFunc_;
} }
} }
void GLPipeline::applyCullState() { void GLPipeline::applyCullState() {
// 检查裁剪模式是否改变 // 检查裁剪模式是否改变
if (cullMode_ != cachedCullMode_) { if (cullMode_ != cachedCullMode_) {
if (cullMode_ == CullMode::None) { if (cullMode_ == CullMode::None) {
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
} else { } else {
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
glCullFace(convertCullMode(cullMode_)); glCullFace(convertCullMode(cullMode_));
}
cachedCullMode_ = cullMode_;
} }
cachedCullMode_ = cullMode_;
}
} }
void GLPipeline::getBlendFactors(BlendMode mode, GLenum& srcFactor, GLenum& dstFactor) { void GLPipeline::getBlendFactors(BlendMode mode, GLenum &srcFactor,
switch (mode) { GLenum &dstFactor) {
case BlendMode::None: switch (mode) {
srcFactor = GL_ONE; case BlendMode::None:
dstFactor = GL_ZERO; srcFactor = GL_ONE;
break; dstFactor = GL_ZERO;
case BlendMode::Alpha: break;
srcFactor = GL_SRC_ALPHA; case BlendMode::Alpha:
dstFactor = GL_ONE_MINUS_SRC_ALPHA; srcFactor = GL_SRC_ALPHA;
break; dstFactor = GL_ONE_MINUS_SRC_ALPHA;
case BlendMode::Additive: break;
srcFactor = GL_SRC_ALPHA; case BlendMode::Additive:
dstFactor = GL_ONE; srcFactor = GL_SRC_ALPHA;
break; dstFactor = GL_ONE;
case BlendMode::Multiply: break;
srcFactor = GL_DST_COLOR; case BlendMode::Multiply:
dstFactor = GL_ONE_MINUS_SRC_ALPHA; srcFactor = GL_DST_COLOR;
break; dstFactor = GL_ONE_MINUS_SRC_ALPHA;
default: break;
srcFactor = GL_SRC_ALPHA; default:
dstFactor = GL_ONE_MINUS_SRC_ALPHA; srcFactor = GL_SRC_ALPHA;
break; dstFactor = GL_ONE_MINUS_SRC_ALPHA;
} break;
}
} }
GLenum GLPipeline::convertDepthFunc(DepthFunc func) { GLenum GLPipeline::convertDepthFunc(DepthFunc func) {
switch (func) { switch (func) {
case DepthFunc::Never: return GL_NEVER; case DepthFunc::Never:
case DepthFunc::Less: return GL_LESS; return GL_NEVER;
case DepthFunc::Equal: return GL_EQUAL; case DepthFunc::Less:
case DepthFunc::LessEqual: return GL_LEQUAL; return GL_LESS;
case DepthFunc::Greater: return GL_GREATER; case DepthFunc::Equal:
case DepthFunc::NotEqual: return GL_NOTEQUAL; return GL_EQUAL;
case DepthFunc::GreaterEqual: return GL_GEQUAL; case DepthFunc::LessEqual:
case DepthFunc::Always: return GL_ALWAYS; return GL_LEQUAL;
default: return GL_LESS; case DepthFunc::Greater:
} return GL_GREATER;
case DepthFunc::NotEqual:
return GL_NOTEQUAL;
case DepthFunc::GreaterEqual:
return GL_GEQUAL;
case DepthFunc::Always:
return GL_ALWAYS;
default:
return GL_LESS;
}
} }
GLenum GLPipeline::convertCullMode(CullMode mode) { GLenum GLPipeline::convertCullMode(CullMode mode) {
switch (mode) { switch (mode) {
case CullMode::Front: return GL_FRONT; case CullMode::Front:
case CullMode::Back: return GL_BACK; return GL_FRONT;
case CullMode::Both: return GL_FRONT_AND_BACK; case CullMode::Back:
default: return GL_BACK; return GL_BACK;
} case CullMode::Both:
return GL_FRONT_AND_BACK;
default:
return GL_BACK;
}
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,6 +1,6 @@
#include <algorithm>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/opengl/gl_context.h> #include <extra2d/graphics/backends/opengl/gl_context.h>
#include <extra2d/graphics/backends/opengl/gl_font_atlas.h> #include <extra2d/graphics/backends/opengl/gl_font_atlas.h>
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h> #include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
@ -11,7 +11,7 @@
#include <extra2d/graphics/memory/vram_manager.h> #include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/graphics/shader/shader_manager.h> #include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/platform/iwindow.h> #include <extra2d/platform/iwindow.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
#include <vector> #include <vector>
namespace extra2d { namespace extra2d {
@ -23,8 +23,8 @@ static constexpr size_t SHAPE_VBO_SIZE = 1024 * sizeof(float);
* @brief OpenGL渲染器成员变量 * @brief OpenGL渲染器成员变量
*/ */
GLRenderer::GLRenderer() GLRenderer::GLRenderer()
: window_(nullptr), shapeVao_(0), lineVao_(0), : window_(nullptr), shapeVao_(0), lineVao_(0), vsync_(true),
vsync_(true), shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES), shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES),
lineVertexCount_(0), currentLineWidth_(1.0f) { lineVertexCount_(0), currentLineWidth_(1.0f) {
resetStats(); resetStats();
for (auto &v : shapeVertexCache_) { for (auto &v : shapeVertexCache_) {
@ -48,7 +48,8 @@ GLRenderer::~GLRenderer() { shutdown(); }
bool GLRenderer::init(IWindow *window) { bool GLRenderer::init(IWindow *window) {
window_ = window; window_ = window;
// 初始化 OpenGL 上下文Switch 平台已通过 SDL2 + EGL 初始化GLContext 会处理兼容性) // 初始化 OpenGL 上下文Switch 平台已通过 SDL2 + EGL 初始化GLContext
// 会处理兼容性)
if (!GLContext::get().init()) { if (!GLContext::get().init()) {
E2D_LOG_ERROR("Failed to initialize OpenGL context"); E2D_LOG_ERROR("Failed to initialize OpenGL context");
return false; return false;
@ -937,7 +938,7 @@ void GLRenderer::flushLineBatch() {
* @param desc * @param desc
* @return * @return
*/ */
Ptr<GLFramebuffer> GLRenderer::createFramebuffer(const FramebufferDesc& desc) { Ptr<GLFramebuffer> GLRenderer::createFramebuffer(const FramebufferDesc &desc) {
auto framebuffer = makePtr<GLFramebuffer>(); auto framebuffer = makePtr<GLFramebuffer>();
if (!framebuffer->init(desc)) { if (!framebuffer->init(desc)) {
E2D_LOG_ERROR("Failed to create framebuffer"); E2D_LOG_ERROR("Failed to create framebuffer");
@ -950,7 +951,7 @@ Ptr<GLFramebuffer> GLRenderer::createFramebuffer(const FramebufferDesc& desc) {
* @brief * @brief
* @param framebuffer nullptr * @param framebuffer nullptr
*/ */
void GLRenderer::bindFramebuffer(GLFramebuffer* framebuffer) { void GLRenderer::bindFramebuffer(GLFramebuffer *framebuffer) {
// 先刷新所有待处理的渲染批次 // 先刷新所有待处理的渲染批次
flush(); flush();
flushShapeBatch(); flushShapeBatch();
@ -972,9 +973,7 @@ void GLRenderer::bindFramebuffer(GLFramebuffer* framebuffer) {
/** /**
* @brief * @brief
*/ */
void GLRenderer::unbindFramebuffer() { void GLRenderer::unbindFramebuffer() { bindFramebuffer(nullptr); }
bindFramebuffer(nullptr);
}
/** /**
* @brief * @brief
@ -996,7 +995,7 @@ Ptr<GLFramebuffer> GLRenderer::getDefaultFramebuffer() const {
* @param clearDepth * @param clearDepth
* @param clearStencil * @param clearStencil
*/ */
void GLRenderer::clearFramebuffer(const Color& color, bool clearColor, void GLRenderer::clearFramebuffer(const Color &color, bool clearColor,
bool clearDepth, bool clearStencil) { bool clearDepth, bool clearStencil) {
GLbitfield mask = 0; GLbitfield mask = 0;

View File

@ -1,45 +1,42 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/opengl/gl_shader.h> #include <extra2d/graphics/backends/opengl/gl_shader.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
/** /**
* @brief ID为0 * @brief ID为0
*/ */
GLShader::GLShader() : programID_(0) { GLShader::GLShader() : programID_(0) {}
}
/** /**
* @brief OpenGL着色器程序 * @brief OpenGL着色器程序
*/ */
GLShader::~GLShader() { GLShader::~GLShader() {
if (programID_ != 0) { if (programID_ != 0) {
glDeleteProgram(programID_); glDeleteProgram(programID_);
programID_ = 0; programID_ = 0;
} }
} }
/** /**
* @brief Shader程序 * @brief Shader程序
*/ */
void GLShader::bind() const { void GLShader::bind() const { glUseProgram(programID_); }
glUseProgram(programID_);
}
/** /**
* @brief Shader程序 * @brief Shader程序
*/ */
void GLShader::unbind() const { void GLShader::unbind() const { glUseProgram(0); }
glUseProgram(0);
}
/** /**
* @brief uniform变量 * @brief uniform变量
* @param name uniform变量名 * @param name uniform变量名
* @param value * @param value
*/ */
void GLShader::setBool(const std::string& name, bool value) { void GLShader::setBool(const std::string &name, bool value) {
glUniform1i(getUniformLocation(name), value ? 1 : 0); glUniform1i(getUniformLocation(name), value ? 1 : 0);
} }
/** /**
@ -47,8 +44,8 @@ void GLShader::setBool(const std::string& name, bool value) {
* @param name uniform变量名 * @param name uniform变量名
* @param value * @param value
*/ */
void GLShader::setInt(const std::string& name, int value) { void GLShader::setInt(const std::string &name, int value) {
glUniform1i(getUniformLocation(name), value); glUniform1i(getUniformLocation(name), value);
} }
/** /**
@ -56,8 +53,8 @@ void GLShader::setInt(const std::string& name, int value) {
* @param name uniform变量名 * @param name uniform变量名
* @param value * @param value
*/ */
void GLShader::setFloat(const std::string& name, float value) { void GLShader::setFloat(const std::string &name, float value) {
glUniform1f(getUniformLocation(name), value); glUniform1f(getUniformLocation(name), value);
} }
/** /**
@ -65,8 +62,8 @@ void GLShader::setFloat(const std::string& name, float value) {
* @param name uniform变量名 * @param name uniform变量名
* @param value * @param value
*/ */
void GLShader::setVec2(const std::string& name, const glm::vec2& value) { void GLShader::setVec2(const std::string &name, const glm::vec2 &value) {
glUniform2fv(getUniformLocation(name), 1, &value[0]); glUniform2fv(getUniformLocation(name), 1, &value[0]);
} }
/** /**
@ -74,8 +71,8 @@ void GLShader::setVec2(const std::string& name, const glm::vec2& value) {
* @param name uniform变量名 * @param name uniform变量名
* @param value * @param value
*/ */
void GLShader::setVec3(const std::string& name, const glm::vec3& value) { void GLShader::setVec3(const std::string &name, const glm::vec3 &value) {
glUniform3fv(getUniformLocation(name), 1, &value[0]); glUniform3fv(getUniformLocation(name), 1, &value[0]);
} }
/** /**
@ -83,8 +80,8 @@ void GLShader::setVec3(const std::string& name, const glm::vec3& value) {
* @param name uniform变量名 * @param name uniform变量名
* @param value * @param value
*/ */
void GLShader::setVec4(const std::string& name, const glm::vec4& value) { void GLShader::setVec4(const std::string &name, const glm::vec4 &value) {
glUniform4fv(getUniformLocation(name), 1, &value[0]); glUniform4fv(getUniformLocation(name), 1, &value[0]);
} }
/** /**
@ -92,8 +89,8 @@ void GLShader::setVec4(const std::string& name, const glm::vec4& value) {
* @param name uniform变量名 * @param name uniform变量名
* @param value 4x4矩阵值 * @param value 4x4矩阵值
*/ */
void GLShader::setMat4(const std::string& name, const glm::mat4& value) { void GLShader::setMat4(const std::string &name, const glm::mat4 &value) {
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, &value[0][0]); glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, &value[0][0]);
} }
/** /**
@ -101,8 +98,8 @@ void GLShader::setMat4(const std::string& name, const glm::mat4& value) {
* @param name uniform变量名 * @param name uniform变量名
* @param color * @param color
*/ */
void GLShader::setColor(const std::string& name, const Color& color) { void GLShader::setColor(const std::string &name, const Color &color) {
glUniform4f(getUniformLocation(name), color.r, color.g, color.b, color.a); glUniform4f(getUniformLocation(name), color.r, color.g, color.b, color.a);
} }
/** /**
@ -111,42 +108,43 @@ void GLShader::setColor(const std::string& name, const Color& color) {
* @param fragmentSource * @param fragmentSource
* @return truefalse * @return truefalse
*/ */
bool GLShader::compileFromSource(const char* vertexSource, const char* fragmentSource) { bool GLShader::compileFromSource(const char *vertexSource,
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource); const char *fragmentSource) {
if (vertexShader == 0) { GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource);
return false; if (vertexShader == 0) {
} return false;
}
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource);
if (fragmentShader == 0) {
glDeleteShader(vertexShader);
return false;
}
if (programID_ != 0) {
glDeleteProgram(programID_);
uniformCache_.clear();
}
programID_ = glCreateProgram();
glAttachShader(programID_, vertexShader);
glAttachShader(programID_, fragmentShader);
glLinkProgram(programID_);
GLint success;
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
E2D_LOG_ERROR("Shader program linking failed: {}", infoLog);
glDeleteProgram(programID_);
programID_ = 0;
}
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource);
if (fragmentShader == 0) {
glDeleteShader(vertexShader); glDeleteShader(vertexShader);
glDeleteShader(fragmentShader); return false;
}
return success == GL_TRUE; if (programID_ != 0) {
glDeleteProgram(programID_);
uniformCache_.clear();
}
programID_ = glCreateProgram();
glAttachShader(programID_, vertexShader);
glAttachShader(programID_, fragmentShader);
glLinkProgram(programID_);
GLint success;
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
E2D_LOG_ERROR("Shader program linking failed: {}", infoLog);
glDeleteProgram(programID_);
programID_ = 0;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return success == GL_TRUE;
} }
/** /**
@ -154,43 +152,45 @@ bool GLShader::compileFromSource(const char* vertexSource, const char* fragmentS
* @param binary * @param binary
* @return truefalse * @return truefalse
*/ */
bool GLShader::compileFromBinary(const std::vector<uint8_t>& binary) { bool GLShader::compileFromBinary(const std::vector<uint8_t> &binary) {
if (binary.empty()) { if (binary.empty()) {
E2D_LOG_ERROR("Binary data is empty"); E2D_LOG_ERROR("Binary data is empty");
return false; return false;
} }
GLint numFormats = 0; GLint numFormats = 0;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numFormats); glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numFormats);
if (numFormats == 0) { if (numFormats == 0) {
E2D_LOG_ERROR("Program binary formats not supported"); E2D_LOG_ERROR("Program binary formats not supported");
return false; return false;
} }
if (programID_ != 0) { if (programID_ != 0) {
glDeleteProgram(programID_); glDeleteProgram(programID_);
uniformCache_.clear(); uniformCache_.clear();
} }
programID_ = glCreateProgram(); programID_ = glCreateProgram();
GLenum binaryFormat = 0; GLenum binaryFormat = 0;
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, reinterpret_cast<GLint*>(&binaryFormat)); glGetIntegerv(GL_PROGRAM_BINARY_FORMATS,
reinterpret_cast<GLint *>(&binaryFormat));
glProgramBinary(programID_, binaryFormat, binary.data(), static_cast<GLsizei>(binary.size())); glProgramBinary(programID_, binaryFormat, binary.data(),
static_cast<GLsizei>(binary.size()));
GLint success = 0; GLint success = 0;
glGetProgramiv(programID_, GL_LINK_STATUS, &success); glGetProgramiv(programID_, GL_LINK_STATUS, &success);
if (!success) { if (!success) {
char infoLog[512]; char infoLog[512];
glGetProgramInfoLog(programID_, 512, nullptr, infoLog); glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
E2D_LOG_ERROR("Failed to load shader from binary: {}", infoLog); E2D_LOG_ERROR("Failed to load shader from binary: {}", infoLog);
glDeleteProgram(programID_); glDeleteProgram(programID_);
programID_ = 0; programID_ = 0;
return false; return false;
} }
return true; return true;
} }
/** /**
@ -198,47 +198,49 @@ bool GLShader::compileFromBinary(const std::vector<uint8_t>& binary) {
* @param outBinary * @param outBinary
* @return truefalse * @return truefalse
*/ */
bool GLShader::getBinary(std::vector<uint8_t>& outBinary) { bool GLShader::getBinary(std::vector<uint8_t> &outBinary) {
if (programID_ == 0) { if (programID_ == 0) {
E2D_LOG_WARN("Cannot get binary: shader program is 0"); E2D_LOG_WARN("Cannot get binary: shader program is 0");
return false; return false;
} }
GLint binaryLength = 0; GLint binaryLength = 0;
glGetProgramiv(programID_, GL_PROGRAM_BINARY_LENGTH, &binaryLength); glGetProgramiv(programID_, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
E2D_LOG_DEBUG("Shader binary length: {}", binaryLength); E2D_LOG_DEBUG("Shader binary length: {}", binaryLength);
if (binaryLength <= 0) { if (binaryLength <= 0) {
E2D_LOG_WARN("Shader binary length is 0 or negative"); E2D_LOG_WARN("Shader binary length is 0 or negative");
return false; return false;
} }
outBinary.resize(binaryLength); outBinary.resize(binaryLength);
GLenum binaryFormat = 0; GLenum binaryFormat = 0;
GLsizei actualLength = 0; GLsizei actualLength = 0;
glGetProgramBinary(programID_, binaryLength, &actualLength, &binaryFormat, outBinary.data()); glGetProgramBinary(programID_, binaryLength, &actualLength, &binaryFormat,
outBinary.data());
GLenum err = glGetError(); GLenum err = glGetError();
if (err != GL_NO_ERROR) { if (err != GL_NO_ERROR) {
E2D_LOG_ERROR("glGetProgramBinary failed with error: {}", err); E2D_LOG_ERROR("glGetProgramBinary failed with error: {}", err);
outBinary.clear(); outBinary.clear();
return false; return false;
} }
if (actualLength == 0) { if (actualLength == 0) {
E2D_LOG_WARN("glGetProgramBinary returned 0 bytes"); E2D_LOG_WARN("glGetProgramBinary returned 0 bytes");
outBinary.clear(); outBinary.clear();
return false; return false;
} }
if (actualLength != binaryLength) { if (actualLength != binaryLength) {
outBinary.resize(actualLength); outBinary.resize(actualLength);
} }
E2D_LOG_DEBUG("Shader binary retrieved: {} bytes, format: {}", actualLength, binaryFormat); E2D_LOG_DEBUG("Shader binary retrieved: {} bytes, format: {}", actualLength,
return true; binaryFormat);
return true;
} }
/** /**
@ -247,22 +249,22 @@ bool GLShader::getBinary(std::vector<uint8_t>& outBinary) {
* @param source * @param source
* @return ID0 * @return ID0
*/ */
GLuint GLShader::compileShader(GLenum type, const char* source) { GLuint GLShader::compileShader(GLenum type, const char *source) {
GLuint shader = glCreateShader(type); GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, nullptr); glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader); glCompileShader(shader);
GLint success; GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success); glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) { if (!success) {
char infoLog[512]; char infoLog[512];
glGetShaderInfoLog(shader, 512, nullptr, infoLog); glGetShaderInfoLog(shader, 512, nullptr, infoLog);
E2D_LOG_ERROR("Shader compilation failed: {}", infoLog); E2D_LOG_ERROR("Shader compilation failed: {}", infoLog);
glDeleteShader(shader); glDeleteShader(shader);
return 0; return 0;
} }
return shader; return shader;
} }
/** /**
@ -270,15 +272,15 @@ GLuint GLShader::compileShader(GLenum type, const char* source) {
* @param name uniform变量名 * @param name uniform变量名
* @return uniform位置 * @return uniform位置
*/ */
GLint GLShader::getUniformLocation(const std::string& name) { GLint GLShader::getUniformLocation(const std::string &name) {
auto it = uniformCache_.find(name); auto it = uniformCache_.find(name);
if (it != uniformCache_.end()) { if (it != uniformCache_.end()) {
return it->second; return it->second;
} }
GLint location = glGetUniformLocation(programID_, name.c_str()); GLint location = glGetUniformLocation(programID_, name.c_str());
uniformCache_[name] = location; uniformCache_[name] = location;
return location; return location;
} }
// ============================================================================ // ============================================================================
@ -292,20 +294,19 @@ GLint GLShader::getUniformLocation(const std::string& name) {
* @param fragSource * @param fragSource
* @return Shader实例 * @return Shader实例
*/ */
Ptr<IShader> GLShaderFactory::createFromSource( Ptr<IShader> GLShaderFactory::createFromSource(const std::string &name,
const std::string& name, const std::string &vertSource,
const std::string& vertSource, const std::string &fragSource) {
const std::string& fragSource) {
auto shader = std::make_shared<GLShader>(); auto shader = std::make_shared<GLShader>();
shader->setName(name); shader->setName(name);
if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) { if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) {
E2D_LOG_ERROR("Failed to compile shader from source: {}", name); E2D_LOG_ERROR("Failed to compile shader from source: {}", name);
return nullptr; return nullptr;
} }
return shader; return shader;
} }
/** /**
@ -314,19 +315,19 @@ Ptr<IShader> GLShaderFactory::createFromSource(
* @param binary * @param binary
* @return Shader实例 * @return Shader实例
*/ */
Ptr<IShader> GLShaderFactory::createFromBinary( Ptr<IShader>
const std::string& name, GLShaderFactory::createFromBinary(const std::string &name,
const std::vector<uint8_t>& binary) { const std::vector<uint8_t> &binary) {
auto shader = std::make_shared<GLShader>(); auto shader = std::make_shared<GLShader>();
shader->setName(name); shader->setName(name);
if (!shader->compileFromBinary(binary)) { if (!shader->compileFromBinary(binary)) {
E2D_LOG_ERROR("Failed to create shader from binary: {}", name); E2D_LOG_ERROR("Failed to create shader from binary: {}", name);
return nullptr; return nullptr;
} }
return shader; return shader;
} }
/** /**
@ -335,14 +336,15 @@ Ptr<IShader> GLShaderFactory::createFromBinary(
* @param outBinary * @param outBinary
* @return truefalse * @return truefalse
*/ */
bool GLShaderFactory::getShaderBinary(const IShader& shader, std::vector<uint8_t>& outBinary) { bool GLShaderFactory::getShaderBinary(const IShader &shader,
const GLShader* glShader = dynamic_cast<const GLShader*>(&shader); std::vector<uint8_t> &outBinary) {
if (!glShader) { const GLShader *glShader = dynamic_cast<const GLShader *>(&shader);
E2D_LOG_ERROR("Shader is not a GLShader instance"); if (!glShader) {
return false; E2D_LOG_ERROR("Shader is not a GLShader instance");
} return false;
}
return const_cast<GLShader*>(glShader)->getBinary(outBinary); return const_cast<GLShader *>(glShader)->getBinary(outBinary);
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,7 +1,9 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/opengl/gl_sprite_batch.h> #include <extra2d/graphics/backends/opengl/gl_sprite_batch.h>
#include <extra2d/graphics/backends/opengl/gl_texture.h> #include <extra2d/graphics/backends/opengl/gl_texture.h>
#include <extra2d/graphics/shader/shader_manager.h> #include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
@ -36,16 +38,19 @@ bool GLSpriteBatch::init() {
// 设置顶点属性 // 设置顶点属性
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex), glVertexAttribPointer(
reinterpret_cast<void *>(offsetof(SpriteVertex, position))); 0, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
reinterpret_cast<void *>(offsetof(SpriteVertex, position)));
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex), glVertexAttribPointer(
reinterpret_cast<void *>(offsetof(SpriteVertex, texCoord))); 1, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
reinterpret_cast<void *>(offsetof(SpriteVertex, texCoord)));
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex), glVertexAttribPointer(
reinterpret_cast<void *>(offsetof(SpriteVertex, color))); 2, 4, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
reinterpret_cast<void *>(offsetof(SpriteVertex, color)));
// 初始化 EBO索引缓冲区- 静态使用模式 // 初始化 EBO索引缓冲区- 静态使用模式
BufferDesc eboDesc; BufferDesc eboDesc;
@ -172,7 +177,7 @@ void GLSpriteBatch::submitBatch() {
// 通过传入 nullptr 进行 orphaning告诉驱动器可以丢弃旧缓冲区并分配新内存 // 通过传入 nullptr 进行 orphaning告诉驱动器可以丢弃旧缓冲区并分配新内存
// 这样可以避免 GPU 等待,提高性能 // 这样可以避免 GPU 等待,提高性能
size_t vertexDataSize = batch_.getVertices().size() * sizeof(SpriteVertex); size_t vertexDataSize = batch_.getVertices().size() * sizeof(SpriteVertex);
vbo_.setData(nullptr, vertexDataSize); // orphaning vbo_.setData(nullptr, vertexDataSize); // orphaning
vbo_.updateData(batch_.getVertices().data(), 0, vertexDataSize); vbo_.updateData(batch_.getVertices().data(), 0, vertexDataSize);
// 绘制 // 绘制

View File

@ -3,10 +3,12 @@
#include <extra2d/graphics/memory/vram_manager.h> #include <extra2d/graphics/memory/vram_manager.h>
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include <cstring> #include <cstring>
#include <extra2d/utils/logger.h> #include <extra2d/core/service_locator.h>
#include <extra2d/services/logger_service.h>
#include <fstream> #include <fstream>
#include <stb/stb_image.h> #include <stb/stb_image.h>
namespace extra2d { namespace extra2d {
// ============================================================================ // ============================================================================

View File

@ -1,150 +1,150 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/vulkan/vk_renderer.h> #include <extra2d/graphics/backends/vulkan/vk_renderer.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
VulkanRenderer::VulkanRenderer() = default; VulkanRenderer::VulkanRenderer() = default;
VulkanRenderer::~VulkanRenderer() { VulkanRenderer::~VulkanRenderer() { shutdown(); }
shutdown();
bool VulkanRenderer::init(IWindow *window) {
E2D_LOG_WARN("Vulkan renderer is not fully implemented yet");
initialized_ = true;
return true;
} }
bool VulkanRenderer::init(IWindow* window) { void VulkanRenderer::shutdown() { initialized_ = false; }
E2D_LOG_WARN("Vulkan renderer is not fully implemented yet");
initialized_ = true;
return true;
}
void VulkanRenderer::shutdown() {
initialized_ = false;
}
void VulkanRenderer::beginFrame(const Color &clearColor) { void VulkanRenderer::beginFrame(const Color &clearColor) {
// TODO: 实现Vulkan帧开始 // TODO: 实现Vulkan帧开始
} }
void VulkanRenderer::endFrame() { void VulkanRenderer::endFrame() {
// TODO: 实现Vulkan帧结束 // TODO: 实现Vulkan帧结束
} }
void VulkanRenderer::setViewport(int x, int y, int width, int height) { void VulkanRenderer::setViewport(int x, int y, int width, int height) {
// TODO: 实现视口设置 // TODO: 实现视口设置
} }
void VulkanRenderer::setVSync(bool enabled) { void VulkanRenderer::setVSync(bool enabled) {
// TODO: 实现垂直同步设置 // TODO: 实现垂直同步设置
} }
void VulkanRenderer::setBlendMode(BlendMode mode) { void VulkanRenderer::setBlendMode(BlendMode mode) {
// TODO: 实现混合模式设置 // TODO: 实现混合模式设置
} }
void VulkanRenderer::setViewProjection(const glm::mat4 &matrix) { void VulkanRenderer::setViewProjection(const glm::mat4 &matrix) {
// TODO: 实现视图投影矩阵设置 // TODO: 实现视图投影矩阵设置
} }
void VulkanRenderer::pushTransform(const glm::mat4 &transform) { void VulkanRenderer::pushTransform(const glm::mat4 &transform) {
// TODO: 实现变换矩阵入栈 // TODO: 实现变换矩阵入栈
} }
void VulkanRenderer::popTransform() { void VulkanRenderer::popTransform() {
// TODO: 实现变换矩阵出栈 // TODO: 实现变换矩阵出栈
} }
glm::mat4 VulkanRenderer::getCurrentTransform() const { glm::mat4 VulkanRenderer::getCurrentTransform() const {
return glm::mat4(1.0f); return glm::mat4(1.0f);
} }
Ptr<Texture> VulkanRenderer::createTexture(int width, int height, const uint8_t *pixels, int channels) { Ptr<Texture> VulkanRenderer::createTexture(int width, int height,
// TODO: 实现Vulkan纹理创建 const uint8_t *pixels,
return nullptr; int channels) {
// TODO: 实现Vulkan纹理创建
return nullptr;
} }
Ptr<Texture> VulkanRenderer::loadTexture(const std::string &filepath) { Ptr<Texture> VulkanRenderer::loadTexture(const std::string &filepath) {
// TODO: 实现Vulkan纹理加载 // TODO: 实现Vulkan纹理加载
return nullptr; return nullptr;
} }
void VulkanRenderer::beginSpriteBatch() { void VulkanRenderer::beginSpriteBatch() {
// TODO: 实现精灵批处理开始 // TODO: 实现精灵批处理开始
} }
void VulkanRenderer::drawSprite(const Texture &texture, const Rect &destRect, void VulkanRenderer::drawSprite(const Texture &texture, const Rect &destRect,
const Rect &srcRect, const Color &tint, float rotation, const Rect &srcRect, const Color &tint,
const Vec2 &anchor) { float rotation, const Vec2 &anchor) {
// TODO: 实现精灵绘制 // TODO: 实现精灵绘制
} }
void VulkanRenderer::drawSprite(const Texture &texture, const Vec2 &position, void VulkanRenderer::drawSprite(const Texture &texture, const Vec2 &position,
const Color &tint) { const Color &tint) {
// TODO: 实现简化精灵绘制 // TODO: 实现简化精灵绘制
} }
void VulkanRenderer::endSpriteBatch() { void VulkanRenderer::endSpriteBatch() {
// TODO: 实现精灵批处理结束 // TODO: 实现精灵批处理结束
} }
void VulkanRenderer::drawLine(const Vec2 &start, const Vec2 &end, const Color &color, void VulkanRenderer::drawLine(const Vec2 &start, const Vec2 &end,
float width) { const Color &color, float width) {
// TODO: 实现线条绘制 // TODO: 实现线条绘制
} }
void VulkanRenderer::drawRect(const Rect &rect, const Color &color, float width) { void VulkanRenderer::drawRect(const Rect &rect, const Color &color,
// TODO: 实现矩形边框绘制 float width) {
// TODO: 实现矩形边框绘制
} }
void VulkanRenderer::fillRect(const Rect &rect, const Color &color) { void VulkanRenderer::fillRect(const Rect &rect, const Color &color) {
// TODO: 实现矩形填充 // TODO: 实现矩形填充
} }
void VulkanRenderer::drawCircle(const Vec2 &center, float radius, const Color &color, void VulkanRenderer::drawCircle(const Vec2 &center, float radius,
int segments, float width) { const Color &color, int segments, float width) {
// TODO: 实现圆形边框绘制 // TODO: 实现圆形边框绘制
} }
void VulkanRenderer::fillCircle(const Vec2 &center, float radius, const Color &color, void VulkanRenderer::fillCircle(const Vec2 &center, float radius,
int segments) { const Color &color, int segments) {
// TODO: 实现圆形填充 // TODO: 实现圆形填充
} }
void VulkanRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, void VulkanRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2,
const Color &color, float width) { const Vec2 &p3, const Color &color,
// TODO: 实现三角形边框绘制 float width) {
// TODO: 实现三角形边框绘制
} }
void VulkanRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, void VulkanRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2,
const Color &color) { const Vec2 &p3, const Color &color) {
// TODO: 实现三角形填充 // TODO: 实现三角形填充
} }
void VulkanRenderer::drawPolygon(const std::vector<Vec2> &points, const Color &color, void VulkanRenderer::drawPolygon(const std::vector<Vec2> &points,
float width) { const Color &color, float width) {
// TODO: 实现多边形边框绘制 // TODO: 实现多边形边框绘制
} }
void VulkanRenderer::fillPolygon(const std::vector<Vec2> &points, void VulkanRenderer::fillPolygon(const std::vector<Vec2> &points,
const Color &color) { const Color &color) {
// TODO: 实现多边形填充 // TODO: 实现多边形填充
} }
Ptr<FontAtlas> VulkanRenderer::createFontAtlas(const std::string &filepath, int fontSize, Ptr<FontAtlas> VulkanRenderer::createFontAtlas(const std::string &filepath,
bool useSDF) { int fontSize, bool useSDF) {
// TODO: 实现字体图集创建 // TODO: 实现字体图集创建
return nullptr; return nullptr;
} }
void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text, void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text,
const Vec2 &position, const Color &color) { const Vec2 &position, const Color &color) {
// TODO: 实现文本绘制 // TODO: 实现文本绘制
} }
void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text, float x, void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text,
float y, const Color &color) { float x, float y, const Color &color) {
// TODO: 实现文本绘制 // TODO: 实现文本绘制
} }
void VulkanRenderer::resetStats() { void VulkanRenderer::resetStats() { stats_ = Stats{}; }
stats_ = Stats{};
}
} // namespace extra2d } // namespace extra2d

View File

@ -1,5 +1,7 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/batch/sprite_batch.h> #include <extra2d/graphics/batch/sprite_batch.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
@ -10,196 +12,195 @@ namespace extra2d {
// TrigLookup 实现 - 三角函数查表 // TrigLookup 实现 - 三角函数查表
// ============================================================================ // ============================================================================
TrigLookup::TrigLookup() { TrigLookup::TrigLookup() {
constexpr float PI = 3.14159265359f; constexpr float PI = 3.14159265359f;
constexpr float DEG2RAD = PI / 180.0f; constexpr float DEG2RAD = PI / 180.0f;
for (int i = 0; i < TABLE_SIZE; ++i) { for (int i = 0; i < TABLE_SIZE; ++i) {
float angle = static_cast<float>(i) * (360.0f / TABLE_SIZE) * DEG2RAD; float angle = static_cast<float>(i) * (360.0f / TABLE_SIZE) * DEG2RAD;
sinTable_[i] = std::sin(angle); sinTable_[i] = std::sin(angle);
cosTable_[i] = std::cos(angle); cosTable_[i] = std::cos(angle);
} }
} }
float TrigLookup::sin(int angle) const { float TrigLookup::sin(int angle) const {
// 规范化角度到 0-360 // 规范化角度到 0-360
angle = ((angle % 360) + 360) % 360; angle = ((angle % 360) + 360) % 360;
return sinTable_[angle * 4]; return sinTable_[angle * 4];
} }
float TrigLookup::cos(int angle) const { float TrigLookup::cos(int angle) const {
// 规范化角度到 0-360 // 规范化角度到 0-360
angle = ((angle % 360) + 360) % 360; angle = ((angle % 360) + 360) % 360;
return cosTable_[angle * 4]; return cosTable_[angle * 4];
} }
float TrigLookup::sinRad(float rad) const { float TrigLookup::sinRad(float rad) const {
constexpr float RAD2DEG = 180.0f / 3.14159265359f; constexpr float RAD2DEG = 180.0f / 3.14159265359f;
int angle = static_cast<int>(rad * RAD2DEG); int angle = static_cast<int>(rad * RAD2DEG);
return sin(angle); return sin(angle);
} }
float TrigLookup::cosRad(float rad) const { float TrigLookup::cosRad(float rad) const {
constexpr float RAD2DEG = 180.0f / 3.14159265359f; constexpr float RAD2DEG = 180.0f / 3.14159265359f;
int angle = static_cast<int>(rad * RAD2DEG); int angle = static_cast<int>(rad * RAD2DEG);
return cos(angle); return cos(angle);
} }
// ============================================================================ // ============================================================================
// SpriteBatch 实现 // SpriteBatch 实现
// ============================================================================ // ============================================================================
SpriteBatch::SpriteBatch() SpriteBatch::SpriteBatch() : spriteCount_(0), vpDirty_(true) {
: spriteCount_(0) // 预分配顶点缓冲区
, vpDirty_(true) { vertices_.reserve(MAX_VERTICES);
// 预分配顶点缓冲区 indices_.reserve(MAX_INDICES);
vertices_.reserve(MAX_VERTICES);
indices_.reserve(MAX_INDICES);
// 生成静态索引缓冲区 // 生成静态索引缓冲区
generateIndices(); generateIndices();
} }
void SpriteBatch::generateIndices() { void SpriteBatch::generateIndices() {
indices_.clear(); indices_.clear();
for (size_t i = 0; i < MAX_SPRITES; ++i) { for (size_t i = 0; i < MAX_SPRITES; ++i) {
uint16_t base = static_cast<uint16_t>(i * 4); uint16_t base = static_cast<uint16_t>(i * 4);
// 两个三角形: (0,1,2) 和 (0,2,3) // 两个三角形: (0,1,2) 和 (0,2,3)
indices_.push_back(base + 0); indices_.push_back(base + 0);
indices_.push_back(base + 1); indices_.push_back(base + 1);
indices_.push_back(base + 2); indices_.push_back(base + 2);
indices_.push_back(base + 0); indices_.push_back(base + 0);
indices_.push_back(base + 2); indices_.push_back(base + 2);
indices_.push_back(base + 3); indices_.push_back(base + 3);
} }
} }
void SpriteBatch::begin(const glm::mat4& viewProjection) { void SpriteBatch::begin(const glm::mat4 &viewProjection) {
viewProjection_ = viewProjection; viewProjection_ = viewProjection;
vpDirty_ = true; vpDirty_ = true;
spriteCount_ = 0; spriteCount_ = 0;
vertices_.clear(); vertices_.clear();
} }
void SpriteBatch::end() { void SpriteBatch::end() {
// 批次结束,数据已准备好供后端使用 // 批次结束,数据已准备好供后端使用
} }
void SpriteBatch::draw(const SpriteData& sprite) { void SpriteBatch::draw(const SpriteData &sprite) {
if (spriteCount_ >= MAX_SPRITES) { if (spriteCount_ >= MAX_SPRITES) {
// 缓冲区已满,需要刷新 // 缓冲区已满,需要刷新
flush(); flush();
}
generateVertices(sprite, spriteCount_ * VERTICES_PER_SPRITE);
spriteCount_++;
}
void SpriteBatch::drawBatch(const std::vector<SpriteData> &sprites) {
size_t index = 0;
while (index < sprites.size()) {
// 计算剩余空间
size_t remainingSpace = MAX_SPRITES - spriteCount_;
size_t batchSize = std::min(remainingSpace, sprites.size() - index);
// 批量生成顶点
for (size_t i = 0; i < batchSize; ++i) {
generateVertices(sprites[index + i], spriteCount_ * VERTICES_PER_SPRITE);
spriteCount_++;
} }
generateVertices(sprite, spriteCount_ * VERTICES_PER_SPRITE); index += batchSize;
spriteCount_++;
}
void SpriteBatch::drawBatch(const std::vector<SpriteData>& sprites) { // 如果缓冲区已满,需要刷新(由后端处理)
size_t index = 0; if (spriteCount_ >= MAX_SPRITES && index < sprites.size()) {
while (index < sprites.size()) { // 通知后端刷新,然后继续
// 计算剩余空间 // 注意:这里只是准备数据,实际 GPU 提交由后端决定
size_t remainingSpace = MAX_SPRITES - spriteCount_; break;
size_t batchSize = std::min(remainingSpace, sprites.size() - index);
// 批量生成顶点
for (size_t i = 0; i < batchSize; ++i) {
generateVertices(sprites[index + i], spriteCount_ * VERTICES_PER_SPRITE);
spriteCount_++;
}
index += batchSize;
// 如果缓冲区已满,需要刷新(由后端处理)
if (spriteCount_ >= MAX_SPRITES && index < sprites.size()) {
// 通知后端刷新,然后继续
// 注意:这里只是准备数据,实际 GPU 提交由后端决定
break;
}
} }
}
} }
void SpriteBatch::drawImmediate(const SpriteData& sprite) { void SpriteBatch::drawImmediate(const SpriteData &sprite) {
// 立即绘制模式:清空当前批次,只绘制这一个精灵 // 立即绘制模式:清空当前批次,只绘制这一个精灵
clear(); clear();
draw(sprite); draw(sprite);
} }
void SpriteBatch::generateVertices(const SpriteData& sprite, size_t vertexOffset) { void SpriteBatch::generateVertices(const SpriteData &sprite,
// 确保顶点缓冲区足够 size_t vertexOffset) {
if (vertices_.size() < vertexOffset + VERTICES_PER_SPRITE) { // 确保顶点缓冲区足够
vertices_.resize(vertexOffset + VERTICES_PER_SPRITE); if (vertices_.size() < vertexOffset + VERTICES_PER_SPRITE) {
} vertices_.resize(vertexOffset + VERTICES_PER_SPRITE);
}
// 计算旋转(使用查表) // 计算旋转(使用查表)
float c = 1.0f; float c = 1.0f;
float s = 0.0f; float s = 0.0f;
if (sprite.rotation != 0.0f) {
c = trigLookup_.cosRad(sprite.rotation);
s = trigLookup_.sinRad(sprite.rotation);
}
// 计算精灵的四个角(相对于中心点)
float halfWidth = sprite.size.x * 0.5f;
float halfHeight = sprite.size.y * 0.5f;
// 考虑 pivot 偏移
float pivotOffsetX = (sprite.pivot.x - 0.5f) * sprite.size.x;
float pivotOffsetY = (sprite.pivot.y - 0.5f) * sprite.size.y;
// 四个角的本地坐标
Vec2 localCorners[4] = {
Vec2(-halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 左下
Vec2(halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 右下
Vec2(halfWidth - pivotOffsetX, halfHeight - pivotOffsetY), // 右上
Vec2(-halfWidth - pivotOffsetX, halfHeight - pivotOffsetY) // 左上
};
// 应用旋转和平移
Vec2 worldPos = sprite.position;
for (int i = 0; i < 4; ++i) {
Vec2 rotated;
if (sprite.rotation != 0.0f) { if (sprite.rotation != 0.0f) {
c = trigLookup_.cosRad(sprite.rotation); rotated.x = localCorners[i].x * c - localCorners[i].y * s;
s = trigLookup_.sinRad(sprite.rotation); rotated.y = localCorners[i].x * s + localCorners[i].y * c;
} else {
rotated = localCorners[i];
} }
// 计算精灵的四个角(相对于中心点) vertices_[vertexOffset + i].position = worldPos + rotated;
float halfWidth = sprite.size.x * 0.5f; }
float halfHeight = sprite.size.y * 0.5f;
// 考虑 pivot 偏移 // 设置纹理坐标
float pivotOffsetX = (sprite.pivot.x - 0.5f) * sprite.size.x; // uvRect.origin = (u0, v0) - 左下
float pivotOffsetY = (sprite.pivot.y - 0.5f) * sprite.size.y; // uvRect.size = (width, height) - 从左上到右下的尺寸
float u0 = sprite.uvRect.origin.x;
float v0 = sprite.uvRect.origin.y;
float u1 = u0 + sprite.uvRect.size.width;
float v1 = v0 + sprite.uvRect.size.height;
// 四个角的本地坐标 // 顶点顺序: 左下, 右下, 右上, 左上
Vec2 localCorners[4] = { // 注意: 在 gl_font_atlas 中 v0 > v1 (因为翻转了V坐标)
Vec2(-halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 左下 // 所以 v0 对应底部v1 对应顶部
Vec2( halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 右下 vertices_[vertexOffset + 0].texCoord = Vec2(u0, v0); // 左
Vec2( halfWidth - pivotOffsetX, halfHeight - pivotOffsetY), // 右上 vertices_[vertexOffset + 1].texCoord = Vec2(u1, v0); // 右下
Vec2(-halfWidth - pivotOffsetX, halfHeight - pivotOffsetY) // 左上 vertices_[vertexOffset + 2].texCoord = Vec2(u1, v1); // 右
}; vertices_[vertexOffset + 3].texCoord = Vec2(u0, v1); // 左上
// 应用旋转和平移 // 设置颜色
Vec2 worldPos = sprite.position; for (int i = 0; i < 4; ++i) {
vertices_[vertexOffset + i].color = sprite.color;
for (int i = 0; i < 4; ++i) { }
Vec2 rotated;
if (sprite.rotation != 0.0f) {
rotated.x = localCorners[i].x * c - localCorners[i].y * s;
rotated.y = localCorners[i].x * s + localCorners[i].y * c;
} else {
rotated = localCorners[i];
}
vertices_[vertexOffset + i].position = worldPos + rotated;
}
// 设置纹理坐标
// uvRect.origin = (u0, v0) - 左下
// uvRect.size = (width, height) - 从左上到右下的尺寸
float u0 = sprite.uvRect.origin.x;
float v0 = sprite.uvRect.origin.y;
float u1 = u0 + sprite.uvRect.size.width;
float v1 = v0 + sprite.uvRect.size.height;
// 顶点顺序: 左下, 右下, 右上, 左上
// 注意: 在 gl_font_atlas 中 v0 > v1 (因为翻转了V坐标)
// 所以 v0 对应底部v1 对应顶部
vertices_[vertexOffset + 0].texCoord = Vec2(u0, v0); // 左下
vertices_[vertexOffset + 1].texCoord = Vec2(u1, v0); // 右下
vertices_[vertexOffset + 2].texCoord = Vec2(u1, v1); // 右上
vertices_[vertexOffset + 3].texCoord = Vec2(u0, v1); // 左上
// 设置颜色
for (int i = 0; i < 4; ++i) {
vertices_[vertexOffset + i].color = sprite.color;
}
} }
void SpriteBatch::flush() { void SpriteBatch::flush() {
// 标记需要刷新 - 实际刷新由后端处理 // 标记需要刷新 - 实际刷新由后端处理
// 这里只是重置计数器,让后端知道需要提交当前批次 // 这里只是重置计数器,让后端知道需要提交当前批次
spriteCount_ = 0; spriteCount_ = 0;
vertices_.clear(); vertices_.clear();
} }
void SpriteBatch::clear() { void SpriteBatch::clear() {
spriteCount_ = 0; spriteCount_ = 0;
vertices_.clear(); vertices_.clear();
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,12 +1,12 @@
#include <extra2d/graphics/core/render_module.h> #include <extra2d/app/application.h>
#include <extra2d/graphics/backends/backend_factory.h>
#include <extra2d/graphics/backends/opengl/gl_shader.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/core/registry.h> #include <extra2d/core/registry.h>
#include <extra2d/core/service_locator.h> #include <extra2d/core/service_locator.h>
#include <extra2d/app/application.h> #include <extra2d/graphics/backends/backend_factory.h>
#include <extra2d/graphics/backends/opengl/gl_shader.h>
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/platform/window_module.h> #include <extra2d/platform/window_module.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
#include <filesystem> #include <filesystem>
namespace extra2d { namespace extra2d {
@ -19,91 +19,97 @@ void initOpenGLBackend();
#ifdef E2D_BACKEND_VULKAN #ifdef E2D_BACKEND_VULKAN
void initVulkanBackend(); void initVulkanBackend();
#endif #endif
} } // namespace graphics
RenderModule::RenderModule(std::function<void(RenderCfg&)> configFn) { RenderModule::RenderModule(std::function<void(RenderCfg &)> configFn) {
configFn(cfg_); configFn(cfg_);
} }
RenderModule::~RenderModule() { RenderModule::~RenderModule() {
if (initialized_) { if (initialized_) {
shutdown(); shutdown();
} }
} }
static std::string getExecutableDir() { static std::string getExecutableDir() {
try { try {
auto currentPath = std::filesystem::current_path(); auto currentPath = std::filesystem::current_path();
return currentPath.string() + "/"; return currentPath.string() + "/";
} catch (...) { } catch (...) {
return "./"; return "./";
} }
} }
bool RenderModule::init() { bool RenderModule::init() {
if (initialized_) return true; if (initialized_)
return true;
auto* winMod = Registry::instance().get<WindowModule>(); auto *winMod = Registry::instance().get<WindowModule>();
if (!winMod || !winMod->win()) { if (!winMod || !winMod->win()) {
E2D_LOG_ERROR("WindowModule not available"); E2D_LOG_ERROR("WindowModule not available");
return false; return false;
} }
// 初始化图形后端(注册到工厂) // 初始化图形后端(注册到工厂)
#ifdef E2D_BACKEND_OPENGL #ifdef E2D_BACKEND_OPENGL
graphics::initOpenGLBackend(); graphics::initOpenGLBackend();
#endif #endif
#ifdef E2D_BACKEND_VULKAN #ifdef E2D_BACKEND_VULKAN
graphics::initVulkanBackend(); graphics::initVulkanBackend();
#endif #endif
if (!ShaderManager::getInstance().isInitialized()) { if (!ShaderManager::getInstance().isInitialized()) {
auto factory = makeShared<GLShaderFactory>(); auto factory = makeShared<GLShaderFactory>();
std::string shaderDir = getExecutableDir() + "shaders/"; std::string shaderDir = getExecutableDir() + "shaders/";
std::string cacheDir = getExecutableDir() + "shader_cache/"; std::string cacheDir = getExecutableDir() + "shader_cache/";
if (!ShaderManager::getInstance().init(shaderDir, cacheDir, factory)) { if (!ShaderManager::getInstance().init(shaderDir, cacheDir, factory)) {
E2D_LOG_WARN("Failed to initialize ShaderManager with dir: {}", shaderDir); E2D_LOG_WARN("Failed to initialize ShaderManager with dir: {}",
} shaderDir);
} }
}
std::string windowBackend = winMod->getWindowBackend(); std::string windowBackend = winMod->getWindowBackend();
if (cfg_.backend.empty()) { if (cfg_.backend.empty()) {
E2D_LOG_INFO("No graphics backend specified, auto-selecting for window backend: {}", windowBackend); E2D_LOG_INFO(
renderer_ = graphics::BackendFactory::createBackendForWindow(windowBackend); "No graphics backend specified, auto-selecting for window backend: {}",
} else { windowBackend);
if (!graphics::BackendFactory::isCompatible(cfg_.backend, windowBackend)) { renderer_ = graphics::BackendFactory::createBackendForWindow(windowBackend);
E2D_LOG_WARN("Graphics backend '{}' is not compatible with window backend '{}'", } else {
cfg_.backend, windowBackend); if (!graphics::BackendFactory::isCompatible(cfg_.backend, windowBackend)) {
} E2D_LOG_WARN(
renderer_ = graphics::BackendFactory::createBackend(cfg_.backend); "Graphics backend '{}' is not compatible with window backend '{}'",
cfg_.backend, windowBackend);
} }
renderer_ = graphics::BackendFactory::createBackend(cfg_.backend);
}
if (!renderer_) { if (!renderer_) {
E2D_LOG_ERROR("Failed to create render backend"); E2D_LOG_ERROR("Failed to create render backend");
return false; return false;
} }
if (!renderer_->init(winMod->win())) { if (!renderer_->init(winMod->win())) {
E2D_LOG_ERROR("Failed to initialize render backend"); E2D_LOG_ERROR("Failed to initialize render backend");
renderer_.reset(); renderer_.reset();
return false; return false;
} }
E2D_LOG_INFO("Render module initialized successfully"); E2D_LOG_INFO("Render module initialized successfully");
initialized_ = true; initialized_ = true;
return true; return true;
} }
void RenderModule::shutdown() { void RenderModule::shutdown() {
if (!initialized_) return; if (!initialized_)
return;
if (renderer_) { if (renderer_) {
renderer_->shutdown(); renderer_->shutdown();
renderer_.reset(); renderer_.reset();
} }
initialized_ = false; initialized_ = false;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,7 +1,9 @@
#include <glad/glad.h> #include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/opengl/gl_texture.h> #include <extra2d/graphics/backends/opengl/gl_texture.h>
#include <extra2d/graphics/core/render_target.h> #include <extra2d/graphics/core/render_target.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
#include <glad/glad.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb/stb_image_write.h> #include <stb/stb_image_write.h>

View File

@ -1,6 +1,8 @@
#include <algorithm> #include <algorithm>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/memory/vram_manager.h> #include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {

View File

@ -1,10 +1,12 @@
#include <extra2d/graphics/shader/shader_cache.h>
#include <extra2d/utils/logger.h>
#include <chrono> #include <chrono>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/shader/shader_cache.h>
#include <extra2d/services/logger_service.h>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
namespace extra2d { namespace extra2d {
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -13,9 +15,9 @@ namespace fs = std::filesystem;
* @brief * @brief
* @return * @return
*/ */
ShaderCache& ShaderCache::getInstance() { ShaderCache &ShaderCache::getInstance() {
static ShaderCache instance; static ShaderCache instance;
return instance; return instance;
} }
/** /**
@ -23,35 +25,35 @@ ShaderCache& ShaderCache::getInstance() {
* @param cacheDir * @param cacheDir
* @return truefalse * @return truefalse
*/ */
bool ShaderCache::init(const std::string& cacheDir) { bool ShaderCache::init(const std::string &cacheDir) {
cacheDir_ = cacheDir; cacheDir_ = cacheDir;
if (!ensureCacheDirectory()) { if (!ensureCacheDirectory()) {
E2D_LOG_ERROR("Failed to create cache directory: {}", cacheDir); E2D_LOG_ERROR("Failed to create cache directory: {}", cacheDir);
return false; return false;
} }
if (!loadCacheIndex()) { if (!loadCacheIndex()) {
E2D_LOG_WARN("Failed to load cache index, starting fresh"); E2D_LOG_WARN("Failed to load cache index, starting fresh");
} }
initialized_ = true; initialized_ = true;
E2D_LOG_INFO("Shader cache initialized at: {}", cacheDir); E2D_LOG_INFO("Shader cache initialized at: {}", cacheDir);
return true; return true;
} }
/** /**
* @brief * @brief
*/ */
void ShaderCache::shutdown() { void ShaderCache::shutdown() {
if (!initialized_) { if (!initialized_) {
return; return;
} }
saveCacheIndex(); saveCacheIndex();
cacheMap_.clear(); cacheMap_.clear();
initialized_ = false; initialized_ = false;
E2D_LOG_INFO("Shader cache shutdown"); E2D_LOG_INFO("Shader cache shutdown");
} }
/** /**
@ -60,13 +62,14 @@ void ShaderCache::shutdown() {
* @param sourceHash * @param sourceHash
* @return truefalse * @return truefalse
*/ */
bool ShaderCache::hasValidCache(const std::string& name, const std::string& sourceHash) { bool ShaderCache::hasValidCache(const std::string &name,
auto it = cacheMap_.find(name); const std::string &sourceHash) {
if (it == cacheMap_.end()) { auto it = cacheMap_.find(name);
return false; if (it == cacheMap_.end()) {
} return false;
}
return it->second.sourceHash == sourceHash; return it->second.sourceHash == sourceHash;
} }
/** /**
@ -74,30 +77,30 @@ bool ShaderCache::hasValidCache(const std::string& name, const std::string& sour
* @param name Shader名称 * @param name Shader名称
* @return nullptr * @return nullptr
*/ */
Ptr<ShaderCacheEntry> ShaderCache::loadCache(const std::string& name) { Ptr<ShaderCacheEntry> ShaderCache::loadCache(const std::string &name) {
auto it = cacheMap_.find(name); auto it = cacheMap_.find(name);
if (it == cacheMap_.end()) { if (it == cacheMap_.end()) {
return nullptr; return nullptr;
} }
std::string cachePath = getCachePath(name); std::string cachePath = getCachePath(name);
std::ifstream file(cachePath, std::ios::binary); std::ifstream file(cachePath, std::ios::binary);
if (!file.is_open()) { if (!file.is_open()) {
E2D_LOG_WARN("Failed to open cache file: {}", cachePath); E2D_LOG_WARN("Failed to open cache file: {}", cachePath);
return nullptr; return nullptr;
} }
auto entry = std::make_shared<ShaderCacheEntry>(it->second); auto entry = std::make_shared<ShaderCacheEntry>(it->second);
entry->binary.clear(); entry->binary.clear();
file.seekg(0, std::ios::end); file.seekg(0, std::ios::end);
size_t fileSize = static_cast<size_t>(file.tellg()); size_t fileSize = static_cast<size_t>(file.tellg());
file.seekg(0, std::ios::beg); file.seekg(0, std::ios::beg);
entry->binary.resize(fileSize); entry->binary.resize(fileSize);
file.read(reinterpret_cast<char*>(entry->binary.data()), fileSize); file.read(reinterpret_cast<char *>(entry->binary.data()), fileSize);
return entry; return entry;
} }
/** /**
@ -105,68 +108,72 @@ Ptr<ShaderCacheEntry> ShaderCache::loadCache(const std::string& name) {
* @param entry * @param entry
* @return truefalse * @return truefalse
*/ */
bool ShaderCache::saveCache(const ShaderCacheEntry& entry) { bool ShaderCache::saveCache(const ShaderCacheEntry &entry) {
if (!initialized_) { if (!initialized_) {
E2D_LOG_WARN("ShaderCache not initialized, cannot save cache"); E2D_LOG_WARN("ShaderCache not initialized, cannot save cache");
return false; return false;
} }
if (entry.binary.empty()) { if (entry.binary.empty()) {
E2D_LOG_WARN("Shader binary is empty, skipping cache save for: {}", entry.name); E2D_LOG_WARN("Shader binary is empty, skipping cache save for: {}",
return false; entry.name);
} return false;
}
std::string cachePath = getCachePath(entry.name); std::string cachePath = getCachePath(entry.name);
E2D_LOG_DEBUG("Saving shader cache to: {} ({} bytes)", cachePath, entry.binary.size()); E2D_LOG_DEBUG("Saving shader cache to: {} ({} bytes)", cachePath,
entry.binary.size());
std::ofstream file(cachePath, std::ios::binary); std::ofstream file(cachePath, std::ios::binary);
if (!file.is_open()) { if (!file.is_open()) {
E2D_LOG_ERROR("Failed to create cache file: {}", cachePath); E2D_LOG_ERROR("Failed to create cache file: {}", cachePath);
return false; return false;
} }
file.write(reinterpret_cast<const char*>(entry.binary.data()), entry.binary.size()); file.write(reinterpret_cast<const char *>(entry.binary.data()),
file.close(); entry.binary.size());
file.close();
cacheMap_[entry.name] = entry; cacheMap_[entry.name] = entry;
saveCacheIndex(); saveCacheIndex();
E2D_LOG_INFO("Shader cache saved: {} ({} bytes)", entry.name, entry.binary.size()); E2D_LOG_INFO("Shader cache saved: {} ({} bytes)", entry.name,
return true; entry.binary.size());
return true;
} }
/** /**
* @brief 使 * @brief 使
* @param name Shader名称 * @param name Shader名称
*/ */
void ShaderCache::invalidate(const std::string& name) { void ShaderCache::invalidate(const std::string &name) {
auto it = cacheMap_.find(name); auto it = cacheMap_.find(name);
if (it == cacheMap_.end()) { if (it == cacheMap_.end()) {
return; return;
} }
std::string cachePath = getCachePath(name); std::string cachePath = getCachePath(name);
fs::remove(cachePath); fs::remove(cachePath);
cacheMap_.erase(it); cacheMap_.erase(it);
saveCacheIndex(); saveCacheIndex();
E2D_LOG_DEBUG("Shader cache invalidated: {}", name); E2D_LOG_DEBUG("Shader cache invalidated: {}", name);
} }
/** /**
* @brief * @brief
*/ */
void ShaderCache::clearAll() { void ShaderCache::clearAll() {
for (const auto& pair : cacheMap_) { for (const auto &pair : cacheMap_) {
std::string cachePath = getCachePath(pair.first); std::string cachePath = getCachePath(pair.first);
fs::remove(cachePath); fs::remove(cachePath);
} }
cacheMap_.clear(); cacheMap_.clear();
saveCacheIndex(); saveCacheIndex();
E2D_LOG_INFO("All shader caches cleared"); E2D_LOG_INFO("All shader caches cleared");
} }
/** /**
@ -175,18 +182,18 @@ void ShaderCache::clearAll() {
* @param fragSource * @param fragSource
* @return * @return
*/ */
std::string ShaderCache::computeHash(const std::string& vertSource, std::string ShaderCache::computeHash(const std::string &vertSource,
const std::string& fragSource) { const std::string &fragSource) {
std::string combined = vertSource + fragSource; std::string combined = vertSource + fragSource;
uint32_t hash = 5381; uint32_t hash = 5381;
for (char c : combined) { for (char c : combined) {
hash = ((hash << 5) + hash) + static_cast<uint32_t>(c); hash = ((hash << 5) + hash) + static_cast<uint32_t>(c);
} }
std::stringstream ss; std::stringstream ss;
ss << std::hex << hash; ss << std::hex << hash;
return ss.str(); return ss.str();
} }
/** /**
@ -194,43 +201,43 @@ std::string ShaderCache::computeHash(const std::string& vertSource,
* @return truefalse * @return truefalse
*/ */
bool ShaderCache::loadCacheIndex() { bool ShaderCache::loadCacheIndex() {
std::string indexPath = cacheDir_ + "/.cache_index"; std::string indexPath = cacheDir_ + "/.cache_index";
if (!fs::exists(indexPath)) {
return true;
}
std::ifstream file(indexPath);
if (!file.is_open()) {
return false;
}
std::string line;
while (std::getline(file, line)) {
if (line.empty() || line[0] == '#') {
continue;
}
size_t pos = line.find('=');
if (pos == std::string::npos) {
continue;
}
std::string name = line.substr(0, pos);
std::string hash = line.substr(pos + 1);
std::string cachePath = getCachePath(name);
if (fs::exists(cachePath)) {
ShaderCacheEntry entry;
entry.name = name;
entry.sourceHash = hash;
entry.compileTime = static_cast<uint64_t>(
std::chrono::system_clock::now().time_since_epoch().count());
cacheMap_[name] = entry;
}
}
if (!fs::exists(indexPath)) {
return true; return true;
}
std::ifstream file(indexPath);
if (!file.is_open()) {
return false;
}
std::string line;
while (std::getline(file, line)) {
if (line.empty() || line[0] == '#') {
continue;
}
size_t pos = line.find('=');
if (pos == std::string::npos) {
continue;
}
std::string name = line.substr(0, pos);
std::string hash = line.substr(pos + 1);
std::string cachePath = getCachePath(name);
if (fs::exists(cachePath)) {
ShaderCacheEntry entry;
entry.name = name;
entry.sourceHash = hash;
entry.compileTime = static_cast<uint64_t>(
std::chrono::system_clock::now().time_since_epoch().count());
cacheMap_[name] = entry;
}
}
return true;
} }
/** /**
@ -238,21 +245,21 @@ bool ShaderCache::loadCacheIndex() {
* @return truefalse * @return truefalse
*/ */
bool ShaderCache::saveCacheIndex() { bool ShaderCache::saveCacheIndex() {
std::string indexPath = cacheDir_ + "/.cache_index"; std::string indexPath = cacheDir_ + "/.cache_index";
std::ofstream file(indexPath); std::ofstream file(indexPath);
if (!file.is_open()) { if (!file.is_open()) {
return false; return false;
} }
file << "# Extra2D Shader Cache Index\n"; file << "# Extra2D Shader Cache Index\n";
file << "# Format: name=hash\n"; file << "# Format: name=hash\n";
for (const auto& pair : cacheMap_) { for (const auto &pair : cacheMap_) {
file << pair.first << "=" << pair.second.sourceHash << "\n"; file << pair.first << "=" << pair.second.sourceHash << "\n";
} }
return true; return true;
} }
/** /**
@ -260,8 +267,8 @@ bool ShaderCache::saveCacheIndex() {
* @param name Shader名称 * @param name Shader名称
* @return * @return
*/ */
std::string ShaderCache::getCachePath(const std::string& name) const { std::string ShaderCache::getCachePath(const std::string &name) const {
return cacheDir_ + "/" + name + ".cache"; return cacheDir_ + "/" + name + ".cache";
} }
/** /**
@ -269,18 +276,18 @@ std::string ShaderCache::getCachePath(const std::string& name) const {
* @return truefalse * @return truefalse
*/ */
bool ShaderCache::ensureCacheDirectory() { bool ShaderCache::ensureCacheDirectory() {
if (cacheDir_.empty()) { if (cacheDir_.empty()) {
return false; return false;
} }
std::error_code ec; std::error_code ec;
if (!fs::exists(cacheDir_)) { if (!fs::exists(cacheDir_)) {
if (!fs::create_directories(cacheDir_, ec)) { if (!fs::create_directories(cacheDir_, ec)) {
return false; return false;
}
} }
}
return true; return true;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,8 +1,10 @@
#include <extra2d/graphics/shader/shader_hot_reloader.h>
#include <extra2d/utils/logger.h>
#include <chrono> #include <chrono>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/shader/shader_hot_reloader.h>
#include <extra2d/services/logger_service.h>
#include <filesystem> #include <filesystem>
namespace extra2d { namespace extra2d {
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -11,9 +13,9 @@ namespace fs = std::filesystem;
* @brief * @brief
* @return * @return
*/ */
ShaderHotReloader& ShaderHotReloader::getInstance() { ShaderHotReloader &ShaderHotReloader::getInstance() {
static ShaderHotReloader instance; static ShaderHotReloader instance;
return instance; return instance;
} }
/** /**
@ -21,38 +23,38 @@ ShaderHotReloader& ShaderHotReloader::getInstance() {
* @return truefalse * @return truefalse
*/ */
bool ShaderHotReloader::init() { bool ShaderHotReloader::init() {
if (initialized_) { if (initialized_) {
return true; return true;
} }
#ifdef _WIN32 #ifdef _WIN32
buffer_.resize(4096); buffer_.resize(4096);
#endif #endif
initialized_ = true; initialized_ = true;
E2D_LOG_INFO("Shader hot reloader initialized"); E2D_LOG_INFO("Shader hot reloader initialized");
return true; return true;
} }
/** /**
* @brief * @brief
*/ */
void ShaderHotReloader::shutdown() { void ShaderHotReloader::shutdown() {
if (!initialized_) { if (!initialized_) {
return; return;
} }
#ifdef _WIN32 #ifdef _WIN32
if (watchHandle_ != nullptr) { if (watchHandle_ != nullptr) {
FindCloseChangeNotification(watchHandle_); FindCloseChangeNotification(watchHandle_);
watchHandle_ = nullptr; watchHandle_ = nullptr;
} }
#endif #endif
watchMap_.clear(); watchMap_.clear();
initialized_ = false; initialized_ = false;
enabled_ = false; enabled_ = false;
E2D_LOG_INFO("Shader hot reloader shutdown"); E2D_LOG_INFO("Shader hot reloader shutdown");
} }
/** /**
@ -61,47 +63,47 @@ void ShaderHotReloader::shutdown() {
* @param filePaths * @param filePaths
* @param callback * @param callback
*/ */
void ShaderHotReloader::watch(const std::string& shaderName, void ShaderHotReloader::watch(const std::string &shaderName,
const std::vector<std::string>& filePaths, const std::vector<std::string> &filePaths,
FileChangeCallback callback) { FileChangeCallback callback) {
if (!initialized_) { if (!initialized_) {
E2D_LOG_WARN("Hot reloader not initialized"); E2D_LOG_WARN("Hot reloader not initialized");
return; return;
} }
WatchInfo info; WatchInfo info;
info.filePaths = filePaths; info.filePaths = filePaths;
info.callback = callback; info.callback = callback;
for (const auto& path : filePaths) { for (const auto &path : filePaths) {
info.modifiedTimes[path] = getFileModifiedTime(path); info.modifiedTimes[path] = getFileModifiedTime(path);
} }
watchMap_[shaderName] = std::move(info); watchMap_[shaderName] = std::move(info);
E2D_LOG_DEBUG("Watching shader: {} ({} files)", shaderName, filePaths.size()); E2D_LOG_DEBUG("Watching shader: {} ({} files)", shaderName, filePaths.size());
} }
/** /**
* @brief * @brief
* @param shaderName Shader名称 * @param shaderName Shader名称
*/ */
void ShaderHotReloader::unwatch(const std::string& shaderName) { void ShaderHotReloader::unwatch(const std::string &shaderName) {
auto it = watchMap_.find(shaderName); auto it = watchMap_.find(shaderName);
if (it != watchMap_.end()) { if (it != watchMap_.end()) {
watchMap_.erase(it); watchMap_.erase(it);
E2D_LOG_DEBUG("Stopped watching shader: {}", shaderName); E2D_LOG_DEBUG("Stopped watching shader: {}", shaderName);
} }
} }
/** /**
* @brief * @brief
*/ */
void ShaderHotReloader::update() { void ShaderHotReloader::update() {
if (!initialized_ || !enabled_) { if (!initialized_ || !enabled_) {
return; return;
} }
pollChanges(); pollChanges();
} }
/** /**
@ -109,40 +111,41 @@ void ShaderHotReloader::update() {
* @param enabled * @param enabled
*/ */
void ShaderHotReloader::setEnabled(bool enabled) { void ShaderHotReloader::setEnabled(bool enabled) {
enabled_ = enabled; enabled_ = enabled;
E2D_LOG_DEBUG("Hot reload {}", enabled ? "enabled" : "disabled"); E2D_LOG_DEBUG("Hot reload {}", enabled ? "enabled" : "disabled");
} }
/** /**
* @brief * @brief
*/ */
void ShaderHotReloader::pollChanges() { void ShaderHotReloader::pollChanges() {
auto now = static_cast<uint64_t>( auto now = static_cast<uint64_t>(
std::chrono::system_clock::now().time_since_epoch().count()); std::chrono::system_clock::now().time_since_epoch().count());
for (auto& pair : watchMap_) { for (auto &pair : watchMap_) {
WatchInfo& info = pair.second; WatchInfo &info = pair.second;
for (const auto& filePath : info.filePaths) { for (const auto &filePath : info.filePaths) {
uint64_t currentModTime = getFileModifiedTime(filePath); uint64_t currentModTime = getFileModifiedTime(filePath);
uint64_t lastModTime = info.modifiedTimes[filePath]; uint64_t lastModTime = info.modifiedTimes[filePath];
if (currentModTime != 0 && lastModTime != 0 && currentModTime != lastModTime) { if (currentModTime != 0 && lastModTime != 0 &&
info.modifiedTimes[filePath] = currentModTime; currentModTime != lastModTime) {
info.modifiedTimes[filePath] = currentModTime;
FileChangeEvent event; FileChangeEvent event;
event.filepath = filePath; event.filepath = filePath;
event.type = FileChangeEvent::Type::Modified; event.type = FileChangeEvent::Type::Modified;
event.timestamp = now; event.timestamp = now;
E2D_LOG_DEBUG("Shader file changed: {}", filePath); E2D_LOG_DEBUG("Shader file changed: {}", filePath);
if (info.callback) { if (info.callback) {
info.callback(event); info.callback(event);
}
}
} }
}
} }
}
} }
/** /**
@ -150,15 +153,16 @@ void ShaderHotReloader::pollChanges() {
* @param filepath * @param filepath
* @return * @return
*/ */
uint64_t ShaderHotReloader::getFileModifiedTime(const std::string& filepath) { uint64_t ShaderHotReloader::getFileModifiedTime(const std::string &filepath) {
try { try {
auto ftime = fs::last_write_time(filepath); auto ftime = fs::last_write_time(filepath);
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>( auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now()); ftime - fs::file_time_type::clock::now() +
return static_cast<uint64_t>(sctp.time_since_epoch().count()); std::chrono::system_clock::now());
} catch (...) { return static_cast<uint64_t>(sctp.time_since_epoch().count());
return 0; } catch (...) {
} return 0;
}
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,7 +1,7 @@
#include <extra2d/graphics/shader/shader_loader.h>
#include <extra2d/utils/logger.h>
#include <algorithm> #include <algorithm>
#include <cctype> #include <extra2d/core/service_locator.h>
#include <extra2d/graphics/shader/shader_loader.h>
#include <extra2d/services/logger_service.h>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
@ -13,8 +13,7 @@ namespace fs = std::filesystem;
/** /**
* @brief Shader加载器 * @brief Shader加载器
*/ */
ShaderLoader::ShaderLoader() { ShaderLoader::ShaderLoader() {}
}
/** /**
* @brief Shader (.vert + .frag) * @brief Shader (.vert + .frag)
@ -23,51 +22,53 @@ ShaderLoader::ShaderLoader() {
* @param fragPath * @param fragPath
* @return * @return
*/ */
ShaderLoadResult ShaderLoader::loadFromSeparateFiles( ShaderLoadResult
const std::string& name, ShaderLoader::loadFromSeparateFiles(const std::string &name,
const std::string& vertPath, const std::string &vertPath,
const std::string& fragPath) { const std::string &fragPath) {
ShaderLoadResult result; ShaderLoadResult result;
if (!fileExists(vertPath)) {
result.errorMessage = "Vertex shader file not found: " + vertPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
if (!fileExists(fragPath)) {
result.errorMessage = "Fragment shader file not found: " + fragPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
std::string vertSource = readFile(vertPath);
std::string fragSource = readFile(fragPath);
if (vertSource.empty()) {
result.errorMessage = "Failed to read vertex shader file: " + vertPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
if (fragSource.empty()) {
result.errorMessage = "Failed to read fragment shader file: " + fragPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
fs::path vertDir = fs::path(vertPath).parent_path();
fs::path fragDir = fs::path(fragPath).parent_path();
vertSource = processIncludes(vertSource, vertDir.string(), result.dependencies);
fragSource = processIncludes(fragSource, fragDir.string(), result.dependencies);
result.vertSource = vertSource;
result.fragSource = fragSource;
result.success = true;
if (!fileExists(vertPath)) {
result.errorMessage = "Vertex shader file not found: " + vertPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result; return result;
}
if (!fileExists(fragPath)) {
result.errorMessage = "Fragment shader file not found: " + fragPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
std::string vertSource = readFile(vertPath);
std::string fragSource = readFile(fragPath);
if (vertSource.empty()) {
result.errorMessage = "Failed to read vertex shader file: " + vertPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
if (fragSource.empty()) {
result.errorMessage = "Failed to read fragment shader file: " + fragPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
fs::path vertDir = fs::path(vertPath).parent_path();
fs::path fragDir = fs::path(fragPath).parent_path();
vertSource =
processIncludes(vertSource, vertDir.string(), result.dependencies);
fragSource =
processIncludes(fragSource, fragDir.string(), result.dependencies);
result.vertSource = vertSource;
result.fragSource = fragSource;
result.success = true;
return result;
} }
/** /**
@ -75,40 +76,42 @@ ShaderLoadResult ShaderLoader::loadFromSeparateFiles(
* @param path Shader文件路径 * @param path Shader文件路径
* @return * @return
*/ */
ShaderLoadResult ShaderLoader::loadFromCombinedFile(const std::string& path) { ShaderLoadResult ShaderLoader::loadFromCombinedFile(const std::string &path) {
ShaderLoadResult result; ShaderLoadResult result;
if (!fileExists(path)) {
result.errorMessage = "Shader file not found: " + path;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
std::string content = readFile(path);
if (content.empty()) {
result.errorMessage = "Failed to read shader file: " + path;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
ShaderMetadata metadata;
std::string vertSource, fragSource;
if (!parseCombinedFile(content, vertSource, fragSource, metadata)) {
result.errorMessage = "Failed to parse combined shader file: " + path;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
fs::path baseDir = fs::path(path).parent_path();
vertSource = processIncludes(vertSource, baseDir.string(), result.dependencies);
fragSource = processIncludes(fragSource, baseDir.string(), result.dependencies);
result.vertSource = vertSource;
result.fragSource = fragSource;
result.success = true;
if (!fileExists(path)) {
result.errorMessage = "Shader file not found: " + path;
E2D_LOG_ERROR("{}", result.errorMessage);
return result; return result;
}
std::string content = readFile(path);
if (content.empty()) {
result.errorMessage = "Failed to read shader file: " + path;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
ShaderMetadata metadata;
std::string vertSource, fragSource;
if (!parseCombinedFile(content, vertSource, fragSource, metadata)) {
result.errorMessage = "Failed to parse combined shader file: " + path;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
fs::path baseDir = fs::path(path).parent_path();
vertSource =
processIncludes(vertSource, baseDir.string(), result.dependencies);
fragSource =
processIncludes(fragSource, baseDir.string(), result.dependencies);
result.vertSource = vertSource;
result.fragSource = fragSource;
result.success = true;
return result;
} }
/** /**
@ -117,15 +120,14 @@ ShaderLoadResult ShaderLoader::loadFromCombinedFile(const std::string& path) {
* @param fragSource * @param fragSource
* @return * @return
*/ */
ShaderLoadResult ShaderLoader::loadFromSource( ShaderLoadResult ShaderLoader::loadFromSource(const std::string &vertSource,
const std::string& vertSource, const std::string &fragSource) {
const std::string& fragSource) {
ShaderLoadResult result; ShaderLoadResult result;
result.vertSource = vertSource; result.vertSource = vertSource;
result.fragSource = fragSource; result.fragSource = fragSource;
result.success = true; result.success = true;
return result; return result;
} }
/** /**
@ -135,51 +137,52 @@ ShaderLoadResult ShaderLoader::loadFromSource(
* @param outDependencies * @param outDependencies
* @return * @return
*/ */
std::string ShaderLoader::processIncludes( std::string
const std::string& source, ShaderLoader::processIncludes(const std::string &source,
const std::string& baseDir, const std::string &baseDir,
std::vector<std::string>& outDependencies) { std::vector<std::string> &outDependencies) {
std::string result; std::string result;
std::istringstream stream(source); std::istringstream stream(source);
std::string line; std::string line;
while (std::getline(stream, line)) { while (std::getline(stream, line)) {
size_t includePos = line.find("#include"); size_t includePos = line.find("#include");
if (includePos != std::string::npos) { if (includePos != std::string::npos) {
size_t startQuote = line.find('"', includePos); size_t startQuote = line.find('"', includePos);
size_t endQuote = line.find('"', startQuote + 1); size_t endQuote = line.find('"', startQuote + 1);
if (startQuote != std::string::npos && endQuote != std::string::npos) { if (startQuote != std::string::npos && endQuote != std::string::npos) {
std::string includeName = line.substr(startQuote + 1, endQuote - startQuote - 1); std::string includeName =
std::string includePath = findIncludeFile(includeName, baseDir); line.substr(startQuote + 1, endQuote - startQuote - 1);
std::string includePath = findIncludeFile(includeName, baseDir);
if (!includePath.empty()) { if (!includePath.empty()) {
auto cacheIt = includeCache_.find(includePath); auto cacheIt = includeCache_.find(includePath);
std::string includeContent; std::string includeContent;
if (cacheIt != includeCache_.end()) { if (cacheIt != includeCache_.end()) {
includeContent = cacheIt->second; includeContent = cacheIt->second;
} else { } else {
includeContent = readFile(includePath); includeContent = readFile(includePath);
includeCache_[includePath] = includeContent; includeCache_[includePath] = includeContent;
} }
outDependencies.push_back(includePath); outDependencies.push_back(includePath);
result += includeContent; result += includeContent;
result += "\n"; result += "\n";
continue; continue;
} else { } else {
E2D_LOG_WARN("Include file not found: {}", includeName); E2D_LOG_WARN("Include file not found: {}", includeName);
}
}
} }
}
result += line;
result += "\n";
} }
return result; result += line;
result += "\n";
}
return result;
} }
/** /**
@ -188,40 +191,40 @@ std::string ShaderLoader::processIncludes(
* @param defines * @param defines
* @return * @return
*/ */
std::string ShaderLoader::applyDefines( std::string
const std::string& source, ShaderLoader::applyDefines(const std::string &source,
const std::vector<std::string>& defines) { const std::vector<std::string> &defines) {
if (defines.empty()) { if (defines.empty()) {
return source; return source;
}
std::string defineBlock;
for (const auto &def : defines) {
defineBlock += "#define " + def + "\n";
}
std::string result;
std::istringstream stream(source);
std::string line;
bool inserted = false;
while (std::getline(stream, line)) {
if (!inserted && (line.find("#version") != std::string::npos ||
line.find("precision") != std::string::npos)) {
result += line + "\n";
continue;
} }
std::string defineBlock; if (!inserted) {
for (const auto& def : defines) { result += defineBlock;
defineBlock += "#define " + def + "\n"; inserted = true;
} }
std::string result; result += line + "\n";
std::istringstream stream(source); }
std::string line;
bool inserted = false;
while (std::getline(stream, line)) { return result;
if (!inserted && (line.find("#version") != std::string::npos ||
line.find("precision") != std::string::npos)) {
result += line + "\n";
continue;
}
if (!inserted) {
result += defineBlock;
inserted = true;
}
result += line + "\n";
}
return result;
} }
/** /**
@ -229,30 +232,31 @@ std::string ShaderLoader::applyDefines(
* @param path Shader文件路径 * @param path Shader文件路径
* @return * @return
*/ */
ShaderMetadata ShaderLoader::getMetadata(const std::string& path) { ShaderMetadata ShaderLoader::getMetadata(const std::string &path) {
ShaderMetadata metadata; ShaderMetadata metadata;
if (!fileExists(path)) {
return metadata;
}
metadata.combinedPath = path;
metadata.lastModified = getFileModifiedTime(path);
fs::path p(path);
metadata.name = p.stem().string();
if (!fileExists(path)) {
return metadata; return metadata;
}
metadata.combinedPath = path;
metadata.lastModified = getFileModifiedTime(path);
fs::path p(path);
metadata.name = p.stem().string();
return metadata;
} }
/** /**
* @brief include搜索路径 * @brief include搜索路径
* @param path * @param path
*/ */
void ShaderLoader::addIncludePath(const std::string& path) { void ShaderLoader::addIncludePath(const std::string &path) {
if (std::find(includePaths_.begin(), includePaths_.end(), path) == includePaths_.end()) { if (std::find(includePaths_.begin(), includePaths_.end(), path) ==
includePaths_.push_back(path); includePaths_.end()) {
} includePaths_.push_back(path);
}
} }
/** /**
@ -260,15 +264,15 @@ void ShaderLoader::addIncludePath(const std::string& path) {
* @param filepath * @param filepath
* @return * @return
*/ */
std::string ShaderLoader::readFile(const std::string& filepath) { std::string ShaderLoader::readFile(const std::string &filepath) {
std::ifstream file(filepath, std::ios::binary); std::ifstream file(filepath, std::ios::binary);
if (!file.is_open()) { if (!file.is_open()) {
return ""; return "";
} }
std::ostringstream content; std::ostringstream content;
content << file.rdbuf(); content << file.rdbuf();
return content.str(); return content.str();
} }
/** /**
@ -276,19 +280,20 @@ std::string ShaderLoader::readFile(const std::string& filepath) {
* @param filepath * @param filepath
* @return * @return
*/ */
uint64_t ShaderLoader::getFileModifiedTime(const std::string& filepath) { uint64_t ShaderLoader::getFileModifiedTime(const std::string &filepath) {
#ifdef __SWITCH__ #ifdef __SWITCH__
(void)filepath; (void)filepath;
return 1; return 1;
#else #else
try { try {
auto ftime = fs::last_write_time(filepath); auto ftime = fs::last_write_time(filepath);
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>( auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now()); ftime - fs::file_time_type::clock::now() +
return static_cast<uint64_t>(sctp.time_since_epoch().count()); std::chrono::system_clock::now());
} catch (...) { return static_cast<uint64_t>(sctp.time_since_epoch().count());
return 0; } catch (...) {
} return 0;
}
#endif #endif
} }
@ -297,8 +302,8 @@ uint64_t ShaderLoader::getFileModifiedTime(const std::string& filepath) {
* @param filepath * @param filepath
* @return truefalse * @return truefalse
*/ */
bool ShaderLoader::fileExists(const std::string& filepath) { bool ShaderLoader::fileExists(const std::string &filepath) {
return fs::exists(filepath); return fs::exists(filepath);
} }
/** /**
@ -309,73 +314,67 @@ bool ShaderLoader::fileExists(const std::string& filepath) {
* @param outMetadata * @param outMetadata
* @return truefalse * @return truefalse
*/ */
bool ShaderLoader::parseCombinedFile(const std::string& content, bool ShaderLoader::parseCombinedFile(const std::string &content,
std::string& outVert, std::string &outVert, std::string &outFrag,
std::string& outFrag, ShaderMetadata &outMetadata) {
ShaderMetadata& outMetadata) { enum class Section { None, Meta, Vertex, Fragment };
enum class Section {
None,
Meta,
Vertex,
Fragment
};
Section currentSection = Section::None; Section currentSection = Section::None;
std::string metaContent; std::string metaContent;
std::string vertContent; std::string vertContent;
std::string fragContent; std::string fragContent;
std::istringstream stream(content); std::istringstream stream(content);
std::string line; std::string line;
while (std::getline(stream, line)) { while (std::getline(stream, line)) {
std::string trimmedLine = line; std::string trimmedLine = line;
size_t start = trimmedLine.find_first_not_of(" \t\r\n"); size_t start = trimmedLine.find_first_not_of(" \t\r\n");
if (start != std::string::npos) { if (start != std::string::npos) {
trimmedLine = trimmedLine.substr(start); trimmedLine = trimmedLine.substr(start);
} }
size_t end = trimmedLine.find_last_not_of(" \t\r\n"); size_t end = trimmedLine.find_last_not_of(" \t\r\n");
if (end != std::string::npos) { if (end != std::string::npos) {
trimmedLine = trimmedLine.substr(0, end + 1); trimmedLine = trimmedLine.substr(0, end + 1);
}
if (trimmedLine == "#meta") {
currentSection = Section::Meta;
continue;
} else if (trimmedLine == "#vertex") {
currentSection = Section::Vertex;
continue;
} else if (trimmedLine == "#fragment") {
currentSection = Section::Fragment;
continue;
}
switch (currentSection) {
case Section::Meta:
metaContent += line + "\n";
break;
case Section::Vertex:
vertContent += line + "\n";
break;
case Section::Fragment:
fragContent += line + "\n";
break;
default:
break;
}
} }
if (vertContent.empty() || fragContent.empty()) { if (trimmedLine == "#meta") {
return false; currentSection = Section::Meta;
continue;
} else if (trimmedLine == "#vertex") {
currentSection = Section::Vertex;
continue;
} else if (trimmedLine == "#fragment") {
currentSection = Section::Fragment;
continue;
} }
if (!metaContent.empty()) { switch (currentSection) {
parseMetadata(metaContent, outMetadata); case Section::Meta:
metaContent += line + "\n";
break;
case Section::Vertex:
vertContent += line + "\n";
break;
case Section::Fragment:
fragContent += line + "\n";
break;
default:
break;
} }
}
outVert = vertContent; if (vertContent.empty() || fragContent.empty()) {
outFrag = fragContent; return false;
return true; }
if (!metaContent.empty()) {
parseMetadata(metaContent, outMetadata);
}
outVert = vertContent;
outFrag = fragContent;
return true;
} }
/** /**
@ -384,44 +383,45 @@ bool ShaderLoader::parseCombinedFile(const std::string& content,
* @param outMetadata * @param outMetadata
* @return truefalse * @return truefalse
*/ */
bool ShaderLoader::parseMetadata(const std::string& jsonContent, ShaderMetadata& outMetadata) { bool ShaderLoader::parseMetadata(const std::string &jsonContent,
std::string content = jsonContent; ShaderMetadata &outMetadata) {
std::string content = jsonContent;
size_t start = content.find('{'); size_t start = content.find('{');
size_t end = content.rfind('}'); size_t end = content.rfind('}');
if (start == std::string::npos || end == std::string::npos || end <= start) { if (start == std::string::npos || end == std::string::npos || end <= start) {
return false; return false;
}
content = content.substr(start, end - start + 1);
auto extractString = [&content](const std::string &key) -> std::string {
std::string searchKey = "\"" + key + "\"";
size_t keyPos = content.find(searchKey);
if (keyPos == std::string::npos) {
return "";
} }
content = content.substr(start, end - start + 1); size_t colonPos = content.find(':', keyPos);
if (colonPos == std::string::npos) {
return "";
}
auto extractString = [&content](const std::string& key) -> std::string { size_t quoteStart = content.find('"', colonPos);
std::string searchKey = "\"" + key + "\""; if (quoteStart == std::string::npos) {
size_t keyPos = content.find(searchKey); return "";
if (keyPos == std::string::npos) { }
return "";
}
size_t colonPos = content.find(':', keyPos); size_t quoteEnd = content.find('"', quoteStart + 1);
if (colonPos == std::string::npos) { if (quoteEnd == std::string::npos) {
return ""; return "";
} }
size_t quoteStart = content.find('"', colonPos); return content.substr(quoteStart + 1, quoteEnd - quoteStart - 1);
if (quoteStart == std::string::npos) { };
return "";
}
size_t quoteEnd = content.find('"', quoteStart + 1); outMetadata.name = extractString("name");
if (quoteEnd == std::string::npos) { return true;
return "";
}
return content.substr(quoteStart + 1, quoteEnd - quoteStart - 1);
};
outMetadata.name = extractString("name");
return true;
} }
/** /**
@ -430,22 +430,23 @@ bool ShaderLoader::parseMetadata(const std::string& jsonContent, ShaderMetadata&
* @param baseDir * @param baseDir
* @return * @return
*/ */
std::string ShaderLoader::findIncludeFile(const std::string& includeName, const std::string& baseDir) { std::string ShaderLoader::findIncludeFile(const std::string &includeName,
fs::path basePath(baseDir); const std::string &baseDir) {
fs::path includePath = basePath / includeName; fs::path basePath(baseDir);
fs::path includePath = basePath / includeName;
if (fs::exists(includePath)) {
return includePath.string();
}
for (const auto &searchPath : includePaths_) {
includePath = fs::path(searchPath) / includeName;
if (fs::exists(includePath)) { if (fs::exists(includePath)) {
return includePath.string(); return includePath.string();
} }
}
for (const auto& searchPath : includePaths_) { return "";
includePath = fs::path(searchPath) / includeName;
if (fs::exists(includePath)) {
return includePath.string();
}
}
return "";
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,5 +1,7 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/shader/shader_manager.h> #include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@ -11,9 +13,9 @@ namespace extra2d {
* @brief * @brief
* @return Shader管理器实例引用 * @return Shader管理器实例引用
*/ */
ShaderManager& ShaderManager::getInstance() { ShaderManager &ShaderManager::getInstance() {
static ShaderManager instance; static ShaderManager instance;
return instance; return instance;
} }
/** /**
@ -23,22 +25,23 @@ ShaderManager& ShaderManager::getInstance() {
* @param appName * @param appName
* @return truefalse * @return truefalse
*/ */
bool ShaderManager::init(Ptr<IShaderFactory> factory, const std::string& appName) { bool ShaderManager::init(Ptr<IShaderFactory> factory,
// 使用相对路径作为Shader目录 const std::string &appName) {
std::string shaderDir = "shaders/"; // 使用相对路径作为Shader目录
std::string cacheDir = "cache/shaders/"; std::string shaderDir = "shaders/";
std::string cacheDir = "cache/shaders/";
// 非Switch平台支持热重载 // 非Switch平台支持热重载
#ifndef __SWITCH__ #ifndef __SWITCH__
hotReloadSupported_ = true; hotReloadSupported_ = true;
#else #else
hotReloadSupported_ = false; hotReloadSupported_ = false;
#endif #endif
E2D_LOG_INFO("ShaderManager init (HotReload: {})", E2D_LOG_INFO("ShaderManager init (HotReload: {})",
hotReloadSupported_ ? "supported" : "not supported"); hotReloadSupported_ ? "supported" : "not supported");
return init(shaderDir, cacheDir, factory); return init(shaderDir, cacheDir, factory);
} }
/** /**
@ -48,75 +51,76 @@ bool ShaderManager::init(Ptr<IShaderFactory> factory, const std::string& appName
* @param factory Shader工厂 * @param factory Shader工厂
* @return truefalse * @return truefalse
*/ */
bool ShaderManager::init(const std::string& shaderDir, bool ShaderManager::init(const std::string &shaderDir,
const std::string& cacheDir, const std::string &cacheDir,
Ptr<IShaderFactory> factory) { Ptr<IShaderFactory> factory) {
if (initialized_) { if (initialized_) {
E2D_LOG_WARN("ShaderManager already initialized"); E2D_LOG_WARN("ShaderManager already initialized");
return true; return true;
} }
if (!factory) { if (!factory) {
E2D_LOG_ERROR("Shader factory is null"); E2D_LOG_ERROR("Shader factory is null");
return false; return false;
} }
shaderDir_ = shaderDir; shaderDir_ = shaderDir;
cacheDir_ = cacheDir; cacheDir_ = cacheDir;
factory_ = factory; factory_ = factory;
// 非Switch平台支持热重载 // 非Switch平台支持热重载
#ifndef __SWITCH__ #ifndef __SWITCH__
hotReloadSupported_ = true; hotReloadSupported_ = true;
#else #else
hotReloadSupported_ = false; hotReloadSupported_ = false;
#endif #endif
#ifdef __SWITCH__ #ifdef __SWITCH__
if (!ShaderCache::getInstance().init(cacheDir_)) { if (!ShaderCache::getInstance().init(cacheDir_)) {
E2D_LOG_WARN("Failed to initialize shader cache on Switch"); E2D_LOG_WARN("Failed to initialize shader cache on Switch");
} }
#else #else
if (!ShaderCache::getInstance().init(cacheDir_)) { if (!ShaderCache::getInstance().init(cacheDir_)) {
E2D_LOG_WARN("Failed to initialize shader cache, caching disabled"); E2D_LOG_WARN("Failed to initialize shader cache, caching disabled");
} }
#endif #endif
if (hotReloadSupported_) { if (hotReloadSupported_) {
if (!ShaderHotReloader::getInstance().init()) { if (!ShaderHotReloader::getInstance().init()) {
E2D_LOG_WARN("Failed to initialize hot reloader"); E2D_LOG_WARN("Failed to initialize hot reloader");
}
} }
}
loader_.addIncludePath(shaderDir_ + "common"); loader_.addIncludePath(shaderDir_ + "common");
initialized_ = true; initialized_ = true;
E2D_LOG_INFO("ShaderManager initialized"); E2D_LOG_INFO("ShaderManager initialized");
E2D_LOG_INFO(" Shader directory: {}", shaderDir_); E2D_LOG_INFO(" Shader directory: {}", shaderDir_);
E2D_LOG_INFO(" Cache directory: {}", cacheDir_); E2D_LOG_INFO(" Cache directory: {}", cacheDir_);
E2D_LOG_INFO(" Hot reload: {}", hotReloadSupported_ ? "supported" : "not supported"); E2D_LOG_INFO(" Hot reload: {}",
hotReloadSupported_ ? "supported" : "not supported");
return true; return true;
} }
/** /**
* @brief Shader系统 * @brief Shader系统
*/ */
void ShaderManager::shutdown() { void ShaderManager::shutdown() {
if (!initialized_) { if (!initialized_) {
return; return;
} }
if (hotReloadSupported_) { if (hotReloadSupported_) {
ShaderHotReloader::getInstance().shutdown(); ShaderHotReloader::getInstance().shutdown();
} }
ShaderCache::getInstance().shutdown(); ShaderCache::getInstance().shutdown();
shaders_.clear(); shaders_.clear();
factory_.reset(); factory_.reset();
initialized_ = false; initialized_ = false;
E2D_LOG_INFO("ShaderManager shutdown"); E2D_LOG_INFO("ShaderManager shutdown");
} }
/** /**
@ -126,72 +130,79 @@ void ShaderManager::shutdown() {
* @param fragPath * @param fragPath
* @return Shader实例 * @return Shader实例
*/ */
Ptr<IShader> ShaderManager::loadFromFiles(const std::string& name, Ptr<IShader> ShaderManager::loadFromFiles(const std::string &name,
const std::string& vertPath, const std::string &vertPath,
const std::string& fragPath) { const std::string &fragPath) {
if (!initialized_) { if (!initialized_) {
E2D_LOG_ERROR("ShaderManager not initialized"); E2D_LOG_ERROR("ShaderManager not initialized");
return nullptr; return nullptr;
} }
auto it = shaders_.find(name); auto it = shaders_.find(name);
if (it != shaders_.end()) { if (it != shaders_.end()) {
return it->second.shader; return it->second.shader;
} }
ShaderLoadResult result = loader_.loadFromSeparateFiles(name, vertPath, fragPath); ShaderLoadResult result =
if (!result.success) { loader_.loadFromSeparateFiles(name, vertPath, fragPath);
E2D_LOG_ERROR("Failed to load shader files: {} - {}", vertPath, fragPath); if (!result.success) {
return nullptr; E2D_LOG_ERROR("Failed to load shader files: {} - {}", vertPath, fragPath);
} return nullptr;
}
std::string sourceHash = ShaderCache::computeHash(result.vertSource, result.fragSource); std::string sourceHash =
Ptr<IShader> shader = loadFromCache(name, sourceHash, result.vertSource, result.fragSource); ShaderCache::computeHash(result.vertSource, result.fragSource);
Ptr<IShader> shader =
loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
if (!shader) {
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}",
name);
shader =
factory_->createFromSource(name, result.vertSource, result.fragSource);
if (!shader) { if (!shader) {
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}", name); E2D_LOG_ERROR("Failed to create shader from source: {}", name);
shader = factory_->createFromSource(name, result.vertSource, result.fragSource); return nullptr;
if (!shader) {
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
return nullptr;
}
std::vector<uint8_t> binary;
if (factory_->getShaderBinary(*shader, binary)) {
E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size());
ShaderCacheEntry entry;
entry.name = name;
entry.sourceHash = sourceHash;
entry.binary = binary;
entry.dependencies = result.dependencies;
ShaderCache::getInstance().saveCache(entry);
} else {
E2D_LOG_WARN("Failed to get shader binary for: {}", name);
}
} }
ShaderInfo info; std::vector<uint8_t> binary;
info.shader = shader; if (factory_->getShaderBinary(*shader, binary)) {
info.vertSource = result.vertSource; E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size());
info.fragSource = result.fragSource; ShaderCacheEntry entry;
info.filePaths = {vertPath, fragPath}; entry.name = name;
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), result.dependencies.end()); entry.sourceHash = sourceHash;
entry.binary = binary;
info.metadata.name = name; entry.dependencies = result.dependencies;
info.metadata.vertPath = vertPath; ShaderCache::getInstance().saveCache(entry);
info.metadata.fragPath = fragPath; } else {
E2D_LOG_WARN("Failed to get shader binary for: {}", name);
shaders_[name] = std::move(info);
if (hotReloadEnabled_ && hotReloadSupported_) {
auto callback = [this, name](const FileChangeEvent& event) {
this->handleFileChange(name, event);
};
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, callback);
} }
}
E2D_LOG_DEBUG("Shader loaded: {}", name); ShaderInfo info;
return shader; info.shader = shader;
info.vertSource = result.vertSource;
info.fragSource = result.fragSource;
info.filePaths = {vertPath, fragPath};
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(),
result.dependencies.end());
info.metadata.name = name;
info.metadata.vertPath = vertPath;
info.metadata.fragPath = fragPath;
shaders_[name] = std::move(info);
if (hotReloadEnabled_ && hotReloadSupported_) {
auto callback = [this, name](const FileChangeEvent &event) {
this->handleFileChange(name, event);
};
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths,
callback);
}
E2D_LOG_DEBUG("Shader loaded: {}", name);
return shader;
} }
/** /**
@ -199,70 +210,76 @@ Ptr<IShader> ShaderManager::loadFromFiles(const std::string& name,
* @param path Shader文件路径 * @param path Shader文件路径
* @return Shader实例 * @return Shader实例
*/ */
Ptr<IShader> ShaderManager::loadFromCombinedFile(const std::string& path) { Ptr<IShader> ShaderManager::loadFromCombinedFile(const std::string &path) {
if (!initialized_) { if (!initialized_) {
E2D_LOG_ERROR("ShaderManager not initialized"); E2D_LOG_ERROR("ShaderManager not initialized");
return nullptr; return nullptr;
} }
ShaderMetadata metadata = loader_.getMetadata(path); ShaderMetadata metadata = loader_.getMetadata(path);
std::string name = metadata.name.empty() ? path : metadata.name; std::string name = metadata.name.empty() ? path : metadata.name;
auto it = shaders_.find(name); auto it = shaders_.find(name);
if (it != shaders_.end()) { if (it != shaders_.end()) {
return it->second.shader; return it->second.shader;
} }
ShaderLoadResult result = loader_.loadFromCombinedFile(path); ShaderLoadResult result = loader_.loadFromCombinedFile(path);
if (!result.success) { if (!result.success) {
E2D_LOG_ERROR("Failed to load combined shader file: {}", path); E2D_LOG_ERROR("Failed to load combined shader file: {}", path);
return nullptr; return nullptr;
} }
std::string sourceHash = ShaderCache::computeHash(result.vertSource, result.fragSource); std::string sourceHash =
Ptr<IShader> shader = loadFromCache(name, sourceHash, result.vertSource, result.fragSource); ShaderCache::computeHash(result.vertSource, result.fragSource);
Ptr<IShader> shader =
loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
if (!shader) {
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}",
name);
shader =
factory_->createFromSource(name, result.vertSource, result.fragSource);
if (!shader) { if (!shader) {
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}", name); E2D_LOG_ERROR("Failed to create shader from source: {}", name);
shader = factory_->createFromSource(name, result.vertSource, result.fragSource); return nullptr;
if (!shader) {
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
return nullptr;
}
std::vector<uint8_t> binary;
if (factory_->getShaderBinary(*shader, binary)) {
E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size());
ShaderCacheEntry entry;
entry.name = name;
entry.sourceHash = sourceHash;
entry.binary = binary;
entry.dependencies = result.dependencies;
ShaderCache::getInstance().saveCache(entry);
} else {
E2D_LOG_WARN("Failed to get shader binary for: {}", name);
}
} }
ShaderInfo info; std::vector<uint8_t> binary;
info.shader = shader; if (factory_->getShaderBinary(*shader, binary)) {
info.vertSource = result.vertSource; E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size());
info.fragSource = result.fragSource; ShaderCacheEntry entry;
info.filePaths = {path}; entry.name = name;
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), result.dependencies.end()); entry.sourceHash = sourceHash;
info.metadata = metadata; entry.binary = binary;
entry.dependencies = result.dependencies;
shaders_[name] = std::move(info); ShaderCache::getInstance().saveCache(entry);
} else {
if (hotReloadEnabled_ && hotReloadSupported_) { E2D_LOG_WARN("Failed to get shader binary for: {}", name);
auto callback = [this, name](const FileChangeEvent& event) {
this->handleFileChange(name, event);
};
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, callback);
} }
}
E2D_LOG_DEBUG("Shader loaded from combined file: {}", name); ShaderInfo info;
return shader; info.shader = shader;
info.vertSource = result.vertSource;
info.fragSource = result.fragSource;
info.filePaths = {path};
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(),
result.dependencies.end());
info.metadata = metadata;
shaders_[name] = std::move(info);
if (hotReloadEnabled_ && hotReloadSupported_) {
auto callback = [this, name](const FileChangeEvent &event) {
this->handleFileChange(name, event);
};
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths,
callback);
}
E2D_LOG_DEBUG("Shader loaded from combined file: {}", name);
return shader;
} }
/** /**
@ -272,35 +289,36 @@ Ptr<IShader> ShaderManager::loadFromCombinedFile(const std::string& path) {
* @param fragSource * @param fragSource
* @return Shader实例 * @return Shader实例
*/ */
Ptr<IShader> ShaderManager::loadFromSource(const std::string& name, Ptr<IShader> ShaderManager::loadFromSource(const std::string &name,
const std::string& vertSource, const std::string &vertSource,
const std::string& fragSource) { const std::string &fragSource) {
if (!initialized_) { if (!initialized_) {
E2D_LOG_ERROR("ShaderManager not initialized"); E2D_LOG_ERROR("ShaderManager not initialized");
return nullptr; return nullptr;
} }
auto it = shaders_.find(name); auto it = shaders_.find(name);
if (it != shaders_.end()) { if (it != shaders_.end()) {
return it->second.shader; return it->second.shader;
} }
Ptr<IShader> shader = factory_->createFromSource(name, vertSource, fragSource); Ptr<IShader> shader =
if (!shader) { factory_->createFromSource(name, vertSource, fragSource);
E2D_LOG_ERROR("Failed to create shader from source: {}", name); if (!shader) {
return nullptr; E2D_LOG_ERROR("Failed to create shader from source: {}", name);
} return nullptr;
}
ShaderInfo info; ShaderInfo info;
info.shader = shader; info.shader = shader;
info.vertSource = vertSource; info.vertSource = vertSource;
info.fragSource = fragSource; info.fragSource = fragSource;
info.metadata.name = name; info.metadata.name = name;
shaders_[name] = std::move(info); shaders_[name] = std::move(info);
E2D_LOG_DEBUG("Shader loaded from source: {}", name); E2D_LOG_DEBUG("Shader loaded from source: {}", name);
return shader; return shader;
} }
/** /**
@ -308,12 +326,12 @@ Ptr<IShader> ShaderManager::loadFromSource(const std::string& name,
* @param name Shader名称 * @param name Shader名称
* @return Shader实例nullptr * @return Shader实例nullptr
*/ */
Ptr<IShader> ShaderManager::get(const std::string& name) const { Ptr<IShader> ShaderManager::get(const std::string &name) const {
auto it = shaders_.find(name); auto it = shaders_.find(name);
if (it != shaders_.end()) { if (it != shaders_.end()) {
return it->second.shader; return it->second.shader;
} }
return nullptr; return nullptr;
} }
/** /**
@ -321,34 +339,34 @@ Ptr<IShader> ShaderManager::get(const std::string& name) const {
* @param name Shader名称 * @param name Shader名称
* @return truefalse * @return truefalse
*/ */
bool ShaderManager::has(const std::string& name) const { bool ShaderManager::has(const std::string &name) const {
return shaders_.find(name) != shaders_.end(); return shaders_.find(name) != shaders_.end();
} }
/** /**
* @brief Shader * @brief Shader
* @param name Shader名称 * @param name Shader名称
*/ */
void ShaderManager::remove(const std::string& name) { void ShaderManager::remove(const std::string &name) {
auto it = shaders_.find(name); auto it = shaders_.find(name);
if (it != shaders_.end()) { if (it != shaders_.end()) {
ShaderHotReloader::getInstance().unwatch(name); ShaderHotReloader::getInstance().unwatch(name);
shaders_.erase(it); shaders_.erase(it);
E2D_LOG_DEBUG("Shader removed: {}", name); E2D_LOG_DEBUG("Shader removed: {}", name);
} }
} }
/** /**
* @brief Shader * @brief Shader
*/ */
void ShaderManager::clear() { void ShaderManager::clear() {
if (hotReloadSupported_) { if (hotReloadSupported_) {
for (const auto& pair : shaders_) { for (const auto &pair : shaders_) {
ShaderHotReloader::getInstance().unwatch(pair.first); ShaderHotReloader::getInstance().unwatch(pair.first);
}
} }
shaders_.clear(); }
E2D_LOG_DEBUG("All shaders cleared"); shaders_.clear();
E2D_LOG_DEBUG("All shaders cleared");
} }
/** /**
@ -356,11 +374,12 @@ void ShaderManager::clear() {
* @param name Shader名称 * @param name Shader名称
* @param callback * @param callback
*/ */
void ShaderManager::setReloadCallback(const std::string& name, ShaderReloadCallback callback) { void ShaderManager::setReloadCallback(const std::string &name,
auto it = shaders_.find(name); ShaderReloadCallback callback) {
if (it != shaders_.end()) { auto it = shaders_.find(name);
it->second.reloadCallback = callback; if (it != shaders_.end()) {
} it->second.reloadCallback = callback;
}
} }
/** /**
@ -368,13 +387,13 @@ void ShaderManager::setReloadCallback(const std::string& name, ShaderReloadCallb
* @param enabled * @param enabled
*/ */
void ShaderManager::setHotReloadEnabled(bool enabled) { void ShaderManager::setHotReloadEnabled(bool enabled) {
if (!hotReloadSupported_) { if (!hotReloadSupported_) {
E2D_LOG_WARN("Hot reload not supported on this platform"); E2D_LOG_WARN("Hot reload not supported on this platform");
return; return;
} }
hotReloadEnabled_ = enabled; hotReloadEnabled_ = enabled;
ShaderHotReloader::getInstance().setEnabled(enabled); ShaderHotReloader::getInstance().setEnabled(enabled);
E2D_LOG_INFO("Hot reload {}", enabled ? "enabled" : "disabled"); E2D_LOG_INFO("Hot reload {}", enabled ? "enabled" : "disabled");
} }
/** /**
@ -382,16 +401,16 @@ void ShaderManager::setHotReloadEnabled(bool enabled) {
* @return truefalse * @return truefalse
*/ */
bool ShaderManager::isHotReloadEnabled() const { bool ShaderManager::isHotReloadEnabled() const {
return hotReloadEnabled_ && hotReloadSupported_; return hotReloadEnabled_ && hotReloadSupported_;
} }
/** /**
* @brief * @brief
*/ */
void ShaderManager::update() { void ShaderManager::update() {
if (hotReloadEnabled_ && hotReloadSupported_) { if (hotReloadEnabled_ && hotReloadSupported_) {
ShaderHotReloader::getInstance().update(); ShaderHotReloader::getInstance().update();
} }
} }
/** /**
@ -399,48 +418,51 @@ void ShaderManager::update() {
* @param name Shader名称 * @param name Shader名称
* @return truefalse * @return truefalse
*/ */
bool ShaderManager::reload(const std::string& name) { bool ShaderManager::reload(const std::string &name) {
auto it = shaders_.find(name); auto it = shaders_.find(name);
if (it == shaders_.end()) { if (it == shaders_.end()) {
E2D_LOG_WARN("Shader not found for reload: {}", name); E2D_LOG_WARN("Shader not found for reload: {}", name);
return false; return false;
}
ShaderInfo &info = it->second;
std::string vertSource = info.vertSource;
std::string fragSource = info.fragSource;
if (!info.metadata.vertPath.empty() && !info.metadata.fragPath.empty()) {
ShaderLoadResult result = loader_.loadFromSeparateFiles(
name, info.metadata.vertPath, info.metadata.fragPath);
if (result.success) {
vertSource = result.vertSource;
fragSource = result.fragSource;
} }
} else if (!info.metadata.combinedPath.empty()) {
ShaderInfo& info = it->second; ShaderLoadResult result =
loader_.loadFromCombinedFile(info.metadata.combinedPath);
std::string vertSource = info.vertSource; if (result.success) {
std::string fragSource = info.fragSource; vertSource = result.vertSource;
fragSource = result.fragSource;
if (!info.metadata.vertPath.empty() && !info.metadata.fragPath.empty()) {
ShaderLoadResult result = loader_.loadFromSeparateFiles(name, info.metadata.vertPath, info.metadata.fragPath);
if (result.success) {
vertSource = result.vertSource;
fragSource = result.fragSource;
}
} else if (!info.metadata.combinedPath.empty()) {
ShaderLoadResult result = loader_.loadFromCombinedFile(info.metadata.combinedPath);
if (result.success) {
vertSource = result.vertSource;
fragSource = result.fragSource;
}
} }
}
Ptr<IShader> newShader = factory_->createFromSource(name, vertSource, fragSource); Ptr<IShader> newShader =
if (!newShader) { factory_->createFromSource(name, vertSource, fragSource);
E2D_LOG_ERROR("Failed to reload shader: {}", name); if (!newShader) {
return false; E2D_LOG_ERROR("Failed to reload shader: {}", name);
} return false;
}
info.shader = newShader; info.shader = newShader;
info.vertSource = vertSource; info.vertSource = vertSource;
info.fragSource = fragSource; info.fragSource = fragSource;
if (info.reloadCallback) { if (info.reloadCallback) {
info.reloadCallback(newShader); info.reloadCallback(newShader);
} }
E2D_LOG_INFO("Shader reloaded: {}", name); E2D_LOG_INFO("Shader reloaded: {}", name);
return true; return true;
} }
/** /**
@ -448,21 +470,21 @@ bool ShaderManager::reload(const std::string& name) {
* @param name Shader名称 * @param name Shader名称
* @return Shader实例 * @return Shader实例
*/ */
Ptr<IShader> ShaderManager::getBuiltin(const std::string& name) { Ptr<IShader> ShaderManager::getBuiltin(const std::string &name) {
Ptr<IShader> shader = get(name); Ptr<IShader> shader = get(name);
if (shader) { if (shader) {
return shader; return shader;
} }
// 尝试从新的多后端JSON元数据加载 // 尝试从新的多后端JSON元数据加载
std::string jsonPath = shaderDir_ + "shared/builtin/" + name + ".json"; std::string jsonPath = shaderDir_ + "shared/builtin/" + name + ".json";
if (loader_.fileExists(jsonPath)) { if (loader_.fileExists(jsonPath)) {
return loadFromMetadata(jsonPath, name); return loadFromMetadata(jsonPath, name);
} }
// 回退到旧的组合文件格式 // 回退到旧的组合文件格式
std::string path = shaderDir_ + "builtin/" + name + ".shader"; std::string path = shaderDir_ + "builtin/" + name + ".shader";
return loadFromCombinedFile(path); return loadFromCombinedFile(path);
} }
/** /**
@ -471,57 +493,60 @@ Ptr<IShader> ShaderManager::getBuiltin(const std::string& name) {
* @param name Shader名称 * @param name Shader名称
* @return Shader实例 * @return Shader实例
*/ */
Ptr<IShader> ShaderManager::loadFromMetadata(const std::string& jsonPath, const std::string& name) { Ptr<IShader> ShaderManager::loadFromMetadata(const std::string &jsonPath,
if (!initialized_) { const std::string &name) {
E2D_LOG_ERROR("ShaderManager not initialized"); if (!initialized_) {
return nullptr; E2D_LOG_ERROR("ShaderManager not initialized");
return nullptr;
}
// 检查是否已加载
auto it = shaders_.find(name);
if (it != shaders_.end()) {
return it->second.shader;
}
// 读取JSON文件
std::string jsonContent = loader_.readFile(jsonPath);
if (jsonContent.empty()) {
E2D_LOG_ERROR("Failed to read shader metadata: {}", jsonPath);
return nullptr;
}
try {
// 使用nlohmann/json解析
nl::json j = nl::json::parse(jsonContent);
// 获取OpenGL后端路径
if (!j.contains("backends") || !j["backends"].contains("opengl")) {
E2D_LOG_ERROR("No OpenGL backend found in shader metadata: {}", jsonPath);
return nullptr;
} }
// 检查是否已加载 auto &opengl = j["backends"]["opengl"];
auto it = shaders_.find(name); if (!opengl.contains("vertex") || !opengl.contains("fragment")) {
if (it != shaders_.end()) { E2D_LOG_ERROR("Missing vertex or fragment path in shader metadata: {}",
return it->second.shader; jsonPath);
return nullptr;
} }
// 读取JSON文件 std::string vertRelativePath = opengl["vertex"].get<std::string>();
std::string jsonContent = loader_.readFile(jsonPath); std::string fragRelativePath = opengl["fragment"].get<std::string>();
if (jsonContent.empty()) {
E2D_LOG_ERROR("Failed to read shader metadata: {}", jsonPath);
return nullptr;
}
try { // 构建完整路径
// 使用nlohmann/json解析 std::string vertPath = shaderDir_ + vertRelativePath;
nl::json j = nl::json::parse(jsonContent); std::string fragPath = shaderDir_ + fragRelativePath;
// 获取OpenGL后端路径 E2D_LOG_DEBUG("Loading shader from metadata: {} -> vert: {}, frag: {}",
if (!j.contains("backends") || !j["backends"].contains("opengl")) { name, vertPath, fragPath);
E2D_LOG_ERROR("No OpenGL backend found in shader metadata: {}", jsonPath);
return nullptr;
}
auto& opengl = j["backends"]["opengl"]; // 使用分离文件加载
if (!opengl.contains("vertex") || !opengl.contains("fragment")) { return loadFromFiles(name, vertPath, fragPath);
E2D_LOG_ERROR("Missing vertex or fragment path in shader metadata: {}", jsonPath);
return nullptr;
}
std::string vertRelativePath = opengl["vertex"].get<std::string>(); } catch (const nl::json::exception &e) {
std::string fragRelativePath = opengl["fragment"].get<std::string>(); E2D_LOG_ERROR("Failed to parse shader metadata {}: {}", jsonPath, e.what());
return nullptr;
// 构建完整路径 }
std::string vertPath = shaderDir_ + vertRelativePath;
std::string fragPath = shaderDir_ + fragRelativePath;
E2D_LOG_DEBUG("Loading shader from metadata: {} -> vert: {}, frag: {}", name, vertPath, fragPath);
// 使用分离文件加载
return loadFromFiles(name, vertPath, fragPath);
} catch (const nl::json::exception& e) {
E2D_LOG_ERROR("Failed to parse shader metadata {}: {}", jsonPath, e.what());
return nullptr;
}
} }
/** /**
@ -529,52 +554,47 @@ Ptr<IShader> ShaderManager::loadFromMetadata(const std::string& jsonPath, const
* @return truefalse * @return truefalse
*/ */
bool ShaderManager::loadBuiltinShaders() { bool ShaderManager::loadBuiltinShaders() {
if (!initialized_) { if (!initialized_) {
E2D_LOG_ERROR("ShaderManager not initialized"); E2D_LOG_ERROR("ShaderManager not initialized");
return false; return false;
}
bool allSuccess = true;
const char *builtinNames[] = {"sprite", "particle", "shape", "postprocess",
"font"};
for (const char *name : builtinNames) {
// 首先尝试新的多后端JSON格式
std::string jsonPath = shaderDir_ + "shared/builtin/" + name + ".json";
std::string shaderName = std::string("builtin_") + name;
Ptr<IShader> shader = nullptr;
if (loader_.fileExists(jsonPath)) {
shader = loadFromMetadata(jsonPath, name);
} else {
// 回退到旧的组合文件格式
std::string path = shaderDir_ + "builtin/" + name + ".shader";
shader = loadFromCombinedFile(path);
} }
bool allSuccess = true; if (!shader) {
E2D_LOG_ERROR("Failed to load builtin {} shader", name);
const char* builtinNames[] = { allSuccess = false;
"sprite", } else {
"particle", // 同时注册带 builtin_ 前缀的名称
"shape", auto it = shaders_.find(name);
"postprocess", if (it != shaders_.end()) {
"font" shaders_[shaderName] = it->second;
}; }
for (const char* name : builtinNames) {
// 首先尝试新的多后端JSON格式
std::string jsonPath = shaderDir_ + "shared/builtin/" + name + ".json";
std::string shaderName = std::string("builtin_") + name;
Ptr<IShader> shader = nullptr;
if (loader_.fileExists(jsonPath)) {
shader = loadFromMetadata(jsonPath, name);
} else {
// 回退到旧的组合文件格式
std::string path = shaderDir_ + "builtin/" + name + ".shader";
shader = loadFromCombinedFile(path);
}
if (!shader) {
E2D_LOG_ERROR("Failed to load builtin {} shader", name);
allSuccess = false;
} else {
// 同时注册带 builtin_ 前缀的名称
auto it = shaders_.find(name);
if (it != shaders_.end()) {
shaders_[shaderName] = it->second;
}
}
} }
}
if (allSuccess) { if (allSuccess) {
E2D_LOG_INFO("All builtin shaders loaded"); E2D_LOG_INFO("All builtin shaders loaded");
} }
return allSuccess; return allSuccess;
} }
/** /**
@ -585,29 +605,29 @@ bool ShaderManager::loadBuiltinShaders() {
* @param fragSource * @param fragSource
* @return Shader实例 * @return Shader实例
*/ */
Ptr<IShader> ShaderManager::loadFromCache(const std::string& name, Ptr<IShader> ShaderManager::loadFromCache(const std::string &name,
const std::string& sourceHash, const std::string &sourceHash,
const std::string& vertSource, const std::string &vertSource,
const std::string& fragSource) { const std::string &fragSource) {
if (!ShaderCache::getInstance().isInitialized()) { if (!ShaderCache::getInstance().isInitialized()) {
return nullptr; return nullptr;
} }
if (!ShaderCache::getInstance().hasValidCache(name, sourceHash)) { if (!ShaderCache::getInstance().hasValidCache(name, sourceHash)) {
return nullptr; return nullptr;
} }
Ptr<ShaderCacheEntry> entry = ShaderCache::getInstance().loadCache(name); Ptr<ShaderCacheEntry> entry = ShaderCache::getInstance().loadCache(name);
if (!entry || entry->binary.empty()) { if (!entry || entry->binary.empty()) {
return nullptr; return nullptr;
} }
Ptr<IShader> shader = factory_->createFromBinary(name, entry->binary); Ptr<IShader> shader = factory_->createFromBinary(name, entry->binary);
if (shader) { if (shader) {
E2D_LOG_DEBUG("Shader loaded from cache: {}", name); E2D_LOG_DEBUG("Shader loaded from cache: {}", name);
} }
return shader; return shader;
} }
/** /**
@ -615,9 +635,10 @@ Ptr<IShader> ShaderManager::loadFromCache(const std::string& name,
* @param shaderName Shader名称 * @param shaderName Shader名称
* @param event * @param event
*/ */
void ShaderManager::handleFileChange(const std::string& shaderName, const FileChangeEvent& event) { void ShaderManager::handleFileChange(const std::string &shaderName,
E2D_LOG_DEBUG("Shader file changed: {} -> {}", shaderName, event.filepath); const FileChangeEvent &event) {
reload(shaderName); E2D_LOG_DEBUG("Shader file changed: {} -> {}", shaderName, event.filepath);
reload(shaderName);
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,6 +1,8 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/shader/shader_manager.h> #include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/graphics/shader/shader_preset.h> #include <extra2d/graphics/shader/shader_preset.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {

View File

@ -1,7 +1,6 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/texture/texture_atlas.h> #include <extra2d/graphics/texture/texture_atlas.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
#include <algorithm>
#include <cstring>
namespace extra2d { namespace extra2d {
@ -46,8 +45,9 @@ TextureAtlasPage::~TextureAtlasPage() = default;
* *
* 使 * 使
*/ */
bool TextureAtlasPage::tryAddTexture(const std::string& name, int texWidth, int texHeight, bool TextureAtlasPage::tryAddTexture(const std::string &name, int texWidth,
const uint8_t* pixels, Rect& outUvRect) { int texHeight, const uint8_t *pixels,
Rect &outUvRect) {
if (isFull_) { if (isFull_) {
return false; return false;
} }
@ -62,7 +62,7 @@ bool TextureAtlasPage::tryAddTexture(const std::string& name, int texWidth, int
} }
// 尝试插入 // 尝试插入
PackNode* node = insert(root_.get(), paddedWidth, paddedHeight); PackNode *node = insert(root_.get(), paddedWidth, paddedHeight);
if (node == nullptr) { if (node == nullptr) {
// 无法放入,标记为满 // 无法放入,标记为满
isFull_ = true; isFull_ = true;
@ -70,12 +70,14 @@ bool TextureAtlasPage::tryAddTexture(const std::string& name, int texWidth, int
} }
// 写入像素数据(跳过边距区域) // 写入像素数据(跳过边距区域)
writePixels(node->x + PADDING, node->y + PADDING, texWidth, texHeight, pixels); writePixels(node->x + PADDING, node->y + PADDING, texWidth, texHeight,
pixels);
// 创建条目 // 创建条目
AtlasEntry entry; AtlasEntry entry;
entry.name = name; entry.name = name;
entry.originalSize = Vec2(static_cast<float>(texWidth), static_cast<float>(texHeight)); entry.originalSize =
Vec2(static_cast<float>(texWidth), static_cast<float>(texHeight));
entry.padding = PADDING; entry.padding = PADDING;
// 计算 UV 坐标(考虑边距) // 计算 UV 坐标(考虑边距)
@ -90,8 +92,8 @@ bool TextureAtlasPage::tryAddTexture(const std::string& name, int texWidth, int
entries_[name] = std::move(entry); entries_[name] = std::move(entry);
usedArea_ += paddedWidth * paddedHeight; usedArea_ += paddedWidth * paddedHeight;
E2D_LOG_DEBUG("Added texture '{}' to atlas: {}x{} at ({}, {})", E2D_LOG_DEBUG("Added texture '{}' to atlas: {}x{} at ({}, {})", name,
name, texWidth, texHeight, node->x, node->y); texWidth, texHeight, node->x, node->y);
return true; return true;
} }
@ -105,14 +107,15 @@ bool TextureAtlasPage::tryAddTexture(const std::string& name, int texWidth, int
* *
* 使 * 使
*/ */
TextureAtlasPage::PackNode* TextureAtlasPage::insert(PackNode* node, int width, int height) { TextureAtlasPage::PackNode *TextureAtlasPage::insert(PackNode *node, int width,
int height) {
if (node == nullptr) { if (node == nullptr) {
return nullptr; return nullptr;
} }
// 如果节点已被使用,尝试子节点 // 如果节点已被使用,尝试子节点
if (node->used) { if (node->used) {
PackNode* result = insert(node->left.get(), width, height); PackNode *result = insert(node->left.get(), width, height);
if (result != nullptr) { if (result != nullptr) {
return result; return result;
} }
@ -136,12 +139,16 @@ TextureAtlasPage::PackNode* TextureAtlasPage::insert(PackNode* node, int width,
if (dw > dh) { if (dw > dh) {
// 水平分割 // 水平分割
node->left = std::make_unique<PackNode>(node->x, node->y, width, node->height); node->left =
node->right = std::make_unique<PackNode>(node->x + width, node->y, dw, node->height); std::make_unique<PackNode>(node->x, node->y, width, node->height);
node->right =
std::make_unique<PackNode>(node->x + width, node->y, dw, node->height);
} else { } else {
// 垂直分割 // 垂直分割
node->left = std::make_unique<PackNode>(node->x, node->y, node->width, height); node->left =
node->right = std::make_unique<PackNode>(node->x, node->y + height, node->width, dh); std::make_unique<PackNode>(node->x, node->y, node->width, height);
node->right =
std::make_unique<PackNode>(node->x, node->y + height, node->width, dh);
} }
// 递归插入到左子节点 // 递归插入到左子节点
@ -158,7 +165,8 @@ TextureAtlasPage::PackNode* TextureAtlasPage::insert(PackNode* node, int width,
* *
* 使glTexSubImage2D更新纹理的指定区域 * 使glTexSubImage2D更新纹理的指定区域
*/ */
void TextureAtlasPage::writePixels(int x, int y, int w, int h, const uint8_t* pixels) { void TextureAtlasPage::writePixels(int x, int y, int w, int h,
const uint8_t *pixels) {
if (texture_ == nullptr || pixels == nullptr) { if (texture_ == nullptr || pixels == nullptr) {
return; return;
} }
@ -168,7 +176,8 @@ void TextureAtlasPage::writePixels(int x, int y, int w, int h, const uint8_t* pi
reinterpret_cast<uintptr_t>(texture_->getNativeHandle())); reinterpret_cast<uintptr_t>(texture_->getNativeHandle()));
glBindTexture(GL_TEXTURE_2D, texID); glBindTexture(GL_TEXTURE_2D, texID);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
pixels);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
@ -177,7 +186,7 @@ void TextureAtlasPage::writePixels(int x, int y, int w, int h, const uint8_t* pi
* @param name * @param name
* @return nullptr * @return nullptr
*/ */
const AtlasEntry* TextureAtlasPage::getEntry(const std::string& name) const { const AtlasEntry *TextureAtlasPage::getEntry(const std::string &name) const {
auto it = entries_.find(name); auto it = entries_.find(name);
if (it != entries_.end()) { if (it != entries_.end()) {
return &it->second; return &it->second;
@ -205,11 +214,8 @@ float TextureAtlasPage::getUsageRatio() const {
* 使 * 使
*/ */
TextureAtlas::TextureAtlas() TextureAtlas::TextureAtlas()
: pageSize_(TextureAtlasPage::DEFAULT_SIZE), : pageSize_(TextureAtlasPage::DEFAULT_SIZE), sizeThreshold_(256),
sizeThreshold_(256), enabled_(true), initialized_(false) {}
enabled_(true),
initialized_(false) {
}
/** /**
* @brief * @brief
@ -240,8 +246,8 @@ void TextureAtlas::init(int pageSize) {
* *
* *
*/ */
bool TextureAtlas::addTexture(const std::string& name, int width, int height, bool TextureAtlas::addTexture(const std::string &name, int width, int height,
const uint8_t* pixels) { const uint8_t *pixels) {
if (!enabled_ || !initialized_) { if (!enabled_ || !initialized_) {
return false; return false;
} }
@ -260,7 +266,7 @@ bool TextureAtlas::addTexture(const std::string& name, int width, int height,
// 尝试添加到现有页面 // 尝试添加到现有页面
Rect uvRect; Rect uvRect;
for (auto& page : pages_) { for (auto &page : pages_) {
if (page->tryAddTexture(name, width, height, pixels, uvRect)) { if (page->tryAddTexture(name, width, height, pixels, uvRect)) {
entryToPage_[name] = page.get(); entryToPage_[name] = page.get();
return true; return true;
@ -284,7 +290,7 @@ bool TextureAtlas::addTexture(const std::string& name, int width, int height,
* @param name * @param name
* @return truefalse * @return truefalse
*/ */
bool TextureAtlas::contains(const std::string& name) const { bool TextureAtlas::contains(const std::string &name) const {
return entryToPage_.find(name) != entryToPage_.end(); return entryToPage_.find(name) != entryToPage_.end();
} }
@ -293,7 +299,7 @@ bool TextureAtlas::contains(const std::string& name) const {
* @param name * @param name
* @return nullptr * @return nullptr
*/ */
const Texture* TextureAtlas::getAtlasTexture(const std::string& name) const { const Texture *TextureAtlas::getAtlasTexture(const std::string &name) const {
auto it = entryToPage_.find(name); auto it = entryToPage_.find(name);
if (it != entryToPage_.end()) { if (it != entryToPage_.end()) {
return it->second->getTexture().get(); return it->second->getTexture().get();
@ -306,10 +312,10 @@ const Texture* TextureAtlas::getAtlasTexture(const std::string& name) const {
* @param name * @param name
* @return UV坐标矩形 * @return UV坐标矩形
*/ */
Rect TextureAtlas::getUVRect(const std::string& name) const { Rect TextureAtlas::getUVRect(const std::string &name) const {
auto it = entryToPage_.find(name); auto it = entryToPage_.find(name);
if (it != entryToPage_.end()) { if (it != entryToPage_.end()) {
const AtlasEntry* entry = it->second->getEntry(name); const AtlasEntry *entry = it->second->getEntry(name);
if (entry != nullptr) { if (entry != nullptr) {
return entry->uvRect; return entry->uvRect;
} }
@ -322,10 +328,10 @@ Rect TextureAtlas::getUVRect(const std::string& name) const {
* @param name * @param name
* @return * @return
*/ */
Vec2 TextureAtlas::getOriginalSize(const std::string& name) const { Vec2 TextureAtlas::getOriginalSize(const std::string &name) const {
auto it = entryToPage_.find(name); auto it = entryToPage_.find(name);
if (it != entryToPage_.end()) { if (it != entryToPage_.end()) {
const AtlasEntry* entry = it->second->getEntry(name); const AtlasEntry *entry = it->second->getEntry(name);
if (entry != nullptr) { if (entry != nullptr) {
return entry->originalSize; return entry->originalSize;
} }
@ -345,7 +351,7 @@ float TextureAtlas::getTotalUsageRatio() const {
} }
float total = 0.0f; float total = 0.0f;
for (const auto& page : pages_) { for (const auto &page : pages_) {
total += page->getUsageRatio(); total += page->getUsageRatio();
} }
return total / pages_.size(); return total / pages_.size();
@ -372,7 +378,7 @@ void TextureAtlas::clear() {
* *
* 使线 * 使线
*/ */
TextureAtlasMgr& TextureAtlasMgr::get() { TextureAtlasMgr &TextureAtlasMgr::get() {
static TextureAtlasMgr instance; static TextureAtlasMgr instance;
return instance; return instance;
} }

View File

@ -1,426 +1,492 @@
#include "glfw_input.h" #include "glfw_input.h"
#include <extra2d/utils/logger.h>
#include <cmath> #include <cmath>
#include <extra2d/core/service_locator.h>
#include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
// GLFW 按键到引擎按键的映射 // GLFW 按键到引擎按键的映射
static Key glfwToKey(int glfwKey) { static Key glfwToKey(int glfwKey) {
switch (glfwKey) { switch (glfwKey) {
// 字母键 // 字母键
case GLFW_KEY_A: return Key::A; case GLFW_KEY_A:
case GLFW_KEY_B: return Key::B; return Key::A;
case GLFW_KEY_C: return Key::C; case GLFW_KEY_B:
case GLFW_KEY_D: return Key::D; return Key::B;
case GLFW_KEY_E: return Key::E; case GLFW_KEY_C:
case GLFW_KEY_F: return Key::F; return Key::C;
case GLFW_KEY_G: return Key::G; case GLFW_KEY_D:
case GLFW_KEY_H: return Key::H; return Key::D;
case GLFW_KEY_I: return Key::I; case GLFW_KEY_E:
case GLFW_KEY_J: return Key::J; return Key::E;
case GLFW_KEY_K: return Key::K; case GLFW_KEY_F:
case GLFW_KEY_L: return Key::L; return Key::F;
case GLFW_KEY_M: return Key::M; case GLFW_KEY_G:
case GLFW_KEY_N: return Key::N; return Key::G;
case GLFW_KEY_O: return Key::O; case GLFW_KEY_H:
case GLFW_KEY_P: return Key::P; return Key::H;
case GLFW_KEY_Q: return Key::Q; case GLFW_KEY_I:
case GLFW_KEY_R: return Key::R; return Key::I;
case GLFW_KEY_S: return Key::S; case GLFW_KEY_J:
case GLFW_KEY_T: return Key::T; return Key::J;
case GLFW_KEY_U: return Key::U; case GLFW_KEY_K:
case GLFW_KEY_V: return Key::V; return Key::K;
case GLFW_KEY_W: return Key::W; case GLFW_KEY_L:
case GLFW_KEY_X: return Key::X; return Key::L;
case GLFW_KEY_Y: return Key::Y; case GLFW_KEY_M:
case GLFW_KEY_Z: return Key::Z; return Key::M;
case GLFW_KEY_N:
return Key::N;
case GLFW_KEY_O:
return Key::O;
case GLFW_KEY_P:
return Key::P;
case GLFW_KEY_Q:
return Key::Q;
case GLFW_KEY_R:
return Key::R;
case GLFW_KEY_S:
return Key::S;
case GLFW_KEY_T:
return Key::T;
case GLFW_KEY_U:
return Key::U;
case GLFW_KEY_V:
return Key::V;
case GLFW_KEY_W:
return Key::W;
case GLFW_KEY_X:
return Key::X;
case GLFW_KEY_Y:
return Key::Y;
case GLFW_KEY_Z:
return Key::Z;
// 数字键 // 数字键
case GLFW_KEY_0: return Key::Num0; case GLFW_KEY_0:
case GLFW_KEY_1: return Key::Num1; return Key::Num0;
case GLFW_KEY_2: return Key::Num2; case GLFW_KEY_1:
case GLFW_KEY_3: return Key::Num3; return Key::Num1;
case GLFW_KEY_4: return Key::Num4; case GLFW_KEY_2:
case GLFW_KEY_5: return Key::Num5; return Key::Num2;
case GLFW_KEY_6: return Key::Num6; case GLFW_KEY_3:
case GLFW_KEY_7: return Key::Num7; return Key::Num3;
case GLFW_KEY_8: return Key::Num8; case GLFW_KEY_4:
case GLFW_KEY_9: return Key::Num9; return Key::Num4;
case GLFW_KEY_5:
return Key::Num5;
case GLFW_KEY_6:
return Key::Num6;
case GLFW_KEY_7:
return Key::Num7;
case GLFW_KEY_8:
return Key::Num8;
case GLFW_KEY_9:
return Key::Num9;
// 功能键 // 功能键
case GLFW_KEY_F1: return Key::F1; case GLFW_KEY_F1:
case GLFW_KEY_F2: return Key::F2; return Key::F1;
case GLFW_KEY_F3: return Key::F3; case GLFW_KEY_F2:
case GLFW_KEY_F4: return Key::F4; return Key::F2;
case GLFW_KEY_F5: return Key::F5; case GLFW_KEY_F3:
case GLFW_KEY_F6: return Key::F6; return Key::F3;
case GLFW_KEY_F7: return Key::F7; case GLFW_KEY_F4:
case GLFW_KEY_F8: return Key::F8; return Key::F4;
case GLFW_KEY_F9: return Key::F9; case GLFW_KEY_F5:
case GLFW_KEY_F10: return Key::F10; return Key::F5;
case GLFW_KEY_F11: return Key::F11; case GLFW_KEY_F6:
case GLFW_KEY_F12: return Key::F12; return Key::F6;
case GLFW_KEY_F7:
return Key::F7;
case GLFW_KEY_F8:
return Key::F8;
case GLFW_KEY_F9:
return Key::F9;
case GLFW_KEY_F10:
return Key::F10;
case GLFW_KEY_F11:
return Key::F11;
case GLFW_KEY_F12:
return Key::F12;
// 特殊键 // 特殊键
case GLFW_KEY_SPACE: return Key::Space; case GLFW_KEY_SPACE:
case GLFW_KEY_ENTER: return Key::Enter; return Key::Space;
case GLFW_KEY_ESCAPE: return Key::Escape; case GLFW_KEY_ENTER:
case GLFW_KEY_TAB: return Key::Tab; return Key::Enter;
case GLFW_KEY_BACKSPACE: return Key::Backspace; case GLFW_KEY_ESCAPE:
case GLFW_KEY_INSERT: return Key::Insert; return Key::Escape;
case GLFW_KEY_DELETE: return Key::Delete; case GLFW_KEY_TAB:
case GLFW_KEY_HOME: return Key::Home; return Key::Tab;
case GLFW_KEY_END: return Key::End; case GLFW_KEY_BACKSPACE:
case GLFW_KEY_PAGE_UP: return Key::PageUp; return Key::Backspace;
case GLFW_KEY_PAGE_DOWN: return Key::PageDown; case GLFW_KEY_INSERT:
return Key::Insert;
case GLFW_KEY_DELETE:
return Key::Delete;
case GLFW_KEY_HOME:
return Key::Home;
case GLFW_KEY_END:
return Key::End;
case GLFW_KEY_PAGE_UP:
return Key::PageUp;
case GLFW_KEY_PAGE_DOWN:
return Key::PageDown;
// 方向键 // 方向键
case GLFW_KEY_UP: return Key::Up; case GLFW_KEY_UP:
case GLFW_KEY_DOWN: return Key::Down; return Key::Up;
case GLFW_KEY_LEFT: return Key::Left; case GLFW_KEY_DOWN:
case GLFW_KEY_RIGHT: return Key::Right; return Key::Down;
case GLFW_KEY_LEFT:
return Key::Left;
case GLFW_KEY_RIGHT:
return Key::Right;
// 修饰键 // 修饰键
case GLFW_KEY_LEFT_SHIFT: return Key::LShift; case GLFW_KEY_LEFT_SHIFT:
case GLFW_KEY_RIGHT_SHIFT: return Key::RShift; return Key::LShift;
case GLFW_KEY_LEFT_CONTROL: return Key::LCtrl; case GLFW_KEY_RIGHT_SHIFT:
case GLFW_KEY_RIGHT_CONTROL: return Key::RCtrl; return Key::RShift;
case GLFW_KEY_LEFT_ALT: return Key::LAlt; case GLFW_KEY_LEFT_CONTROL:
case GLFW_KEY_RIGHT_ALT: return Key::RAlt; return Key::LCtrl;
case GLFW_KEY_RIGHT_CONTROL:
return Key::RCtrl;
case GLFW_KEY_LEFT_ALT:
return Key::LAlt;
case GLFW_KEY_RIGHT_ALT:
return Key::RAlt;
// 锁定键 // 锁定键
case GLFW_KEY_CAPS_LOCK: return Key::CapsLock; case GLFW_KEY_CAPS_LOCK:
case GLFW_KEY_NUM_LOCK: return Key::NumLock; return Key::CapsLock;
case GLFW_KEY_SCROLL_LOCK: return Key::ScrollLock; case GLFW_KEY_NUM_LOCK:
return Key::NumLock;
case GLFW_KEY_SCROLL_LOCK:
return Key::ScrollLock;
default: return Key::None; default:
} return Key::None;
}
} }
GLFWInput::GLFWInput() { GLFWInput::GLFWInput() {
keyCurrent_.fill(false); keyCurrent_.fill(false);
keyPrevious_.fill(false); keyPrevious_.fill(false);
mouseCurrent_.fill(false); mouseCurrent_.fill(false);
mousePrevious_.fill(false); mousePrevious_.fill(false);
gamepadCurrent_.fill(false); gamepadCurrent_.fill(false);
gamepadPrevious_.fill(false); gamepadPrevious_.fill(false);
} }
GLFWInput::~GLFWInput() { GLFWInput::~GLFWInput() { shutdown(); }
shutdown();
}
void GLFWInput::init() { void GLFWInput::init() {
E2D_LOG_INFO("GLFWInput initialized"); E2D_LOG_INFO("GLFWInput initialized");
openGamepad(); openGamepad();
} }
void GLFWInput::shutdown() { void GLFWInput::shutdown() {
closeGamepad(); closeGamepad();
E2D_LOG_INFO("GLFWInput shutdown"); E2D_LOG_INFO("GLFWInput shutdown");
} }
void GLFWInput::update() { void GLFWInput::update() {
// 保存上一帧状态 // 保存上一帧状态
keyPrevious_ = keyCurrent_; keyPrevious_ = keyCurrent_;
mousePrevious_ = mouseCurrent_; mousePrevious_ = mouseCurrent_;
gamepadPrevious_ = gamepadCurrent_; gamepadPrevious_ = gamepadCurrent_;
// 重置增量 // 重置增量
scrollDelta_ = 0.0f; scrollDelta_ = 0.0f;
mouseDelta_ = Vec2{0.0f, 0.0f}; mouseDelta_ = Vec2{0.0f, 0.0f};
// 更新游戏手柄 // 更新游戏手柄
updateGamepad(); updateGamepad();
// 更新键盘状态(通过轮询 GLFW // 更新键盘状态(通过轮询 GLFW
if (window_) { if (window_) {
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; ++i) { for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; ++i) {
Key key = glfwToKey(i); Key key = glfwToKey(i);
if (key != Key::None) { if (key != Key::None) {
int state = glfwGetKey(window_, i); int state = glfwGetKey(window_, i);
keyCurrent_[static_cast<size_t>(key)] = (state == GLFW_PRESS); keyCurrent_[static_cast<size_t>(key)] = (state == GLFW_PRESS);
} }
}
// 更新鼠标按钮状态
for (int i = 0; i < static_cast<int>(Mouse::Count); ++i) {
int glfwButton = GLFW_MOUSE_BUTTON_1 + i;
if (glfwButton <= GLFW_MOUSE_BUTTON_LAST) {
int state = glfwGetMouseButton(window_, glfwButton);
mouseCurrent_[i] = (state == GLFW_PRESS);
}
}
// 获取鼠标位置
double x, y;
glfwGetCursorPos(window_, &x, &y);
mousePos_ = Vec2{static_cast<float>(x), static_cast<float>(y)};
} }
// 更新鼠标按钮状态
for (int i = 0; i < static_cast<int>(Mouse::Count); ++i) {
int glfwButton = GLFW_MOUSE_BUTTON_1 + i;
if (glfwButton <= GLFW_MOUSE_BUTTON_LAST) {
int state = glfwGetMouseButton(window_, glfwButton);
mouseCurrent_[i] = (state == GLFW_PRESS);
}
}
// 获取鼠标位置
double x, y;
glfwGetCursorPos(window_, &x, &y);
mousePos_ = Vec2{static_cast<float>(x), static_cast<float>(y)};
}
} }
bool GLFWInput::down(Key key) const { bool GLFWInput::down(Key key) const {
size_t idx = static_cast<size_t>(key); size_t idx = static_cast<size_t>(key);
if (idx < keyCurrent_.size()) { if (idx < keyCurrent_.size()) {
return keyCurrent_[idx]; return keyCurrent_[idx];
} }
return false; return false;
} }
bool GLFWInput::pressed(Key key) const { bool GLFWInput::pressed(Key key) const {
size_t idx = static_cast<size_t>(key); size_t idx = static_cast<size_t>(key);
if (idx < keyCurrent_.size()) { if (idx < keyCurrent_.size()) {
return keyCurrent_[idx] && !keyPrevious_[idx]; return keyCurrent_[idx] && !keyPrevious_[idx];
} }
return false; return false;
} }
bool GLFWInput::released(Key key) const { bool GLFWInput::released(Key key) const {
size_t idx = static_cast<size_t>(key); size_t idx = static_cast<size_t>(key);
if (idx < keyCurrent_.size()) { if (idx < keyCurrent_.size()) {
return !keyCurrent_[idx] && keyPrevious_[idx]; return !keyCurrent_[idx] && keyPrevious_[idx];
} }
return false; return false;
} }
bool GLFWInput::down(Mouse btn) const { bool GLFWInput::down(Mouse btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < mouseCurrent_.size()) { if (idx < mouseCurrent_.size()) {
return mouseCurrent_[idx]; return mouseCurrent_[idx];
} }
return false; return false;
} }
bool GLFWInput::pressed(Mouse btn) const { bool GLFWInput::pressed(Mouse btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < mouseCurrent_.size()) { if (idx < mouseCurrent_.size()) {
return mouseCurrent_[idx] && !mousePrevious_[idx]; return mouseCurrent_[idx] && !mousePrevious_[idx];
} }
return false; return false;
} }
bool GLFWInput::released(Mouse btn) const { bool GLFWInput::released(Mouse btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < mouseCurrent_.size()) { if (idx < mouseCurrent_.size()) {
return !mouseCurrent_[idx] && mousePrevious_[idx]; return !mouseCurrent_[idx] && mousePrevious_[idx];
} }
return false; return false;
} }
Vec2 GLFWInput::mouse() const { Vec2 GLFWInput::mouse() const { return mousePos_; }
return mousePos_;
Vec2 GLFWInput::mouseDelta() const { return mouseDelta_; }
float GLFWInput::scroll() const { return scroll_; }
float GLFWInput::scrollDelta() const { return scrollDelta_; }
void GLFWInput::setMouse(const Vec2 &pos) {
if (window_) {
glfwSetCursorPos(window_, pos.x, pos.y);
}
} }
Vec2 GLFWInput::mouseDelta() const { bool GLFWInput::gamepad() const { return gamepadId_ != -1; }
return mouseDelta_;
}
float GLFWInput::scroll() const {
return scroll_;
}
float GLFWInput::scrollDelta() const {
return scrollDelta_;
}
void GLFWInput::setMouse(const Vec2& pos) {
if (window_) {
glfwSetCursorPos(window_, pos.x, pos.y);
}
}
bool GLFWInput::gamepad() const {
return gamepadId_ != -1;
}
bool GLFWInput::down(Gamepad btn) const { bool GLFWInput::down(Gamepad btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < gamepadCurrent_.size()) { if (idx < gamepadCurrent_.size()) {
return gamepadCurrent_[idx]; return gamepadCurrent_[idx];
} }
return false; return false;
} }
bool GLFWInput::pressed(Gamepad btn) const { bool GLFWInput::pressed(Gamepad btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < gamepadCurrent_.size()) { if (idx < gamepadCurrent_.size()) {
return gamepadCurrent_[idx] && !gamepadPrevious_[idx]; return gamepadCurrent_[idx] && !gamepadPrevious_[idx];
} }
return false; return false;
} }
bool GLFWInput::released(Gamepad btn) const { bool GLFWInput::released(Gamepad btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < gamepadCurrent_.size()) { if (idx < gamepadCurrent_.size()) {
return !gamepadCurrent_[idx] && gamepadPrevious_[idx]; return !gamepadCurrent_[idx] && gamepadPrevious_[idx];
} }
return false; return false;
} }
Vec2 GLFWInput::leftStick() const { Vec2 GLFWInput::leftStick() const { return leftStick_; }
return leftStick_;
}
Vec2 GLFWInput::rightStick() const { Vec2 GLFWInput::rightStick() const { return rightStick_; }
return rightStick_;
}
float GLFWInput::leftTrigger() const { float GLFWInput::leftTrigger() const { return leftTrigger_; }
return leftTrigger_;
}
float GLFWInput::rightTrigger() const { float GLFWInput::rightTrigger() const { return rightTrigger_; }
return rightTrigger_;
}
void GLFWInput::vibrate(float left, float right) { void GLFWInput::vibrate(float left, float right) {
// GLFW 本身不支持震动,需要平台特定的代码 // GLFW 本身不支持震动,需要平台特定的代码
// 这里可以扩展为使用平台特定的 API // 这里可以扩展为使用平台特定的 API
(void)left; (void)left;
(void)right; (void)right;
} }
bool GLFWInput::touching() const { bool GLFWInput::touching() const { return false; }
return false;
}
int GLFWInput::touchCount() const { int GLFWInput::touchCount() const { return 0; }
return 0;
}
Vec2 GLFWInput::touch(int index) const { Vec2 GLFWInput::touch(int index) const {
(void)index; (void)index;
return Vec2{0.0f, 0.0f}; return Vec2{0.0f, 0.0f};
} }
TouchPoint GLFWInput::touchPoint(int index) const { TouchPoint GLFWInput::touchPoint(int index) const {
(void)index; (void)index;
return TouchPoint{}; return TouchPoint{};
} }
// 事件处理函数 // 事件处理函数
void GLFWInput::handleKeyEvent(int key, int scancode, int action, int mods) { void GLFWInput::handleKeyEvent(int key, int scancode, int action, int mods) {
(void)scancode; (void)scancode;
(void)mods; (void)mods;
Key eKey = glfwToKey(key); Key eKey = glfwToKey(key);
if (eKey != Key::None) { if (eKey != Key::None) {
size_t idx = static_cast<size_t>(eKey); size_t idx = static_cast<size_t>(eKey);
if (action == GLFW_PRESS) { if (action == GLFW_PRESS) {
keyCurrent_[idx] = true; keyCurrent_[idx] = true;
} else if (action == GLFW_RELEASE) { } else if (action == GLFW_RELEASE) {
keyCurrent_[idx] = false; keyCurrent_[idx] = false;
}
} }
}
} }
void GLFWInput::handleMouseButtonEvent(int button, int action, int mods) { void GLFWInput::handleMouseButtonEvent(int button, int action, int mods) {
(void)mods; (void)mods;
if (button >= GLFW_MOUSE_BUTTON_1 && button <= GLFW_MOUSE_BUTTON_LAST) { if (button >= GLFW_MOUSE_BUTTON_1 && button <= GLFW_MOUSE_BUTTON_LAST) {
size_t idx = static_cast<size_t>(button - GLFW_MOUSE_BUTTON_1); size_t idx = static_cast<size_t>(button - GLFW_MOUSE_BUTTON_1);
if (idx < mouseCurrent_.size()) { if (idx < mouseCurrent_.size()) {
if (action == GLFW_PRESS) { if (action == GLFW_PRESS) {
mouseCurrent_[idx] = true; mouseCurrent_[idx] = true;
} else if (action == GLFW_RELEASE) { } else if (action == GLFW_RELEASE) {
mouseCurrent_[idx] = false; mouseCurrent_[idx] = false;
} }
}
} }
}
} }
void GLFWInput::handleCursorPosEvent(double xpos, double ypos) { void GLFWInput::handleCursorPosEvent(double xpos, double ypos) {
Vec2 newPos{static_cast<float>(xpos), static_cast<float>(ypos)}; Vec2 newPos{static_cast<float>(xpos), static_cast<float>(ypos)};
mouseDelta_ = newPos - mousePos_; mouseDelta_ = newPos - mousePos_;
mousePos_ = newPos; mousePos_ = newPos;
} }
void GLFWInput::handleScrollEvent(double xoffset, double yoffset) { void GLFWInput::handleScrollEvent(double xoffset, double yoffset) {
(void)xoffset; (void)xoffset;
scroll_ += static_cast<float>(yoffset); scroll_ += static_cast<float>(yoffset);
scrollDelta_ += static_cast<float>(yoffset); scrollDelta_ += static_cast<float>(yoffset);
} }
void GLFWInput::handleJoystickEvent(int jid, int event) { void GLFWInput::handleJoystickEvent(int jid, int event) {
if (event == GLFW_CONNECTED) { if (event == GLFW_CONNECTED) {
E2D_LOG_INFO("Gamepad connected: {}", jid); E2D_LOG_INFO("Gamepad connected: {}", jid);
if (gamepadId_ == -1) { if (gamepadId_ == -1) {
openGamepad(); openGamepad();
}
} else if (event == GLFW_DISCONNECTED) {
if (jid == gamepadId_) {
E2D_LOG_INFO("Gamepad disconnected: {}", jid);
closeGamepad();
}
} }
} else if (event == GLFW_DISCONNECTED) {
if (jid == gamepadId_) {
E2D_LOG_INFO("Gamepad disconnected: {}", jid);
closeGamepad();
}
}
} }
void GLFWInput::updateGamepad() { void GLFWInput::updateGamepad() {
if (gamepadId_ == -1) { if (gamepadId_ == -1) {
return; return;
} }
GLFWgamepadstate state; GLFWgamepadstate state;
if (!glfwGetGamepadState(gamepadId_, &state)) { if (!glfwGetGamepadState(gamepadId_, &state)) {
return; return;
} }
// 更新按钮状态 // 更新按钮状态
gamepadCurrent_[static_cast<size_t>(Gamepad::A)] = state.buttons[GLFW_GAMEPAD_BUTTON_A] == GLFW_PRESS; gamepadCurrent_[static_cast<size_t>(Gamepad::A)] =
gamepadCurrent_[static_cast<size_t>(Gamepad::B)] = state.buttons[GLFW_GAMEPAD_BUTTON_B] == GLFW_PRESS; state.buttons[GLFW_GAMEPAD_BUTTON_A] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::X)] = state.buttons[GLFW_GAMEPAD_BUTTON_X] == GLFW_PRESS; gamepadCurrent_[static_cast<size_t>(Gamepad::B)] =
gamepadCurrent_[static_cast<size_t>(Gamepad::Y)] = state.buttons[GLFW_GAMEPAD_BUTTON_Y] == GLFW_PRESS; state.buttons[GLFW_GAMEPAD_BUTTON_B] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::Back)] = state.buttons[GLFW_GAMEPAD_BUTTON_BACK] == GLFW_PRESS; gamepadCurrent_[static_cast<size_t>(Gamepad::X)] =
gamepadCurrent_[static_cast<size_t>(Gamepad::Start)] = state.buttons[GLFW_GAMEPAD_BUTTON_START] == GLFW_PRESS; state.buttons[GLFW_GAMEPAD_BUTTON_X] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::LStick)] = state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB] == GLFW_PRESS; gamepadCurrent_[static_cast<size_t>(Gamepad::Y)] =
gamepadCurrent_[static_cast<size_t>(Gamepad::RStick)] = state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB] == GLFW_PRESS; state.buttons[GLFW_GAMEPAD_BUTTON_Y] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::LB)] = state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] == GLFW_PRESS; gamepadCurrent_[static_cast<size_t>(Gamepad::Back)] =
gamepadCurrent_[static_cast<size_t>(Gamepad::RB)] = state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] == GLFW_PRESS; state.buttons[GLFW_GAMEPAD_BUTTON_BACK] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::DUp)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP] == GLFW_PRESS; gamepadCurrent_[static_cast<size_t>(Gamepad::Start)] =
gamepadCurrent_[static_cast<size_t>(Gamepad::DDown)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN] == GLFW_PRESS; state.buttons[GLFW_GAMEPAD_BUTTON_START] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::DLeft)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT] == GLFW_PRESS; gamepadCurrent_[static_cast<size_t>(Gamepad::LStick)] =
gamepadCurrent_[static_cast<size_t>(Gamepad::DRight)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] == GLFW_PRESS; state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::Guide)] = state.buttons[GLFW_GAMEPAD_BUTTON_GUIDE] == GLFW_PRESS; gamepadCurrent_[static_cast<size_t>(Gamepad::RStick)] =
state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::LB)] =
state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::RB)] =
state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::DUp)] =
state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::DDown)] =
state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::DLeft)] =
state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::DRight)] =
state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] == GLFW_PRESS;
gamepadCurrent_[static_cast<size_t>(Gamepad::Guide)] =
state.buttons[GLFW_GAMEPAD_BUTTON_GUIDE] == GLFW_PRESS;
// 更新摇杆值(应用死区) // 更新摇杆值(应用死区)
leftStick_.x = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_X]); leftStick_.x = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_X]);
leftStick_.y = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]); leftStick_.y = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]);
rightStick_.x = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]); rightStick_.x = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]);
rightStick_.y = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]); rightStick_.y = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]);
// 更新扳机值(范围 [0, 1] // 更新扳机值(范围 [0, 1]
leftTrigger_ = (state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] + 1.0f) * 0.5f; leftTrigger_ = (state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] + 1.0f) * 0.5f;
rightTrigger_ = (state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] + 1.0f) * 0.5f; rightTrigger_ = (state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] + 1.0f) * 0.5f;
} }
void GLFWInput::openGamepad() { void GLFWInput::openGamepad() {
for (int jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; ++jid) { for (int jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; ++jid) {
if (glfwJoystickPresent(jid) && glfwJoystickIsGamepad(jid)) { if (glfwJoystickPresent(jid) && glfwJoystickIsGamepad(jid)) {
gamepadId_ = jid; gamepadId_ = jid;
E2D_LOG_INFO("Gamepad opened: {}", glfwGetGamepadName(jid)); E2D_LOG_INFO("Gamepad opened: {}", glfwGetGamepadName(jid));
return; return;
}
} }
}
} }
void GLFWInput::closeGamepad() { void GLFWInput::closeGamepad() {
if (gamepadId_ != -1) { if (gamepadId_ != -1) {
gamepadId_ = -1; gamepadId_ = -1;
gamepadCurrent_.fill(false); gamepadCurrent_.fill(false);
gamepadPrevious_.fill(false); gamepadPrevious_.fill(false);
leftStick_ = Vec2{0.0f, 0.0f}; leftStick_ = Vec2{0.0f, 0.0f};
rightStick_ = Vec2{0.0f, 0.0f}; rightStick_ = Vec2{0.0f, 0.0f};
leftTrigger_ = 0.0f; leftTrigger_ = 0.0f;
rightTrigger_ = 0.0f; rightTrigger_ = 0.0f;
} }
} }
float GLFWInput::applyDeadzone(float value) const { float GLFWInput::applyDeadzone(float value) const {
if (std::abs(value) < deadzone_) { if (std::abs(value) < deadzone_) {
return 0.0f; return 0.0f;
} }
float sign = value >= 0.0f ? 1.0f : -1.0f; float sign = value >= 0.0f ? 1.0f : -1.0f;
return sign * (std::abs(value) - deadzone_) / (1.0f - deadzone_); return sign * (std::abs(value) - deadzone_) / (1.0f - deadzone_);
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,449 +1,441 @@
#include "glfw_window.h" #include "glfw_window.h"
#include "glfw_input.h" #include "glfw_input.h"
#include <extra2d/utils/logger.h> #include <extra2d/core/service_locator.h>
#include <extra2d/services/logger_service.h>
#include <glad/glad.h> #include <glad/glad.h>
namespace extra2d { namespace extra2d {
GLFWWindow::GLFWWindow() {} GLFWWindow::GLFWWindow() {}
GLFWWindow::~GLFWWindow() { GLFWWindow::~GLFWWindow() { destroy(); }
destroy();
}
bool GLFWWindow::create(const std::string& title, int width, int height, bool vsync) { bool GLFWWindow::create(const std::string &title, int width, int height,
if (!initGLFW()) { bool vsync) {
return false; if (!initGLFW()) {
} return false;
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_DEPTH_BITS, 24); glfwWindowHint(GLFW_DEPTH_BITS, 24);
glfwWindowHint(GLFW_STENCIL_BITS, 8); glfwWindowHint(GLFW_STENCIL_BITS, 8);
#ifdef __SWITCH__ #ifdef __SWITCH__
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
fullscreen_ = true; fullscreen_ = true;
#else #else
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
#endif #endif
GLFWmonitor* monitor = nullptr; GLFWmonitor *monitor = nullptr;
#ifdef __SWITCH__ #ifdef __SWITCH__
monitor = glfwGetPrimaryMonitor(); monitor = glfwGetPrimaryMonitor();
#endif #endif
glfwWindow_ = glfwCreateWindow( glfwWindow_ =
width, height, glfwCreateWindow(width, height, title.c_str(), monitor, nullptr);
title.c_str(),
monitor,
nullptr
);
if (!glfwWindow_) { if (!glfwWindow_) {
E2D_LOG_ERROR("Failed to create GLFW window"); E2D_LOG_ERROR("Failed to create GLFW window");
deinitGLFW(); deinitGLFW();
return false; return false;
} }
#ifndef __SWITCH__ #ifndef __SWITCH__
if (!fullscreen_ && !monitor) { if (!fullscreen_ && !monitor) {
GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor(); GLFWmonitor *primaryMonitor = glfwGetPrimaryMonitor();
if (primaryMonitor) { if (primaryMonitor) {
const GLFWvidmode* mode = glfwGetVideoMode(primaryMonitor); const GLFWvidmode *mode = glfwGetVideoMode(primaryMonitor);
if (mode) { if (mode) {
int screenWidth = mode->width; int screenWidth = mode->width;
int screenHeight = mode->height; int screenHeight = mode->height;
int windowX = (screenWidth - width) / 2; int windowX = (screenWidth - width) / 2;
int windowY = (screenHeight - height) / 2; int windowY = (screenHeight - height) / 2;
glfwSetWindowPos(glfwWindow_, windowX, windowY); glfwSetWindowPos(glfwWindow_, windowX, windowY);
} }
}
} }
}
#endif #endif
glfwMakeContextCurrent(glfwWindow_); glfwMakeContextCurrent(glfwWindow_);
if (!gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress)) { if (!gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress)) {
E2D_LOG_ERROR("Failed to initialize GLAD GLES2"); E2D_LOG_ERROR("Failed to initialize GLAD GLES2");
glfwDestroyWindow(glfwWindow_); glfwDestroyWindow(glfwWindow_);
glfwWindow_ = nullptr; glfwWindow_ = nullptr;
deinitGLFW(); deinitGLFW();
return false; return false;
} }
glfwSwapInterval(vsync ? 1 : 0); glfwSwapInterval(vsync ? 1 : 0);
vsync_ = vsync; vsync_ = vsync;
glfwGetWindowSize(glfwWindow_, &width_, &height_); glfwGetWindowSize(glfwWindow_, &width_, &height_);
updateContentScale(); updateContentScale();
glfwSetWindowUserPointer(glfwWindow_, this); glfwSetWindowUserPointer(glfwWindow_, this);
glfwSetFramebufferSizeCallback(glfwWindow_, framebufferSizeCallback); glfwSetFramebufferSizeCallback(glfwWindow_, framebufferSizeCallback);
glfwSetWindowCloseCallback(glfwWindow_, windowCloseCallback); glfwSetWindowCloseCallback(glfwWindow_, windowCloseCallback);
glfwSetWindowFocusCallback(glfwWindow_, windowFocusCallback); glfwSetWindowFocusCallback(glfwWindow_, windowFocusCallback);
glfwSetWindowIconifyCallback(glfwWindow_, windowIconifyCallback); glfwSetWindowIconifyCallback(glfwWindow_, windowIconifyCallback);
glfwSetCursorPosCallback(glfwWindow_, cursorPosCallback); glfwSetCursorPosCallback(glfwWindow_, cursorPosCallback);
glfwSetMouseButtonCallback(glfwWindow_, mouseButtonCallback); glfwSetMouseButtonCallback(glfwWindow_, mouseButtonCallback);
glfwSetScrollCallback(glfwWindow_, scrollCallback); glfwSetScrollCallback(glfwWindow_, scrollCallback);
glfwSetKeyCallback(glfwWindow_, keyCallback); glfwSetKeyCallback(glfwWindow_, keyCallback);
glfwSetJoystickCallback(joystickCallback); glfwSetJoystickCallback(joystickCallback);
input_ = makeUnique<GLFWInput>(); input_ = makeUnique<GLFWInput>();
input_->setWindow(glfwWindow_); input_->setWindow(glfwWindow_);
input_->init(); input_->init();
E2D_LOG_INFO("GLFW window created: {}x{}", width_, height_); E2D_LOG_INFO("GLFW window created: {}x{}", width_, height_);
E2D_LOG_INFO(" Platform: OpenGL ES 3.2"); E2D_LOG_INFO(" Platform: OpenGL ES 3.2");
return true; return true;
} }
void GLFWWindow::destroy() { void GLFWWindow::destroy() {
if (input_) { if (input_) {
input_->shutdown(); input_->shutdown();
input_.reset(); input_.reset();
} }
if (glfwWindow_) { if (glfwWindow_) {
glfwDestroyWindow(glfwWindow_); glfwDestroyWindow(glfwWindow_);
glfwWindow_ = nullptr; glfwWindow_ = nullptr;
} }
deinitGLFW(); deinitGLFW();
} }
void GLFWWindow::poll() { void GLFWWindow::poll() {
if (!glfwWindow_) return; if (!glfwWindow_)
return;
if (input_) { if (input_) {
input_->update(); input_->update();
} }
glfwPollEvents(); glfwPollEvents();
} }
void GLFWWindow::swap() { void GLFWWindow::swap() {
if (glfwWindow_) { if (glfwWindow_) {
glfwSwapBuffers(glfwWindow_); glfwSwapBuffers(glfwWindow_);
} }
} }
bool GLFWWindow::shouldClose() const { bool GLFWWindow::shouldClose() const {
if (!glfwWindow_) return true; if (!glfwWindow_)
return shouldClose_ || glfwWindowShouldClose(glfwWindow_); return true;
return shouldClose_ || glfwWindowShouldClose(glfwWindow_);
} }
void GLFWWindow::close() { void GLFWWindow::close() {
shouldClose_ = true; shouldClose_ = true;
if (glfwWindow_) { if (glfwWindow_) {
glfwSetWindowShouldClose(glfwWindow_, GLFW_TRUE); glfwSetWindowShouldClose(glfwWindow_, GLFW_TRUE);
} }
} }
void GLFWWindow::setTitle(const std::string& title) { void GLFWWindow::setTitle(const std::string &title) {
if (glfwWindow_) { if (glfwWindow_) {
glfwSetWindowTitle(glfwWindow_, title.c_str()); glfwSetWindowTitle(glfwWindow_, title.c_str());
} }
} }
void GLFWWindow::setSize(int w, int h) { void GLFWWindow::setSize(int w, int h) {
if (glfwWindow_) { if (glfwWindow_) {
glfwSetWindowSize(glfwWindow_, w, h); glfwSetWindowSize(glfwWindow_, w, h);
width_ = w; width_ = w;
height_ = h; height_ = h;
} }
} }
void GLFWWindow::setPos(int x, int y) { void GLFWWindow::setPos(int x, int y) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (glfwWindow_) { if (glfwWindow_) {
glfwSetWindowPos(glfwWindow_, x, y); glfwSetWindowPos(glfwWindow_, x, y);
} }
#else #else
(void)x; (void)x;
(void)y; (void)y;
#endif #endif
} }
void GLFWWindow::setFullscreen(bool fs) { void GLFWWindow::setFullscreen(bool fs) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (!glfwWindow_) return; if (!glfwWindow_)
return;
if (fs == fullscreen_) return; if (fs == fullscreen_)
return;
if (fs) { if (fs) {
GLFWmonitor* monitor = glfwGetPrimaryMonitor(); GLFWmonitor *monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(monitor); const GLFWvidmode *mode = glfwGetVideoMode(monitor);
glfwSetWindowMonitor(glfwWindow_, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); glfwSetWindowMonitor(glfwWindow_, monitor, 0, 0, mode->width, mode->height,
} else { mode->refreshRate);
glfwSetWindowMonitor(glfwWindow_, nullptr, 100, 100, 1280, 720, 0); } else {
} glfwSetWindowMonitor(glfwWindow_, nullptr, 100, 100, 1280, 720, 0);
fullscreen_ = fs; }
glfwGetWindowSize(glfwWindow_, &width_, &height_); fullscreen_ = fs;
updateContentScale(); glfwGetWindowSize(glfwWindow_, &width_, &height_);
updateContentScale();
#else #else
(void)fs; (void)fs;
#endif #endif
} }
void GLFWWindow::setVSync(bool vsync) { void GLFWWindow::setVSync(bool vsync) {
if (glfwWindow_) { if (glfwWindow_) {
glfwSwapInterval(vsync ? 1 : 0); glfwSwapInterval(vsync ? 1 : 0);
vsync_ = vsync; vsync_ = vsync;
} }
} }
void GLFWWindow::setVisible(bool visible) { void GLFWWindow::setVisible(bool visible) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (glfwWindow_) { if (glfwWindow_) {
if (visible) { if (visible) {
glfwShowWindow(glfwWindow_); glfwShowWindow(glfwWindow_);
} else { } else {
glfwHideWindow(glfwWindow_); glfwHideWindow(glfwWindow_);
}
} }
}
#else #else
(void)visible; (void)visible;
#endif #endif
} }
int GLFWWindow::width() const { int GLFWWindow::width() const { return width_; }
return width_;
}
int GLFWWindow::height() const { int GLFWWindow::height() const { return height_; }
return height_;
}
Size GLFWWindow::size() const { Size GLFWWindow::size() const {
return Size(static_cast<float>(width_), static_cast<float>(height_)); return Size(static_cast<float>(width_), static_cast<float>(height_));
} }
Vec2 GLFWWindow::pos() const { Vec2 GLFWWindow::pos() const {
int x = 0, y = 0; int x = 0, y = 0;
#ifndef __SWITCH__ #ifndef __SWITCH__
if (glfwWindow_) { if (glfwWindow_) {
glfwGetWindowPos(glfwWindow_, &x, &y); glfwGetWindowPos(glfwWindow_, &x, &y);
} }
#endif #endif
return Vec2(static_cast<float>(x), static_cast<float>(y)); return Vec2(static_cast<float>(x), static_cast<float>(y));
} }
bool GLFWWindow::fullscreen() const { bool GLFWWindow::fullscreen() const { return fullscreen_; }
return fullscreen_;
}
bool GLFWWindow::vsync() const { bool GLFWWindow::vsync() const { return vsync_; }
return vsync_;
}
bool GLFWWindow::focused() const { bool GLFWWindow::focused() const { return focused_; }
return focused_;
}
bool GLFWWindow::minimized() const { bool GLFWWindow::minimized() const { return minimized_; }
return minimized_;
}
float GLFWWindow::scaleX() const { float GLFWWindow::scaleX() const { return scaleX_; }
return scaleX_;
}
float GLFWWindow::scaleY() const { float GLFWWindow::scaleY() const { return scaleY_; }
return scaleY_;
}
void GLFWWindow::setCursor(Cursor cursor) { void GLFWWindow::setCursor(Cursor cursor) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (!glfwWindow_) return; if (!glfwWindow_)
return;
if (cursor == Cursor::Hidden) { if (cursor == Cursor::Hidden) {
glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
return; return;
} }
glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_NORMAL); glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
GLFWcursor* glfwCursor = nullptr; GLFWcursor *glfwCursor = nullptr;
switch (cursor) { switch (cursor) {
case Cursor::Arrow: case Cursor::Arrow:
glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
break; break;
case Cursor::IBeam: case Cursor::IBeam:
glfwCursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); glfwCursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
break; break;
case Cursor::Crosshair: case Cursor::Crosshair:
glfwCursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR); glfwCursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR);
break; break;
case Cursor::Hand: case Cursor::Hand:
glfwCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR); glfwCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
break; break;
case Cursor::HResize: case Cursor::HResize:
glfwCursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); glfwCursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
break; break;
case Cursor::VResize: case Cursor::VResize:
glfwCursor = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); glfwCursor = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
break; break;
default: default:
glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
break; break;
} }
if (glfwCursor) { if (glfwCursor) {
glfwSetCursor(glfwWindow_, glfwCursor); glfwSetCursor(glfwWindow_, glfwCursor);
} }
#else #else
(void)cursor; (void)cursor;
#endif #endif
} }
void GLFWWindow::showCursor(bool show) { void GLFWWindow::showCursor(bool show) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (glfwWindow_) { if (glfwWindow_) {
glfwSetInputMode(glfwWindow_, GLFW_CURSOR, show ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN); glfwSetInputMode(glfwWindow_, GLFW_CURSOR,
cursorVisible_ = show; show ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN);
} cursorVisible_ = show;
}
#else #else
(void)show; (void)show;
#endif #endif
} }
void GLFWWindow::lockCursor(bool lock) { void GLFWWindow::lockCursor(bool lock) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (glfwWindow_) { if (glfwWindow_) {
glfwSetInputMode(glfwWindow_, GLFW_CURSOR, lock ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL); glfwSetInputMode(glfwWindow_, GLFW_CURSOR,
cursorLocked_ = lock; lock ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
} cursorLocked_ = lock;
}
#else #else
(void)lock; (void)lock;
#endif #endif
} }
IInput* GLFWWindow::input() const { IInput *GLFWWindow::input() const { return input_.get(); }
return input_.get();
}
void GLFWWindow::onResize(ResizeCb cb) { void GLFWWindow::onResize(ResizeCb cb) { resizeCb_ = cb; }
resizeCb_ = cb;
}
void GLFWWindow::onClose(CloseCb cb) { void GLFWWindow::onClose(CloseCb cb) { closeCb_ = cb; }
closeCb_ = cb;
}
void GLFWWindow::onFocus(FocusCb cb) { void GLFWWindow::onFocus(FocusCb cb) { focusCb_ = cb; }
focusCb_ = cb;
}
void* GLFWWindow::native() const { void *GLFWWindow::native() const { return glfwWindow_; }
return glfwWindow_;
}
bool GLFWWindow::initGLFW() { bool GLFWWindow::initGLFW() {
static int glfwInitCount = 0; static int glfwInitCount = 0;
if (glfwInitCount == 0) { if (glfwInitCount == 0) {
if (!glfwInit()) { if (!glfwInit()) {
E2D_LOG_ERROR("Failed to initialize GLFW"); E2D_LOG_ERROR("Failed to initialize GLFW");
return false; return false;
}
glfwInitCount++;
} }
return true; glfwInitCount++;
}
return true;
} }
void GLFWWindow::deinitGLFW() { void GLFWWindow::deinitGLFW() {
static int glfwInitCount = 1; static int glfwInitCount = 1;
glfwInitCount--; glfwInitCount--;
if (glfwInitCount == 0) { if (glfwInitCount == 0) {
glfwTerminate(); glfwTerminate();
} }
} }
void GLFWWindow::updateContentScale() { void GLFWWindow::updateContentScale() {
if (glfwWindow_) { if (glfwWindow_) {
int fbWidth, fbHeight; int fbWidth, fbHeight;
glfwGetFramebufferSize(glfwWindow_, &fbWidth, &fbHeight); glfwGetFramebufferSize(glfwWindow_, &fbWidth, &fbHeight);
scaleX_ = fbWidth > 0 ? static_cast<float>(fbWidth) / width_ : 1.0f; scaleX_ = fbWidth > 0 ? static_cast<float>(fbWidth) / width_ : 1.0f;
scaleY_ = fbHeight > 0 ? static_cast<float>(fbHeight) / height_ : 1.0f; scaleY_ = fbHeight > 0 ? static_cast<float>(fbHeight) / height_ : 1.0f;
} }
} }
// 静态回调函数 // 静态回调函数
void GLFWWindow::framebufferSizeCallback(GLFWwindow* window, int width, int height) { void GLFWWindow::framebufferSizeCallback(GLFWwindow *window, int width,
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window)); int height) {
if (self) { GLFWWindow *self =
self->width_ = width; static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
self->height_ = height; if (self) {
self->updateContentScale(); self->width_ = width;
if (self->resizeCb_) { self->height_ = height;
self->resizeCb_(width, height); self->updateContentScale();
} if (self->resizeCb_) {
self->resizeCb_(width, height);
} }
}
} }
void GLFWWindow::windowCloseCallback(GLFWwindow* window) { void GLFWWindow::windowCloseCallback(GLFWwindow *window) {
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window)); GLFWWindow *self =
if (self) { static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
self->shouldClose_ = true; if (self) {
if (self->closeCb_) { self->shouldClose_ = true;
self->closeCb_(); if (self->closeCb_) {
} self->closeCb_();
} }
}
} }
void GLFWWindow::windowFocusCallback(GLFWwindow* window, int focused) { void GLFWWindow::windowFocusCallback(GLFWwindow *window, int focused) {
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window)); GLFWWindow *self =
if (self) { static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
self->focused_ = (focused == GLFW_TRUE); if (self) {
if (self->focusCb_) { self->focused_ = (focused == GLFW_TRUE);
self->focusCb_(self->focused_); if (self->focusCb_) {
} self->focusCb_(self->focused_);
} }
}
} }
void GLFWWindow::windowIconifyCallback(GLFWwindow* window, int iconified) { void GLFWWindow::windowIconifyCallback(GLFWwindow *window, int iconified) {
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window)); GLFWWindow *self =
if (self) { static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
self->minimized_ = (iconified == GLFW_TRUE); if (self) {
} self->minimized_ = (iconified == GLFW_TRUE);
}
} }
void GLFWWindow::cursorPosCallback(GLFWwindow* window, double xpos, double ypos) { void GLFWWindow::cursorPosCallback(GLFWwindow *window, double xpos,
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window)); double ypos) {
if (self && self->input_) { GLFWWindow *self =
self->input_->handleCursorPosEvent(xpos, ypos); static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
} if (self && self->input_) {
self->input_->handleCursorPosEvent(xpos, ypos);
}
} }
void GLFWWindow::mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { void GLFWWindow::mouseButtonCallback(GLFWwindow *window, int button, int action,
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window)); int mods) {
if (self && self->input_) { GLFWWindow *self =
self->input_->handleMouseButtonEvent(button, action, mods); static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
} if (self && self->input_) {
self->input_->handleMouseButtonEvent(button, action, mods);
}
} }
void GLFWWindow::scrollCallback(GLFWwindow* window, double xoffset, double yoffset) { void GLFWWindow::scrollCallback(GLFWwindow *window, double xoffset,
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window)); double yoffset) {
if (self && self->input_) { GLFWWindow *self =
self->input_->handleScrollEvent(xoffset, yoffset); static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
} if (self && self->input_) {
self->input_->handleScrollEvent(xoffset, yoffset);
}
} }
void GLFWWindow::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { void GLFWWindow::keyCallback(GLFWwindow *window, int key, int scancode,
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window)); int action, int mods) {
if (self && self->input_) { GLFWWindow *self =
self->input_->handleKeyEvent(key, scancode, action, mods); static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
} if (self && self->input_) {
self->input_->handleKeyEvent(key, scancode, action, mods);
}
} }
void GLFWWindow::joystickCallback(int jid, int event) { void GLFWWindow::joystickCallback(int jid, int event) {
// 通过全局回调找到对应的窗口实例 // 通过全局回调找到对应的窗口实例
// 由于 GLFW 的 joystick 回调没有窗口参数,我们需要其他方式处理 // 由于 GLFW 的 joystick 回调没有窗口参数,我们需要其他方式处理
// 这里简化处理,让输入系统在 update() 中轮询 // 这里简化处理,让输入系统在 update() 中轮询
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,451 +1,454 @@
#include "sdl2_input.h" #include "sdl2_input.h"
#include <extra2d/utils/logger.h>
#include <cmath> #include <cmath>
#include <extra2d/core/service_locator.h>
#include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
SDL2Input::SDL2Input() { SDL2Input::SDL2Input() {
keyCurrent_.fill(false); keyCurrent_.fill(false);
keyPrevious_.fill(false); keyPrevious_.fill(false);
mouseCurrent_.fill(false); mouseCurrent_.fill(false);
mousePrevious_.fill(false); mousePrevious_.fill(false);
gamepadCurrent_.fill(false); gamepadCurrent_.fill(false);
gamepadPrevious_.fill(false); gamepadPrevious_.fill(false);
} }
SDL2Input::~SDL2Input() { SDL2Input::~SDL2Input() { shutdown(); }
shutdown();
}
void SDL2Input::init() { void SDL2Input::init() {
E2D_LOG_INFO("SDL2Input initialized"); E2D_LOG_INFO("SDL2Input initialized");
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) { if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) {
E2D_LOG_WARN("Failed to init gamecontroller subsystem: {}", SDL_GetError()); E2D_LOG_WARN("Failed to init gamecontroller subsystem: {}", SDL_GetError());
} }
openGamepad(); openGamepad();
} }
void SDL2Input::shutdown() { void SDL2Input::shutdown() {
closeGamepad(); closeGamepad();
E2D_LOG_INFO("SDL2Input shutdown"); E2D_LOG_INFO("SDL2Input shutdown");
} }
void SDL2Input::update() { void SDL2Input::update() {
keyPrevious_ = keyCurrent_; keyPrevious_ = keyCurrent_;
mousePrevious_ = mouseCurrent_; mousePrevious_ = mouseCurrent_;
gamepadPrevious_ = gamepadCurrent_; gamepadPrevious_ = gamepadCurrent_;
scrollDelta_ = 0.0f; scrollDelta_ = 0.0f;
mouseDelta_ = Vec2{0.0f, 0.0f}; mouseDelta_ = Vec2{0.0f, 0.0f};
updateGamepad(); updateGamepad();
} }
void SDL2Input::setEventCallback(EventCallback callback) { void SDL2Input::setEventCallback(EventCallback callback) {
eventCallback_ = std::move(callback); eventCallback_ = std::move(callback);
} }
void SDL2Input::handleSDLEvent(const SDL_Event& event) { void SDL2Input::handleSDLEvent(const SDL_Event &event) {
switch (event.type) { switch (event.type) {
case SDL_KEYDOWN: { case SDL_KEYDOWN: {
int key = event.key.keysym.scancode; int key = event.key.keysym.scancode;
if (key >= 0 && key < static_cast<int>(Key::Count)) { if (key >= 0 && key < static_cast<int>(Key::Count)) {
if (!keyCurrent_[key]) { if (!keyCurrent_[key]) {
keyCurrent_[key] = true; keyCurrent_[key] = true;
Event e = Event::createKeyPress( Event e = Event::createKeyPress(event.key.keysym.sym,
event.key.keysym.sym, event.key.keysym.scancode,
event.key.keysym.scancode, event.key.keysym.mod);
event.key.keysym.mod dispatchEvent(e);
); }
dispatchEvent(e);
}
}
break;
}
case SDL_KEYUP: {
int key = event.key.keysym.scancode;
if (key >= 0 && key < static_cast<int>(Key::Count)) {
keyCurrent_[key] = false;
Event e = Event::createKeyRelease(
event.key.keysym.sym,
event.key.keysym.scancode,
event.key.keysym.mod
);
dispatchEvent(e);
}
break;
}
case SDL_MOUSEBUTTONDOWN: {
int btn = event.button.button - 1;
if (btn >= 0 && btn < static_cast<int>(Mouse::Count)) {
mouseCurrent_[btn] = true;
Vec2 pos{static_cast<float>(event.button.x),
static_cast<float>(event.button.y)};
Event e = Event::createMouseButtonPress(btn, 0, pos);
dispatchEvent(e);
}
break;
}
case SDL_MOUSEBUTTONUP: {
int btn = event.button.button - 1;
if (btn >= 0 && btn < static_cast<int>(Mouse::Count)) {
mouseCurrent_[btn] = false;
Vec2 pos{static_cast<float>(event.button.x),
static_cast<float>(event.button.y)};
Event e = Event::createMouseButtonRelease(btn, 0, pos);
dispatchEvent(e);
}
break;
}
case SDL_MOUSEMOTION: {
Vec2 newPos{static_cast<float>(event.motion.x),
static_cast<float>(event.motion.y)};
Vec2 delta{static_cast<float>(event.motion.xrel),
static_cast<float>(event.motion.yrel)};
mouseDelta_ = mouseDelta_ + delta;
mousePos_ = newPos;
Event e = Event::createMouseMove(newPos, delta);
dispatchEvent(e);
break;
}
case SDL_MOUSEWHEEL: {
Vec2 offset{static_cast<float>(event.wheel.x),
static_cast<float>(event.wheel.y)};
Vec2 pos = mousePos_;
scroll_ += event.wheel.y;
scrollDelta_ += event.wheel.y;
Event e = Event::createMouseScroll(offset, pos);
dispatchEvent(e);
break;
}
case SDL_CONTROLLERDEVICEADDED:
E2D_LOG_INFO("Gamepad connected: index {}", event.cdevice.which);
openGamepad();
break;
case SDL_CONTROLLERDEVICEREMOVED:
if (gamepad_ && event.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepad_))) {
E2D_LOG_INFO("Gamepad disconnected");
closeGamepad();
}
break;
case SDL_CONTROLLERBUTTONDOWN:
if (gamepad_) {
int btn = event.cbutton.button;
if (btn >= 0 && btn < static_cast<int>(Gamepad::Count)) {
gamepadCurrent_[btn] = true;
GamepadButtonEvent btnEvent;
btnEvent.gamepadId = gamepadIndex_;
btnEvent.button = btn;
Event e;
e.type = EventType::GamepadButtonPressed;
e.data = btnEvent;
dispatchEvent(e);
}
}
break;
case SDL_CONTROLLERBUTTONUP:
if (gamepad_) {
int btn = event.cbutton.button;
if (btn >= 0 && btn < static_cast<int>(Gamepad::Count)) {
gamepadCurrent_[btn] = false;
GamepadButtonEvent btnEvent;
btnEvent.gamepadId = gamepadIndex_;
btnEvent.button = btn;
Event e;
e.type = EventType::GamepadButtonReleased;
e.data = btnEvent;
dispatchEvent(e);
}
}
break;
default:
break;
} }
break;
}
case SDL_KEYUP: {
int key = event.key.keysym.scancode;
if (key >= 0 && key < static_cast<int>(Key::Count)) {
keyCurrent_[key] = false;
Event e = Event::createKeyRelease(event.key.keysym.sym,
event.key.keysym.scancode,
event.key.keysym.mod);
dispatchEvent(e);
}
break;
}
case SDL_MOUSEBUTTONDOWN: {
int btn = event.button.button - 1;
if (btn >= 0 && btn < static_cast<int>(Mouse::Count)) {
mouseCurrent_[btn] = true;
Vec2 pos{static_cast<float>(event.button.x),
static_cast<float>(event.button.y)};
Event e = Event::createMouseButtonPress(btn, 0, pos);
dispatchEvent(e);
}
break;
}
case SDL_MOUSEBUTTONUP: {
int btn = event.button.button - 1;
if (btn >= 0 && btn < static_cast<int>(Mouse::Count)) {
mouseCurrent_[btn] = false;
Vec2 pos{static_cast<float>(event.button.x),
static_cast<float>(event.button.y)};
Event e = Event::createMouseButtonRelease(btn, 0, pos);
dispatchEvent(e);
}
break;
}
case SDL_MOUSEMOTION: {
Vec2 newPos{static_cast<float>(event.motion.x),
static_cast<float>(event.motion.y)};
Vec2 delta{static_cast<float>(event.motion.xrel),
static_cast<float>(event.motion.yrel)};
mouseDelta_ = mouseDelta_ + delta;
mousePos_ = newPos;
Event e = Event::createMouseMove(newPos, delta);
dispatchEvent(e);
break;
}
case SDL_MOUSEWHEEL: {
Vec2 offset{static_cast<float>(event.wheel.x),
static_cast<float>(event.wheel.y)};
Vec2 pos = mousePos_;
scroll_ += event.wheel.y;
scrollDelta_ += event.wheel.y;
Event e = Event::createMouseScroll(offset, pos);
dispatchEvent(e);
break;
}
case SDL_CONTROLLERDEVICEADDED:
E2D_LOG_INFO("Gamepad connected: index {}", event.cdevice.which);
openGamepad();
break;
case SDL_CONTROLLERDEVICEREMOVED:
if (gamepad_ &&
event.cdevice.which ==
SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepad_))) {
E2D_LOG_INFO("Gamepad disconnected");
closeGamepad();
}
break;
case SDL_CONTROLLERBUTTONDOWN:
if (gamepad_) {
int btn = event.cbutton.button;
if (btn >= 0 && btn < static_cast<int>(Gamepad::Count)) {
gamepadCurrent_[btn] = true;
GamepadButtonEvent btnEvent;
btnEvent.gamepadId = gamepadIndex_;
btnEvent.button = btn;
Event e;
e.type = EventType::GamepadButtonPressed;
e.data = btnEvent;
dispatchEvent(e);
}
}
break;
case SDL_CONTROLLERBUTTONUP:
if (gamepad_) {
int btn = event.cbutton.button;
if (btn >= 0 && btn < static_cast<int>(Gamepad::Count)) {
gamepadCurrent_[btn] = false;
GamepadButtonEvent btnEvent;
btnEvent.gamepadId = gamepadIndex_;
btnEvent.button = btn;
Event e;
e.type = EventType::GamepadButtonReleased;
e.data = btnEvent;
dispatchEvent(e);
}
}
break;
default:
break;
}
} }
void SDL2Input::dispatchEvent(const Event& event) { void SDL2Input::dispatchEvent(const Event &event) {
if (eventCallback_) { if (eventCallback_) {
eventCallback_(event); eventCallback_(event);
} }
} }
bool SDL2Input::down(Key key) const { bool SDL2Input::down(Key key) const {
size_t idx = static_cast<size_t>(key); size_t idx = static_cast<size_t>(key);
if (idx < keyCurrent_.size()) { if (idx < keyCurrent_.size()) {
return keyCurrent_[idx]; return keyCurrent_[idx];
} }
return false; return false;
} }
bool SDL2Input::pressed(Key key) const { bool SDL2Input::pressed(Key key) const {
size_t idx = static_cast<size_t>(key); size_t idx = static_cast<size_t>(key);
if (idx < keyCurrent_.size()) { if (idx < keyCurrent_.size()) {
return keyCurrent_[idx] && !keyPrevious_[idx]; return keyCurrent_[idx] && !keyPrevious_[idx];
} }
return false; return false;
} }
bool SDL2Input::released(Key key) const { bool SDL2Input::released(Key key) const {
size_t idx = static_cast<size_t>(key); size_t idx = static_cast<size_t>(key);
if (idx < keyCurrent_.size()) { if (idx < keyCurrent_.size()) {
return !keyCurrent_[idx] && keyPrevious_[idx]; return !keyCurrent_[idx] && keyPrevious_[idx];
} }
return false; return false;
} }
bool SDL2Input::down(Mouse btn) const { bool SDL2Input::down(Mouse btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < mouseCurrent_.size()) { if (idx < mouseCurrent_.size()) {
return mouseCurrent_[idx]; return mouseCurrent_[idx];
} }
return false; return false;
} }
bool SDL2Input::pressed(Mouse btn) const { bool SDL2Input::pressed(Mouse btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < mouseCurrent_.size()) { if (idx < mouseCurrent_.size()) {
return mouseCurrent_[idx] && !mousePrevious_[idx]; return mouseCurrent_[idx] && !mousePrevious_[idx];
} }
return false; return false;
} }
bool SDL2Input::released(Mouse btn) const { bool SDL2Input::released(Mouse btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < mouseCurrent_.size()) { if (idx < mouseCurrent_.size()) {
return !mouseCurrent_[idx] && mousePrevious_[idx]; return !mouseCurrent_[idx] && mousePrevious_[idx];
} }
return false; return false;
} }
Vec2 SDL2Input::mouse() const { Vec2 SDL2Input::mouse() const { return mousePos_; }
return mousePos_;
Vec2 SDL2Input::mouseDelta() const { return mouseDelta_; }
float SDL2Input::scroll() const { return scroll_; }
float SDL2Input::scrollDelta() const { return scrollDelta_; }
void SDL2Input::setMouse(const Vec2 &pos) {
SDL_WarpMouseInWindow(nullptr, static_cast<int>(pos.x),
static_cast<int>(pos.y));
} }
Vec2 SDL2Input::mouseDelta() const { bool SDL2Input::gamepad() const { return gamepad_ != nullptr; }
return mouseDelta_;
}
float SDL2Input::scroll() const {
return scroll_;
}
float SDL2Input::scrollDelta() const {
return scrollDelta_;
}
void SDL2Input::setMouse(const Vec2& pos) {
SDL_WarpMouseInWindow(nullptr, static_cast<int>(pos.x), static_cast<int>(pos.y));
}
bool SDL2Input::gamepad() const {
return gamepad_ != nullptr;
}
bool SDL2Input::down(Gamepad btn) const { bool SDL2Input::down(Gamepad btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < gamepadCurrent_.size()) { if (idx < gamepadCurrent_.size()) {
return gamepadCurrent_[idx]; return gamepadCurrent_[idx];
} }
return false; return false;
} }
bool SDL2Input::pressed(Gamepad btn) const { bool SDL2Input::pressed(Gamepad btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < gamepadCurrent_.size()) { if (idx < gamepadCurrent_.size()) {
return gamepadCurrent_[idx] && !gamepadPrevious_[idx]; return gamepadCurrent_[idx] && !gamepadPrevious_[idx];
} }
return false; return false;
} }
bool SDL2Input::released(Gamepad btn) const { bool SDL2Input::released(Gamepad btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx < gamepadCurrent_.size()) { if (idx < gamepadCurrent_.size()) {
return !gamepadCurrent_[idx] && gamepadPrevious_[idx]; return !gamepadCurrent_[idx] && gamepadPrevious_[idx];
} }
return false; return false;
} }
Vec2 SDL2Input::leftStick() const { Vec2 SDL2Input::leftStick() const { return leftStick_; }
return leftStick_;
}
Vec2 SDL2Input::rightStick() const { Vec2 SDL2Input::rightStick() const { return rightStick_; }
return rightStick_;
}
float SDL2Input::leftTrigger() const { float SDL2Input::leftTrigger() const { return leftTrigger_; }
return leftTrigger_;
}
float SDL2Input::rightTrigger() const { float SDL2Input::rightTrigger() const { return rightTrigger_; }
return rightTrigger_;
}
void SDL2Input::vibrate(float left, float right) { void SDL2Input::vibrate(float left, float right) {
if (gamepad_) { if (gamepad_) {
Uint16 lowFreq = static_cast<Uint16>(left * 65535.0f); Uint16 lowFreq = static_cast<Uint16>(left * 65535.0f);
Uint16 highFreq = static_cast<Uint16>(right * 65535.0f); Uint16 highFreq = static_cast<Uint16>(right * 65535.0f);
SDL_GameControllerRumble(gamepad_, lowFreq, highFreq, 500); SDL_GameControllerRumble(gamepad_, lowFreq, highFreq, 500);
} }
} }
bool SDL2Input::touching() const { bool SDL2Input::touching() const { return false; }
return false;
}
int SDL2Input::touchCount() const { int SDL2Input::touchCount() const { return 0; }
return 0;
}
Vec2 SDL2Input::touch(int index) const { Vec2 SDL2Input::touch(int index) const {
(void)index; (void)index;
return Vec2{0.0f, 0.0f}; return Vec2{0.0f, 0.0f};
} }
TouchPoint SDL2Input::touchPoint(int index) const { TouchPoint SDL2Input::touchPoint(int index) const {
(void)index; (void)index;
return TouchPoint{}; return TouchPoint{};
} }
void SDL2Input::updateKeyboard() { void SDL2Input::updateKeyboard() {}
}
void SDL2Input::updateMouse() { void SDL2Input::updateMouse() {
int x = 0, y = 0; int x = 0, y = 0;
SDL_GetMouseState(&x, &y); SDL_GetMouseState(&x, &y);
mousePos_ = Vec2{static_cast<float>(x), static_cast<float>(y)}; mousePos_ = Vec2{static_cast<float>(x), static_cast<float>(y)};
} }
void SDL2Input::updateGamepad() { void SDL2Input::updateGamepad() {
if (!gamepad_) { if (!gamepad_) {
return; return;
}
auto applyDeadzone = [this](float value) -> float {
if (std::abs(value) < deadzone_) {
return 0.0f;
} }
float sign = value >= 0.0f ? 1.0f : -1.0f;
return sign * (std::abs(value) - deadzone_) / (1.0f - deadzone_);
};
auto applyDeadzone = [this](float value) -> float { int lx = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_LEFTX);
if (std::abs(value) < deadzone_) { int ly = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_LEFTY);
return 0.0f; int rx = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_RIGHTX);
} int ry = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_RIGHTY);
float sign = value >= 0.0f ? 1.0f : -1.0f;
return sign * (std::abs(value) - deadzone_) / (1.0f - deadzone_);
};
int lx = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_LEFTX); leftStick_.x = applyDeadzone(lx / 32767.0f);
int ly = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_LEFTY); leftStick_.y = applyDeadzone(ly / 32767.0f);
int rx = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_RIGHTX); rightStick_.x = applyDeadzone(rx / 32767.0f);
int ry = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_RIGHTY); rightStick_.y = applyDeadzone(ry / 32767.0f);
leftStick_.x = applyDeadzone(lx / 32767.0f); int lt = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
leftStick_.y = applyDeadzone(ly / 32767.0f); int rt =
rightStick_.x = applyDeadzone(rx / 32767.0f); SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
rightStick_.y = applyDeadzone(ry / 32767.0f);
int lt = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_TRIGGERLEFT); leftTrigger_ = lt / 32767.0f;
int rt = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); rightTrigger_ = rt / 32767.0f;
leftTrigger_ = lt / 32767.0f;
rightTrigger_ = rt / 32767.0f;
} }
void SDL2Input::openGamepad() { void SDL2Input::openGamepad() {
int numJoysticks = SDL_NumJoysticks(); int numJoysticks = SDL_NumJoysticks();
for (int i = 0; i < numJoysticks; ++i) { for (int i = 0; i < numJoysticks; ++i) {
if (SDL_IsGameController(i)) { if (SDL_IsGameController(i)) {
gamepad_ = SDL_GameControllerOpen(i); gamepad_ = SDL_GameControllerOpen(i);
if (gamepad_) { if (gamepad_) {
gamepadIndex_ = i; gamepadIndex_ = i;
E2D_LOG_INFO("Gamepad opened: {}", SDL_GameControllerName(gamepad_)); E2D_LOG_INFO("Gamepad opened: {}", SDL_GameControllerName(gamepad_));
return; return;
} }
}
} }
}
} }
void SDL2Input::closeGamepad() { void SDL2Input::closeGamepad() {
if (gamepad_) { if (gamepad_) {
SDL_GameControllerClose(gamepad_); SDL_GameControllerClose(gamepad_);
gamepad_ = nullptr; gamepad_ = nullptr;
gamepadIndex_ = -1; gamepadIndex_ = -1;
gamepadCurrent_.fill(false); gamepadCurrent_.fill(false);
gamepadPrevious_.fill(false); gamepadPrevious_.fill(false);
} }
} }
int SDL2Input::keyToSDL(Key key) { int SDL2Input::keyToSDL(Key key) { return static_cast<int>(key); }
return static_cast<int>(key);
}
int SDL2Input::mouseToSDL(Mouse btn) { int SDL2Input::mouseToSDL(Mouse btn) {
switch (btn) { switch (btn) {
case Mouse::Left: return SDL_BUTTON_LEFT; case Mouse::Left:
case Mouse::Middle: return SDL_BUTTON_MIDDLE; return SDL_BUTTON_LEFT;
case Mouse::Right: return SDL_BUTTON_RIGHT; case Mouse::Middle:
case Mouse::X1: return SDL_BUTTON_X1; return SDL_BUTTON_MIDDLE;
case Mouse::X2: return SDL_BUTTON_X2; case Mouse::Right:
default: return 0; return SDL_BUTTON_RIGHT;
} case Mouse::X1:
return SDL_BUTTON_X1;
case Mouse::X2:
return SDL_BUTTON_X2;
default:
return 0;
}
} }
int SDL2Input::gamepadToSDL(Gamepad btn) { int SDL2Input::gamepadToSDL(Gamepad btn) {
switch (btn) { switch (btn) {
case Gamepad::A: return SDL_CONTROLLER_BUTTON_A; case Gamepad::A:
case Gamepad::B: return SDL_CONTROLLER_BUTTON_B; return SDL_CONTROLLER_BUTTON_A;
case Gamepad::X: return SDL_CONTROLLER_BUTTON_X; case Gamepad::B:
case Gamepad::Y: return SDL_CONTROLLER_BUTTON_Y; return SDL_CONTROLLER_BUTTON_B;
case Gamepad::Back: return SDL_CONTROLLER_BUTTON_BACK; case Gamepad::X:
case Gamepad::Start: return SDL_CONTROLLER_BUTTON_START; return SDL_CONTROLLER_BUTTON_X;
case Gamepad::LStick: return SDL_CONTROLLER_BUTTON_LEFTSTICK; case Gamepad::Y:
case Gamepad::RStick: return SDL_CONTROLLER_BUTTON_RIGHTSTICK; return SDL_CONTROLLER_BUTTON_Y;
case Gamepad::LB: return SDL_CONTROLLER_BUTTON_LEFTSHOULDER; case Gamepad::Back:
case Gamepad::RB: return SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; return SDL_CONTROLLER_BUTTON_BACK;
case Gamepad::DUp: return SDL_CONTROLLER_BUTTON_DPAD_UP; case Gamepad::Start:
case Gamepad::DDown: return SDL_CONTROLLER_BUTTON_DPAD_DOWN; return SDL_CONTROLLER_BUTTON_START;
case Gamepad::DLeft: return SDL_CONTROLLER_BUTTON_DPAD_LEFT; case Gamepad::LStick:
case Gamepad::DRight: return SDL_CONTROLLER_BUTTON_DPAD_RIGHT; return SDL_CONTROLLER_BUTTON_LEFTSTICK;
case Gamepad::Guide: return SDL_CONTROLLER_BUTTON_GUIDE; case Gamepad::RStick:
default: return 0; return SDL_CONTROLLER_BUTTON_RIGHTSTICK;
} case Gamepad::LB:
return SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
case Gamepad::RB:
return SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
case Gamepad::DUp:
return SDL_CONTROLLER_BUTTON_DPAD_UP;
case Gamepad::DDown:
return SDL_CONTROLLER_BUTTON_DPAD_DOWN;
case Gamepad::DLeft:
return SDL_CONTROLLER_BUTTON_DPAD_LEFT;
case Gamepad::DRight:
return SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
case Gamepad::Guide:
return SDL_CONTROLLER_BUTTON_GUIDE;
default:
return 0;
}
} }
Key SDL2Input::sdlToKey(int sdlKey) { Key SDL2Input::sdlToKey(int sdlKey) {
if (sdlKey >= 0 && sdlKey < static_cast<int>(Key::Count)) { if (sdlKey >= 0 && sdlKey < static_cast<int>(Key::Count)) {
return static_cast<Key>(sdlKey); return static_cast<Key>(sdlKey);
} }
return Key::None; return Key::None;
} }
Mouse SDL2Input::sdlToMouse(int sdlButton) { Mouse SDL2Input::sdlToMouse(int sdlButton) {
switch (sdlButton) { switch (sdlButton) {
case SDL_BUTTON_LEFT: return Mouse::Left; case SDL_BUTTON_LEFT:
case SDL_BUTTON_MIDDLE: return Mouse::Middle; return Mouse::Left;
case SDL_BUTTON_RIGHT: return Mouse::Right; case SDL_BUTTON_MIDDLE:
case SDL_BUTTON_X1: return Mouse::X1; return Mouse::Middle;
case SDL_BUTTON_X2: return Mouse::X2; case SDL_BUTTON_RIGHT:
default: return Mouse::Count; return Mouse::Right;
} case SDL_BUTTON_X1:
return Mouse::X1;
case SDL_BUTTON_X2:
return Mouse::X2;
default:
return Mouse::Count;
}
} }
} } // namespace extra2d

View File

@ -1,400 +1,373 @@
#include "sdl2_window.h" #include "sdl2_window.h"
#include "sdl2_input.h" #include "sdl2_input.h"
#include <extra2d/utils/logger.h> #include <extra2d/core/service_locator.h>
#include <extra2d/services/logger_service.h>
#include <glad/glad.h> #include <glad/glad.h>
namespace extra2d { namespace extra2d {
SDL2Window::SDL2Window() { SDL2Window::SDL2Window() {
for (int i = 0; i < 7; ++i) { for (int i = 0; i < 7; ++i) {
sdlCursors_[i] = nullptr; sdlCursors_[i] = nullptr;
} }
} }
SDL2Window::~SDL2Window() { SDL2Window::~SDL2Window() { destroy(); }
destroy();
}
bool SDL2Window::create(const std::string& title, int width, int height, bool vsync) { bool SDL2Window::create(const std::string &title, int width, int height,
if (!initSDL()) { bool vsync) {
return false; if (!initSDL()) {
} return false;
}
Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
#ifdef __SWITCH__ #ifdef __SWITCH__
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
#endif #endif
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 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);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
sdlWindow_ = SDL_CreateWindow( sdlWindow_ = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED,
title.c_str(), SDL_WINDOWPOS_CENTERED, width, height, flags);
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
width, height,
flags
);
if (!sdlWindow_) { if (!sdlWindow_) {
E2D_LOG_ERROR("Failed to create SDL window: {}", SDL_GetError()); E2D_LOG_ERROR("Failed to create SDL window: {}", SDL_GetError());
deinitSDL(); deinitSDL();
return false; return false;
} }
glContext_ = SDL_GL_CreateContext(sdlWindow_); glContext_ = SDL_GL_CreateContext(sdlWindow_);
if (!glContext_) { if (!glContext_) {
E2D_LOG_ERROR("Failed to create OpenGL context: {}", SDL_GetError()); E2D_LOG_ERROR("Failed to create OpenGL context: {}", SDL_GetError());
SDL_DestroyWindow(sdlWindow_); SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr; sdlWindow_ = nullptr;
deinitSDL(); deinitSDL();
return false; return false;
} }
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) { if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
E2D_LOG_ERROR("Failed to initialize GLAD GLES2"); E2D_LOG_ERROR("Failed to initialize GLAD GLES2");
SDL_GL_DeleteContext(glContext_); SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr; glContext_ = nullptr;
SDL_DestroyWindow(sdlWindow_); SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr; sdlWindow_ = nullptr;
deinitSDL(); deinitSDL();
return false; return false;
} }
SDL_GL_SetSwapInterval(vsync ? 1 : 0); SDL_GL_SetSwapInterval(vsync ? 1 : 0);
SDL_GetWindowSize(sdlWindow_, &width_, &height_); SDL_GetWindowSize(sdlWindow_, &width_, &height_);
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
vsync_ = vsync; vsync_ = vsync;
#ifndef __SWITCH__ #ifndef __SWITCH__
initCursors(); initCursors();
#endif #endif
updateContentScale(); updateContentScale();
input_ = makeUnique<SDL2Input>(); input_ = makeUnique<SDL2Input>();
input_->init(); input_->init();
E2D_LOG_INFO("SDL2 window created: {}x{}", width_, height_); E2D_LOG_INFO("SDL2 window created: {}x{}", width_, height_);
E2D_LOG_INFO(" Platform: OpenGL ES 3.2"); E2D_LOG_INFO(" Platform: OpenGL ES 3.2");
return true; return true;
} }
void SDL2Window::destroy() { void SDL2Window::destroy() {
if (input_) { if (input_) {
input_->shutdown(); input_->shutdown();
input_.reset(); input_.reset();
} }
#ifndef __SWITCH__ #ifndef __SWITCH__
deinitCursors(); deinitCursors();
#endif #endif
if (glContext_) { if (glContext_) {
SDL_GL_DeleteContext(glContext_); SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr; glContext_ = nullptr;
} }
if (sdlWindow_) { if (sdlWindow_) {
SDL_DestroyWindow(sdlWindow_); SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr; sdlWindow_ = nullptr;
} }
deinitSDL(); deinitSDL();
} }
void SDL2Window::poll() { void SDL2Window::poll() {
if (!sdlWindow_) return; if (!sdlWindow_)
return;
if (input_) { if (input_) {
input_->update(); input_->update();
} }
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
handleEvent(event); handleEvent(event);
} }
} }
void SDL2Window::swap() { void SDL2Window::swap() {
if (sdlWindow_ && glContext_) { if (sdlWindow_ && glContext_) {
SDL_GL_SwapWindow(sdlWindow_); SDL_GL_SwapWindow(sdlWindow_);
} }
} }
bool SDL2Window::shouldClose() const { bool SDL2Window::shouldClose() const { return shouldClose_; }
return shouldClose_;
}
void SDL2Window::close() { void SDL2Window::close() { shouldClose_ = true; }
shouldClose_ = true;
}
void SDL2Window::setTitle(const std::string& title) { void SDL2Window::setTitle(const std::string &title) {
if (sdlWindow_) { if (sdlWindow_) {
SDL_SetWindowTitle(sdlWindow_, title.c_str()); SDL_SetWindowTitle(sdlWindow_, title.c_str());
} }
} }
void SDL2Window::setSize(int w, int h) { void SDL2Window::setSize(int w, int h) {
if (sdlWindow_) { if (sdlWindow_) {
SDL_SetWindowSize(sdlWindow_, w, h); SDL_SetWindowSize(sdlWindow_, w, h);
width_ = w; width_ = w;
height_ = h; height_ = h;
} }
} }
void SDL2Window::setPos(int x, int y) { void SDL2Window::setPos(int x, int y) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (sdlWindow_) { if (sdlWindow_) {
SDL_SetWindowPosition(sdlWindow_, x, y); SDL_SetWindowPosition(sdlWindow_, x, y);
} }
#else #else
(void)x; (void)x;
(void)y; (void)y;
#endif #endif
} }
void SDL2Window::setFullscreen(bool fs) { void SDL2Window::setFullscreen(bool fs) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (sdlWindow_) { if (sdlWindow_) {
SDL_SetWindowFullscreen(sdlWindow_, fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); SDL_SetWindowFullscreen(sdlWindow_, fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
fullscreen_ = fs; fullscreen_ = fs;
} }
#else #else
(void)fs; (void)fs;
#endif #endif
} }
void SDL2Window::setVSync(bool vsync) { void SDL2Window::setVSync(bool vsync) {
if (glContext_) { if (glContext_) {
SDL_GL_SetSwapInterval(vsync ? 1 : 0); SDL_GL_SetSwapInterval(vsync ? 1 : 0);
vsync_ = vsync; vsync_ = vsync;
} }
} }
void SDL2Window::setVisible(bool visible) { void SDL2Window::setVisible(bool visible) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (sdlWindow_) { if (sdlWindow_) {
if (visible) { if (visible) {
SDL_ShowWindow(sdlWindow_); SDL_ShowWindow(sdlWindow_);
} else { } else {
SDL_HideWindow(sdlWindow_); SDL_HideWindow(sdlWindow_);
}
} }
}
#else #else
(void)visible; (void)visible;
#endif #endif
} }
int SDL2Window::width() const { int SDL2Window::width() const { return width_; }
return width_;
}
int SDL2Window::height() const { int SDL2Window::height() const { return height_; }
return height_;
}
Size SDL2Window::size() const { Size SDL2Window::size() const {
return Size(static_cast<float>(width_), static_cast<float>(height_)); return Size(static_cast<float>(width_), static_cast<float>(height_));
} }
Vec2 SDL2Window::pos() const { Vec2 SDL2Window::pos() const {
int x = 0, y = 0; int x = 0, y = 0;
#ifndef __SWITCH__ #ifndef __SWITCH__
if (sdlWindow_) { if (sdlWindow_) {
SDL_GetWindowPosition(sdlWindow_, &x, &y); SDL_GetWindowPosition(sdlWindow_, &x, &y);
} }
#endif #endif
return Vec2(static_cast<float>(x), static_cast<float>(y)); return Vec2(static_cast<float>(x), static_cast<float>(y));
} }
bool SDL2Window::fullscreen() const { bool SDL2Window::fullscreen() const { return fullscreen_; }
return fullscreen_;
}
bool SDL2Window::vsync() const { bool SDL2Window::vsync() const { return vsync_; }
return vsync_;
}
bool SDL2Window::focused() const { bool SDL2Window::focused() const { return focused_; }
return focused_;
}
bool SDL2Window::minimized() const { bool SDL2Window::minimized() const { return minimized_; }
return minimized_;
}
float SDL2Window::scaleX() const { float SDL2Window::scaleX() const { return scaleX_; }
return scaleX_;
}
float SDL2Window::scaleY() const { float SDL2Window::scaleY() const { return scaleY_; }
return scaleY_;
}
void SDL2Window::setCursor(Cursor cursor) { void SDL2Window::setCursor(Cursor cursor) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (cursor == Cursor::Hidden) { if (cursor == Cursor::Hidden) {
SDL_ShowCursor(SDL_DISABLE); SDL_ShowCursor(SDL_DISABLE);
return; return;
} }
SDL_ShowCursor(SDL_ENABLE); SDL_ShowCursor(SDL_ENABLE);
int idx = static_cast<int>(cursor); int idx = static_cast<int>(cursor);
if (idx >= 0 && idx < 7 && sdlCursors_[idx]) { if (idx >= 0 && idx < 7 && sdlCursors_[idx]) {
SDL_SetCursor(sdlCursors_[idx]); SDL_SetCursor(sdlCursors_[idx]);
currentCursor_ = idx; currentCursor_ = idx;
} }
#else #else
(void)cursor; (void)cursor;
#endif #endif
} }
void SDL2Window::showCursor(bool show) { void SDL2Window::showCursor(bool show) {
#ifndef __SWITCH__ #ifndef __SWITCH__
SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE); SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE);
cursorVisible_ = show; cursorVisible_ = show;
#else #else
(void)show; (void)show;
#endif #endif
} }
void SDL2Window::lockCursor(bool lock) { void SDL2Window::lockCursor(bool lock) {
#ifndef __SWITCH__ #ifndef __SWITCH__
if (sdlWindow_) { if (sdlWindow_) {
SDL_SetRelativeMouseMode(lock ? SDL_TRUE : SDL_FALSE); SDL_SetRelativeMouseMode(lock ? SDL_TRUE : SDL_FALSE);
cursorLocked_ = lock; cursorLocked_ = lock;
} }
#else #else
(void)lock; (void)lock;
#endif #endif
} }
IInput* SDL2Window::input() const { IInput *SDL2Window::input() const { return input_.get(); }
return input_.get();
}
void SDL2Window::onResize(ResizeCb cb) { void SDL2Window::onResize(ResizeCb cb) { resizeCb_ = cb; }
resizeCb_ = cb;
}
void SDL2Window::onClose(CloseCb cb) { void SDL2Window::onClose(CloseCb cb) { closeCb_ = cb; }
closeCb_ = cb;
}
void SDL2Window::onFocus(FocusCb cb) { void SDL2Window::onFocus(FocusCb cb) { focusCb_ = cb; }
focusCb_ = cb;
}
void* SDL2Window::native() const { void *SDL2Window::native() const { return sdlWindow_; }
return sdlWindow_;
}
bool SDL2Window::initSDL() { bool SDL2Window::initSDL() {
static int sdlInitCount = 0; static int sdlInitCount = 0;
if (sdlInitCount == 0) { if (sdlInitCount == 0) {
Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER; Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER;
if (SDL_Init(initFlags) != 0) { if (SDL_Init(initFlags) != 0) {
E2D_LOG_ERROR("Failed to initialize SDL: {}", SDL_GetError()); E2D_LOG_ERROR("Failed to initialize SDL: {}", SDL_GetError());
return false; return false;
}
sdlInitCount++;
} }
return true; sdlInitCount++;
}
return true;
} }
void SDL2Window::deinitSDL() { void SDL2Window::deinitSDL() {
static int sdlInitCount = 1; static int sdlInitCount = 1;
sdlInitCount--; sdlInitCount--;
if (sdlInitCount == 0) { if (sdlInitCount == 0) {
SDL_Quit(); SDL_Quit();
} }
} }
#ifndef __SWITCH__ #ifndef __SWITCH__
void SDL2Window::initCursors() { void SDL2Window::initCursors() {
sdlCursors_[0] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); sdlCursors_[0] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
sdlCursors_[1] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); sdlCursors_[1] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
sdlCursors_[2] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR); sdlCursors_[2] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
sdlCursors_[3] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); sdlCursors_[3] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
sdlCursors_[4] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); sdlCursors_[4] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
sdlCursors_[5] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); sdlCursors_[5] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
sdlCursors_[6] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); sdlCursors_[6] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
} }
void SDL2Window::deinitCursors() { void SDL2Window::deinitCursors() {
for (int i = 0; i < 7; ++i) { for (int i = 0; i < 7; ++i) {
if (sdlCursors_[i]) { if (sdlCursors_[i]) {
SDL_FreeCursor(sdlCursors_[i]); SDL_FreeCursor(sdlCursors_[i]);
sdlCursors_[i] = nullptr; sdlCursors_[i] = nullptr;
}
} }
}
} }
#endif #endif
void SDL2Window::updateContentScale() { void SDL2Window::updateContentScale() {
if (sdlWindow_) { if (sdlWindow_) {
SDL_GetWindowSize(sdlWindow_, &width_, &height_); SDL_GetWindowSize(sdlWindow_, &width_, &height_);
int dw, dh; int dw, dh;
SDL_GL_GetDrawableSize(sdlWindow_, &dw, &dh); SDL_GL_GetDrawableSize(sdlWindow_, &dw, &dh);
scaleX_ = dw > 0 ? static_cast<float>(dw) / width_ : 1.0f; scaleX_ = dw > 0 ? static_cast<float>(dw) / width_ : 1.0f;
scaleY_ = dh > 0 ? static_cast<float>(dh) / height_ : 1.0f; scaleY_ = dh > 0 ? static_cast<float>(dh) / height_ : 1.0f;
} }
} }
void SDL2Window::handleEvent(const SDL_Event& event) { void SDL2Window::handleEvent(const SDL_Event &event) {
if (input_) { if (input_) {
input_->handleSDLEvent(event); input_->handleSDLEvent(event);
} }
switch (event.type) { switch (event.type) {
case SDL_QUIT: case SDL_QUIT:
shouldClose_ = true; shouldClose_ = true;
if (closeCb_) closeCb_(); if (closeCb_)
break; closeCb_();
break;
case SDL_WINDOWEVENT:
switch (event.window.event) { case SDL_WINDOWEVENT:
case SDL_WINDOWEVENT_RESIZED: switch (event.window.event) {
case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_WINDOWEVENT_RESIZED:
width_ = event.window.data1; case SDL_WINDOWEVENT_SIZE_CHANGED:
height_ = event.window.data2; width_ = event.window.data1;
updateContentScale(); height_ = event.window.data2;
if (resizeCb_) resizeCb_(width_, height_); updateContentScale();
break; if (resizeCb_)
resizeCb_(width_, height_);
case SDL_WINDOWEVENT_FOCUS_GAINED: break;
focused_ = true;
if (focusCb_) focusCb_(true); case SDL_WINDOWEVENT_FOCUS_GAINED:
break; focused_ = true;
if (focusCb_)
case SDL_WINDOWEVENT_FOCUS_LOST: focusCb_(true);
focused_ = false; break;
if (focusCb_) focusCb_(false);
break; case SDL_WINDOWEVENT_FOCUS_LOST:
focused_ = false;
case SDL_WINDOWEVENT_MINIMIZED: if (focusCb_)
minimized_ = true; focusCb_(false);
break; break;
case SDL_WINDOWEVENT_RESTORED: case SDL_WINDOWEVENT_MINIMIZED:
minimized_ = false; minimized_ = true;
break; break;
case SDL_WINDOWEVENT_CLOSE: case SDL_WINDOWEVENT_RESTORED:
shouldClose_ = true; minimized_ = false;
if (closeCb_) closeCb_(); break;
break;
} case SDL_WINDOWEVENT_CLOSE:
break; shouldClose_ = true;
if (closeCb_)
closeCb_();
break;
} }
break;
}
} }
} } // namespace extra2d

View File

@ -1,7 +1,7 @@
#include <extra2d/platform/window_module.h>
#include <extra2d/platform/backend_factory.h>
#include <extra2d/core/service_locator.h> #include <extra2d/core/service_locator.h>
#include <extra2d/utils/logger.h> #include <extra2d/platform/backend_factory.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/services/logger_service.h>
#ifdef __SWITCH__ #ifdef __SWITCH__
#include <switch.h> #include <switch.h>
@ -16,61 +16,63 @@ void initSDL2Backend();
#elif defined(E2D_BACKEND_GLFW) #elif defined(E2D_BACKEND_GLFW)
void initGLFWBackend(); void initGLFWBackend();
#endif #endif
} } // namespace platform
WindowModule::WindowModule(std::function<void(WindowCfg&)> configFn) { WindowModule::WindowModule(std::function<void(WindowCfg &)> configFn) {
configFn(cfg_); configFn(cfg_);
} }
WindowModule::~WindowModule() { WindowModule::~WindowModule() {
if (initialized_) { if (initialized_) {
shutdown(); shutdown();
} }
} }
bool WindowModule::init() { bool WindowModule::init() {
if (initialized_) return true; if (initialized_)
return true;
// 初始化后端(注册到工厂) // 初始化后端(注册到工厂)
#if defined(E2D_BACKEND_SDL2) #if defined(E2D_BACKEND_SDL2)
platform::initSDL2Backend(); platform::initSDL2Backend();
#elif defined(E2D_BACKEND_GLFW) #elif defined(E2D_BACKEND_GLFW)
platform::initGLFWBackend(); platform::initGLFWBackend();
#else #else
#error "No window backend defined" #error "No window backend defined"
#endif #endif
E2D_LOG_INFO("Window backend initialized"); E2D_LOG_INFO("Window backend initialized");
E2D_LOG_INFO("Creating window with size {}x{}", cfg_.w, cfg_.h); E2D_LOG_INFO("Creating window with size {}x{}", cfg_.w, cfg_.h);
// 创建窗口(使用配置的后端) // 创建窗口(使用配置的后端)
win_ = platform::BackendFactory::createWindow(cfg_.backend); win_ = platform::BackendFactory::createWindow(cfg_.backend);
if (!win_) { if (!win_) {
E2D_LOG_ERROR("Failed to create window backend: {}", cfg_.backend); E2D_LOG_ERROR("Failed to create window backend: {}", cfg_.backend);
return false; return false;
} }
if (!win_->create(cfg_.title, cfg_.w, cfg_.h, cfg_.vsync)) { if (!win_->create(cfg_.title, cfg_.w, cfg_.h, cfg_.vsync)) {
E2D_LOG_ERROR("Failed to create window"); E2D_LOG_ERROR("Failed to create window");
shutdown(); shutdown();
return false; return false;
} }
E2D_LOG_INFO("Window created successfully"); E2D_LOG_INFO("Window created successfully");
initialized_ = true; initialized_ = true;
return true; return true;
} }
void WindowModule::shutdown() { void WindowModule::shutdown() {
if (!initialized_) return; if (!initialized_)
return;
if (win_) { if (win_) {
win_->destroy(); win_->destroy();
win_.reset(); win_.reset();
} }
initialized_ = false; initialized_ = false;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,9 +1,11 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_command.h> #include <extra2d/graphics/core/render_command.h>
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
#include <extra2d/scene/scene.h> #include <extra2d/scene/scene.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {

View File

@ -1,7 +1,9 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/core/render_command.h> #include <extra2d/graphics/core/render_command.h>
#include <extra2d/scene/scene.h> #include <extra2d/scene/scene.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {

View File

@ -1,5 +1,6 @@
#include <algorithm> #include <algorithm>
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/core/render_command.h> #include <extra2d/graphics/core/render_command.h>
#include <extra2d/platform/iinput.h> #include <extra2d/platform/iinput.h>
@ -10,7 +11,8 @@
#include <extra2d/scene/transition_scale_scene.h> #include <extra2d/scene/transition_scale_scene.h>
#include <extra2d/scene/transition_scene.h> #include <extra2d/scene/transition_scene.h>
#include <extra2d/scene/transition_slide_scene.h> #include <extra2d/scene/transition_slide_scene.h>
#include <extra2d/utils/logger.h> #include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {
@ -748,7 +750,8 @@ void SceneManager::finishTransition() {
*/ */
void SceneManager::dispatchPointerEvents(Scene &scene) { void SceneManager::dispatchPointerEvents(Scene &scene) {
auto *input = Application::get().input(); auto *input = Application::get().input();
if (!input) return; if (!input)
return;
Vec2 screenPos = input->mouse(); Vec2 screenPos = input->mouse();
Vec2 worldPos = screenPos; Vec2 worldPos = screenPos;
@ -832,7 +835,6 @@ void SceneManager::dispatchPointerEvents(Scene &scene) {
lastPointerWorld_ = worldPos; lastPointerWorld_ = worldPos;
} }
void SceneManager::doSceneSwitch() { void SceneManager::doSceneSwitch() {}
}
} // namespace extra2d } // namespace extra2d

View File

@ -1,9 +1,9 @@
#include <extra2d/scene/transition_fade_scene.h>
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/platform/iwindow.h> #include <extra2d/platform/iwindow.h>
#include <extra2d/utils/logger.h> #include <extra2d/scene/transition_fade_scene.h>
#include <algorithm> #include <extra2d/services/logger_service.h>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
namespace extra2d { namespace extra2d {

View File

@ -1,6 +1,7 @@
#include <extra2d/scene/transition_scene.h> #include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/utils/logger.h> #include <extra2d/scene/transition_scene.h>
#include <extra2d/services/logger_service.h>
namespace extra2d { namespace extra2d {