develop
shevernitskiy 2023-09-02 08:38:33 +03:00
parent 5b34ac63e1
commit 469a97f781
2 changed files with 66 additions and 15 deletions

@ -2594,6 +2594,11 @@ invalidates the ``texpos`` value that used to point to that texture.
The ``textures`` module solves this problem by providing a stable handle instead of a The ``textures`` module solves this problem by providing a stable handle instead of a
raw ``texpos``. When we need to draw a particular tile, we can look up the current raw ``texpos``. When we need to draw a particular tile, we can look up the current
``texpos`` value via the handle. ``texpos`` value via the handle.
Texture module can register textures in two ways: to reserved and dynamic ranges.
Reserved range is a limit buffer in a game texture vector, that will never be wiped.
It is good for static assets, which need to be loaded at the very beginning and will be used during the process running.
In other cases, it is better to use dynamic range.
If reserved range buffer limit has been reached, dynamic range will be used by default.
* ``loadTileset(file, tile_px_w, tile_px_h, reserved?)`` * ``loadTileset(file, tile_px_w, tile_px_h, reserved?)``

@ -1,3 +1,4 @@
#include <algorithm>
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
#include <numeric> #include <numeric>
@ -34,6 +35,7 @@ struct ReservedRange {
this->start = start; this->start = start;
this->end = start + ReservedRange::size; this->end = start + ReservedRange::size;
this->current = start; this->current = start;
this->is_installed = true;
} }
long get_new_texpos() { long get_new_texpos() {
if (this->current == this->end) if (this->current == this->end)
@ -47,6 +49,7 @@ struct ReservedRange {
int32_t start = -1; int32_t start = -1;
int32_t end = -1; int32_t end = -1;
long current = -1; long current = -1;
bool is_installed = false;
}; };
static ReservedRange reserved_range{}; static ReservedRange reserved_range{};
@ -54,6 +57,7 @@ static std::unordered_map<TexposHandle, long> g_handle_to_texpos;
static std::unordered_map<TexposHandle, long> g_handle_to_reserved_texpos; static std::unordered_map<TexposHandle, long> g_handle_to_reserved_texpos;
static std::unordered_map<TexposHandle, SDL_Surface*> g_handle_to_surface; static std::unordered_map<TexposHandle, SDL_Surface*> g_handle_to_surface;
static std::unordered_map<std::string, std::vector<TexposHandle>> g_tileset_to_handles; static std::unordered_map<std::string, std::vector<TexposHandle>> g_tileset_to_handles;
static std::vector<TexposHandle> g_delayed_regs;
static std::mutex g_adding_mutex; static std::mutex g_adding_mutex;
static std::atomic<bool> loading_state = false; static std::atomic<bool> loading_state = false;
@ -134,6 +138,12 @@ std::vector<TexposHandle> slice_tileset(SDL_Surface* surface, int tile_px_w, int
int dimx = surface->w / tile_px_w; int dimx = surface->w / tile_px_w;
int dimy = surface->h / tile_px_h; int dimy = surface->h / tile_px_h;
if (reserved && (dimx * dimy > reserved_range.end - reserved_range.current)) {
WARN(textures).print(
"there is not enough space in reserved range for whole tileset, using dynamic range\n");
reserved = false;
}
for (int y = 0; y < dimy; y++) { for (int y = 0; y < dimy; y++) {
for (int x = 0; x < dimx; x++) { for (int x = 0; x < dimx; x++) {
SDL_Surface* tile = DFSDL_CreateRGBSurface( SDL_Surface* tile = DFSDL_CreateRGBSurface(
@ -153,26 +163,35 @@ std::vector<TexposHandle> slice_tileset(SDL_Surface* surface, int tile_px_w, int
TexposHandle Textures::loadTexture(SDL_Surface* surface, bool reserved) { TexposHandle Textures::loadTexture(SDL_Surface* surface, bool reserved) {
if (!surface || !enabler) if (!surface || !enabler)
return 0; // should be some error, i guess return 0; // should be some error, i guess
if (loading_state) { if (loading_state)
ERR(textures).printerr("unable to load texture during game loading\n"); reserved = true; // use reserved range during loading for all textures
return 0;
}
auto handle = reinterpret_cast<uintptr_t>(surface); auto handle = reinterpret_cast<uintptr_t>(surface);
g_handle_to_surface.emplace(handle, surface); g_handle_to_surface.emplace(handle, surface);
surface->refcount++; // prevent destruct on next FreeSurface by game surface->refcount++; // prevent destruct on next FreeSurface by game
if (reserved) {
if (reserved && reserved_range.is_installed) {
auto texpos = reserved_range.get_new_texpos(); auto texpos = reserved_range.get_new_texpos();
if (texpos == -1) { if (texpos != -1) {
insert_texture(surface, texpos);
g_handle_to_reserved_texpos.emplace(handle, texpos);
return handle;
}
if (loading_state) { // if we in loading state and reserved range is full -> error
ERR(textures).printerr("reserved range limit has been reached, use dynamic range\n"); ERR(textures).printerr("reserved range limit has been reached, use dynamic range\n");
return 0; return 0;
} }
insert_texture(surface, texpos); }
g_handle_to_reserved_texpos.emplace(handle, texpos);
// if we here in loading state = true, then it should be dynamic range -> delay reg
if (loading_state) {
g_delayed_regs.push_back(handle);
} else { } else {
auto texpos = add_texture(surface); auto texpos = add_texture(surface);
g_handle_to_texpos.emplace(handle, texpos); g_handle_to_texpos.emplace(handle, texpos);
} }
return handle; return handle;
} }
@ -192,10 +211,9 @@ std::vector<TexposHandle> Textures::loadTileset(const std::string& file, int til
surface = canonicalize_format(surface); surface = canonicalize_format(surface);
auto handles = slice_tileset(surface, tile_px_w, tile_px_h, reserved); auto handles = slice_tileset(surface, tile_px_w, tile_px_h, reserved);
DEBUG(textures).print("loaded %zd textures from '%s' to %s range\n", handles.size(), DEBUG(textures).print("loaded %zd textures from '%s'\n", handles.size(), file.c_str());
file.c_str(), reserved ? "reserved" : "dynamic");
g_tileset_to_handles[file] = handles; g_tileset_to_handles[file] = handles;
return handles; return handles;
} }
@ -207,12 +225,14 @@ long Textures::getTexposByHandle(TexposHandle handle) {
return g_handle_to_reserved_texpos[handle]; return g_handle_to_reserved_texpos[handle];
if (g_handle_to_texpos.contains(handle)) if (g_handle_to_texpos.contains(handle))
return g_handle_to_texpos[handle]; return g_handle_to_texpos[handle];
if (std::find(g_delayed_regs.begin(), g_delayed_regs.end(), handle) != g_delayed_regs.end())
return 0;
if (g_handle_to_surface.contains(handle)) { if (g_handle_to_surface.contains(handle)) {
if (loading_state) {
ERR(textures).printerr("unable reinit texture from dynamic range during loading\n");
return -1;
}
g_handle_to_surface[handle]->refcount++; // prevent destruct on next FreeSurface by game g_handle_to_surface[handle]->refcount++; // prevent destruct on next FreeSurface by game
if (loading_state) { // reinit dor dynamic range during loading -> delayed
g_delayed_regs.push_back(handle);
return 0;
}
auto texpos = add_texture(g_handle_to_surface[handle]); auto texpos = add_texture(g_handle_to_surface[handle]);
g_handle_to_texpos.emplace(handle, texpos); g_handle_to_texpos.emplace(handle, texpos);
return texpos; return texpos;
@ -284,6 +304,15 @@ static void reset_surface() {
g_handle_to_surface.clear(); g_handle_to_surface.clear();
} }
static void register_delayed_handles() {
DEBUG(textures).print("register delayed handles, size %zd\n", g_delayed_regs.size());
for (auto& handle : g_delayed_regs) {
auto texpos = add_texture(g_handle_to_surface[handle]);
g_handle_to_texpos.emplace(handle, texpos);
}
g_delayed_regs.clear();
}
// reset point on New Game // reset point on New Game
struct tracking_stage_new_region : df::viewscreen_new_regionst { struct tracking_stage_new_region : df::viewscreen_new_regionst {
typedef df::viewscreen_new_regionst interpose_base; typedef df::viewscreen_new_regionst interpose_base;
@ -292,7 +321,10 @@ struct tracking_stage_new_region : df::viewscreen_new_regionst {
if (this->m_raw_load_stage != this->raw_load_stage) { if (this->m_raw_load_stage != this->raw_load_stage) {
TRACE(textures).print("raw_load_stage %d -> %d\n", this->m_raw_load_stage, TRACE(textures).print("raw_load_stage %d -> %d\n", this->m_raw_load_stage,
this->raw_load_stage); this->raw_load_stage);
bool tmp_state = loading_state;
loading_state = this->raw_load_stage >= 0 && this->raw_load_stage < 3 ? true : false; loading_state = this->raw_load_stage >= 0 && this->raw_load_stage < 3 ? true : false;
if (tmp_state != loading_state && !loading_state)
register_delayed_handles();
this->m_raw_load_stage = this->raw_load_stage; this->m_raw_load_stage = this->raw_load_stage;
if (this->m_raw_load_stage == 1) if (this->m_raw_load_stage == 1)
reset_texpos(); reset_texpos();
@ -312,7 +344,10 @@ struct tracking_stage_adopt_region : df::viewscreen_adopt_regionst {
DEFINE_VMETHOD_INTERPOSE(void, logic, ()) { DEFINE_VMETHOD_INTERPOSE(void, logic, ()) {
if (this->m_cur_step != this->cur_step) { if (this->m_cur_step != this->cur_step) {
TRACE(textures).print("step %d -> %d\n", this->m_cur_step, this->cur_step); TRACE(textures).print("step %d -> %d\n", this->m_cur_step, this->cur_step);
bool tmp_state = loading_state;
loading_state = this->cur_step >= 0 && this->cur_step < 3 ? true : false; loading_state = this->cur_step >= 0 && this->cur_step < 3 ? true : false;
if (tmp_state != loading_state && !loading_state)
register_delayed_handles();
this->m_cur_step = this->cur_step; this->m_cur_step = this->cur_step;
if (this->m_cur_step == 1) if (this->m_cur_step == 1)
reset_texpos(); reset_texpos();
@ -332,7 +367,10 @@ struct tracking_stage_load_region : df::viewscreen_loadgamest {
DEFINE_VMETHOD_INTERPOSE(void, logic, ()) { DEFINE_VMETHOD_INTERPOSE(void, logic, ()) {
if (this->m_cur_step != this->cur_step) { if (this->m_cur_step != this->cur_step) {
TRACE(textures).print("step %d -> %d\n", this->m_cur_step, this->cur_step); TRACE(textures).print("step %d -> %d\n", this->m_cur_step, this->cur_step);
bool tmp_state = loading_state;
loading_state = this->cur_step >= 0 && this->cur_step < 3 ? true : false; loading_state = this->cur_step >= 0 && this->cur_step < 3 ? true : false;
if (tmp_state != loading_state && !loading_state)
register_delayed_handles();
this->m_cur_step = this->cur_step; this->m_cur_step = this->cur_step;
if (this->m_cur_step == 1) if (this->m_cur_step == 1)
reset_texpos(); reset_texpos();
@ -352,7 +390,10 @@ struct tracking_stage_new_arena : df::viewscreen_new_arenast {
DEFINE_VMETHOD_INTERPOSE(void, logic, ()) { DEFINE_VMETHOD_INTERPOSE(void, logic, ()) {
if (this->m_cur_step != this->cur_step) { if (this->m_cur_step != this->cur_step) {
TRACE(textures).print("step %d -> %d\n", this->m_cur_step, this->cur_step); TRACE(textures).print("step %d -> %d\n", this->m_cur_step, this->cur_step);
bool tmp_state = loading_state;
loading_state = this->cur_step >= 0 && this->cur_step < 3 ? true : false; loading_state = this->cur_step >= 0 && this->cur_step < 3 ? true : false;
if (tmp_state != loading_state && !loading_state)
register_delayed_handles();
this->m_cur_step = this->cur_step; this->m_cur_step = this->cur_step;
if (this->m_cur_step == 0) if (this->m_cur_step == 0)
reset_texpos(); reset_texpos();
@ -380,6 +421,11 @@ static void uninstall_reset_point() {
} }
static void reserve_static_range() { static void reserve_static_range() {
if (static_cast<size_t>(enabler->textures.init_texture_size) != enabler->textures.raws.size()) {
WARN(textures).print(
"reserved range can't be installed! all textures will be loaded to dynamic range!");
return;
}
reserved_range.init(enabler->textures.init_texture_size); reserved_range.init(enabler->textures.init_texture_size);
auto dummy_surface = auto dummy_surface =
DFSDL_CreateRGBSurfaceWithFormat(0, 0, 0, 32, SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGBA32); DFSDL_CreateRGBSurfaceWithFormat(0, 0, 0, 32, SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGBA32);