Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion libgag/include/SDLGraphicContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,8 @@ namespace GAGCore
};

friend class GraphicContext;
friend class Toolkit;
static void resetHighResolutionState();

std::string fileName;
//#define DEBUG_SPRITE_NOT_DRAWN
Expand Down Expand Up @@ -544,9 +546,15 @@ namespace GAGCore
public:
//! Opt into batching variable-size frames; callers must finishDrawingSprite.
bool createTextureAtlas(bool allowVariableSizes = false);
struct HighResolutionStats {size_t cpuBytes=0, coloredFrames=0;};
struct HighResolutionStats
{
size_t cpuBytes = 0, coloredFrames = 0;
// Cumulative load work for this Toolkit session, including failed pack reads.
size_t manifestParses = 0, imageLoads = 0, packReloads = 0;
};
static HighResolutionStats highResolutionStats();
static void setHighResolution(bool enabled);
static void reloadHighResolutionPack();
static void flushBatches(GraphicContext *gc);
//! Constructor
Sprite() : fileName("not loaded yet") { }
Expand Down
3 changes: 3 additions & 0 deletions libgag/src/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ if not env['server']:
aspect_test = aspect_env.Program('FullscreenAspectHarness',
[aspect_env.Object('FullscreenAspectHarness.o', '#test/FullscreenAspectHarness.cpp'), l1])
env.Alias('aspect-test', aspect_test)
artwork_test = aspect_env.Program('ArtworkPackLifecycleHarness',
[aspect_env.Object('ArtworkPackLifecycleHarness.o', '#test/ArtworkPackLifecycleHarness.cpp'), l1])
env.Alias('artwork-pack-test', artwork_test)
resize_test = aspect_env.Program('WindowResizeHarness',
[aspect_env.Object('WindowResizeHarness.o', '#test/WindowResizeHarness.cpp'), l1])
env.Alias('resize-test', resize_test)
Expand Down
176 changes: 126 additions & 50 deletions libgag/src/Sprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <unordered_map>

