mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 21:39:52 +00:00
Refactored Texture Scaling, changed options & gui to separately select scaling level and method
This commit is contained in:
parent
90f8d48da1
commit
3e5023ea97
@ -115,7 +115,8 @@ void Config::Load(const char *iniFileName)
|
||||
#else
|
||||
graphics->Get("MipMap", &bMipMap, false);
|
||||
#endif
|
||||
graphics->Get("XBRZTexScalingLevel", &iXBRZTexScalingLevel, 1);
|
||||
graphics->Get("TexScalingLevel", &iTexScalingLevel, 1);
|
||||
graphics->Get("TexScalingType", &iTexScalingType, 1);
|
||||
|
||||
IniFile::Section *sound = iniFile.GetOrCreateSection("Sound");
|
||||
sound->Get("Enable", &bEnableSound, true);
|
||||
@ -197,7 +198,8 @@ void Config::Save()
|
||||
graphics->Set("StretchToDisplay", bStretchToDisplay);
|
||||
graphics->Set("TrueColor", bTrueColor);
|
||||
graphics->Set("MipMap", bMipMap);
|
||||
graphics->Set("XBRZTexScalingLevel", iXBRZTexScalingLevel);
|
||||
graphics->Set("TexScalingLevel", iTexScalingLevel);
|
||||
graphics->Set("TexScalingType", iTexScalingType);
|
||||
|
||||
IniFile::Section *sound = iniFile.GetOrCreateSection("Sound");
|
||||
sound->Set("Enable", bEnableSound);
|
||||
|
@ -74,7 +74,8 @@ public:
|
||||
bool bTrueColor;
|
||||
bool bMipMap;
|
||||
bool bAnisotropicFiltering;
|
||||
int iXBRZTexScalingLevel; // 1 = off, 2 = 2xBRZ, ..., 5 = 5xBRZ
|
||||
int iTexScalingLevel; // 1 = off, 2 = 2x, ..., 5 = 5x
|
||||
int iTexScalingType; // 0 = Bilinear, 1 = xBRZ, 2 = Hybrid
|
||||
|
||||
// Sound
|
||||
bool bEnableSound;
|
||||
|
@ -27,7 +27,7 @@
|
||||
namespace p = std::placeholders;
|
||||
|
||||
// Report the time and throughput for each larger scaling operation in the log
|
||||
//#define SCALING_MEASURE_TIME
|
||||
#define SCALING_MEASURE_TIME
|
||||
|
||||
#ifdef SCALING_MEASURE_TIME
|
||||
#include "native/base/timeutil.h"
|
||||
@ -71,6 +71,74 @@ namespace {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is sadly much faster than an inline function with a loop
|
||||
#define MIX_PIXELS(p0, p1, p2, factors) \
|
||||
((((p0>> 0)&0xFF)*factors[0] + ((p1>> 0)&0xFF)*factors[1] + ((p2>> 0)&0xFF)*factors[2])/255 << 0 ) | \
|
||||
((((p0>> 8)&0xFF)*factors[0] + ((p1>> 8)&0xFF)*factors[1] + ((p2>> 8)&0xFF)*factors[2])/255 << 8 ) | \
|
||||
((((p0>>16)&0xFF)*factors[0] + ((p1>>16)&0xFF)*factors[1] + ((p2>>16)&0xFF)*factors[2])/255 << 16 ) | \
|
||||
((((p0>>24)&0xFF)*factors[0] + ((p1>>24)&0xFF)*factors[1] + ((p2>>24)&0xFF)*factors[2])/255 << 24 )
|
||||
|
||||
const static u8 BILINEAR_FACTORS[4][5][3] = {
|
||||
{ {127,128, 0}, { 0,128,127}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0} }, // x2
|
||||
{ {170, 85, 0}, { 0,255, 0}, { 0, 85,170}, { 0, 0, 0}, { 0, 0, 0} }, // x3
|
||||
{ {102,153, 0}, { 51,204, 0}, { 0,204, 51}, { 0,153,102}, { 0, 0, 0} }, // x4
|
||||
{ {102,153, 0}, { 51,204, 0}, { 0,255, 0}, { 0,204, 51}, { 0,153,102} }, // x5
|
||||
};
|
||||
// integral bilinear upscaling by factor f, horizontal part
|
||||
template<int f>
|
||||
void bilinearHt(u32* data, u32* out, int w, int l, int u) {
|
||||
static_assert(f>1 && f<=5, "Bilinear scaling only implemented for factors 2 to 5");
|
||||
int outw = w*f;
|
||||
for(int y = l; y < u; ++y) {
|
||||
for(int x = 0; x < w; ++x) {
|
||||
int inpos = y*w + x;
|
||||
u32 left = data[inpos - (x==0 ?0:1)];
|
||||
u32 center = data[inpos];
|
||||
u32 right = data[inpos + (x==w-1?0:1)];
|
||||
for(int i=0; i<f; ++i) { // hope the compiler unrolls this
|
||||
out[y*outw + x*f + i] = MIX_PIXELS(left, center, right, BILINEAR_FACTORS[f-2][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void bilinearH(int factor, u32* data, u32* out, int w, int l, int u) {
|
||||
switch(factor) {
|
||||
case 2: bilinearHt<2>(data, out, w, l, u); break;
|
||||
case 3: bilinearHt<3>(data, out, w, l, u); break;
|
||||
case 4: bilinearHt<4>(data, out, w, l, u); break;
|
||||
case 5: bilinearHt<5>(data, out, w, l, u); break;
|
||||
default: ERROR_LOG(G3D, "Bilinear upsampling only implemented for factors 2 to 5");
|
||||
}
|
||||
}
|
||||
// integral bilinear upscaling by factor f, vertical part
|
||||
// gl/gu == global lower and upper bound
|
||||
template<int f>
|
||||
void bilinearVt(u32* data, u32* out, int w, int gl, int gu, int l, int u) {
|
||||
static_assert(f>1 && f<=5, "Bilinear scaling only implemented for 2x, 3x, 4x, and 5x");
|
||||
int outw = w*f;
|
||||
for(int y = l; y < u; ++y) {
|
||||
for(int x = 0; x < outw; ++x) {
|
||||
u32 upper = data[(y - (y==gl ?0:1)) * outw + x];
|
||||
u32 center = data[y * outw + x];
|
||||
u32 lower = data[(y + (y==gu-1?0:1)) * outw + x];
|
||||
for(int i=0; i<f; ++i) { // hope the compiler unrolls this
|
||||
out[(y*f + i)*outw + x] = MIX_PIXELS(upper, center, lower, BILINEAR_FACTORS[f-2][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void bilinearV(int factor, u32* data, u32* out, int w, int gl, int gu, int l, int u) {
|
||||
switch(factor) {
|
||||
case 2: bilinearVt<2>(data, out, w, gl, gu, l, u); break;
|
||||
case 3: bilinearVt<3>(data, out, w, gl, gu, l, u); break;
|
||||
case 4: bilinearVt<4>(data, out, w, gl, gu, l, u); break;
|
||||
case 5: bilinearVt<5>(data, out, w, gl, gu, l, u); break;
|
||||
default: ERROR_LOG(G3D, "Bilinear upsampling only implemented for factors 2 to 5");
|
||||
}
|
||||
}
|
||||
|
||||
#undef MIX_PIXELS
|
||||
}
|
||||
|
||||
|
||||
@ -78,47 +146,36 @@ TextureScaler::TextureScaler() {
|
||||
}
|
||||
|
||||
void TextureScaler::Scale(u32* &data, GLenum &dstFmt, int &width, int &height) {
|
||||
if(g_Config.iXBRZTexScalingLevel > 1) {
|
||||
if(g_Config.iTexScalingLevel > 1) {
|
||||
#ifdef SCALING_MEASURE_TIME
|
||||
double t_start = real_time_now();
|
||||
#endif
|
||||
|
||||
int factor = g_Config.iXBRZTexScalingLevel;
|
||||
int factor = g_Config.iTexScalingLevel;
|
||||
|
||||
// depending on the factor and texture sizes, these can be pretty large (25 MB for a 512 by 512 texture with scaling factor 5)
|
||||
bufInput.resize(width*height); // used to store the input image image if it needs to be reformatted
|
||||
bufOutput.resize(width*height*factor*factor); // used to store the upscaled image
|
||||
u32 *xbrzInputBuf = bufInput.data();
|
||||
u32 *xbrzBuf = bufOutput.data();
|
||||
u32 *inputBuf = bufInput.data();
|
||||
u32 *outputBuf = bufOutput.data();
|
||||
|
||||
// convert texture to correct format for xBRZ
|
||||
switch(dstFmt) {
|
||||
case GL_UNSIGNED_BYTE:
|
||||
xbrzInputBuf = data; // already fine
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_4_4_4_4:
|
||||
GlobalThreadPool::Loop(std::bind(&convert4444, (u16*)data, xbrzInputBuf, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_5_6_5:
|
||||
GlobalThreadPool::Loop(std::bind(&convert565, (u16*)data, xbrzInputBuf, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_5_5_5_1:
|
||||
GlobalThreadPool::Loop(std::bind(&convert5551, (u16*)data, xbrzInputBuf, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format");
|
||||
}
|
||||
// convert texture to correct format for scaling
|
||||
ConvertTo8888(dstFmt, data, inputBuf, width, height);
|
||||
|
||||
// scale
|
||||
xbrz::ScalerCfg cfg;
|
||||
GlobalThreadPool::Loop(std::bind(&xbrz::scale, factor, xbrzInputBuf, xbrzBuf, width, height, cfg, p::_1, p::_2), 0, height);
|
||||
switch(g_Config.iTexScalingType) {
|
||||
case BILINEAR:
|
||||
ScaleBilinear(factor, inputBuf, outputBuf, width, height);
|
||||
break;
|
||||
case XBRZ:
|
||||
ScaleXBRZ(factor, inputBuf, outputBuf, width, height);
|
||||
break;
|
||||
case HYBRID:
|
||||
ScaleHybrid(factor, inputBuf, outputBuf, width, height);
|
||||
break;
|
||||
}
|
||||
|
||||
// update values accordingly
|
||||
data = xbrzBuf;
|
||||
data = outputBuf;
|
||||
dstFmt = GL_UNSIGNED_BYTE;
|
||||
width *= factor;
|
||||
height *= factor;
|
||||
@ -132,3 +189,43 @@ void TextureScaler::Scale(u32* &data, GLenum &dstFmt, int &width, int &height) {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void TextureScaler::ScaleXBRZ(int factor, u32* source, u32* dest, int width, int height) {
|
||||
xbrz::ScalerCfg cfg;
|
||||
GlobalThreadPool::Loop(std::bind(&xbrz::scale, factor, source, dest, width, height, cfg, p::_1, p::_2), 0, height);
|
||||
}
|
||||
|
||||
void TextureScaler::ScaleBilinear(int factor, u32* source, u32* dest, int width, int height) {
|
||||
bufTmp1.resize(width*height*factor);
|
||||
u32 *tmpBuf = bufTmp1.data();
|
||||
GlobalThreadPool::Loop(std::bind(&bilinearH, factor, source, tmpBuf, width, p::_1, p::_2), 0, height);
|
||||
GlobalThreadPool::Loop(std::bind(&bilinearV, factor, tmpBuf, dest, width, 0, height, p::_1, p::_2), 0, height);
|
||||
}
|
||||
|
||||
void TextureScaler::ScaleHybrid(int factor, u32* source, u32* dest, int width, int height) {
|
||||
|
||||
}
|
||||
|
||||
void TextureScaler::ConvertTo8888(GLenum format, u32* source, u32* &dest, int width, int height) {
|
||||
switch(format) {
|
||||
case GL_UNSIGNED_BYTE:
|
||||
dest = source; // already fine
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_4_4_4_4:
|
||||
GlobalThreadPool::Loop(std::bind(&convert4444, (u16*)source, dest, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_5_6_5:
|
||||
GlobalThreadPool::Loop(std::bind(&convert565, (u16*)source, dest, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_5_5_5_1:
|
||||
GlobalThreadPool::Loop(std::bind(&convert5551, (u16*)source, dest, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
default:
|
||||
dest = source;
|
||||
ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format");
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,16 @@ public:
|
||||
|
||||
void Scale(u32* &data, GLenum &dstfmt, int &width, int &height);
|
||||
|
||||
enum { BILINEAR = 0, XBRZ= 1, HYBRID = 2 };
|
||||
|
||||
private:
|
||||
SimpleBuf<u32> bufInput;
|
||||
SimpleBuf<u32> bufOutput;
|
||||
void ScaleXBRZ(int factor, u32* source, u32* dest, int width, int height);
|
||||
void ScaleBilinear(int factor, u32* source, u32* dest, int width, int height);
|
||||
void ScaleHybrid(int factor, u32* source, u32* dest, int width, int height);
|
||||
void ConvertTo8888(GLenum format, u32* source, u32* &dest, int width, int height);
|
||||
|
||||
// depending on the factor and texture sizes, these can get pretty large
|
||||
// maximum is (100 MB total for a 512 by 512 texture with scaling factor 5 and hybrid scaling)
|
||||
// of course, scaling factor 5 is totally silly anyway
|
||||
SimpleBuf<u32> bufInput, bufOutput, bufTmp1, bufTmp2;
|
||||
};
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "GPU/GPUInterface.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "native/image/png_load.h"
|
||||
#include "GPU/GLES/TextureScaler.h"
|
||||
|
||||
#ifdef THEMES
|
||||
#include "XPTheme.h"
|
||||
@ -175,8 +176,12 @@ namespace MainWindow
|
||||
ResizeDisplay();
|
||||
}
|
||||
|
||||
void setXbrzTexScaling(int num) {
|
||||
g_Config.iXBRZTexScalingLevel = num;
|
||||
void setTexScalingLevel(int num) {
|
||||
g_Config.iTexScalingLevel = num;
|
||||
if(gpu) gpu->ClearCacheNextFrame();
|
||||
}
|
||||
void setTexScalingType(int num) {
|
||||
g_Config.iTexScalingType = num;
|
||||
if(gpu) gpu->ClearCacheNextFrame();
|
||||
}
|
||||
|
||||
@ -501,19 +506,29 @@ namespace MainWindow
|
||||
break;
|
||||
|
||||
case ID_TEXTURESCALING_OFF:
|
||||
setXbrzTexScaling(1);
|
||||
setTexScalingLevel(1);
|
||||
break;
|
||||
case ID_TEXTURESCALING_2XBRZ:
|
||||
setXbrzTexScaling(2);
|
||||
case ID_TEXTURESCALING_2X:
|
||||
setTexScalingLevel(2);
|
||||
break;
|
||||
case ID_TEXTURESCALING_3XBRZ:
|
||||
setXbrzTexScaling(3);
|
||||
case ID_TEXTURESCALING_3X:
|
||||
setTexScalingLevel(3);
|
||||
break;
|
||||
case ID_TEXTURESCALING_4XBRZ:
|
||||
setXbrzTexScaling(4);
|
||||
case ID_TEXTURESCALING_4X:
|
||||
setTexScalingLevel(4);
|
||||
break;
|
||||
case ID_TEXTURESCALING_5XBRZ:
|
||||
setXbrzTexScaling(5);
|
||||
case ID_TEXTURESCALING_5X:
|
||||
setTexScalingLevel(5);
|
||||
break;
|
||||
|
||||
case ID_TEXTURESCALING_BILINEAR:
|
||||
setTexScalingType(TextureScaler::BILINEAR);
|
||||
break;
|
||||
case ID_TEXTURESCALING_XBRZ:
|
||||
setTexScalingType(TextureScaler::XBRZ);
|
||||
break;
|
||||
case ID_TEXTURESCALING_HYBRID:
|
||||
setTexScalingType(TextureScaler::HYBRID);
|
||||
break;
|
||||
|
||||
case ID_OPTIONS_BUFFEREDRENDERING:
|
||||
@ -809,13 +824,22 @@ namespace MainWindow
|
||||
|
||||
static const int texscalingitems[] = {
|
||||
ID_TEXTURESCALING_OFF,
|
||||
ID_TEXTURESCALING_2XBRZ,
|
||||
ID_TEXTURESCALING_3XBRZ,
|
||||
ID_TEXTURESCALING_4XBRZ,
|
||||
ID_TEXTURESCALING_5XBRZ,
|
||||
ID_TEXTURESCALING_2X,
|
||||
ID_TEXTURESCALING_3X,
|
||||
ID_TEXTURESCALING_4X,
|
||||
ID_TEXTURESCALING_5X,
|
||||
};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
CheckMenuItem(menu, texscalingitems[i], MF_BYCOMMAND | ((i == g_Config.iXBRZTexScalingLevel-1) ? MF_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(menu, texscalingitems[i], MF_BYCOMMAND | ((i == g_Config.iTexScalingLevel-1) ? MF_CHECKED : MF_UNCHECKED));
|
||||
}
|
||||
|
||||
static const int texscalingtypeitems[] = {
|
||||
ID_TEXTURESCALING_BILINEAR,
|
||||
ID_TEXTURESCALING_XBRZ,
|
||||
ID_TEXTURESCALING_HYBRID,
|
||||
};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
CheckMenuItem(menu, texscalingtypeitems[i], MF_BYCOMMAND | ((i == g_Config.iTexScalingType) ? MF_CHECKED : MF_UNCHECKED));
|
||||
}
|
||||
}
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user