- added new default PNG format for doing screenshots AND the saving to disk is queued in a background thread allowing almost no delay when saving many screenshots!

This commit is contained in:
Mark Vejvoda 2010-10-30 02:21:47 +00:00
parent 369a0dc215
commit 77bad3ce07
5 changed files with 315 additions and 26 deletions

View File

@ -1197,13 +1197,15 @@ void Game::keyDown(char key) {
string path = GameConstants::folder_path_screenshots;
if(isdir(path.c_str()) == true) {
Config &config= Config::getInstance();
string fileFormat = config.getString("ScreenShotFileType","bmp");
string fileFormat = config.getString("ScreenShotFileType","png");
for(int i=0; i<250; ++i){
unsigned int queueSize = Renderer::getInstance().getSaveScreenQueueSize();
for(int i=0; i < 250; ++i) {
path = GameConstants::folder_path_screenshots;
path += "screen" + intToStr(i) + "." + fileFormat;
path += "screen" + intToStr(i + queueSize) + "." + fileFormat;
FILE *f= fopen(path.c_str(), "rb");
if(f==NULL) {
if(f == NULL) {
Renderer::getInstance().saveScreen(path);
break;
}

View File

@ -152,12 +152,11 @@ Renderer::Renderer() {
modelRenderer = NULL;
textRenderer = NULL;
particleRenderer = NULL;
saveScreenShotThread = NULL;
lastRenderFps=MIN_FPS_NORMAL_RENDERING;
shadowsOffDueToMinRender=false;
pixmapScreenShot = NULL;;
//resources
for(int i=0; i < rsCount; ++i) {
modelManager[i] = NULL;
@ -189,9 +188,13 @@ Renderer::Renderer() {
particleManager[i]= graphicsFactory->newParticleManager();
fontManager[i]= graphicsFactory->newFontManager();
}
saveScreenShotThread = new SimpleTaskThread(this,0,25);
saveScreenShotThread->setUniqueID(__FILE__);
saveScreenShotThread->start();
}
Renderer::~Renderer(){
Renderer::~Renderer() {
delete modelRenderer;
modelRenderer = NULL;
delete textRenderer;
@ -211,12 +214,50 @@ Renderer::~Renderer(){
fontManager[i] = NULL;
}
delete pixmapScreenShot;
// Wait for the queue to become empty or timeout the thread at 7 seconds
for(time_t elapsed = time(NULL);
getSaveScreenQueueSize() > 0 && difftime(time(NULL),elapsed) <= 7;) {
sleep(10);
}
delete saveScreenShotThread;
saveScreenShotThread = NULL;
if(getSaveScreenQueueSize() > 0) {
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d] FORCING MEMORY CLEANUP and NOT SAVING screenshots, saveScreenQueue.size() = %d\n",__FILE__,__FUNCTION__,__LINE__,saveScreenQueue.size());
for(std::list<std::pair<string,Pixmap2D *> >::iterator iter = saveScreenQueue.begin();
iter != saveScreenQueue.end(); ++iter) {
delete iter->second;
}
}
this->menu = NULL;
this->game = NULL;
}
void Renderer::simpleTask() {
// This code reads pixmaps from a queue and saves them to disk
Pixmap2D *savePixMapBuffer=NULL;
string path="";
MutexSafeWrapper safeMutex(&saveScreenShotThreadAccessor);
if(saveScreenQueue.size() > 0) {
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d] saveScreenQueue.size() = %d\n",__FILE__,__FUNCTION__,__LINE__,saveScreenQueue.size());
savePixMapBuffer = saveScreenQueue.front().second;
path = saveScreenQueue.front().first;
saveScreenQueue.pop_front();
}
safeMutex.ReleaseLock();
if(savePixMapBuffer != NULL) {
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d] about to save [%s]\n",__FILE__,__FUNCTION__,__LINE__,path.c_str());
savePixMapBuffer->save(path);
delete savePixMapBuffer;
}
}
Renderer &Renderer::getInstance(){
static Renderer renderer;
return renderer;
@ -2945,21 +2986,13 @@ void Renderer::loadConfig(){
}
}
void Renderer::saveScreen(const string &path){
void Renderer::saveScreen(const string &path) {
const Metrics &sm= Metrics::getInstance();
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
//Pixmap2D pixmap(sm.getScreenW(), sm.getScreenH(), 3);
if( pixmapScreenShot == NULL ||
pixmapScreenShot->getW() != sm.getScreenW() ||
pixmapScreenShot->getH() != sm.getScreenH()) {
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
delete pixmapScreenShot;
pixmapScreenShot = new Pixmap2D(sm.getScreenW(), sm.getScreenH(), 3);
}
Pixmap2D *pixmapScreenShot = new Pixmap2D(sm.getScreenW(), sm.getScreenH(), 3);
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
glFinish();
@ -2969,11 +3002,23 @@ void Renderer::saveScreen(const string &path){
GL_RGB, GL_UNSIGNED_BYTE, pixmapScreenShot->getPixels());
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
pixmapScreenShot->save(path);
// Signal the threads queue to add a screenshot save request
MutexSafeWrapper safeMutex(&saveScreenShotThreadAccessor);
saveScreenQueue.push_back(make_pair(path,pixmapScreenShot));
safeMutex.ReleaseLock();
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
}
unsigned int Renderer::getSaveScreenQueueSize() {
MutexSafeWrapper safeMutex(&saveScreenShotThreadAccessor);
int queueSize = saveScreenQueue.size();
safeMutex.ReleaseLock();
return queueSize;
}
// ==================== PRIVATE ====================
float Renderer::computeSunAngle(float time) {

View File

@ -31,6 +31,7 @@
#include "model.h"
#include "graphics_interface.h"
#include "base_renderer.h"
#include "simple_threads.h"
#ifdef DEBUG_RENDERING_ENABLED
# define IF_DEBUG_EDITION(x) x
@ -44,6 +45,7 @@
namespace Glest{ namespace Game{
using namespace Shared::Graphics;
using namespace Shared::PlatformCommon;
// =====================================================
// class MeshCallbackTeamColor
@ -131,7 +133,7 @@ public:
};
class Renderer : public RendererInterface, public BaseRenderer {
class Renderer : public RendererInterface, public BaseRenderer, public SimpleTaskCallbackInterface {
public:
//progress bar
static const int maxProgressBar;
@ -242,7 +244,9 @@ private:
bool useQuadCache;
Pixmap2D *pixmapScreenShot;
SimpleTaskThread *saveScreenShotThread;
Mutex saveScreenShotThreadAccessor;
std::list<std::pair<string,Pixmap2D *> > saveScreenQueue;
private:
Renderer();
@ -386,6 +390,7 @@ public:
void removeUnitFromQuadCache(const Unit *unit);
uint64 getCurrentPixelByteCount(ResourceScope rs=rsGame) const;
unsigned int getSaveScreenQueueSize();
private:
//private misc
@ -421,6 +426,8 @@ private:
void renderTile(const Vec2i &pos);
void renderQuad(int x, int y, int w, int h, const Texture2D *texture);
void simpleTask();
//static
static Texture2D::Filter strToTextureFilter(const string &s);
};

View File

@ -33,7 +33,7 @@ namespace Shared{ namespace Graphics{
// class PixmapIo
// =====================================================
class PixmapIo{
class PixmapIo {
protected:
int w;
int h;
@ -58,7 +58,7 @@ public:
// class PixmapIoTga
// =====================================================
class PixmapIoTga: public PixmapIo{
class PixmapIoTga: public PixmapIo {
private:
FILE *file;
@ -78,7 +78,7 @@ public:
// class PixmapIoBmp
// =====================================================
class PixmapIoBmp: public PixmapIo{
class PixmapIoBmp: public PixmapIo {
private:
FILE *file;
@ -94,6 +94,27 @@ public:
virtual void write(uint8 *pixels);
};
// =====================================================
// class PixmapIoBmp
// =====================================================
class PixmapIoPng: public PixmapIo {
private:
FILE *file;
string path;
public:
PixmapIoPng();
virtual ~PixmapIoPng();
virtual void openRead(const string &path);
virtual void read(uint8 *pixels);
virtual void read(uint8 *pixels, int components);
virtual void openWrite(const string &path, int w, int h, int components);
virtual void write(uint8 *pixels);
};
// =====================================================
// class Pixmap1D
// =====================================================
@ -157,7 +178,7 @@ public:
void save(const string &path);
void saveBmp(const string &path);
void saveTga(const string &path);
void savePng(const string &path);
//get
int getW() const {return w;}

View File

@ -21,6 +21,8 @@
#include "randomgen.h"
#include "FileReader.h"
#include "ImageReaders.h"
#include <png.h>
#include <setjmp.h>
#include "leak_dumper.h"
@ -285,7 +287,7 @@ void PixmapIoBmp::openWrite(const string &path, int w, int h, int components) {
file= fopen(path.c_str(),"wb");
if (file == NULL) {
throw runtime_error("Can't open BMP file for writting: "+ path);
throw runtime_error("Can't open BMP file for writing: "+ path);
}
BitmapFileHeader fileHeader;
@ -321,6 +323,210 @@ void PixmapIoBmp::write(uint8 *pixels) {
}
}
// =====================================================
// class PixmapIoPng
// =====================================================
PixmapIoPng::PixmapIoPng() {
file= NULL;
}
PixmapIoPng::~PixmapIoPng() {
if(file!=NULL){
fclose(file);
file=NULL;
}
}
void PixmapIoPng::openRead(const string &path) {
throw runtime_error("PixmapIoPng::openRead not implemented!");
/*
file= fopen(path.c_str(),"rb");
if (file==NULL){
throw runtime_error("Can't open BMP file: "+ path);
}
//read file header
BitmapFileHeader fileHeader;
size_t readBytes = fread(&fileHeader, sizeof(BitmapFileHeader), 1, file);
if(fileHeader.type1!='B' || fileHeader.type2!='M'){
throw runtime_error(path +" is not a bitmap");
}
//read info header
BitmapInfoHeader infoHeader;
readBytes = fread(&infoHeader, sizeof(BitmapInfoHeader), 1, file);
if(infoHeader.bitCount!=24){
throw runtime_error(path+" is not a 24 bit bitmap");
}
h= infoHeader.height;
w= infoHeader.width;
components= 3;
*/
}
void PixmapIoPng::read(uint8 *pixels) {
throw runtime_error("PixmapIoPng::read not implemented!");
//read(pixels, 3);
}
void PixmapIoPng::read(uint8 *pixels, int components) {
throw runtime_error("PixmapIoPng::read #2 not implemented!");
/*
for(int i=0; i<h*w*components; i+=components) {
uint8 r, g, b;
size_t readBytes = fread(&b, 1, 1, file);
readBytes = fread(&g, 1, 1, file);
readBytes = fread(&r, 1, 1, file);
switch(components){
case 1:
pixels[i]= (r+g+b)/3;
break;
case 3:
pixels[i]= r;
pixels[i+1]= g;
pixels[i+2]= b;
break;
case 4:
pixels[i]= r;
pixels[i+1]= g;
pixels[i+2]= b;
pixels[i+3]= 255;
break;
}
}
*/
}
void PixmapIoPng::openWrite(const string &path, int w, int h, int components) {
this->path = path;
this->w= w;
this->h= h;
this->components= components;
file= fopen(path.c_str(),"wb");
if (file == NULL) {
throw runtime_error("Can't open PNG file for writing: "+ path);
}
}
void PixmapIoPng::write(uint8 *pixels) {
// Allocate write & info structures
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(!png_ptr) {
fclose(file);
throw runtime_error("OpenGlDevice::saveImageAsPNG() - out of memory creating write structure");
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if(!info_ptr) {
png_destroy_write_struct(&png_ptr,
(png_infopp)NULL);
fclose(file);
throw runtime_error("OpenGlDevice::saveImageAsPNG() - out of memery creating info structure");
}
// setjmp() must be called in every function that calls a PNG-writing
// libpng function, unless an alternate error handler was installed--
// but compatible error handlers must either use longjmp() themselves
// (as in this program) or exit immediately, so here we go: */
if(setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(file);
throw runtime_error("OpenGlDevice::saveImageAsPNG() - setjmp problem");
}
// make sure outfile is (re)opened in BINARY mode
png_init_io(png_ptr, file);
// set the compression levels--in general, always want to leave filtering
// turned on (except for palette images) and allow all of the filters,
// which is the default; want 32K zlib window, unless entire image buffer
// is 16K or smaller (unknown here)--also the default; usually want max
// compression (NOT the default); and remaining compression flags should
// be left alone
//png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
//
// this is default for no filtering; Z_FILTERED is default otherwise:
// png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
// these are all defaults:
// png_set_compression_mem_level(png_ptr, 8);
// png_set_compression_window_bits(png_ptr, 15);
// png_set_compression_method(png_ptr, 8);
// Set some options: color_type, interlace_type
int color_type=0, interlace_type=0, numChannels=0;
// color_type = PNG_COLOR_TYPE_GRAY;
// color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
color_type = PNG_COLOR_TYPE_RGB;
numChannels = 3;
// color_type = PNG_COLOR_TYPE_RGB_ALPHA;
interlace_type = PNG_INTERLACE_NONE;
// interlace_type = PNG_INTERLACE_ADAM7;
int bit_depth = 8;
png_set_IHDR(png_ptr, info_ptr, this->w, this->h, bit_depth,
color_type,
interlace_type,
PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
// Optional gamma chunk is strongly suggested if you have any guess
// as to the correct gamma of the image. (we don't have a guess)
//
// png_set_gAMA(png_ptr, info_ptr, image_gamma);
// write all chunks up to (but not including) first IDAT
png_write_info(png_ptr, info_ptr);
// set up the row pointers for the image so we can use png_write_image
png_bytep* row_pointers = new png_bytep[this->h];
if (row_pointers == 0) {
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(file);
throw runtime_error("OpenGlDevice::failed to allocate memory for row pointers");
}
unsigned int row_stride = this->w * numChannels;
unsigned char *rowptr = (unsigned char*) pixels;
for (int row = this->h-1; row >=0 ; row--) {
row_pointers[row] = rowptr;
rowptr += row_stride;
}
// now we just write the whole image; libpng takes care of interlacing for us
png_write_image(png_ptr, row_pointers);
// since that's it, we also close out the end of the PNG file now--if we
// had any text or time info to write after the IDATs, second argument
// would be info_ptr, but we optimize slightly by sending NULL pointer: */
png_write_end(png_ptr, info_ptr);
//
// clean up after the write
// free any memory allocated & close the file
//
png_destroy_write_struct(&png_ptr, &info_ptr);
delete [] row_pointers;
//fclose(file);
}
// =====================================================
// class Pixmap1D
// =====================================================
@ -512,6 +718,9 @@ void Pixmap2D::save(const string &path) {
else if(toLower(extension) == "tga") {
saveTga(path);
}
else if(toLower(extension) == "png") {
savePng(path);
}
else {
throw runtime_error("Unknown pixmap extension: " + extension);
}
@ -528,7 +737,12 @@ void Pixmap2D::saveTga(const string &path) {
pst.openWrite(path, w, h, components);
pst.write(pixels);
}
void Pixmap2D::savePng(const string &path) {
PixmapIoPng pst;
pst.openWrite(path, w, h, components);
pst.write(pixels);
}
void Pixmap2D::getPixel(int x, int y, uint8 *value) const {
for(int i=0; i<components; ++i){
value[i]= pixels[(w*y+x)*components+i];