#if __cplusplus >= 201402L
#include <memory>
Expand Down Expand Up @@ -50,24 +51,74 @@ using boost::make_unique;
namespace GAGCore
{
static std::set<Sprite*> loadedSprites;
static bool highResolutionEnabled = false;
static std::string packDirectory, packText;
static bool packRead=false;
static bool readPack(const std::string &directory)
{
if(packRead && packDirectory==directory)return !packText.empty();
packRead=true;packDirectory=directory;packText.clear();
auto input=Toolkit::getFileManager()->open((directory+"/frames.txt").c_str(),"rb");
if(!input){std::cerr<<"High-resolution pack unavailable: "<<directory<<std::endl;return false;}
const auto size=SDL_RWsize(input);
if(size<=0||size>1024*1024){SDL_RWclose(input);std::cerr<<"Invalid high-resolution manifest size"<<std::endl;return false;}
std::string text(static_cast<size_t>(size),'\0');
const auto count=SDL_RWread(input,text.data(),1,text.size());SDL_RWclose(input);
if(count!=text.size())return false;
std::istringstream header(text);std::string magic;int version=0;header>>magic>>version;
if(magic!="GLOB2_HIGHRES"||version!=1){std::cerr<<"Unsupported high-resolution pack"<<std::endl;return false;}
packText=std::move(text);return true;
}
namespace
{
struct PackFrame
{
int width, height, scale;
std::string base, team;
};
struct ArtworkSelection
{
bool enabled = false;
bool gpu = false;
std::string directory;
bool operator==(const ArtworkSelection&) const = default;
};
ArtworkSelection artwork;
bool artworkSelected = false;
bool highResolutionRequested = false;
bool packRead = false;
std::unordered_map<std::string, PackFrame> packFrames;
Sprite::HighResolutionStats loadStats;

ArtworkSelection selectedArtwork(bool requested, GraphicContext* gc)
{
const char* overrideDir = std::getenv("GLOB2_EXPERIMENT_TEXTURE_DIR");
const bool gpu = gc && (gc->getOptionFlags() & GraphicContext::USEGPU);
return {(requested || overrideDir) && gpu, gpu, overrideDir ? overrideDir : "data/highres/v1"};
}

void readPack()
{
if (packRead) return;
packRead = true;
++loadStats.manifestParses;
auto input = Toolkit::getFileManager()->open((artwork.directory + "/frames.txt").c_str(), "rb");
if (!input)
{
std::cerr << "High-resolution pack unavailable: " << artwork.directory << std::endl;
return;
}
const auto size = SDL_RWsize(input);
if (size <= 0 || size > 1024 * 1024)
{
SDL_RWclose(input);
std::cerr << "Invalid high-resolution manifest size" << std::endl;
return;
}
std::string text(static_cast<size_t>(size), '\0');
const auto count = SDL_RWread(input, text.data(), 1, text.size());
SDL_RWclose(input);
if (count != text.size())
{
std::cerr << "Truncated high-resolution manifest: " << artwork.directory << std::endl;
return;
}
std::istringstream stream(text);
std::string magic, id;
int version = 0;
stream >> magic >> version;
if (magic != "GLOB2_HIGHRES" || version != 1)
{
std::cerr << "Unsupported high-resolution pack" << std::endl;
return;
}
PackFrame frame;
while (stream >> id >> frame.width >> frame.height >> frame.scale >> frame.base >> frame.team)
packFrames.emplace(id, frame);
}
}

Sprite::RotatedImage::~RotatedImage()
{
Expand All @@ -84,6 +135,7 @@ namespace GAGCore
SDL_RWops *rotatedStream;
unsigned i = 0;

if (!artworkSelected) setHighResolution(highResolutionRequested);
this->fileName = filename;
loadedSprites.insert(this);

Expand Down Expand Up @@ -265,7 +317,7 @@ namespace GAGCore

Sprite::HighResolutionStats Sprite::highResolutionStats()
{
HighResolutionStats stats;
HighResolutionStats stats = loadStats;
for(auto sprite:loadedSprites)
{
for(auto s:sprite->experimentImages)if(s)stats.cpuBytes+=s->getW()*s->getH()*4;
Expand All @@ -283,11 +335,31 @@ namespace GAGCore

void Sprite::setHighResolution(bool enabled)
{
highResolutionEnabled = enabled;
packRead=false;packText.clear();
highResolutionRequested = enabled;
const auto selection = selectedArtwork(enabled, Toolkit::gc);
if (artworkSelected && artwork == selection) return;
reloadHighResolutionPack();
}

void Sprite::reloadHighResolutionPack()
{
if (Toolkit::gc) flushBatches(Toolkit::gc);
artwork = selectedArtwork(highResolutionRequested, Toolkit::gc);
artworkSelected = true;
packRead = false;
packFrames.clear();
++loadStats.packReloads;
for (auto sprite : loadedSprites) sprite->reloadHighResolution();
}

void Sprite::resetHighResolutionState()
{
artwork = {};
artworkSelected = highResolutionRequested = packRead = false;
packFrames.clear();
loadStats = {};
}

void Sprite::reloadHighResolution()
{
highResolutionAtlas.reset();
Expand Down Expand Up @@ -324,13 +396,13 @@ namespace GAGCore
for(int i=0;i<count;++i)if(!experimentImages[i]){reject();return;}
GLint maxSize=0;glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSize);
if(atlasW>maxSize || atlasH>maxSize){reject();return;}
const char *overrideDir=std::getenv("GLOB2_EXPERIMENT_TEXTURE_DIR");
std::string directory=overrideDir?overrideDir:"data/highres/v1";
const auto& directory = artwork.directory;
std::vector<std::unique_ptr<DrawableSurface>> levels;
for(int mip=0;mip<4;++mip)
{
auto rw=Toolkit::getFileManager()->open((directory+"/"+prefix+"-atlas-mip"+std::to_string(mip)+".png").c_str(),"rb");
if(!rw){reject();return;}
++loadStats.imageLoads;
auto s=IMG_Load_RW(rw,1);if(!s){reject();return;}
if(s->w!=(atlasW>>mip)||s->h!=(atlasH>>mip)){SDL_FreeSurface(s);reject();return;}
levels.emplace_back(new DrawableSurface(s));SDL_FreeSurface(s);
Expand Down Expand Up @@ -376,36 +448,38 @@ namespace GAGCore
{
const size_t index=experimentImages.size();
experimentImages.push_back(nullptr); experimentRotated.push_back(nullptr);
const char *overrideDir=std::getenv("GLOB2_EXPERIMENT_TEXTURE_DIR");
if ((!highResolutionEnabled && !overrideDir) || !Toolkit::gc || !(Toolkit::gc->getOptionFlags() & GraphicContext::USEGPU)) return;
std::string directory=overrideDir ? overrideDir : "data/highres/v1";
if(!readPack(directory))return;
std::istringstream stream(packText);std::string magic,id,base,team;int version,w,h,scale;
stream>>magic>>version;
std::string wanted=frameName.substr(frameName.find_last_of('/')+1);wanted.resize(wanted.size()-4);
while(stream>>id>>w>>h>>scale>>base>>team)
if (!artwork.enabled) return;
readPack();
std::string wanted = frameName.substr(frameName.find_last_of('/') + 1);
wanted.resize(wanted.size() - 4);
const auto found = packFrames.find(wanted);
if (found == packFrames.end()) return;
const auto& [w, h, scale, base, team] = found->second;
const auto& id = found->first;
const auto& directory = artwork.directory;
if (w != getW(index) || h != getH(index) || scale != 4)
{
if(id!=wanted)continue;
if(w!=getW(index)||h!=getH(index)||scale!=4){std::cerr<<"High-resolution dimensions rejected: "<<id<<std::endl;return;}
auto load=[&](const std::string &name,DrawableSurface *original)->DrawableSurface*
{
if(name=="-")return nullptr;
if(name.find_first_of("/\\:")!=std::string::npos || name.find("..")!=std::string::npos)return nullptr;
SDL_RWops *rw=Toolkit::getFileManager()->open((directory+"/"+name).c_str(),"rb");
if(!rw)return nullptr;
SDL_Surface *surface=IMG_Load_RW(rw,1);if(!surface)return nullptr;
int lw=original?original->getW():w,lh=original?original->getH():h;
if(surface->w!=lw*scale||surface->h!=lh*scale){SDL_FreeSurface(surface);return nullptr;}
auto result=new DrawableSurface(surface);result->highResolutionSampling=true;SDL_FreeSurface(surface);return result;
};
auto normal=load(base,images[index]);
auto colored=load(team,rotated[index]?rotated[index]->orig:nullptr);
if((base!="-"&&!normal)||(team!="-"&&!colored)||(images[index]&&base=="-")||(rotated[index]&&team=="-"))
{delete normal;delete colored;std::cerr<<"High-resolution frame rejected: "<<id<<std::endl;return;}
experimentImages.back()=normal;
if(colored)experimentRotated.back()=new RotatedImage(colored);
std::cerr << "High-resolution dimensions rejected: " << id << std::endl;
return;
}
auto load=[&](const std::string &name,DrawableSurface *original)->DrawableSurface*
{
if(name=="-")return nullptr;
if(name.find_first_of("/\\:")!=std::string::npos || name.find("..")!=std::string::npos)return nullptr;
SDL_RWops *rw=Toolkit::getFileManager()->open((directory+"/"+name).c_str(),"rb");
if(!rw)return nullptr;
++loadStats.imageLoads;
SDL_Surface *surface=IMG_Load_RW(rw,1);if(!surface)return nullptr;
int lw=original?original->getW():w,lh=original?original->getH():h;
if(surface->w!=lw*scale||surface->h!=lh*scale){SDL_FreeSurface(surface);return nullptr;}
auto result=new DrawableSurface(surface);result->highResolutionSampling=true;SDL_FreeSurface(surface);return result;
};
auto normal=load(base,images[index]);
auto colored=load(team,rotated[index]?rotated[index]->orig:nullptr);
if((base!="-"&&!normal)||(team!="-"&&!colored)||(images[index]&&base=="-")||(rotated[index]&&team=="-"))
{delete normal;delete colored;std::cerr<<"High-resolution frame rejected: "<<id<<std::endl;return;}
experimentImages.back()=normal;
if(colored)experimentRotated.back()=new RotatedImage(colored);
}

DrawableSurface *Sprite::prepareDrawSurface(unsigned index, bool teamColor, bool experiment)
Expand Down Expand Up @@ -457,6 +531,7 @@ namespace GAGCore
{
if (frameStream)
{
++loadStats.imageLoads;
SDL_Surface *sprite = IMG_Load_RW(frameStream, 0);
assert(sprite);
images.push_back(new DrawableSurface(sprite));
Expand All @@ -467,6 +542,7 @@ namespace GAGCore

if (rotatedStream)
{
++loadStats.imageLoads;
SDL_Surface *sprite = IMG_Load_RW(rotatedStream, 0);
assert(sprite);
rotated.push_back(new RotatedImage(new DrawableSurface(sprite)));
Expand Down
2 changes: 2 additions & 0 deletions libgag/src/Toolkit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@ namespace GAGCore
void Toolkit::close(void)
{
#ifndef YOG_SERVER_ONLY
if (gc) Sprite::flushBatches(gc);
for (SpriteMap::iterator it=spriteMap.begin(); it!=spriteMap.end(); ++it)
delete (*it).second;
spriteMap.clear();
Sprite::resetHighResolutionState();
for (FontMap::iterator it=fontMap.begin(); it!=fontMap.end(); ++it)
delete (*it).second;
fontMap.clear();
Expand Down
1 change: 1 addition & 0 deletions src/GlobalContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ void GlobalContainer::loadClient(void)
// create graphic context
gfx = Toolkit::initGraphic(settings.screenWidth, settings.screenHeight, settings.screenFlags, "Globulation 2", "glob 2");
gfx->setMinRes(640, 480);
Sprite::setHighResolution(settings.highResolutionArtwork);

// load data required for drawing progress screen
title = std::make_unique<DrawableSurface>("data/gfx/title.png");
Expand Down
2 changes: 1 addition & 1 deletion src/SettingsScreenGeneral.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ void SettingsScreen::buildGeneral()
auto& flags=globalContainer->settings.optionFlags;
if(v)flags|=GlobalContainer::OPTION_LOW_SPEED_GFX;else flags&=~GlobalContainer::OPTION_LOW_SPEED_GFX;commit();
});
toggle("graphics.artwork","High-resolution artwork","Apply artwork on the next game or editor load (OpenGL).",s.highResolutionArtwork,[this](int v){globalContainer->settings.highResolutionArtwork=v;commit();});
toggle("graphics.artwork","High-resolution artwork","Applies immediately (OpenGL).",s.highResolutionArtwork,[this](int v){globalContainer->settings.highResolutionArtwork=v;Sprite::setHighResolution(v);commit();});
toggle("graphics.torus","Automatic torus view","Automatically show the torus overview while moving around the map (OpenGL).",s.automaticTorus,[this](int v){globalContainer->settings.automaticTorus=v;commit();});
choice("graphics.renderer","Renderer","Changing the renderer requires a restart.",bool(s.screenFlags & GraphicContext::USEGPU),
{tr("Software"),"OpenGL"},[this](int v){changeDisplay([v](Settings& s){if(v)s.screenFlags|=GraphicContext::USEGPU;else s.screenFlags&=~GraphicContext::USEGPU;});});
Expand Down
2 changes: 0 additions & 2 deletions src/gui/GameGUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ GameGUI::GameGUI()

GameGUI::~GameGUI()
{
if (!globalContainer->runNoX) Sprite::setHighResolution(false);
for (ParticleSet::iterator it = particles.begin(); it != particles.end(); ++it)
delete *it;
if (globalContainer->settings.rememberUnit)
Expand All @@ -84,7 +83,6 @@ void GameGUI::init()
torusView.reset();
torusPointerDown = false;
camera=MapCamera();zoomControlPushed=false;
if (!globalContainer->runNoX) Sprite::setHighResolution(globalContainer->settings.highResolutionArtwork);
notmenu = false;
isRunning=true;
gamePaused=false;
Expand Down
3 changes: 0 additions & 3 deletions src/map/edit/MapEditCtor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ MapEdit::MapEdit()
128, // height
Minimap::HideFOW)
{
Sprite::setHighResolution(globalContainer->settings.highResolutionArtwork);
doQuit=false;
doFullQuit=false;
doQuitAfterLoadSave=false;
Expand Down Expand Up @@ -307,8 +306,6 @@ MapEdit::MapEdit()

MapEdit::~MapEdit()
{
Sprite::setHighResolution(false);
Toolkit::releaseSprite("data/gui/editor");
for(std::vector<MapEditorWidget*>::iterator i=mew.begin(); i!=mew.end(); ++i)
{
delete *i;
Expand Down
Loading
Loading