From 3e5023ea97f7e6f77c8a3bf4b1b7ef56a5900641 Mon Sep 17 00:00:00 2001 From: Peter Thoman Date: Wed, 1 May 2013 23:55:34 +0200 Subject: [PATCH] Refactored Texture Scaling, changed options & gui to separately select scaling level and method --- Core/Config.cpp | 6 +- Core/Config.h | 3 +- GPU/GLES/TextureScaler.cpp | 157 ++++++++++++++++++++++++++++++------- GPU/GLES/TextureScaler.h | 13 ++- Windows/WndMainWindow.cpp | 56 +++++++++---- Windows/ppsspp.rc | Bin 37142 -> 37650 bytes Windows/resource.h | Bin 163982 -> 164734 bytes 7 files changed, 184 insertions(+), 51 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 266dad3d39..ea92470728 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -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); diff --git a/Core/Config.h b/Core/Config.h index bb134edb3a..90d3bdaf01 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -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; diff --git a/GPU/GLES/TextureScaler.cpp b/GPU/GLES/TextureScaler.cpp index ddb8146267..6708fc8a81 100644 --- a/GPU/GLES/TextureScaler.cpp +++ b/GPU/GLES/TextureScaler.cpp @@ -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 + 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(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 + 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(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"); + } +} diff --git a/GPU/GLES/TextureScaler.h b/GPU/GLES/TextureScaler.h index 93e141237d..711c7f7c2b 100644 --- a/GPU/GLES/TextureScaler.h +++ b/GPU/GLES/TextureScaler.h @@ -31,7 +31,16 @@ public: void Scale(u32* &data, GLenum &dstfmt, int &width, int &height); + enum { BILINEAR = 0, XBRZ= 1, HYBRID = 2 }; + private: - SimpleBuf bufInput; - SimpleBuf 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 bufInput, bufOutput, bufTmp1, bufTmp2; }; diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index 883ccf722d..361b572be6 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -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)); } } diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 172aeaeab6a310b0d6b70dec1ed792d2c7e291eb..78958e482c8e3876f24667f78e6b1415d08a424f 100644 GIT binary patch delta 235 zcmYk!zY75Y0LAgwQHR1Mq5PPLj55f?m2xG?AWB`taV3M{?vxUL0r$#`!Av%jKTRiO z{=UyUoCW(LTzv+&TAGp=revjE1$4QOB-t8;c{JSr%{JY%BMB$Dim)b`=$Jvr9AgZycz-;n0j|uHUxtDP6f{vol{Yo|RXiJIRstSpMZqk2 l>sVrl1$3^pETvJ%Dc(AIkkN+1(! delta 146 zcmbQVjA_~;rVU(WtWFF;3{jH}%Z(==;A5LyP%g)5#1O#%5uI3W$Z5<_0p%g9F-B2i af<=uI)Z~ffrdaGSMd%k3-RxI&L;?WIbtw=4 diff --git a/Windows/resource.h b/Windows/resource.h index 444cd777181de5cbd014f5f71a982df72014ce63..41bec3dae571e1f1fb80894f1fe7a0f560b87c4e 100644 GIT binary patch delta 231 zcmeBc&};+VcApQ&Q{ zy&NWmX}L^N)8FMWaZNuW#UwU;LmrdVbf*F)53nSs2SX%-6GIS#=j6mxVTjIx0;o<# zqv?TpjM5O%b8t~(BvGA0h+38DA$g1v5S3j}F$G3bh{^|rObrm*oESVAd>A|#{3a{< h3s3jUXX4oIR>YLUJN-)_lh|~DdM1(Scj}oo0RY2>OpyQp delta 98 zcmey@#?{xz)zHGYh3Q=W^oATJvFT0)Odb%{KR9bwA(O}Sgd(Omkc7x|gJLEx2rk`nI+5`Y1P$Vz_