Merge remote-tracking branch 'ppssppUpstream/master' into focus_pause

Conflicts:
	Windows/resource.h
This commit is contained in:
szdarkhack 2013-10-11 17:47:43 +03:00
commit b98a073b26
56 changed files with 1151 additions and 491 deletions

View File

@ -564,6 +564,8 @@ add_library(native STATIC
native/gfx_es2/glsl_program.h
native/gfx_es2/vertex_format.cpp
native/gfx_es2/vertex_format.h
native/gfx_es2/gl3stub.c
native/gfx_es2/gl3stub.h
native/i18n/i18n.cpp
native/i18n/i18n.h
native/image/png_load.cpp

View File

@ -28,6 +28,14 @@
#endif
#endif
struct u24_be {
unsigned char value[3];
operator unsigned int(){
return 0x00000000 | (value[0] << 16) | (value[1] << 8) | (value[2] << 0);
}
};
#ifdef _WIN32
typedef unsigned __int8 u8;

View File

@ -189,6 +189,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename)
sound->Get("LowLatency", &bLowLatencyAudio, false);
IniFile::Section *control = iniFile.GetOrCreateSection("Control");
control->Get("HapticFeedback", &bHapticFeedback, true);
control->Get("ShowAnalogStick", &bShowAnalogStick, true);
#ifdef BLACKBERRY
control->Get("ShowTouchControls", &bShowTouchControls, pixel_xres != pixel_yres);
@ -243,8 +244,8 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename)
debugConfig->Get("ShowBottomTabTitles",&bShowBottomTabTitles,true);
debugConfig->Get("ShowDeveloperMenu", &bShowDeveloperMenu, false);
IniFile::Section *gleshacks = iniFile.GetOrCreateSection("GLESHacks");
gleshacks->Get("PrescaleUV", &bPrescaleUV, false);
IniFile::Section *speedhacks = iniFile.GetOrCreateSection("SpeedHacks");
speedhacks->Get("PrescaleUV", &bPrescaleUV, false);
INFO_LOG(LOADER, "Loading controller config: %s", controllerIniFilename_.c_str());
bSaveSettings = true;
@ -358,6 +359,7 @@ void Config::Save() {
sound->Set("LowLatency", bLowLatencyAudio);
IniFile::Section *control = iniFile.GetOrCreateSection("Control");
control->Set("HapticFeedback", bHapticFeedback);
control->Set("ShowAnalogStick", bShowAnalogStick);
control->Set("ShowTouchControls", bShowTouchControls);
// control->Set("KeyMapping",iMappingMap);
@ -401,6 +403,9 @@ void Config::Save() {
debugConfig->Set("ShowBottomTabTitles",bShowBottomTabTitles);
debugConfig->Set("ShowDeveloperMenu", bShowDeveloperMenu);
IniFile::Section *speedhacks = iniFile.GetOrCreateSection("SpeedHacks");
speedhacks->Set("PrescaleUV", bPrescaleUV);
if (!iniFile.Save(iniFilename_.c_str())) {
ERROR_LOG(LOADER, "Error saving config - can't write ini %s", iniFilename_.c_str());
return;

View File

@ -36,7 +36,6 @@ public:
bool bAutoRun; // start immediately
bool bBrowse; // when opening the emulator, immediately show a file browser
// General
int iNumWorkerThreads;
bool bScreenshotsAsPNG;
@ -104,10 +103,15 @@ public:
int iSFXVolume;
int iBGMVolume;
// UI
// Controls
bool bShowTouchControls;
bool bShowDebuggerOnLoad;
bool bShowAnalogStick;
bool bHapticFeedback;
int iTouchButtonOpacity;
float fButtonScale;
// UI
bool bShowDebuggerOnLoad;
int iShowFPSCounter;
bool bShowDebugStats;
bool bAccelerometerToAnalogHoriz;
@ -118,9 +122,6 @@ public:
bool bGridView2;
bool bGridView3;
// Control
int iTouchButtonOpacity;
float fButtonScale;
// GLES backend-specific hacks. Not saved to the ini file, do not add checkboxes. Will be made into

View File

@ -489,7 +489,7 @@ void CWCheatEngine::Run() {
break;
case 0xE: // Test commands, multiple skip
{
bool is8Bit = (comm >> 24) == 0x1;
bool is8Bit = (comm >> 24) == 0xE1;
addr = GetAddress(arg & 0x0FFFFFFF);
if (Memory::IsValidAddress(addr)) {
int memoryValue = is8Bit ? Memory::Read_U8(addr) : Memory::Read_U16(addr);

View File

@ -177,9 +177,21 @@ bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpat
lock_guard guard(lock);
std::string realpath;
std::string inpath = _inpath;
// "ms0:/file.txt" is equivalent to " ms0:/file.txt". Yes, really.
if (inpath.find(':') != inpath.npos) {
size_t offset = 0;
while (inpath[offset] == ' ') {
offset++;
}
if (offset > 0) {
inpath = inpath.substr(offset);
}
}
// Special handling: host0:command.txt (as seen in Super Monkey Ball Adventures, for example)
// appears to mean the current directory on the UMD. Let's just assume the current directory.
std::string inpath = _inpath;
if (strncasecmp(inpath.c_str(), "host0:", strlen("host0:")) == 0) {
INFO_LOG(FILESYS, "Host0 path detected, stripping: %s", inpath.c_str());
inpath = inpath.substr(strlen("host0:"));

View File

@ -175,6 +175,10 @@ public:
static int GetStaticIDType() { return PPSSPP_KERNEL_TMID_File; }
int GetIDType() const { return PPSSPP_KERNEL_TMID_File; }
bool asyncBusy() {
return pendingAsyncResult || hasAsyncResult;
}
virtual void DoState(PointerWrap &p) {
auto s = p.Section("FileNode", 1);
if (!s)
@ -323,6 +327,8 @@ void __IoAsyncNotify(u64 userdata, int cyclesLate) {
u32 address = __KernelGetWaitValue(threadID, error);
if (HLEKernel::VerifyWait(threadID, WAITTYPE_ASYNCIO, f->GetUID())) {
HLEKernel::ResumeFromWait(threadID, WAITTYPE_ASYNCIO, f->GetUID(), 0);
// Someone woke up, so it's no longer got one.
f->hasAsyncResult = false;
if (Memory::IsValidAddress(address)) {
Memory::Write_U64((u64) f->asyncResult, address);
@ -348,7 +354,7 @@ void __IoSyncNotify(u64 userdata, int cyclesLate) {
}
f->pendingAsyncResult = false;
f->hasAsyncResult = true;
f->hasAsyncResult = false;
AsyncIOResult managerResult;
if (ioManager.WaitResult(f->handle, managerResult)) {
@ -711,6 +717,10 @@ bool __IoRead(int &result, int id, u32 data_addr, int size) {
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
if (f->asyncBusy()) {
result = SCE_KERNEL_ERROR_ASYNC_BUSY;
return true;
}
if (!(f->openMode & FILEACCESS_READ)) {
result = ERROR_KERNEL_BAD_FILE_DESCRIPTOR;
return true;
@ -731,14 +741,13 @@ bool __IoRead(int &result, int id, u32 data_addr, int size) {
return true;
}
} else {
ERROR_LOG(SCEIO, "sceIoRead Reading into bad pointer %08x", data_addr);
ERROR_LOG_REPORT(SCEIO, "sceIoRead Reading into bad pointer %08x", data_addr);
// TODO: Returning 0 because it wasn't being sign-extended in async result before.
// What should this do?
result = 0;
return true;
}
} else {
ERROR_LOG(SCEIO, "sceIoRead ERROR: no file open");
result = error;
return true;
}
@ -776,6 +785,7 @@ u32 sceIoRead(int id, u32 data_addr, int size) {
DEBUG_LOG(SCEIO, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size);
return hleDelayResult(result, "io read", us);
} else {
WARN_LOG(SCEIO, "sceIoRead(%d, %08x, %x): error %08x", id, data_addr, size, result);
return result;
}
}
@ -790,6 +800,10 @@ u32 sceIoReadAsync(int id, u32 data_addr, int size) {
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoReadAsync(%d, %08x, %x): async busy", id, data_addr, size);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
}
int result;
bool complete = __IoRead(result, id, data_addr, size);
if (complete) {
@ -806,7 +820,8 @@ u32 sceIoReadAsync(int id, u32 data_addr, int size) {
}
}
bool __IoWrite(int &result, int id, void *data_ptr, int size) {
bool __IoWrite(int &result, int id, u32 data_addr, int size) {
const void *data_ptr = Memory::GetPointer(data_addr);
// Let's handle stdout/stderr specially.
if (id == 1 || id == 2) {
const char *str = (const char *) data_ptr;
@ -818,6 +833,10 @@ bool __IoWrite(int &result, int id, void *data_ptr, int size) {
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
if (f->asyncBusy()) {
result = SCE_KERNEL_ERROR_ASYNC_BUSY;
return true;
}
if (!(f->openMode & FILEACCESS_WRITE)) {
result = ERROR_KERNEL_BAD_FILE_DESCRIPTOR;
return true;
@ -861,7 +880,7 @@ u32 sceIoWrite(int id, u32 data_addr, int size) {
}
int result;
bool complete = __IoWrite(result, id, Memory::GetPointer(data_addr), size);
bool complete = __IoWrite(result, id, data_addr, size);
if (!complete) {
DEBUG_LOG(SCEIO, "sceIoWrite(%d, %08x, %x): deferring result", id, data_addr, size);
@ -876,6 +895,7 @@ u32 sceIoWrite(int id, u32 data_addr, int size) {
return result;
}
} else {
WARN_LOG(SCEIO, "sceIoWrite(%d, %08x, %x): error %08x", id, data_addr, size, result);
return result;
}
}
@ -890,8 +910,12 @@ u32 sceIoWriteAsync(int id, u32 data_addr, int size) {
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoWriteAsync(%d, %08x, %x): async busy", id, data_addr, size);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
}
int result;
bool complete = __IoWrite(result, id, Memory::GetPointer(data_addr), size);
bool complete = __IoWrite(result, id, data_addr, size);
if (complete) {
f->asyncResult = result;
DEBUG_LOG(SCEIO, "%llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
@ -964,6 +988,10 @@ s64 __IoLseek(SceUID id, s64 offset, int whence) {
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoLseek*(%d, %llx, %i): async busy", id, offset, whence);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
}
FileMove seek = FILEMOVE_BEGIN;
s64 newPos = 0;
@ -1027,6 +1055,10 @@ u32 sceIoLseekAsync(int id, s64 offset, int whence) {
WARN_LOG(SCEIO, "sceIoLseekAsync(%d, %llx, %i): invalid whence", id, offset, whence);
return SCE_KERNEL_ERROR_INVAL;
}
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoLseekAsync(%d, %llx, %i): async busy", id, offset, whence);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
}
f->asyncResult = __IoLseek(id, offset, whence);
// Educated guess at timing.
__IoSchedAsync(f, id, 100);
@ -1047,6 +1079,10 @@ u32 sceIoLseek32Async(int id, int offset, int whence) {
WARN_LOG(SCEIO, "sceIoLseek32Async(%d, %x, %i): invalid whence", id, offset, whence);
return SCE_KERNEL_ERROR_INVAL;
}
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoLseek*(%d, %x, %i): async busy", id, offset, whence);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
}
f->asyncResult = __IoLseek(id, offset, whence);
// Educated guess at timing.
__IoSchedAsync(f, id, 100);
@ -1860,9 +1896,13 @@ int __IoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 out
u32 error;
FileNode *f = __IoGetFd(id, error);
if (error) {
ERROR_LOG(SCEIO, "UNIMPL %08x=sceIoIoctl id: %08x, cmd %08x, bad file", error, id, cmd);
ERROR_LOG(SCEIO, "%08x=sceIoIoctl id: %08x, cmd %08x, bad file", error, id, cmd);
return error;
}
if (f->asyncBusy()) {
ERROR_LOG(SCEIO, "%08x=sceIoIoctl id: %08x, cmd %08x, async busy", error, id, cmd);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
}
//KD Hearts:
//56:46:434 HLE\sceIo.cpp:886 E[HLE]: UNIMPL 0=sceIoIoctrl id: 0000011f, cmd 04100001, indataPtr 08b313d8, inlen 00000010, outdataPtr 00000000, outLen 0
@ -1957,7 +1997,8 @@ int __IoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 out
}
break;
//Unknown command, always expects return value of 1 according to JPCSP, used by Pangya Fantasy Golf.
// Unknown command, always expects return value of 1 according to JPCSP, used by Pangya Fantasy Golf.
// TODO: This is unsupported on ms0:/ (SCE_KERNEL_ERROR_UNSUP.)
case 0x01f30003:
INFO_LOG(SCEIO, "sceIoIoCtl: Unknown cmd %08x always returns 1", cmd);
if(inlen != 4 || outlen != 1 || Memory::Read_U32(indataPtr) != outlen) {
@ -1986,7 +2027,11 @@ u32 sceIoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 ou
{
int result = __IoIoctl(id, cmd, indataPtr, inlen, outdataPtr, outlen);
// Just a low estimate on timing.
return hleDelayResult(result, "io ctrl command", 100);
// TODO: What errors are delayed?
if (result != (int)SCE_KERNEL_ERROR_ASYNC_BUSY && result != (int)SCE_KERNEL_ERROR_UNSUP) {
return hleDelayResult(result, "io ctrl command", 100);
}
return result;
}
u32 sceIoIoctlAsync(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen)
@ -1994,6 +2039,10 @@ u32 sceIoIoctlAsync(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoIoctlAsync(%08x, %08x, %08x, %08x, %08x, %08x): async busy", id, cmd, indataPtr, inlen, outdataPtr, outlen);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
}
DEBUG_LOG(SCEIO, "sceIoIoctlAsync(%08x, %08x, %08x, %08x, %08x, %08x)", id, cmd, indataPtr, inlen, outdataPtr, outlen);
f->asyncResult = __IoIoctl(id, cmd, indataPtr, inlen, outdataPtr, outlen);
__IoSchedAsync(f, id, 100);

View File

@ -52,7 +52,7 @@ u32 convertYCbCrToABGR (int y, int cb, int cr) {
if (g > 0xFF) g = 0xFF; if(g < 0) g = 0;
if (b > 0xFF) b = 0xFF; if(b < 0) b = 0;
return 0xFF000000 | (r << 16) | (g << 8) | (b << 0);
return 0xFF000000 | (b << 16) | (g << 8) | (r << 0);
}
//Uncomment if you want to dump JPEGs loaded through sceJpeg to a file
@ -64,38 +64,7 @@ int sceJpegDecompressAllImage()
return 0;
}
int sceJpegMJpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bufferWidth)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceJpegMJpegCsc(%i, %i, %i, %i)", imageAddr, yCbCrAddr, widthHeight, bufferWidth);
return 0;
}
int sceJpegDecodeMJpeg(u32 jpegAddr, int jpegSize, u32 imageAddr, int dhtMode)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceJpegDecodeMJpeg(%i, %i, %i, %i)", jpegAddr, jpegSize, imageAddr, dhtMode);
return 0;
}
int sceJpegDecodeMJpegYCbCrSuccessively(u32 jpegAddr, int jpegSize, u32 yCbCrAddr, int yCbCrSize, int dhtMode)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceJpegDecodeMJpegYCbCrSuccessively(%i, %i, %i, %i, %i)", jpegAddr, jpegSize, yCbCrAddr, yCbCrSize, dhtMode);
return 0;
}
int sceJpegDeleteMJpeg()
{
ERROR_LOG_REPORT(ME, "UNIMPL sceJpegDeleteMJpeg()");
return 0;
}
int sceJpegDecodeMJpegSuccessively(u32 jpegAddr, int jpegSize, u32 imageAddr, int dhtMode)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceJpegDecodeMJpegSuccessively(%i, %i, %i, %i)", jpegAddr, jpegSize, imageAddr, dhtMode);
return 0;
}
int sceJpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bufferWidth, int colourInfo)
{
void __JpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bufferWidth) {
int height = widthHeight & 0xFFF;
int width = (widthHeight >> 16) & 0xFFF;
int lineWidth = std::min(width, bufferWidth);
@ -132,6 +101,41 @@ int sceJpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bufferWidth, i
imageBuffer += width;
imageBuffer += skipEndOfLine;
}
}
int sceJpegMJpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bufferWidth)
{
__JpegCsc(imageAddr, yCbCrAddr, widthHeight, bufferWidth);
DEBUG_LOG(ME, "UNIMPL sceJpegMJpegCsc(%i, %i, %i, %i)", imageAddr, yCbCrAddr, widthHeight, bufferWidth);
return 0;
}
int sceJpegDecodeMJpeg(u32 jpegAddr, int jpegSize, u32 imageAddr, int dhtMode)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceJpegDecodeMJpeg(%i, %i, %i, %i)", jpegAddr, jpegSize, imageAddr, dhtMode);
return 0;
}
int sceJpegDecodeMJpegYCbCrSuccessively(u32 jpegAddr, int jpegSize, u32 yCbCrAddr, int yCbCrSize, int dhtMode)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceJpegDecodeMJpegYCbCrSuccessively(%i, %i, %i, %i, %i)", jpegAddr, jpegSize, yCbCrAddr, yCbCrSize, dhtMode);
return 0;
}
int sceJpegDeleteMJpeg()
{
ERROR_LOG_REPORT(ME, "UNIMPL sceJpegDeleteMJpeg()");
return 0;
}
int sceJpegDecodeMJpegSuccessively(u32 jpegAddr, int jpegSize, u32 imageAddr, int dhtMode)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceJpegDecodeMJpegSuccessively(%i, %i, %i, %i)", jpegAddr, jpegSize, imageAddr, dhtMode);
return 0;
}
int sceJpegCsc(u32 imageAddr, u32 yCbCrAddr, int widthHeight, int bufferWidth, int colourInfo)
{
__JpegCsc(imageAddr, yCbCrAddr, widthHeight, bufferWidth);
DEBUG_LOG(ME, "UNIMPL sceJpegCsc(%i, %i, %i, %i, %i)", imageAddr, yCbCrAddr, widthHeight, bufferWidth, colourInfo);
return 0;
}
@ -192,6 +196,10 @@ int sceJpegGetOutputInfo(u32 jpegAddr, int jpegSize, u32 colourInfoAddr, int dht
uint32 jpeg_cityhash = CityHash32((const char *)jpegBuf, jpegSize);
sprintf(jpeg_fname, "Jpeg\\%X.jpg", jpeg_cityhash);
FILE *wfp = fopen(jpeg_fname, "wb");
if (!wfp) {
_wmkdir(L"Jpeg\\");
wfp = fopen(jpeg_fname, "wb");
}
fwrite(jpegBuf, 1, jpegSize, wfp);
fclose(wfp);
#endif //JPEG_DEBUG
@ -204,6 +212,57 @@ int getWidthHeight(int width, int height)
return (width << 16) | height;
}
u32 convertRGBToYCbCr(u32 rgb) {
//see http://en.wikipedia.org/wiki/Yuv#Y.27UV444_to_RGB888_conversion for more information.
u8 r = (rgb >> 16) & 0xFF;
u8 g = (rgb >> 8) & 0xFF;
u8 b = (rgb >> 0) & 0xFF;
int y = 0.299f * r + 0.587f * g + 0.114f * b + 0;
int cb = -0.169f * r - 0.331f * g + 0.499f * b + 128.0f;
int cr = 0.499f * r - 0.418f * g - 0.0813f * b + 128.0f;
// check yCbCr value
if ( y > 0xFF) y = 0xFF; if ( y < 0) y = 0;
if (cb > 0xFF) cb = 0xFF; if (cb < 0) cb = 0;
if (cr > 0xFF) cr = 0xFF; if (cr < 0) cr = 0;
return (y << 16) | (cb << 8) | cr;
}
int __JpegConvertRGBToYCbCr (const void *data, u32 bufferOutputAddr, int width, int height) {
u24_be *imageBuffer = (u24_be*)data;
int sizeY = width * height;
int sizeCb = sizeY >> 2;
u8 *Y = (u8*)Memory::GetPointer(bufferOutputAddr);
u8 *Cb = Y + sizeY;
u8 *Cr = Cb + sizeCb;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; x += 4) {
u32 abgr0 = imageBuffer[x + 0];
u32 abgr1 = imageBuffer[x + 1];
u32 abgr2 = imageBuffer[x + 2];
u32 abgr3 = imageBuffer[x + 3];
u32 yCbCr0 = convertRGBToYCbCr(abgr0);
u32 yCbCr1 = convertRGBToYCbCr(abgr1);
u32 yCbCr2 = convertRGBToYCbCr(abgr2);
u32 yCbCr3 = convertRGBToYCbCr(abgr3);
Y[x + 0] = (yCbCr0 >> 16) & 0xFF;
Y[x + 1] = (yCbCr1 >> 16) & 0xFF;
Y[x + 2] = (yCbCr2 >> 16) & 0xFF;
Y[x + 3] = (yCbCr3 >> 16) & 0xFF;
*Cb++ = (yCbCr0 >> 8) & 0xFF;
*Cr++ = yCbCr0 & 0xFF;
}
imageBuffer += width;
Y += width ;
}
return (width << 16) | height;
}
int sceJpegDecodeMJpegYCbCr(u32 jpegAddr, int jpegSize, u32 yCbCrAddr, int yCbCrSize, int dhtMode)
{
ERROR_LOG_REPORT(ME, "sceJpegDecodeMJpegYCbCr(%i, %i, %i, %i, %i)", jpegAddr, jpegSize, yCbCrAddr, yCbCrSize, dhtMode);
@ -225,7 +284,8 @@ int sceJpegDecodeMJpegYCbCr(u32 jpegAddr, int jpegSize, u32 yCbCrAddr, int yCbCr
}
if (jpegBuf == NULL)
return getWidthHeight(0, 0);
if (actual_components == 3)
__JpegConvertRGBToYCbCr(jpegBuf, yCbCrAddr, width, height);
// TODO: There's more...
return getWidthHeight(width, height);

View File

@ -402,6 +402,7 @@ public:
if (currentStack.start == (u32)-1)
{
currentStack.start = 0;
nt.initialStack = 0;
ERROR_LOG(SCEKERNEL, "Failed to allocate stack for thread");
return false;
}
@ -437,8 +438,8 @@ public:
if (currentStack.start != 0) {
DEBUG_LOG(SCEKERNEL, "Freeing thread stack %s", nt.name);
if (nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) {
Memory::Memset(currentStack.start, 0, currentStack.end - currentStack.start);
if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) {
Memory::Memset(nt.initialStack, 0, nt.stackSize);
}
if (nt.attr & PSP_THREAD_ATTR_KERNEL) {

View File

@ -1363,11 +1363,11 @@ u32 sceMpegAvcResourceInit(u32 mpeg)
return 0;
}
u32 convertARGBToYCbCr(u32 abgr) {
u32 convertABGRToYCbCr(u32 abgr) {
//see http://en.wikipedia.org/wiki/Yuv#Y.27UV444_to_RGB888_conversion for more information.
u8 r = (abgr >> 16) & 0xFF;
u8 r = (abgr >> 0) & 0xFF;
u8 g = (abgr >> 8) & 0xFF;
u8 b = (abgr >> 0) & 0xFF;
u8 b = (abgr >> 16) & 0xFF;
int y = 0.299f * r + 0.587f * g + 0.114f * b + 0;
int cb = -0.169f * r - 0.331f * g + 0.499f * b + 128.0f;
int cr = 0.499f * r - 0.418f * g - 0.0813f * b + 128.0f;
@ -1390,15 +1390,15 @@ int __MpegAvcConvertToYuv420(const void *data, u32 bufferOutputAddr, int width,
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; x += 4) {
u32 argb0 = imageBuffer[x + 0];
u32 argb1 = imageBuffer[x + 1];
u32 argb2 = imageBuffer[x + 2];
u32 argb3 = imageBuffer[x + 3];
u32 abgr0 = imageBuffer[x + 0];
u32 abgr1 = imageBuffer[x + 1];
u32 abgr2 = imageBuffer[x + 2];
u32 abgr3 = imageBuffer[x + 3];
u32 yCbCr0 = convertARGBToYCbCr(argb0);
u32 yCbCr1 = convertARGBToYCbCr(argb1);
u32 yCbCr2 = convertARGBToYCbCr(argb2);
u32 yCbCr3 = convertARGBToYCbCr(argb3);
u32 yCbCr0 = convertABGRToYCbCr(abgr0);
u32 yCbCr1 = convertABGRToYCbCr(abgr1);
u32 yCbCr2 = convertABGRToYCbCr(abgr2);
u32 yCbCr3 = convertABGRToYCbCr(abgr3);
Y[x + 0] = (yCbCr0 >> 16) & 0xFF;
Y[x + 1] = (yCbCr1 >> 16) & 0xFF;

View File

@ -18,17 +18,60 @@
#include "HLE.h"
#include "scePauth.h"
#include "zlib.h"
int scePauth_F7AA47F6(u32 srcPtr, int srcLength, u32 destLengthPtr, u32 workArea)
{
ERROR_LOG(HLE, "UNIMPL scePauth_F7AA47F6(%d, %d, %d, %d)", srcPtr, srcLength, destLengthPtr, workArea);
ERROR_LOG(HLE, "UNIMPL scePauth_F7AA47F6(%08x, %08x, %08x, %08x)", srcPtr, srcLength, destLengthPtr, workArea);
return 0;
}
int scePauth_98B83B5D(u32 srcPtr, int srcLength, u32 destLengthPtr, u32 workArea)
{
ERROR_LOG(HLE, "UNIMPL scePauth_98B83B5D(%d, %d, %d, %d)", srcPtr, srcLength, destLengthPtr, workArea);
return 0;
u8 *src, *key;
u32 crc;
char path[256], name[256];
std::string hostPath;
FILE *fp;
int size;
INFO_LOG(HLE, "scePauth_98B83B5D(%08x, %08x, %08x, %08x)", srcPtr, srcLength, destLengthPtr, workArea);
sprintf(name, "ms0:/PAUTH");
pspFileSystem.GetHostPath(std::string(name), hostPath);
src = (u8*) Memory::GetPointer(srcPtr);
key = (u8*) Memory::GetPointer(workArea);
crc = crc32(0, src, srcLength);
sprintf(name, "%s/pauth_%08x.bin.decrypt", hostPath.c_str(), crc);
fp = fopen(name, "rb");
if(fp){
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
fread(src, 1, size, fp);
fclose(fp);
Memory::Write_U32(size, destLengthPtr);
INFO_LOG(HLE, " read from decrypted file %s", name);
return 0;
}
pspFileSystem.MkDir("ms0:/PAUTH");
sprintf(name, "%s/pauth_%08x.bin", hostPath.c_str(), crc);
ERROR_LOG(HLE, " no decrypted file found! save as %s", name);
fp = fopen(name, "wb");
fwrite(src, 1, srcLength, fp);
fclose(fp);
sprintf(name, "%s/pauth_%08x.key", hostPath.c_str(), crc);
fp = fopen(name, "wb");
fwrite(key, 1, 16, fp);
fclose(fp);
return -1;
}
const HLEFunction scePauth[] = {
@ -39,4 +82,4 @@ const HLEFunction scePauth[] = {
void Register_scePauth()
{
RegisterModule("scePauth", ARRAY_SIZE(scePauth), scePauth);
}
}

View File

@ -29,6 +29,8 @@
#include "Core/MIPS/ARM/ArmJit.h"
#include "Core/MIPS/ARM/ArmRegCache.h"
// Cool NEON references:
// http://www.delmarnorth.com/microwave/requirements/neon-test-tutorial.pdf
const bool disablePrefixes = false;

View File

@ -32,6 +32,18 @@ using namespace Gen;
// Temp regs: 4 from S prefix, 4 from T prefix, 4 from D mask, and 4 for work (worst case.)
// But most of the time prefixes aren't used that heavily so we won't use all of them.
// PLANS FOR PROPER SIMD
// 1, 2, 3, and 4-vectors will be loaded into single XMM registers
// Matrices will be loaded into pairs, triads, or quads of XMM registers - simply by loading
// the columns or the rows one by one.
// On x86 this means that only one 4x4 matrix can be fully loaded at once but that's alright.
// We might want to keep "linearized" columns in memory.
// Implement optimized vec/matrix multiplications of all types and transposes that
// take into account in which XMM registers the values are. Fallback: Just dump out the values
// and do it the old way.
enum {
NUM_TEMPS = 16,
TEMP0 = 32 + 128,

View File

@ -36,9 +36,13 @@
#include <algorithm>
#if defined(USING_GLES2)
#ifndef GL_READ_FRAMEBUFFER
#define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER
#define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER
#endif
#ifndef GL_RGBA8
#define GL_RGBA8 GL_RGBA
#endif
#ifndef GL_DEPTH_COMPONENT24
#define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
#endif
@ -97,10 +101,8 @@ inline u16 RGBA8888toRGBA5551(u32 px) {
void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 stride, u32 height, GEBufferFormat format);
void CenterRect(float *x, float *y, float *w, float *h,
float origW, float origH, float frameW, float frameH)
{
if (g_Config.bStretchToDisplay)
{
float origW, float origH, float frameW, float frameH) {
if (g_Config.bStretchToDisplay) {
*x = 0;
*y = 0;
*w = frameW;
@ -111,8 +113,7 @@ void CenterRect(float *x, float *y, float *w, float *h,
float origRatio = origW/origH;
float frameRatio = frameW/frameH;
if (origRatio > frameRatio)
{
if (origRatio > frameRatio) {
// Image is wider than frame. Center vertically.
float scale = origW / frameW;
*x = 0.0f;
@ -124,9 +125,7 @@ void CenterRect(float *x, float *y, float *w, float *h,
*h = (frameH + *h) / 2.0f; // (408 + 720) / 2 = 564
#endif
*y = (frameH - *h) / 2.0f;
}
else
{
} else {
// Image is taller than frame. Center horizontally.
float scale = origH / frameH;
*y = 0.0f;
@ -168,20 +167,25 @@ void FramebufferManager::SetNumExtraFBOs(int num) {
void FramebufferManager::CompileDraw2DProgram() {
if (!draw2dprogram_) {
SetNumExtraFBOs(0);
draw2dprogram_ = glsl_create_source(basic_vs, tex_fs);
glsl_bind(draw2dprogram_);
glUniform1i(draw2dprogram_->sampler0, 0);
SetNumExtraFBOs(0);
if (g_Config.bFXAA) {
useFXAA_ = true;
fxaaProgram_ = glsl_create("shaders/fxaa.vsh", "shaders/fxaa.fsh");
glsl_bind(fxaaProgram_);
glUniform1i(fxaaProgram_->sampler0, 0);
SetNumExtraFBOs(1);
float u_delta = 1.0f / PSP_CoreParameter().renderWidth;
float v_delta = 1.0f / PSP_CoreParameter().renderHeight;
glUniform2f(glsl_uniform_loc(fxaaProgram_, "u_texcoordDelta"), u_delta, v_delta);
if (!fxaaProgram_) {
ERROR_LOG(G3D, "Failed to build FXAA program");
useFXAA_ = false;
} else {
glsl_bind(fxaaProgram_);
glUniform1i(fxaaProgram_->sampler0, 0);
SetNumExtraFBOs(1);
float u_delta = 1.0f / PSP_CoreParameter().renderWidth;
float v_delta = 1.0f / PSP_CoreParameter().renderHeight;
glUniform2f(glsl_uniform_loc(fxaaProgram_, "u_texcoordDelta"), u_delta, v_delta);
}
} else {
fxaaProgram_ = 0;
useFXAA_ = false;
@ -195,6 +199,8 @@ void FramebufferManager::DestroyDraw2DProgram() {
if (draw2dprogram_) {
glsl_destroy(draw2dprogram_);
draw2dprogram_ = 0;
}
if (fxaaProgram_) {
glsl_destroy(fxaaProgram_);
fxaaProgram_ = 0;
}
@ -233,33 +239,35 @@ FramebufferManager::FramebufferManager() :
// Check vendor string to try and guess GPU
const char *cvendor = (char *)glGetString(GL_VENDOR);
if(cvendor) {
if (cvendor) {
const std::string vendor(cvendor);
if(vendor == "NVIDIA Corporation"
if (vendor == "NVIDIA Corporation"
|| vendor == "Nouveau"
|| vendor == "nouveau") {
gpuVendor = GPU_VENDOR_NVIDIA;
} else if(vendor == "Advanced Micro Devices, Inc."
} else if (vendor == "Advanced Micro Devices, Inc."
|| vendor == "ATI Technologies Inc.") {
gpuVendor = GPU_VENDOR_AMD;
} else if(vendor == "Intel"
} else if (vendor == "Intel"
|| vendor == "Intel Inc."
|| vendor == "Intel Corporation"
|| vendor == "Tungsten Graphics, Inc") { // We'll assume this last one means Intel
gpuVendor = GPU_VENDOR_INTEL;
} else if(vendor == "ARM")
} else if (vendor == "ARM") {
gpuVendor = GPU_VENDOR_ARM;
else if(vendor == "Imagination Technologies")
} else if (vendor == "Imagination Technologies") {
gpuVendor = GPU_VENDOR_POWERVR;
else if(vendor == "Qualcomm")
} else if (vendor == "Qualcomm") {
gpuVendor = GPU_VENDOR_ADRENO;
else
} else {
gpuVendor = GPU_VENDOR_UNKNOWN;
} else
}
} else {
gpuVendor = GPU_VENDOR_UNKNOWN;
}
gstate_c.gpuVendor = gpuVendor;
NOTICE_LOG(SCEGE, "GPU Vendor : %s", cvendor);
SetLineWidth();
}
FramebufferManager::~FramebufferManager() {
@ -387,7 +395,7 @@ void FramebufferManager::DrawActiveTexture(float x, float y, float w, float h, f
const float pos[12] = {x,y,0, x+w,y,0, x+w,y+h,0, x,y+h,0};
const float texCoords[8] = {0,v1, u2,v1, u2,v2, 0,v2};
const GLubyte indices[4] = {0,1,3,2};
if (!draw2dprogram_) {
CompileDraw2DProgram();
}
@ -410,7 +418,10 @@ void FramebufferManager::DrawActiveTexture(float x, float y, float w, float h, f
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);
glDisableVertexAttribArray(program->a_position);
glDisableVertexAttribArray(program->a_texcoord0);
glsl_unbind();
shaderManager_->DirtyLastShader(); // dirty lastShader_
}
VirtualFramebuffer *FramebufferManager::GetVFBAt(u32 addr) {
@ -733,6 +744,18 @@ void FramebufferManager::SetRenderFrameBuffer() {
}
}
void FramebufferManager::SetLineWidth() {
#ifndef USING_GLES2
if (g_Config.iInternalResolution == 0) {
glLineWidth(std::max(1, (int)(PSP_CoreParameter().renderWidth / 480)));
glPointSize(std::max(1.0f, (float)(PSP_CoreParameter().renderWidth / 480.f)));
} else {
glLineWidth(g_Config.iInternalResolution);
glPointSize((float)g_Config.iInternalResolution);
}
#endif
}
void FramebufferManager::CopyDisplayToOutput() {
fbo_unbind();
currentRenderVfb_ = 0;
@ -777,6 +800,7 @@ void FramebufferManager::CopyDisplayToOutput() {
if (resized_) {
ClearBuffer();
DestroyDraw2DProgram();
SetLineWidth();
}
if (vfb->fbo) {
@ -785,6 +809,9 @@ void FramebufferManager::CopyDisplayToOutput() {
GLuint colorTexture = fbo_get_color_texture(vfb->fbo);
// TODO ES3: Use glInvalidateFramebuffer to discard depth/stencil data at the end of frame.
// and to discard extraFBOs_ after using them.
if (useFXAA_ && extraFBOs_.size() == 1) {
glBindTexture(GL_TEXTURE_2D, colorTexture);
@ -806,12 +833,13 @@ void FramebufferManager::CopyDisplayToOutput() {
// These are in the output display coordinates
float x, y, w, h;
CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight);
#if defined(USING_GLES2) && !defined(__SYMBIAN32__) && !defined(MEEGO_EDITION_HARMATTAN) && !defined(IOS)
if (gl_extensions.NV_draw_texture) {
// Fast path for Tegra. TODO: Make this path work on desktop nvidia, seems glew doesn't have a clue.
glDrawTextureNV(colorTexture, 0,
x, y, w, h, 0.0f,
// Actually, on Desktop we should just use glBlitFramebuffer.
glDrawTextureNV(colorTexture, 0,
x, y, w, h, 0.0f,
0, 0, 480.0f / (float)vfb->width, 272.0f / (float)vfb->height);
return;
}
@ -825,12 +853,12 @@ void FramebufferManager::CopyDisplayToOutput() {
void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync) {
#ifndef USING_GLES2
if(sync) {
if (sync) {
PackFramebufferAsync_(NULL); // flush async just in case when we go for synchronous update
}
#endif
#endif
if(vfb) {
if (vfb) {
// We'll pseudo-blit framebuffers here to get a resized and flipped version of vfb.
// For now we'll keep these on the same struct as the ones that can get displayed
// (and blatantly copy work already done above while at it).
@ -851,7 +879,7 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool s
}
// Create a new fbo if none was found for the size
if(!nvfb) {
if (!nvfb) {
nvfb = new VirtualFramebuffer();
nvfb->fbo = 0;
nvfb->fb_address = vfb->fb_address;
@ -876,7 +904,7 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool s
case GE_FORMAT_5551:
nvfb->colorDepth = FBO_5551;
break;
case GE_FORMAT_565:
case GE_FORMAT_565:
nvfb->colorDepth = FBO_565;
break;
case GE_FORMAT_8888:
@ -893,7 +921,7 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool s
nvfb->last_frame_render = gpuStats.numFlips;
bvfbs_.push_back(nvfb);
fbo_bind_as_render_target(nvfb->fbo);
fbo_bind_as_render_target(nvfb->fbo);
ClearBuffer();
glEnable(GL_DITHER);
} else {
@ -923,8 +951,8 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool s
#ifdef USING_GLES2
PackFramebufferSync_(nvfb); // synchronous glReadPixels
#else
if(gl_extensions.PBO_ARB || !gl_extensions.ATIClampBug) {
if(!sync) {
if (gl_extensions.PBO_ARB || !gl_extensions.ATIClampBug) {
if (!sync) {
PackFramebufferAsync_(nvfb); // asynchronous glReadPixels using PBOs
} else {
PackFramebufferSync_(nvfb); // synchronous glReadPixels
@ -935,7 +963,6 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool s
}
void FramebufferManager::BlitFramebuffer_(VirtualFramebuffer *src, VirtualFramebuffer *dst, bool flip, float upscale, float vscale) {
if (dst->fbo) {
fbo_bind_as_render_target(dst->fbo);
} else {
@ -943,13 +970,13 @@ void FramebufferManager::BlitFramebuffer_(VirtualFramebuffer *src, VirtualFrameb
fbo_unbind();
return;
}
if(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
ERROR_LOG(SCEGE, "Incomplete target framebuffer, aborting blit");
fbo_unbind();
return;
}
glstate.viewport.set(0, 0, dst->width, dst->height);
DisableState();
@ -967,15 +994,15 @@ void FramebufferManager::BlitFramebuffer_(VirtualFramebuffer *src, VirtualFrameb
CompileDraw2DProgram();
DrawActiveTexture(x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, flip, upscale, vscale, draw2dprogram_);
glBindTexture(GL_TEXTURE_2D, 0);
fbo_unbind();
}
// TODO: SSE/NEON
void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 stride, u32 height, GEBufferFormat format) {
if(format == GE_FORMAT_8888) {
if(src == dst) {
if (format == GE_FORMAT_8888) {
if (src == dst) {
return;
} else { // Here lets assume they don't intersect
memcpy(dst, src, stride * height * 4);
@ -986,18 +1013,17 @@ void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 stride, u32 height, GEBufferForma
u16 *dst16 = (u16 *)dst;
switch (format) {
case GE_FORMAT_565: // BGR 565
for(int i = 0; i < size; i++) {
for (int i = 0; i < size; i++) {
dst16[i] = RGBA8888toRGB565(src32[i]);
}
break;
case GE_FORMAT_5551: // ABGR 1555
for(int i = 0; i < size; i++) {
for (int i = 0; i < size; i++) {
dst16[i] = RGBA8888toRGBA5551(src32[i]);
}
break;
case GE_FORMAT_4444: // ABGR 4444
for(int i = 0; i < size; i++) {
for (int i = 0; i < size; i++) {
dst16[i] = RGBA8888toRGBA4444(src32[i]);
}
break;
@ -1020,35 +1046,32 @@ void FramebufferManager::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
bool useCPU = g_Config.iRenderingMode == FB_READFBOMEMORY_CPU;
// We'll prepare two PBOs to switch between readying and reading
if(!pixelBufObj_) {
if (!pixelBufObj_) {
GLuint pbos[MAX_PBO];
glGenBuffers(MAX_PBO, pbos);
pixelBufObj_ = new AsyncPBO[MAX_PBO];
for(int i = 0; i < MAX_PBO; i++) {
for (int i = 0; i < MAX_PBO; i++) {
pixelBufObj_[i].handle = pbos[i];
pixelBufObj_[i].maxSize = 0;
pixelBufObj_[i].reading = false;
}
}
// Receive previously requested data from a PBO
if(pixelBufObj_[nextPBO].reading) {
if (pixelBufObj_[nextPBO].reading) {
glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBufObj_[nextPBO].handle);
packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
if(packed) {
DEBUG_LOG(SCEGE, "Reading PBO to memory , bufSize = %u, packed = %08x, fb_address = %08x, stride = %u, pbo = %u",
if (packed) {
DEBUG_LOG(SCEGE, "Reading PBO to memory , bufSize = %u, packed = %08x, fb_address = %08x, stride = %u, pbo = %u",
pixelBufObj_[nextPBO].size, packed, pixelBufObj_[nextPBO].fb_address, pixelBufObj_[nextPBO].stride, nextPBO);
if(useCPU) {
ConvertFromRGBA8888(Memory::GetPointer(pixelBufObj_[nextPBO].fb_address), packed,
pixelBufObj_[nextPBO].stride, pixelBufObj_[nextPBO].height,
if (useCPU) {
ConvertFromRGBA8888(Memory::GetPointer(pixelBufObj_[nextPBO].fb_address), packed,
pixelBufObj_[nextPBO].stride, pixelBufObj_[nextPBO].height,
pixelBufObj_[nextPBO].format);
} else {
} else {
// We don't need to convert, GPU already did (or should have)
Memory::Memcpy(pixelBufObj_[nextPBO].fb_address, packed, pixelBufObj_[nextPBO].size);
}
@ -1057,12 +1080,11 @@ void FramebufferManager::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
}
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
unbind = true;
}
// Order packing/readback of the framebuffer
if(vfb) {
if (vfb) {
int pixelType, pixelSize, pixelFormat, align;
bool reverseOrder = (gpuVendor == GPU_VENDOR_NVIDIA) || (gpuVendor == GPU_VENDOR_AMD);
@ -1104,16 +1126,16 @@ void FramebufferManager::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
fbo_bind_for_read(vfb->fbo);
} else {
fbo_unbind();
if(gl_extensions.FBO_ARB) {
if (gl_extensions.FBO_ARB) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
return;
}
if(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
if (glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
ERROR_LOG(SCEGE, "Incomplete source framebuffer, aborting read");
fbo_unbind();
if(gl_extensions.FBO_ARB) {
if (gl_extensions.FBO_ARB) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
return;
@ -1121,9 +1143,9 @@ void FramebufferManager::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBufObj_[currentPBO_].handle);
if(pixelBufObj_[currentPBO_].maxSize < bufSize) {
if (pixelBufObj_[currentPBO_].maxSize < bufSize) {
// We reserve a buffer big enough to fit all those pixels
if(useCPU && pixelType != GL_UNSIGNED_BYTE) {
if (useCPU && pixelType != GL_UNSIGNED_BYTE) {
// Wnd result may be 16-bit but we are reading 32-bit, so we need double the space on the buffer
glBufferData(GL_PIXEL_PACK_BUFFER, bufSize*2, NULL, GL_DYNAMIC_READ);
} else {
@ -1132,7 +1154,7 @@ void FramebufferManager::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
pixelBufObj_[currentPBO_].maxSize = bufSize;
}
if(useCPU) {
if (useCPU) {
// If converting pixel formats on the CPU we'll always request RGBA8888
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glReadPixels(0, 0, vfb->fb_stride, vfb->height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
@ -1146,21 +1168,21 @@ void FramebufferManager::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
switch(error) {
case 0:
break;
case GL_INVALID_ENUM:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_ENUM");
case GL_INVALID_ENUM:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_ENUM");
break;
case GL_INVALID_VALUE:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_VALUE");
case GL_INVALID_VALUE:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_VALUE");
break;
case GL_INVALID_OPERATION:
// GL_INVALID_OPERATION will happen sometimes midframe but everything
// seems to work out when actually mapping buffers?
// GL_SAMPLE_BUFFERS, GL_READ_BUFFER, GL_BUFFER_SIZE/MAPPED,
case GL_INVALID_OPERATION:
// GL_INVALID_OPERATION will happen sometimes midframe but everything
// seems to work out when actually mapping buffers?
// GL_SAMPLE_BUFFERS, GL_READ_BUFFER, GL_BUFFER_SIZE/MAPPED,
// GL_PIXEL_PACK_BUFFER_BINDING, all have the expected values.
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_OPERATION");
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_OPERATION");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION");
case GL_INVALID_FRAMEBUFFER_OPERATION:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION");
break;
default:
ERROR_LOG(SCEGE, "glReadPixels: UNKNOWN OPENGL ERROR %u", error);
@ -1168,7 +1190,7 @@ void FramebufferManager::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
}
fbo_unbind();
if(gl_extensions.FBO_ARB) {
if (gl_extensions.FBO_ARB) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
@ -1184,7 +1206,7 @@ void FramebufferManager::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
currentPBO_ = nextPBO;
if(unbind) {
if (unbind) {
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
}
@ -1205,13 +1227,13 @@ void FramebufferManager::PackFramebufferSync_(VirtualFramebuffer *vfb) {
u32 fb_address = (0x04000000) | vfb->fb_address;
GLubyte *packed = 0;
if(vfb->format == GE_FORMAT_8888) {
if (vfb->format == GE_FORMAT_8888) {
packed = (GLubyte *)Memory::GetPointer(fb_address);
} else { // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address
packed = (GLubyte *)malloc(bufSize * sizeof(GLubyte));
}
if(packed) {
if (packed) {
DEBUG_LOG(SCEGE, "Reading framebuffer to mem, bufSize = %u, packed = %p, fb_address = %08x",
(u32)bufSize, packed, fb_address);
@ -1221,26 +1243,26 @@ void FramebufferManager::PackFramebufferSync_(VirtualFramebuffer *vfb) {
switch(error) {
case 0:
break;
case GL_INVALID_ENUM:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_ENUM");
case GL_INVALID_ENUM:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_ENUM");
break;
case GL_INVALID_VALUE:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_VALUE");
case GL_INVALID_VALUE:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_VALUE");
break;
case GL_INVALID_OPERATION:
// GL_INVALID_OPERATION will happen sometimes midframe but everything
// seems to work out when actually reading?
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_OPERATION");
// GL_INVALID_OPERATION will happen sometimes midframe but everything
// seems to work out when actually reading?
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_OPERATION");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION");
case GL_INVALID_FRAMEBUFFER_OPERATION:
ERROR_LOG(SCEGE, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION");
break;
default:
ERROR_LOG(SCEGE, "glReadPixels: UNKNOWN OPENGL ERROR %u", error);
break;
}
if(vfb->format != GE_FORMAT_8888) { // If not RGBA 8888 we need to convert
if (vfb->format != GE_FORMAT_8888) { // If not RGBA 8888 we need to convert
ConvertFromRGBA8888(Memory::GetPointer(fb_address), packed, vfb->fb_stride, vfb->height, vfb->format);
free(packed);
}
@ -1319,7 +1341,7 @@ void FramebufferManager::DecimateFBOs() {
VirtualFramebuffer *vfb = vfbs_[i];
int age = frameLastFramebufUsed - std::max(vfb->last_frame_render, vfb->last_frame_used);
if(updateVram && age == 0 && !vfb->memoryUpdated && vfb == displayFramebuf_)
if (updateVram && age == 0 && !vfb->memoryUpdated && vfb == displayFramebuf_)
ReadFramebufferToMemory(vfb);
if (vfb == displayFramebuf_ || vfb == prevDisplayFramebuf_ || vfb == prevPrevDisplayFramebuf_) {

View File

@ -137,6 +137,7 @@ public:
void CopyDisplayToOutput();
void SetRenderFrameBuffer(); // Uses parameters computed from gstate
void UpdateFromMemory(u32 addr, int size);
void SetLineWidth();
#ifdef USING_GLES2
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync = true);

View File

@ -52,8 +52,6 @@ struct CommandTableEntry {
static const CommandTableEntry commandTable[] = {
// Changes that dirty the framebuffer
{GE_CMD_REGION1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
{GE_CMD_REGION2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
{GE_CMD_FRAMEBUFPTR, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
{GE_CMD_FRAMEBUFWIDTH, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
{GE_CMD_FRAMEBUFPIXFORMAT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
@ -144,8 +142,6 @@ static const CommandTableEntry commandTable[] = {
{GE_CMD_TEXENVCOLOR, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
// Simple render state changes. Handled in StateMapping.cpp.
{GE_CMD_SCISSOR1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
{GE_CMD_SCISSOR2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
{GE_CMD_OFFSETX, FLAG_FLUSHBEFOREONCHANGE},
{GE_CMD_OFFSETY, FLAG_FLUSHBEFOREONCHANGE},
{GE_CMD_CULL, FLAG_FLUSHBEFOREONCHANGE},
@ -198,6 +194,14 @@ static const CommandTableEntry commandTable[] = {
{GE_CMD_VIEWPORTZ1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
{GE_CMD_VIEWPORTZ2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
// Region
{GE_CMD_REGION1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
{GE_CMD_REGION2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
// Scissor
{GE_CMD_SCISSOR1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
{GE_CMD_SCISSOR2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
// These dirty various vertex shader uniforms. Could embed information about that in this table and call dirtyuniform directly, hm...
{GE_CMD_AMBIENTCOLOR, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
{GE_CMD_AMBIENTALPHA, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE},
@ -351,6 +355,23 @@ static const CommandTableEntry commandTable[] = {
{GE_CMD_UNKNOWN_B7, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_D1, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_ED, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_EF, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_F0, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_F1, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_F2, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_F3, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_F4, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_F5, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_F6, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_F7, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_F8, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_F9, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_FA, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_FB, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_FC, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_FD, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_FE, FLAG_EXECUTE},
{GE_CMD_UNKNOWN_FF, FLAG_EXECUTE},
};
@ -363,26 +384,30 @@ GLES_GPU::GLES_GPU()
} else {
glstate.SetVSyncInterval(g_Config.bVSync ? 1 : 0);
}
#ifdef ANDROID
if (gl_extensions.QCOM_binning_control)
if (gl_extensions.QCOM_binning_control) {
/*
We can try different HINTS later or even with option to toggle for Adreno GPU
CPU_OPTIMIZED_QCOM
CPU_OPTIMIZED_QCOM
- binning algorithm focuses on lower CPU utilization (this path increases vertex processing
GPU_OPTIMIZED_QCOM
GPU_OPTIMIZED_QCOM
- binning algorithm focuses on lower GPU utilization (this path increases CPU usage
RENDER_DIRECT_TO_FRAMEBUFFER_QCOM
- render directly to the final framebuffer and bypass tile memory
RENDER_DIRECT_TO_FRAMEBUFFER_QCOM
- render directly to the final framebuffer and bypass tile memory
(this path has a low CPU usage, but in some cases uses more memory bandwidth)
*/
glHint(GL_BINNING_CONTROL_HINT_QCOM, GL_RENDER_DIRECT_TO_FRAMEBUFFER_QCOM);
#endif
// Got a report this might be causing crashes, so I disabled it.
// There have been no reports of a consistent speedup with it on, so meh.
//
// glHint(GL_BINNING_CONTROL_HINT_QCOM, GL_RENDER_DIRECT_TO_FRAMEBUFFER_QCOM);
}
#endif
shaderManager_ = new ShaderManager();
transformDraw_.SetShaderManager(shaderManager_);
transformDraw_.SetTextureCache(&textureCache_);
@ -415,6 +440,16 @@ GLES_GPU::GLES_GPU()
}
}
// No need to flush before the tex scale/offset commands if we are baking
// the tex scale/offset into the vertices anyway.
if (g_Config.bPrescaleUV) {
commandFlags_[GE_CMD_TEXSCALEU] &= ~FLAG_FLUSHBEFOREONCHANGE;
commandFlags_[GE_CMD_TEXSCALEV] &= ~FLAG_FLUSHBEFOREONCHANGE;
commandFlags_[GE_CMD_TEXOFFSETU] &= ~FLAG_FLUSHBEFOREONCHANGE;
commandFlags_[GE_CMD_TEXOFFSETV] &= ~FLAG_FLUSHBEFOREONCHANGE;
}
BuildReportingInfo();
}
@ -448,10 +483,12 @@ void GLES_GPU::BuildReportingInfo() {
}
void GLES_GPU::DeviceLost() {
ILOG("GLES_GPU: DeviceLost");
// Should only be executed on the GL thread.
// Simply drop all caches and textures.
// FBOs appear to survive? Or no?
// TransformDraw has registered as a GfxResourceHolder.
shaderManager_->ClearCache(false);
textureCache_.Clear(false);
framebufferManager_.DeviceLost();
@ -569,7 +606,7 @@ void GLES_GPU::CopyDisplayToOutputInternal() {
framebufferManager_.CopyDisplayToOutput();
framebufferManager_.EndFrame();
shaderManager_->EndFrame();
shaderManager_->DirtyLastShader();
// If buffered, discard the depth buffer of the backbuffer. Don't even know if we need one.
#if 0
@ -956,8 +993,10 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
case GE_CMD_TEXADDR5:
case GE_CMD_TEXADDR6:
case GE_CMD_TEXADDR7:
gstate_c.textureChanged = true;
shaderManager_->DirtyUniform(DIRTY_UVSCALEOFFSET);
if (diff) {
gstate_c.textureChanged = true;
shaderManager_->DirtyUniform(DIRTY_UVSCALEOFFSET);
}
break;
case GE_CMD_TEXBUFWIDTH0:
@ -968,13 +1007,17 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
case GE_CMD_TEXBUFWIDTH5:
case GE_CMD_TEXBUFWIDTH6:
case GE_CMD_TEXBUFWIDTH7:
gstate_c.textureChanged = true;
if (diff) {
gstate_c.textureChanged = true;
}
break;
case GE_CMD_CLUTADDR:
case GE_CMD_CLUTADDRUPPER:
case GE_CMD_CLUTFORMAT:
gstate_c.textureChanged = true;
if (diff) {
gstate_c.textureChanged = true;
}
// This could be used to "dirty" textures with clut.
break;
@ -1243,6 +1286,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
case GE_CMD_STENCILTESTENABLE:
case GE_CMD_ZTESTENABLE:
case GE_CMD_ZTEST:
case GE_CMD_ZWRITEDISABLE:
break;
case GE_CMD_MORPHWEIGHT0:
@ -1399,6 +1443,23 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
case GE_CMD_UNKNOWN_B7:
case GE_CMD_UNKNOWN_D1:
case GE_CMD_UNKNOWN_ED:
case GE_CMD_UNKNOWN_EF:
case GE_CMD_UNKNOWN_F0:
case GE_CMD_UNKNOWN_F1:
case GE_CMD_UNKNOWN_F2:
case GE_CMD_UNKNOWN_F3:
case GE_CMD_UNKNOWN_F4:
case GE_CMD_UNKNOWN_F5:
case GE_CMD_UNKNOWN_F6:
case GE_CMD_UNKNOWN_F7:
case GE_CMD_UNKNOWN_F8:
case GE_CMD_UNKNOWN_F9:
case GE_CMD_UNKNOWN_FA:
case GE_CMD_UNKNOWN_FB:
case GE_CMD_UNKNOWN_FC:
case GE_CMD_UNKNOWN_FD:
case GE_CMD_UNKNOWN_FE:
case GE_CMD_UNKNOWN_FF:
if (data != 0)
WARN_LOG_REPORT_ONCE(unknowncmd, G3D, "Unknown GE command : %08x ", op);
break;

View File

@ -31,8 +31,7 @@
class ShaderManager;
class LinkedShader;
class GLES_GPU : public GPUCommon
{
class GLES_GPU : public GPUCommon {
public:
GLES_GPU();
~GLES_GPU();
@ -51,7 +50,7 @@ public:
virtual void DumpNextFrame();
virtual void DoState(PointerWrap &p);
// Called by the window system if the window size changed. This will be reflected in PSPCoreParam.pixel*.
virtual void Resized();
virtual bool DecodeTexture(u8* dest, GPUgstate state) {

View File

@ -44,7 +44,7 @@ Shader::Shader(const char *code, uint32_t shaderType, bool useHWTransform) : fai
OutputDebugStringUTF8(code);
#endif
shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &code, 0);
glShaderSource(shader, 1, &code, 0);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
@ -73,13 +73,33 @@ Shader::~Shader() {
glDeleteShader(shader);
}
LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTransform)
LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTransform, LinkedShader *previous)
: useHWTransform_(useHWTransform), program(0), dirtyUniforms(0) {
program = glCreateProgram();
glAttachShader(program, vs->shader);
glAttachShader(program, fs->shader);
// Bind attribute locations to fixed locations so that they're
// the same in all shaders. We use this later to minimize the calls to
// glEnableVertexAttribArray and glDisableVertexAttribArray.
glBindAttribLocation(program, ATTR_POSITION, "position");
glBindAttribLocation(program, ATTR_TEXCOORD, "texcoord");
glBindAttribLocation(program, ATTR_NORMAL, "normal");
glBindAttribLocation(program, ATTR_W1, "w1");
glBindAttribLocation(program, ATTR_W2, "w2");
glBindAttribLocation(program, ATTR_COLOR0, "color0");
glBindAttribLocation(program, ATTR_COLOR1, "color1");
glLinkProgram(program);
// Detaching shaders is annoying when debugging with gDebugger
// so let's not do that on Windows.
#ifdef USING_GLES
glDetachShader(program, vs->shader);
glDetachShader(program, fs->shader);
#endif
GLint linkStatus = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
@ -162,13 +182,14 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans
u_lightspecular[i] = glGetUniformLocation(program, temp);
}
a_position = glGetAttribLocation(program, "a_position");
a_color0 = glGetAttribLocation(program, "a_color0");
a_color1 = glGetAttribLocation(program, "a_color1");
a_texcoord = glGetAttribLocation(program, "a_texcoord");
a_normal = glGetAttribLocation(program, "a_normal");
a_weight0123 = glGetAttribLocation(program, "a_w1");
a_weight4567 = glGetAttribLocation(program, "a_w2");
attrMask = 0;
if (-1 != glGetAttribLocation(program, "position")) attrMask |= 1 << ATTR_POSITION;
if (-1 != glGetAttribLocation(program, "texcoord")) attrMask |= 1 << ATTR_TEXCOORD;
if (-1 != glGetAttribLocation(program, "normal")) attrMask |= 1 << ATTR_NORMAL;
if (-1 != glGetAttribLocation(program, "w1")) attrMask |= 1 << ATTR_W1;
if (-1 != glGetAttribLocation(program, "w2")) attrMask |= 1 << ATTR_W2;
if (-1 != glGetAttribLocation(program, "color0")) attrMask |= 1 << ATTR_COLOR0;
if (-1 != glGetAttribLocation(program, "color1")) attrMask |= 1 << ATTR_COLOR1;
glUseProgram(program);
@ -176,10 +197,11 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans
glUniform1i(u_tex, 0);
// The rest, use the "dirty" mechanism.
dirtyUniforms = DIRTY_ALL;
use(vertType);
use(vertType, previous);
}
LinkedShader::~LinkedShader() {
// Shaders are automatically detached by glDeleteProgram.
glDeleteProgram(program);
}
@ -259,26 +281,30 @@ static void SetMatrix4x3(int uniform, const float *m4x3) {
glUniformMatrix4fv(uniform, 1, GL_FALSE, m4x4);
}
void LinkedShader::use(u32 vertType) {
void LinkedShader::use(u32 vertType, LinkedShader *previous) {
glUseProgram(program);
updateUniforms(vertType);
glEnableVertexAttribArray(a_position);
if (a_texcoord != -1) glEnableVertexAttribArray(a_texcoord);
if (a_color0 != -1) glEnableVertexAttribArray(a_color0);
if (a_color1 != -1) glEnableVertexAttribArray(a_color1);
if (a_normal != -1) glEnableVertexAttribArray(a_normal);
if (a_weight0123 != -1) glEnableVertexAttribArray(a_weight0123);
if (a_weight4567 != -1) glEnableVertexAttribArray(a_weight4567);
int enable, disable;
if (previous) {
enable = attrMask & ~previous->attrMask;
disable = (~attrMask) & previous->attrMask;
} else {
enable = attrMask;
disable = ~attrMask;
}
for (int i = 0; i < ATTR_COUNT; i++) {
if (enable & (1 << i))
glEnableVertexAttribArray(i);
else if (disable & (1 << i))
glDisableVertexAttribArray(i);
}
}
void LinkedShader::stop() {
glDisableVertexAttribArray(a_position);
if (a_texcoord != -1) glDisableVertexAttribArray(a_texcoord);
if (a_color0 != -1) glDisableVertexAttribArray(a_color0);
if (a_color1 != -1) glDisableVertexAttribArray(a_color1);
if (a_normal != -1) glDisableVertexAttribArray(a_normal);
if (a_weight0123 != -1) glDisableVertexAttribArray(a_weight0123);
if (a_weight4567 != -1) glDisableVertexAttribArray(a_weight4567);
for (int i = 0; i < ATTR_COUNT; i++) {
if (attrMask & (1 << i))
glDisableVertexAttribArray(i);
}
}
void LinkedShader::updateUniforms(u32 vertType) {
@ -331,10 +357,10 @@ void LinkedShader::updateUniforms(u32 vertType) {
if (gstate.isModeThrough()) {
// We never get here because we don't use HW transform with through mode.
// Although - why don't we?
uvscaleoff[0] = gstate_c.uv.uScale / gstate_c.curTextureWidth;
uvscaleoff[1] = gstate_c.uv.vScale / gstate_c.curTextureHeight;
uvscaleoff[2] = gstate_c.uv.uOff / gstate_c.curTextureWidth;
uvscaleoff[3] = gstate_c.uv.vOff / gstate_c.curTextureHeight;
uvscaleoff[0] = gstate_c.uv.uScale / (float)gstate_c.curTextureWidth;
uvscaleoff[1] = gstate_c.uv.vScale / (float)gstate_c.curTextureHeight;
uvscaleoff[2] = gstate_c.uv.uOff / (float)gstate_c.curTextureWidth;
uvscaleoff[3] = gstate_c.uv.vOff / (float)gstate_c.curTextureHeight;
glUniform4fv(u_uvscaleoffset, 1, uvscaleoff);
} else {
int w = gstate.getTextureWidth(0);
@ -372,7 +398,6 @@ void LinkedShader::updateUniforms(u32 vertType) {
// TODO: Could even set all bones in one go if they're all dirty.
#ifdef USE_BONE_ARRAY
if (u_bone != -1) {
float allBones[8 * 16];
@ -493,7 +518,7 @@ void ShaderManager::DirtyShader() {
shaderSwitchDirty_ = 0;
}
void ShaderManager::EndFrame() { // disables vertex arrays
void ShaderManager::DirtyLastShader() { // disables vertex arrays
if (lastShader_)
lastShader_->stop();
lastShader_ = 0;
@ -524,11 +549,6 @@ LinkedShader *ShaderManager::ApplyShader(int prim, u32 vertType) {
return lastShader_; // Already all set.
}
if (lastShader_ != 0) {
// There was a previous shader and we're switching.
lastShader_->stop();
}
lastVSID_ = VSID;
lastFSID_ = FSID;
@ -583,11 +603,11 @@ LinkedShader *ShaderManager::ApplyShader(int prim, u32 vertType) {
shaderSwitchDirty_ = 0;
if (ls == NULL) {
ls = new LinkedShader(vs, fs, vertType, vs->UseHWTransform()); // This does "use" automatically
ls = new LinkedShader(vs, fs, vertType, vs->UseHWTransform(), lastShader_); // This does "use" automatically
const LinkedShaderCacheEntry entry(vs, fs, ls);
linkedShaderCache_.push_back(entry);
} else {
ls->use(vertType);
ls->use(vertType, lastShader_);
}
lastShader_ = ls;

View File

@ -25,13 +25,25 @@
class Shader;
class LinkedShader
{
// Pre-fetched attrs and uniforms
enum {
ATTR_POSITION = 0,
ATTR_TEXCOORD = 1,
ATTR_NORMAL = 2,
ATTR_W1 = 3,
ATTR_W2 = 4,
ATTR_COLOR0 = 5,
ATTR_COLOR1 = 6,
ATTR_COUNT,
};
class LinkedShader {
public:
LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTransform);
LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTransform, LinkedShader *previous);
~LinkedShader();
void use(u32 vertType);
void use(u32 vertType, LinkedShader *previous);
void stop();
void updateUniforms(u32 vertType);
@ -41,14 +53,8 @@ public:
uint32_t program;
u32 dirtyUniforms;
// Pre-fetched attrs and uniforms
int a_position;
int a_color0;
int a_color1;
int a_texcoord;
int a_normal;
int a_weight0123;
int a_weight4567;
// Present attributes in the shader.
int attrMask; // 1 << ATTR_ ... or-ed together.
int u_tex;
int u_proj;
@ -63,7 +69,7 @@ public:
int u_bone[8];
#endif
int numBones;
// Fragment processing inputs
int u_alphacolorref;
int u_colormask;
@ -159,7 +165,7 @@ public:
void DirtyUniform(u32 what) {
globalDirty_ |= what;
}
void EndFrame(); // disables vertex arrays
void DirtyLastShader(); // disables vertex arrays
int NumVertexShaders() const { return (int)vsCache_.size(); }
int NumFragmentShaders() const { return (int)fsCache_.size(); }

View File

@ -172,7 +172,7 @@ struct BezierPatch {
lerpColor(points[bl]->color, points[br]->color, fracU, lowerColor);
lerpColor(upperColor, lowerColor, fracV, color);
}
void sampleTexUV(float u, float v, float &tu, float &tv) const {
u *= 3.0f;
v *= 3.0f;
@ -324,7 +324,7 @@ void spline_knot(int n, int type, float *knot) {
if ((type & 1) == 0) {
knot[0] = -3;
knot[1] = -2;
knot[2] = -1;
knot[2] = -1;
}
if ((type & 2) == 0) {
knot[n + 2] = n - 1;
@ -452,7 +452,7 @@ void TesselateSplinePatch(u8 *&dest, int &count, const SplinePatch &spatch, u32
// Collect influences from surrounding control points.
float u_weights[4];
float v_weights[4];
int iu = (int)u;
int iv = (int)v;
spline_n_4(iu, u, knot_u, u_weights);
@ -463,7 +463,7 @@ void TesselateSplinePatch(u8 *&dest, int &count, const SplinePatch &spatch, u32
float u_spline = u_weights[ii];
float v_spline = v_weights[jj];
float f = u_spline * v_spline;
if (f > 0.0f) {
SimpleVertex *a = spatch.points[spatch.count_u * (iv + jj) + (iu + ii)];
vert->pos += a->pos * f;
@ -532,7 +532,7 @@ void TesselateSplinePatch(u8 *&dest, int &count, const SplinePatch &spatch, u32
}
}
void TesselateBezierPatch(u8 *&dest, int &count, const BezierPatch &patch, u32 origVertType) {
void TesselateBezierPatch(u8 *&dest, int &count, int tess_u, int tess_v, const BezierPatch &patch, u32 origVertType) {
const float third = 1.0f / 3.0f;
if (g_Config.bLowQualitySplineBezier) {
@ -586,9 +586,6 @@ void TesselateBezierPatch(u8 *&dest, int &count, const BezierPatch &patch, u32 o
// Full correct tesselation of bezier patches.
// Note: Does not handle splines correctly.
int tess_u = gstate.getPatchDivisionU();
int tess_v = gstate.getPatchDivisionV();
// First compute all the vertices and put them in an array
SimpleVertex *vertices = new SimpleVertex[(tess_u + 1) * (tess_v + 1)];
@ -798,9 +795,17 @@ void TransformDrawEngine::SubmitBezier(void* control_points, void* indices, int
int count = 0;
u8 *dest = decoded2;
// Simple approximation of the real tesselation factor.
// We shouldn't really split up into separate 4x4 patches, instead we should do something that works
// like the splines, so we subdivide across the whole "mega-patch".
int tess_u = gstate.getPatchDivisionU() / num_patches_u;
int tess_v = gstate.getPatchDivisionV() / num_patches_v;
if (tess_u < 4) tess_u = 4;
if (tess_v < 4) tess_v = 4;
for (int patch_idx = 0; patch_idx < num_patches_u*num_patches_v; ++patch_idx) {
BezierPatch& patch = patches[patch_idx];
TesselateBezierPatch(dest, count, patch, origVertType);
TesselateBezierPatch(dest, count, tess_u, tess_v, patch, origVertType);
}
delete[] patches;

View File

@ -286,7 +286,8 @@ void TransformDrawEngine::ApplyDrawState(int prim) {
// TODO: In clear mode, the stencil value is set to the alpha value of the vertex.
// A normal clear will be 2 points, the second point has the color.
// We should set "ref" to that value instead of 0.
glstate.stencilFunc.set(GL_ALWAYS, 0, 0xFF);
// In case of clear rectangles, we set it again once we know what the color is.
glstate.stencilFunc.set(GL_ALWAYS, 255, 0xFF);
} else
glstate.stencilTest.disable();

View File

@ -508,11 +508,11 @@ void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MagFiltGL[magFilt]);
entry.magFilt = magFilt;
}
//Workaround to fix a clamping bug in pre-HD ATI/AMD drivers
// Workaround for a clamping bug in pre-HD ATI/AMD drivers
if (gl_extensions.ATIClampBug && entry.framebuffer)
return;
if (force || entry.sClamp != sClamp) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
entry.sClamp = sClamp;
@ -824,7 +824,7 @@ void TextureCache::SetTexture(bool force) {
} else {
cluthash = 0;
}
int bufw = GetTextureBufw(0, texaddr, format);
int w = gstate.getTextureWidth(0);
int h = gstate.getTextureHeight(0);
@ -839,7 +839,7 @@ void TextureCache::SetTexture(bool force) {
gstate_c.skipDrawReason &= ~SKIPDRAW_BAD_FB_TEXTURE;
bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
bool replaceImages = false;
if (iter != cache.end()) {
entry = &iter->second;
// Validate the texture still matches the cache entry.
@ -984,7 +984,7 @@ void TextureCache::SetTexture(bool force) {
entry->framebuffer = 0;
entry->maxLevel = maxLevel;
entry->lodBias = 0.0f;
entry->dim = gstate.getTextureDimension(0);
entry->bufw = bufw;
@ -1030,7 +1030,7 @@ void TextureCache::SetTexture(bool force) {
}
glBindTexture(GL_TEXTURE_2D, entry->texture);
lastBoundTexture = entry->texture;
// Adjust maxLevel to actually present levels..
for (int i = 0; i <= maxLevel; i++) {
// If encountering levels pointing to nothing, adjust max level.
@ -1041,19 +1041,48 @@ void TextureCache::SetTexture(bool force) {
}
}
if (g_Config.bMipMap) {
// In addition, simply don't load more than level 0 if g_Config.bMipMap is false.
if (!g_Config.bMipMap) {
maxLevel = 0;
}
// GLES2 doesn't have support for a "Max lod" which is critical as PSP games often
// don't specify mips all the way down. As a result, we either need to manually generate
// the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not
// be as good quality as the game's own (might even be better in some cases though).
// If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient.
GLenum dstFmt = GetDestFormat(format, gstate.getClutPaletteFormat());
// For now, I choose to use autogen mips on GLES2 and the game's own on other platforms.
// As is usual, GLES3 will solve this problem nicely but wide distribution of that is
// years away.
LoadTextureLevel(*entry, 0, replaceImages);
if (maxLevel > 0)
glGenerateMipmap(GL_TEXTURE_2D);
#if 0 // Needs more testing
#ifdef MAY_HAVE_GLES3
if (gl_extensions.GLES3) {
// glTexStorage2D requires the use of sized formats.
GLenum storageFmt = GL_RGBA8;
switch (dstFmt) {
case GL_UNSIGNED_BYTE: storageFmt = GL_RGBA8; break;
case GL_UNSIGNED_SHORT_5_6_5: storageFmt = GL_RGB565; break;
case GL_UNSIGNED_SHORT_4_4_4_4: storageFmt = GL_RGBA4; break;
case GL_UNSIGNED_SHORT_5_5_5_1: storageFmt = GL_RGB5_A1; break;
default:
ERROR_LOG(G3D, "Unknown dstfmt %i", (int)dstFmt);
break;
}
glTexStorage2D(GL_TEXTURE_2D, maxLevel + 1, storageFmt, w, h);
// Make sure we don't use glTexImage2D after glTexStorage2D.
replaceImages = true;
}
#endif
#endif
// GLES2 doesn't have support for a "Max lod" which is critical as PSP games often
// don't specify mips all the way down. As a result, we either need to manually generate
// the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not
// be as good quality as the game's own (might even be better in some cases though).
// For now, I choose to use autogen mips on GLES2 and the game's own on other platforms.
// As is usual, GLES3 will solve this problem nicely but wide distribution of that is
// years away.
//
// Actually, seems we reverted to autogen mipmaps on all platforms.
LoadTextureLevel(*entry, 0, replaceImages, dstFmt);
if (maxLevel > 0) {
glGenerateMipmap(GL_TEXTURE_2D);
/*
for (int i = 0; i <= maxLevel; i++) {
LoadTextureLevel(*entry, i, replaceImages);
@ -1062,8 +1091,8 @@ void TextureCache::SetTexture(bool force) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (float)maxLevel);
*/
} else {
LoadTextureLevel(*entry, 0, replaceImages);
#ifndef USING_GLES2
// TODO: This is supported on GLES3
#if !defined(USING_GLES2)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
#endif
}
@ -1082,7 +1111,29 @@ void TextureCache::SetTexture(bool force) {
gstate_c.textureFullAlpha = (entry->status & TexCacheEntry::STATUS_ALPHA_MASK) == TexCacheEntry::STATUS_ALPHA_FULL;
}
void *TextureCache::DecodeTextureLevel(GETextureFormat format, GEPaletteFormat clutformat, int level, u32 &texByteAlign, GLenum &dstFmt) {
GLenum TextureCache::GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const {
switch (format) {
case GE_TFMT_CLUT4:
case GE_TFMT_CLUT8:
case GE_TFMT_CLUT16:
case GE_TFMT_CLUT32:
return getClutDestFormat(clutFormat);
case GE_TFMT_4444:
return GL_UNSIGNED_SHORT_4_4_4_4;
case GE_TFMT_5551:
return GL_UNSIGNED_SHORT_5_5_5_1;
case GE_TFMT_5650:
return GL_UNSIGNED_SHORT_5_6_5;
case GE_TFMT_8888:
case GE_TFMT_DXT1:
case GE_TFMT_DXT3:
case GE_TFMT_DXT5:
default:
return GL_UNSIGNED_BYTE;
}
}
void *TextureCache::DecodeTextureLevel(GETextureFormat format, GEPaletteFormat clutformat, int level, u32 &texByteAlign, GLenum dstFmt) {
void *finalBuf = NULL;
u32 texaddr = gstate.getTextureAddress(level);
@ -1095,8 +1146,6 @@ void *TextureCache::DecodeTextureLevel(GETextureFormat format, GEPaletteFormat c
switch (format) {
case GE_TFMT_CLUT4:
{
dstFmt = getClutDestFormat(clutformat);
const bool mipmapShareClut = (gstate.texmode & 0x100) == 0;
const int clutSharingOffset = mipmapShareClut ? 0 : level * 16;
@ -1154,19 +1203,16 @@ void *TextureCache::DecodeTextureLevel(GETextureFormat format, GEPaletteFormat c
break;
case GE_TFMT_CLUT8:
dstFmt = getClutDestFormat(gstate.getClutPaletteFormat());
texByteAlign = texByteAlignMap[gstate.getClutPaletteFormat()];
finalBuf = ReadIndexedTex(level, texaddr, 1, dstFmt, bufw);
break;
case GE_TFMT_CLUT16:
dstFmt = getClutDestFormat(gstate.getClutPaletteFormat());
texByteAlign = texByteAlignMap[gstate.getClutPaletteFormat()];
finalBuf = ReadIndexedTex(level, texaddr, 2, dstFmt, bufw);
break;
case GE_TFMT_CLUT32:
dstFmt = getClutDestFormat(gstate.getClutPaletteFormat());
texByteAlign = texByteAlignMap[gstate.getClutPaletteFormat()];
finalBuf = ReadIndexedTex(level, texaddr, 4, dstFmt, bufw);
break;
@ -1174,12 +1220,6 @@ void *TextureCache::DecodeTextureLevel(GETextureFormat format, GEPaletteFormat c
case GE_TFMT_4444:
case GE_TFMT_5551:
case GE_TFMT_5650:
if (format == GE_TFMT_4444)
dstFmt = GL_UNSIGNED_SHORT_4_4_4_4;
else if (format == GE_TFMT_5551)
dstFmt = GL_UNSIGNED_SHORT_5_5_5_1;
else if (format == GE_TFMT_5650)
dstFmt = GL_UNSIGNED_SHORT_5_6_5;
texByteAlign = 2;
if (!gstate.isTextureSwizzled()) {
@ -1196,7 +1236,6 @@ void *TextureCache::DecodeTextureLevel(GETextureFormat format, GEPaletteFormat c
break;
case GE_TFMT_8888:
dstFmt = GL_UNSIGNED_BYTE;
if (!gstate.isTextureSwizzled()) {
// Special case: if we don't need to deal with packing, we don't need to copy.
if (w == bufw) {
@ -1217,7 +1256,6 @@ void *TextureCache::DecodeTextureLevel(GETextureFormat format, GEPaletteFormat c
break;
case GE_TFMT_DXT1:
dstFmt = GL_UNSIGNED_BYTE;
{
int minw = std::min(bufw, w);
tmpTexBuf32.resize(std::max(bufw, w) * h);
@ -1238,7 +1276,6 @@ void *TextureCache::DecodeTextureLevel(GETextureFormat format, GEPaletteFormat c
break;
case GE_TFMT_DXT3:
dstFmt = GL_UNSIGNED_BYTE;
{
int minw = std::min(bufw, w);
tmpTexBuf32.resize(std::max(bufw, w) * h);
@ -1259,7 +1296,6 @@ void *TextureCache::DecodeTextureLevel(GETextureFormat format, GEPaletteFormat c
break;
case GE_TFMT_DXT5:
dstFmt = GL_UNSIGNED_BYTE;
{
int minw = std::min(bufw, w);
tmpTexBuf32.resize(std::max(bufw, w) * h);
@ -1377,12 +1413,11 @@ void TextureCache::CheckAlpha(TexCacheEntry &entry, u32 *pixelData, GLenum dstFm
entry.status |= TexCacheEntry::STATUS_ALPHA_FULL;
}
void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level, bool replaceImages) {
void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level, bool replaceImages, GLenum dstFmt) {
// TODO: only do this once
u32 texByteAlign = 1;
// TODO: Look into using BGRA for 32-bit textures when the GL_EXT_texture_format_BGRA8888 extension is available, as it's faster than RGBA on some chips.
GLenum dstFmt = 0;
GEPaletteFormat clutformat = gstate.getClutPaletteFormat();
void *finalBuf = DecodeTextureLevel(GETextureFormat(entry.format), clutformat, level, texByteAlign, dstFmt);
@ -1409,7 +1444,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level, bool replac
scaleFactor = 1;
u32 *pixelData = (u32 *)finalBuf;
if (scaleFactor > 1 && entry.numInvalidated == 0)
if (scaleFactor > 1 && entry.numInvalidated == 0)
scaler.Scale(pixelData, dstFmt, w, h, scaleFactor);
// Or always?
if (entry.numInvalidated == 0)
@ -1434,8 +1469,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level, bool replac
}
// Only used by Qt UI?
bool TextureCache::DecodeTexture(u8* output, GPUgstate state)
{
bool TextureCache::DecodeTexture(u8* output, GPUgstate state) {
GPUgstate oldState = gstate;
gstate = state;
@ -1461,12 +1495,10 @@ bool TextureCache::DecodeTexture(u8* output, GPUgstate state)
return false;
}
switch (dstFmt)
{
switch (dstFmt) {
case GL_UNSIGNED_SHORT_4_4_4_4:
for(int y = 0; y < h; y++)
for(int x = 0; x < bufw; x++)
{
for (int y = 0; y < h; y++)
for (int x = 0; x < bufw; x++) {
u32 val = ((u16*)finalBuf)[y*bufw + x];
u32 r = ((val>>12) & 0xF) * 17;
u32 g = ((val>> 8) & 0xF) * 17;
@ -1477,9 +1509,8 @@ bool TextureCache::DecodeTexture(u8* output, GPUgstate state)
break;
case GL_UNSIGNED_SHORT_5_5_5_1:
for(int y = 0; y < h; y++)
for(int x = 0; x < bufw; x++)
{
for (int y = 0; y < h; y++)
for (int x = 0; x < bufw; x++) {
u32 val = ((u16*)finalBuf)[y*bufw + x];
u32 r = Convert5To8((val>>11) & 0x1F);
u32 g = Convert5To8((val>> 6) & 0x1F);
@ -1490,9 +1521,8 @@ bool TextureCache::DecodeTexture(u8* output, GPUgstate state)
break;
case GL_UNSIGNED_SHORT_5_6_5:
for(int y = 0; y < h; y++)
for(int x = 0; x < bufw; x++)
{
for (int y = 0; y < h; y++)
for (int x = 0; x < bufw; x++) {
u32 val = ((u16*)finalBuf)[y*bufw + x];
u32 a = 0xFF;
u32 r = Convert5To8((val>>11) & 0x1F);
@ -1503,9 +1533,8 @@ bool TextureCache::DecodeTexture(u8* output, GPUgstate state)
break;
default:
for(int y = 0; y < h; y++)
for(int x = 0; x < bufw; x++)
{
for (int y = 0; y < h; y++)
for (int x = 0; x < bufw; x++) {
u32 val = ((u32*)finalBuf)[y*bufw + x];
((u32*)output)[y*w + x] = ((val & 0xFF000000)) | ((val & 0x00FF0000)>>16) | ((val & 0x0000FF00)) | ((val & 0x000000FF)<<16);
}

View File

@ -28,7 +28,7 @@ struct VirtualFramebuffer;
enum TextureFiltering {
AUTO = 1,
NEAREST = 2,
LINEAR = 3,
LINEAR = 3,
LINEARFMV = 4,
};
@ -38,8 +38,7 @@ enum FramebufferNotification {
NOTIFY_FB_DESTROYED,
};
class TextureCache
{
class TextureCache {
public:
TextureCache();
~TextureCache();
@ -117,8 +116,9 @@ private:
void *UnswizzleFromMem(u32 texaddr, u32 bufw, u32 bytesPerPixel, u32 level);
void *ReadIndexedTex(int level, u32 texaddr, int bytesPerIndex, GLuint dstFmt, int bufw);
void UpdateSamplingParams(TexCacheEntry &entry, bool force);
void LoadTextureLevel(TexCacheEntry &entry, int level, bool replaceImages);
void *DecodeTextureLevel(GETextureFormat format, GEPaletteFormat clutformat, int level, u32 &texByteAlign, GLenum &dstFmt);
void LoadTextureLevel(TexCacheEntry &entry, int level, bool replaceImages, GLenum dstFmt);
GLenum GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const;
void *DecodeTextureLevel(GETextureFormat format, GEPaletteFormat clutformat, int level, u32 &texByteAlign, GLenum dstFmt);
void CheckAlpha(TexCacheEntry &entry, u32 *pixelData, GLenum dstFmt, int w, int h);
template <typename T>
const T *GetCurrentClut();

View File

@ -15,6 +15,55 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// Ideas for speeding things up on mobile OpenGL ES implementations
//
// Use superbuffers! Yes I just invented that name.
//
// The idea is to avoid respecifying the vertex format between every draw call (multiple glVertexAttribPointer ...)
// by combining the contents of multiple draw calls into one buffer, as long as
// they have exactly the same output vertex format. (different input formats is fine! This way
// we can combine the data for multiple draws with different numbers of bones, as we consider numbones < 4 to be = 4)
// into one VBO.
//
// This will likely be a win because I believe that between every change of VBO + glVertexAttribPointer*N, the driver will
// perform a lot of validation, probably at draw call time, while all the validation can be skipped if the only thing
// that changes between two draw calls is simple state or texture or a matrix etc, not anything vertex related.
// Also the driver will have to manage hundreds instead of thousands of VBOs in games like GTA.
//
// * Every 10 frames or something, do the following:
// - Frame 1:
// + Mark all drawn buffers with in-frame sequence numbers (alternatively,
// just log them in an array)
// - Frame 2 (beginning?):
// + Take adjacent buffers that have the same output vertex format, and add them
// to a list of buffers to combine. Create said buffers with appropriate sizes
// and precompute the offsets that the draws should be written into.
// - Frame 2 (end):
// + Actually do the work of combining the buffers. This probably means re-decoding
// the vertices into a new one. Will also have to apply index offsets.
//
// Also need to change the drawing code so that we don't glBindBuffer and respecify glVAP if
// two subsequent drawcalls come from the same superbuffer.
//
// Or we ignore all of this including vertex caching and simply find a way to do highly optimized vertex streaming,
// like Dolphin is trying to. That will likely never be able to reach the same speed as perfectly optimized
// superbuffers though. For this we will have to JIT the vertex decoder but that's not too hard.
//
// Now, when do we delete superbuffers? Maybe when half the buffers within have been killed?
//
// Another idea for GTA which switches textures a lot while not changing much other state is to use ES 3 Array
// textures, if they are the same size (even if they aren't, might be okay to simply resize the textures to match
// if they're just a multiple of 2 away) or something. Then we'd have to add a W texture coordinate to choose the
// texture within the bound texture array to the vertex data when merging into superbuffers.
//
// There are even more things to try. For games that do matrix palette skinning by quickly switching bones and
// just drawing a few triangles per call (NBA, FF:CC, Tekken 6 etc) we could even collect matrices, upload them
// all at once, writing matrix indices into the vertices in addition to the weights, and then doing a single
// draw call with specially generated shader to draw the whole mesh. This code will be seriously complex though.
#include "base/logging.h"
#include "base/timeutil.h"
#include "Common/MemoryUtil.h"
@ -60,8 +109,9 @@ enum {
#define VERTEXCACHE_DECIMATION_INTERVAL 17
inline float clamp(float in, float min, float max) {
return in < min ? min : (in > max ? max : in);
// Check for max first as clamping to max is more common than min when lighting.
inline float clamp(float in, float min, float max) {
return in > max ? max : (in < min ? min : in);
}
TransformDrawEngine::TransformDrawEngine()
@ -137,6 +187,7 @@ void TransformDrawEngine::DestroyDeviceObjects() {
}
void TransformDrawEngine::GLLost() {
ILOG("TransformDrawEngine::GLLost()");
// The objects have already been deleted.
memset(vbo_, 0, sizeof(vbo_));
memset(ebo_, 0, sizeof(ebo_));
@ -329,13 +380,13 @@ static inline void VertexAttribSetup(int attrib, int fmt, int stride, u8 *ptr) {
// TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets
static void SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt, u8 *vertexData) {
VertexAttribSetup(program->a_weight0123, decFmt.w0fmt, decFmt.stride, vertexData + decFmt.w0off);
VertexAttribSetup(program->a_weight4567, decFmt.w1fmt, decFmt.stride, vertexData + decFmt.w1off);
VertexAttribSetup(program->a_texcoord, decFmt.uvfmt, decFmt.stride, vertexData + decFmt.uvoff);
VertexAttribSetup(program->a_color0, decFmt.c0fmt, decFmt.stride, vertexData + decFmt.c0off);
VertexAttribSetup(program->a_color1, decFmt.c1fmt, decFmt.stride, vertexData + decFmt.c1off);
VertexAttribSetup(program->a_normal, decFmt.nrmfmt, decFmt.stride, vertexData + decFmt.nrmoff);
VertexAttribSetup(program->a_position, decFmt.posfmt, decFmt.stride, vertexData + decFmt.posoff);
VertexAttribSetup(ATTR_W1, decFmt.w0fmt, decFmt.stride, vertexData + decFmt.w0off);
VertexAttribSetup(ATTR_W2, decFmt.w1fmt, decFmt.stride, vertexData + decFmt.w1off);
VertexAttribSetup(ATTR_TEXCOORD, decFmt.uvfmt, decFmt.stride, vertexData + decFmt.uvoff);
VertexAttribSetup(ATTR_COLOR0, decFmt.c0fmt, decFmt.stride, vertexData + decFmt.c0off);
VertexAttribSetup(ATTR_COLOR1, decFmt.c1fmt, decFmt.stride, vertexData + decFmt.c1off);
VertexAttribSetup(ATTR_NORMAL, decFmt.nrmfmt, decFmt.stride, vertexData + decFmt.nrmoff);
VertexAttribSetup(ATTR_POSITION, decFmt.posfmt, decFmt.stride, vertexData + decFmt.posoff);
}
// The verts are in the order: BR BL TL TR
@ -375,7 +426,6 @@ static void RotateUVThrough(TransformedVertex v[4]) {
SwapUVs(v[1], v[3]);
}
// Clears on the PSP are best done by drawing a series of vertical strips
// in clear mode. This tries to detect that.
bool TransformDrawEngine::IsReallyAClear(int numVerts) const {
@ -395,7 +445,7 @@ bool TransformDrawEngine::IsReallyAClear(int numVerts) const {
memcpy(&vcolor, transformed[i].color0, 4);
if (vcolor != matchcolor || transformed[i].z != matchz)
return false;
if ((i & 1) == 0) {
// Top left of a rectangle
if (transformed[i].y != 0)
@ -598,7 +648,7 @@ void TransformDrawEngine::SoftwareTransformAndDraw(
uv[1] = vscale * (ruv[1]*gstate_c.uv.vScale + gstate_c.uv.vOff);
uv[2] = 1.0f;
break;
case GE_TEXMAP_TEXTURE_MATRIX:
{
// Projection mapping
@ -607,11 +657,11 @@ void TransformDrawEngine::SoftwareTransformAndDraw(
case GE_PROJMAP_POSITION: // Use model space XYZ as source
source = pos;
break;
case GE_PROJMAP_UV: // Use unscaled UV as source
source = Vec3f(ruv[0], ruv[1], 0.0f);
break;
case GE_PROJMAP_NORMALIZED_NORMAL: // Use normalized normal as source
if (reader.hasNormal()) {
source = Vec3f(norm).Normalized();
@ -620,7 +670,7 @@ void TransformDrawEngine::SoftwareTransformAndDraw(
source = Vec3f(0.0f, 0.0f, 1.0f);
}
break;
case GE_PROJMAP_NORMAL: // Use non-normalized normal as source!
if (reader.hasNormal()) {
source = Vec3f(norm);
@ -638,7 +688,7 @@ void TransformDrawEngine::SoftwareTransformAndDraw(
uv[2] = uvw[2];
}
break;
case GE_TEXMAP_ENVIRONMENT_MAP:
// Shade mapping - use two light sources to generate U and V.
{
@ -650,15 +700,16 @@ void TransformDrawEngine::SoftwareTransformAndDraw(
uv[2] = 1.0f;
}
break;
default:
// Illegal
ERROR_LOG_REPORT(G3D, "Impossible UV gen mode? %d", gstate.getUVGenMode());
break;
}
uv[0] = uv[0] * widthFactor;
uv[1] = uv[1] * heightFactor;
// Transform the coord by the view matrix.
Vec3ByMatrix43(v, out, gstate.viewMatrix);
fogCoef = (v[2] + fog_end) * fog_slope;
@ -731,11 +782,14 @@ void TransformDrawEngine::SoftwareTransformAndDraw(
drawBuffer = transformedExpanded;
TransformedVertex *trans = &transformedExpanded[0];
TransformedVertex saved;
u32 stencilValue;
for (int i = 0; i < vertexCount; i += 2) {
int index = ((const u16*)inds)[i];
saved = transformed[index];
int index2 = ((const u16*)inds)[i + 1];
TransformedVertex &transVtx = transformed[index2];
if (i == 0)
stencilValue = transVtx.color0[3];
// We have to turn the rectangle into two triangles, so 6 points. Sigh.
// bottom right
@ -765,6 +819,7 @@ void TransformDrawEngine::SoftwareTransformAndDraw(
// Apparently, non-through RotateUV just breaks things.
// If we find a game where it helps, we'll just have to figure out how they differ.
// Possibly, it has something to do with flipped viewport Y axis, which a few games use.
// One game might be one of the Metal Gear ones, can't find the issue right now though.
// else
// RotateUV(trans);
@ -777,27 +832,33 @@ void TransformDrawEngine::SoftwareTransformAndDraw(
numTrans += 6;
}
}
// We don't know the color until here, so we have to do it now, instead of in StateMapping.
// Might want to reconsider the order of things later...
if (gstate.isModeClear() && gstate.isClearModeAlphaMask()) {
glstate.stencilFunc.set(GL_ALWAYS, stencilValue, 255);
}
}
// TODO: Add a post-transform cache here for multi-RECTANGLES only.
// Might help for text drawing.
// these spam the gDebugger log.
const int vertexSize = sizeof(transformed[0]);
bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(program->a_position, 4, GL_FLOAT, GL_FALSE, vertexSize, drawBuffer);
if (program->a_texcoord != -1) glVertexAttribPointer(program->a_texcoord, doTextureProjection ? 3 : 2, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 4 * 4);
if (program->a_color0 != -1) glVertexAttribPointer(program->a_color0, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, ((uint8_t*)drawBuffer) + 7 * 4);
if (program->a_color1 != -1) glVertexAttribPointer(program->a_color1, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, ((uint8_t*)drawBuffer) + 8 * 4);
glVertexAttribPointer(ATTR_POSITION, 4, GL_FLOAT, GL_FALSE, vertexSize, drawBuffer);
int attrMask = program->attrMask;
if (attrMask & (1 << ATTR_TEXCOORD)) glVertexAttribPointer(ATTR_TEXCOORD, doTextureProjection ? 3 : 2, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 4 * 4);
if (attrMask & (1 << ATTR_COLOR0)) glVertexAttribPointer(ATTR_COLOR0, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, ((uint8_t*)drawBuffer) + 7 * 4);
if (attrMask & (1 << ATTR_COLOR1)) glVertexAttribPointer(ATTR_COLOR1, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, ((uint8_t*)drawBuffer) + 8 * 4);
if (drawIndexed) {
//#ifdef USING_GLES2
#ifdef USING_GLES2
glDrawElements(glprim[prim], numTrans, GL_UNSIGNED_SHORT, inds);
//#else
// glDrawRangeElements(glprim[prim], 0, indexGen.MaxIndex(), numTrans, GL_UNSIGNED_SHORT, inds);
//#endif
#else
glDrawRangeElements(glprim[prim], 0, indexGen.MaxIndex(), numTrans, GL_UNSIGNED_SHORT, inds);
#endif
} else {
glDrawArrays(glprim[prim], 0, numTrans);
}
@ -807,7 +868,7 @@ VertexDecoder *TransformDrawEngine::GetVertexDecoder(u32 vtype) {
auto iter = decoderMap_.find(vtype);
if (iter != decoderMap_.end())
return iter->second;
VertexDecoder *dec = new VertexDecoder();
VertexDecoder *dec = new VertexDecoder();
dec->SetVertexType(vtype);
decoderMap_[vtype] = dec;
return dec;
@ -856,13 +917,13 @@ void TransformDrawEngine::SubmitPrim(void *verts, void *inds, GEPrimitiveType pr
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS)
Flush();
// TODO: Is this the right thing to do?
if (prim == GE_PRIM_KEEP_PREVIOUS) {
prim = prevPrim_;
}
prevPrim_ = prim;
SetupVertexDecoder(vertType);
dec_->IncrementStat(STAT_VERTSSUBMITTED, vertexCount);
@ -894,6 +955,9 @@ void TransformDrawEngine::SubmitPrim(void *verts, void *inds, GEPrimitiveType pr
}
void TransformDrawEngine::DecodeVerts() {
UVScale origUV;
if (uvScale)
origUV = gstate_c.uv;
for (int i = 0; i < numDrawCalls; i++) {
const DeferredDrawCall &dc = drawCalls[i];
@ -923,7 +987,7 @@ void TransformDrawEngine::DecodeVerts() {
while (j < numDrawCalls) {
if (drawCalls[j].verts != dc.verts)
break;
if (uvScale && memcmp(&uvScale[j], &uvScale[i], sizeof(uvScale[0]) != 0))
if (uvScale && memcmp(&uvScale[j], &uvScale[i], sizeof(uvScale[0])) != 0)
break;
indexLowerBound = std::min(indexLowerBound, (int)drawCalls[j].indexLowerBound);
@ -964,6 +1028,8 @@ void TransformDrawEngine::DecodeVerts() {
// Force to points (0)
indexGen.AddPrim(GE_PRIM_POINTS, 0);
}
if (uvScale)
gstate_c.uv = origUV;
}
u32 TransformDrawEngine::ComputeHash() {
@ -984,6 +1050,11 @@ u32 TransformDrawEngine::ComputeHash() {
fullhash += XXH32((const char *)drawCalls[i].inds, indexSize * drawCalls[i].vertexCount, 0x955FD1CA);
}
}
if (uvScale) {
for (int i = 0; i < numDrawCalls; i++) {
fullhash += XXH32(&uvScale[i], sizeof(uvScale[0]), 0x0123e658);
}
}
return fullhash;
}
@ -1065,6 +1136,7 @@ void TransformDrawEngine::DoFlush() {
if (program->useHWTransform_) {
GLuint vbo = 0, ebo = 0;
int vertexCount = 0;
int maxIndex = 0;
bool useElements = true;
// Cannot cache vertex data with morph enabled.
if (g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK)) {
@ -1090,6 +1162,7 @@ void TransformDrawEngine::DoFlush() {
DecodeVerts(); // writes to indexGen
vai->numVerts = indexGen.VertexCount();
vai->prim = indexGen.Prim();
vai->maxIndex = indexGen.MaxIndex();
goto rotateVBO;
}
@ -1136,10 +1209,12 @@ void TransformDrawEngine::DoFlush() {
DecodeVerts();
vai->numVerts = indexGen.VertexCount();
vai->prim = indexGen.Prim();
vai->maxIndex = indexGen.MaxIndex();
useElements = !indexGen.SeenOnlyPurePrims();
if (!useElements && indexGen.PureCount()) {
vai->numVerts = indexGen.PureCount();
}
glGenBuffers(1, &vai->vbo);
glBindBuffer(GL_ARRAY_BUFFER, vai->vbo);
glBufferData(GL_ARRAY_BUFFER, dec_->GetDecVtxFmt().stride * indexGen.MaxIndex(), decoded, GL_STATIC_DRAW);
@ -1165,6 +1240,7 @@ void TransformDrawEngine::DoFlush() {
vbo = vai->vbo;
ebo = vai->ebo;
vertexCount = vai->numVerts;
maxIndex = vai->maxIndex;
prim = static_cast<GEPrimitiveType>(vai->prim);
break;
}
@ -1184,6 +1260,7 @@ void TransformDrawEngine::DoFlush() {
if (ebo)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
vertexCount = vai->numVerts;
maxIndex = vai->maxIndex;
prim = static_cast<GEPrimitiveType>(vai->prim);
break;
}
@ -1206,6 +1283,7 @@ rotateVBO:
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
useElements = !indexGen.SeenOnlyPurePrims();
vertexCount = indexGen.VertexCount();
maxIndex = indexGen.MaxIndex();
if (!useElements && indexGen.PureCount()) {
vertexCount = indexGen.PureCount();
}
@ -1219,11 +1297,11 @@ rotateVBO:
SetupDecFmtForDraw(program, dec_->GetDecVtxFmt(), vbo ? 0 : decoded);
if (useElements) {
//#ifdef USING_GLES2
#ifdef USING_GLES2
glDrawElements(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, ebo ? 0 : (GLvoid*)decIndex);
//#else
// glDrawRangeElements(glprim[prim], 0, indexGen.MaxIndex(), vertexCount, GL_UNSIGNED_SHORT, ebo ? 0 : (GLvoid*)decIndex);
//#endif
#else
glDrawRangeElements(glprim[prim], 0, maxIndex, vertexCount, GL_UNSIGNED_SHORT, ebo ? 0 : (GLvoid*)decIndex);
#endif
if (ebo)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
} else {

View File

@ -42,12 +42,6 @@ struct DecVtxFormat;
// DRAWN_ONCE -> death
// DRAWN_RELIABLE -> death
// Don't bother storing information about draws smaller than this.
enum {
VERTEX_CACHE_THRESHOLD = 20,
};
// Try to keep this POD.
class VertexArrayInfo {
public:
@ -64,6 +58,7 @@ public:
drawsUntilNextFullHash = 0;
}
~VertexArrayInfo();
enum Status {
VAI_NEW,
VAI_HASHING,
@ -78,8 +73,9 @@ public:
u32 vbo;
u32 ebo;
// Precalculated parameter for drawdrawElements
// Precalculated parameter for drawRangeElements
u16 numVerts;
u16 maxIndex;
s8 prim;
// ID information

View File

@ -113,14 +113,14 @@ void ComputeVertexShaderID(VertexShaderID *id, u32 vertType, int prim, bool useH
static const char * const boneWeightAttrDecl[9] = {
"#ERROR#",
"attribute mediump float a_w1;\n",
"attribute mediump vec2 a_w1;\n",
"attribute mediump vec3 a_w1;\n",
"attribute mediump vec4 a_w1;\n",
"attribute mediump vec4 a_w1;\nattribute mediump float a_w2;\n",
"attribute mediump vec4 a_w1;\nattribute mediump vec2 a_w2;\n",
"attribute mediump vec4 a_w1;\nattribute mediump vec3 a_w2;\n",
"attribute mediump vec4 a_w1, a_w2;\n",
"attribute mediump float w1;\n",
"attribute mediump vec2 w1;\n",
"attribute mediump vec3 w1;\n",
"attribute mediump vec4 w1;\n",
"attribute mediump vec4 w1;\nattribute mediump float w2;\n",
"attribute mediump vec4 w1;\nattribute mediump vec2 w2;\n",
"attribute mediump vec4 w1;\nattribute mediump vec3 w2;\n",
"attribute mediump vec4 w1, w2;\n",
};
enum DoLightComputation {
@ -177,23 +177,23 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
}
if (useHWTransform)
WRITE(p, "attribute vec3 a_position;\n");
WRITE(p, "attribute vec3 position;\n");
else
WRITE(p, "attribute vec4 a_position;\n"); // need to pass the fog coord in w
WRITE(p, "attribute vec4 position;\n"); // need to pass the fog coord in w
if (useHWTransform && hasNormal)
WRITE(p, "attribute mediump vec3 a_normal;\n");
WRITE(p, "attribute mediump vec3 normal;\n");
if (doTexture) {
if (!useHWTransform && doTextureProjection)
WRITE(p, "attribute vec3 a_texcoord;\n");
WRITE(p, "attribute vec3 texcoord;\n");
else
WRITE(p, "attribute vec2 a_texcoord;\n");
WRITE(p, "attribute vec2 texcoord;\n");
}
if (hasColor) {
WRITE(p, "attribute lowp vec4 a_color0;\n");
WRITE(p, "attribute lowp vec4 color0;\n");
if (lmode && !useHWTransform) // only software transform supplies color1 as vertex data
WRITE(p, "attribute lowp vec3 a_color1;\n");
WRITE(p, "attribute lowp vec3 color1;\n");
}
if (gstate.isModeThrough()) {
@ -277,31 +277,31 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
if (!useHWTransform) {
// Simple pass-through of vertex data to fragment shader
if (doTexture)
WRITE(p, " v_texcoord = a_texcoord;\n");
WRITE(p, " v_texcoord = texcoord;\n");
if (hasColor) {
WRITE(p, " v_color0 = a_color0;\n");
WRITE(p, " v_color0 = color0;\n");
if (lmode)
WRITE(p, " v_color1 = a_color1;\n");
WRITE(p, " v_color1 = color1;\n");
} else {
WRITE(p, " v_color0 = u_matambientalpha;\n");
if (lmode)
WRITE(p, " v_color1 = vec3(0.0);\n");
}
if (enableFog) {
WRITE(p, " v_fogdepth = a_position.w;\n");
WRITE(p, " v_fogdepth = position.w;\n");
}
if (gstate.isModeThrough()) {
WRITE(p, " gl_Position = u_proj_through * vec4(a_position.xyz, 1.0);\n");
WRITE(p, " gl_Position = u_proj_through * vec4(position.xyz, 1.0);\n");
} else {
WRITE(p, " gl_Position = u_proj * vec4(a_position.xyz, 1.0);\n");
WRITE(p, " gl_Position = u_proj * vec4(position.xyz, 1.0);\n");
}
} else {
// Step 1: World Transform / Skinning
if (!vertTypeIsSkinningEnabled(vertType)) {
// No skinning, just standard T&L.
WRITE(p, " vec3 worldpos = (u_world * vec4(a_position.xyz, 1.0)).xyz;\n");
WRITE(p, " vec3 worldpos = (u_world * vec4(position.xyz, 1.0)).xyz;\n");
if (hasNormal)
WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(a_normal, 0.0)).xyz);\n");
WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(normal, 0.0)).xyz);\n");
else
WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n");
} else {
@ -311,8 +311,8 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
const char *factor = rescale[vertTypeGetWeightMask(vertType) >> GE_VTYPE_WEIGHT_SHIFT];
static const char * const boneWeightAttr[8] = {
"a_w1.x", "a_w1.y", "a_w1.z", "a_w1.w",
"a_w2.x", "a_w2.y", "a_w2.z", "a_w2.w",
"w1.x", "w1.y", "w1.z", "w1.w",
"w2.x", "w2.y", "w2.z", "w2.w",
};
#if defined(USE_FOR_LOOP) && defined(USE_BONE_ARRAY)
@ -320,14 +320,14 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
// To loop through the weights, we unfortunately need to put them in a float array.
// GLSL ES sucks - no way to directly initialize an array!
switch (numWeights) {
case 1: WRITE(p, " float w[1]; w[0] = a_w1;\n"); break;
case 2: WRITE(p, " float w[2]; w[0] = a_w1.x; w[1] = a_w1.y;\n"); break;
case 3: WRITE(p, " float w[3]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z;\n"); break;
case 4: WRITE(p, " float w[4]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w;\n"); break;
case 5: WRITE(p, " float w[5]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2;\n"); break;
case 6: WRITE(p, " float w[6]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2.x; w[5] = a_w2.y;\n"); break;
case 7: WRITE(p, " float w[7]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2.x; w[5] = a_w2.y; w[6] = a_w2.z;\n"); break;
case 8: WRITE(p, " float w[8]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2.x; w[5] = a_w2.y; w[6] = a_w2.z; w[7] = a_w2.w;\n"); break;
case 1: WRITE(p, " float w[1]; w[0] = w1;\n"); break;
case 2: WRITE(p, " float w[2]; w[0] = w1.x; w[1] = w1.y;\n"); break;
case 3: WRITE(p, " float w[3]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z;\n"); break;
case 4: WRITE(p, " float w[4]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w;\n"); break;
case 5: WRITE(p, " float w[5]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2;\n"); break;
case 6: WRITE(p, " float w[6]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2.x; w[5] = w2.y;\n"); break;
case 7: WRITE(p, " float w[7]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2.x; w[5] = w2.y; w[6] = w2.z;\n"); break;
case 8: WRITE(p, " float w[8]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2.x; w[5] = w2.y; w[6] = w2.z; w[7] = w2.w;\n"); break;
}
WRITE(p, " mat4 skinMatrix = w[0] * u_bone[0];\n");
@ -341,28 +341,28 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
#ifdef USE_BONE_ARRAY
if (numWeights == 1)
WRITE(p, " mat4 skinMatrix = a_w1 * u_bone[0]");
WRITE(p, " mat4 skinMatrix = w1 * u_bone[0]");
else
WRITE(p, " mat4 skinMatrix = a_w1.x * u_bone[0]");
WRITE(p, " mat4 skinMatrix = w1.x * u_bone[0]");
for (int i = 1; i < numWeights; i++) {
const char *weightAttr = boneWeightAttr[i];
// workaround for "cant do .x of scalar" issue
if (numWeights == 1 && i == 0) weightAttr = "a_w1";
if (numWeights == 5 && i == 4) weightAttr = "a_w2";
if (numWeights == 1 && i == 0) weightAttr = "w1";
if (numWeights == 5 && i == 4) weightAttr = "w2";
WRITE(p, " + %s * u_bone[%i]", weightAttr, i);
}
#else
// Uncomment this to screw up bone shaders to check the vertex shader software fallback
// WRITE(p, "THIS SHOULD ERROR! #error");
if (numWeights == 1)
WRITE(p, " mat4 skinMatrix = a_w1 * u_bone0");
WRITE(p, " mat4 skinMatrix = w1 * u_bone0");
else
WRITE(p, " mat4 skinMatrix = a_w1.x * u_bone0");
WRITE(p, " mat4 skinMatrix = w1.x * u_bone0");
for (int i = 1; i < numWeights; i++) {
const char *weightAttr = boneWeightAttr[i];
// workaround for "cant do .x of scalar" issue
if (numWeights == 1 && i == 0) weightAttr = "a_w1";
if (numWeights == 5 && i == 4) weightAttr = "a_w2";
if (numWeights == 1 && i == 0) weightAttr = "w1";
if (numWeights == 5 && i == 4) weightAttr = "w2";
WRITE(p, " + %s * u_bone%i", weightAttr, i);
}
#endif
@ -372,11 +372,11 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
WRITE(p, ";\n");
// Trying to simplify this results in bugs in LBP...
WRITE(p, " vec3 skinnedpos = (skinMatrix * vec4(a_position, 1.0)).xyz %s;\n", factor);
WRITE(p, " vec3 skinnedpos = (skinMatrix * vec4(position, 1.0)).xyz %s;\n", factor);
WRITE(p, " vec3 worldpos = (u_world * vec4(skinnedpos, 1.0)).xyz;\n");
if (hasNormal) {
WRITE(p, " mediump vec3 skinnednormal = (skinMatrix * vec4(a_normal, 0.0)).xyz %s;\n", factor);
WRITE(p, " mediump vec3 skinnednormal = (skinMatrix * vec4(normal, 0.0)).xyz %s;\n", factor);
WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(skinnednormal, 0.0)).xyz);\n");
} else {
WRITE(p, " mediump vec3 worldnormal = (u_world * (skinMatrix * vec4(0.0, 0.0, 1.0, 0.0))).xyz;\n");
@ -390,9 +390,9 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
// TODO: Declare variables for dots for shade mapping if needed.
const char *ambientStr = (gstate.materialupdate & 1) ? (hasColor ? "a_color0" : "u_matambientalpha") : "u_matambientalpha";
const char *diffuseStr = (gstate.materialupdate & 2) ? (hasColor ? "a_color0.rgb" : "u_matambientalpha.rgb") : "u_matdiffuse";
const char *specularStr = (gstate.materialupdate & 4) ? (hasColor ? "a_color0.rgb" : "u_matambientalpha.rgb") : "u_matspecular.rgb";
const char *ambientStr = (gstate.materialupdate & 1) ? (hasColor ? "color0" : "u_matambientalpha") : "u_matambientalpha";
const char *diffuseStr = (gstate.materialupdate & 2) ? (hasColor ? "color0.rgb" : "u_matambientalpha.rgb") : "u_matdiffuse";
const char *specularStr = (gstate.materialupdate & 4) ? (hasColor ? "color0.rgb" : "u_matambientalpha.rgb") : "u_matspecular.rgb";
bool diffuseIsZero = true;
bool specularIsZero = true;
@ -508,7 +508,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
} else {
// Lighting doesn't affect color.
if (hasColor) {
WRITE(p, " v_color0 = a_color0;\n");
WRITE(p, " v_color0 = color0;\n");
} else {
WRITE(p, " v_color0 = u_matambientalpha;\n");
}
@ -524,9 +524,9 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
case GE_TEXMAP_TEXTURE_COORDS: // Scale-offset. Easy.
case GE_TEXMAP_UNKNOWN: // Not sure what this is, but Riviera uses it. Treating as coords works.
if (prescale) {
WRITE(p, " v_texcoord = a_texcoord;\n");
WRITE(p, " v_texcoord = texcoord;\n");
} else {
WRITE(p, " v_texcoord = a_texcoord * u_uvscaleoffset.xy + u_uvscaleoffset.zw;\n");
WRITE(p, " v_texcoord = texcoord * u_uvscaleoffset.xy + u_uvscaleoffset.zw;\n");
}
break;
@ -535,24 +535,24 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
std::string temp_tc;
switch (gstate.getUVProjMode()) {
case GE_PROJMAP_POSITION: // Use model space XYZ as source
temp_tc = "vec4(a_position.xyz, 1.0)";
temp_tc = "vec4(position.xyz, 1.0)";
break;
case GE_PROJMAP_UV: // Use unscaled UV as source
{
static const char *rescaleuv[4] = {"", " * 1.9921875", " * 1.999969482421875", ""}; // 2*127.5f/128.f, 2*32767.5f/32768.f, 1.0f};
const char *factor = rescaleuv[(vertType & GE_VTYPE_TC_MASK) >> GE_VTYPE_TC_SHIFT];
temp_tc = StringFromFormat("vec4(a_texcoord.xy %s, 0.0, 1.0)", factor);
temp_tc = StringFromFormat("vec4(texcoord.xy %s, 0.0, 1.0)", factor);
}
break;
case GE_PROJMAP_NORMALIZED_NORMAL: // Use normalized transformed normal as source
if (hasNormal)
temp_tc = "vec4(normalize(a_normal), 1.0)";
temp_tc = "vec4(normalize(normal), 1.0)";
else
temp_tc = "vec4(0.0, 0.0, 1.0, 1.0)";
break;
case GE_PROJMAP_NORMAL: // Use non-normalized transformed normal as source
if (hasNormal)
temp_tc = "vec4(a_normal, 1.0)";
temp_tc = "vec4(normal, 1.0)";
else
temp_tc = "vec4(0.0, 0.0, 1.0, 1.0)";
break;

View File

@ -485,6 +485,7 @@ bool GPUCommon::InterpretList(DisplayList &list) {
#endif
cycleLastPC = list.pc;
cyclesExecuted += 300;
downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;
list.state = PSP_GE_DL_STATE_RUNNING;
list.interrupted = false;

View File

@ -392,7 +392,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer) {
case GE_CMD_TEXBUFWIDTH5:
case GE_CMD_TEXBUFWIDTH6:
case GE_CMD_TEXBUFWIDTH7:
sprintf(buffer, "Texture BUFWIDTHess %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data);
sprintf(buffer, "Texture BUFWIDTH %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data);
break;
case GE_CMD_CLUTADDR:

View File

@ -234,7 +234,7 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) {
case GE_CMD_TEXBUFWIDTH5:
case GE_CMD_TEXBUFWIDTH6:
case GE_CMD_TEXBUFWIDTH7:
DEBUG_LOG(G3D,"DL Texture BUFWIDTHess %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data);
DEBUG_LOG(G3D,"DL Texture BUFWIDTH %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data);
break;
case GE_CMD_CLUTADDR:

View File

@ -259,6 +259,23 @@ enum GECommand
GE_CMD_UNKNOWN_B7 = 0xB7,
GE_CMD_UNKNOWN_D1 = 0xD1,
GE_CMD_UNKNOWN_ED = 0xED,
GE_CMD_UNKNOWN_EF = 0xEF,
GE_CMD_UNKNOWN_F0 = 0xF0,
GE_CMD_UNKNOWN_F1 = 0xF1,
GE_CMD_UNKNOWN_F2 = 0xF2,
GE_CMD_UNKNOWN_F3 = 0xF3,
GE_CMD_UNKNOWN_F4 = 0xF4,
GE_CMD_UNKNOWN_F5 = 0xF5,
GE_CMD_UNKNOWN_F6 = 0xF6,
GE_CMD_UNKNOWN_F7 = 0xF7,
GE_CMD_UNKNOWN_F8 = 0xF8,
GE_CMD_UNKNOWN_F9 = 0xF9,
GE_CMD_UNKNOWN_FA = 0xFA,
GE_CMD_UNKNOWN_FB = 0xFB,
GE_CMD_UNKNOWN_FC = 0xFC,
GE_CMD_UNKNOWN_FD = 0xFD,
GE_CMD_UNKNOWN_FE = 0xFE,
GE_CMD_UNKNOWN_FF = 0xFF,
};
enum GEBufferFormat

View File

@ -85,6 +85,7 @@ SOURCES += ../native/audio/*.cpp \
../native/gfx/texture_atlas.cpp \
../native/gfx/texture_gen.cpp \
../native/gfx_es2/*.cpp \
../native/gfx_es2/*.c \
../native/i18n/*.cpp \
../native/image/*.cpp \
../native/input/*.cpp \

View File

@ -13,7 +13,7 @@ symbian: MOBILITY += systeminfo
# Libs
symbian {
LIBS += -lCore.lib -lCommon.lib -lNative.lib -llibglib
LIBS += -lCore.lib -lCommon.lib -lNative.lib -llibglib -lhwrmvibraclient
# For now you have to copy these to the Symbian lib dir using ffmpeg/symbian-install.sh
LIBS += -lavformat.lib -lavcodec.lib -lavutil.lib -lswresample.lib -lswscale.lib
}

View File

@ -12,6 +12,7 @@
#include "Core/Host.h"
#include "base/display.h"
#include "mainwindow.h"
#include "QtHost.h"
#include "GPU/GLES/VertexDecoder.h"
#include "ext/glew/GL/glew.h"

View File

@ -47,7 +47,7 @@ MainWindow::MainWindow(QWidget *parent) :
createLanguageMenu();
UpdateMenus();
int zoom = g_Config.iWindowZoom;
int zoom = g_Config.iInternalResolution;
if (zoom < 1) zoom = 1;
if (zoom > 4) zoom = 4;
SetZoom(zoom);
@ -131,12 +131,12 @@ void MainWindow::UpdateMenus()
ui->action_OptionsBufferedRendering->setChecked(g_Config.iRenderingMode == 1);
ui->action_OptionsLinearFiltering->setChecked(3 == g_Config.iTexFiltering);
ui->action_Simple_2xAA->setChecked(g_Config.bAntiAliasing);
ui->action_Simple_2xAA->setChecked(g_Config.bFXAA);
ui->action_OptionsScreen1x->setChecked(0 == (g_Config.iWindowZoom - 1));
ui->action_OptionsScreen2x->setChecked(1 == (g_Config.iWindowZoom - 1));
ui->action_OptionsScreen3x->setChecked(2 == (g_Config.iWindowZoom - 1));
ui->action_OptionsScreen4x->setChecked(3 == (g_Config.iWindowZoom - 1));
ui->action_OptionsScreen1x->setChecked(0 == (g_Config.iInternalResolution - 1));
ui->action_OptionsScreen2x->setChecked(1 == (g_Config.iInternalResolution - 1));
ui->action_OptionsScreen3x->setChecked(2 == (g_Config.iInternalResolution - 1));
ui->action_OptionsScreen4x->setChecked(3 == (g_Config.iInternalResolution - 1));
ui->action_Stretch_to_display->setChecked(g_Config.bStretchToDisplay);
ui->action_OptionsHardwareTransform->setChecked(g_Config.bHardwareTransform);
@ -485,7 +485,7 @@ void MainWindow::on_action_OptionsLinearFiltering_triggered()
void MainWindow::on_action_Simple_2xAA_triggered()
{
g_Config.bAntiAliasing = !g_Config.bAntiAliasing;
g_Config.bFXAA = !g_Config.bFXAA;
UpdateMenus();
}
@ -552,7 +552,7 @@ void MainWindow::on_action_OptionsFullScreen_triggered()
showNormal();
ui->menubar->setVisible(true);
ui->statusbar->setVisible(true);
SetZoom(g_Config.iWindowZoom);
SetZoom(g_Config.iInternalResolution);
}
else {
g_Config.bFullScreen = true;
@ -575,7 +575,7 @@ void MainWindow::on_action_OptionsFullScreen_triggered()
PSP_CoreParameter().outputHeight = height;
int antialias = 1;
if (g_Config.bAntiAliasing) antialias = 2;
if (g_Config.bFXAA) antialias = 2;
PSP_CoreParameter().renderWidth = width * antialias;
PSP_CoreParameter().renderHeight = height * antialias;
@ -700,7 +700,7 @@ void MainWindow::on_language_changed(QAction *action)
/* Private functions */
void MainWindow::SetZoom(float zoom) {
if (zoom < 5)
g_Config.iWindowZoom = (int) zoom;
g_Config.iInternalResolution = (int) zoom;
pixel_xres = 480 * zoom;
pixel_yres = 272 * zoom;
@ -722,7 +722,7 @@ void MainWindow::SetZoom(float zoom) {
PSP_CoreParameter().outputWidth = pixel_xres;
PSP_CoreParameter().outputHeight = pixel_yres;
if (g_Config.bAntiAliasing)
if (g_Config.bFXAA)
{
zoom *= 2;
PSP_CoreParameter().renderWidth = 480 * zoom;

View File

@ -197,12 +197,6 @@ You will need to add the GCCE 4.6.3 variant to Symbian\tools\sbs\lib\config\vari
</var>
```
You will also need to increase the data section of the executable in linking stage by modifying Symbian\tools\sbs\lib\config\gcce.xml as follows:
```
<set name="RW_BASE" value="$(RW_BASE_OPTION)0x3000000"/>
```
Then simply compile the PPSSPPQt.pro with `qmake` from the SDK or the included QtCreator.

View File

@ -30,6 +30,7 @@
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "ext/disarm.h"
#include "Common/CPUDetect.h"
#include <algorithm>
@ -136,6 +137,7 @@ void SystemInfoScreen::CreateViews() {
scroll->Add(new ItemHeader("System Information"));
scroll->Add(new InfoItem("System Name", System_GetProperty(SYSPROP_NAME)));
scroll->Add(new InfoItem("System Lang/Region", System_GetProperty(SYSPROP_LANGREGION)));
scroll->Add(new InfoItem("CPU", cpu_info.brand_string));
scroll->Add(new InfoItem("GPU Vendor", (char *)glGetString(GL_VENDOR)));
scroll->Add(new InfoItem("GPU Model", (char *)glGetString(GL_RENDERER)));
scroll->Add(new InfoItem("OpenGL Version Supported", (char *)glGetString(GL_VERSION)));

View File

@ -173,6 +173,8 @@ void GameScreen::CallbackDeleteSaveData(bool yes) {
GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, false);
if (yes) {
info->DeleteAllSaveData();
info->saveDataSize = 0;
info->installDataSize = 0;
}
}

View File

@ -164,6 +164,9 @@ void GameSettingsScreen::CreateViews() {
graphicsSettings->Add(new ItemHeader(gs->T("Hack Settings")));
graphicsSettings->Add(new CheckBox(&g_Config.bDisableStencilTest, gs->T("Disable Stencil Test")));
graphicsSettings->Add(new CheckBox(&g_Config.bAlwaysDepthWrite, gs->T("Always Depth Write")));
CheckBox *prescale = graphicsSettings->Add(new CheckBox(&g_Config.bPrescaleUV, gs->T("Texture Coord Speedhack")));
if (PSP_IsInited())
prescale->SetEnabled(false);
// Developer tools are not accessible ingame, so it goes here.
graphicsSettings->Add(new ItemHeader(gs->T("Debugging")));
@ -193,7 +196,7 @@ void GameSettingsScreen::CreateViews() {
audioSettings->Add(new PopupSliderChoice(&g_Config.iSFXVolume, 0, 8, a->T("SFX volume"), screenManager()));
audioSettings->Add(new PopupSliderChoice(&g_Config.iBGMVolume, 0, 8, a->T("BGM volume"), screenManager()));
audioSettings->Add(new CheckBox(&g_Config.bEnableSound, a->T("Enable Sound")));
audioSettings->Add(new CheckBox(&g_Config.bEnableAtrac3plus, a->T("Enable Atrac3+")));
audioSettings->Add(new CheckBox(&g_Config.bLowLatencyAudio, a->T("Low latency audio")));
@ -207,6 +210,7 @@ void GameSettingsScreen::CreateViews() {
controlsSettings->Add(new ItemHeader(ms->T("Controls")));
controlsSettings->Add(new Choice(c->T("Control Mapping")))->OnClick.Handle(this, &GameSettingsScreen::OnControlMapping);
#ifdef USING_GLES2
controlsSettings->Add(new CheckBox(&g_Config.bHapticFeedback, c->T("HapticFeedback", "Haptic Feedback (vibration)")));
controlsSettings->Add(new CheckBox(&g_Config.bAccelerometerToAnalogHoriz, c->T("Tilt", "Tilt to Analog (horizontal)")));
controlsSettings->Add(new PopupSliderChoice(&g_Config.iTiltSensitivity, 10, 200, c->T("Tilt Sensitivity"), screenManager()));
#endif
@ -215,7 +219,7 @@ void GameSettingsScreen::CreateViews() {
controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonOpacity, 0, 100, c->T("Button Opacity"), screenManager()));
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fButtonScale, 0.80, 2.0, c->T("Button Scaling"), screenManager()));
controlsSettings->Add(new CheckBox(&g_Config.bShowAnalogStick, c->T("Show Left Analog Stick")));
// System
ViewGroup *systemSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
LinearLayout *systemSettings = new LinearLayout(ORIENT_VERTICAL);

View File

@ -17,6 +17,7 @@
#include "GamepadEmu.h"
#include "base/colorutil.h"
#include "base/NativeApp.h"
#include "math/math_util.h"
#include "ui/virtual_input.h"
#include "ui/ui_context.h"
@ -56,7 +57,7 @@ void MultiTouchButton::Touch(const TouchInput &input) {
void MultiTouchButton::Draw(UIContext &dc) {
float opacity = g_Config.iTouchButtonOpacity / 100.0f;
float scale = scale_;
if (IsDown()) {
scale *= 2.0f;
@ -84,6 +85,9 @@ void PSPButton::Touch(const TouchInput &input) {
MultiTouchButton::Touch(input);
bool down = pointerDownMask_ != 0;
if (down && !lastDown) {
if (g_Config.bHapticFeedback) {
Vibrate(HAPTIC_VIRTUAL_KEY);
}
__CtrlButtonDown(pspButtonBit_);
} else if (lastDown && !down) {
__CtrlButtonUp(pspButtonBit_);
@ -158,8 +162,15 @@ void PSPCross::ProcessTouch(float x, float y, bool down) {
int released = (~down_) & lastDown;
static const int dir[4] = {CTRL_RIGHT, CTRL_DOWN, CTRL_LEFT, CTRL_UP};
for (int i = 0; i < 4; i++) {
if (pressed & dir[i]) __CtrlButtonDown(dir[i]);
if (released & dir[i]) __CtrlButtonUp(dir[i]);
if (pressed & dir[i]) {
if (g_Config.bHapticFeedback) {
Vibrate(HAPTIC_VIRTUAL_KEY);
}
__CtrlButtonDown(dir[i]);
}
if (released & dir[i]) {
__CtrlButtonUp(dir[i]);
}
}
}

View File

@ -67,7 +67,6 @@ class PSPButton : public MultiTouchButton {
public:
PSPButton(int pspButtonBit, int bgImg, int img, float scale, UI::LayoutParams *layoutParams)
: MultiTouchButton(bgImg, img, scale, layoutParams), pspButtonBit_(pspButtonBit) {
}
virtual void Touch(const TouchInput &input);
virtual bool IsDown();

View File

@ -48,6 +48,8 @@
#include <QDir>
#endif
#include <sstream>
#ifdef _WIN32
namespace MainWindow {
void BrowseAndBoot(std::string defaultPath, bool browseDirectory = false);
@ -98,7 +100,7 @@ public:
private:
bool gridStyle_;
std::string gamePath_;
int holdFrameCount_;
};
@ -327,10 +329,10 @@ private:
bool *gridStyle_;
bool allowBrowsing_;
std::string lastText_;
std::string lastLink_;
std::string lastLink_;
};
GameBrowser::GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams)
GameBrowser::GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams)
: LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path), gameList_(0), allowBrowsing_(allowBrowsing), gridStyle_(gridStyle), lastText_(lastText), lastLink_(lastLink) {
using namespace UI;
Refresh();
@ -365,7 +367,8 @@ void GameBrowser::Refresh() {
if (allowBrowsing_) {
LinearLayout *topBar = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
topBar->Add(new TextView(path_.GetFriendlyPath().c_str(), ALIGN_VCENTER, 0.7f, new LinearLayoutParams(WRAP_CONTENT, FILL_PARENT, 1.0f)));
Margins pathMargins(5, 0);
topBar->Add(new TextView(path_.GetFriendlyPath().c_str(), ALIGN_VCENTER, 0.7f, new LinearLayoutParams(WRAP_CONTENT, FILL_PARENT, 1.0f, pathMargins)));
#ifdef ANDROID
topBar->Add(new Choice(m->T("Home")))->OnClick.Handle(this, &GameBrowser::HomeClick);
#endif
@ -376,7 +379,7 @@ void GameBrowser::Refresh() {
layoutChoice->OnChoice.Handle(this, &GameBrowser::LayoutChange);
Add(topBar);
}
if (*gridStyle_) {
gameList_ = new UI::GridLayout(UI::GridLayoutSettings(150, 85), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
} else {
@ -473,15 +476,16 @@ void MainScreen::CreateViews() {
// Scrolling action menu to the right.
using namespace UI;
bool vertical = dp_yres > dp_xres;
ILOG("Vertical? %c : %i %i", vertical ? 'Y' : 'N', dp_xres, dp_yres);
I18NCategory *m = GetI18NCategory("MainMenu");
Margins actionMenuMargins(0, 10, 10, 0);
root_ = new LinearLayout(ORIENT_HORIZONTAL);
TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, new LinearLayoutParams(1.0));
TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64);
leftColumn->SetClip(true);
root_->Add(leftColumn);
ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
ScrollView *scrollAllGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
@ -521,10 +525,8 @@ void MainScreen::CreateViews() {
}
*/
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));
root_->Add(rightColumn);
LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL);
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL);
LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
rightColumn->Add(rightColumnItems);
char versionString[256];
@ -549,6 +551,20 @@ void MainScreen::CreateViews() {
rightColumnItems->Add(new Choice(m->T("www.ppsspp.org")))->OnClick.Handle(this, &MainScreen::OnPPSSPPOrg);
#endif
rightColumnItems->Add(new Choice(m->T("Support PPSSPP")))->OnClick.Handle(this, &MainScreen::OnSupport);
if (vertical) {
root_ = new LinearLayout(ORIENT_VERTICAL);
rightColumn->ReplaceLayoutParams(new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
leftColumn->ReplaceLayoutParams(new LinearLayoutParams(1.0));
root_->Add(rightColumn);
root_->Add(leftColumn);
} else {
root_ = new LinearLayout(ORIENT_HORIZONTAL);
leftColumn->ReplaceLayoutParams(new LinearLayoutParams(1.0));
rightColumn->ReplaceLayoutParams(new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));
root_->Add(leftColumn);
root_->Add(rightColumn);
}
}
void MainScreen::sendMessage(const char *message, const char *value) {
@ -707,6 +723,8 @@ GamePauseScreen::~GamePauseScreen() {
}
void GamePauseScreen::CreateViews() {
static const int NUM_SAVESLOTS = 5;
using namespace UI;
Margins actionMenuMargins(0, 100, 15, 0);
I18NCategory *gs = GetI18NCategory("Graphics");
@ -721,14 +739,21 @@ void GamePauseScreen::CreateViews() {
ViewGroup *leftColumnItems = new LinearLayout(ORIENT_VERTICAL);
leftColumn->Add(leftColumnItems);
saveSlots_ = leftColumnItems->Add(new ChoiceStrip(ORIENT_HORIZONTAL, new LinearLayoutParams(300, WRAP_CONTENT)));
saveSlots_->AddChoice(" 1 ");
saveSlots_->AddChoice(" 2 ");
saveSlots_->AddChoice(" 3 ");
saveSlots_->AddChoice(" 4 ");
saveSlots_->AddChoice(" 5 ");
for (int i = 0; i < NUM_SAVESLOTS; i++){
std::stringstream saveSlotText;
saveSlotText<<" "<<i + 1<<" ";
saveSlots_->AddChoice(saveSlotText.str());
if (SaveState::HasSaveInSlot(i)) {
saveSlots_->HighlightChoice(i);
}
}
saveSlots_->SetSelection(g_Config.iCurrentStateSlot);
saveSlots_->OnChoice.Handle(this, &GamePauseScreen::OnStateSelected);
saveStateButton_ = leftColumnItems->Add(new Choice(i->T("Save State")));

View File

@ -451,11 +451,14 @@ void NativeInitGraphics() {
ui_theme.itemDownStyle.fgColor = 0xFFFFFFFF;
ui_theme.itemDisabledStyle.background = UI::Drawable(0x55E0D4AF);
ui_theme.itemDisabledStyle.fgColor = 0xFFcccccc;
ui_theme.itemHighlightedStyle.background = UI::Drawable(0x55bdBB39);
ui_theme.itemHighlightedStyle.fgColor = 0xFFFFFFFF;
ui_theme.buttonStyle = ui_theme.itemStyle;
ui_theme.buttonFocusedStyle = ui_theme.itemFocusedStyle;
ui_theme.buttonDownStyle = ui_theme.itemDownStyle;
ui_theme.buttonDisabledStyle = ui_theme.itemDisabledStyle;
ui_theme.buttonHighlightedStyle = ui_theme.itemHighlightedStyle;
ui_theme.popupTitle.fgColor = 0xFFE3BE59;
ui_draw2d.Init();

View File

@ -25,6 +25,7 @@
#include "Windows/GEDebugger/CtrlDisplayListView.h"
#include "Windows/GEDebugger/TabDisplayLists.h"
#include "Windows/GEDebugger/TabState.h"
#include "Windows/InputBox.h"
#include "Windows/WindowsHost.h"
#include "Windows/WndMainWindow.h"
#include "Windows/main.h"
@ -60,8 +61,8 @@ static std::vector<bool> breakCmds;
static std::set<u32> breakPCs;
static u32 tempBreakpoint = -1;
static std::set<u32> breakTextures;
static bool breakNextOp = false;
static bool breakNextDraw = false;
static BreakNextType breakNext = BREAK_NONE;
static u32 lastTexture = -1;
static bool bufferResult;
static GPUDebugBuffer bufferFrame;
@ -93,10 +94,23 @@ bool CGEDebugger::IsOpBreakPoint(u32 op) {
return breakCmds[cmd];
}
// Hmm, this is probably kinda slow now...
bool CGEDebugger::IsTextureBreakPoint(u32 op) {
u8 cmd = op >> 24;
bool interesting = (cmd >= GE_CMD_TEXADDR0 && cmd <= GE_CMD_TEXADDR7);
interesting = interesting || (cmd >= GE_CMD_TEXBUFWIDTH0 && cmd <= GE_CMD_TEXBUFWIDTH7);
if (breakNext == BREAK_NEXT_NONTEX) {
// Okay, so we hit an interesting texture, but let's not break if this is a clut/etc.
// Otherwise we get garbage widths and colors. It's annoying.
bool textureCmd = interesting || cmd == GE_CMD_CLUTADDR || cmd == GE_CMD_CLUTADDRUPPER || cmd == GE_CMD_LOADCLUT || cmd == GE_CMD_CLUTFORMAT;
textureCmd = textureCmd || (cmd >= GE_CMD_TEXSIZE0 && cmd <= GE_CMD_TEXSIZE7);
textureCmd = textureCmd || cmd == GE_CMD_TEXFORMAT || cmd == GE_CMD_TEXMODE || cmd == GE_CMD_TEXTUREMAPENABLE;
if (!textureCmd) {
return true;
}
}
if (!interesting || !gpuDebug) {
return false;
}
@ -104,8 +118,18 @@ bool CGEDebugger::IsTextureBreakPoint(u32 op) {
// Okay, so we just set a texture of some sort, check if it was one we were waiting for.
auto state = gpuDebug->GetGState();
int level = cmd <= GE_CMD_TEXADDR7 ? cmd - GE_CMD_TEXADDR0 : cmd - GE_CMD_TEXBUFWIDTH0;
// Are we breaking on any texture? As long as it's level 0.
if (level == 0 && breakNext == BREAK_NEXT_TEX && lastTexture != state.getTextureAddress(level)) {
// Don't break right away, we'll get a garbage texture...
breakNext = BREAK_NEXT_NONTEX;
}
lock_guard guard(breaksLock);
return breakTextures.find(state.getTextureAddress(level)) != breakTextures.end();
if (breakTextures.find(state.getTextureAddress(level)) != breakTextures.end() && breakNext == BREAK_NONE) {
breakNext = BREAK_NEXT_NONTEX;
}
return false;
}
bool CGEDebugger::IsOpOrTextureBreakPoint(u32 op) {
@ -310,6 +334,10 @@ void CGEDebugger::UpdatePreviews() {
}
_snwprintf(desc, ARRAY_SIZE(desc), L"Texture: 0x%08x (%dx%d)", state.getTextureAddress(0), state.getTextureWidth(0), state.getTextureHeight(0));
SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, desc);
lastTexture = state.getTextureAddress(0);
} else {
lastTexture = -1;
}
} else {
texWindow->Clear();
@ -318,6 +346,7 @@ void CGEDebugger::UpdatePreviews() {
} else {
SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, L"Texture: disabled");
}
lastTexture = -1;
}
DisplayList list;
@ -358,6 +387,14 @@ void CGEDebugger::SavePosition()
}
}
void CGEDebugger::SetBreakNext(BreakNextType type) {
attached = true;
SetupPreviews();
SetPauseAction(PAUSE_CONTINUE, false);
breakNext = type;
}
BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG:
@ -413,16 +450,33 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_GEDBG_STEPDRAW:
attached = true;
SetupPreviews();
SetPauseAction(PAUSE_CONTINUE, false);
breakNextOp = false;
breakNextDraw = true;
SetBreakNext(BREAK_NEXT_DRAW);
break;
case IDC_GEDBG_STEP:
SendMessage(m_hDlg,WM_GEDBG_STEPDISPLAYLIST,0,0);
SetBreakNext(BREAK_NEXT_OP);
break;
case IDC_GEDBG_STEPTEX:
SetBreakNext(BREAK_NEXT_TEX);
break;
case IDC_GEDBG_STEPFRAME:
SetBreakNext(BREAK_NEXT_FRAME);
break;
case IDC_GEDBG_BREAKTEX:
{
u32 texAddr;
// TODO: Better interface that allows add/remove or something.
if (InputBox_GetHex(GetModuleHandle(NULL), m_hDlg, L"Texture Address", lastTexture, texAddr)) {
if (breakTextures.find(texAddr) != breakTextures.end()) {
breakTextures.erase(texAddr);
} else {
breakTextures.insert(texAddr);
}
}
}
break;
case IDC_GEDBG_RESUME:
@ -433,8 +487,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
// TODO: detach? Should probably have separate UI, or just on activate?
SetPauseAction(PAUSE_CONTINUE, false);
breakNextOp = false;
breakNextDraw = false;
breakNext = BREAK_NONE;
break;
}
break;
@ -457,12 +510,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
break;
case WM_GEDBG_STEPDISPLAYLIST:
attached = true;
SetupPreviews();
SetPauseAction(PAUSE_CONTINUE, false);
breakNextOp = true;
breakNextDraw = false;
SetBreakNext(BREAK_NEXT_OP);
break;
case WM_GEDBG_TOGGLEPCBREAKPOINT:
@ -526,16 +574,20 @@ void WindowsHost::GPUNotifyCommand(u32 pc) {
u32 op = Memory::ReadUnchecked_U32(pc);
u8 cmd = op >> 24;
if (breakNextOp || CGEDebugger::IsOpOrTextureBreakPoint(op) || CGEDebugger::IsAddressBreakPoint(pc) || pc == tempBreakpoint) {
if (breakNext == BREAK_NEXT_OP || CGEDebugger::IsOpOrTextureBreakPoint(op) || CGEDebugger::IsAddressBreakPoint(pc) || pc == tempBreakpoint) {
PauseWithMessage(WM_GEDBG_BREAK_CMD, (WPARAM) pc);
}
}
void WindowsHost::GPUNotifyDisplay(u32 framebuf, u32 stride, int format) {
if (breakNext == BREAK_NEXT_FRAME) {
// This should work fine, start stepping at the first op of the new frame.
breakNext = BREAK_NEXT_OP;
}
}
void WindowsHost::GPUNotifyDraw() {
if (breakNextDraw) {
if (breakNext == BREAK_NEXT_DRAW) {
PauseWithMessage(WM_GEDBG_BREAK_DRAW);
}
}

View File

@ -33,6 +33,15 @@ enum {
WM_GEDBG_SETCMDWPARAM,
};
enum BreakNextType {
BREAK_NONE,
BREAK_NEXT_OP,
BREAK_NEXT_DRAW,
BREAK_NEXT_TEX,
BREAK_NEXT_NONTEX,
BREAK_NEXT_FRAME,
};
class CtrlDisplayListView;
class TabDisplayLists;
class TabStateFlags;
@ -61,6 +70,7 @@ private:
void UpdatePreviews();
void UpdateSize(WORD width, WORD height);
void SavePosition();
void SetBreakNext(BreakNextType type);
CtrlDisplayListView *displayList;
TabDisplayLists *lists;

View File

@ -172,6 +172,12 @@ bool GL_Init(HWND window, std::string *error_message) {
m_hrc = hRC;
}
if (GLEW_OK != glewInit()) {
*error_message = "Failed to re-initialize GLEW.";
return false;
}
if (!m_hrc) {
*error_message = "No m_hrc";
return false;

View File

@ -23,7 +23,7 @@ HWND TabControl::AddTabWindow(wchar_t* className, wchar_t* title, DWORD style)
style = (style |WS_CHILD) & tabControlStyleMask;
if (showTabTitles)
AppendPageToControl(title);
int index = tabs.size();
int index = (int)tabs.size();
RECT tabRect;
GetWindowRect(hwnd,&tabRect);
@ -60,7 +60,7 @@ void TabControl::AddTab(HWND handle, wchar_t* title)
{
if (showTabTitles)
AppendPageToControl(title);
int index = tabs.size();
int index = (int)tabs.size();
TabInfo info = {0};
if (!noDisplayArea_)
@ -155,7 +155,7 @@ void TabControl::ShowTab(HWND pageHandle)
{
if (tabs[i].pageHandle == pageHandle)
{
currentTab = i;
currentTab = (int)i;
if (showTabTitles)
TabCtrl_SetCurSel(hwnd,i);
if (oldFocus)

View File

@ -277,7 +277,7 @@ void WindowsHost::UpdateConsolePosition()
{
RECT rc;
HWND console = GetConsoleWindow();
if (console != NULL && GetWindowRect(console, &rc))
if (console != NULL && GetWindowRect(console, &rc) && !IsIconic(console))
{
g_Config.iConsoleWindowX = rc.left;
g_Config.iConsoleWindowY = rc.top;

View File

@ -56,15 +56,99 @@ CGEDebugger *geDebuggerWindow = 0;
CMemoryDlg *memoryWindow[MAX_CPUCOUNT] = {0};
static std::string langRegion;
static std::string osName;
void LaunchBrowser(const char *url) {
ShellExecute(NULL, L"open", ConvertUTF8ToWString(url).c_str(), NULL, NULL, SW_SHOWNORMAL);
}
void Vibrate(int length_ms) {
// Ignore on PC
}
bool DoesVersionMatchWindows(const u32 major, const u32 minor, const u32 spMajor = 0, const u32 spMinor = 0) {
u64 conditionMask = 0;
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(osvi);
osvi.dwMajorVersion = major;
osvi.dwMinorVersion = minor;
osvi.wServicePackMajor = spMajor;
osvi.wServicePackMinor = spMinor;
u32 op = VER_EQUAL;
VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, op);
VER_SET_CONDITION(conditionMask, VER_MINORVERSION, op);
VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMAJOR, op);
VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMINOR, op);
const u32 typeMask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR;
return VerifyVersionInfo(&osvi, typeMask, conditionMask) != FALSE;
}
std::string GetWindowsVersion() {
const bool IsWindowsXPSP2 = DoesVersionMatchWindows(5, 1, 2, 0);
const bool IsWindowsXPSP3 = DoesVersionMatchWindows(5, 1, 3, 0);
const bool IsWindowsVista = DoesVersionMatchWindows(6, 0);
const bool IsWindowsVistaSP1 = DoesVersionMatchWindows(6, 0, 1, 0);
const bool IsWindowsVistaSP2 = DoesVersionMatchWindows(6, 0, 2, 0);
const bool IsWindows7 = DoesVersionMatchWindows(6, 1);
const bool IsWindows7SP1 = DoesVersionMatchWindows(6, 1, 1, 0);
const bool IsWindows8 = DoesVersionMatchWindows(6, 2);
const bool IsWindows8_1 = DoesVersionMatchWindows(6, 3);
if (IsWindowsXPSP2)
return "Microsoft Windows XP, Service Pack 2";
if (IsWindowsXPSP3)
return "Microsoft Windows XP, Service Pack 3";
if (IsWindowsVista)
return "Microsoft Windows Vista";
if (IsWindowsVistaSP1)
return "Microsoft Windows Vista, Service Pack 1";
if (IsWindowsVistaSP2)
return "Microsoft Windows Vista, Service Pack 2";
if (IsWindows7)
return "Microsoft Windows 7";
if (IsWindows7SP1)
return "Microsoft Windows 7, Service Pack 1";
if (IsWindows8)
return "Microsoft Windows 8";
if (IsWindows8_1)
return "Microsoft Windows 8.1";
return "Unsupported version of Microsoft Windows.";
}
std::string GetWindowsSystemArchitecture() {
SYSTEM_INFO sysinfo;
ZeroMemory(&sysinfo, sizeof(SYSTEM_INFO));
GetNativeSystemInfo(&sysinfo);
if (sysinfo.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_AMD64)
return "(x64)";
// Need to check for equality here, since ANDing with 0 is always 0.
else if (sysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
return "(x86)";
else if (sysinfo.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_ARM)
return "(ARM)";
else
return "(Unknown)";
}
std::string System_GetProperty(SystemProperty prop) {
switch (prop) {
case SYSPROP_NAME:
return "PC:Windows";
return osName;
case SYSPROP_LANGREGION:
return langRegion;
default:
@ -109,6 +193,8 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
langRegion = "en_US";
}
osName = GetWindowsVersion() + " " + GetWindowsSystemArchitecture();
std::string configFilename;
const char *configOption = "--config=";

View File

@ -175,12 +175,15 @@ FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
PUSHBUTTON "Step Draw",IDC_GEDBG_STEPDRAW,0,0,48,14
PUSHBUTTON "Step Into",IDC_GEDBG_STEP,52,0,48,14
PUSHBUTTON "Resume",IDC_GEDBG_RESUME,104,0,48,14
PUSHBUTTON "Step Tex",IDC_GEDBG_STEPTEX,104,0,48,14
PUSHBUTTON "Step Frame",IDC_GEDBG_STEPFRAME,156,0,48,14
PUSHBUTTON "Resume",IDC_GEDBG_RESUME,208,0,48,14
CONTROL "",IDC_GEDBG_TEX,"SimpleGLWindow",WS_CHILD | WS_VISIBLE,10,20,128,128
CONTROL "",IDC_GEDBG_FRAME,"SimpleGLWindow",WS_CHILD | WS_VISIBLE,148,20,256,136
CONTROL "",IDC_GEDBG_MAINTAB,"SysTabControl32",TCS_TABS | TCS_FOCUSNEVER,10,216,480,180
EDITTEXT IDC_GEDBG_FRAMEBUFADDR,148,192,128,12,ES_READONLY | NOT WS_BORDER
EDITTEXT IDC_GEDBG_TEXADDR,10,152,128,12,ES_READONLY | NOT WS_BORDER
PUSHBUTTON "Break on Texture...",IDC_GEDBG_BREAKTEX,24,172,100,14
CONTROL "",IDC_GEDBG_FBTABS,"SysTabControl32",TCS_BUTTONS | TCS_FOCUSNEVER,384,192,110,12
END

View File

@ -290,7 +290,10 @@
#define ID_GEDBG_SETSTALLADDR 40130
#define ID_GEDBG_GOTOPC 40131
#define ID_GEDBG_GOTOADDR 40132
#define ID_OPTIONS_PAUSE_FOCUS 40133
#define IDC_GEDBG_STEPTEX 40133
#define IDC_GEDBG_STEPFRAME 40134
#define IDC_GEDBG_BREAKTEX 40135
#define ID_OPTIONS_PAUSE_FOCUS 40133
// Dummy option to let the buffered rendering hotkey cycle through all the options.
#define ID_OPTIONS_BUFFEREDRENDERINGDUMMY 40500
@ -303,7 +306,7 @@
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 254
#define _APS_NEXT_COMMAND_VALUE 40133
#define _APS_NEXT_COMMAND_VALUE 40136
#define _APS_NEXT_CONTROL_VALUE 1193
#define _APS_NEXT_SYMED_VALUE 101
#endif

View File

@ -5,7 +5,7 @@
android:versionName="0.9.1"
android:installLocation="auto" >
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17"/>
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18"/>
<uses-feature android:glEsVersion="0x00020000"></uses-feature>
<uses-feature android:name="android.hardware.screen.landscape" />
@ -30,7 +30,8 @@
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:configChanges="orientation|locale|keyboard|keyboardHidden|navigation|fontScale|uiMode"
android:screenOrientation="landscape" >
android:screenOrientation="landscape"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

Binary file not shown.

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="ga_trackingId">UA-36039055-2</string>
<!--Enable automatic activity tracking-->
<bool name="ga_autoActivityTracking">true</bool>
<!--Enable automatic exception tracking-->
<bool name="ga_reportUncaughtExceptions">true</bool>
</resources>

View File

@ -1,6 +1,9 @@
package org.ppsspp.ppsspp;
import android.os.Bundle;
import com.henrikrydgard.libnative.NativeActivity;
import com.google.analytics.tracking.android.EasyTracker;
public class PpssppActivity extends NativeActivity {
static {
@ -9,11 +12,26 @@ public class PpssppActivity extends NativeActivity {
public PpssppActivity() {
super();
}
public boolean overrideKeys()
{
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onStart() {
super.onStart();
EasyTracker.getInstance(this).activityStart(this);
}
@Override
public void onStop() {
super.onStop();
EasyTracker.getInstance(this).activityStop(this);
}
public boolean overrideKeys() {
return false;
}
}
}
}

2
lang

@ -1 +1 @@
Subproject commit fa980bf1e567687c3649e4b2b461d1a223f800cc
Subproject commit 50fe9235cde6004892fe3bb04f8780c693d5b38a

2
native

@ -1 +1 @@
Subproject commit b5c7f637df25c40ba9a497f5c98d2377962bbf7a
Subproject commit b16b67d8b6e2fd5127f9b84ec9f5496d8f782e71