mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
ROADMAP.md
This commit is contained in:
@@ -33,6 +33,10 @@ std::string ToLower(std::string value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
uint16_t ClampViewExtent(uint32_t value) {
|
||||
return static_cast<uint16_t>(std::min<uint32_t>(value, std::numeric_limits<uint16_t>::max()));
|
||||
}
|
||||
|
||||
glm::mat4 ToMat4(const std::array<float, 16>& value) {
|
||||
return glm::make_mat4(value.data());
|
||||
}
|
||||
@@ -240,17 +244,9 @@ std::vector<bgfx::RendererType::Enum> BuildPreferredRenderers(
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::OpenGL);
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::Vulkan);
|
||||
} else {
|
||||
const bool waylandOrX11 = (videoLower == "wayland") || (videoLower == "x11");
|
||||
const bool kmsdrm = (videoLower == "kmsdrm");
|
||||
if (waylandOrX11 || kmsdrm) {
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::Vulkan);
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::OpenGL);
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::OpenGLES);
|
||||
} else {
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::Vulkan);
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::OpenGL);
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::OpenGLES);
|
||||
}
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::Vulkan);
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::OpenGL);
|
||||
AddRendererIfSupported(preferred, supportedRenderers, bgfx::RendererType::OpenGLES);
|
||||
}
|
||||
|
||||
return preferred;
|
||||
@@ -694,6 +690,7 @@ GraphicsDeviceHandle BgfxGraphicsBackend::CreateDevice() {
|
||||
}
|
||||
|
||||
void BgfxGraphicsBackend::DestroyDevice(GraphicsDeviceHandle device) {
|
||||
(void)device;
|
||||
if (logger_) {
|
||||
logger_->Trace("BgfxGraphicsBackend", "DestroyDevice");
|
||||
}
|
||||
@@ -794,8 +791,10 @@ bgfx::TextureHandle BgfxGraphicsBackend::LoadTextureFromFile(const std::string&
|
||||
// Validate texture dimensions against GPU capabilities
|
||||
const bgfx::Caps* caps = bgfx::getCaps();
|
||||
if (caps) {
|
||||
const uint16_t maxTextureSize = caps->limits.maxTextureSize;
|
||||
if (width > maxTextureSize || height > maxTextureSize) {
|
||||
const uint32_t maxTextureSize = caps->limits.maxTextureSize;
|
||||
const uint32_t widthU = static_cast<uint32_t>(width);
|
||||
const uint32_t heightU = static_cast<uint32_t>(height);
|
||||
if (widthU > maxTextureSize || heightU > maxTextureSize) {
|
||||
if (logger_) {
|
||||
logger_->Error("BgfxGraphicsBackend::LoadTextureFromFile: texture " + path +
|
||||
" size (" + std::to_string(width) + "x" + std::to_string(height) +
|
||||
@@ -936,6 +935,7 @@ void BgfxGraphicsBackend::ApplyMaterialXUniforms(const std::array<float, 16>& mo
|
||||
GraphicsPipelineHandle BgfxGraphicsBackend::CreatePipeline(GraphicsDeviceHandle device,
|
||||
const std::string& shaderKey,
|
||||
const ShaderPaths& shaderPaths) {
|
||||
(void)device;
|
||||
if (logger_) {
|
||||
logger_->Trace("BgfxGraphicsBackend", "CreatePipeline", "shaderKey=" + shaderKey);
|
||||
}
|
||||
@@ -1061,6 +1061,7 @@ GraphicsPipelineHandle BgfxGraphicsBackend::CreatePipeline(GraphicsDeviceHandle
|
||||
}
|
||||
|
||||
void BgfxGraphicsBackend::DestroyPipeline(GraphicsDeviceHandle device, GraphicsPipelineHandle pipeline) {
|
||||
(void)device;
|
||||
if (logger_) {
|
||||
logger_->Trace("BgfxGraphicsBackend", "DestroyPipeline");
|
||||
}
|
||||
@@ -1088,6 +1089,7 @@ void BgfxGraphicsBackend::DestroyPipeline(GraphicsDeviceHandle device, GraphicsP
|
||||
|
||||
GraphicsBufferHandle BgfxGraphicsBackend::CreateVertexBuffer(GraphicsDeviceHandle device,
|
||||
const std::vector<uint8_t>& data) {
|
||||
(void)device;
|
||||
if (logger_) {
|
||||
logger_->Trace("BgfxGraphicsBackend", "CreateVertexBuffer",
|
||||
"data.size=" + std::to_string(data.size()));
|
||||
@@ -1109,6 +1111,7 @@ GraphicsBufferHandle BgfxGraphicsBackend::CreateVertexBuffer(GraphicsDeviceHandl
|
||||
|
||||
GraphicsBufferHandle BgfxGraphicsBackend::CreateIndexBuffer(GraphicsDeviceHandle device,
|
||||
const std::vector<uint8_t>& data) {
|
||||
(void)device;
|
||||
if (logger_) {
|
||||
logger_->Trace("BgfxGraphicsBackend", "CreateIndexBuffer",
|
||||
"data.size=" + std::to_string(data.size()));
|
||||
@@ -1129,6 +1132,7 @@ GraphicsBufferHandle BgfxGraphicsBackend::CreateIndexBuffer(GraphicsDeviceHandle
|
||||
}
|
||||
|
||||
void BgfxGraphicsBackend::DestroyBuffer(GraphicsDeviceHandle device, GraphicsBufferHandle buffer) {
|
||||
(void)device;
|
||||
if (logger_) {
|
||||
logger_->Trace("BgfxGraphicsBackend", "DestroyBuffer");
|
||||
}
|
||||
@@ -1150,15 +1154,19 @@ void BgfxGraphicsBackend::DestroyBuffer(GraphicsDeviceHandle device, GraphicsBuf
|
||||
}
|
||||
|
||||
bool BgfxGraphicsBackend::BeginFrame(GraphicsDeviceHandle device) {
|
||||
(void)device;
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
bgfx::setViewRect(viewId_, 0, 0, viewportWidth_, viewportHeight_);
|
||||
const uint16_t viewWidth = ClampViewExtent(viewportWidth_);
|
||||
const uint16_t viewHeight = ClampViewExtent(viewportHeight_);
|
||||
bgfx::setViewRect(viewId_, 0, 0, viewWidth, viewHeight);
|
||||
bgfx::touch(viewId_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BgfxGraphicsBackend::EndFrame(GraphicsDeviceHandle device) {
|
||||
(void)device;
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
@@ -1189,7 +1197,9 @@ void BgfxGraphicsBackend::ConfigureView(GraphicsDeviceHandle device,
|
||||
return;
|
||||
}
|
||||
|
||||
bgfx::setViewRect(viewId, 0, 0, viewportWidth_, viewportHeight_);
|
||||
const uint16_t viewWidth = ClampViewExtent(viewportWidth_);
|
||||
const uint16_t viewHeight = ClampViewExtent(viewportHeight_);
|
||||
bgfx::setViewRect(viewId, 0, 0, viewWidth, viewHeight);
|
||||
|
||||
const bool hasClear = clearConfig.enabled ||
|
||||
clearConfig.clearColor || clearConfig.clearDepth || clearConfig.clearStencil;
|
||||
@@ -1223,6 +1233,7 @@ void BgfxGraphicsBackend::Draw(GraphicsDeviceHandle device, GraphicsPipelineHand
|
||||
GraphicsBufferHandle vertexBuffer, GraphicsBufferHandle indexBuffer,
|
||||
uint32_t indexOffset, uint32_t indexCount, int32_t vertexOffset,
|
||||
const std::array<float, 16>& modelMatrix) {
|
||||
(void)device;
|
||||
auto reportError = [&](const std::string& code, const std::string& message) {
|
||||
if (!probeService_) {
|
||||
return;
|
||||
|
||||
@@ -42,14 +42,15 @@ CrashRecoveryService::CrashRecoveryService(std::shared_ptr<ILogger> logger, Cras
|
||||
, lastHeartbeatNs_(0)
|
||||
, heartbeatSeen_(false)
|
||||
, heartbeatMonitorRunning_(false)
|
||||
, config_(std::move(config))
|
||||
, memoryLimitBytes_(0)
|
||||
, lastSuccessfulFrameTime_(0.0)
|
||||
, consecutiveFrameTimeouts_(0)
|
||||
, luaExecutionFailures_(0)
|
||||
, fileFormatErrors_(0)
|
||||
, memoryWarnings_(0)
|
||||
, lastHealthCheck_(std::chrono::steady_clock::now())
|
||||
, signalHandlersInstalled_(false)
|
||||
, config_(std::move(config)) {
|
||||
, signalHandlersInstalled_(false) {
|
||||
heartbeatTimeout_ = std::chrono::milliseconds(config_.heartbeatTimeoutMs);
|
||||
heartbeatPollInterval_ = std::chrono::milliseconds(config_.heartbeatPollIntervalMs);
|
||||
memoryLimitBytes_ = config_.memoryLimitMB * 1024 * 1024;
|
||||
@@ -193,7 +194,7 @@ void CrashRecoveryService::RecordFrameHeartbeat(double frameTimeSeconds) {
|
||||
void CrashRecoveryService::MonitorHeartbeats() {
|
||||
logger_->Trace("CrashRecoveryService", "MonitorHeartbeats", "", "Starting heartbeat monitor");
|
||||
|
||||
const int64_t timeoutNs = static_cast<int64_t>(heartbeatTimeout_.count()) * 1000 * 1000;
|
||||
const int64_t timeoutNs = heartbeatTimeout_.count() * 1000 * 1000;
|
||||
while (heartbeatMonitorRunning_.load()) {
|
||||
std::this_thread::sleep_for(heartbeatPollInterval_);
|
||||
if (!heartbeatSeen_.load(std::memory_order_acquire)) {
|
||||
@@ -572,7 +573,10 @@ size_t CrashRecoveryService::GetCurrentMemoryUsage() const {
|
||||
struct rusage usage;
|
||||
if (getrusage(RUSAGE_SELF, &usage) == 0) {
|
||||
// Return resident set size in bytes
|
||||
return usage.ru_maxrss * 1024; // ru_maxrss is in KB on Linux
|
||||
if (usage.ru_maxrss <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<size_t>(usage.ru_maxrss) * 1024; // ru_maxrss is in KB on Linux
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -897,9 +897,9 @@ void MeshService::PushMeshToLua(lua_State* L, const MeshPayload& payload) {
|
||||
lua_newtable(L);
|
||||
|
||||
lua_newtable(L);
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
for (size_t component = 0; component < 3; ++component) {
|
||||
lua_pushnumber(L, payload.positions[vertexIndex][component]);
|
||||
lua_rawseti(L, -2, component + 1);
|
||||
lua_rawseti(L, -2, static_cast<int>(component + 1));
|
||||
}
|
||||
lua_setfield(L, -2, "position");
|
||||
|
||||
@@ -908,9 +908,9 @@ void MeshService::PushMeshToLua(lua_State* L, const MeshPayload& payload) {
|
||||
if (vertexIndex < payload.normals.size()) {
|
||||
normal = payload.normals[vertexIndex];
|
||||
}
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
for (size_t component = 0; component < 3; ++component) {
|
||||
lua_pushnumber(L, normal[component]);
|
||||
lua_rawseti(L, -2, component + 1);
|
||||
lua_rawseti(L, -2, static_cast<int>(component + 1));
|
||||
}
|
||||
lua_setfield(L, -2, "normal");
|
||||
|
||||
@@ -919,16 +919,16 @@ void MeshService::PushMeshToLua(lua_State* L, const MeshPayload& payload) {
|
||||
if (vertexIndex < payload.tangents.size()) {
|
||||
tangent = payload.tangents[vertexIndex];
|
||||
}
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
for (size_t component = 0; component < 3; ++component) {
|
||||
lua_pushnumber(L, tangent[component]);
|
||||
lua_rawseti(L, -2, component + 1);
|
||||
lua_rawseti(L, -2, static_cast<int>(component + 1));
|
||||
}
|
||||
lua_setfield(L, -2, "tangent");
|
||||
|
||||
lua_newtable(L);
|
||||
for (int component = 0; component < 3; ++component) {
|
||||
for (size_t component = 0; component < 3; ++component) {
|
||||
lua_pushnumber(L, payload.colors[vertexIndex][component]);
|
||||
lua_rawseti(L, -2, component + 1);
|
||||
lua_rawseti(L, -2, static_cast<int>(component + 1));
|
||||
}
|
||||
lua_setfield(L, -2, "color");
|
||||
|
||||
@@ -937,9 +937,9 @@ void MeshService::PushMeshToLua(lua_State* L, const MeshPayload& payload) {
|
||||
if (vertexIndex < payload.texcoords.size()) {
|
||||
texcoord = payload.texcoords[vertexIndex];
|
||||
}
|
||||
for (int component = 0; component < 2; ++component) {
|
||||
for (size_t component = 0; component < 2; ++component) {
|
||||
lua_pushnumber(L, texcoord[component]);
|
||||
lua_rawseti(L, -2, component + 1);
|
||||
lua_rawseti(L, -2, static_cast<int>(component + 1));
|
||||
}
|
||||
lua_setfield(L, -2, "texcoord");
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ int PhysicsBridgeService::StepSimulation(float deltaTime, int maxSubSteps) {
|
||||
if (maxSubSteps < 0) {
|
||||
maxSubSteps = 0;
|
||||
}
|
||||
return static_cast<int>(world_->stepSimulation(deltaTime, maxSubSteps, 1.0f / 60.0f));
|
||||
return world_->stepSimulation(deltaTime, maxSubSteps, 1.0f / 60.0f);
|
||||
}
|
||||
|
||||
bool PhysicsBridgeService::GetRigidBodyTransform(const std::string& name,
|
||||
|
||||
@@ -53,7 +53,7 @@ bool PipelineCompilerService::Compile(const std::string& inputPath,
|
||||
|
||||
if (profile == "glsl") {
|
||||
// For GLSL, just copy the source
|
||||
outputFile.write(source.c_str(), source.size());
|
||||
outputFile.write(source.c_str(), static_cast<std::streamsize>(source.size()));
|
||||
} else {
|
||||
// For SPIR-V, compile using shaderc library
|
||||
shaderc_shader_kind kind = isVertex ? shaderc_vertex_shader : shaderc_fragment_shader;
|
||||
@@ -68,14 +68,15 @@ bool PipelineCompilerService::Compile(const std::string& inputPath,
|
||||
shaderc::SpvCompilationResult result = compiler.CompileGlslToSpv(processedSource, kind, inputPath.c_str(), options);
|
||||
|
||||
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
|
||||
lastError_ = "Shader compilation failed: " + std::string(result.GetErrorMessage());
|
||||
lastError_ = "Shader compilation failed: " + result.GetErrorMessage();
|
||||
logger_->Error(lastError_.value());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the SPIR-V binary data
|
||||
const uint32_t* spirvData = result.begin();
|
||||
size_t spirvSize = (result.end() - result.begin()) * sizeof(uint32_t);
|
||||
const size_t spirvWords = static_cast<size_t>(result.end() - result.begin());
|
||||
const size_t spirvSize = spirvWords * sizeof(uint32_t);
|
||||
|
||||
// Write bgfx shader header
|
||||
const uint32_t shaderBinVersion = 11;
|
||||
@@ -91,7 +92,7 @@ bool PipelineCompilerService::Compile(const std::string& inputPath,
|
||||
outputFile.write(reinterpret_cast<const char*>(&hash), sizeof(hash));
|
||||
|
||||
// Write SPIR-V data
|
||||
outputFile.write(reinterpret_cast<const char*>(spirvData), spirvSize);
|
||||
outputFile.write(reinterpret_cast<const char*>(spirvData), static_cast<std::streamsize>(spirvSize));
|
||||
|
||||
// Write uniform information (bgfx expects this after the SPIR-V)
|
||||
if (isVertex) {
|
||||
@@ -101,7 +102,7 @@ bool PipelineCompilerService::Compile(const std::string& inputPath,
|
||||
|
||||
// Uniform name (null-terminated) - include block name for Vulkan
|
||||
const char* uniformName = "UniformBuffer.u_modelViewProj";
|
||||
outputFile.write(uniformName, strlen(uniformName) + 1);
|
||||
outputFile.write(uniformName, static_cast<std::streamsize>(strlen(uniformName) + 1));
|
||||
|
||||
// Uniform type (Mat4 = 4)
|
||||
uint8_t uniformType = 4;
|
||||
@@ -131,7 +132,7 @@ bool PipelineCompilerService::Compile(const std::string& inputPath,
|
||||
|
||||
// Uniform name (null-terminated)
|
||||
const char* uniformName = "s_tex";
|
||||
outputFile.write(uniformName, strlen(uniformName) + 1);
|
||||
outputFile.write(uniformName, static_cast<std::streamsize>(strlen(uniformName) + 1));
|
||||
|
||||
// Uniform type (Sampler = 5)
|
||||
uint8_t uniformType = 5;
|
||||
|
||||
@@ -7,13 +7,16 @@ namespace sdl3cpp::services::impl {
|
||||
|
||||
SceneService::SceneService(std::shared_ptr<ISceneScriptService> scriptService,
|
||||
std::shared_ptr<IEcsService> ecsService,
|
||||
std::shared_ptr<ILogger> logger)
|
||||
std::shared_ptr<ILogger> logger,
|
||||
std::shared_ptr<IProbeService> probeService)
|
||||
: scriptService_(std::move(scriptService)),
|
||||
ecsService_(std::move(ecsService)),
|
||||
logger_(std::move(logger)) {
|
||||
logger_(std::move(logger)),
|
||||
probeService_(std::move(probeService)) {
|
||||
logger_->Trace("SceneService", "SceneService",
|
||||
"scriptService=" + std::string(scriptService_ ? "set" : "null") +
|
||||
", ecsService=" + std::string(ecsService_ ? "set" : "null"));
|
||||
", ecsService=" + std::string(ecsService_ ? "set" : "null") +
|
||||
", probeService=" + std::string(probeService_ ? "set" : "null"));
|
||||
|
||||
if (!scriptService_ || !ecsService_) {
|
||||
throw std::invalid_argument("Scene script service and ECS service cannot be null");
|
||||
@@ -39,6 +42,7 @@ void SceneService::LoadScene(const std::vector<SceneObject>& objects) {
|
||||
|
||||
if (objects.empty()) {
|
||||
initialized_ = false;
|
||||
hasSceneSignature_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -61,6 +65,12 @@ void SceneService::LoadScene(const std::vector<SceneObject>& objects) {
|
||||
throw std::runtime_error("Scene vertex count exceeds uint16_t index range");
|
||||
}
|
||||
|
||||
const bool shouldReportSceneLoad = ShouldEmitRuntimeProbe() &&
|
||||
(!hasSceneSignature_ ||
|
||||
objects.size() != lastSceneObjectCount_ ||
|
||||
totalVertices != lastSceneVertexCount_ ||
|
||||
totalIndices != lastSceneIndexCount_);
|
||||
|
||||
combinedVertices_.reserve(totalVertices);
|
||||
combinedIndices_.reserve(totalIndices);
|
||||
drawInfos_.reserve(objects.size());
|
||||
@@ -134,6 +144,17 @@ void SceneService::LoadScene(const std::vector<SceneObject>& objects) {
|
||||
", combinedIndices=" + std::to_string(combinedIndices_.size()) +
|
||||
", drawCalls=" + std::to_string(drawInfos_.size()));
|
||||
}
|
||||
lastSceneObjectCount_ = objects.size();
|
||||
lastSceneVertexCount_ = totalVertices;
|
||||
lastSceneIndexCount_ = totalIndices;
|
||||
hasSceneSignature_ = true;
|
||||
if (shouldReportSceneLoad) {
|
||||
ReportRuntimeProbe("SCENE_LOAD",
|
||||
"Scene loaded",
|
||||
"objects=" + std::to_string(objects.size()) +
|
||||
", vertices=" + std::to_string(totalVertices) +
|
||||
", indices=" + std::to_string(totalIndices));
|
||||
}
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
@@ -216,4 +237,23 @@ void SceneService::ClearSceneEntities() {
|
||||
sceneEntities_.clear();
|
||||
}
|
||||
|
||||
bool SceneService::ShouldEmitRuntimeProbe() const {
|
||||
if (!probeService_ || !logger_) {
|
||||
return false;
|
||||
}
|
||||
LogLevel level = logger_->GetLevel();
|
||||
return level == LogLevel::TRACE || level == LogLevel::DEBUG;
|
||||
}
|
||||
|
||||
void SceneService::ReportRuntimeProbe(const std::string& code,
|
||||
const std::string& message,
|
||||
const std::string& details) const {
|
||||
ProbeReport report{};
|
||||
report.severity = ProbeSeverity::Info;
|
||||
report.code = code;
|
||||
report.message = message;
|
||||
report.details = details;
|
||||
probeService_->Report(report);
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "../interfaces/i_scene_script_service.hpp"
|
||||
#include "../interfaces/i_ecs_service.hpp"
|
||||
#include "../interfaces/i_logger.hpp"
|
||||
#include "../interfaces/i_probe_service.hpp"
|
||||
#include "../../di/lifecycle.hpp"
|
||||
#include <entt/entt.hpp>
|
||||
#include <memory>
|
||||
@@ -23,7 +24,8 @@ class SceneService : public ISceneService,
|
||||
public:
|
||||
SceneService(std::shared_ptr<ISceneScriptService> scriptService,
|
||||
std::shared_ptr<IEcsService> ecsService,
|
||||
std::shared_ptr<ILogger> logger);
|
||||
std::shared_ptr<ILogger> logger,
|
||||
std::shared_ptr<IProbeService> probeService = nullptr);
|
||||
~SceneService() override;
|
||||
|
||||
// ISceneService interface
|
||||
@@ -59,17 +61,26 @@ private:
|
||||
std::vector<std::string> shaderKeys;
|
||||
};
|
||||
|
||||
bool ShouldEmitRuntimeProbe() const;
|
||||
void ReportRuntimeProbe(const std::string& code,
|
||||
const std::string& message,
|
||||
const std::string& details) const;
|
||||
void ClearSceneEntities();
|
||||
|
||||
std::shared_ptr<ISceneScriptService> scriptService_;
|
||||
std::shared_ptr<IEcsService> ecsService_;
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
std::shared_ptr<IProbeService> probeService_;
|
||||
entt::registry* registry_ = nullptr;
|
||||
std::vector<entt::entity> sceneEntities_;
|
||||
std::vector<core::Vertex> combinedVertices_;
|
||||
std::vector<uint16_t> combinedIndices_;
|
||||
std::vector<SceneDrawInfo> drawInfos_;
|
||||
bool initialized_ = false;
|
||||
size_t lastSceneObjectCount_ = 0;
|
||||
size_t lastSceneVertexCount_ = 0;
|
||||
size_t lastSceneIndexCount_ = 0;
|
||||
bool hasSceneSignature_ = false;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "sdl_audio_service.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
@@ -9,6 +10,11 @@ namespace sdl3cpp::services::impl {
|
||||
namespace {
|
||||
constexpr int kDecodeChunkSize = 4096;
|
||||
constexpr int kMixFrames = 1024;
|
||||
uint32_t GetBytesPerFrame(const SDL_AudioSpec& spec) {
|
||||
const uint32_t formatMask = SDL_AUDIO_MASK_BITSIZE;
|
||||
const uint32_t bitsPerSample = static_cast<uint32_t>(spec.format) & formatMask;
|
||||
return (bitsPerSample / 8u) * static_cast<uint32_t>(spec.channels);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SdlAudioService::SdlAudioService(std::shared_ptr<ILogger> logger)
|
||||
@@ -254,8 +260,8 @@ void SdlAudioService::Update() {
|
||||
return;
|
||||
}
|
||||
|
||||
const int bytesPerFrame = SDL_AUDIO_FRAMESIZE(mixSpec_);
|
||||
if (bytesPerFrame <= 0) {
|
||||
const uint32_t bytesPerFrame = GetBytesPerFrame(mixSpec_);
|
||||
if (bytesPerFrame == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -267,7 +273,8 @@ void SdlAudioService::Update() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (queuedBytes >= bytesPerFrame * kMixFrames) {
|
||||
const uint32_t queueLimit = bytesPerFrame * static_cast<uint32_t>(kMixFrames);
|
||||
if (static_cast<uint32_t>(queuedBytes) >= queueLimit) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -322,7 +329,8 @@ void SdlAudioService::Update() {
|
||||
outputBuffer_.assign(sampleCount, 0);
|
||||
const float volume = volume_;
|
||||
for (size_t i = 0; i < sampleCount; ++i) {
|
||||
int32_t value = static_cast<int32_t>(mixBuffer_[i] * volume);
|
||||
const float mixed = static_cast<float>(mixBuffer_[i]) * volume;
|
||||
int32_t value = static_cast<int32_t>(mixed);
|
||||
value = std::clamp(value, -32768, 32767);
|
||||
outputBuffer_[i] = static_cast<int16_t>(value);
|
||||
}
|
||||
@@ -369,7 +377,7 @@ bool SdlAudioService::LoadAudioFile(const std::filesystem::path& path, AudioData
|
||||
|
||||
audioData.sourceSpec.format = SDL_AUDIO_S16;
|
||||
audioData.sourceSpec.channels = info->channels;
|
||||
audioData.sourceSpec.freq = info->rate;
|
||||
audioData.sourceSpec.freq = static_cast<int>(info->rate);
|
||||
audioData.convertStream = SDL_CreateAudioStream(&audioData.sourceSpec, &mixSpec_);
|
||||
if (!audioData.convertStream) {
|
||||
ov_clear(&audioData.vorbisFile);
|
||||
@@ -410,9 +418,17 @@ int SdlAudioService::ReadStreamSamples(AudioData& audioData, std::vector<int16_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int bytesPerFrame = SDL_AUDIO_FRAMESIZE(mixSpec_);
|
||||
const int bytesNeeded = frames * bytesPerFrame;
|
||||
const size_t sampleCount = static_cast<size_t>(bytesNeeded / static_cast<int>(sizeof(int16_t)));
|
||||
const uint32_t bytesPerFrame = GetBytesPerFrame(mixSpec_);
|
||||
if (bytesPerFrame == 0) {
|
||||
return 0;
|
||||
}
|
||||
const uint32_t bytesNeeded = bytesPerFrame * static_cast<uint32_t>(frames);
|
||||
if (bytesNeeded > static_cast<uint32_t>(std::numeric_limits<int>::max())) {
|
||||
audioData.finished = true;
|
||||
return 0;
|
||||
}
|
||||
const int bytesNeededInt = static_cast<int>(bytesNeeded);
|
||||
const size_t sampleCount = static_cast<size_t>(bytesNeededInt / static_cast<int>(sizeof(int16_t)));
|
||||
output.assign(sampleCount, 0);
|
||||
|
||||
while (!audioData.finished) {
|
||||
@@ -424,13 +440,17 @@ int SdlAudioService::ReadStreamSamples(AudioData& audioData, std::vector<int16_t
|
||||
audioData.finished = true;
|
||||
break;
|
||||
}
|
||||
if (available >= bytesNeeded) {
|
||||
if (available >= bytesNeededInt) {
|
||||
break;
|
||||
}
|
||||
|
||||
char decodeBuffer[kDecodeChunkSize];
|
||||
int bytesRead = ov_read(&audioData.vorbisFile, decodeBuffer, kDecodeChunkSize, 0, 2, 1, nullptr);
|
||||
if (bytesRead > 0) {
|
||||
long bytesReadLong = ov_read(&audioData.vorbisFile, decodeBuffer, kDecodeChunkSize, 0, 2, 1, nullptr);
|
||||
if (bytesReadLong > 0) {
|
||||
if (bytesReadLong > std::numeric_limits<int>::max()) {
|
||||
bytesReadLong = std::numeric_limits<int>::max();
|
||||
}
|
||||
const int bytesRead = static_cast<int>(bytesReadLong);
|
||||
if (!SDL_PutAudioStreamData(audioData.convertStream, decodeBuffer, bytesRead)) {
|
||||
if (logger_) {
|
||||
logger_->Error("Failed to queue decoded audio: " + std::string(SDL_GetError()));
|
||||
@@ -438,7 +458,7 @@ int SdlAudioService::ReadStreamSamples(AudioData& audioData, std::vector<int16_t
|
||||
audioData.finished = true;
|
||||
break;
|
||||
}
|
||||
} else if (bytesRead == 0) {
|
||||
} else if (bytesReadLong == 0) {
|
||||
if (audioData.loop) {
|
||||
ov_pcm_seek(&audioData.vorbisFile, 0);
|
||||
continue;
|
||||
@@ -455,7 +475,7 @@ int SdlAudioService::ReadStreamSamples(AudioData& audioData, std::vector<int16_t
|
||||
}
|
||||
}
|
||||
|
||||
int bytesRead = SDL_GetAudioStreamData(audioData.convertStream, output.data(), bytesNeeded);
|
||||
int bytesRead = SDL_GetAudioStreamData(audioData.convertStream, output.data(), bytesNeededInt);
|
||||
if (bytesRead < 0) {
|
||||
if (logger_) {
|
||||
logger_->Error("Failed to read audio stream data: " + std::string(SDL_GetError()));
|
||||
@@ -464,10 +484,11 @@ int SdlAudioService::ReadStreamSamples(AudioData& audioData, std::vector<int16_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bytesRead < bytesNeeded) {
|
||||
if (bytesRead < bytesNeededInt) {
|
||||
const size_t samplesRead = static_cast<size_t>(bytesRead / static_cast<int>(sizeof(int16_t)));
|
||||
if (samplesRead < output.size()) {
|
||||
std::fill(output.begin() + samplesRead, output.end(), 0);
|
||||
using Difference = std::vector<int16_t>::difference_type;
|
||||
std::fill(output.begin() + static_cast<Difference>(samplesRead), output.end(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ void ThrowSdlErrorIfFailed(bool success, const char* context, const std::shared_
|
||||
}
|
||||
|
||||
void ShowErrorDialog(const char* title, const std::string& message) {
|
||||
(void)title;
|
||||
(void)message;
|
||||
// Disabled for headless environments
|
||||
// SDL_ShowSimpleMessageBox(
|
||||
// SDL_MESSAGEBOX_ERROR,
|
||||
@@ -185,10 +187,12 @@ void SdlWindowService::CreateWindow(const WindowConfig& config) {
|
||||
logger_->TraceVariable("sdl.windowFlags", static_cast<int>(flags));
|
||||
|
||||
logger_->Trace("SdlWindowService", "CreateWindow", "SDL_CreateWindow");
|
||||
const int windowWidth = static_cast<int>(config.width);
|
||||
const int windowHeight = static_cast<int>(config.height);
|
||||
window_ = SDL_CreateWindow(
|
||||
config.title.c_str(),
|
||||
config.width,
|
||||
config.height,
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
flags
|
||||
);
|
||||
|
||||
@@ -248,7 +252,7 @@ bool SdlWindowService::IsMinimized() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t flags = SDL_GetWindowFlags(window_);
|
||||
const SDL_WindowFlags flags = SDL_GetWindowFlags(window_);
|
||||
return (flags & SDL_WINDOW_MINIMIZED) != 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user