Merge branch 'master' into indev

This commit is contained in:
Daemo
2026-08-09 00:15:07 +02:00
8 changed files with 92 additions and 79 deletions
+4 -3
View File
@@ -19,9 +19,10 @@ namespace mlx
enum class Event enum class Event
{ {
ResizeEventCode = 56, ResizeEventCode = 1,
FrameBeginEventCode = 57, SwapchainResizeEventCode,
FatalErrorEventCode = 168, FrameBeginEventCode,
FatalErrorEventCode,
EndEnum EndEnum
}; };
+12 -8
View File
@@ -1,26 +1,30 @@
#ifndef __MLX_FPS__ #ifndef __MLX_FPS__
#define __MLX_FPS__ #define __MLX_FPS__
#include <chrono>
namespace mlx namespace mlx
{ {
typedef std::chrono::steady_clock fps_clock;
class FpsManager class FpsManager
{ {
public: public:
FpsManager() = default; FpsManager() = default;
void Init(); void Init();
bool Update(); void WaitUntilNextFrame();
inline void SetMaxFPS(std::uint32_t fps) noexcept { m_max_fps = fps; m_ns = 1000000000.0 / fps; } inline void SetMaxFPS(std::uint32_t fps) noexcept { m_target_delta = fps_clock::duration(std::chrono::seconds(1)) / fps;}
~FpsManager() = default; ~FpsManager() = default;
private: private:
double m_ns = 1000000000.0 / 1'337'000.0; fps_clock::time_point m_current_time;
std::int64_t m_fps_before = 0; fps_clock::time_point m_target_time;
std::int64_t m_fps_now = 0; fps_clock::time_point m_last_time_record;
std::int64_t m_timer = 0; fps_clock::duration m_delta_time = fps_clock::duration().zero();
std::uint32_t m_max_fps = 1'337'000; fps_clock::duration m_target_delta = fps_clock::duration().zero();
std::uint32_t m_fps_elapsed_time = 0; fps_clock::duration m_sleep_margin = std::chrono::microseconds(200);
}; };
} }
+2 -3
View File
@@ -8,7 +8,7 @@
namespace mlx namespace mlx
{ {
Application::Application() : p_mem_manager(std::make_unique<MemManager>()), p_sdl_manager(std::make_unique<SDLManager>()), m_fps(), m_in() Application::Application() : p_mem_manager(std::make_unique<MemManager>()), p_sdl_manager(std::make_unique<SDLManager>()), m_fps(), m_in()
{ {
MLX_PROFILE_FUNCTION(); MLX_PROFILE_FUNCTION();
std::srand(std::time(nullptr)); std::srand(std::time(nullptr));
@@ -33,8 +33,7 @@ namespace mlx
while(m_in.IsRunning()) while(m_in.IsRunning())
{ {
if(!m_fps.Update()) m_fps.WaitUntilNextFrame();
continue;
m_in.FetchInputs(); m_in.FetchInputs();
+6 -7
View File
@@ -45,10 +45,9 @@ extern "C"
void mlx_set_fps_goal(mlx_context mlx, int fps) void mlx_set_fps_goal(mlx_context mlx, int fps)
{ {
MLX_CHECK_APPLICATION_POINTER(mlx); MLX_CHECK_APPLICATION_POINTER(mlx);
if(fps < 0) if(fps <= 0)
mlx::Error("You cannot set a negative FPS cap (nice try)"); fps = -1;
else mlx->app->SetFPSCap(static_cast<std::uint32_t>(fps));
mlx->app->SetFPSCap(static_cast<std::uint32_t>(fps));
} }
void mlx_destroy_context(mlx_context mlx) void mlx_destroy_context(mlx_context mlx)
@@ -313,14 +312,14 @@ extern "C"
mlx::Error("Font loader: filepath is NULL"); mlx::Error("Font loader: filepath is NULL");
return; return;
} }
std::filesystem::path file(filepath); std::filesystem::path file(filepath);
if (std::strcmp(filepath, "default") != 0 && !std::filesystem::exists(file)) if (std::strcmp(filepath, "default") != 0 && !std::filesystem::exists(file))
{ {
mlx::Error("TTF loader: unable to find file '%'", filepath); mlx::Error("TTF loader: unable to find file '%'", filepath);
return; return;
} }
if(std::strcmp(filepath, "default") != 0) if(std::strcmp(filepath, "default") != 0)
{ {
if(file.extension() != ".ttf" && file.extension() != ".tte") if(file.extension() != ".ttf" && file.extension() != ".tte")
@@ -350,7 +349,7 @@ extern "C"
mlx::Error("Font loader: filepath is NULL"); mlx::Error("Font loader: filepath is NULL");
return; return;
} }
std::filesystem::path file(filepath); std::filesystem::path file(filepath);
if (std::strcmp(filepath, "default") != 0 && !std::filesystem::exists(file)) if (std::strcmp(filepath, "default") != 0 && !std::filesystem::exists(file))
{ {
+23 -16
View File
@@ -1,30 +1,37 @@
#include <PreCompiled.h> #include <PreCompiled.h>
#include <Core/Fps.h> #include <Core/Fps.h>
#ifndef __APPLE__
#include <emmintrin.h>
#endif
namespace mlx namespace mlx
{ {
void FpsManager::Init() void FpsManager::Init()
{ {
m_timer = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count()); m_current_time = fps_clock::now();
m_fps_before = m_timer; m_target_time = m_current_time + m_target_delta;
m_fps_now = m_timer;
} }
bool FpsManager::Update() void FpsManager::WaitUntilNextFrame()
{ {
using namespace std::chrono_literals; m_current_time = fps_clock::now();
m_fps_now = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count()); if(m_current_time < m_target_time)
if(std::chrono::duration<std::uint64_t>{m_fps_now - m_timer} >= 1s)
m_timer += m_fps_now;
m_fps_elapsed_time = m_fps_now - m_fps_before;
if(m_fps_elapsed_time >= m_ns)
{ {
m_fps_before += m_ns; std::this_thread::sleep_until(m_target_time - m_sleep_margin);
return true; m_current_time = fps_clock::now();
while (m_current_time < m_target_time)
{
#ifndef __APPLE__
_mm_pause(); // reduces CPU usage on x86 without yielding
#endif
m_current_time = fps_clock::now();
}
} }
std::this_thread::sleep_for(std::chrono::duration<double, std::nano>(m_ns - 1)); else if (m_target_time < m_current_time - m_target_delta * 4)
return false; m_target_time = m_current_time;
m_target_time += m_target_delta;
m_delta_time = m_current_time - m_last_time_record;
m_last_time_record = m_current_time;
} }
} }
+3 -3
View File
@@ -8,9 +8,9 @@ namespace mlx
{ {
namespace Internal namespace Internal
{ {
struct ResizeEventBroadcast : public EventBase struct SwapchainResizeEventBroadcast : public EventBase
{ {
Event What() const override { return Event::ResizeEventCode; } Event What() const override { return Event::SwapchainResizeEventCode; }
}; };
} }
@@ -23,7 +23,7 @@ namespace mlx
if(!m_events_hooks.contains(window_id) || m_events_hooks[window_id][event].empty()) if(!m_events_hooks.contains(window_id) || m_events_hooks[window_id][event].empty())
return; return;
if(event == MLX_WINDOW_EVENT && code == 8) if(event == MLX_WINDOW_EVENT && code == 8)
EventBus::SendBroadcast(Internal::ResizeEventBroadcast{}); EventBus::SendBroadcast(Internal::SwapchainResizeEventBroadcast{});
for(const auto& hook : m_events_hooks[window_id][event]) for(const auto& hook : m_events_hooks[window_id][event])
{ {
if(hook.fn) if(hook.fn)
+40 -37
View File
@@ -226,25 +226,34 @@ namespace mlx
void Texture::SetRegion(int x, int y, int w, int h, mlx_color* pixels) noexcept void Texture::SetRegion(int x, int y, int w, int h, mlx_color* pixels) noexcept
{ {
MLX_PROFILE_FUNCTION(); MLX_PROFILE_FUNCTION();
if(x < 0 || y < 0 || static_cast<std::uint32_t>(x) >= m_width || static_cast<std::uint32_t>(y) >= m_height) if(w < 0 || h < 0 || x < -w || y < -h
return; || x >= static_cast<int>(m_width) || y >= static_cast<int>(m_height))
if(w < 0 || h < 0)
return; return;
if(!m_staging_buffer.has_value()) if(!m_staging_buffer.has_value())
OpenCPUBuffer(); OpenCPUBuffer();
for(std::uint32_t i = 0, moving_x = x, moving_y = y;; i++, moving_x++) const int
start_x = std::max<int>(x, 0),
start_y = std::max<int>(y, 0),
start_row = start_y * m_width,
start_i = (start_y - y) * w + (start_x - x),
end_x = std::min<int>(x + w, m_width),
end_y = std::min<int>(y + h, m_height),
end_row = end_y * m_width,
incr = (x + w) - end_x + (start_x - x);
for(int i = start_i, dx = start_x, row = start_row;; i++, dx++)
{ {
if(moving_x >= static_cast<std::uint32_t>(x + w) || moving_x >= m_width) if(dx >= end_x)
{ {
moving_x = x; i += incr;
moving_y++; dx = start_x;
if(moving_y >= static_cast<std::uint32_t>(y + h) || moving_y >= m_height) row += m_width;
if(row >= end_row)
break; break;
} }
if constexpr(std::endian::native == std::endian::little) if constexpr(std::endian::native == std::endian::little)
m_staging_buffer->GetMap<mlx_color*>()[(moving_y * m_width) + moving_x] = ReverseColor(pixels[i]); m_staging_buffer->GetMap<mlx_color*>()[row + dx] = ReverseColor(pixels[i]);
else else
m_staging_buffer->GetMap<mlx_color*>()[(moving_y * m_width) + moving_x] = pixels[i]; m_staging_buffer->GetMap<mlx_color*>()[row + dx] = pixels[i];
} }
m_has_been_modified = true; m_has_been_modified = true;
} }
@@ -252,23 +261,25 @@ namespace mlx
void Texture::SetLinearRegion(int x, int y, std::size_t len, mlx_color* pixels) noexcept void Texture::SetLinearRegion(int x, int y, std::size_t len, mlx_color* pixels) noexcept
{ {
MLX_PROFILE_FUNCTION(); MLX_PROFILE_FUNCTION();
if(x < 0 || y < 0 || static_cast<std::uint32_t>(x) >= m_width || static_cast<std::uint32_t>(y) >= m_height) if(x >= static_cast<int>(m_width) || y >= static_cast<int>(m_height))
return; return;
if(!m_staging_buffer.has_value()) if(!m_staging_buffer.has_value())
OpenCPUBuffer(); OpenCPUBuffer();
int
start = y * m_width + x,
dest_start = std::max<int>(start, 0),
src_start = dest_start - start,
dest_end = std::min<int>(start + len, m_width * m_height);
if constexpr(std::endian::native == std::endian::little) if constexpr(std::endian::native == std::endian::little)
{ {
for(std::size_t i = 0; i < len && (y * m_width) + x + i < m_width * m_height; i++) for(int i = dest_start, j = src_start; i < dest_end; i++, j++)
m_staging_buffer->GetMap<mlx_color*>()[(y * m_width) + x + i] = ReverseColor(pixels[i]); m_staging_buffer->GetMap<mlx_color*>()[i] = ReverseColor(pixels[j]);
} }
else else
{ {
std::size_t len_guard; std::memcpy(
if((y * m_width + x + len) < m_width * m_height) &m_staging_buffer->GetMap<mlx_color*>()[dest_start],
len_guard = len; &pixels[src_start], dest_end - dest_start);
else
len_guard = len - (m_width * m_height - (y * m_width + x + len));
std::memcpy(&m_staging_buffer->GetMap<mlx_color*>()[(y * m_width) + x], pixels, len_guard);
} }
m_has_been_modified = true; m_has_been_modified = true;
} }
@@ -289,23 +300,23 @@ namespace mlx
void Texture::GetRegion(int x, int y, int w, int h, mlx_color* dst) noexcept void Texture::GetRegion(int x, int y, int w, int h, mlx_color* dst) noexcept
{ {
MLX_PROFILE_FUNCTION(); MLX_PROFILE_FUNCTION();
if(x < 0 || y < 0 || static_cast<std::uint32_t>(x) >= m_width || static_cast<std::uint32_t>(y) >= m_height) if(w < 0 || h < 0 || x < 0 || y < 0 || static_cast<std::uint32_t>(x + w) >= m_width || static_cast<std::uint32_t>(y + h) >= m_height)
return; return;
if(!m_staging_buffer.has_value()) if(!m_staging_buffer.has_value())
OpenCPUBuffer(); OpenCPUBuffer();
for(std::uint32_t i = 0, moving_x = x, moving_y = y;; i++, moving_x++) for(std::uint32_t i = 0, dx = x, row = y * m_width;; i++, dx++)
{ {
if(moving_x >= static_cast<std::uint32_t>(x + w) || moving_x >= m_width) if(dx >= m_width)
{ {
moving_x = x; dx = x;
moving_y++; row += m_width;
if(moving_y >= static_cast<std::uint32_t>(y + h) || moving_y >= m_height) if(row >= m_height * m_width)
break; break;
} }
if constexpr(std::endian::native == std::endian::little) if constexpr(std::endian::native == std::endian::little)
dst[i] = ReverseColor(m_staging_buffer->GetMap<mlx_color*>()[(moving_y * m_width) + moving_x]); dst[i] = ReverseColor(m_staging_buffer->GetMap<mlx_color*>()[row + dx]);
else else
dst[i] = m_staging_buffer->GetMap<mlx_color*>()[(moving_y * m_width) + moving_x]; dst[i] = m_staging_buffer->GetMap<mlx_color*>()[row + dx];
} }
} }
@@ -320,16 +331,8 @@ namespace mlx
processed_color.g = static_cast<std::uint8_t>(color.g * 255.f); processed_color.g = static_cast<std::uint8_t>(color.g * 255.f);
processed_color.b = static_cast<std::uint8_t>(color.b * 255.f); processed_color.b = static_cast<std::uint8_t>(color.b * 255.f);
processed_color.a = static_cast<std::uint8_t>(color.a * 255.f); processed_color.a = static_cast<std::uint8_t>(color.a * 255.f);
if(processed_color.r == 0 && processed_color.g == 0 && processed_color.b == 0) for(std::size_t i = 0; i < m_width * m_height; i++)
std::memset(m_staging_buffer->GetMap(), processed_color.a, m_staging_buffer->GetSize()); m_staging_buffer->GetMap<mlx_color*>()[i] = processed_color;
else
{
for(std::size_t y = 0; y < m_height; y++)
{
for(std::size_t x = 0; x < m_width; x++)
m_staging_buffer->GetMap<mlx_color*>()[y * m_width + x] = processed_color;
}
}
} }
} }
+2 -2
View File
@@ -15,7 +15,7 @@ namespace mlx
Event What() const override { return Event::ResizeEventCode; } Event What() const override { return Event::ResizeEventCode; }
}; };
} }
std::string VulkanFormatName(VkFormat format) std::string VulkanFormatName(VkFormat format)
{ {
#define STRINGIFY(x) case x: return #x #define STRINGIFY(x) case x: return #x
@@ -161,7 +161,7 @@ namespace mlx
std::function<void(const EventBase&)> functor = [this](const EventBase& event) std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{ {
if(event.What() == Event::ResizeEventCode && !m_resize) if(event.What() == Event::SwapchainResizeEventCode && !m_resize)
m_resize = true; m_resize = true;
}; };
EventBus::RegisterListener({ functor, "mlx_swapchain_" + std::to_string(reinterpret_cast<std::uintptr_t>(this)) }); EventBus::RegisterListener({ functor, "mlx_swapchain_" + std::to_string(reinterpret_cast<std::uintptr_t>(this)) });