revision of events

This commit is contained in:
Ondřej Novák 2025-01-27 17:33:59 +01:00
parent 858c4384e8
commit 669f72908e
33 changed files with 661 additions and 382 deletions

View file

@ -1,9 +1,28 @@
#include <cstdarg>
#include <cstdint>
#include <iostream>
extern "C" {
#include "error.h"
}
#include "platform.h"
void display_error(const char *text) {
std::cerr << "ERROR:" << text << std::endl;
}
static std::uint32_t gtick = get_game_tick_count();
void send_log_impl(int task, const char *format, ...) {
va_list args;
char buff2[1000];
va_start(args, format);
auto reltik = get_game_tick_count() - gtick;
double sec = reltik * 0.001;
std::cerr << sec << "[" << task << "]";
vsnprintf(buff2,1000,format, args);
std::cerr << buff2 << std::endl;
va_end(args);
}

View file

@ -1,3 +1,4 @@
#pragma once
void display_error(const char *text);
void send_log_impl(int task, const char *format, ...);

View file

@ -4,6 +4,7 @@
#include <thread>
#include <atomic>
#include <memory>
#include <queue>
#include <unordered_map>
struct TaskInfo {
@ -13,6 +14,7 @@ struct TaskInfo {
std::chrono::system_clock::time_point _wake_up_after = {};
int wake_up_msg = -1;
bool request_exit = false;
bool exited = false;
TaskInfo(int id):id(id) {}
};
@ -32,41 +34,83 @@ static std::atomic<bool> resume_master_flag = {false};
static TaskInfo *current_task_inst = NULL;
static EVENT_MSG *cur_message = NULL;
struct MsgQueue {
EVENT_MSG *msg;
TaskInfo *sender;
};
static std::queue<MsgQueue> msg_queue;
void flush_message_queue();
static void switch_to_task(TaskInfo *task) {
if (task == current_task_inst) return;
if (task == NULL) {
TaskInfo *me = current_task_inst;
current_task_inst = NULL;
me->resume_flag = false;
resume_master_flag = true;
resume_master_flag.notify_all();
me->resume_flag.wait(false);
me->resume_flag = false;
} else if (current_task_inst == NULL) {
if (task->exited) return ;
current_task_inst = task;
resume_master_flag = false;
task->resume_flag = true;
task->resume_flag.notify_all();
resume_master_flag.wait(false);
resume_master_flag = false;
flush_message_queue();
} else {
if (task->exited) return ;
TaskInfo *me = current_task_inst;
me->resume_flag = false;
current_task_inst = task;
task->resume_flag = true;
task->resume_flag.notify_all();
me->resume_flag.wait(false);
me->resume_flag = false;
}
}
static void clean_task_table() {
for (auto iter = task_list.begin(); iter != task_list.end();) {
if (iter->second->exited) {
iter = task_list.erase(iter);
} else {
++iter;
}
}
}
static void clean_up_current_task() {
TaskInfo *me = current_task_inst;
if (!me) return;
int id = me->id;
me->thr.detach();
task_list.erase(id);
me->exited = true;
current_task_inst = NULL;
resume_master_flag = true;
resume_master_flag.notify_all();
}
void flush_message_queue() {
while (!msg_queue.empty()) {
auto m = msg_queue.front();
msg_queue.pop();
for (auto &[id, task]: task_list) {
if (task->wake_up_msg == m.msg->msg && task.get() != m.sender) {
EVENT_MSG cpy;
cpy.msg = m.msg->msg;
va_copy(cpy.data, m.msg->data);
cur_message = &cpy;
switch_to_task(task.get());
va_end(cpy.data);
cur_message = NULL;
}
}
clean_task_table();
}
}
int add_task(int stack,TaskerFunctionName fcname,...) {
int id = get_new_task_id();
auto st = task_list.emplace(id, std::make_unique<TaskInfo>(id));
@ -75,6 +119,7 @@ int add_task(int stack,TaskerFunctionName fcname,...) {
va_start(args, fcname);
new_task->thr = std::thread([&]{
new_task->resume_flag.wait(false);
new_task->resume_flag = false;
fcname(args);
clean_up_current_task();
});
@ -93,17 +138,13 @@ char is_running(int id_num) {
return id_num < 0 || task_list.find(id_num) != task_list.end();
}
void unsuspend_task(EVENT_MSG *msg) {
for (auto &[id, task]: task_list) {
if (task->wake_up_msg == msg->msg) {
EVENT_MSG cpy;
cpy.msg = msg->msg;
va_copy(cpy.data, msg->data);
cur_message = &cpy;
switch_to_task(task.get());
va_end(cpy.data);
cur_message = NULL;
}
msg_queue.push({msg, current_task_inst});
if (current_task_inst) {
switch_to_task(NULL);
} else {
flush_message_queue();
}
}
void task_sleep(void) {
if (current_task_inst) {
@ -115,13 +156,14 @@ void task_sleep(void) {
switch_to_task(task.get());
}
}
clean_task_table();
}
}
EVENT_MSG *task_wait_event(int32_t event_number) {
if (current_task_inst == NULL) return NULL;
current_task_inst->wake_up_msg = event_number;
switch_to_task(NULL);
return NULL;
return cur_message;
}
int q_any_task() {
return task_list.size();

View file

@ -26,9 +26,10 @@ char DXInit64(char inwindow,int zoom,int monitor, int refresh) {
get_sdl_global_context().init_screen(mode, "Skeldal"); //todo allow change
screen_buffer = std::make_unique<uint16_t[]>(screen_pitch*480);
buffer2nd = std::make_unique<uint16_t[]>(screen_pitch*480);
std::fill(screen_buffer.get(), screen_buffer.get()+screen_pitch*480,0);
render_target = screen_buffer.get();
return 0;
return 1;
}
void DXCloseMode() {
@ -77,7 +78,7 @@ void StripBlt(void *data, unsigned int startline, uint32_t width) {
while (width--)
{
memcpy(start,data,640*2);
data=(void *)(reinterpret_cast<char *>(data)+get_sdl_global_context().get_surface_pitch());
data=(void *)(reinterpret_cast<short *>(data)+GetScreenPitch());
start=start+GetScreenPitch();
}

View file

@ -1,3 +1,4 @@
add_subdirectory(tests)
add_library(skeldal_sdl sdl_context.cpp BGraph2.cpp input.cpp sound.cpp)
add_library(skeldal_sdl sdl_context.cpp BGraph2.cpp input.cpp sound.cpp)
set_property(TARGET skeldal_sdl PROPERTY CXX_STANDARD 20)

View file

@ -15,14 +15,15 @@ void SetWheelMapping(char up, char down) { //todo
}
static MS_EVENT ms_event = {};
void get_ms_event(MS_EVENT *event) {
*event = ms_event;
*event = get_sdl_global_context().getMsEvent();
}
void ShareCPU() {
if (q_is_mastertask()) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}

View file

@ -1,4 +1,7 @@
#include "sdl_context.h"
#include <atomic>
#include <cassert>
#include "../platform.h"
#include <stdexcept>
@ -44,6 +47,11 @@ SDLContext::SDLContext() {
void SDLContext::init_screen(DisplayMode mode, const char *title) {
char buff[256];
static Uint32 update_request_event = SDL_RegisterEvents(1);
_update_request_event = update_request_event;
assert(!_render_thread.joinable());
int width = 640;
int height = 480;
@ -51,90 +59,137 @@ void SDLContext::init_screen(DisplayMode mode, const char *title) {
width*=2;
height*=2;
}
SDL_Window *window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
width, height, SDL_WINDOW_RESIZABLE|(mode==fullscreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0));
if (!window) {
snprintf(buff, sizeof(buff), "SDL Error create window: %s\n", SDL_GetError());
throw std::runtime_error(buff);
}
_window.reset(window);
SDL_Renderer *renderer = SDL_CreateRenderer(_window.get(), -1, 0);
if (!renderer) {
snprintf(buff,sizeof(buff), "Chyba při vytváření rendereru: %s\n", SDL_GetError());
throw std::runtime_error(buff);
}
_renderer.reset(renderer);
// Vytvoření softwarového backbufferu (surface)
SDL_Surface *backbuffer = SDL_CreateRGBSurfaceWithFormat(0, 640, 480, 16, SDL_PIXELFORMAT_RGB565);
if (!backbuffer) {
snprintf(buff,sizeof(buff), "Chyba při vytváření surface: %s\n", SDL_GetError());
throw std::runtime_error(buff);
}
_surface.reset(backbuffer);
// Vytvoření textury pro zobrazení backbufferu
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, 640, 480);
if (!texture) {
snprintf(buff, sizeof(buff), "Chyba při vytváření textury: %s\n", SDL_GetError());
throw std::runtime_error(buff);
}
_texture.reset(texture);
SDL_LockSurface(_surface.get());
if (!_timer_event) _timer_event = SDL_RegisterEvents(1);
_fullscreen_mode = mode == fullscreen;
std::atomic<bool> done = false;
std::exception_ptr e;
_render_thread = std::jthread([&](std::stop_token stp){
bool err = false;
try {
SDL_Window *window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
width, height, SDL_WINDOW_RESIZABLE|(mode==fullscreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0));
if (!window) {
snprintf(buff, sizeof(buff), "SDL Error create window: %s\n", SDL_GetError());
throw std::runtime_error(buff);
}
_window.reset(window);
SDL_Renderer *renderer = SDL_CreateRenderer(_window.get(), -1, 0);
if (!renderer) {
snprintf(buff,sizeof(buff), "Chyba při vytváření rendereru: %s\n", SDL_GetError());
throw std::runtime_error(buff);
}
_renderer.reset(renderer);
// Vytvoření textury pro zobrazení backbufferu
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, 640, 480);
if (!texture) {
snprintf(buff, sizeof(buff), "Chyba při vytváření textury: %s\n", SDL_GetError());
throw std::runtime_error(buff);
}
_texture.reset(texture);
} catch (...) {
e = std::current_exception();
err = true;
}
done = true;
done.notify_all();
if (!err) event_loop(stp);
_texture.reset();
_renderer.reset();
_window.reset();
});
done.wait(false);
if (e) {
_render_thread.join();
std::rethrow_exception(e);
}
}
void SDLContext::close_screen() {
SDL_UnlockSurface(_surface.get());
_texture.reset();
_surface.reset();
_renderer.reset();
_window.reset();
_render_thread.request_stop();
_render_thread.join();
}
uint16_t* SDLContext::get_surface_addr() {
return reinterpret_cast<uint16_t *>(_surface->pixels);
void SDLContext::event_loop(std::stop_token stp) {
static Uint32 exit_loop_event = SDL_RegisterEvents(1);
std::stop_callback stopcb(stp,[&]{
SDL_Event event;
event.type = exit_loop_event;
SDL_PushEvent(&event);
});
SDL_Event e;
while (SDL_WaitEvent(&e)) {
if (e.type == SDL_QUIT) {
return;
}
if (e.type == exit_loop_event) {
return;
}
if (e.type == _update_request_event) {
update_screen();
}
if (e.type == SDL_KEYDOWN) {
if (e.key.keysym.sym == SDLK_RETURN && (e.key.keysym.mod & KMOD_ALT)) {
_fullscreen_mode = !_fullscreen_mode;
SDL_SetWindowFullscreen(_window.get(), _fullscreen_mode ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
} else if (e.type == SDL_MOUSEMOTION) {
int mouseX = e.motion.x;
int mouseY = e.motion.y;
int windowWidth;
int windowHeight;
SDL_GetWindowSize(_window.get(), &windowWidth, &windowHeight);
float normalizedX = (float)mouseX / windowWidth;
float normalizedY = (float)mouseY / windowHeight;
ms_event.event = 1;
ms_event.event_type = 1;
ms_event.x = (int16_t)(640*normalizedX);
ms_event.y = (int16_t)(480*normalizedY);
} else if (e.type == SDL_MOUSEBUTTONDOWN || e.type == SDL_MOUSEBUTTONUP) {
int button = e.button.button;
int up = e.type == SDL_MOUSEBUTTONUP?1:0;
ms_event.event = 1;
ms_event.event_type = (1<<(2*button-1+up));
switch (button) {
default: break;
case 1: ms_event.tl1 = !up; break;
case 2: ms_event.tl2 = !up; break;
case 3: ms_event.tl3 = !up; break;
}
}
}
}
int32_t SDLContext::get_surface_pitch() {
return _surface->pitch;
}
void SDLContext::charge_timer() {
_active_timer = SDL_AddTimer(1000/TIMERSPEED, [](Uint32 interval, void *param) -> Uint32 {
SDLContext *me = reinterpret_cast<SDLContext *>(param);
SDL_Event* event = (SDL_Event*)param;
event->type = me->_timer_event;
SDL_PushEvent(event);
return 0;
}, this);
}
/*
void SDLContext::pool_events() {
if (!_active_timer.has_value()) charge_timer();
SDL_RenderClear(_renderer.get());
SDL_RenderCopy(_renderer.get(), _texture.get(), NULL, NULL);
SDL_RenderPresent(_renderer.get());
ms_event.event = 0;
SDL_Event e;
while (true) {
if (SDL_WaitEvent(&e)) {
if (e.type == SDL_QUIT) {
_quit_requested = true;
return;
}
if (e.type == _timer_event) {
break;
}
if (e.type == SDL_KEYDOWN) {
if (e.key.keysym.sym == SDLK_RETURN && (e.key.keysym.mod & KMOD_ALT)) {
_fullscreen_mode = !_fullscreen_mode;
SDL_SetWindowFullscreen(_window.get(), _fullscreen_mode ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
} else if (e.type == SDL_MOUSEMOTION) {
std::lock_guard _(_mx);
int mouseX = e.motion.x;
int mouseY = e.motion.y;
int windowWidth;
@ -147,6 +202,7 @@ void SDLContext::pool_events() {
ms_event.x = (int16_t)(640*normalizedX);
ms_event.y = (int16_t)(480*normalizedY);
} else if (e.type == SDL_MOUSEBUTTONDOWN || e.type == SDL_MOUSEBUTTONUP) {
std::lock_guard _(_mx);
int button = e.button.button;
int up = e.type == SDL_MOUSEBUTTONUP?1:0;
ms_event.event = 1;
@ -164,21 +220,46 @@ void SDLContext::pool_events() {
}
}
charge_timer();
}*/
}
void SDLContext::present_rect(uint16_t *pixels, unsigned int pitch,
unsigned int x, unsigned int y, unsigned int xs, unsigned ys) {
auto beg = pixels + y * pitch + y;
auto beg = pixels + y * pitch + x;
SDL_Rect r = {static_cast<int>(x),
static_cast<int>(y),
static_cast<int>(xs),
static_cast<int>(ys)};
SDL_UpdateTexture(_texture.get(), &r, beg, pitch*2);
SDL_RenderClear(_renderer.get());
SDL_RenderCopy(_renderer.get(), _texture.get(), NULL, NULL);
SDL_RenderPresent(_renderer.get());
std::vector<short> data;
data.resize(xs*ys);
auto iter = data.begin();
for (unsigned int yp = 0; yp <ys; ++yp) {
iter = std::copy(beg, beg+xs,iter );
beg+=pitch;
}
std::lock_guard _(_mx);
if (_display_update_queue.empty()) {
SDL_Event event;
event.type = _update_request_event;
SDL_PushEvent(&event);
}
_display_update_queue.push_back({std::move(r), std::move(data)});
}
void SDLContext::update_screen() {
{
std::lock_guard _(_mx);
for (const UpdateMsg &msg:_display_update_queue) {
SDL_UpdateTexture(_texture.get(), &msg.rc, msg.data.data(), msg.rc.w*2);
}
_display_update_queue.clear();
}
SDL_RenderClear(_renderer.get());
SDL_RenderCopy(_renderer.get(), _texture.get(), NULL, NULL);
SDL_RenderPresent(_renderer.get());
}

View file

@ -3,6 +3,8 @@
#include <memory>
#include <optional>
#include <SDL2/SDL.h>
#include <thread>
#include <vector>
#include <mouse.h>
class SDLContext {
@ -22,14 +24,15 @@ public:
void close_screen();
uint16_t *get_surface_addr();
int32_t get_surface_pitch();
void pool_events();
void present_rect(uint16_t *pixels, unsigned int pitch, unsigned int x, unsigned int y, unsigned int xs,unsigned ys);
MS_EVENT getMsEvent() {
std::lock_guard _(_mx);
MS_EVENT out = ms_event;
ms_event.event = 0;
return out;
}
protected:
struct SDL_Deleter {
@ -43,13 +46,26 @@ protected:
std::unique_ptr<SDL_Window, SDL_Deleter> _window;
std::unique_ptr<SDL_Renderer, SDL_Deleter> _renderer;
std::unique_ptr<SDL_Surface, SDL_Deleter> _surface;
std::unique_ptr<SDL_Texture, SDL_Deleter> _texture;
std::optional<SDL_TimerID> _active_timer;
Uint32 _timer_event = 0;
std::jthread _render_thread;
bool _quit_requested = false;
bool _fullscreen_mode = false;
bool _present = false;
void charge_timer();
void event_loop(std::stop_token stp);
std::mutex _mx;
struct UpdateMsg {
SDL_Rect rc;
std::vector<short> data;
};
std::vector<UpdateMsg> _display_update_queue;
Uint32 _update_request_event;
void update_screen();
};

View file

@ -74,5 +74,10 @@ void DoneVideoSound(void *buffer) {
}
const char *device_name(int )
{
return "SDL sound device";
}