Compare commits

...

2 Commits

Author SHA1 Message Date
b8f041584f new update loop 2024-08-08 07:50:38 +03:00
a99f58e475 CacheStorage 2024-08-07 19:40:55 +03:00
13 changed files with 122 additions and 94 deletions

2
dependencies/imgui vendored

@ -1 +1 @@
Subproject commit 271910e3495e686a252b4b85369dec0605ba0d20 Subproject commit 7b6314f47d2aaa3758cfeeca66af34f5c9309ca4

@ -1 +1 @@
Subproject commit b921791be512bffc252ab555e47d48e0fb8261b5 Subproject commit e062aba71a93f4504180d1a4037b1849c95ba51d

View File

@ -20,7 +20,9 @@ f32 MainWindow::getDPI(){
return dpi; return dpi;
} }
void MainWindow::init(const char* window_title){ void MainWindow::open(const char* window_title, UpdatingFunc _update){
update = _update;
SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING)); SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING));
SDL_version v; SDL_version v;
SDL_GetVersion(&v); SDL_GetVersion(&v);
@ -76,9 +78,9 @@ void MainWindow::init(const char* window_title){
} }
// Wait, poll and handle events (inputs, window resize, etc.) // Wait, poll and handle events (inputs, window resize, etc.)
void MainWindow::poll_events(u16& frame_updates_requested, bool wait){ void MainWindow::poll_events(bool waitForEvent){
SDL_Event event; SDL_Event event;
if(wait){ if(waitForEvent){
// waits for first event in cpu-efficient way // waits for first event in cpu-efficient way
SDL_TRY(SDL_WaitEvent(&event) != 1); SDL_TRY(SDL_WaitEvent(&event) != 1);
} }
@ -87,8 +89,7 @@ void MainWindow::poll_events(u16& frame_updates_requested, bool wait){
return; return;
do { do {
if(ImGui_ImplSDL2_ProcessEvent(&event)) ImGui_ImplSDL2_ProcessEvent(&event);
frame_updates_requested=2;
switch(event.type){ switch(event.type){
case SDL_QUIT: { case SDL_QUIT: {
close(); close();
@ -159,62 +160,45 @@ void MainWindow::draw_frame(){
SDL_SetRenderDrawColor(sdl_renderer, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255)); SDL_SetRenderDrawColor(sdl_renderer, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255));
SDL_RenderClear(sdl_renderer); SDL_RenderClear(sdl_renderer);
// Resources::getCached // Example sprite
Resources::Texture sprite(sdl_renderer); //Resources::Texture tutel(Resources::getResource("tutel.png"), sdl_renderer);
sprite.loadFrom(Resources::getResource("tutel.png")); static Resources::CacheStorage<Resources::Texture> textures;
sprite.render(SDL_FRectConstruct(100, 100, 400, 400)); Resources::Texture& tutel = textures.getOrCreate("tutel.png", sdl_renderer);
tutel.render(SDL_FRectConstruct(100, 100, 400, 400));
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer); ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
// Swap buffers // Swap buffers
SDL_RenderPresent(sdl_renderer); SDL_RenderPresent(sdl_renderer);
} }
void MainWindow::startAndWait(){ void MainWindow::startUpdateLoop(){
if(loop_running) if(loop_running)
throw UsefulException("loop is already running"); throw UsefulException("loop is running already");
// draw first frame nsec_t prev_update_time_ns = getMonotonicTimeNsec();
draw_frame();
u16 frame_updates_requested=1;
u64 prev_update_time_ms=SDL_GetTicks64();
loop_running=true; loop_running=true;
// main loop // main loop
while(loop_running){ while(loop_running){
// waits for events poll_events(false);
main_loop_wait_for_input = false;
poll_events(frame_updates_requested, main_loop_wait_for_input);
if(frame_updates_requested==0) nsec_t update_time_ns = getMonotonicTimeNsec();
{ if(update_time_ns < prev_update_time_ns)
u64 update_time_ms=SDL_GetTicks64(); throw UsefulException("monotonic clock returned unexpected value");
if(update_time_ms >= prev_update_time_ms + 1000/fps_min){ f64 delta_time_s = (f64)(update_time_ns - prev_update_time_ns) / 1e9;
// if frame rate < fps_min then requests frame draw prev_update_time_ns = update_time_ns;
// works only if main_loop_wait_for_input = false
frame_updates_requested=1;
prev_update_time_ms=update_time_ms;
}
else {
// skips frame rendering and waits to limit fps
u32 frame_delay_ms=1000/fps_max;
SDL_Delay(frame_delay_ms);
continue;
}
}
// deaws requested number of frames update(delta_time_s);
while(frame_updates_requested>0) { draw_frame();
draw_frame();
frame_updates_requested--; nsec_t after_update_time_ns = getMonotonicTimeNsec();
nsec_t frame_delay_ns = (nsec_t)1e9 / fps_max - (after_update_time_ns - update_time_ns);
if(frame_delay_ns > 0){
std::cout<<"frameDelay: "<<frame_delay_ns / 1e9f<<std::endl;
SDL_Delay(frame_delay_ns / 1e6);
} }
} }
// Cleanup destroy();
MainWindow::destroy();
}
void MainWindow::close(){
loop_running=false;
} }
void MainWindow::destroy(){ void MainWindow::destroy(){
@ -226,6 +210,10 @@ void MainWindow::destroy(){
SDL_Quit(); SDL_Quit();
} }
void MainWindow::close(){
loop_running = false;
}
void MainWindow::draw_bg_window(){ void MainWindow::draw_bg_window(){
const ImGuiDockNodeFlags dockspace_flags = const ImGuiDockNodeFlags dockspace_flags =
ImGuiDockNodeFlags_PassthruCentralNode; ImGuiDockNodeFlags_PassthruCentralNode;
@ -292,13 +280,12 @@ void MainWindow::draw_bg_window(){
void MainWindow::draw_debug_window(){ void MainWindow::draw_debug_window(){
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui::Begin("Debug Options"); ImGui::Begin("Debug Options");
ImGui::ColorEdit3("clear_color", (float*)&clear_color); ImGui::ColorEdit3("clear_color", (float*)&clear_color);
ImGui::Checkbox("main_loop_wait_for_input", &main_loop_wait_for_input); ImGui::InputInt("fps_max", &fps_max);
ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::Checkbox("Demo Window", &show_demo_window); ImGui::Checkbox("Demo Window", &show_demo_window);
ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window); ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window);
ImGui::End(); ImGui::End();
if (show_demo_window) if (show_demo_window)

View File

@ -3,6 +3,7 @@
#include <SDL.h> #include <SDL.h>
#include <imgui.h> #include <imgui.h>
#include "../std.hpp" #include "../std.hpp"
#include "../time.hpp"
/// converts hex color to float vector /// converts hex color to float vector
#define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f) #define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f)
@ -13,30 +14,32 @@ namespace ougge::GUI {
#define default_font "DroidSans" #define default_font "DroidSans"
using UpdatingFunc = void (*)(f64 deltaTime);
class MainWindow { class MainWindow {
public: public:
ImVec4 clear_color = RGBAHexToF(35,35,50,255); ImVec4 clear_color = RGBAHexToF(35,35,50,255);
f32 default_font_size = 14.0f; f32 default_font_size = 14.0f;
u8 fps_min = 30; int fps_max = 30;
u8 fps_max = 60; // called on each frame
UpdatingFunc update = nullptr;
private: private:
bool loop_running=false; bool loop_running = false;
bool main_loop_wait_for_input=true;
bool show_demo_window = false; bool show_demo_window = false;
bool show_metrics_window = false; bool show_metrics_window = false;
SDL_Window* sdl_window = nullptr; SDL_Window* sdl_window = nullptr;
SDL_Renderer* sdl_renderer = nullptr; SDL_Renderer* sdl_renderer = nullptr;
public: public:
void init(const char* window_title); void open(const char* window_title, UpdatingFunc update);
void startAndWait(); void startUpdateLoop();
void close(); void close();
f32 getDPI();
private: private:
void destroy(); void destroy();
void poll_events(u16& frame_updates_requested, bool wait); f32 getDPI();
void poll_events(bool waitForEvent);
void draw_frame(); void draw_frame();
void draw_ui(); void draw_ui();
void draw_debug_window(); void draw_debug_window();

View File

@ -1,4 +1,3 @@
#include <unordered_map>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include "../exceptions.hpp" #include "../exceptions.hpp"

View File

@ -4,6 +4,8 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <functional> #include <functional>
#include <optional>
#include <unordered_map>
namespace ougge::Resources { namespace ougge::Resources {
@ -43,4 +45,21 @@ public:
Resource& getResource(const std::string& path); Resource& getResource(const std::string& path);
/// @brief stores requested resources in memory
/// @tparam T must implement constructor `T(const Resource&, ...)`
template<class T>
class CacheStorage {
std::unordered_map<std::string, T> _map;
public:
template<typename... TCtorArgs>
T& getOrCreate(const std::string& name, TCtorArgs&&... ctor_args){
auto it = _map.find(name);
if(it != _map.end())
return it->second;
auto& res = getResource(name);
auto e = _map.emplace(name, T(res, ctor_args...));
return e.first->second;
}
};
} }

View File

@ -23,6 +23,7 @@ extern "C" {
#endif #endif
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
typedef struct { typedef struct {
const char* path; const char* path;

View File

@ -22,7 +22,7 @@ ImFont* ImFont_LoadFromResource(const std::string& font_name, f32 font_size, f32
ImFontConfig font_cfg = ImFontConfig(); ImFontConfig font_cfg = ImFontConfig();
std::sprintf(font_cfg.Name, "%s %ipx", font_name.c_str(), (i32)font_size); std::sprintf(font_cfg.Name, "%s %ipx", font_name.c_str(), (i32)font_size);
Resource& res = getResource("fonts/" + font_name + ".ttf"); auto& res = getResource("fonts/" + font_name + ".ttf");
char* font_data = new char[res.size]; char* font_data = new char[res.size];
res.openStream()->read(font_data, res.size); res.openStream()->read(font_data, res.size);

View File

@ -4,29 +4,20 @@
namespace ougge::Resources { namespace ougge::Resources {
Texture::Texture(SDL_Renderer* renderer) Texture::Texture(const Resource& r, SDL_Renderer* renderer)
: renderer(renderer), texture(nullptr), w(0), h(0) : Texture(*r.openStream(), r.size, renderer)
{} {}
Texture::~Texture(){ Texture::Texture(std::istream& s, size_t size, SDL_Renderer* renderer)
SDL_DestroyTexture(texture); : renderer(renderer), texture(nullptr), w(0), h(0)
} {
void Texture::loadFrom(const Resource& r){
auto s = r.openStream();
loadFrom(*s, r.size);
}
void Texture::loadFrom(std::istream& s, size_t size){
if(texture)
throw UsefulException("texture has been loaded already");
SDL_RWops* sdl_stream = SDL_RWFromIStream(s, size); SDL_RWops* sdl_stream = SDL_RWFromIStream(s, size);
if(!sdl_stream) if(!sdl_stream)
throw SDLException(); throw SDLException();
texture = IMG_LoadTexture_RW(renderer, sdl_stream, 1); texture = std::shared_ptr<SDL_Texture>(IMG_LoadTexture_RW(renderer, sdl_stream, 1), SDL_DestroyTexture);
if(!texture) if(!texture)
throw IMGException(); throw IMGException();
SDL_TRY(SDL_QueryTexture(texture, nullptr, nullptr, &w, &h)); SDL_TRY(SDL_QueryTexture(texture.get(), nullptr, nullptr, &w, &h));
} }
SDL_RenderCopyExF_Params::SDL_RenderCopyExF_Params() SDL_RenderCopyExF_Params::SDL_RenderCopyExF_Params()
@ -35,19 +26,19 @@ SDL_RenderCopyExF_Params::SDL_RenderCopyExF_Params()
void Texture::render(const SDL_FRect& target_section){ void Texture::render(const SDL_FRect& target_section){
SDL_TRY( SDL_TRY(
SDL_RenderCopyF(renderer, texture, nullptr, &target_section) SDL_RenderCopyF(renderer, texture.get(), nullptr, &target_section)
); );
} }
void Texture::render(const SDL_FRect& target_section, const SDL_Rect& texture_section){ void Texture::render(const SDL_FRect& target_section, const SDL_Rect& texture_section){
SDL_TRY( SDL_TRY(
SDL_RenderCopyF(renderer, texture, &texture_section, &target_section) SDL_RenderCopyF(renderer, texture.get(), &texture_section, &target_section)
); );
} }
void Texture::render(const SDL_RenderCopyExF_Params& p){ void Texture::render(const SDL_RenderCopyExF_Params& p){
SDL_TRY( SDL_TRY(
SDL_RenderCopyExF(renderer, texture, SDL_RenderCopyExF(renderer, texture.get(),
optional_value_ptr_or_null(p.texture_section), optional_value_ptr_or_null(p.texture_section),
optional_value_ptr_or_null(p.target_section), optional_value_ptr_or_null(p.target_section),
p.rotation_angle, p.rotation_angle,

View File

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <optional> #include <optional>
#include <memory>
#include <SDL.h> #include <SDL.h>
#include "Resources.hpp" #include "Resources.hpp"
@ -29,15 +30,12 @@ struct SDL_RenderCopyExF_Params {
struct Texture { struct Texture {
SDL_Renderer* renderer; SDL_Renderer* renderer;
SDL_Texture* texture; std::shared_ptr<SDL_Texture> texture;
int w; int w;
int h; int h;
Texture(SDL_Renderer* renderer); Texture(const Resource& r, SDL_Renderer* renderer);
~Texture(); Texture(std::istream& s, size_t size, SDL_Renderer* renderer);
void loadFrom(const Resource& r);
void loadFrom(std::istream& s, size_t size);
void render(const SDL_FRect& target_section); void render(const SDL_FRect& target_section);
void render(const SDL_FRect& target_section, const SDL_Rect& texture_section); void render(const SDL_FRect& target_section, const SDL_Rect& texture_section);

View File

@ -8,15 +8,16 @@
using namespace ougge; using namespace ougge;
int main(int argc, const char** argv){ void update(f64 deltaTime){
if(setlocale(LC_CTYPE, "C.UTF-8")!=0) std::cout<<"deltaTime: "<<deltaTime<<std::endl;
std::cerr<<"\e[93msetlocale failed!\n"; }
int main(int argc, const char** argv){
try { try {
Resources::init(); Resources::init();
GUI::MainWindow w; GUI::MainWindow w;
w.init("ougge"); w.open("ougge", update);
w.startAndWait(); w.startUpdateLoop();
} }
catch(const std::exception& e){ catch(const std::exception& e){
std::cerr<<"Catched exception: "<<e.what()<<std::endl; std::cerr<<"Catched exception: "<<e.what()<<std::endl;

19
src/time.cpp Normal file
View File

@ -0,0 +1,19 @@
// posix version definition to use clock_gettime
#ifndef _XOPEN_SOURCE
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif
#endif
#include "time.hpp"
#include <ctime>
#include "UsefulException.hpp"
nsec_t getMonotonicTimeNsec(){
struct timespec t;
if(clock_gettime(CLOCK_MONOTONIC, &t) != 0)
throw UsefulException("clock_gettime(CLOCK_MONOTONIC) error");
return (nsec_t)t.tv_sec * 1e9 + (nsec_t)t.tv_nsec;
}

10
src/time.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "std.hpp"
/// nanoseconds
typedef i64 nsec_t;
/// can be used to measure delta time
///@return time from some moment in nanoseconds.
nsec_t getMonotonicTimeNsec();