-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved library performance by caching the internal image data, #9
base: master
Are you sure you want to change the base?
Changes from 5 commits
ad0f4d5
835a665
8b9105e
d2e4adc
520ca47
580885c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,22 +18,35 @@ | |
|
||
#include "libxbrzscale.h" | ||
|
||
#include <cstdio> | ||
#include <iostream> | ||
|
||
#ifndef XBRZLIB_RELATIVEPATHSDL | ||
#include <SDL2/SDL_endian.h> | ||
#include <SDL2/SDL_error.h> | ||
#include <SDL2/SDL_pixels.h> | ||
#include <SDL2/SDL_surface.h> | ||
#include <cstdio> | ||
#else | ||
#include "SDL_endian.h" | ||
#include "SDL_error.h" | ||
#include "SDL_pixels.h" | ||
#include "SDL_surface.h" | ||
#endif | ||
|
||
#include "xbrz/xbrz.h" | ||
|
||
//#include <cstdio> | ||
//#include <cstdint> | ||
//#include "SDL.h" | ||
//#include "SDL_image.h" | ||
//#include "xbrz/xbrz.h" | ||
|
||
bool libxbrzscale::bEnableOutput=false; | ||
|
||
bool libxbrzscale::bDbgMsg=false; | ||
|
||
bool libxbrzscale::bUseCache=false; | ||
|
||
bool libxbrzscale::bFreeInputSurfaceAfterScale=true; | ||
bool libxbrzscale::bFreeOutputSurfaceAfterScale=true; | ||
|
||
#ifndef XBRZLIB_NOINLINEGETSETPIX //this may be required to let some compillers linking actually work w/o error: "undefined reference to" these | ||
inline | ||
#endif | ||
Uint32 libxbrzscale::SDL_GetPixel(SDL_Surface *surface, int x, int y) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since those functions don't touch any instance properties or methods, they don't need to be inside the class. I think inline class methods should have implementation in the header if the class declaration is there. It is possible this caused IVAN build issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can try it. I upload a commit and travisci compile ivanWin (that was failing cuz of inline on cpp file). |
||
{ | ||
int bpp = surface->format->BytesPerPixel; | ||
|
@@ -61,6 +74,9 @@ Uint32 libxbrzscale::SDL_GetPixel(SDL_Surface *surface, int x, int y) | |
} | ||
} | ||
|
||
#ifndef XBRZLIB_NOINLINEGETSETPIX //this may be required to let some compillers linking actually work w/o error: "undefined reference to" these | ||
inline | ||
#endif | ||
void libxbrzscale::SDL_PutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel) | ||
{ | ||
int bpp = surface->format->BytesPerPixel; | ||
|
@@ -96,6 +112,7 @@ void libxbrzscale::SDL_PutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel | |
|
||
|
||
SDL_Surface* libxbrzscale::createARGBSurface(int w, int h) { | ||
if(bDbgMsg)printf("Creating SDL RGB surface w=%d h=%d\n",w,h); | ||
return SDL_CreateRGBSurface(0, w, h, 32, 0xff0000U, 0xff00U, 0xffU, 0xff000000U); | ||
} | ||
|
||
|
@@ -134,8 +151,37 @@ void displayImage(SDL_Surface* surface, const char* message) { | |
} | ||
*/ | ||
|
||
uint32_t* libxbrzscale::surfaceToUint32(SDL_Surface* img){ | ||
uint32_t *data = new uint32_t[img->w * img->h]; | ||
|
||
unsigned long lUInt32ImgCacheSizeIN=0; | ||
unsigned long lUInt32ImgCacheSizeOUT=0; | ||
uint32_t* pUInt32ImgCacheIN=NULL; | ||
uint32_t* pUInt32ImgCacheOUT=NULL; | ||
void DeleteCache(bool bIn){ | ||
if(bIn){ | ||
delete [] pUInt32ImgCacheIN; | ||
pUInt32ImgCacheIN=NULL; | ||
lUInt32ImgCacheSizeIN=0; | ||
}else{ | ||
delete [] pUInt32ImgCacheOUT; | ||
pUInt32ImgCacheOUT=NULL; | ||
lUInt32ImgCacheSizeOUT=0; | ||
} | ||
} | ||
uint32_t* GetImgUint32Cache(bool bIn, unsigned long lSize){ //as that memory deletion wont happen promptly, it may consume too much memory too fast unnecessarily becoming a bottle neck on performance | ||
if(lSize > (bIn?lUInt32ImgCacheSizeIN:lUInt32ImgCacheSizeOUT)){ | ||
if(libxbrzscale::isDbgMsg())std::cerr<<"libxBRZ: increasing "<<(bIn?"IN":"OUT")<<" image cache to "<<lSize<<std::endl; | ||
|
||
if((bIn?pUInt32ImgCacheIN:pUInt32ImgCacheOUT)!=NULL)DeleteCache(bIn); | ||
|
||
(bIn?lUInt32ImgCacheSizeIN:lUInt32ImgCacheSizeOUT)=lSize; | ||
(bIn?pUInt32ImgCacheIN:pUInt32ImgCacheOUT)=new uint32_t[bIn?lUInt32ImgCacheSizeIN:lUInt32ImgCacheSizeOUT]; | ||
} | ||
|
||
return (bIn?pUInt32ImgCacheIN:pUInt32ImgCacheOUT); | ||
} | ||
|
||
uint32_t* libxbrzscale::surfaceToUint32(bool bIn, SDL_Surface* img){ | ||
uint32_t *data = GetImgUint32Cache(bIn, img->w * img->h); | ||
|
||
int x, y, offset=0; | ||
Uint8 r, g, b, a; | ||
|
@@ -180,29 +226,51 @@ void libxbrzscale::uint32toSurface(uint32_t* ui32src, SDL_Surface* dst_img){ | |
} | ||
|
||
SDL_Surface* libxbrzscale::scale(SDL_Surface* src_img, int scale){ | ||
return libxbrzscale::scale(NULL, src_img, scale); | ||
} | ||
/** | ||
* dst_img if not null may be re-used, and is also returned | ||
*/ | ||
SDL_Surface* libxbrzscale::scale(SDL_Surface* dst_imgCache, SDL_Surface* src_img, int scale){ | ||
if(scale<2 || scale>6){ | ||
fprintf(stderr, "invalid stretch value min=2, max=6, requested=%d\n", scale); | ||
return NULL; | ||
} | ||
|
||
int src_width = src_img->w; | ||
int src_height = src_img->h; | ||
int dst_width = src_width * scale; | ||
int dst_height = src_height * scale; | ||
|
||
uint32_t *in_data = surfaceToUint32(src_img); | ||
SDL_FreeSurface(src_img); | ||
uint32_t *in_data = surfaceToUint32(true, src_img); | ||
if(bFreeInputSurfaceAfterScale && src_img!=NULL && src_img->refcount>0){ | ||
SDL_FreeSurface(src_img); //previous INPUT surface | ||
src_img=NULL; //just to prevent future troubles here, but pointless outside here | ||
} | ||
|
||
if(bEnableOutput)printf("Scaling image...\n"); | ||
uint32_t* dest = new uint32_t[dst_width * dst_height]; | ||
uint32_t* dest = GetImgUint32Cache(false, dst_width * dst_height); | ||
|
||
xbrz::scale(scale, in_data, dest, src_width, src_height, xbrz::ColorFormat::ARGB); | ||
delete [] in_data; | ||
if(!bUseCache){DeleteCache(true);in_data=NULL;} | ||
|
||
if(bEnableOutput)printf("Saving image...\n"); | ||
SDL_Surface* dst_img = createARGBSurface(dst_width, dst_height); | ||
if (!dst_img) { | ||
delete [] dest; | ||
if(bEnableOutput)fprintf(stderr, "Failed to create SDL surface: %s\n", SDL_GetError()); | ||
|
||
if(dst_imgCache==NULL || dst_imgCache->w!=dst_width || dst_imgCache->h!=dst_height || dst_imgCache->refcount==0){ | ||
if(bFreeOutputSurfaceAfterScale && dst_imgCache!=NULL && dst_imgCache->refcount>0){ | ||
SDL_FreeSurface(dst_imgCache); //previous OUTPUT surface | ||
} | ||
|
||
dst_imgCache = createARGBSurface(dst_width, dst_height); | ||
} | ||
|
||
if (!dst_imgCache) { | ||
if(!bUseCache){DeleteCache(false);dest=NULL;} | ||
fprintf(stderr, "Failed to create SDL surface: %s\n", SDL_GetError()); //error messages must always output | ||
return NULL; | ||
} | ||
|
||
uint32toSurface(dest,dst_img); | ||
uint32toSurface(dest,dst_imgCache); | ||
|
||
return dst_img; | ||
return dst_imgCache; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you check performance gains of using inline versus more aggressive compiler automatic optimizations? I'm not sure using inline these days is really useful - compilers are usually smarter that we are :)
if you really think it's needed, people usually do this:
That way code is more readable. XXX should obviously be some library tag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I actually just learned about inline.
I was just trying to keep things as they originally were (with inline).
I dont know how to actually test it, I guess some profiler would be required, and to disable dynamic CPU throttle, and also a stress test code, but I have only practice in profilling java using NetBeans (that has in integrated profiler that can hook on any java running application, so it is ready for profilling noobs like me :))
So, if you believe inline is unnecessary, I can just remove it xD.
but anyway, your alternative define is indeed much more readable!
what you think? we keep it? do you have practice on profilling cpp?
EDIT: I can't even debug ivan in eclipse yet shame :/ thats why I created this optional code that is a bit troubling to use but works very fine: https://github.com/AquariusPower/CppDebugMessages