Merge remote-tracking branch 'upstream/master'

Conflicts:
	Core/Dialog/SavedataParam.cpp
This commit is contained in:
Xele02 2012-12-22 18:57:10 +01:00
commit d23e751557
91 changed files with 3631 additions and 1262 deletions

33
.gitignore vendored
View file

@ -1,37 +1,46 @@
# For MSVC
*.lastcodeanalysissucceeded
*.pdb
*.ilk
*.obj
*.pch
Logs
*.log
*.dll
*.rar
*.exe
*.ini
*.map
*.lib
*.user
*.sdf
*.ncb
Debug
DebugFast
Release
*.opensdf
*.suo
*.aps
*.exp
Debug
DebugFast
Release
Windows/x64
Windows/ipch
# For ppsspp.ini, etc.
*.ini
Logs
Memstick
bin
gen
libs
obj
*.exp
build*/
.pspsh.hist
GameLogNotes.txt
Windows/x64
Windows/ipch
Memstick
android/ui_atlas.zim
__testoutput.txt
__testerror.txt
__testfinish.txt
GameLogNotes.txt
android/ui_atlas.zim
ppge_atlas.zim.png
local.properties
build*/

View file

@ -786,6 +786,8 @@ add_library(GPU OBJECT
GPU/GLES/FragmentShaderGenerator.h
GPU/GLES/Framebuffer.cpp
GPU/GLES/Framebuffer.h
GPU/GLES/IndexGenerator.cpp
GPU/GLES/IndexGenerator.h
GPU/GLES/ShaderManager.cpp
GPU/GLES/ShaderManager.h
GPU/GLES/StateMapping.cpp

View file

@ -318,9 +318,7 @@ void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
Text += 10;
}
SetConsoleTextAttribute(hConsole, Color);
size_t len = strlen(Text);
if (Text[len-1] == '\n' && Text[len-1] == '\r')
len--;
size_t len = strlen(Text);
WriteConsole(hConsole, Text, (DWORD)len, &cCharsWritten, NULL);
#else
char ColorAttr[16] = "";

View file

@ -283,7 +283,7 @@ static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32
int i;
for (i = 0; i < num_views; i++)
{
const MemoryView &view = views[i];
const MemoryView &view = views[i];
SKIP(flags, view.flags);
if (view.flags & MV_MIRROR_PREVIOUS) {
position = last_position;

View file

@ -49,14 +49,17 @@ void CConfig::Load(const char *iniFileName)
general->Get("IgnoreBadMemAccess", &bIgnoreBadMemAccess, true);
general->Get("CurrentDirectory", &currentDirectory, "");
general->Get("ShowDebuggerOnLoad", &bShowDebuggerOnLoad, false);
IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU");
cpu->Get("Core", &iCpuCore, 0);
cpu->Get("FastMemory", &bFastMemory, false);
IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics");
graphics->Get("ShowFPSCounter", &bShowFPSCounter, false);
graphics->Get("DisplayFramebuffer", &bDisplayFramebuffer, false);
graphics->Get("WindowZoom", &iWindowZoom, 1);
graphics->Get("BufferedRendering", &bBufferedRendering, true);
graphics->Get("HardwareTransform", &bHardwareTransform, false);
IniFile::Section *sound = iniFile.GetOrCreateSection("Sound");
sound->Get("Enable", &bEnableSound, true);
@ -64,6 +67,10 @@ void CConfig::Load(const char *iniFileName)
IniFile::Section *control = iniFile.GetOrCreateSection("Control");
control->Get("ShowStick", &bShowAnalogStick, false);
control->Get("ShowTouchControls", &bShowTouchControls, true);
// Ephemeral settings
bDrawWireframe = false;
}
void CConfig::Save()
@ -83,12 +90,14 @@ void CConfig::Save()
general->Set("ShowDebuggerOnLoad", bShowDebuggerOnLoad);
IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU");
cpu->Set("Core", iCpuCore);
cpu->Set("FastMemory", bFastMemory);
IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics");
graphics->Set("ShowFPSCounter", bShowFPSCounter);
graphics->Set("DisplayFramebuffer", bDisplayFramebuffer);
graphics->Set("WindowZoom", iWindowZoom);
graphics->Set("BufferedRendering", bBufferedRendering);
graphics->Set("HardwareTransform", bHardwareTransform);
IniFile::Section *sound = iniFile.GetOrCreateSection("Sound");
sound->Set("Enable", bEnableSound);

View file

@ -41,8 +41,13 @@ public:
bool bSpeedLimit;
bool bConfirmOnQuit;
bool bIgnoreBadMemAccess;
bool bFastMemory;
// GFX
bool bDisplayFramebuffer;
bool bHardwareTransform;
bool bBufferedRendering;
bool bDrawWireframe;
bool bShowTouchControls;
bool bShowDebuggerOnLoad;

View file

@ -91,6 +91,9 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>

View file

@ -79,6 +79,7 @@ void (*advanceCallback)(int cyclesExecuted) = NULL;
void SetClockFrequencyMHz(int cpuMhz)
{
CPU_HZ = cpuMhz * 1000000;
// TODO: Rescale times of scheduled events?
}
int GetClockFrequencyMHz()
@ -245,7 +246,7 @@ void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata)
Event *ne = GetNewEvent();
ne->userdata = userdata;
ne->type = event_type;
ne->time = globalTimer + cyclesIntoFuture;
ne->time = GetTicks() + cyclesIntoFuture;
AddEventToQueue(ne);
}

View file

@ -58,6 +58,10 @@ inline int usToCycles(int us) {
return (int)(CPU_HZ / 1000000 * us);
}
inline u64 usToCycles(u64 us) {
return (u64)(CPU_HZ / 1000000ULL * us);
}
inline u64 cyclesToUs(u64 cycles) {
return cycles / (CPU_HZ / 1000000);
}

View file

@ -40,7 +40,7 @@ void MemCheck::Action(u32 iValue, u32 addr, bool write, int size, u32 pc)
if (bLog)
{
char temp[256];
printf(temp,"CHK %08x %s%i at %08x (%s), PC=%08x (%s)",iValue,write?"Write":"Read",size*8,addr,symbolMap.GetDescription(addr),pc,symbolMap.GetDescription(pc));
sprintf(temp,"CHK %08x %s%i at %08x (%s), PC=%08x (%s)",iValue,write?"Write":"Read",size*8,addr,symbolMap.GetDescription(addr),pc,symbolMap.GetDescription(pc));
ERROR_LOG(MEMMAP,"%s",temp);
}
if (bBreak)

View file

@ -144,10 +144,9 @@ bool SymbolMap::LoadSymbolMap(const char *filename)
{
char line[512],temp[256];
fgets(line,511,f);
if (strlen(line)<4)
if (strlen(line) < 4 || sscanf(line, "%s", temp) != 1)
continue;
sscanf(line,"%s",temp);
if (strcmp(temp,"UNUSED")==0) continue;
if (strcmp(temp,".text")==0) {started=true;continue;};
if (strcmp(temp,".init")==0) {started=true;continue;};
@ -233,7 +232,7 @@ int SymbolMap::GetSymbolNum(unsigned int address, SymbolType symmask)
}
char temp[256];
char descriptionTemp[256];
char *SymbolMap::GetDescription(unsigned int address)
{
@ -244,8 +243,8 @@ char *SymbolMap::GetDescription(unsigned int address)
return entries[fun].name;
else
{
sprintf(temp, "(%08x)", address);
return temp;
sprintf(descriptionTemp, "(%08x)", address);
return descriptionTemp;
}
//}
//else
@ -438,11 +437,12 @@ void SymbolMap::UseFuncSignaturesFile(const char *filename, u32 maxAddress)
//#1: Read the signature file and put them in a fast data structure
FILE *f = fopen(filename, "r");
int count;
fscanf(f,"%08x\n",&count);
u32 inst,size,hash;
if (fscanf(f, "%08x\n", &count) != 1)
count = 0;
char name[256];
for (int a=0; a<count; a++)
{
u32 inst, size, hash;
if (fscanf(f,"%08x\t%08x\t%08x\t%s\n",&inst,&size,&hash,name)!=EOF)
sigs[numSigs++]=Sig(inst,size,hash,name);
else

View file

@ -160,7 +160,7 @@ void PSPOskDialog::HackyGetStringWide(std::string& _string, const u32 em_address
char *string = stringBuffer;
char c;
u32 addr = em_address;
while ((c = (Memory::Read_U16(addr))))
while ((c = (char)(Memory::Read_U16(addr))))
{
*string++ = c;
addr+=2;

View file

@ -31,19 +31,17 @@ PSPSaveDialog::PSPSaveDialog()
PSPSaveDialog::~PSPSaveDialog() {
}
void PSPSaveDialog::Init(int paramAddr)
u32 PSPSaveDialog::Init(int paramAddr)
{
// Ignore if already running
if (status != SCE_UTILITY_STATUS_NONE && status != SCE_UTILITY_STATUS_SHUTDOWN)
{
return;
return 0;
}
param.SetPspParam((SceUtilitySavedataParam*)Memory::GetPointer(paramAddr));
DEBUG_LOG(HLE,"sceUtilitySavedataInitStart(%08x)", paramAddr);
u32 retval = param.SetPspParam((SceUtilitySavedataParam*)Memory::GetPointer(paramAddr));
DEBUG_LOG(HLE,"Mode: %i", param.GetPspParam()->mode);
switch(param.GetPspParam()->mode)
switch (param.GetPspParam()->mode)
{
case SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD:
case SCE_UTILITY_SAVEDATA_TYPE_LOAD:
@ -85,12 +83,12 @@ void PSPSaveDialog::Init(int paramAddr)
ERROR_LOG(HLE, "Load/Save function %d not coded. Title: %s Save: %s File: %s", param.GetPspParam()->mode, param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
param.GetPspParam()->result = 0;
display = DS_NONE;
return; // Return 0 should allow the game to continue, but missing function must be implemented and returning the right value or the game can block.
return 0; // Return 0 should allow the game to continue, but missing function must be implemented and returning the right value or the game can block.
}
break;
}
status = SCE_UTILITY_STATUS_INITIALIZE;
status = (int)retval < 0 ? SCE_UTILITY_STATUS_SHUTDOWN : SCE_UTILITY_STATUS_INITIALIZE;
currentSelectedSave = 0;
lastButtons = __CtrlPeekButtons();
@ -126,7 +124,7 @@ void PSPSaveDialog::Init(int paramAddr)
INFO_LOG(HLE,"snd0 data : %08x",*((unsigned int*)&param.GetPspParam()->snd0FileData.buf));
INFO_LOG(HLE,"snd0 size : %u",param.GetPspParam()->snd0FileData.bufSize);*/
return retval;
}
void PSPSaveDialog::DisplaySaveList(bool canMove)
@ -142,16 +140,16 @@ void PSPSaveDialog::DisplaySaveList(bool canMove)
}
// Calc save image position on screen
int w = 150;
int h = 80;
int x = 20;
float w = 150;
float h = 80;
float x = 20;
if(displayCount != currentSelectedSave)
{
w = 80;
h = 40;
x = 50;
}
int y = 80;
float y = 80;
if(displayCount < currentSelectedSave)
y -= 50 * (currentSelectedSave - displayCount);
else if(displayCount > currentSelectedSave)
@ -199,10 +197,10 @@ void PSPSaveDialog::DisplaySaveIcon()
}
// Calc save image position on screen
int w = 150;
int h = 80;
int x = 20;
int y = 80;
float w = 150;
float h = 80;
float x = 20;
float y = 80;
int tw = 256;
int th = 256;
@ -232,7 +230,7 @@ void PSPSaveDialog::DisplaySaveDataInfo1()
else
{
char txt[1024];
sprintf(txt,"%s\n%02d/%02d/%d %02d:%02d %d KB\n%s\n%s"
sprintf(txt,"%s\n%02d/%02d/%d %02d:%02d %lld KB\n%s\n%s"
, param.GetFileInfo(currentSelectedSave).title
, param.GetFileInfo(currentSelectedSave).modif_time.tm_mday
, param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1
@ -256,7 +254,7 @@ void PSPSaveDialog::DisplaySaveDataInfo2()
else
{
char txt[1024];
sprintf(txt,"%s\n%02d/%02d/%d %02d:%02d\n%d KB"
sprintf(txt,"%s\n%02d/%02d/%d %02d:%02d\n%lld KB"
, param.GetFileInfo(currentSelectedSave).saveTitle
, param.GetFileInfo(currentSelectedSave).modif_time.tm_mday
, param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1

View file

@ -62,7 +62,7 @@ public:
PSPSaveDialog();
virtual ~PSPSaveDialog();
virtual void Init(int paramAddr);
virtual u32 Init(int paramAddr);
virtual void Update();
void Shutdown();

View file

@ -21,6 +21,7 @@
#include "../HLE/sceKernelMemory.h"
#include "../ELF/ParamSFO.h"
#include "Core/HW/MemoryStick.h"
#include "PSPSaveDialog.h"
std::string icon0Name = "ICON0.PNG";
std::string icon1Name = "ICON1.PMF";
@ -51,7 +52,7 @@ SavedataParam::SavedataParam()
void SavedataParam::Init()
{
if(!pspFileSystem.GetFileInfo(savePath).exists)
if (!pspFileSystem.GetFileInfo(savePath).exists)
{
pspFileSystem.MkDir(savePath);
}
@ -64,7 +65,7 @@ std::string SavedataParam::GetSaveDir(SceUtilitySavedataParam* param, int saveId
}
std::string dirPath = GetGameName(param)+GetSaveName(param);
if(saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it
if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it
dirPath = std::string(GetGameName(param))+GetFilename(saveId);
return dirPath;
@ -111,9 +112,9 @@ bool SavedataParam::Delete(SceUtilitySavedataParam* param, int saveId)
}
std::string dirPath = GetSaveFilePath(param,saveId);
if(saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it
if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it
{
if(saveDataList[saveId].size == 0) // don't delete no existing file
if (saveDataList[saveId].size == 0) // don't delete no existing file
{
return false;
}
@ -129,22 +130,22 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, int saveId)
return false;
}
u8* data_ = (u8*)Memory::GetPointer(*((unsigned int*)&param->dataBuf));
u8 *data_ = (u8*)Memory::GetPointer(*((unsigned int*)&param->dataBuf));
std::string dirPath = GetSaveFilePath(param, saveId);
if(!pspFileSystem.GetFileInfo(dirPath).exists)
if (!pspFileSystem.GetFileInfo(dirPath).exists)
pspFileSystem.MkDir(dirPath);
std::string filePath = dirPath+"/"+GetFileName(param);
INFO_LOG(HLE,"Saving file with size %u in %s",param->dataBufSize,filePath.c_str());
unsigned int handle = pspFileSystem.OpenFile(filePath,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE));
if(handle == 0)
if (handle == 0)
{
ERROR_LOG(HLE,"Error opening file %s",filePath.c_str());
return false;
}
if(!pspFileSystem.WriteFile(handle, data_, param->dataBufSize))
if (!pspFileSystem.WriteFile(handle, data_, param->dataBufSize))
{
pspFileSystem.CloseFile(handle);
ERROR_LOG(HLE,"Error writing file %s",filePath.c_str());
@ -164,12 +165,12 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, int saveId)
sfoFile.SetValue("SAVEDATA_DIRECTORY",GetSaveDir(param,saveId),64);
sfoFile.SetValue("SAVEDATA_FILE_LIST","",3168); // This need to be filed with the save filename and a hash
sfoFile.SetValue("SAVEDATA_PARAMS","",128); // This need to be filled with a hash of the save file encrypted.
u8* sfoData;
u8 *sfoData;
size_t sfoSize;
sfoFile.WriteSFO(&sfoData,&sfoSize);
std::string sfopath = dirPath+"/"+sfoName;
handle = pspFileSystem.OpenFile(sfopath,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE));
if(handle)
if (handle)
{
pspFileSystem.WriteFile(handle, sfoData, sfoSize);
pspFileSystem.CloseFile(handle);
@ -177,36 +178,36 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, int saveId)
delete[] sfoData;
// SAVE ICON0
if(param->icon0FileData.buf)
if (param->icon0FileData.buf)
{
data_ = (u8*)Memory::GetPointer(*((unsigned int*)&param->icon0FileData.buf));
std::string icon0path = dirPath+"/"+icon0Name;
handle = pspFileSystem.OpenFile(icon0path,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE));
if(handle)
if (handle)
{
pspFileSystem.WriteFile(handle, data_, param->icon0FileData.bufSize);
pspFileSystem.CloseFile(handle);
}
}
// SAVE ICON1
if(param->icon1FileData.buf)
if (param->icon1FileData.buf)
{
data_ = (u8*)Memory::GetPointer(*((unsigned int*)&param->icon1FileData.buf));
std::string icon1path = dirPath+"/"+icon1Name;
handle = pspFileSystem.OpenFile(icon1path,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE));
if(handle)
if (handle)
{
pspFileSystem.WriteFile(handle, data_, param->icon1FileData.bufSize);
pspFileSystem.CloseFile(handle);
}
}
// SAVE PIC1
if(param->pic1FileData.buf)
if (param->pic1FileData.buf)
{
data_ = (u8*)Memory::GetPointer(*((unsigned int*)&param->pic1FileData.buf));
std::string pic1path = dirPath+"/"+pic1Name;
handle = pspFileSystem.OpenFile(pic1path,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE));
if(handle)
if (handle)
{
pspFileSystem.WriteFile(handle, data_, param->pic1FileData.bufSize);
pspFileSystem.CloseFile(handle);
@ -214,12 +215,12 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, int saveId)
}
// Save SND
if(param->snd0FileData.buf)
if (param->snd0FileData.buf)
{
data_ = (u8*)Memory::GetPointer(*((unsigned int*)&param->snd0FileData.buf));
std::string snd0path = dirPath+"/"+snd0Name;
handle = pspFileSystem.OpenFile(snd0path,(FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE));
if(handle)
if (handle)
{
pspFileSystem.WriteFile(handle, data_, param->snd0FileData.bufSize);
pspFileSystem.CloseFile(handle);
@ -229,18 +230,18 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, int saveId)
return true;
}
bool SavedataParam::Load(SceUtilitySavedataParam* param, int saveId)
bool SavedataParam::Load(SceUtilitySavedataParam *param, int saveId)
{
if (!param) {
return false;
}
u8* data_ = (u8*)Memory::GetPointer(*((unsigned int*)&param->dataBuf));
u8 *data_ = (u8*)Memory::GetPointer(*((unsigned int*)&param->dataBuf));
std::string dirPath = GetSaveFilePath(param, saveId);
if(saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it
if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it
{
if(saveDataList[saveId].size == 0) // don't read no existing file
if (saveDataList[saveId].size == 0) // don't read no existing file
{
return false;
}
@ -249,12 +250,12 @@ bool SavedataParam::Load(SceUtilitySavedataParam* param, int saveId)
std::string filePath = dirPath+"/"+GetFileName(param);
INFO_LOG(HLE,"Loading file with size %u in %s",param->dataBufSize,filePath.c_str());
u32 handle = pspFileSystem.OpenFile(filePath,FILEACCESS_READ);
if(!handle)
if (!handle)
{
ERROR_LOG(HLE,"Error opening file %s",filePath.c_str());
return false;
}
if(!pspFileSystem.ReadFile(handle, data_, param->dataBufSize))
if (!pspFileSystem.ReadFile(handle, data_, param->dataBufSize))
{
pspFileSystem.CloseFile(handle);
ERROR_LOG(HLE,"Error reading file %s",filePath.c_str());
@ -264,6 +265,7 @@ bool SavedataParam::Load(SceUtilitySavedataParam* param, int saveId)
return true;
}
<<<<<<< HEAD
std::string SavedataParam::GetSpaceText(int size)
{
char text[50];
@ -299,12 +301,15 @@ std::string SavedataParam::GetSpaceText(int size)
// Perhaps changed to use mode 22 id SDK >= 2
// For now we always return results
bool SavedataParam::GetSizes(SceUtilitySavedataParam* param)
=======
bool SavedataParam::GetSizes(SceUtilitySavedataParam *param)
>>>>>>> upstream/master
{
if (!param) {
return false;
}
if(Memory::IsValidAddress(param->msFree))
if (Memory::IsValidAddress(param->msFree))
{
Memory::Write_U32((u32)MemoryStick_SectorSize(),param->msFree); // cluster Size
Memory::Write_U32((u32)(MemoryStick_FreeSpace() / MemoryStick_SectorSize()),param->msFree+4); // Free cluster
@ -313,7 +318,7 @@ bool SavedataParam::GetSizes(SceUtilitySavedataParam* param)
Memory::Memset(param->msFree+12,0,spaceTxt.size()+1);
Memory::Memcpy(param->msFree+12,spaceTxt.c_str(),spaceTxt.size()); // Text representing free space
}
if(Memory::IsValidAddress(param->msData))
if (Memory::IsValidAddress(param->msData))
{
std::string path = GetSaveFilePath(param,0);
PSPFileInfo finfo = pspFileSystem.GetFileInfo(path);
@ -336,7 +341,11 @@ bool SavedataParam::GetSizes(SceUtilitySavedataParam* param)
//return false;
}
}
<<<<<<< HEAD
if(Memory::IsValidAddress(param->utilityData)) // Calc space required for save
=======
if (Memory::IsValidAddress(param->utilityData))
>>>>>>> upstream/master
{
int total_size = 0;
total_size += getSizeNormalized(1); // SFO;
@ -360,13 +369,13 @@ bool SavedataParam::GetSizes(SceUtilitySavedataParam* param)
}
bool SavedataParam::GetList(SceUtilitySavedataParam* param)
bool SavedataParam::GetList(SceUtilitySavedataParam *param)
{
if (!param) {
return false;
}
if(Memory::IsValidAddress(param->idListAddr))
if (Memory::IsValidAddress(param->idListAddr))
{
Memory::Write_U32(0,param->idListAddr+4);
}
@ -375,11 +384,11 @@ bool SavedataParam::GetList(SceUtilitySavedataParam* param)
void SavedataParam::Clear()
{
if(saveDataList)
if (saveDataList)
{
for(int i = 0; i < saveNameListDataCount; i++)
for (int i = 0; i < saveNameListDataCount; i++)
{
if(saveDataList[i].textureData != 0)
if (saveDataList[i].textureData != 0)
kernelMemory.Free(saveDataList[i].textureData);
saveDataList[i].textureData = 0;
}
@ -389,23 +398,23 @@ void SavedataParam::Clear()
}
}
void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
u32 SavedataParam::SetPspParam(SceUtilitySavedataParam *param)
{
pspParam = param;
if(!pspParam)
if (!pspParam)
{
Clear();
return;
return 0;
}
bool listEmptyFile = true;
if(param->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTLOAD ||
if (param->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTLOAD ||
param->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE)
{
listEmptyFile = false;
}
if(param->saveNameList != 0)
if (param->saveNameList != 0)
{
saveNameListData = (char(*)[20])Memory::GetPointer(param->saveNameList);
@ -421,13 +430,13 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
// get and stock file info for each file
int realCount = 0;
for(int i = 0; i <count; i++)
for (int i = 0; i < count; i++)
{
DEBUG_LOG(HLE,"Name : %s",saveNameListData[i]);
std::string fileDataPath = savePath+GetGameName(param)+saveNameListData[i]+"/"+param->fileName;
PSPFileInfo info = pspFileSystem.GetFileInfo(fileDataPath);
if(info.exists)
if (info.exists)
{
saveDataList[realCount].size = info.size;
saveDataList[realCount].saveName = saveNameListData[i];
@ -438,15 +447,15 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
// TODO : If icon0 don't exist, need to use icon1 which is a moving icon. Also play sound
std::string fileDataPath2 = savePath+GetGameName(param)+saveNameListData[i]+"/"+icon0Name;
PSPFileInfo info2 = pspFileSystem.GetFileInfo(fileDataPath2);
if(info2.exists)
if (info2.exists)
{
u8* textureDataPNG = new u8[info2.size];
u8 *textureDataPNG = new u8[(size_t)info2.size];
int handle = pspFileSystem.OpenFile(fileDataPath2,FILEACCESS_READ);
pspFileSystem.ReadFile(handle,textureDataPNG,info2.size);
pspFileSystem.CloseFile(handle);
unsigned char* textureData;
int w,h;
pngLoadPtr(textureDataPNG, info2.size, &w, &h, &textureData, false);
pngLoadPtr(textureDataPNG, (int)info2.size, &w, &h, &textureData, false);
delete[] textureDataPNG;
u32 texSize = w*h*4;
u32 atlasPtr = kernelMemory.Alloc(texSize, true, "SaveData Icon");
@ -464,14 +473,14 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
// Load info in PARAM.SFO
fileDataPath2 = savePath+GetGameName(param)+saveNameListData[i]+"/"+sfoName;
info2 = pspFileSystem.GetFileInfo(fileDataPath2);
if(info2.exists)
if (info2.exists)
{
u8* sfoParam = new u8[info2.size];
u8 *sfoParam = new u8[(size_t)info2.size];
int handle = pspFileSystem.OpenFile(fileDataPath2,FILEACCESS_READ);
pspFileSystem.ReadFile(handle,sfoParam,info2.size);
pspFileSystem.CloseFile(handle);
ParamSFOData sfoFile;
if(sfoFile.ReadSFO(sfoParam,info2.size))
if (sfoFile.ReadSFO(sfoParam, (size_t)info2.size))
{
std::string title = sfoFile.GetValueString("TITLE");
memcpy(saveDataList[realCount].title,title.c_str(),title.size());
@ -485,7 +494,7 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
memcpy(saveDataList[realCount].saveDetail,savedetail.c_str(),savedetail.size());
saveDataList[realCount].saveDetail[savedetail.size()] = 0;
}
delete sfoParam;
delete [] sfoParam;
}
DEBUG_LOG(HLE,"%s Exist",fileDataPath.c_str());
@ -493,7 +502,7 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
}
else
{
if(listEmptyFile)
if (listEmptyFile)
{
saveDataList[realCount].size = 0;
saveDataList[realCount].saveName = saveNameListData[i];
@ -518,7 +527,7 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
std::string fileDataPath = savePath+GetGameName(param)+GetSaveName(param)+"/"+param->fileName;
PSPFileInfo info = pspFileSystem.GetFileInfo(fileDataPath);
if(info.exists)
if (info.exists)
{
saveDataList[0].size = info.size;
saveDataList[0].saveName = GetSaveName(param);
@ -529,15 +538,15 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
// TODO : If icon0 don't exist, need to use icon1 which is a moving icon. Also play sound
std::string fileDataPath2 = savePath+GetGameName(param)+GetSaveName(param)+"/"+icon0Name;
PSPFileInfo info2 = pspFileSystem.GetFileInfo(fileDataPath2);
if(info2.exists)
if (info2.exists)
{
u8* textureDataPNG = new u8[info2.size];
u8 *textureDataPNG = new u8[(size_t)info2.size];
int handle = pspFileSystem.OpenFile(fileDataPath2,FILEACCESS_READ);
pspFileSystem.ReadFile(handle,textureDataPNG,info2.size);
pspFileSystem.CloseFile(handle);
unsigned char* textureData;
unsigned char *textureData;
int w,h;
pngLoadPtr(textureDataPNG, info2.size, &w, &h, &textureData, false);
pngLoadPtr(textureDataPNG, (int)info2.size, &w, &h, &textureData, false);
delete[] textureDataPNG;
u32 texSize = w*h*4;
u32 atlasPtr = kernelMemory.Alloc(texSize, true, "SaveData Icon");
@ -555,14 +564,14 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
// Load info in PARAM.SFO
fileDataPath2 = savePath+GetGameName(param)+GetSaveName(param)+"/"+sfoName;
info2 = pspFileSystem.GetFileInfo(fileDataPath2);
if(info2.exists)
if (info2.exists)
{
u8* sfoParam = new u8[info2.size];
u8 *sfoParam = new u8[(size_t)info2.size];
int handle = pspFileSystem.OpenFile(fileDataPath2,FILEACCESS_READ);
pspFileSystem.ReadFile(handle,sfoParam,info2.size);
pspFileSystem.CloseFile(handle);
ParamSFOData sfoFile;
if(sfoFile.ReadSFO(sfoParam,info2.size))
if (sfoFile.ReadSFO(sfoParam,(size_t)info2.size))
{
std::string title = sfoFile.GetValueString("TITLE");
memcpy(saveDataList[0].title,title.c_str(),title.size());
@ -576,7 +585,7 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
memcpy(saveDataList[0].saveDetail,savedetail.c_str(),savedetail.size());
saveDataList[0].saveDetail[savedetail.size()] = 0;
}
delete sfoParam;
delete [] sfoParam;
}
DEBUG_LOG(HLE,"%s Exist",fileDataPath.c_str());
@ -584,7 +593,7 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
}
else
{
if(listEmptyFile)
if (listEmptyFile)
{
saveDataList[0].size = 0;
saveDataList[0].saveName = GetSaveName(param);
@ -593,8 +602,10 @@ void SavedataParam::SetPspParam(SceUtilitySavedataParam* param)
DEBUG_LOG(HLE,"Don't Exist");
}
saveNameListDataCount = 0;
return 0;
}
}
return 0;
}
SceUtilitySavedataParam* SavedataParam::GetPspParam()
@ -620,8 +631,8 @@ int SavedataParam::GetSelectedSave()
{
return selectedSave;
}
void SavedataParam::SetSelectedSave(int idx)
{
selectedSave = idx;
}

View file

@ -118,9 +118,10 @@ struct SceUtilitySavedataParam
};
// Non native, this one we can reorganize as we like
struct SaveFileInfo
{
int size;
s64 size;
std::string saveName;
int idx;
@ -155,7 +156,7 @@ public:
SavedataParam();
void SetPspParam(SceUtilitySavedataParam* param);
u32 SetPspParam(SceUtilitySavedataParam* param);
SceUtilitySavedataParam* GetPspParam();
int GetFilenameCount();

View file

@ -92,7 +92,7 @@ bool DirectoryFileSystem::DeleteFile(const std::string &filename)
{
std::string fullName = GetLocalPath(filename);
#ifdef _WIN32
return DeleteFile(fullName.c_str()) == TRUE;
return ::DeleteFile(fullName.c_str()) == TRUE;
#else
return 0 == unlink(fullName.c_str());
#endif
@ -183,11 +183,11 @@ size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
EntryMap::iterator iter = entries.find(handle);
if (iter != entries.end())
{
size_t bytesRead;
size_t bytesRead;
#ifdef _WIN32
::ReadFile(iter->second.hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesRead, 0);
#else
bytesRead = fread(pointer, 1, size, iter->second.hFile);
bytesRead = fread(pointer, 1, size, iter->second.hFile);
#endif
return bytesRead;
}

View file

@ -16,10 +16,11 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Globals.h"
#include "Log.h"
#include "Common.h"
#include "ISOFileSystem.h"
#include <cstring>
#include <cstdio>
#include <ctype.h>
const int sectorSize = 2048;
@ -28,11 +29,13 @@ static bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize)
{
if (filename.substr(0, 8) != "/sce_lbn")
return false;
std::string yo = filename;
std::string prev = filename;
filename.erase(0, 10);
sscanf(filename.c_str(), "%08x", sectorStart);
if (sscanf(filename.c_str(), "%08x", sectorStart) != 1)
WARN_LOG(FILESYS, "Invalid LBN reference: %s", prev.c_str());
filename.erase(0, filename.find("_size") + 7);
sscanf(filename.c_str(), "%08x", readSize);
if (sscanf(filename.c_str(), "%08x", readSize) != 1)
WARN_LOG(FILESYS, "Incomplete LBN reference: %s", prev.c_str());
return true;
}
@ -392,6 +395,8 @@ size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
}
else
{
_dbg_assert_msg_(HLE, e.file != 0, "Expecting non-raw fd to have a tree entry.");
//clamp read length
if ((s64)e.seekPos > e.file->size - (s64)size)
{

View file

@ -18,7 +18,7 @@
#include <set>
#include "MetaFileSystem.h"
bool applyPathStringToComponentsVector(std::vector<std::string> &vector, const std::string &pathString)
static bool ApplyPathStringToComponentsVector(std::vector<std::string> &vector, const std::string &pathString)
{
size_t len = pathString.length();
size_t start = 0;
@ -61,9 +61,10 @@ bool applyPathStringToComponentsVector(std::vector<std::string> &vector, const s
/*
* Changes relative paths to absolute, removes ".", "..", and trailing "/"
* "drive:./blah" is absolute (ignore the dot) and "/blah" is relative (because it's missing "drive:")
* babel (and possibly other games) use "/directoryThatDoesNotExist/../directoryThatExists/filename"
*/
bool RealPath(const std::string &currentDirectory, const std::string &inPath, std::string &outPath)
static bool RealPath(const std::string &currentDirectory, const std::string &inPath, std::string &outPath)
{
size_t inLen = inPath.length();
if (inLen == 0)
@ -81,70 +82,27 @@ bool RealPath(const std::string &currentDirectory, const std::string &inPath, st
return true;
}
std::string curDirPrefix;
size_t curDirColon = std::string::npos, curDirLen = currentDirectory.length();
if (curDirLen != 0)
{
curDirColon = currentDirectory.find(':');
if (curDirColon == std::string::npos)
{
DEBUG_LOG(HLE, "RealPath: currentDirectory has no prefix: \"%s\"", currentDirectory.c_str());
}
else
{
if (curDirColon + 1 == curDirLen)
DEBUG_LOG(HLE, "RealPath: currentDirectory is all prefix and no path: \"%s\"", currentDirectory.c_str());
curDirPrefix = currentDirectory.substr(0, curDirColon + 1);
}
}
std::string inPrefix, inAfter;
if (inColon == std::string::npos)
{
inPrefix = curDirPrefix;
inAfter = inPath;
}
else
{
inPrefix = inPath.substr(0, inColon + 1);
inAfter = inPath.substr(inColon + 1);
}
bool relative = (inColon == std::string::npos);
std::string prefix, inAfterColon;
std::vector<std::string> cmpnts; // path components
size_t capacityGuess = inPath.length();
size_t outPathCapacityGuess = inPath.length();
// Special hack for strange root paths.
// Don't understand why this is needed. I don't think the current
// directory should be the root.
if (inAfter.substr(0, 11) == "./PSP_GAME/")
inAfter = inAfter.substr(1);
// Apparently it's okay for relative paths to start with '/'.
// For example, kahoots does sceIoChdir(disc0:/PSP_GAME/USRDIR/)
// then opens paths like "/images/gui". Support this.
if (inColon == std::string::npos && inAfter[0] == '/')
inAfter = inAfter.substr(1);
if (inAfter[0] != '/')
if (relative)
{
size_t curDirLen = currentDirectory.length();
if (curDirLen == 0)
{
ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory is empty", inPath.c_str());
return false;
}
if (curDirColon == std::string::npos || curDirPrefix.length() == 0)
size_t curDirColon = currentDirectory.find(':');
if (curDirColon == std::string::npos)
{
ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" has no prefix", inPath.c_str(), currentDirectory.c_str());
return false;
}
if (inPrefix != curDirPrefix)
WARN_LOG(HLE, "RealPath: inPath \"%s\" is relative, but specifies a different prefix than current directory \"%s\"", inPath.c_str(), currentDirectory.c_str());
if (curDirColon + 1 == curDirLen)
{
ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" is all prefix and no path. Using \"/\" as path for current directory.", inPath.c_str(), currentDirectory.c_str());
@ -152,26 +110,34 @@ bool RealPath(const std::string &currentDirectory, const std::string &inPath, st
else
{
const std::string curDirAfter = currentDirectory.substr(curDirColon + 1);
if (! applyPathStringToComponentsVector(cmpnts, curDirAfter) )
if (! ApplyPathStringToComponentsVector(cmpnts, curDirAfter) )
{
ERROR_LOG(HLE,"RealPath: currentDirectory is not a valid path: \"%s\"", currentDirectory.c_str());
return false;
}
outPathCapacityGuess += curDirLen;
}
capacityGuess += currentDirectory.length();
prefix = currentDirectory.substr(0, curDirColon + 1);
inAfterColon = inPath;
}
else
{
prefix = inPath.substr(0, inColon + 1);
inAfterColon = inPath.substr(inColon + 1);
}
if (! applyPathStringToComponentsVector(cmpnts, inAfter) )
if (! ApplyPathStringToComponentsVector(cmpnts, inAfterColon) )
{
DEBUG_LOG(HLE, "RealPath: inPath is not a valid path: \"%s\"", inPath.c_str());
WARN_LOG(HLE, "RealPath: inPath is not a valid path: \"%s\"", inPath.c_str());
return false;
}
outPath.clear();
outPath.reserve(capacityGuess);
outPath.reserve(outPathCapacityGuess);
outPath.append(inPrefix);
outPath.append(prefix);
size_t numCmpnts = cmpnts.size();
for (size_t i = 0; i < numCmpnts; i++)
@ -194,30 +160,31 @@ IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
return 0;
}
bool MetaFileSystem::MapFilePath(std::string inpath, std::string &outpath, IFileSystem **system)
bool MetaFileSystem::MapFilePath(const std::string &inpath, std::string &outpath, IFileSystem **system)
{
//TODO: implement current directory per thread (NOT per drive)
//DEBUG_LOG(HLE, "MapFilePath: starting with \"%s\"", inpath.c_str());
if ( RealPath(currentDirectory, inpath, inpath) )
std::string realpath;
if ( RealPath(currentDirectory, inpath, realpath) )
{
for (size_t i = 0; i < fileSystems.size(); i++)
{
size_t prefLen = fileSystems[i].prefix.size();
if (fileSystems[i].prefix == inpath.substr(0, prefLen))
if (fileSystems[i].prefix == realpath.substr(0, prefLen))
{
outpath = inpath.substr(prefLen);
outpath = realpath.substr(prefLen);
*system = fileSystems[i].system;
DEBUG_LOG(HLE, "MapFilePath: mapped to prefix: \"%s\", path: \"%s\"", fileSystems[i].prefix.c_str(), outpath.c_str());
DEBUG_LOG(HLE, "MapFilePath: mapped \"%s\" to prefix: \"%s\", path: \"%s\"", inpath.c_str(), fileSystems[i].prefix.c_str(), outpath.c_str());
return true;
}
}
}
DEBUG_LOG(HLE, "MapFilePath: failed, returning false");
DEBUG_LOG(HLE, "MapFilePath: failed mapping \"%s\", returning false", inpath.c_str());
return false;
}

View file

@ -37,7 +37,7 @@ public:
void FreeHandle(u32 handle) {}
IFileSystem *GetHandleOwner(u32 handle);
bool MapFilePath(std::string inpath, std::string &outpath, IFileSystem **system);
bool MapFilePath(const std::string &inpath, std::string &outpath, IFileSystem **system);
std::vector<PSPFileInfo> GetDirListing(std::string path);
u32 OpenFile(std::string filename, FileAccess access);

View file

@ -26,6 +26,7 @@
#include "sceAudio.h"
#include "sceKernelMemory.h"
#include "sceKernelThread.h"
#include "sceKernelInterrupt.h"
#include "../MIPS/MIPSCodeUtils.h"
enum
@ -40,6 +41,8 @@ enum
HLE_AFTER_ALL_CALLBACKS = 0x04,
// Reschedule and process current thread's callbacks after the syscall.
HLE_AFTER_RESCHED_CALLBACKS = 0x08,
// Run interrupts (and probably reschedule) after the syscall.
HLE_AFTER_RUN_INTERRUPTS = 0x10,
};
static std::vector<HLEModule> moduleDB;
@ -208,7 +211,7 @@ void hleCheckCurrentCallbacks()
void hleReSchedule(const char *reason)
{
_dbg_assert_msg_(HLE, reason != 0, "hleReSchedule: Expecting a valid reason.");
_dbg_assert_msg_(HLE, strlen(reason) < 256, "hleReSchedule: Not too long reason.");
_dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "hleReSchedule: Not too long reason.");
hleAfterSyscall |= HLE_AFTER_RESCHED;
@ -231,11 +234,19 @@ void hleReSchedule(bool callbacks, const char *reason)
hleAfterSyscall |= HLE_AFTER_RESCHED_CALLBACKS;
}
void hleRunInterrupts()
{
hleAfterSyscall |= HLE_AFTER_RUN_INTERRUPTS;
}
inline void hleFinishSyscall()
{
if ((hleAfterSyscall & HLE_AFTER_CURRENT_CALLBACKS) != 0)
__KernelForceCallbacks();
if ((hleAfterSyscall & HLE_AFTER_RUN_INTERRUPTS) != 0)
__RunOnePendingInterrupt();
// Rescheduling will also do HLE_AFTER_ALL_CALLBACKS.
if ((hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) != 0)
__KernelReSchedule(true, hleAfterSyscallReschedReason);
@ -253,7 +264,7 @@ void CallSyscall(u32 op)
u32 callno = (op >> 6) & 0xFFFFF; //20 bits
int funcnum = callno & 0xFFF;
int modulenum = (callno & 0xFF000) >> 12;
if (funcnum == 0xfff)
if (funcnum == 0xfff || op == 0xffff)
{
_dbg_assert_msg_(HLE,0,"Unknown syscall");
ERROR_LOG(HLE,"Unknown syscall: Module: %s", moduleDB[modulenum].name);

View file

@ -81,6 +81,8 @@ void hleCheckAllCallbacks();
void hleReSchedule(const char *reason);
// Reschedule and go into a callback processing state after the syscall finishes.
void hleReSchedule(bool callbacks, const char *reason);
// Run interrupts after the syscall finishes.
void hleRunInterrupts();
void HLEInit();
void HLEShutdown();

View file

@ -77,7 +77,7 @@ const HLEFunction UtilsForUser[] =
{0x91E4F6A7, WrapU_V<sceKernelLibcClock>, "sceKernelLibcClock"},
{0x27CC57F0, sceKernelLibcTime, "sceKernelLibcTime"},
{0x71EC4271, sceKernelLibcGettimeofday, "sceKernelLibcGettimeofday"},
{0xBFA98062, 0, "sceKernelDcacheInvalidateRange"},
{0xBFA98062, WrapV_UI<sceKernelDcacheInvalidateRange>, "sceKernelDcacheInvalidateRange"},
{0xC8186A58, 0, "sceKernelUtilsMd5Digest"},
{0x9E5C5086, 0, "sceKernelUtilsMd5BlockInit"},
{0x61E1E525, 0, "sceKernelUtilsMd5BlockUpdate"},
@ -92,8 +92,8 @@ const HLEFunction UtilsForUser[] =
{0x6AD345D7, sceKernelSetGPO, "sceKernelSetGPO"},
{0x79D1C3FA, sceKernelDcacheWritebackAll, "sceKernelDcacheWritebackAll"},
{0xB435DEC5, sceKernelDcacheWritebackInvalidateAll, "sceKernelDcacheWritebackInvalidateAll"},
{0x3EE30821, sceKernelDcacheWritebackRange, "sceKernelDcacheWritebackRange"},
{0x34B9FA9E, sceKernelDcacheWritebackInvalidateRange, "sceKernelDcacheWritebackInvalidateRange"},
{0x3EE30821, WrapV_UI<sceKernelDcacheWritebackRange>, "sceKernelDcacheWritebackRange"},
{0x34B9FA9E, WrapV_UI<sceKernelDcacheWritebackInvalidateRange>, "sceKernelDcacheWritebackInvalidateRange"},
{0xC2DF770E, 0, "sceKernelIcacheInvalidateRange"},
{0x80001C4C, 0, "sceKernelDcacheProbe"},
{0x16641D70, 0, "sceKernelDcacheReadTag"},

View file

@ -186,7 +186,7 @@ void __AudioUpdate()
} else {
// This happens quite a lot. There's still something slightly off
// about the amount of audio we produce.
DEBUG_LOG(HLE, "Audio outbuffer overrun! room = %i / %i", outAudioQueue.room(), outAudioQueue.capacity());
DEBUG_LOG(HLE, "Audio outbuffer overrun! room = %i / %i", outAudioQueue.room(), (u32)outAudioQueue.capacity());
}
}

View file

@ -186,7 +186,7 @@ u32 sceAudioChReserve(u32 channel, u32 sampleCount, u32 format) //.Allocate soun
{
WARN_LOG(HLE, "WARNING: Reserving already reserved channel. Error?");
}
DEBUG_LOG(HLE, "%i = sceAudioChReserve(%i, %i, %i)", channel, sampleCount, format);
DEBUG_LOG(HLE, "sceAudioChReserve(channel = %d, sampleCount = %d, format = %d)", channel, sampleCount, format);
chans[channel].sampleCount = sampleCount;
chans[channel].reserved = true;
@ -288,7 +288,7 @@ u32 sceAudioEnd()
u32 sceAudioOutput2Reserve(u32 sampleCount)
{
ERROR_LOG(HLE,"sceAudioOutput2Reserve(%i)", sampleCount);
DEBUG_LOG(HLE,"sceAudioOutput2Reserve(%i)", sampleCount);
chans[0].sampleCount = sampleCount;
chans[0].reserved = true;
return 0;
@ -305,20 +305,20 @@ u32 sceAudioOutput2OutputBlocking(u32 vol, u32 dataPtr)
u32 sceAudioOutput2ChangeLength(u32 sampleCount)
{
WARN_LOG(HLE,"sceAudioOutput2ChangeLength(%i)", sampleCount);
DEBUG_LOG(HLE,"sceAudioOutput2ChangeLength(%i)", sampleCount);
chans[0].sampleCount = sampleCount;
return 0;
}
u32 sceAudioOutput2GetRestSample()
{
WARN_LOG(HLE,"UNTESTED sceAudioOutput2GetRestSample()");
DEBUG_LOG(HLE,"UNTESTED sceAudioOutput2GetRestSample()");
return chans[0].sampleQueue.size() * 2;
}
u32 sceAudioOutput2Release()
{
WARN_LOG(HLE,"sceAudioOutput2Release()");
DEBUG_LOG(HLE,"sceAudioOutput2Release()");
chans[0].reserved = false;
return 0;
}

View file

@ -145,13 +145,13 @@ void hleEnterVblank(u64 userdata, int cyclesLate)
__DisplayFireVblank();
// Wake up threads waiting for VBlank
for (int i = 0; i < vblankWaitingThreads.size(); i++) {
for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
}
vblankWaitingThreads.clear();
// Trigger VBlank interrupt handlers.
__TriggerInterrupt(PSP_VBLANK_INTR);
__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED, PSP_VBLANK_INTR);
CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount+1);
@ -182,24 +182,27 @@ void hleEnterVblank(u64 userdata, int cyclesLate)
sprintf(stats,
"Frames: %i\n"
"Draw calls: %i\n"
"Draw flushes: %i\n"
"Vertices Transformed: %i\n"
"Textures active: %i\n"
"Textures decoded: %i\n"
"Vertex shaders loaded: %i\n"
"Fragment shaders loaded: %i\n"
"Combined shaders loaded: %i\n",
gpuStats.numFrames,
gpuStats.numDrawCalls,
gpuStats.numFlushes,
gpuStats.numVertsTransformed,
gpuStats.numTextures,
gpuStats.numTexturesDecoded,
gpuStats.numVertexShaders,
gpuStats.numFragmentShaders,
gpuStats.numShaders
);
float zoom = 0.7f / g_Config.iWindowZoom;
float zoom = 0.7f; /// g_Config.iWindowZoom;
PPGeBegin();
PPGeDrawText(stats, 2, 2, 0, zoom, 0x90000000);
PPGeDrawText(stats, 0, 0, 0, zoom);
PPGeDrawText(stats, 0, 0, 0, zoom, 0xFFc0c0c0);
PPGeEnd();
gpuStats.resetFrame();
@ -295,8 +298,13 @@ u32 sceDisplaySetFramebuf()
if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE)
{
// Write immediately to the current framebuffer parameters
framebuf = fbstate;
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
if (topaddr != 0)
{
framebuf = fbstate;
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
}
else
WARN_LOG(HLE, "%s: PSP_DISPLAY_SETBUF_IMMEDIATE without topaddr?", __FUNCTION__);
}
else if (topaddr != 0)
{

View file

@ -235,32 +235,32 @@ int sceFontGetFontInfo(u32 fontHandle, u32 fontInfoPtr)
memset (&fi, 0, sizeof(fi));
if (Memory::IsValidAddress(fontInfoPtr))
{
fi.BPP =4;
fi.BPP = 4;
fi.charMapLength = 255;
// fi.fontStyle =1;
fi.maxGlyphAdvanceXF = 2.0;
fi.maxGlyphAdvanceXI =2;
fi.maxGlyphAdvanceXI = 2;
fi.maxGlyphAdvanceYF = 2.0;
fi.maxGlyphAdvanceYI = 32 << 6;
fi.maxGlyphAscenderF =32 << 6;
fi.maxGlyphAscenderF = 32 << 6;
fi.maxGlyphAscenderI = 32 << 6;
fi.maxGlyphBaseYF= 0.0;
fi.maxGlyphBaseYI=0.0;
fi.maxGlyphDescenderF =0;
fi.maxGlyphDescenderI =0;
fi.maxGlyphBaseYF = 0.0;
fi.maxGlyphBaseYI = 0;
fi.maxGlyphDescenderF = 0;
fi.maxGlyphDescenderI = 0;
fi.maxGlyphHeight = 32;
fi.maxGlyphHeightF= 32;
fi.maxGlyphHeightF = 32;
fi.maxGlyphHeightI = 32;
fi.maxGlyphLeftXF= 0;
fi.maxGlyphLeftXF = 0;
fi.maxGlyphLeftXI = 0;
fi.maxGlyphTopYF =0;
fi.maxGlyphTopYF = 0;
fi.maxGlyphTopYI = 0;
fi.maxGlyphWidth =32;
fi.maxGlyphWidth = 32;
fi.maxGlyphWidthF = 32;
fi.maxGlyphWidthI= 32;
fi.maxGlyphWidthI = 32;
fi.minGlyphCenterXF = 16;
fi.minGlyphCenterXI= 16;
fi.shadowMapLength=0;
fi.minGlyphCenterXI = 16;
fi.shadowMapLength = 0;
Memory::WriteStruct(fontInfoPtr, &fi);
}

View file

@ -166,7 +166,7 @@ void sceGeUnsetCallback(u32 cbID) {
u32 sceGeSaveContext(u32 ctxAddr)
{
DEBUG_LOG(HLE, "sceGeSaveContext(%08x)", ctxAddr);
gpu->Flush();
if (sizeof(gstate) > 512 * 4)
{
ERROR_LOG(HLE, "AARGH! sizeof(gstate) has grown too large!");
@ -187,6 +187,7 @@ u32 sceGeSaveContext(u32 ctxAddr)
u32 sceGeRestoreContext(u32 ctxAddr)
{
DEBUG_LOG(HLE, "sceGeRestoreContext(%08x)", ctxAddr);
gpu->Flush();
if (sizeof(gstate) > 512 * 4)
{
@ -225,12 +226,12 @@ const HLEFunction sceGe_user[] =
{0xE0D68148,&WrapV_UU<sceGeListUpdateStallAddr>, "sceGeListUpdateStallAddr"},
{0x03444EB4,&WrapI_UU<sceGeListSync>, "sceGeListSync"},
{0xB287BD61,&WrapU_U<sceGeDrawSync>, "sceGeDrawSync"},
{0xB448EC0D,&WrapV_U<sceGeBreak>, "sceGeBreak"},
{0xB448EC0D,&WrapV_U<sceGeBreak>, "sceGeBreak"},
{0x4C06E472,sceGeContinue, "sceGeContinue"},
{0xA4FC06A4,&WrapU_U<sceGeSetCallback>, "sceGeSetCallback"},
{0x05DB22CE,&WrapV_U<sceGeUnsetCallback>, "sceGeUnsetCallback"},
{0x1F6752AD,&WrapU_V<sceGeEdramGetSize>, "sceGeEdramGetSize"},
{0xB77905EA,&WrapU_I<sceGeEdramSetAddrTranslation>,"sceGeEdramSetAddrTranslation"},
{0xB77905EA,&WrapU_I<sceGeEdramSetAddrTranslation>,"sceGeEdramSetAddrTranslation"},
{0xDC93CFEF,0,"sceGeGetCmd"},
{0x57C8945B,&sceGeGetMtx,"sceGeGetMtx"},
{0x438A385A,&WrapU_U<sceGeSaveContext>,"sceGeSaveContext"},

View file

@ -192,7 +192,6 @@ void __IoInit() {
}
void __IoShutdown() {
}
u32 sceIoAssign(const char *aliasname, const char *physname, const char *devname, u32 flag) {
@ -245,13 +244,16 @@ void __IoGetStat(SceIoStat *stat, PSPFileInfo &info) {
u32 sceIoGetstat(const char *filename, u32 addr) {
SceIoStat stat;
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
__IoGetStat(&stat, info);
Memory::WriteStruct(addr, &stat);
DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr,
if (info.exists) {
__IoGetStat(&stat, info);
Memory::WriteStruct(addr, &stat);
DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr,
info.startSector);
return 0;
return 0;
} else {
DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : FILE NOT FOUND", filename, addr);
return SCE_KERNEL_ERROR_NOFILE;
}
}
//Not sure about wrapping it or not, since the log seems to take the address of the data var
@ -428,11 +430,11 @@ void sceIoSync() {
}
struct DeviceSize {
u32 maxClusters;
u32 freeClusters;
u32 maxSectors;
u32 sectorSize;
u32 sectorsPerCluster;
u32 totalClusters;
u32 freeClusters;
u32 sectorCount;
};
u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, int outLen) {
@ -494,9 +496,16 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr,
}
break;
case 0x02025806: // Memory stick inserted?
case 0x02025801: // Memstick Driver status?
if (Memory::IsValidAddress(outPtr)) {
if (Memory::IsValidAddress(outPtr) && outLen >= 4) {
Memory::Write_U32(4, outPtr); // JPSCP: The right return value is 4 for some reason
return 0;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
}
case 0x02025806: // Memory stick inserted?
if (Memory::IsValidAddress(outPtr) && outLen >= 4) {
Memory::Write_U32(1, outPtr);
return 0;
} else {
@ -505,20 +514,23 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr,
case 0x02425818: // Get memstick size etc
// Pretend we have a 2GB memory stick.
if (Memory::IsValidAddress(argAddr)) { // "Should" be outPtr but isn't
if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // "Should" be outPtr but isn't
u32 pointer = Memory::Read_U32(argAddr);
u64 totalSize = (u32)2 * 1024 * 1024 * 1024;
u64 freeSize = 1 * 1024 * 1024 * 1024;
u32 sectorSize = 0x200;
u32 memStickSectorSize = 32 * 1024;
u32 sectorCount = memStickSectorSize / sectorSize;
u64 freeSize = 1 * 1024 * 1024 * 1024;
DeviceSize deviceSize;
deviceSize.maxSectors = 512;
deviceSize.sectorSize = 0x200;
deviceSize.sectorsPerCluster = 0x08;
deviceSize.totalClusters = (u32)((totalSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.freeClusters = (u32)((freeSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount));
deviceSize.freeClusters = deviceSize.maxClusters;
deviceSize.maxSectors = deviceSize.maxClusters;
deviceSize.sectorSize = sectorSize;
deviceSize.sectorCount = sectorCount;
Memory::WriteStruct(pointer, &deviceSize);
DEBUG_LOG(HLE, "Returned memstick size: maxSectors=%i", deviceSize.maxSectors);
return 0;
} else {
ERROR_LOG(HLE, "memstick size query: bad params");
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
}
}
@ -573,17 +585,18 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr,
case 0x02425818: // Get memstick size etc
// Pretend we have a 2GB memory stick.
{
if (Memory::IsValidAddress(argAddr)) { // "Should" be outPtr but isn't
if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // NOTE: not outPtr
u32 pointer = Memory::Read_U32(argAddr);
u64 totalSize = (u32)2 * 1024 * 1024 * 1024;
u64 freeSize = 1 * 1024 * 1024 * 1024;
u32 sectorSize = 0x200;
u32 memStickSectorSize = 32 * 1024;
u32 sectorCount = memStickSectorSize / sectorSize;
u64 freeSize = 1 * 1024 * 1024 * 1024;
DeviceSize deviceSize;
deviceSize.maxSectors = 512;
deviceSize.sectorSize = 0x200;
deviceSize.sectorsPerCluster = 0x08;
deviceSize.totalClusters = (u32)((totalSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.freeClusters = (u32)((freeSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount));
deviceSize.freeClusters = deviceSize.maxClusters;
deviceSize.maxSectors = deviceSize.maxClusters;
deviceSize.sectorSize = sectorSize;
deviceSize.sectorCount = sectorCount;
Memory::WriteStruct(pointer, &deviceSize);
return 0;
} else {
@ -869,8 +882,29 @@ u32 sceIoDclose(int id) {
u32 sceIoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen)
{
ERROR_LOG(HLE, "UNIMPL 0=sceIoIoctrl id: %08x, cmd %08x, indataPtr %08x, inlen %08x, outdataPtr %08x, outLen %08x", id,cmd,indataPtr,inlen,outdataPtr,outlen);
return 0;
ERROR_LOG(HLE, "UNIMPL PARTIAL 0=sceIoIoctl id: %08x, cmd %08x, indataPtr %08x, inlen %08x, outdataPtr %08x, outLen %08x", id,cmd,indataPtr,inlen,outdataPtr,outlen);
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (error) {
return error;
}
//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
// 0000000
switch (cmd) {
case 0x04100001: // Define decryption key (amctrl.prx DRM)
if (Memory::IsValidAddress(indataPtr) && inlen == 16) {
u8 keybuf[16];
memcpy(keybuf, Memory::GetPointer(indataPtr), 16);
ERROR_LOG(HLE, "PGD DRM not yet supported, sorry.");
}
break;
}
return 0;
}
const HLEFunction IoFileMgrForUser[] = {

View file

@ -25,6 +25,8 @@
#include "../PSPLoaders.h"
#include "../../Core/CoreTiming.h"
#include "../../Core/System.h"
#include "../../GPU/GPUInterface.h"
#include "../../GPU/GPUState.h"
#include "__sceAudio.h"
@ -183,18 +185,25 @@ void sceKernelGetGPI()
}
// Don't even log these, they're spammy and we probably won't
// need to emulate them.
// need to emulate them. Might be useful for invalidating cached
// textures, and in the future display lists, in some cases though.
void sceKernelDcacheInvalidateRange(u32 addr, int size)
{
gpu->InvalidateCache(addr, size);
}
void sceKernelDcacheWritebackAll()
{
}
void sceKernelDcacheWritebackRange()
void sceKernelDcacheWritebackRange(u32 addr, int size)
{
}
void sceKernelDcacheWritebackInvalidateRange()
void sceKernelDcacheWritebackInvalidateRange(u32 addr, int size)
{
gpu->InvalidateCache(addr, size);
}
void sceKernelDcacheWritebackInvalidateAll()
{
gpu->InvalidateCache(0, -1);
}
KernelObjectPool kernelObjects;
@ -261,12 +270,12 @@ void KernelObjectPool::List()
if (pool[i])
{
pool[i]->GetQuickInfo(buffer,256);
INFO_LOG(HLE, "KO %i: %s \"%s\": %s", i + handleOffset, pool[i]->GetTypeName(), pool[i]->GetName(), buffer);
}
else
{
strcpy(buffer,"WTF? Zero Pointer");
}
INFO_LOG(HLE, "KO %i: %s \"%s\": %s", i + handleOffset, pool[i]->GetTypeName(), pool[i]->GetName(), buffer);
}
}
}
@ -412,10 +421,10 @@ const HLEFunction ThreadManForUser[] =
{0x64D4540E,0,"sceKernelReferThreadProfiler"},
//Fifa Street 2 uses alarms
{0x6652b8ca,sceKernelSetAlarm,"sceKernelSetAlarm"},
{0xB2C25152,sceKernelSetSysClockAlarm,"sceKernelSetSysClockAlarm"},
{0x7e65b999,sceKernelCancelAlarm,"sceKernelCancelAlarm"},
{0xDAA3F564,sceKernelReferAlarmStatus,"sceKernelReferAlarmStatus"},
{0x6652b8ca,WrapI_UUU<sceKernelSetAlarm>,"sceKernelSetAlarm"},
{0xB2C25152,WrapI_UUU<sceKernelSetSysClockAlarm>,"sceKernelSetSysClockAlarm"},
{0x7e65b999,WrapI_I<sceKernelCancelAlarm>,"sceKernelCancelAlarm"},
{0xDAA3F564,WrapI_IU<sceKernelReferAlarmStatus>,"sceKernelReferAlarmStatus"},
{0xba6b92e2,sceKernelSysClock2USec,"sceKernelSysClock2USec"},
{0x110DEC9A,0,"sceKernelUSec2SysClock"},

View file

@ -283,9 +283,10 @@ void sceKernelFindModuleByName();
void sceKernelSetGPO();
void sceKernelGetGPI();
void sceKernelDcacheInvalidateRange(u32 addr, int size);
void sceKernelDcacheWritebackAll();
void sceKernelDcacheWritebackRange();
void sceKernelDcacheWritebackInvalidateRange();
void sceKernelDcacheWritebackRange(u32 addr, int size);
void sceKernelDcacheWritebackInvalidateRange(u32 addr, int size);
void sceKernelDcacheWritebackInvalidateAll();
void sceKernelGetThreadStackFreeSize();
void sceKernelIcacheInvalidateAll();

View file

@ -17,28 +17,173 @@
#include "sceKernel.h"
#include "sceKernelAlarm.h"
#include "sceKernelInterrupt.h"
#include "HLE.h"
#include "../../Core/CoreTiming.h"
void sceKernelSetAlarm()
const int NATIVEALARM_SIZE = 20;
struct NativeAlarm
{
ERROR_LOG(HLE,"UNIMPL sceKernelSetAlarm");
RETURN(-1);
SceSize size;
u64 schedule;
u32 handlerPtr;
u32 commonPtr;
};
struct Alarm : public KernelObject
{
const char *GetName() {return "[Alarm]";}
const char *GetTypeName() {return "Alarm";}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_ALMID; }
int GetIDType() const { return SCE_KERNEL_TMID_Alarm; }
NativeAlarm alm;
};
void __KernelScheduleAlarm(Alarm *alarm, u64 ticks);
class AlarmIntrHandler : public SubIntrHandler
{
public:
AlarmIntrHandler(Alarm *alarm)
{
this->alarm = alarm;
handlerAddress = alarm->alm.handlerPtr;
enabled = true;
}
virtual void copyArgsToCPU(const PendingInterrupt &pend)
{
SubIntrHandler::copyArgsToCPU(pend);
currentMIPS->r[MIPS_REG_A0] = alarm->alm.commonPtr;
}
virtual void handleResult(int result)
{
// A non-zero result means to reschedule.
if (result > 0)
__KernelScheduleAlarm(alarm, (u64) usToCycles(result));
else
{
if (result < 0)
WARN_LOG(HLE, "Alarm requested reschedule for negative value %u, ignoring", (unsigned) result);
// Delete the alarm if it's not rescheduled.
__ReleaseSubInterruptHandler(PSP_SYSTIMER0_INTR, alarm->GetUID());
kernelObjects.Destroy<Alarm>(alarm->GetUID());
}
}
Alarm *alarm;
};
bool alarmInitComplete = false;
int alarmTimer = 0;
void __KernelTriggerAlarm(u64 userdata, int cyclesLate);
void __KernelAlarmInit()
{
alarmTimer = CoreTiming::RegisterEvent("Alarm", __KernelTriggerAlarm);
alarmInitComplete = true;
}
void sceKernelSetSysClockAlarm()
void __KernelTriggerAlarm(u64 userdata, int cyclesLate)
{
ERROR_LOG(HLE,"UNIMPL sceKernelSetSysClockAlarm");
RETURN(-1);
int uid = (int) userdata;
u32 error;
Alarm *alarm = kernelObjects.Get<Alarm>(uid, error);
if (alarm)
__TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_SYSTIMER0_INTR, uid);
}
void sceKernelCancelAlarm()
void __KernelScheduleAlarm(Alarm *alarm, u64 ticks)
{
ERROR_LOG(HLE,"UNIMPL sceKernelCancelAlarm");
RETURN(-1);
alarm->alm.schedule = (CoreTiming::GetTicks() + ticks) / (u64) CoreTiming::GetClockFrequencyMHz();
CoreTiming::ScheduleEvent((int) ticks, alarmTimer, alarm->GetUID());
}
void sceKernelReferAlarmStatus()
SceUID __KernelSetAlarm(u64 ticks, u32 handlerPtr, u32 commonPtr)
{
ERROR_LOG(HLE,"UNIMPL sceKernelReferAlarmStatus");
RETURN(-1);
if (!alarmInitComplete)
__KernelAlarmInit();
if (!Memory::IsValidAddress(handlerPtr))
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
Alarm *alarm = new Alarm;
SceUID uid = kernelObjects.Create(alarm);
alarm->alm.size = NATIVEALARM_SIZE;
alarm->alm.handlerPtr = handlerPtr;
alarm->alm.commonPtr = commonPtr;
u32 error = __RegisterSubInterruptHandler(PSP_SYSTIMER0_INTR, uid, new AlarmIntrHandler(alarm));
if (error != 0)
return error;
__KernelScheduleAlarm(alarm, ticks);
return uid;
}
SceUID sceKernelSetAlarm(SceUInt micro, u32 handlerPtr, u32 commonPtr)
{
DEBUG_LOG(HLE, "sceKernelSetAlarm(%d, %08x, %08x)", micro, handlerPtr, commonPtr);
return __KernelSetAlarm(usToCycles((u64) micro), handlerPtr, commonPtr);
}
SceUID sceKernelSetSysClockAlarm(u32 microPtr, u32 handlerPtr, u32 commonPtr)
{
u64 micro;
if (Memory::IsValidAddress(microPtr))
micro = Memory::Read_U64(microPtr);
else
return -1;
DEBUG_LOG(HLE, "sceKernelSetSysClockAlarm(%lld, %08x, %08x)", micro, handlerPtr, commonPtr);
return __KernelSetAlarm(usToCycles(micro), handlerPtr, commonPtr);
}
int sceKernelCancelAlarm(SceUID uid)
{
DEBUG_LOG(HLE, "sceKernelCancelAlarm(%08x)", uid);
CoreTiming::UnscheduleEvent(alarmTimer, uid);
__ReleaseSubInterruptHandler(PSP_SYSTIMER0_INTR, uid);
return kernelObjects.Destroy<Alarm>(uid);
}
int sceKernelReferAlarmStatus(SceUID uid, u32 infoPtr)
{
u32 error;
Alarm *alarm = kernelObjects.Get<Alarm>(uid, error);
if (!alarm)
{
ERROR_LOG(HLE, "sceKernelReferAlarmStatus(%08x, %08x): invalid alarm", uid, infoPtr);
return error;
}
DEBUG_LOG(HLE, "sceKernelReferAlarmStatus(%08x, %08x)", uid, infoPtr);
if (!Memory::IsValidAddress(infoPtr))
return -1;
u32 size = Memory::Read_U32(infoPtr);
// Alarms actually respect size and write (kinda) what it can hold.
if (size > 0)
Memory::Write_U32(alarm->alm.size, infoPtr);
if (size > 4)
Memory::Write_U64(alarm->alm.schedule, infoPtr + 4);
if (size > 12)
Memory::Write_U32(alarm->alm.handlerPtr, infoPtr + 12);
if (size > 16)
Memory::Write_U32(alarm->alm.commonPtr, infoPtr + 16);
return 0;
}

View file

@ -17,7 +17,7 @@
#pragma once
void sceKernelSetAlarm();
void sceKernelSetSysClockAlarm();
void sceKernelCancelAlarm();
void sceKernelReferAlarmStatus();
SceUID sceKernelSetAlarm(SceUInt clock, u32 handlerPtr, u32 commonPtr);
SceUID sceKernelSetSysClockAlarm(u32 sysClockPtr, u32 handlerPtr, u32 commonPtr);
int sceKernelCancelAlarm(SceUID uid);
int sceKernelReferAlarmStatus(SceUID uid, u32 infoPtr);

View file

@ -397,7 +397,7 @@ int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti
th.bits = bits;
th.wait = wait;
// If < 5ms, sometimes hardware doesn't write this, but it's unpredictable.
th.outAddr = timeout == 0 ? NULL : outBitsPtr;
th.outAddr = timeout == 0 ? 0 : outBitsPtr;
e->waitingThreads.push_back(th);
__KernelSetEventFlagTimeout(e, timeoutPtr);
@ -450,7 +450,7 @@ int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32
th.bits = bits;
th.wait = wait;
// If < 5ms, sometimes hardware doesn't write this, but it's unpredictable.
th.outAddr = timeout == 0 ? NULL : outBitsPtr;
th.outAddr = timeout == 0 ? 0 : outBitsPtr;
e->waitingThreads.push_back(th);
__KernelSetEventFlagTimeout(e, timeoutPtr);

View file

@ -35,9 +35,9 @@ struct Interrupt
// Yeah, this bit is a bit silly.
static int interruptsEnabled = 1;
static bool inInterrupt;
void __InterruptsInit()
{
interruptsEnabled = 1;
@ -89,6 +89,7 @@ void sceKernelCpuResumeIntr(u32 enable)
if (enable)
{
__EnableInterrupts();
hleRunInterrupts();
}
else
{
@ -127,74 +128,19 @@ bool __CanExecuteInterrupt()
return !inInterrupt;
}
class AllegrexInterruptHandler;
struct PendingInterrupt {
AllegrexInterruptHandler *handler;
int arg;
bool hasArg;
};
class AllegrexInterruptHandler
{
public:
virtual ~AllegrexInterruptHandler() {}
virtual void copyArgsToCPU(const PendingInterrupt &pend) = 0;
virtual void queueUp() = 0;
virtual void queueUpWithArg(int arg) = 0;
};
std::list<PendingInterrupt> pendingInterrupts;
class SubIntrHandler : public AllegrexInterruptHandler
{
public:
SubIntrHandler() {}
virtual void queueUp()
{
if (!enabled)
return;
PendingInterrupt pend;
pend.handler = this;
pend.hasArg = false;
pendingInterrupts.push_back(pend);
}
virtual void queueUpWithArg(int arg)
{
if (!enabled)
return;
PendingInterrupt pend;
pend.handler = this;
pend.arg = arg;
pend.hasArg = true;
pendingInterrupts.push_back(pend);
}
virtual void copyArgsToCPU(const PendingInterrupt &pend)
{
DEBUG_LOG(CPU, "Entering interrupt handler %08x", handlerAddress);
currentMIPS->pc = handlerAddress;
currentMIPS->r[MIPS_REG_A0] = pend.hasArg ? pend.arg : number;
currentMIPS->r[MIPS_REG_A1] = handlerArg;
// RA is already taken care of
}
bool enabled;
int number;
u32 handlerAddress;
u32 handlerArg;
};
class IntrHandler {
public:
void add(int subIntrNum, SubIntrHandler handler)
void add(int subIntrNum, SubIntrHandler *handler)
{
subIntrHandlers[subIntrNum] = handler;
}
void remove(int subIntrNum)
{
subIntrHandlers.erase(subIntrNum);
if (has(subIntrNum))
{
delete subIntrHandlers[subIntrNum];
subIntrHandlers.erase(subIntrNum);
}
}
bool has(int subIntrNum) const
{
@ -203,7 +149,7 @@ public:
SubIntrHandler *get(int subIntrNum)
{
if (has(subIntrNum))
return &subIntrHandlers[subIntrNum];
return subIntrHandlers[subIntrNum];
else
return 0;
// what to do, what to do...
@ -213,10 +159,10 @@ public:
{
// Just call execute on all the subintr handlers for this interrupt.
// They will get queued up.
for (std::map<int, SubIntrHandler>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
for (std::map<int, SubIntrHandler *>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
{
if (subintr == -1 || iter->first == subintr)
iter->second.queueUp();
iter->second->queueUp();
}
}
@ -224,18 +170,17 @@ public:
{
// Just call execute on all the subintr handlers for this interrupt.
// They will get queued up.
for (std::map<int, SubIntrHandler>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
for (std::map<int, SubIntrHandler *>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
{
if (subintr == -1 || iter->first == subintr)
iter->second.queueUpWithArg(arg);
iter->second->queueUpWithArg(arg);
}
}
private:
std::map<int, SubIntrHandler> subIntrHandlers;
std::map<int, SubIntrHandler *> subIntrHandlers;
};
class InterruptState
{
public:
@ -261,16 +206,52 @@ public:
InterruptState intState;
IntrHandler intrHandlers[PSP_NUMBER_INTERRUPTS];
std::list<PendingInterrupt> pendingInterrupts;
// http://forums.ps2dev.org/viewtopic.php?t=5687
// http://www.google.se/url?sa=t&rct=j&q=&esrc=s&source=web&cd=7&ved=0CFYQFjAG&url=http%3A%2F%2Fdev.psnpt.com%2Fredmine%2Fprojects%2Fuofw%2Frepository%2Frevisions%2F65%2Fraw%2Ftrunk%2Finclude%2Finterruptman.h&ei=J4pCUKvyK4nl4QSu-YC4Cg&usg=AFQjCNFxJcgzQnv6dK7aiQlht_BM9grfQQ&sig2=GGk5QUEWI6qouYDoyE07YQ
void SubIntrHandler::queueUp()
{
if (!enabled)
return;
PendingInterrupt pend;
pend.handler = this;
pend.hasArg = false;
pend.intr = intrNumber;
pend.subintr = number;
pendingInterrupts.push_back(pend);
};
void SubIntrHandler::queueUpWithArg(int arg)
{
if (!enabled)
return;
PendingInterrupt pend;
pend.handler = this;
pend.arg = arg;
pend.hasArg = true;
pend.intr = intrNumber;
pend.subintr = number;
pendingInterrupts.push_back(pend);
}
void SubIntrHandler::copyArgsToCPU(const PendingInterrupt &pend)
{
DEBUG_LOG(CPU, "Entering interrupt handler %08x", handlerAddress);
currentMIPS->pc = handlerAddress;
currentMIPS->r[MIPS_REG_A0] = pend.hasArg ? pend.arg : number;
currentMIPS->r[MIPS_REG_A1] = handlerArg;
// RA is already taken care of
}
// Returns true if anything was executed.
bool __RunOnePendingInterrupt()
{
if (inInterrupt)
if (inInterrupt || !interruptsEnabled)
{
// Already in an interrupt! We'll keep going when it's done.
return false;
@ -283,7 +264,6 @@ bool __RunOnePendingInterrupt()
__KernelSwitchOffThread("interrupt");
PendingInterrupt pend = pendingInterrupts.front();
pendingInterrupts.pop_front();
intState.save();
pend.handler->copyArgsToCPU(pend);
@ -298,37 +278,81 @@ bool __RunOnePendingInterrupt()
}
}
void __TriggerInterrupt(PSPInterrupt intno, int subintr)
void __TriggerRunInterrupts(int type)
{
intrHandlers[intno].queueUp(subintr);
DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i (%i in queue)", intno, subintr, pendingInterrupts.size());
if (!inInterrupt)
__RunOnePendingInterrupt();
// If interrupts aren't enabled, we run them later.
if (interruptsEnabled && !inInterrupt)
{
if ((type & PSP_INTR_HLE) != 0)
hleRunInterrupts();
else
__RunOnePendingInterrupt();
}
}
void __TriggerInterruptWithArg(PSPInterrupt intno, int subintr, int arg)
void __TriggerInterrupt(int type, PSPInterrupt intno, int subintr)
{
intrHandlers[intno].queueUpWithArg(subintr, arg);
DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i with arg %i (%i in queue)", intno, subintr, arg, pendingInterrupts.size());
if (!inInterrupt)
__RunOnePendingInterrupt();
if (interruptsEnabled || (type & PSP_INTR_ONLY_IF_ENABLED) == 0)
{
intrHandlers[intno].queueUp(subintr);
DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i (%i in queue)", intno, subintr, (u32)pendingInterrupts.size());
__TriggerRunInterrupts(type);
}
}
void __TriggerInterruptWithArg(int type, PSPInterrupt intno, int subintr, int arg)
{
if (interruptsEnabled || (type & PSP_INTR_ONLY_IF_ENABLED) == 0)
{
intrHandlers[intno].queueUpWithArg(subintr, arg);
DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i with arg %i (%i in queue)", intno, subintr, arg,
(u32)pendingInterrupts.size());
__TriggerRunInterrupts(type);
}
}
void __KernelReturnFromInterrupt()
{
DEBUG_LOG(CPU, "Left interrupt handler at %08x", currentMIPS->pc);
inInterrupt = false;
// This is what we just ran.
PendingInterrupt pend = pendingInterrupts.front();
pendingInterrupts.pop_front();
pend.handler->handleResult(currentMIPS->r[MIPS_REG_V0]);
// Restore context after running the interrupt.
intState.restore();
// All should now be back to normal, including PC.
// Alright, let's see if there's any more interrupts queued...
if (!__RunOnePendingInterrupt())
__KernelReSchedule("return from interrupt");
}
u32 __RegisterSubInterruptHandler(u32 intrNumber, u32 subIntrNumber, SubIntrHandler *subIntrHandler)
{
subIntrHandler->number = subIntrNumber;
subIntrHandler->intrNumber = intrNumber;
intrHandlers[intrNumber].add(subIntrNumber, subIntrHandler);
return 0;
}
u32 __ReleaseSubInterruptHandler(u32 intrNumber, u32 subIntrNumber)
{
if (!intrHandlers[intrNumber].has(subIntrNumber))
return -1;
for (std::list<PendingInterrupt>::iterator it = pendingInterrupts.begin(); it != pendingInterrupts.end(); )
{
// Hmmm...
//__KernelReSchedule("return from interrupt");
if (it->intr == intrNumber && it->subintr == subIntrNumber)
pendingInterrupts.erase(it++);
else
++it;
}
intrHandlers[intrNumber].remove(subIntrNumber);
return 0;
}
u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg)
@ -338,29 +362,21 @@ u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handl
if (intrNumber >= PSP_NUMBER_INTERRUPTS)
return -1;
SubIntrHandler subIntrHandler;
subIntrHandler.number = subIntrNumber;
subIntrHandler.enabled = false;
subIntrHandler.handlerAddress = handler;
subIntrHandler.handlerArg = handlerArg;
intrHandlers[intrNumber].add(subIntrNumber, subIntrHandler);
return 0;
SubIntrHandler *subIntrHandler = new SubIntrHandler();
subIntrHandler->enabled = false;
subIntrHandler->handlerAddress = handler;
subIntrHandler->handlerArg = handlerArg;
return __RegisterSubInterruptHandler(intrNumber, subIntrNumber, subIntrHandler);
}
u32 sceKernelReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber)
{
DEBUG_LOG(HLE,"sceKernelReleaseSubIntrHandler(%i, %i)", PARAM(0), PARAM(1));
// TODO: should check if it's pending and remove it from pending list! (although that's probably unlikely)
if (intrNumber >= PSP_NUMBER_INTERRUPTS)
return -1;
if (!intrHandlers[intrNumber].has(subIntrNumber))
return -1;
intrHandlers[intrNumber].remove(subIntrNumber);
return 0;
return __ReleaseSubInterruptHandler(intrNumber, subIntrNumber);
}
u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber)

View file

@ -54,14 +54,63 @@ enum PSPGeSubInterrupts {
PSP_GE_SUBINTR_SIGNAL = 15
};
enum PSPInterruptTriggerType {
// Trigger immediately, for CoreTiming events.
PSP_INTR_IMMEDIATE = 0x0,
// Trigger after the HLE syscall finishes.
PSP_INTR_HLE = 0x1,
// Only trigger (as above) if interrupts are not suspended.
PSP_INTR_ONLY_IF_ENABLED = 0x2,
};
class AllegrexInterruptHandler;
struct PendingInterrupt {
AllegrexInterruptHandler *handler;
int arg;
bool hasArg;
int intr;
int subintr;
};
class AllegrexInterruptHandler
{
public:
virtual ~AllegrexInterruptHandler() {}
virtual void copyArgsToCPU(const PendingInterrupt &pend) = 0;
virtual void queueUp() = 0;
virtual void queueUpWithArg(int arg) = 0;
virtual void handleResult(int result) = 0;
};
class SubIntrHandler : public AllegrexInterruptHandler
{
public:
SubIntrHandler() {}
virtual void queueUp();
virtual void queueUpWithArg(int arg);
virtual void copyArgsToCPU(const PendingInterrupt &pend);
virtual void handleResult(int result) {}
bool enabled;
int intrNumber;
int number;
u32 handlerAddress;
u32 handlerArg;
};
bool __IsInInterrupt();
void __InterruptsInit();
void __InterruptsShutdown();
void __TriggerInterrupt(PSPInterrupt intno, int subInterrupts = -1);
void __TriggerInterruptWithArg(PSPInterrupt intno, int subintr, int arg); // For GE "callbacks"
void __TriggerInterrupt(int type, PSPInterrupt intno, int subInterrupts = -1);
void __TriggerInterruptWithArg(int type, PSPInterrupt intno, int subintr, int arg); // For GE "callbacks"
bool __RunOnePendingInterrupt();
void __KernelReturnFromInterrupt();
u32 __RegisterSubInterruptHandler(u32 intrNumber, u32 subIntrNumber, SubIntrHandler *subIntrHandler);
u32 __ReleaseSubInterruptHandler(u32 intrNumber, u32 subIntrNumber);
u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg);
u32 sceKernelReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber);
u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber);

View file

@ -448,13 +448,13 @@ bool __KernelLoadPBP(const char *filename, std::string *error_string)
in.seekg(offsets[5]);
//in.read((char*)&id,4);
{
u8 *temp = new u8[1024*1024*8];
in.read((char*)temp, 1024*1024*8);
Module *module = __KernelLoadELFFromPtr(temp, PSP_GetDefaultLoadAddress(), error_string);
u8 *elftemp = new u8[1024*1024*8];
in.read((char*)elftemp, 1024*1024*8);
Module *module = __KernelLoadELFFromPtr(elftemp, PSP_GetDefaultLoadAddress(), error_string);
if (!module)
return false;
mipsr4k.pc = module->nm.entry_addr;
delete [] temp;
delete [] elftemp;
}
in.close();
return true;
@ -612,7 +612,7 @@ u32 sceKernelLoadModule(const char *name, u32 flags)
// TODO: Use position to decide whether to load high or low
if (PARAM(2))
{
SceKernelLMOption *lmoption = (SceKernelLMOption *)Memory::GetPointer(PARAM(2));
lmoption = (SceKernelLMOption *)Memory::GetPointer(PARAM(2));
}

View file

@ -98,7 +98,7 @@ struct MsgPipe : public KernelObject
if (sendWaitingThreads.empty())
return;
MsgPipeWaitingThread *thread = &sendWaitingThreads.front();
if (nmp.freeSize >= thread->bufSize)
if ((u32) nmp.freeSize >= thread->bufSize)
{
// Put all the data to the buffer
memcpy(buffer + (nmp.bufSize - nmp.freeSize), Memory::GetPointer(thread->bufAddr), thread->bufSize);
@ -126,7 +126,7 @@ struct MsgPipe : public KernelObject
if (receiveWaitingThreads.empty())
return;
MsgPipeWaitingThread *thread = &receiveWaitingThreads.front();
if (nmp.bufSize - nmp.freeSize >= thread->bufSize)
if ((u32) nmp.bufSize - (u32) nmp.freeSize >= thread->bufSize)
{
// Get the needed data from the buffer
Memory::Memcpy(thread->bufAddr, buffer, thread->bufSize);
@ -271,7 +271,7 @@ void __KernelSendMsgPipe(MsgPipe *m, u32 sendBufAddr, u32 sendSize, int waitMode
}
else
{
if (sendSize <= m->nmp.freeSize)
if (sendSize <= (u32) m->nmp.freeSize)
{
memcpy(m->buffer + (m->nmp.bufSize - m->nmp.freeSize), Memory::GetPointer(sendBufAddr), sendSize);
m->nmp.freeSize -= sendSize;
@ -445,7 +445,7 @@ void __KernelReceiveMsgPipe(MsgPipe *m, u32 receiveBufAddr, u32 receiveSize, int
else
{
// Enough data in the buffer: copy just the needed amount of data
if (receiveSize <= m->nmp.bufSize - m->nmp.freeSize)
if (receiveSize <= (u32) m->nmp.bufSize - (u32) m->nmp.freeSize)
{
Memory::Memcpy(receiveBufAddr, m->buffer, receiveSize);
m->nmp.freeSize += receiveSize;

View file

@ -569,7 +569,7 @@ u32 sceKernelGetThreadmanIdList(u32 type, u32 readBufPtr, u32 readBufSize, u32 i
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
}
for (size_t i = 0; i < std::min(readBufSize, threadqueue.size()); i++)
for (size_t i = 0; i < std::min((size_t)readBufSize, threadqueue.size()); i++)
{
Memory::Write_U32(threadqueue[i]->GetUID(), readBufPtr + i * 4);
}
@ -845,7 +845,7 @@ void __KernelReSchedule(bool doCallbacks, const char *reason)
thread->isProcessingCallbacks = doCallbacks;
}
__KernelReSchedule(reason);
if (doCallbacks && thread == currentThread) {
if (doCallbacks && thread != NULL && thread == currentThread) {
if (thread->isRunning()) {
thread->isProcessingCallbacks = false;
}
@ -981,7 +981,7 @@ int sceKernelCreateThread(const char *threadName, u32 entry, u32 prio, int stack
__KernelCreateThread(id, curModule, threadName, entry, prio, stacksize, attr);
INFO_LOG(HLE, "%i = sceKernelCreateThread(name=\"%s\", entry=%08x, prio=%x, stacksize=%i)", id, threadName, entry, prio, stacksize);
if (optionAddr != 0)
WARN_LOG(HLE, "sceKernelCreateThread: unsupported options parameter.", threadName);
WARN_LOG(HLE, "sceKernelCreateThread(name=\"%s\"): unsupported options parameter %08x", threadName, optionAddr);
return id;
}
@ -1136,10 +1136,10 @@ void sceKernelExitDeleteThread()
Thread *t = kernelObjects.Get<Thread>(threadHandle, error);
if (t)
{
ERROR_LOG(HLE,"sceKernelExitDeleteThread()");
INFO_LOG(HLE,"sceKernelExitDeleteThread()");
currentThread->nt.status = THREADSTATUS_DORMANT;
currentThread->nt.exitStatus = PARAM(0);
__KernelFireThreadEnd(currentThread);
__KernelFireThreadEnd(currentThread);
//userMemory.Free(currentThread->stackBlock);
currentThread->stackBlock = 0;
@ -1739,14 +1739,22 @@ ThreadWaitInfo Thread::getWaitInfo()
void __KernelSwitchContext(Thread *target, const char *reason)
{
u32 oldPC = 0;
u32 oldUID = 0;
const char *oldName = "(none)";
if (currentThread) // It might just have been deleted.
{
__KernelSaveContext(&currentThread->context);
DEBUG_LOG(HLE,"Context saved (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc);
oldPC = currentMIPS->pc;
oldUID = currentThread->GetUID();
oldName = currentThread->GetName();
}
currentThread = target;
__KernelLoadContext(&currentThread->context);
DEBUG_LOG(HLE,"Context loaded (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc);
DEBUG_LOG(HLE,"Context switched: %s -> %s (%s) (%i - pc: %08x -> %i - pc: %08x)",
oldName, currentThread->GetName(),
reason,
oldUID, oldPC, currentThread->GetUID(), currentMIPS->pc);
// No longer waiting.
currentThread->nt.waitType = WAITTYPE_NONE;
@ -1825,8 +1833,12 @@ void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, bo
}
if (!called) {
DEBUG_LOG(HLE, "Making mipscall pending on thread");
thread->pendingMipsCalls.push_back(callId);
if (thread) {
DEBUG_LOG(HLE, "Making mipscall pending on thread");
thread->pendingMipsCalls.push_back(callId);
} else {
WARN_LOG(HLE, "Ignoring mispcall on NULL/deleted thread");
}
}
}

View file

@ -299,7 +299,7 @@ u32 sceRtcGetDayOfWeek(u32 year, u32 month, u32 day)
return 0;
}
year -= month < 3;
return ( year + year/4 - year/100 + year/400 + t[month-1] + day) % 7;
return (year + year/4 - year/100 + year/400 + t[month-1] + day) % 7;
}
u32 sceRtcGetDaysInMonth(u32 year, u32 month)
@ -307,7 +307,7 @@ u32 sceRtcGetDaysInMonth(u32 year, u32 month)
DEBUG_LOG(HLE, "sceRtcGetDaysInMonth(%d, %d)", year, month);
u32 numberOfDays;
if (year <= 0 || month <= 0 || month > 12)
if (year == 0 || month == 0 || month > 12)
return SCE_KERNEL_ERROR_INVALID_ARGUMENT;
switch (month)
@ -381,36 +381,32 @@ int sceRtcCheckValid(u32 datePtr)
ScePspDateTime pt;
Memory::ReadStruct(datePtr, &pt);
if (pt.year < 1 || pt.year > 9999)
{
ret = PSP_TIME_INVALID_YEAR;
}
{
ret = PSP_TIME_INVALID_YEAR;
}
else if (pt.month < 1 || pt.month > 12)
{
ret = PSP_TIME_INVALID_MONTH;
ret = PSP_TIME_INVALID_MONTH;
}
else if (pt.day < 1 || pt.day > 31)
else if (pt.day < 1 || pt.day > 31) // TODO: Needs to check actual days in month, including leaps
{
ret = PSP_TIME_INVALID_DAY;
}
else if (pt.day < 0 || pt.day > 31) // TODO: Needs to check actual days in month, including leaps
{
ret = PSP_TIME_INVALID_DAY;
ret = PSP_TIME_INVALID_DAY;
}
else if (pt.hour < 0 || pt.hour > 23)
else if (pt.hour > 23)
{
ret = PSP_TIME_INVALID_HOUR;
ret = PSP_TIME_INVALID_HOUR;
}
else if (pt.minute < 0 || pt.minute > 59)
else if (pt.minute > 59)
{
ret = PSP_TIME_INVALID_MINUTES;
ret = PSP_TIME_INVALID_MINUTES;
}
else if (pt.second < 0 || pt.second > 59)
else if (pt.second > 59)
{
ret = PSP_TIME_INVALID_SECONDS;
ret = PSP_TIME_INVALID_SECONDS;
}
else if (pt.microsecond < 0 || pt.microsecond >= 1000000)
else if (pt.microsecond >= 1000000)
{
ret = PSP_TIME_INVALID_MICROSECONDS;
ret = PSP_TIME_INVALID_MICROSECONDS;
}
}
else
@ -439,7 +435,7 @@ int sceRtcSetTime_t(u32 datePtr, u32 time)
int sceRtcSetTime64_t(u32 datePtr, u64 time)
{
ERROR_LOG(HLE, "HACK sceRtcSetTime64_t(%d,%d)", datePtr, time);
ERROR_LOG(HLE, "HACK sceRtcSetTime64_t(%d,%lld)", datePtr, time);
if (Memory::IsValidAddress(datePtr))
{
ScePspDateTime pt;
@ -457,13 +453,13 @@ int sceRtcSetTime64_t(u32 datePtr, u64 time)
int sceRtcGetTime_t(u32 datePtr, u32 timePtr)
{
ERROR_LOG(HLE, "HACK sceRtcGetTime_t(%d,%d)", datePtr, time);
ERROR_LOG(HLE, "HACK sceRtcGetTime_t(%d,%d)", datePtr, timePtr);
if (Memory::IsValidAddress(datePtr)&&Memory::IsValidAddress(timePtr))
{
ScePspDateTime pt;
Memory::ReadStruct(datePtr, &pt);
pt.year-=1969;
u64 result = __RtcPspTimeToTicks(pt)/1000000ULL;
u32 result = (u32) (__RtcPspTimeToTicks(pt)/1000000ULL);
Memory::Write_U32(result, timePtr);
}
else
@ -476,7 +472,7 @@ int sceRtcGetTime_t(u32 datePtr, u32 timePtr)
int sceRtcGetTime64_t(u32 datePtr, u32 timePtr)
{
ERROR_LOG(HLE, "HACK sceRtcGetTime64_t(%d,%d)", datePtr, time);
ERROR_LOG(HLE, "HACK sceRtcGetTime64_t(%d,%d)", datePtr, timePtr);
if (Memory::IsValidAddress(datePtr)&&Memory::IsValidAddress(timePtr))
{
ScePspDateTime pt;
@ -572,7 +568,7 @@ int sceRtcTickAddTicks(u32 destTickPtr, u32 srcTickPtr, u64 numTicks)
Memory::Write_U64(srcTick, destTickPtr);
}
DEBUG_LOG(HLE, "sceRtcTickAddTicks(%d,%d,%d)", destTickPtr, srcTickPtr, numTicks);
DEBUG_LOG(HLE, "sceRtcTickAddTicks(%x,%x,%llu)", destTickPtr, srcTickPtr, numTicks);
return 0;
}
@ -586,7 +582,7 @@ int sceRtcTickAddMicroseconds(u32 destTickPtr,u32 srcTickPtr, u64 numMS)
Memory::Write_U64(srcTick, destTickPtr);
}
ERROR_LOG(HLE, "HACK sceRtcTickAddMicroseconds(%d,%d,%d)", destTickPtr, srcTickPtr, numMS);
ERROR_LOG(HLE, "HACK sceRtcTickAddMicroseconds(%x,%x,%llu)", destTickPtr, srcTickPtr, numMS);
return 0;
}
@ -599,7 +595,7 @@ int sceRtcTickAddSeconds(u32 destTickPtr, u32 srcTickPtr, u64 numSecs)
srcTick += numSecs * 1000000UL;
Memory::Write_U64(srcTick, destTickPtr);
}
ERROR_LOG(HLE, "HACK sceRtcTickAddSeconds(%d,%d,%d)", destTickPtr, srcTickPtr, numSecs);
ERROR_LOG(HLE, "HACK sceRtcTickAddSeconds(%x,%x,%llu)", destTickPtr, srcTickPtr, numSecs);
return 0;
}
@ -612,7 +608,7 @@ int sceRtcTickAddMinutes(u32 destTickPtr, u32 srcTickPtr, u64 numMins)
srcTick += numMins*60000000UL;
Memory::Write_U64(srcTick, destTickPtr);
}
ERROR_LOG(HLE, "HACK sceRtcTickAddMinutes(%d,%d,%d)", destTickPtr, srcTickPtr, numMins);
ERROR_LOG(HLE, "HACK sceRtcTickAddMinutes(%x,%x,%llu)", destTickPtr, srcTickPtr, numMins);
return 0;
}

View file

@ -114,6 +114,11 @@ u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop)
return ERROR_SAS_INVALID_VOICE;
}
if (!Memory::IsValidAddress(vagAddr)) {
ERROR_LOG(HLE, "Ignoring invalid VAG audio address %08x", vagAddr);
return 0;
}
//Real VAG header is 0x30 bytes behind the vagAddr
SasVoice &v = sas->voices[voiceNum];
v.type = VOICETYPE_VAG;
@ -126,7 +131,7 @@ u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop)
u32 sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int loop)
{
DEBUG_LOG(HLE,"0=sceSasSetVoicePCM(core=%08x, voicenum=%i, pcmAddr=%08x, size=%i, loop=%i)",core, voiceNum, pcmAddr, size, loop);
DEBUG_LOG(HLE,"0=sceSasSetVoicePCM(core=%08x, voicenum=%i, pcmAddr=%08x, size=%i, loop=%i)", core, voiceNum, pcmAddr, size, loop);
SasVoice &v = sas->voices[voiceNum];
v.type = VOICETYPE_PCM;
v.pcmAddr = pcmAddr;

View file

@ -41,8 +41,8 @@ void __UtilityInit()
int sceUtilitySavedataInitStart(u32 paramAddr)
{
saveDialog.Init(paramAddr);
return 0;
DEBUG_LOG(HLE,"sceUtilitySavedataInitStart(%08x)", paramAddr);
return (u32)saveDialog.Init(paramAddr);
}
int sceUtilitySavedataShutdownStart()

View file

@ -215,7 +215,7 @@ void SasInstance::Mix(u32 outAddr) {
// Figure out number of samples to read.
int curSample = voice.samplePos / PSP_SAS_PITCH_BASE;
int lastSample = (voice.samplePos + grainSize * voice.pitch) / PSP_SAS_PITCH_BASE;
u32 numSamples = lastSample - curSample;
int numSamples = lastSample - curSample;
if (numSamples > grainSize * 4) {
ERROR_LOG(SAS, "numSamples too large, clamping: %i vs %i", numSamples, grainSize * 4);
numSamples = grainSize * 4;

View file

@ -301,13 +301,13 @@ namespace MIPSAnalyst
{
if (addr >= furthestBranch)
{
u32 target = GetSureBranchTarget(addr);
if (target != INVALIDTARGET && target < addr)
u32 sureTarget = GetSureBranchTarget(addr);
if (sureTarget != INVALIDTARGET && sureTarget < addr)
{
end = true;
}
target = GetJumpTarget(addr);
if (target != INVALIDTARGET && target < addr && ((op&0xFC000000)==0x08000000))
sureTarget = GetJumpTarget(addr);
if (sureTarget != INVALIDTARGET && sureTarget < addr && ((op&0xFC000000)==0x08000000))
{
end = true;
}

View file

@ -506,6 +506,29 @@ namespace MIPSInt
PC += 4;
EatPrefixes();
}
void Int_Vsocp(u32 op)
{
float s[4], d[4];
int vd = _VD;
int vs = _VS;
VectorSize sz = GetVecSize(op);
ReadVector(s, sz, vs);
ApplySwizzleS(s, sz);
int n=GetNumVectorElements(sz);
float x = s[0];
d[0] = std::min(std::max(0.0f, 1.0f - x), 1.0f);
d[1] = std::min(std::max(0.0f, x), 1.0f);
if (n > 1) {
float y = s[1];
d[2] = std::min(std::max(0.0f, 1.0f - y), 1.0f);
d[3] = std::min(std::max(0.0f, y), 1.0f);
}
ApplyPrefixD(d, sz);
WriteVector(d, sz, vd);
PC += 4;
EatPrefixes();
}
void Int_Vsgn(u32 op)
{
@ -907,7 +930,99 @@ namespace MIPSInt
PC += 4;
EatPrefixes();
}
void Int_Vsrt1(u32 op)
{
float s[4];
float d[4];
int vd = _VD;
int vs = _VS;
VectorSize sz = GetVecSize(op);
ReadVector(s, sz, vs);
ApplySwizzleS(s, sz);
float x = s[0];
float y = s[1];
float z = s[2];
float w = s[3];
d[0] = std::min(x, y);
d[1] = std::max(x, y);
d[2] = std::min(z, w);
d[3] = std::max(z, w);
ApplyPrefixD(d, sz);
WriteVector(d, sz, vd);
PC += 4;
EatPrefixes();
}
void Int_Vsrt2(u32 op)
{
float s[4];
float d[4];
int vd = _VD;
int vs = _VS;
VectorSize sz = GetVecSize(op);
ReadVector(s, sz, vs);
ApplySwizzleS(s, sz);
float x = s[0];
float y = s[1];
float z = s[2];
float w = s[3];
d[0] = std::min(x, w);
d[1] = std::min(y, z);
d[2] = std::max(y, z);
d[3] = std::max(x, w);
ApplyPrefixD(d, sz);
WriteVector(d, sz, vd);
PC += 4;
EatPrefixes();
}
void Int_Vsrt3(u32 op)
{
float s[4];
float d[4];
int vd = _VD;
int vs = _VS;
VectorSize sz = GetVecSize(op);
ReadVector(s, sz, vs);
ApplySwizzleS(s, sz);
float x = s[0];
float y = s[1];
float z = s[2];
float w = s[3];
d[0] = std::max(x, y);
d[1] = std::min(x, y);
d[2] = std::max(z, w);
d[3] = std::min(z, w);
ApplyPrefixD(d, sz);
WriteVector(d, sz, vd);
PC += 4;
EatPrefixes();
}
void Int_Vsrt4(u32 op)
{
float s[4];
float d[4];
int vd = _VD;
int vs = _VS;
VectorSize sz = GetVecSize(op);
ReadVector(s, sz, vs);
ApplySwizzleS(s, sz);
float x = s[0];
float y = s[1];
float z = s[2];
float w = s[3];
d[0] = std::max(x, w);
d[1] = std::max(y, z);
d[2] = std::min(y, z);
d[3] = std::min(x, w);
ApplyPrefixD(d, sz);
WriteVector(d, sz, vd);
PC += 4;
EatPrefixes();
}
void Int_Vcrs(u32 op)
{
//half a cross product
@ -931,7 +1046,26 @@ namespace MIPSInt
PC += 4;
EatPrefixes();
}
void Int_Vdet(u32 op)
{
float s[4], t[4];
float d[4];
int vd = _VD;
int vs = _VS;
int vt = _VT;
VectorSize sz = GetVecSize(op);
if (sz != V_Pair)
_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
ReadVector(s, sz, vs);
ReadVector(t, sz, vt);
d[0] = s[0] * t[1] - s[1] * t[0];
ApplyPrefixD(d, sz);
WriteVector(d, sz, vd);
PC += 4;
EatPrefixes();
}
void Int_Vfad(u32 op)
{
float s[4];
@ -1346,7 +1480,30 @@ namespace MIPSInt
PC += 4;
EatPrefixes();
}
void Int_Vscmp(u32 op) {
int vt = _VT;
int vs = _VS;
int vd = _VD;
VectorSize sz = GetVecSize(op);
float s[4];
float t[4];
float d[4];
ReadVector(s, sz, vs);
ApplySwizzleS(s, sz);
ReadVector(t, sz, vt);
ApplySwizzleT(t, sz);
int n = GetNumVectorElements(sz);
for (int i = 0; i < n ; i++) {
float a = s[i] - t[i];
d[i] = (float) ((0.0 < a) - (a < 0.0));
}
ApplyPrefixD(d, sz);
WriteVector(d, sz, vd);
PC += 4;
EatPrefixes();
}
void Int_Vsge(u32 op) {
int vt = _VT;
int vs = _VS;

View file

@ -43,6 +43,7 @@ namespace MIPSInt
void Int_Vavg(u32 op);
void Int_Vfad(u32 op);
void Int_Vocp(u32 op);
void Int_Vsocp(u32 op);
void Int_Vsgn(u32 op);
void Int_Vtfm(u32 op);
void Int_Viim(u32 op);
@ -50,12 +51,18 @@ namespace MIPSInt
void Int_Vidt(u32 op);
void Int_Vcmp(u32 op);
void Int_Vminmax(u32 op);
void Int_Vscmp(u32 op);
void Int_Vcrs(u32 op);
void Int_Vdet(u32 op);
void Int_Vcmov(u32 op);
void Int_CrossQuat(u32 op);
void Int_VPFX(u32 op);
void Int_Vflush(u32 op);
void Int_Vbfy(u32 op);
void Int_Vsrt1(u32 op);
void Int_Vsrt2(u32 op);
void Int_Vsrt3(u32 op);
void Int_Vsrt4(u32 op);
void Int_Vf2i(u32 op);
void Int_Vi2f(u32 op);
void Int_Vi2x(u32 op);

View file

@ -495,7 +495,7 @@ MIPSInstruction tableVFPU1[8] =
{-2},
INSTR("vhdp",&Jit::Comp_Generic, Dis_Generic, Int_VHdp, IS_VFPU),
INSTR("vcrs",&Jit::Comp_Generic, Dis_Vcrs, Int_Vcrs, IS_VFPU),
INSTR("vdet",&Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU),
INSTR("vdet",&Jit::Comp_Generic, Dis_Generic, Int_Vdet, IS_VFPU),
{-2},
};
@ -506,7 +506,7 @@ MIPSInstruction tableVFPU3[8] = //011011 xxx
INSTR("vmin",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vminmax, IS_VFPU),
INSTR("vmax",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vminmax, IS_VFPU),
{-2},
INSTR("vscmp",&Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU),
INSTR("vscmp",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vscmp, IS_VFPU),
INSTR("vsge",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vsge, IS_VFPU),
INSTR("vslt",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vslt, IS_VFPU),
};
@ -685,18 +685,18 @@ MIPSInstruction tableVFPUMatrixSet1[16] = //111100 11100 0xxxx (rm x is 16)
MIPSInstruction tableVFPU9[32] = //110100 00010 xxxxx
{
INSTR("vsrt1", &Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU),
INSTR("vsrt2", &Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU),
INSTR("vsrt1", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt1, IS_VFPU),
INSTR("vsrt2", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt2, IS_VFPU),
INSTR("vbfy1", &Jit::Comp_Generic, Dis_Vbfy, Int_Vbfy, IS_VFPU),
INSTR("vbfy2", &Jit::Comp_Generic, Dis_Vbfy, Int_Vbfy, IS_VFPU),
//4
INSTR("vocp", &Jit::Comp_Generic, Dis_Vbfy, Int_Vocp, IS_VFPU), // one's complement
INSTR("vsocp", &Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU),
INSTR("vsocp", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsocp, IS_VFPU),
INSTR("vfad", &Jit::Comp_Generic, Dis_Vfad, Int_Vfad, IS_VFPU),
INSTR("vavg", &Jit::Comp_Generic, Dis_Vfad, Int_Vavg, IS_VFPU),
//8
INSTR("vsrt3", &Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU),
INSTR("vsrt4", &Jit::Comp_Generic, Dis_Generic, 0, IS_VFPU),
INSTR("vsrt3", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt3, IS_VFPU),
INSTR("vsrt4", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt4, IS_VFPU),
INSTR("vsgn", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsgn, IS_VFPU),
{-2},
//12

View file

@ -17,6 +17,7 @@
#include "../MIPS.h"
#include "../../Config.h"
#include "Common/Common.h"
#include "Jit.h"
#include "RegCache.h"
@ -86,6 +87,10 @@ void Jit::Comp_FPU3op(u32 op)
void Jit::Comp_FPULS(u32 op)
{
CONDITIONAL_DISABLE;
if (!g_Config.bFastMemory) {
DISABLE;
}
s32 offset = (s16)(op&0xFFFF);
int ft = ((op>>16)&0x1f);

View file

@ -17,6 +17,7 @@
#include "../../MemMap.h"
#include "../MIPSAnalyst.h"
#include "../../Config.h"
#include "Jit.h"
#include "RegCache.h"
@ -42,7 +43,11 @@ namespace MIPSComp
{
void Jit::Comp_ITypeMem(u32 op)
{
// OLDD
if (!g_Config.bFastMemory)
{
DISABLE;
}
int offset = (signed short)(op&0xFFFF);
int rt = _RT;
int rs = _RS;

View file

@ -4,6 +4,7 @@ set(SRCS
GLES/DisplayListInterpreter.cpp
GLES/FragmentShaderGenerator.cpp
GLES/Framebuffer.cpp
GLES/IndexGenerator.cpp
GLES/ShaderManager.cpp
GLES/StateMapping.cpp
GLES/TextureCache.cpp

View file

@ -43,21 +43,143 @@ ShaderManager shaderManager;
extern u32 curTextureWidth;
extern u32 curTextureHeight;
bool *flushBeforeCommand = 0;
const int flushBeforeCommandList[] = {
GE_CMD_BEZIER,
GE_CMD_SPLINE,
GE_CMD_SIGNAL,
GE_CMD_FINISH,
GE_CMD_BJUMP,
GE_CMD_VERTEXTYPE,
GE_CMD_OFFSETADDR,
GE_CMD_REGION1,
GE_CMD_REGION2,
GE_CMD_CULLFACEENABLE,
GE_CMD_TEXTUREMAPENABLE,
GE_CMD_LIGHTINGENABLE,
GE_CMD_FOGENABLE,
GE_CMD_TEXSCALEU,
GE_CMD_TEXSCALEV,
GE_CMD_TEXOFFSETU,
GE_CMD_TEXOFFSETV,
GE_CMD_MINZ,
GE_CMD_MAXZ,
GE_CMD_FRAMEBUFPTR,
GE_CMD_FRAMEBUFWIDTH,
GE_CMD_FRAMEBUFPIXFORMAT,
GE_CMD_TEXADDR0,
GE_CMD_CLUTADDR,
GE_CMD_LOADCLUT,
GE_CMD_TEXMAPMODE,
GE_CMD_TEXSHADELS,
GE_CMD_CLUTFORMAT,
GE_CMD_TRANSFERSTART,
GE_CMD_TEXSIZE0,
GE_CMD_TEXSIZE1,
GE_CMD_TEXSIZE2,
GE_CMD_TEXSIZE3,
GE_CMD_TEXSIZE4,
GE_CMD_TEXSIZE5,
GE_CMD_TEXSIZE6,
GE_CMD_TEXSIZE7,
GE_CMD_ZBUFPTR,
GE_CMD_ZBUFWIDTH,
GE_CMD_AMBIENTCOLOR,
GE_CMD_AMBIENTALPHA,
GE_CMD_MATERIALAMBIENT,
GE_CMD_MATERIALDIFFUSE,
GE_CMD_MATERIALEMISSIVE,
GE_CMD_MATERIALSPECULAR,
GE_CMD_MATERIALALPHA,
GE_CMD_MATERIALSPECULARCOEF,
GE_CMD_LIGHTTYPE0,
GE_CMD_LIGHTTYPE1,
GE_CMD_LIGHTTYPE2,
GE_CMD_LIGHTTYPE3,
GE_CMD_LX0,
GE_CMD_LX1,
GE_CMD_LX2,
GE_CMD_LX3,
GE_CMD_LDX0,
GE_CMD_LDX1,
GE_CMD_LDX2,
GE_CMD_LDX3,
GE_CMD_LKA0,
GE_CMD_LAC0,
GE_CMD_LDC0,
GE_CMD_LSC0,
GE_CMD_VIEWPORTX1,
GE_CMD_VIEWPORTY1,
GE_CMD_VIEWPORTX2,
GE_CMD_VIEWPORTY2,
GE_CMD_VIEWPORTZ1,
GE_CMD_VIEWPORTZ2,
GE_CMD_LIGHTENABLE0,
GE_CMD_LIGHTENABLE1,
GE_CMD_LIGHTENABLE2,
GE_CMD_LIGHTENABLE3,
GE_CMD_CULL,
GE_CMD_LMODE,
GE_CMD_REVERSENORMAL,
GE_CMD_PATCHDIVISION,
GE_CMD_MATERIALUPDATE,
GE_CMD_CLEARMODE,
GE_CMD_ALPHABLENDENABLE,
GE_CMD_BLENDMODE,
GE_CMD_BLENDFIXEDA,
GE_CMD_BLENDFIXEDB,
GE_CMD_ALPHATESTENABLE,
GE_CMD_ALPHATEST,
GE_CMD_TEXFUNC,
GE_CMD_TEXFILTER,
GE_CMD_TEXENVCOLOR,
GE_CMD_TEXMODE,
GE_CMD_TEXFORMAT,
GE_CMD_TEXFLUSH,
GE_CMD_TEXWRAP,
GE_CMD_ZTESTENABLE,
GE_CMD_STENCILTESTENABLE,
GE_CMD_STENCILOP,
GE_CMD_ZTEST,
GE_CMD_MORPHWEIGHT0,
GE_CMD_MORPHWEIGHT1,
GE_CMD_MORPHWEIGHT2,
GE_CMD_MORPHWEIGHT3,
GE_CMD_MORPHWEIGHT4,
GE_CMD_MORPHWEIGHT5,
GE_CMD_MORPHWEIGHT6,
GE_CMD_MORPHWEIGHT7,
GE_CMD_WORLDMATRIXNUMBER,
GE_CMD_VIEWMATRIXNUMBER,
GE_CMD_PROJMATRIXNUMBER,
GE_CMD_PROJMATRIXDATA,
GE_CMD_TGENMATRIXNUMBER,
GE_CMD_BONEMATRIXNUMBER,
};
GLES_GPU::GLES_GPU(int renderWidth, int renderHeight)
: interruptsEnabled_(true),
: interruptsEnabled_(true),
displayFramebufPtr_(0),
renderWidth_(renderWidth),
renderHeight_(renderHeight),
dlIdGenerator(1),
displayFramebufPtr_(0)
dlIdGenerator(1)
{
renderWidthFactor_ = (float)renderWidth / 480.0f;
renderHeightFactor_ = (float)renderHeight / 272.0f;
shaderManager_ = &shaderManager;
TextureCache_Init();
InitTransform();
// Sanity check gstate
if ((int *)&gstate.transferstart - (int *)&gstate != 0xEA) {
ERROR_LOG(G3D, "gstate has drifted out of sync!");
}
flushBeforeCommand = new bool[256];
memset(flushBeforeCommand, 0, 256 * sizeof(bool));
for (int i = 0; i < ARRAY_SIZE(flushBeforeCommandList); i++) {
flushBeforeCommand[flushBeforeCommandList[i]] = true;
}
flushBeforeCommand[1] = false;
}
GLES_GPU::~GLES_GPU()
@ -103,6 +225,7 @@ void GLES_GPU::BeginFrame()
void GLES_GPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, int format)
{
if (framebuf & 0x04000000) {
DEBUG_LOG(G3D, "Switch display framebuffer %08x", framebuf);
displayFramebufPtr_ = framebuf;
displayStride_ = stride;
displayFormat_ = format;
@ -113,9 +236,12 @@ void GLES_GPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, int format)
void GLES_GPU::CopyDisplayToOutput()
{
Flush();
if (!g_Config.bBufferedRendering)
return;
EndDebugDraw();
VirtualFramebuffer *vfb = GetDisplayFBO();
fbo_unbind();
@ -145,6 +271,8 @@ void GLES_GPU::CopyDisplayToOutput()
shaderManager.DirtyShader();
shaderManager.DirtyUniform(DIRTY_ALL);
gstate_c.textureChanged = true;
BeginDebugDraw();
}
GLES_GPU::VirtualFramebuffer *GLES_GPU::GetDisplayFBO()
@ -193,6 +321,8 @@ void GLES_GPU::SetRenderFrameBuffer()
// None found? Create one.
if (!vfb) {
Flush();
gstate_c.textureChanged = true;
vfb = new VirtualFramebuffer;
vfb->fb_address = fb_address;
vfb->fb_stride = fb_stride;
@ -213,14 +343,30 @@ void GLES_GPU::SetRenderFrameBuffer()
if (vfb != currentRenderVfb_)
{
Flush();
// Use it as a render target.
DEBUG_LOG(HLE, "Switching render target to FBO for %08x", vfb->fb_address);
gstate_c.textureChanged = true;
fbo_bind_as_render_target(vfb->fbo);
glViewport(0, 0, renderWidth_, renderHeight_);
currentRenderVfb_ = vfb;
}
}
void GLES_GPU::BeginDebugDraw()
{
if (g_Config.bDrawWireframe) {
#ifndef USING_GLES2
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
// glClear(GL_COLOR_BUFFER_BIT);
}
}
void GLES_GPU::EndDebugDraw() {
#ifndef USING_GLES2
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
}
// Render queue
@ -280,7 +426,7 @@ void GLES_GPU::UpdateStall(int listid, u32 newstall)
void GLES_GPU::DrawSync(int mode)
{
Flush();
}
void GLES_GPU::Continue()
@ -382,12 +528,24 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
};
DEBUG_LOG(G3D, "DL DrawPrim type: %s count: %i vaddr= %08x, iaddr= %08x", type<7 ? types[type] : "INVALID", count, gstate_c.vertexAddr, gstate_c.indexAddr);
if (!Memory::IsValidAddress(gstate_c.vertexAddr))
{
ERROR_LOG(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
break;
}
// TODO: Split this so that we can collect sequences of primitives, can greatly speed things up
// on platforms where draw calls are expensive like mobile and D3D
void *verts = Memory::GetPointer(gstate_c.vertexAddr);
void *inds = 0;
if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE)
{
if (!Memory::IsValidAddress(gstate_c.indexAddr))
{
ERROR_LOG(G3D, "Bad index address %08x!", gstate_c.indexAddr);
break;
}
inds = Memory::GetPointer(gstate_c.indexAddr);
}
// Seems we have to advance the vertex addr, at least in some cases.
// Question: Should we also advance the index addr?
@ -451,15 +609,16 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
case GE_CMD_SIGNAL:
{
ERROR_LOG(G3D, "DL GE_CMD_SIGNAL %08x", data & 0xFFFFFF);
DEBUG_LOG(G3D, "DL GE_CMD_SIGNAL %08x", data & 0xFFFFFF);
// Processed in GE_END.
}
break;
case GE_CMD_FINISH:
DEBUG_LOG(G3D,"DL CMD FINISH");
// TODO: Should this run while interrupts are suspended?
if (interruptsEnabled_)
__TriggerInterruptWithArg(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, 0);
__TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, 0);
break;
case GE_CMD_END:
@ -468,6 +627,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
{
case GE_CMD_SIGNAL:
{
// TODO: see http://code.google.com/p/jpcsp/source/detail?r=2935#
int behaviour = (prev >> 16) & 0xFF;
int signal = prev & 0xFFFF;
int enddata = data & 0xFFFF;
@ -495,8 +655,9 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
ERROR_LOG(G3D, "UNKNOWN Signal UNIMPLEMENTED %i ! signal/end: %04x %04x", behaviour, signal, enddata);
break;
}
// TODO: Should this run while interrupts are suspended?
if (interruptsEnabled_)
__TriggerInterruptWithArg(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, signal);
__TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, signal);
}
break;
case GE_CMD_FINISH:
@ -510,12 +671,12 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
case GE_CMD_BJUMP:
// bounding box jump. Let's just not jump, for now.
ERROR_LOG(G3D,"DL BBOX JUMP - unimplemented");
DEBUG_LOG(G3D,"DL BBOX JUMP - unimplemented");
break;
case GE_CMD_BOUNDINGBOX:
// bounding box test. Let's do nothing.
ERROR_LOG(G3D,"DL BBOX TEST - unimplemented");
DEBUG_LOG(G3D,"DL BBOX TEST - unimplemented");
break;
case GE_CMD_ORIGIN:
@ -591,21 +752,25 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
case GE_CMD_TEXSCALEU:
gstate_c.uScale = getFloat24(data);
DEBUG_LOG(G3D, "DL Texture U Scale: %f", gstate_c.uScale);
shaderManager.DirtyUniform(DIRTY_UVSCALEOFFSET);
break;
case GE_CMD_TEXSCALEV:
gstate_c.vScale = getFloat24(data);
DEBUG_LOG(G3D, "DL Texture V Scale: %f", gstate_c.vScale);
shaderManager.DirtyUniform(DIRTY_UVSCALEOFFSET);
break;
case GE_CMD_TEXOFFSETU:
gstate_c.uOff = getFloat24(data);
DEBUG_LOG(G3D, "DL Texture U Offset: %f", gstate_c.uOff);
shaderManager.DirtyUniform(DIRTY_UVSCALEOFFSET);
break;
case GE_CMD_TEXOFFSETV:
gstate_c.vOff = getFloat24(data);
DEBUG_LOG(G3D, "DL Texture V Offset: %f", gstate_c.vOff);
shaderManager.DirtyUniform(DIRTY_UVSCALEOFFSET);
break;
case GE_CMD_SCISSOR1:
@ -651,7 +816,6 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
break;
case GE_CMD_TEXADDR0:
gstate_c.textureChanged = true;
case GE_CMD_TEXADDR1:
case GE_CMD_TEXADDR2:
case GE_CMD_TEXADDR3:
@ -659,11 +823,11 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
case GE_CMD_TEXADDR5:
case GE_CMD_TEXADDR6:
case GE_CMD_TEXADDR7:
gstate_c.textureChanged = true;
DEBUG_LOG(G3D,"DL Texture address %i: %06x", cmd-GE_CMD_TEXADDR0, data);
break;
case GE_CMD_TEXBUFWIDTH0:
gstate_c.textureChanged = true;
case GE_CMD_TEXBUFWIDTH1:
case GE_CMD_TEXBUFWIDTH2:
case GE_CMD_TEXBUFWIDTH3:
@ -671,18 +835,23 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
case GE_CMD_TEXBUFWIDTH5:
case GE_CMD_TEXBUFWIDTH6:
case GE_CMD_TEXBUFWIDTH7:
gstate_c.textureChanged = true;
DEBUG_LOG(G3D,"DL Texture BUFWIDTHess %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data);
break;
case GE_CMD_CLUTADDR:
gstate_c.textureChanged = true;
//DEBUG_LOG(G3D,"CLUT base addr: %06x", data);
gstate_c.textureChanged = true;
break;
case GE_CMD_CLUTADDRUPPER:
gstate_c.textureChanged = true;
DEBUG_LOG(G3D,"DL CLUT addr: %08x", ((gstate.clutaddrupper & 0xFF0000)<<8) | (gstate.clutaddr & 0xFFFFFF));
break;
case GE_CMD_LOADCLUT:
gstate_c.textureChanged = true;
// This could be used to "dirty" textures with clut.
{
u32 clutAddr = ((gstate.clutaddrupper & 0xFF0000)<<8) | (gstate.clutaddr & 0xFFFFFF);
@ -698,6 +867,21 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
}
break;
case GE_CMD_TEXMAPMODE:
DEBUG_LOG(G3D,"Tex map mode: %06x", data);
break;
case GE_CMD_TEXSHADELS:
DEBUG_LOG(G3D,"Tex shade light sources: %06x", data);
break;
case GE_CMD_CLUTFORMAT:
{
gstate_c.textureChanged = true;
DEBUG_LOG(G3D,"DL Clut format: %06x", data);
}
break;
case GE_CMD_TRANSFERSRC:
{
// Nothing to do, the next one prints
@ -759,7 +943,6 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
}
case GE_CMD_TEXSIZE0:
gstate_c.textureChanged = true;
gstate_c.curTextureWidth = 1 << (gstate.texsize[0] & 0xf);
gstate_c.curTextureHeight = 1 << ((gstate.texsize[0]>>8) & 0xf);
//fall thru - ignoring the mipmap sizes for now
@ -771,6 +954,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
case GE_CMD_TEXSIZE6:
case GE_CMD_TEXSIZE7:
DEBUG_LOG(G3D,"DL Texture Size %i: %06x", cmd - GE_CMD_TEXSIZE0, data);
gstate_c.textureChanged = true;
break;
case GE_CMD_ZBUFPTR:
@ -797,26 +981,38 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
case GE_CMD_MATERIALAMBIENT:
DEBUG_LOG(G3D,"DL Material Ambient Color: %06x", data);
if (diff)
shaderManager.DirtyUniform(DIRTY_MATAMBIENTALPHA);
break;
case GE_CMD_MATERIALDIFFUSE:
DEBUG_LOG(G3D,"DL Material Diffuse Color: %06x", data);
if (diff)
shaderManager.DirtyUniform(DIRTY_MATDIFFUSE);
break;
case GE_CMD_MATERIALEMISSIVE:
DEBUG_LOG(G3D,"DL Material Emissive Color: %06x", data);
if (diff)
shaderManager.DirtyUniform(DIRTY_MATEMISSIVE);
break;
case GE_CMD_MATERIALSPECULAR:
DEBUG_LOG(G3D,"DL Material Specular Color: %06x", data);
if (diff)
shaderManager.DirtyUniform(DIRTY_MATSPECULAR);
break;
case GE_CMD_MATERIALALPHA:
DEBUG_LOG(G3D,"DL Material Alpha Color: %06x", data);
if (diff)
shaderManager.DirtyUniform(DIRTY_MATAMBIENTALPHA);
break;
case GE_CMD_MATERIALSPECULARCOEF:
DEBUG_LOG(G3D,"DL Material specular coef: %f", getFloat24(data));
if (diff)
shaderManager.DirtyUniform(DIRTY_MATSPECULAR);
break;
case GE_CMD_LIGHTTYPE0:
@ -837,6 +1033,8 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
float val = getFloat24(data);
DEBUG_LOG(G3D,"DL Light %i %c pos: %f", l, c+'X', val);
gstate_c.lightpos[l][c] = val;
if (diff)
shaderManager.DirtyUniform(DIRTY_LIGHT0 << l);
}
break;
@ -851,6 +1049,8 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
float val = getFloat24(data);
DEBUG_LOG(G3D,"DL Light %i %c dir: %f", l, c+'X', val);
gstate_c.lightdir[l][c] = val;
if (diff)
shaderManager.DirtyUniform(DIRTY_LIGHT0 << l);
}
break;
@ -865,6 +1065,8 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
float val = getFloat24(data);
DEBUG_LOG(G3D,"DL Light %i %c att: %f", l, c+'X', val);
gstate_c.lightatt[l][c] = val;
if (diff)
shaderManager.DirtyUniform(DIRTY_LIGHT0 << l);
}
break;
@ -879,9 +1081,12 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
int l = (cmd - GE_CMD_LAC0) / 3;
int t = (cmd - GE_CMD_LAC0) % 3;
gstate_c.lightColor[t][l].r = r;
gstate_c.lightColor[t][l].g = g;
gstate_c.lightColor[t][l].b = b;
DEBUG_LOG(G3D,"DL Light color %i", l);
gstate_c.lightColor[t][l][0] = r;
gstate_c.lightColor[t][l][1] = g;
gstate_c.lightColor[t][l][2] = b;
if (diff)
shaderManager.DirtyUniform(DIRTY_LIGHT0 << l);
}
break;
@ -963,7 +1168,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
case GE_CMD_ALPHATEST:
DEBUG_LOG(G3D,"DL Alpha test settings");
shaderManager.DirtyUniform(DIRTY_ALPHAREF);
shaderManager.DirtyUniform(DIRTY_ALPHACOLORREF);
break;
case GE_CMD_TEXFUNC:
@ -1002,6 +1207,8 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
break;
case GE_CMD_TEXENVCOLOR:
DEBUG_LOG(G3D,"DL TexEnvColor %06x", data);
if (diff)
shaderManager.DirtyUniform(DIRTY_TEXENV);
break;
case GE_CMD_TEXMODE:
DEBUG_LOG(G3D,"DL TexMode %08x", data);
@ -1068,6 +1275,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
if (num < 12)
gstate.worldMatrix[num++] = getFloat24(data);
gstate.worldmtxnum = (gstate.worldmtxnum & 0xFF000000) | (num & 0xF);
shaderManager.DirtyUniform(DIRTY_WORLDMATRIX);
}
break;
@ -1083,6 +1291,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
if (num < 12)
gstate.viewMatrix[num++] = getFloat24(data);
gstate.viewmtxnum = (gstate.viewmtxnum & 0xFF000000) | (num & 0xF);
shaderManager.DirtyUniform(DIRTY_VIEWMATRIX);
}
break;
@ -1114,6 +1323,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
gstate.tgenMatrix[num++] = getFloat24(data);
gstate.texmtxnum = (gstate.texmtxnum & 0xFF000000) | (num & 0xF);
}
shaderManager.DirtyUniform(DIRTY_TEXMATRIX);
break;
case GE_CMD_BONEMATRIXNUMBER:
@ -1125,6 +1335,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff)
DEBUG_LOG(G3D,"DL BONE data #%i %f", gstate.boneMatrixNumber & 0x7f, getFloat24(data));
{
int num = gstate.boneMatrixNumber & 0x7F;
shaderManager.DirtyUniform(DIRTY_BONEMATRIX0 << (num / 12));
if (num < 96) {
gstate.boneMatrix[num++] = getFloat24(data);
}
@ -1159,7 +1370,9 @@ bool GLES_GPU::InterpretList()
op = Memory::ReadUnchecked_U32(dcontext.pc); //read from memory
u32 cmd = op >> 24;
u32 diff = op ^ gstate.cmdmem[cmd];
gstate.cmdmem[cmd] = op; // crashes if I try to put the whole op there??
if (flushBeforeCommand[cmd])
Flush();
gstate.cmdmem[cmd] = op;
ExecuteOp(op, diff);
@ -1180,6 +1393,15 @@ void GLES_GPU::UpdateStats()
void GLES_GPU::DoBlockTransfer()
{
// TODO: This is used a lot to copy data around between render targets and textures,
// and also to quickly load textures from RAM to VRAM. So we should do checks like the following:
// * Does dstBasePtr point to an existing texture? If so maybe reload it immediately.
//
// * Does srcBasePtr point to a render target, and dstBasePtr to a texture? If so
// either copy between rt and texture or reassign the texture to point to the render target
//
// etc....
u32 srcBasePtr = (gstate.transfersrc & 0xFFFFFF) | ((gstate.transfersrcw & 0xFF0000) << 8);
u32 srcStride = gstate.transfersrcw & 0x3FF;
@ -1207,4 +1429,14 @@ void GLES_GPU::DoBlockTransfer()
}
// TODO: Notify all overlapping textures that it's time to die/reload.
TextureCache_Invalidate(dstBasePtr + dstY * dstStride + dstX, height * dstStride + width * bpp);
}
void GLES_GPU::InvalidateCache(u32 addr, int size)
{
if (size > 0)
TextureCache_Invalidate(addr, size);
else
TextureCache_Clear(true);
}

View file

@ -22,9 +22,11 @@
#include "../GPUInterface.h"
#include "Framebuffer.h"
#include "VertexDecoder.h"
#include "gfx_es2/fbo.h"
class ShaderManager;
class LinkedShader;
class GLES_GPU : public GPUInterface
{
@ -47,15 +49,24 @@ public:
virtual void CopyDisplayToOutput();
virtual void BeginFrame();
virtual void UpdateStats();
virtual void InvalidateCache(u32 addr, int size);
private:
// TransformPipeline.cpp
void InitTransform();
void TransformAndDrawPrim(void *verts, void *inds, int prim, int vertexCount, float *customUV, int forceIndexType, int *bytesRead = 0);
//void SoftwareTransformAndDraw(int prim, LinkedShader *program, int forceIndexType, int vertexCount, void *inds, const DecVtxFormat &decVtxFormat, int indexLowerBound, int indexUpperBound, float *customUV);
void ApplyDrawState();
void Flush();
void UpdateViewportAndProjection();
void DrawBezier(int ucount, int vcount);
void DoBlockTransfer();
bool ProcessDLQueue();
// Applies states for debugging if enabled.
void BeginDebugDraw();
void EndDebugDraw();
FramebufferManager framebufferManager;
ShaderManager *shaderManager_;

View file

@ -79,16 +79,16 @@ char *GenerateFragmentShader()
if (doTexture)
WRITE(p, "uniform sampler2D tex;\n");
if (gstate.alphaTestEnable & 1)
WRITE(p, "uniform vec4 u_alpharef;\n");
if ((gstate.alphaTestEnable & 1) || (gstate.colorTestEnable & 1))
WRITE(p, "uniform vec4 u_alphacolorref;\n");
if (gstate.fogEnable & 1) {
WRITE(p, "uniform vec3 u_fogcolor;\n");
WRITE(p, "uniform vec2 u_fogcoef;\n");
}
WRITE(p, "uniform vec4 u_texenv;\n");
WRITE(p, "uniform vec3 u_texenv;\n");
WRITE(p, "varying vec4 v_color0;\n");
if (lmode)
WRITE(p, "varying vec4 v_color1;\n");
WRITE(p, "varying vec3 v_color1;\n");
if (doTexture)
WRITE(p, "varying vec2 v_texcoord;\n");
if (gstate.isFogEnabled())
@ -107,7 +107,7 @@ char *GenerateFragmentShader()
const char *secondary = "";
// Secondary color for specular on top of texture
if (lmode) {
WRITE(p, " vec4 s = vec4(v_color1.xyz, 0.0);");
WRITE(p, " vec4 s = vec4(v_color1, 0.0);");
secondary = " + s";
} else {
WRITE(p, " vec4 s = vec4(0.0, 0.0, 0.0, 0.0);\n");
@ -163,9 +163,20 @@ char *GenerateFragmentShader()
int alphaTestFunc = gstate.alphatest & 7;
const char *alphaTestFuncs[] = { "#", "#", " == ", " != ", " < ", " <= ", " > ", " >= " }; // never/always don't make sense
if (alphaTestFuncs[alphaTestFunc][0] != '#')
WRITE(p, "if (!(v.a %s u_alpharef.x)) discard;", alphaTestFuncs[alphaTestFunc]);
WRITE(p, "if (!(v.a %s u_alphacolorref.a)) discard;", alphaTestFuncs[alphaTestFunc]);
}
// Disabled for now until we actually find a need for it.
/*
if (gstate.colorTestEnable & 1) {
// TODO: There are some colortestmasks we could handle.
int colorTestFunc = gstate.colortest & 3;
const char *colorTestFuncs[] = { "#", "#", " == ", " != " }; // never/always don't make sense}
int colorTestMask = gstate.colormask;
if (colorTestFuncs[colorTestFunc][0] != '#')
WRITE(p, "if (!(v.rgb %s u_alphacolorref.rgb)) discard;", colorTestFuncs[colorTestFunc]);
}*/
if (gstate.isFogEnabled()) {
// Haven't figured out how to adjust the depth range yet.
// WRITE(p, " v = mix(v, u_fogcolor, u_fogcoef.x + u_fogcoef.y * v_depth;\n");

View file

@ -37,14 +37,17 @@ const char tex_fs[] =
"}\n";
const char basic_vs[] =
#ifndef USING_GLES2
"#version 120\n"
#endif
"attribute vec4 a_position;\n"
"attribute vec2 a_texcoord0;\n"
"uniform mat4 u_viewproj;\n"
"varying vec4 v_color;\n"
"varying vec2 v_texcoord0;\n"
"void main() {\n"
" v_texcoord0 = a_texcoord0;\n"
" gl_Position = u_viewproj * a_position;\n"
" v_texcoord0 = a_texcoord0;\n"
" gl_Position = u_viewproj * a_position;\n"
"}\n";
FramebufferManager::FramebufferManager() {

338
GPU/GLES/IndexGenerator.cpp Normal file
View file

@ -0,0 +1,338 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "IndexGenerator.h"
// Points don't need indexing...
const u8 indexedPrimitiveType[7] = {
GE_PRIM_POINTS,
GE_PRIM_LINES,
GE_PRIM_LINES,
GE_PRIM_TRIANGLES,
GE_PRIM_TRIANGLES,
GE_PRIM_TRIANGLES,
GE_PRIM_RECTANGLES,
};
void IndexGenerator::Reset() {
prim_ = -1;
count_ = 0;
index_ = 0;
this->inds_ = indsBase_;
}
bool IndexGenerator::PrimCompatible(int prim) {
if (prim_ == -1)
return true;
return indexedPrimitiveType[prim] == prim_;
}
void IndexGenerator::Setup(u16 *inds)
{
this->indsBase_ = inds;
Reset();
}
void IndexGenerator::AddPoints(int numVerts)
{
//if we have no vertices return
for (int i = 0; i < numVerts; i++)
{
*inds_++ = index_ + i;
}
// ignore overflow verts
index_ += numVerts;
count_ += numVerts;
prim_ = GE_PRIM_POINTS;
}
void IndexGenerator::AddList(int numVerts)
{
//if we have no vertices return
int numTris = numVerts / 3;
for (int i = 0; i < numTris; i++)
{
*inds_++ = index_ + i*3;
*inds_++ = index_ + i*3 + 1;
*inds_++ = index_ + i*3 + 2;
}
// ignore overflow verts
index_ += numVerts;
count_ += numTris * 3;
prim_ = GE_PRIM_TRIANGLES;
}
void IndexGenerator::AddStrip(int numVerts)
{
bool wind = false;
int numTris = numVerts - 2;
for (int i = 0; i < numTris; i++)
{
*inds_++ = index_ + i;
*inds_++ = index_ + i+(wind?2:1);
*inds_++ = index_ + i+(wind?1:2);
wind = !wind;
}
index_ += numVerts;
count_ += numTris * 3;
prim_ = GE_PRIM_TRIANGLES;
}
void IndexGenerator::AddFan(int numVerts)
{
int numTris = numVerts - 2;
for (int i = 0; i < numTris; i++)
{
*inds_++ = index_;
*inds_++ = index_ + i + 1;
*inds_++ = index_ + i + 2;
}
index_ += numVerts;
count_ += numTris * 3;
prim_ = GE_PRIM_TRIANGLES;
}
void IndexGenerator::TranslatePoints(int numVerts, const u8 *inds, int offset)
{
for (int i = 0; i < numVerts; i++)
{
*inds_++ = index_ + offset + inds[i];
}
index_ += numVerts;
count_ += numVerts;
prim_ = GE_PRIM_POINTS;
}
void IndexGenerator::TranslatePoints(int numVerts, const u16 *inds, int offset)
{
for (int i = 0; i < numVerts; i++)
{
*inds_++ = index_ + offset + inds[i];
}
index_ += numVerts;
count_ += numVerts;
prim_ = GE_PRIM_POINTS;
}
void IndexGenerator::TranslateList(int numVerts, const u8 *inds, int offset)
{
int numTris = numVerts / 3;
for (int i = 0; i < numTris; i++)
{
*inds_++ = index_ + offset + inds[i*3];
*inds_++ = index_ + offset + inds[i*3 + 1];
*inds_++ = index_ + offset + inds[i*3 + 2];
}
index_ += numVerts;
count_ += numTris * 3;
prim_ = GE_PRIM_TRIANGLES;
}
void IndexGenerator::TranslateStrip(int numVerts, const u8 *inds, int offset)
{
bool wind = false;
int numTris = numVerts - 2;
for (int i = 0; i < numTris; i++)
{
*inds_++ = index_ + offset + inds[i];
*inds_++ = index_ + offset + inds[i + (wind?2:1)];
*inds_++ = index_ + offset + inds[i + (wind?1:2)];
wind = !wind;
}
index_ += numVerts;
count_ += numTris * 3;
prim_ = GE_PRIM_TRIANGLES;
}
void IndexGenerator::TranslateFan(int numVerts, const u8 *inds, int offset)
{
if (numVerts <= 0) return;
int numTris = numVerts - 2;
for (int i = 0; i < numTris; i++)
{
*inds_++ = index_ + offset + inds[0];
*inds_++ = index_ + offset + inds[i + 1];
*inds_++ = index_ + offset + inds[i + 2];
}
index_ += numVerts;
count_ += numTris * 3;
prim_ = GE_PRIM_TRIANGLES;
}
void IndexGenerator::TranslateList(int numVerts, const u16 *inds, int offset)
{
int numTris = numVerts / 3;
for (int i = 0; i < numTris; i++)
{
*inds_++ = index_ + offset + inds[i*3];
*inds_++ = index_ + offset + inds[i*3 + 1];
*inds_++ = index_ + offset + inds[i*3 + 2];
}
index_ += numVerts;
count_ += numTris * 3;
prim_ = GE_PRIM_TRIANGLES;
}
void IndexGenerator::TranslateStrip(int numVerts, const u16 *inds, int offset)
{
bool wind = false;
int numTris = numVerts - 2;
for (int i = 0; i < numTris; i++)
{
*inds_++ = index_ + offset + inds[i];
*inds_++ = index_ + offset + inds[i + (wind?2:1)];
*inds_++ = index_ + offset + inds[i + (wind?1:2)];
wind = !wind;
}
index_ += numVerts;
count_ += numTris * 3;
prim_ = GE_PRIM_TRIANGLES;
}
void IndexGenerator::TranslateFan(int numVerts, const u16 *inds, int offset)
{
if (numVerts <= 0) return;
int numTris = numVerts - 2;
for (int i = 0; i < numTris; i++)
{
*inds_++ = index_ + offset + inds[0];
*inds_++ = index_ + offset + inds[i + 1];
*inds_++ = index_ + offset + inds[i + 2];
}
index_ += numVerts;
count_ += numTris * 3;
prim_ = GE_PRIM_TRIANGLES;
}
//Lines
void IndexGenerator::AddLineList(int numVerts)
{
int numLines = numVerts / 2;
for (int i = 0; i < numLines; i++)
{
*inds_++ = index_ + i*2;
*inds_++ = index_ + i*2+1;
}
index_ += numVerts;
count_ += numLines * 2;
prim_ = GE_PRIM_LINES;
}
void IndexGenerator::AddLineStrip(int numVerts)
{
int numLines = numVerts - 1;
for (int i = 0; i < numLines; i++)
{
*inds_++ = index_ + i;
*inds_++ = index_ + i + 1;
}
index_ += numVerts;
count_ += numLines * 2;
prim_ = GE_PRIM_LINES;
}
void IndexGenerator::AddRectangles(int numVerts)
{
int numRects = numVerts / 2;
for (int i = 0; i < numRects; i++)
{
*inds_++ = index_ + i*2;
*inds_++ = index_ + i*2+1;
}
index_ += numVerts;
count_ += numRects * 2;
prim_ = GE_PRIM_RECTANGLES;
}
void IndexGenerator::TranslateLineList(int numVerts, const u8 *inds, int offset)
{
int numLines = numVerts / 2;
for (int i = 0; i < numLines; i++)
{
*inds_++ = index_ + i*2;
*inds_++ = index_ + i*2+1;
}
index_ += numVerts;
count_ += numLines * 2;
prim_ = GE_PRIM_LINES;
}
void IndexGenerator::TranslateLineStrip(int numVerts, const u8 *inds, int offset)
{
int numLines = numVerts - 1;
for (int i = 0; i < numLines; i++)
{
*inds_++ = index_ + i;
*inds_++ = index_ + i + 1;
}
index_ += numVerts;
count_ += numLines * 2;
prim_ = GE_PRIM_LINES;
}
void IndexGenerator::TranslateLineList(int numVerts, const u16 *inds, int offset)
{
int numLines = numVerts / 2;
for (int i = 0; i < numLines; i++)
{
*inds_++ = index_ + i*2;
*inds_++ = index_ + i*2+1;
}
index_ += numVerts;
count_ += numLines * 2;
prim_ = GE_PRIM_LINES;
}
void IndexGenerator::TranslateLineStrip(int numVerts, const u16 *inds, int offset)
{
int numLines = numVerts - 1;
for (int i = 0; i < numLines; i++)
{
*inds_++ = index_ + i;
*inds_++ = index_ + i + 1;
}
index_ += numVerts;
count_ += numLines * 2;
prim_ = GE_PRIM_LINES;
}
void IndexGenerator::TranslateRectangles(int numVerts, const u8 *inds, int offset)
{
int numRects = numVerts / 2;
for (int i = 0; i < numRects; i++)
{
*inds_++ = index_ + i*2;
*inds_++ = index_ + i*2+1;
}
index_ += numVerts;
count_ += numRects * 2;
prim_ = GE_PRIM_RECTANGLES;
}
void IndexGenerator::TranslateRectangles(int numVerts, const u16 *inds, int offset)
{
int numRects = numVerts / 2;
for (int i = 0; i < numRects; i++)
{
*inds_++ = index_ + i*2;
*inds_++ = index_ + i*2+1;
}
index_ += numVerts;
count_ += numRects * 2;
prim_ = GE_PRIM_RECTANGLES;
}

76
GPU/GLES/IndexGenerator.h Normal file
View file

@ -0,0 +1,76 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "CommonTypes.h"
#include "../ge_constants.h"
class IndexGenerator
{
public:
void Setup(u16 *indexptr);
void Reset();
bool PrimCompatible(int prim);
int Prim() const { return prim_; }
// Points (why index these? code simplicity)
void AddPoints(int numVerts);
// Triangles
void AddList(int numVerts);
void AddStrip(int numVerts);
void AddFan(int numVerts);
// Lines
void AddLineList(int numVerts);
void AddLineStrip(int numVerts);
// Rectangles
void AddRectangles(int numVerts);
void TranslatePoints(int numVerts, const u8 *inds, int offset);
void TranslatePoints(int numVerts, const u16 *inds, int offset);
// Translates already indexed lists
void TranslateLineList(int numVerts, const u8 *inds, int offset);
void TranslateLineList(int numVerts, const u16 *inds, int offset);
void TranslateLineStrip(int numVerts, const u8 *inds, int offset);
void TranslateLineStrip(int numVerts, const u16 *inds, int offset);
void TranslateRectangles(int numVerts, const u8 *inds, int offset);
void TranslateRectangles(int numVerts, const u16 *inds, int offset);
void TranslateList(int numVerts, const u8 *inds, int offset);
void TranslateStrip(int numVerts, const u8 *inds, int offset);
void TranslateFan(int numVerts, const u8 *inds, int offset);
void TranslateList(int numVerts, const u16 *inds, int offset);
void TranslateStrip(int numVerts, const u16 *inds, int offset);
void TranslateFan(int numVerts, const u16 *inds, int offset);
int MaxIndex() { return index_; }
int VertexCount() { return count_; }
bool Empty() { return index_ == 0; }
void SetIndex(int ind) { index_ = ind; }
private:
u16 *indsBase_;
u16 *inds_;
int index_;
int count_;
int prim_;
};

View file

@ -79,27 +79,110 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs)
u_texenv = glGetUniformLocation(program, "u_texenv");
u_fogcolor = glGetUniformLocation(program, "u_fogcolor");
u_fogcoef = glGetUniformLocation(program, "u_fogcoef");
u_alpharef = glGetUniformLocation(program, "u_alpharef");
u_alphacolorref = glGetUniformLocation(program, "u_alphacolorref");
// Transform
u_view = glGetUniformLocation(program, "u_view");
u_world = glGetUniformLocation(program, "u_world");
u_texmtx = glGetUniformLocation(program, "u_texmtx");
for (int i = 0; i < 8; i++) {
char name[64];
sprintf(name, "u_bone%i", i);
u_bone[i] = glGetUniformLocation(program, name);
}
// Lighting, texturing
u_ambient = glGetUniformLocation(program, "u_ambient");
u_matambientalpha = glGetUniformLocation(program, "u_matambientalpha");
u_matdiffuse = glGetUniformLocation(program, "u_matdiffuse");
u_matspecular = glGetUniformLocation(program, "u_matspecular");
u_matemissive = glGetUniformLocation(program, "u_matemissive");
u_uvscaleoffset = glGetUniformLocation(program, "u_uvscaleoffset");
for (int i = 0; i < 4; i++) {
char temp[64];
sprintf(temp, "u_lightpos%i", i);
u_lightpos[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightdir%i", i);
u_lightdir[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightatt%i", i);
u_lightatt[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightambient%i", i);
u_lightambient[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightdiffuse%i", i);
u_lightdiffuse[i] = glGetUniformLocation(program, temp);
sprintf(temp, "u_lightspecular%i", i);
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_weight0123");
a_weight4567 = glGetAttribLocation(program, "a_weight4567");
glUseProgram(program);
// Default uniform values
glUniform1i(u_tex, 0);
// The rest, use the "dirty" mechanism.
dirtyUniforms = DIRTY_PROJMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_TEXENV | DIRTY_ALPHAREF;
dirtyUniforms = DIRTY_ALL;
}
LinkedShader::~LinkedShader() {
glDeleteProgram(program);
}
// Utility
static void SetColorUniform3(int uniform, u32 color)
{
const float col[3] = { ((color & 0xFF0000) >> 16) / 255.0f, ((color & 0xFF00) >> 8) / 255.0f, ((color & 0xFF)) / 255.0f};
glUniform3fv(uniform, 1, col);
}
static void SetColorUniform3Alpha(int uniform, u32 color, u8 alpha)
{
const float col[4] = { ((color & 0xFF0000) >> 16) / 255.0f, ((color & 0xFF00) >> 8) / 255.0f, ((color & 0xFF)) / 255.0f, alpha/255.0f};
glUniform4fv(uniform, 1, col);
}
static void SetColorUniform3ExtraFloat(int uniform, u32 color, float extra)
{
const float col[4] = { ((color & 0xFF0000) >> 16) / 255.0f, ((color & 0xFF00) >> 8) / 255.0f, ((color & 0xFF)) / 255.0f, extra};
glUniform4fv(uniform, 1, col);
}
static void SetMatrix4x3(int uniform, const float *m4x3) {
float m4x4[16];
m4x4[0] = m4x3[0];
m4x4[1] = m4x3[1];
m4x4[2] = m4x3[2];
m4x4[3] = 0.0f;
m4x4[4] = m4x3[3];
m4x4[5] = m4x3[4];
m4x4[6] = m4x3[5];
m4x4[7] = 0.0f;
m4x4[8] = m4x3[6];
m4x4[9] = m4x3[7];
m4x4[10] = m4x3[8];
m4x4[11] = 0.0f;
m4x4[12] = m4x3[9];
m4x4[13] = m4x3[10];
m4x4[14] = m4x3[11];
m4x4[15] = 1.0f;
glUniformMatrix4fv(uniform, 1, GL_FALSE, m4x4);
}
void LinkedShader::use() {
glUseProgram(program);
glUniform1i(u_tex, 0);
updateUniforms();
}
void LinkedShader::updateUniforms() {
if (!dirtyUniforms)
return;
// Update any dirty uniforms before we draw
if (u_proj != -1 && (dirtyUniforms & DIRTY_PROJMATRIX)) {
glUniformMatrix4fv(u_proj, 1, GL_FALSE, gstate.projMatrix);
@ -122,20 +205,69 @@ void LinkedShader::use() {
glUniformMatrix4fv(u_proj_through, 1, GL_FALSE, proj_through.getReadPtr());
}
if (u_texenv != -1 && (dirtyUniforms & DIRTY_TEXENV)) {
glUniform4f(u_texenv, 1.0, 1.0, 1.0, 1.0); // TODO
SetColorUniform3(u_texenv, gstate.texenvcolor);
}
if (u_alpharef != -1 && (dirtyUniforms & DIRTY_ALPHAREF)) {
glUniform4f(u_alpharef, ((float)((gstate.alphatest >> 8) & 0xFF)) / 255.0f, 0.0f, 0.0f, 0.0f);
if (u_alphacolorref != -1 && (dirtyUniforms & DIRTY_ALPHACOLORREF)) {
glUniform4f(u_alphacolorref, 0.0f, 0.0f, 0.0f, ((float)((gstate.alphatest >> 8) & 0xFF)) / 255.0f);
}
if (u_fogcolor != -1 && (dirtyUniforms & DIRTY_FOGCOLOR)) {
const float fogc[3] = { ((gstate.fogcolor & 0xFF0000) >> 16) / 255.0f, ((gstate.fogcolor & 0xFF00) >> 8) / 255.0f, ((gstate.fogcolor & 0xFF)) / 255.0f};
glUniform3fv(u_fogcolor, 1, fogc);
SetColorUniform3(u_fogcolor, gstate.fogcolor);
}
if (u_fogcoef != -1 && (dirtyUniforms & DIRTY_FOGCOEF)) {
const float fogcoef[2] = { getFloat24(gstate.fog1), getFloat24(gstate.fog2) };
glUniform2fv(u_fogcoef, 1, fogcoef);
}
// Texturing
if (u_uvscaleoffset != -1 && (dirtyUniforms & DIRTY_UVSCALEOFFSET)) {
const float uvscaleoff[4] = { gstate_c.uScale, gstate_c.vScale, gstate_c.uOff, gstate_c.vOff};
glUniform4fv(u_uvscaleoffset, 1, uvscaleoff);
}
// Transform
if (u_world != -1 && (dirtyUniforms & DIRTY_WORLDMATRIX)) {
SetMatrix4x3(u_world, gstate.worldMatrix);
}
if (u_view != -1 && (dirtyUniforms & DIRTY_VIEWMATRIX)) {
SetMatrix4x3(u_view, gstate.viewMatrix);
}
if (u_texmtx != -1 && (dirtyUniforms & DIRTY_TEXMATRIX)) {
SetMatrix4x3(u_texmtx, gstate.tgenMatrix);
}
for (int i = 0; i < 8; i++) {
if (u_bone[i] != -1 && (dirtyUniforms & (DIRTY_BONEMATRIX0 << i))) {
SetMatrix4x3(u_bone[i], gstate.boneMatrix + 12 * i);
}
}
// Lighting
if (u_ambient != -1 && (dirtyUniforms & DIRTY_AMBIENT)) {
SetColorUniform3Alpha(u_ambient, gstate.ambientcolor, gstate.ambientalpha & 0xFF);
}
if (u_matambientalpha != -1 && (dirtyUniforms & DIRTY_MATAMBIENTALPHA)) {
SetColorUniform3Alpha(u_matambientalpha, gstate.materialambient, gstate.materialalpha & 0xFF);
}
if (u_matdiffuse != -1 && (dirtyUniforms & DIRTY_MATDIFFUSE)) {
SetColorUniform3(u_matdiffuse, gstate.materialdiffuse);
}
if (u_matemissive != -1 && (dirtyUniforms & DIRTY_MATEMISSIVE)) {
SetColorUniform3(u_matemissive, gstate.materialemissive);
}
if (u_matspecular != -1 && (dirtyUniforms & DIRTY_MATSPECULAR)) {
SetColorUniform3ExtraFloat(u_matspecular, gstate.materialspecular, getFloat24(gstate.materialspecularcoef));
}
for (int i = 0; i < 4; i++) {
if (u_lightdiffuse[i] != -1 && (dirtyUniforms & (DIRTY_LIGHT0 << i))) {
glUniform3fv(u_lightpos[i], 1, gstate_c.lightpos[i]);
glUniform3fv(u_lightdir[i], 1, gstate_c.lightdir[i]);
glUniform3fv(u_lightatt[i], 1, gstate_c.lightatt[i]);
glUniform3fv(u_lightambient[i], 1, gstate_c.lightColor[0][i]);
glUniform3fv(u_lightdiffuse[i], 1, gstate_c.lightColor[1][i]);
glUniform3fv(u_lightspecular[i], 1, gstate_c.lightColor[2][i]);
}
}
dirtyUniforms = 0;
}
@ -170,6 +302,7 @@ void ShaderManager::DirtyShader()
// Forget the last shader ID
lastFSID.clear();
lastVSID.clear();
lastShader = 0;
}
@ -188,8 +321,11 @@ LinkedShader *ShaderManager::ApplyShader(int prim)
ComputeVertexShaderID(&VSID, prim);
ComputeFragmentShaderID(&FSID);
// Bail quickly in the no-op case. TODO: why does it cause trouble?
// if (VSID == lastVSID && FSID == lastFSID) return lastShader; // Already all set.
// Just update uniforms if this is the same shader as last time.
if (lastShader != 0 && VSID == lastVSID && FSID == lastFSID) {
lastShader->updateUniforms();
return lastShader; // Already all set.
}
lastVSID = VSID;
lastFSID = FSID;
@ -198,7 +334,7 @@ LinkedShader *ShaderManager::ApplyShader(int prim)
Shader *vs;
if (vsIter == vsCache.end()) {
// Vertex shader not in cache. Let's compile it.
char *shaderCode = GenerateVertexShader();
char *shaderCode = GenerateVertexShader(prim);
vs = new Shader(shaderCode, GL_VERTEX_SHADER);
vsCache[VSID] = vs;
} else {
@ -225,10 +361,9 @@ LinkedShader *ShaderManager::ApplyShader(int prim)
linkedShaderCache[linkedID] = ls;
} else {
ls = iter->second;
ls->use();
}
ls->use();
lastShader = ls;
return ls;
}

View file

@ -23,14 +23,16 @@
#include "VertexShaderGenerator.h"
#include "FragmentShaderGenerator.h"
struct Shader;
class Shader;
struct LinkedShader
class LinkedShader
{
public:
LinkedShader(Shader *vs, Shader *fs);
~LinkedShader();
void use();
void updateUniforms();
uint32_t program;
u32 dirtyUniforms;
@ -40,22 +42,39 @@ struct LinkedShader
int a_color0;
int a_color1;
int a_texcoord;
// int a_blendWeight0123;
// int a_blendWeight4567;
int a_normal;
int a_weight0123;
int a_weight4567;
int u_tex;
int u_proj;
int u_proj_through;
int u_texenv;
int u_view;
int u_texmtx;
int u_world;
int u_bone[8];
// Fragment processing inputs
int u_alpharef;
int u_alphacolorref;
int u_fogcolor;
int u_fogcoef;
// Texturing
int u_uvscaleoffset;
// Lighting
int u_ambientcolor;
int u_light[4]; // each light consist of vec4[3]
int u_ambient;
int u_matambientalpha;
int u_matdiffuse;
int u_matspecular;
int u_matemissive;
int u_lightpos[4];
int u_lightdir[4];
int u_lightatt[4]; // attenuation
int u_lightdiffuse[4]; // each light consist of vec4[3]
int u_lightspecular[4]; // attenuation
int u_lightambient[4]; // attenuation
};
// Will reach 32 bits soon :P
@ -66,18 +85,23 @@ enum
DIRTY_FOGCOLOR = (1 << 2),
DIRTY_FOGCOEF = (1 << 3),
DIRTY_TEXENV = (1 << 4),
DIRTY_ALPHAREF = (1 << 5),
DIRTY_ALPHACOLORREF = (1 << 5),
DIRTY_COLORREF = (1 << 6),
DIRTY_LIGHT0 = (1 << 12),
DIRTY_LIGHT1 = (1 << 13),
DIRTY_LIGHT2 = (1 << 14),
DIRTY_LIGHT3 = (1 << 15),
DIRTY_LIGHT0 = (1 << 8),
DIRTY_LIGHT1 = (1 << 9),
DIRTY_LIGHT2 = (1 << 10),
DIRTY_LIGHT3 = (1 << 11),
DIRTY_GLOBALAMBIENT = (1 << 16),
DIRTY_MATDIFFUSE = (1 << 12),
DIRTY_MATSPECULAR = (1 << 13),
DIRTY_MATEMISSIVE = (1 << 14),
DIRTY_AMBIENT = (1 << 15),
DIRTY_MATAMBIENTALPHA = (1 << 16),
DIRTY_MATERIAL = (1 << 17), // let's set all 4 together (emissive ambient diffuse specular). We hide specular coef in specular.a
DIRTY_UVSCALEOFFSET = (1 << 18), // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares"
DIRTY_WORLDMATRIX = (1 << 21),
DIRTY_VIEWMATRIX = (1 << 22), // Maybe we'll fold this into projmatrix eventually
DIRTY_TEXMATRIX = (1 << 23),
DIRTY_BONEMATRIX0 = (1 << 24),
@ -94,11 +118,12 @@ enum
// Real public interface
struct Shader
{
class Shader {
public:
Shader(const char *code, uint32_t shaderType);
uint32_t shader;
const std::string &source() const { return source_; }
private:
std::string source_;
};

View file

@ -1,4 +1,11 @@
#include "StateMapping.h"
#include "../../native/gfx_es2/gl_state.h"
#include "../Math3D.h"
#include "../GPUState.h"
#include "../ge_constants.h"
#include "DisplayListInterpreter.h"
#include "ShaderManager.h"
const GLint aLookup[] = {
GL_DST_COLOR,
@ -51,3 +58,165 @@ const GLuint ztests[] =
GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL,
GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL,
};
void GLES_GPU::ApplyDrawState()
{
// TODO: All this setup is soon so expensive that we'll need dirty flags, or simply do it in the command writes where we detect dirty by xoring. Silly to do all this work on every drawcall.
// TODO: The top bit of the alpha channel should be written to the stencil bit somehow. This appears to require very expensive multipass rendering :( Alternatively, one could do a
// single fullscreen pass that converts alpha to stencil (or 2 passes, to set both the 0 and 1 values) very easily.
// Set cull
bool wantCull = !gstate.isModeClear() && !gstate.isModeThrough() && gstate.isCullEnabled();
glstate.cullFace.set(wantCull);
if(wantCull) {
u8 cullMode = gstate.getCullMode();
glstate.cullFaceMode.set(cullingMode[cullMode]);
}
// Set blend
bool wantBlend = !gstate.isModeClear() && (gstate.alphaBlendEnable & 1);
glstate.blend.set(wantBlend);
if(wantBlend) {
// This can't be done exactly as there are several PSP blend modes that are impossible to do on OpenGL ES 2.0, and some even on regular OpenGL for desktop.
// HOWEVER - we should be able to approximate the 2x modes in the shader, although they will clip wrongly.
// Examples of seen unimplementable blend states:
// Mortal Kombat Unchained: FixA=0000ff FixB=000080 FuncA=10 FuncB=10
int blendFuncA = gstate.getBlendFuncA();
int blendFuncB = gstate.getBlendFuncB();
int blendFuncEq = gstate.getBlendEq();
glstate.blendEquation.set(eqLookup[blendFuncEq]);
if (blendFuncA != GE_SRCBLEND_FIXA && blendFuncB != GE_DSTBLEND_FIXB) {
// All is valid, no blendcolor needed
glstate.blendFunc.set(aLookup[blendFuncA], bLookup[blendFuncB]);
} else {
GLuint glBlendFuncA = blendFuncA == GE_SRCBLEND_FIXA ? GL_INVALID_ENUM : aLookup[blendFuncA];
GLuint glBlendFuncB = blendFuncB == GE_DSTBLEND_FIXB ? GL_INVALID_ENUM : bLookup[blendFuncB];
u32 fixA = gstate.getFixA();
u32 fixB = gstate.getFixB();
// Shortcut by using GL_ONE where possible, no need to set blendcolor
if (glBlendFuncA == GL_INVALID_ENUM && blendFuncA == GE_SRCBLEND_FIXA) {
if (fixA == 0xFFFFFF)
glBlendFuncA = GL_ONE;
else if (fixA == 0)
glBlendFuncA = GL_ZERO;
}
if (glBlendFuncB == GL_INVALID_ENUM && blendFuncB == GE_DSTBLEND_FIXB) {
if (fixB == 0xFFFFFF)
glBlendFuncB = GL_ONE;
else if (fixB == 0)
glBlendFuncB = GL_ZERO;
}
if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB != GL_INVALID_ENUM) {
// Can use blendcolor trivially.
const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f};
glstate.blendColor.set(blendColor);
glBlendFuncA = GL_CONSTANT_COLOR;
} else if (glBlendFuncA != GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) {
// Can use blendcolor trivially.
const float blendColor[4] = {(fixB & 0xFF)/255.0f, ((fixB >> 8) & 0xFF)/255.0f, ((fixB >> 16) & 0xFF)/255.0f, 1.0f};
glstate.blendColor.set(blendColor);
glBlendFuncB = GL_CONSTANT_COLOR;
} else if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) { // Should also check for approximate equality
if (fixA == (fixB ^ 0xFFFFFF)) {
glBlendFuncA = GL_CONSTANT_COLOR;
glBlendFuncB = GL_ONE_MINUS_CONSTANT_COLOR;
const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f};
glstate.blendColor.set(blendColor);
} else if (fixA == fixB) {
glBlendFuncA = GL_CONSTANT_COLOR;
glBlendFuncB = GL_CONSTANT_COLOR;
const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f};
glstate.blendColor.set(blendColor);
} else {
DEBUG_LOG(HLE, "ERROR INVALID blendcolorstate: FixA=%06x FixB=%06x FuncA=%i FuncB=%i", gstate.getFixA(), gstate.getFixB(), gstate.getBlendFuncA(), gstate.getBlendFuncB());
glBlendFuncA = GL_ONE;
glBlendFuncB = GL_ONE;
}
}
// At this point, through all paths above, glBlendFuncA and glBlendFuncB will be set somehow.
glstate.blendFunc.set(glBlendFuncA, glBlendFuncB);
}
}
bool wantDepthTest = gstate.isModeClear() || gstate.isDepthTestEnabled();
glstate.depthTest.set(wantDepthTest);
if(wantDepthTest) {
// Force GL_ALWAYS if mode clear
int depthTestFunc = gstate.isModeClear() ? 1 : gstate.getDepthTestFunc();
glstate.depthFunc.set(ztests[depthTestFunc]);
}
bool wantDepthWrite = gstate.isModeClear() || gstate.isDepthWriteEnabled();
glstate.depthWrite.set(wantDepthWrite ? GL_TRUE : GL_FALSE);
float depthRangeMin = gstate_c.zOff - gstate_c.zScale;
float depthRangeMax = gstate_c.zOff + gstate_c.zScale;
glstate.depthRange.set(depthRangeMin, depthRangeMax);
}
void GLES_GPU::UpdateViewportAndProjection()
{
bool throughmode = (gstate.vertType & GE_VTYPE_THROUGH_MASK) != 0;
// We can probably use these to simply set scissors? Maybe we need to offset by regionX1/Y1
int regionX1 = gstate.region1 & 0x3FF;
int regionY1 = (gstate.region1 >> 10) & 0x3FF;
int regionX2 = (gstate.region2 & 0x3FF) + 1;
int regionY2 = ((gstate.region2 >> 10) & 0x3FF) + 1;
float offsetX = (float)(gstate.offsetx & 0xFFFF) / 16.0f;
float offsetY = (float)(gstate.offsety & 0xFFFF) / 16.0f;
if (throughmode) {
// No viewport transform here. Let's experiment with using region.
return;
glViewport((0 + regionX1) * renderWidthFactor_, (0 - regionY1) * renderHeightFactor_, (regionX2 - regionX1) * renderWidthFactor_, (regionY2 - regionY1) * renderHeightFactor_);
} else {
// These we can turn into a glViewport call, offset by offsetX and offsetY. Math after.
float vpXa = getFloat24(gstate.viewportx1);
float vpXb = getFloat24(gstate.viewportx2);
float vpYa = getFloat24(gstate.viewporty1);
float vpYb = getFloat24(gstate.viewporty2);
float vpZa = getFloat24(gstate.viewportz1); // / 65536.0f should map it to OpenGL's 0.0-1.0 Z range
float vpZb = getFloat24(gstate.viewportz2); // / 65536.0f
// The viewport transform appears to go like this:
// Xscreen = -offsetX + vpXb + vpXa * Xview
// Yscreen = -offsetY + vpYb + vpYa * Yview
// Zscreen = vpZb + vpZa * Zview
// This means that to get the analogue glViewport we must:
float vpX0 = vpXb - offsetX - vpXa;
float vpY0 = vpYb - offsetY + vpYa; // Need to account for sign of Y
gstate_c.vpWidth = vpXa * 2;
gstate_c.vpHeight = -vpYa * 2;
return;
float vpWidth = fabsf(gstate_c.vpWidth);
float vpHeight = fabsf(gstate_c.vpHeight);
// TODO: These two should feed into glDepthRange somehow.
float vpZ0 = (vpZb - vpZa) / 65536.0f;
float vpZ1 = (vpZa * 2) / 65536.0f;
vpX0 *= renderWidthFactor_;
vpY0 *= renderHeightFactor_;
vpWidth *= renderWidthFactor_;
vpHeight *= renderHeightFactor_;
// Flip vpY0 to match the OpenGL coordinate system.
vpY0 = renderHeight_ - (vpY0 + vpHeight);
glViewport(vpX0, vpY0, vpWidth, vpHeight);
// Sadly, as glViewport takes integers, we will not be able to support sub pixel offsets this way. But meh.
shaderManager_->DirtyUniform(DIRTY_PROJMATRIX);
}
}

View file

@ -106,6 +106,26 @@ void TextureCache_Decimate()
}
}
void TextureCache_Invalidate(u32 addr, int size)
{
u32 addr_end = addr + size;
for (TexCache::iterator iter = cache.begin(); iter != cache.end(); )
{
// Clear if either the addr or clutaddr is in the range.
bool invalidate = iter->second.addr >= addr && iter->second.addr < addr_end;
invalidate |= iter->second.clutaddr >= addr && iter->second.clutaddr < addr_end;
if (invalidate)
{
glDeleteTextures(1, &iter->second.texture);
cache.erase(iter++);
}
else
++iter;
}
}
int TextureCache_NumLoadedTextures()
{
return cache.size();
@ -597,17 +617,16 @@ void convertColors(u8 *finalBuf, GLuint dstFmt, int numPixels)
void PSPSetTexture()
{
static int lastBoundTexture = -1;
u32 texaddr = (gstate.texaddr[0] & 0xFFFFF0) | ((gstate.texbufwidth[0]<<8) & 0xFF000000);
texaddr &= 0xFFFFFFF;
if (!texaddr) return;
u8 level = 0;
u32 format = gstate.texformat & 0xF;
u32 clutformat = gstate.clutformat & 3;
u32 clutaddr = GetClutAddr(clutformat == GE_CMODE_32BIT_ABGR8888 ? 4 : 2);
DEBUG_LOG(G3D,"Texture at %08x",texaddr);
u8 *texptr = Memory::GetPointer(texaddr);
u32 texhash = texptr ? *(u32*)texptr : 0;
@ -636,8 +655,11 @@ void PSPSetTexture()
if (match) {
//got one!
entry.frameCounter = gpuStats.numFrames;
glBindTexture(GL_TEXTURE_2D, entry.texture);
UpdateSamplingParams();
if (true || entry.texture != lastBoundTexture) {
glBindTexture(GL_TEXTURE_2D, entry.texture);
UpdateSamplingParams();
lastBoundTexture = entry.texture;
}
DEBUG_LOG(G3D, "Texture at %08x Found in Cache, applying", texaddr);
return; //Done!
} else {
@ -653,7 +675,7 @@ void PSPSetTexture()
//we have to decode it
TexCacheEntry entry;
TexCacheEntry entry = {0};
entry.addr = texaddr;
entry.hash = texhash;
@ -671,9 +693,6 @@ void PSPSetTexture()
entry.clutaddr = 0;
}
glGenTextures(1, &entry.texture);
glBindTexture(GL_TEXTURE_2D, entry.texture);
int bufw = gstate.texbufwidth[0] & 0x3ff;
entry.dim = gstate.texsize[0] & 0xF0F;
@ -681,8 +700,6 @@ void PSPSetTexture()
int w = 1 << (gstate.texsize[0] & 0xf);
int h = 1 << ((gstate.texsize[0]>>8) & 0xf);
INFO_LOG(G3D, "Creating texture %i from %08x: %i x %i (stride: %i). fmt: %i", entry.texture, entry.addr, w, h, bufw, entry.format);
gstate_c.curTextureWidth=w;
gstate_c.curTextureHeight=h;
GLenum dstFmt = 0;
@ -932,19 +949,20 @@ void PSPSetTexture()
}
}
gpuStats.numTexturesDecoded++;
// Can restore these and remove the above fixup on some platforms.
//glPixelStorei(GL_UNPACK_ROW_LENGTH, bufw);
glPixelStorei(GL_UNPACK_ALIGNMENT, texByteAlign);
//glPixelStorei(GL_PACK_ROW_LENGTH, bufw);
glPixelStorei(GL_PACK_ALIGNMENT, texByteAlign);
INFO_LOG(G3D, "Creating texture %i from %08x: %i x %i (stride: %i). fmt: %i", entry.texture, entry.addr, w, h, bufw, entry.format);
glGenTextures(1, &entry.texture);
glBindTexture(GL_TEXTURE_2D, entry.texture);
lastBoundTexture = entry.texture;
GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA;
glTexImage2D(GL_TEXTURE_2D, 0, components, w, h, 0, components, dstFmt, finalBuf);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glGenerateMipmap(GL_TEXTURE_2D);
UpdateSamplingParams();

View file

@ -25,4 +25,5 @@ void TextureCache_Init();
void TextureCache_Shutdown();
void TextureCache_Clear(bool delete_them);
void TextureCache_Decimate(); // Run this once per frame to get rid of old textures.
void TextureCache_Invalidate(u32 addr, int size);
int TextureCache_NumLoadedTextures();

View file

@ -30,9 +30,9 @@
#include "VertexDecoder.h"
#include "ShaderManager.h"
#include "DisplayListInterpreter.h"
#include "IndexGenerator.h"
GLuint glprim[8] =
{
const GLuint glprim[8] = {
GL_POINTS,
GL_LINES,
GL_LINE_STRIP,
@ -42,10 +42,16 @@ GLuint glprim[8] =
GL_TRIANGLES, // With OpenGL ES we have to expand sprites into triangles, tripling the data instead of doubling. sigh. OpenGL ES, Y U NO SUPPORT GL_QUADS?
};
DecodedVertex decoded[65536];
u8 decoded[65536 * 32];
VertexDecoder dec;
uint16_t decIndex[65536];
int numVerts;
IndexGenerator indexGen;
TransformedVertex transformed[65536];
TransformedVertex transformedExpanded[65536];
uint16_t indexBuffer[65536]; // Unused
// TODO: This should really return 2 colors, one for specular and one for diffuse.
@ -64,7 +70,7 @@ private:
Color4 materialDiffuse;
Color4 materialSpecular;
float specCoef_;
Vec3 viewer_;
// Vec3 viewer_;
bool doShadeMapping_;
int materialUpdate_;
};
@ -87,7 +93,7 @@ Lighter::Lighter() {
materialSpecular.GetFromRGB(gstate.materialspecular);
materialSpecular.a = 1.0f;
specCoef_ = getFloat24(gstate.materialspecularcoef);
viewer_ = Vec3(-gstate.viewMatrix[9], -gstate.viewMatrix[10], -gstate.viewMatrix[11]);
// viewer_ = Vec3(-gstate.viewMatrix[9], -gstate.viewMatrix[10], -gstate.viewMatrix[11]);
materialUpdate_ = gstate.materialupdate & 7;
}
@ -143,14 +149,6 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[
bool doSpecular = (comp != GE_LIGHTCOMP_ONLYDIFFUSE);
bool poweredDiffuse = comp == GE_LIGHTCOMP_BOTHWITHPOWDIFFUSE;
float lightScale = 1.0f;
if (type != GE_LIGHTTYPE_DIRECTIONAL)
{
float distance = toLight.Normalize();
lightScale = 1.0f / (gstate_c.lightatt[l][0] + gstate_c.lightatt[l][1]*distance + gstate_c.lightatt[l][2]*distance*distance);
if (lightScale > 1.0f) lightScale = 1.0f;
}
float dot = toLight * norm;
// Clamp dot to zero.
@ -159,7 +157,16 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[
if (poweredDiffuse)
dot = powf(dot, specCoef_);
Color4 diff = (gstate_c.lightColor[1][l] * *diffuse) * (dot * lightScale);
float lightScale = 1.0f;
float distance = toLight.Normalize();
if (type != GE_LIGHTTYPE_DIRECTIONAL)
{
lightScale = 1.0f / (gstate_c.lightatt[l][0] + gstate_c.lightatt[l][1]*distance + gstate_c.lightatt[l][2]*distance*distance);
if (lightScale > 1.0f) lightScale = 1.0f;
}
Color4 lightDiff(gstate_c.lightColor[1][l], 0.0f);
Color4 diff = (lightDiff * *diffuse) * (dot * lightScale);
// Real PSP specular
Vec3 toViewer(0,0,1);
@ -175,13 +182,15 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[
dot = halfVec * norm;
if (dot >= 0)
{
lightSum1 += (gstate_c.lightColor[2][l] * *specular * (powf(dot, specCoef_)*lightScale));
Color4 lightSpec(gstate_c.lightColor[2][l], 0.0f);
lightSum1 += (lightSpec * *specular * (powf(dot, specCoef_)*lightScale));
}
}
dots[l] = dot;
if (gstate.lightEnable[l] & 1)
{
lightSum0 += gstate_c.lightColor[0][l] * *ambient + diff;
Color4 lightAmbient(gstate_c.lightColor[2][l], 1.0f);
lightSum0 += lightAmbient * *ambient + diff;
}
}
@ -192,58 +201,88 @@ void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[
}
}
struct GlTypeInfo {
GLuint type;
int count;
GLboolean normalized;
};
const GlTypeInfo GLComp[8] = {
{0}, // DEC_NONE,
{GL_FLOAT, 1, GL_FALSE}, // DEC_FLOAT_1,
{GL_FLOAT, 2, GL_FALSE}, // DEC_FLOAT_2,
{GL_FLOAT, 3, GL_FALSE}, // DEC_FLOAT_3,
{GL_FLOAT, 4, GL_FALSE}, // DEC_FLOAT_4,
{GL_BYTE, 3, GL_TRUE}, // DEC_S8_3,
{GL_SHORT, 3, GL_TRUE},// DEC_S16_3,
{GL_UNSIGNED_BYTE, 4, GL_TRUE},// DEC_U8_4,
};
static inline void VertexAttribSetup(int attrib, int fmt, int stride, u8 *ptr) {
if (attrib != -1 && fmt) {
const GlTypeInfo &type = GLComp[fmt];
glEnableVertexAttribArray(attrib);
glVertexAttribPointer(attrib, type.count, type.type, type.normalized, stride, ptr);
}
}
static inline void VertexAttribDisable(int attrib, int fmt) {
if (attrib != -1 && fmt) {
glDisableVertexAttribArray(attrib);
}
}
// 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);
}
static void DesetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt) {
VertexAttribDisable(program->a_weight0123, decFmt.w0fmt);
VertexAttribDisable(program->a_weight4567, decFmt.w1fmt);
VertexAttribDisable(program->a_texcoord, decFmt.uvfmt);
VertexAttribDisable(program->a_color0, decFmt.c0fmt);
VertexAttribDisable(program->a_color1, decFmt.c1fmt);
VertexAttribDisable(program->a_normal, decFmt.nrmfmt);
VertexAttribDisable(program->a_position, decFmt.posfmt);
}
// This is the software transform pipeline, which is necessary for supporting RECT
// primitives correctly. Other primitives are possible to transform and light in hardware
// using vertex shader, which will be way, way faster, especially on mobile. This has
// not yet been implemented though.
void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int vertexCount, float *customUV, int forceIndexType, int *bytesRead)
// primitives correctly, and may be easier to use for debugging than the hardware
// transform pipeline.
// There's code here that simply expands transformed RECTANGLES into plain triangles.
// We're gonna have to keep software transforming RECTANGLES, unless we use a geom shader which we can't on OpenGL ES 2.0.
// Usually, though, these primitives don't use lighting etc so it's no biggie performance wise, but it would be nice to get rid of
// this code.
// Actually, if we find the camera-relative right and down vectors, it might even be possible to add the extra points in pre-transformed
// space and thus make decent use of hardware transform.
// Actually again, single quads could be drawn more efficiently using GL_TRIANGLE_STRIP, no need to duplicate verts as for
// GL_TRIANGLES. Still need to sw transform to compute the extra two corners though.
void SoftwareTransformAndDraw(int prim, LinkedShader *program, int vertexCount, void *inds, int indexType, const DecVtxFormat &decVtxFormat, int maxIndex)
{
int indexLowerBound, indexUpperBound;
// First, decode the verts and apply morphing
VertexDecoder dec;
dec.SetVertexType(gstate.vertType);
dec.DecodeVerts(decoded, verts, inds, prim, vertexCount, &indexLowerBound, &indexUpperBound);
#if 0
for (int i = indexLowerBound; i <= indexUpperBound; i++) {
PrintDecodedVertex(decoded[i], gstate.vertType);
}
#endif
bool useTexCoord = false;
/*
DEBUG_LOG(G3D, "View matrix:");
const float *m = &gstate.viewMatrix[0];
DEBUG_LOG(G3D, "%f %f %f", m[0], m[1], m[2]);
DEBUG_LOG(G3D, "%f %f %f", m[3], m[4], m[5]);
DEBUG_LOG(G3D, "%f %f %f", m[6], m[7], m[8]);
DEBUG_LOG(G3D, "%f %f %f", m[9], m[10], m[11]);
*/
// Check if anything needs updating
if (gstate_c.textureChanged)
{
if ((gstate.textureMapEnable & 1) && !gstate.isModeClear())
{
PSPSetTexture();
useTexCoord = true;
}
}
gpuStats.numDrawCalls++;
gpuStats.numVertsTransformed += vertexCount;
if (bytesRead)
*bytesRead = vertexCount * dec.VertexSize();
bool throughmode = (gstate.vertType & GE_VTYPE_THROUGH_MASK) != 0;
// Then, transform and draw in one big swoop (urgh!)
// need to move this to the shader.
// We're gonna have to keep software transforming RECTANGLES, unless we use a geom shader which we can't on OpenGL ES 2.0.
// Usually, though, these primitives don't use lighting etc so it's no biggie performance wise, but it would be nice to get rid of
// this code.
// Actually, if we find the camera-relative right and down vectors, it might even be possible to add the extra points in pre-transformed
// space and thus make decent use of hardware transform.
// Actually again, single quads could be drawn more efficiently using GL_TRIANGLE_STRIP, no need to duplicate verts as for
// GL_TRIANGLES. Still need to sw transform to compute the extra two corners though.
// Temporary storage for RECTANGLES emulation
float v2[3] = {0};
float uv2[2] = {0};
// TODO: Could use glDrawElements in some cases, see below.
bool throughmode = (gstate.vertType & GE_VTYPE_THROUGH_MASK) != 0;
// TODO: Split up into multiple draw calls for GLES 2.0 where you can't guarantee support for more than 0x10000 verts.
@ -254,8 +293,11 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte
Lighter lighter;
for (int index = indexLowerBound; index <= indexUpperBound; index++)
VertexReader reader(decoded, decVtxFormat);
for (int index = 0; index < maxIndex; index++)
{
reader.Goto(index);
float v[3] = {0, 0, 0};
float c0[4] = {1, 1, 1, 1};
float c1[4] = {0, 0, 0, 0};
@ -264,11 +306,10 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte
if (throughmode)
{
// Do not touch the coordinates or the colors. No lighting.
for (int j=0; j<3; j++)
v[j] = decoded[index].pos[j];
if(dec.hasColor()) {
for (int j=0; j<4; j++) {
c0[j] = decoded[index].color[j] / 255.0f;
reader.ReadPos(v);
if (reader.hasColor0()) {
reader.ReadColor0(c0);
for (int j = 0; j < 4; j++) {
c1[j] = 0.0f;
}
}
@ -280,48 +321,68 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte
c0[3] = (gstate.materialalpha & 0xFF) / 255.f;
}
// TODO : check if has uv
for (int j=0; j<2; j++)
uv[j] = decoded[index].uv[j];
// Rescale UV?
if (reader.hasUV()) {
reader.ReadUV(uv);
}
// Scale UV?
}
else
{
// We do software T&L for now
float out[3], norm[3];
float pos[3], nrm[3] = {0};
reader.ReadPos(pos);
if (reader.hasNormal())
reader.ReadNrm(nrm);
if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) == GE_VTYPE_WEIGHT_NONE)
{
Vec3ByMatrix43(out, decoded[index].pos, gstate.worldMatrix);
Norm3ByMatrix43(norm, decoded[index].normal, gstate.worldMatrix);
Vec3ByMatrix43(out, pos, gstate.worldMatrix);
if (reader.hasNormal()) {
Norm3ByMatrix43(norm, nrm, gstate.worldMatrix);
} else {
memset(norm, 0, 12);
}
}
else
{
float weights[8];
reader.ReadWeights(weights);
// Skinning
Vec3 psum(0,0,0);
Vec3 nsum(0,0,0);
int nweights = ((gstate.vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT) + 1;
for (int i = 0; i < nweights; i++)
{
if (decoded[index].weights[i] != 0.0f) {
Vec3ByMatrix43(out, decoded[index].pos, gstate.boneMatrix+i*12);
Norm3ByMatrix43(norm, decoded[index].normal, gstate.boneMatrix+i*12);
Vec3 tpos(out), tnorm(norm);
psum += tpos*decoded[index].weights[i];
nsum += tnorm*decoded[index].weights[i];
if (weights[i] != 0.0f) {
Vec3ByMatrix43(out, pos, gstate.boneMatrix+i*12);
Vec3 tpos(out);
psum += tpos * weights[i];
if (reader.hasNormal()) {
Norm3ByMatrix43(norm, nrm, gstate.boneMatrix+i*12);
Vec3 tnorm(norm);
nsum += tnorm * weights[i];
}
}
}
nsum.Normalize();
// Yes, we really must multiply by the world matrix too.
Vec3ByMatrix43(out, psum.v, gstate.worldMatrix);
Norm3ByMatrix43(norm, nsum.v, gstate.worldMatrix);
if (reader.hasNormal()) {
Norm3ByMatrix43(norm, nsum.v, gstate.worldMatrix);
}
}
// Perform lighting here if enabled. don't need to check through, it's checked above.
float dots[4] = {0,0,0,0};
float unlitColor[4];
for (int j = 0; j < 4; j++) {
unlitColor[j] = decoded[index].color[j] / 255.0f;
float unlitColor[4] = {1, 1, 1, 1};
if (reader.hasColor0()) {
reader.ReadColor0(unlitColor);
} else {
unlitColor[0] = (gstate.materialambient & 0xFF) / 255.f;
unlitColor[1] = ((gstate.materialambient >> 8) & 0xFF) / 255.f;
unlitColor[2] = ((gstate.materialambient >> 16) & 0xFF) / 255.f;
unlitColor[3] = (gstate.materialalpha & 0xFF) / 255.f;
}
float litColor0[4];
float litColor1[4];
@ -329,7 +390,7 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte
if (gstate.lightingEnable & 1)
{
// TODO: don't ignore gstate.lmode - we should send two colors in that case
// Don't ignore gstate.lmode - we should send two colors in that case
if (gstate.lmode & 1) {
// Separate colors
for (int j = 0; j < 4; j++) {
@ -346,7 +407,7 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte
}
else
{
if(dec.hasColor()) {
if (reader.hasColor0()) {
for (int j = 0; j < 4; j++) {
c0[j] = unlitColor[j];
c1[j] = 0.0f;
@ -359,29 +420,28 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte
}
}
if (customUV) {
uv[0] = customUV[index * 2 + 0]*gstate_c.uScale + gstate_c.uOff;
uv[1] = customUV[index * 2 + 1]*gstate_c.vScale + gstate_c.vOff;
} else {
if (reader.hasUV()) {
float ruv[2];
reader.ReadUV(ruv);
// Perform texture coordinate generation after the transform and lighting - one style of UV depends on lights.
switch (gstate.texmapmode & 0x3)
switch (gstate.getUVGenMode())
{
case 0: // UV mapping
// Texture scale/offset is only performed in this mode.
uv[0] = decoded[index].uv[0]*gstate_c.uScale + gstate_c.uOff;
uv[1] = decoded[index].uv[1]*gstate_c.vScale + gstate_c.vOff;
uv[0] = ruv[0]*gstate_c.uScale + gstate_c.uOff;
uv[1] = ruv[1]*gstate_c.vScale + gstate_c.vOff;
break;
case 1:
{
// Projection mapping
Vec3 source;
switch ((gstate.texmapmode >> 8) & 0x3)
switch (gstate.getUVProjMode())
{
case 0: // Use model space XYZ as source
source = decoded[index].pos;
source = pos;
break;
case 1: // Use unscaled UV as source
source = Vec3(decoded[index].uv[0], decoded[index].uv[1], 0.0f);
source = Vec3(ruv[0], ruv[1], 0.0f);
break;
case 2: // Use normalized normal as source
source = Vec3(norm).Normalized();
@ -390,6 +450,7 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte
source = Vec3(norm);
break;
}
float uvw[3];
Vec3ByMatrix43(uvw, &source.x, gstate.tgenMatrix);
uv[0] = uvw[0];
@ -397,12 +458,10 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte
}
break;
case 2:
// Shade mapping
// Shade mapping - use dot products from light sources to generate U and V.
{
int lightsource1 = gstate.texshade & 0x3;
int lightsource2 = (gstate.texshade >> 8) & 0x3;
uv[0] = dots[lightsource1];
uv[1] = dots[lightsource2];
uv[0] = dots[gstate.getUVLS0()];
uv[1] = dots[gstate.getUVLS1()];
}
break;
case 3:
@ -412,66 +471,33 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte
}
// Transform the coord by the view matrix.
// We only really need to do it here for RECTANGLES drawing. However,
// there's no point in optimizing it out because all other primitives
// will be moved to hardware transform anyway.
Vec3ByMatrix43(v, out, gstate.viewMatrix);
}
// TODO: Write to a flexible buffer, we don't always need all four components.
memcpy(&transformed[index].x, v, 3 * sizeof(float));
memcpy(&transformed[index].uv, uv, 2 * sizeof(float));
memcpy(&transformed[index].color0, c0, 4 * sizeof(float));
memcpy(&transformed[index].color1, c1, 4 * sizeof(float));
memcpy(&transformed[index].color1, c1, 3 * sizeof(float));
}
// Step 2: Expand using the index buffer, and expand rectangles.
// Step 2: expand rectangles.
const TransformedVertex *drawBuffer = transformed;
int numTrans = 0;
int indexType = (gstate.vertType & GE_VTYPE_IDX_MASK);
if (forceIndexType != -1) {
indexType = forceIndexType;
}
bool drawIndexed = false;
GLuint glIndexType = 0;
if (prim != GE_PRIM_RECTANGLES) {
// We can simply draw the unexpanded buffer.
numTrans = vertexCount;
switch (indexType) {
case GE_VTYPE_IDX_8BIT:
drawIndexed = true;
glIndexType = GL_UNSIGNED_BYTE;
break;
case GE_VTYPE_IDX_16BIT:
drawIndexed = true;
glIndexType = GL_UNSIGNED_SHORT;
break;
default:
drawIndexed = false;
break;
}
drawIndexed = true;
} else {
numTrans = 0;
drawBuffer = transformedExpanded;
TransformedVertex *trans = &transformedExpanded[0];
TransformedVertex saved;
for (int i = 0; i < vertexCount; i++) {
int index;
if (indexType == GE_VTYPE_IDX_8BIT)
{
index = ((u8*)inds)[i];
}
else if (indexType == GE_VTYPE_IDX_16BIT)
{
index = ((u16*)inds)[i];
}
else
{
index = i;
}
int index = ((u16*)inds)[i];
TransformedVertex &transVtx = transformed[index];
if ((i & 1) == 0)
@ -526,181 +552,145 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte
}
}
// TODO: All this setup is soon so expensive that we'll need dirty flags, or simply do it in the command writes where we detect dirty by xoring. Silly to do all this work on every drawcall.
// TODO: The top bit of the alpha channel should be written to the stencil bit somehow. This appears to require very expensive multipass rendering :( Alternatively, one could do a
// single fullscreen pass that converts alpha to stencil (or 2 passes, to set both the 0 and 1 values) very easily.
// Set cull
bool wantCull = !gstate.isModeClear() && !gstate.isModeThrough() && gstate.isCullEnabled();
glstate.cullFace.set(wantCull);
if(wantCull) {
u8 cullMode = gstate.getCullMode();
glstate.cullFaceMode.set(cullingMode[cullMode]);
}
// Set blend
bool wantBlend = !gstate.isModeClear() && (gstate.alphaBlendEnable & 1);
glstate.blend.set(wantBlend);
if(wantBlend) {
// This can't be done exactly as there are several PSP blend modes that are impossible to do on OpenGL ES 2.0, and some even on regular OpenGL for desktop.
// HOWEVER - we should be able to approximate the 2x modes in the shader, although they will clip wrongly.
int blendFuncA = gstate.getBlendFuncA();
int blendFuncB = gstate.getBlendFuncB();
int blendFuncEq = gstate.getBlendEq();
glstate.blendEquation.set(eqLookup[blendFuncEq]);
if (blendFuncA != GE_SRCBLEND_FIXA && blendFuncB != GE_DSTBLEND_FIXB) {
// All is valid, no blendcolor needed
glstate.blendFunc.set(aLookup[blendFuncA], bLookup[blendFuncB]);
} else {
GLuint glBlendFuncA = blendFuncA == GE_SRCBLEND_FIXA ? GL_INVALID_ENUM : aLookup[blendFuncA];
GLuint glBlendFuncB = blendFuncB == GE_DSTBLEND_FIXB ? GL_INVALID_ENUM : bLookup[blendFuncB];
u32 fixA = gstate.getFixA();
u32 fixB = gstate.getFixB();
// Shortcut by using GL_ONE where possible, no need to set blendcolor
if (glBlendFuncA == GL_INVALID_ENUM && blendFuncA == GE_SRCBLEND_FIXA) {
if (fixA == 0xFFFFFF)
glBlendFuncA = GL_ONE;
else if (fixA == 0)
glBlendFuncA = GL_ZERO;
}
if (glBlendFuncB == GL_INVALID_ENUM && blendFuncB == GE_DSTBLEND_FIXB) {
if (fixB == 0xFFFFFF)
glBlendFuncB = GL_ONE;
else if (fixB == 0)
glBlendFuncB = GL_ZERO;
}
if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB != GL_INVALID_ENUM) {
// Can use blendcolor trivially.
const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f};
glstate.blendColor.set(blendColor);
glBlendFuncA = GL_CONSTANT_COLOR;
} else if (glBlendFuncA != GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) {
// Can use blendcolor trivially.
const float blendColor[4] = {(fixB & 0xFF)/255.0f, ((fixB >> 8) & 0xFF)/255.0f, ((fixB >> 16) & 0xFF)/255.0f, 1.0f};
glstate.blendColor.set(blendColor);
glBlendFuncB = GL_CONSTANT_COLOR;
} else if (glBlendFuncA == GL_INVALID_ENUM && glBlendFuncB == GL_INVALID_ENUM) { // Should also check for approximate equality
if (fixA == (fixB ^ 0xFFFFFF)) {
glBlendFuncA = GL_CONSTANT_COLOR;
glBlendFuncB = GL_ONE_MINUS_CONSTANT_COLOR;
const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f};
glstate.blendColor.set(blendColor);
} else if (fixA == fixB) {
glBlendFuncA = GL_CONSTANT_COLOR;
glBlendFuncB = GL_CONSTANT_COLOR;
const float blendColor[4] = {(fixA & 0xFF)/255.0f, ((fixA >> 8) & 0xFF)/255.0f, ((fixA >> 16) & 0xFF)/255.0f, 1.0f};
glstate.blendColor.set(blendColor);
} else {
NOTICE_LOG(HLE, "ERROR INVALID blendcolorstate: FixA=%06x FixB=%06x FuncA=%i FuncB=%i", gstate.getFixA(), gstate.getFixB(), gstate.getBlendFuncA(), gstate.getBlendFuncB());
glBlendFuncA = GL_ONE;
glBlendFuncB = GL_ONE;
}
}
// At this point, through all paths above, glBlendFuncA and glBlendFuncB will be set somehow.
glstate.blendFunc.set(glBlendFuncA, glBlendFuncB);
}
}
bool wantDepthTest = gstate.isModeClear() || gstate.isDepthTestEnabled();
glstate.depthTest.set(wantDepthTest);
if(wantDepthTest) {
// Force GL_ALWAYS if mode clear
int depthTestFunc = gstate.isModeClear() ? 1 : gstate.getDepthTestFunc();
glstate.depthFunc.set(ztests[depthTestFunc]);
}
bool wantDepthWrite = gstate.isModeClear() || gstate.isDepthWriteEnabled();
glstate.depthWrite.set(wantDepthWrite ? GL_TRUE : GL_FALSE);
float depthRangeMin = gstate_c.zOff - gstate_c.zScale;
float depthRangeMax = gstate_c.zOff + gstate_c.zScale;
glstate.depthRange.set(depthRangeMin, depthRangeMax);
UpdateViewportAndProjection();
LinkedShader *program = shaderManager_->ApplyShader(prim);
// TODO: Make a cache for glEnableVertexAttribArray and glVertexAttribPtr states, these spam the gDebugger log.
// TODO: Make a cache for glEnableVertexAttribArray and glVertexAttribPtr states,
// these spam the gDebugger log.
glEnableVertexAttribArray(program->a_position);
if (useTexCoord && program->a_texcoord != -1) glEnableVertexAttribArray(program->a_texcoord);
if (program->a_texcoord != -1) glEnableVertexAttribArray(program->a_texcoord);
if (program->a_color0 != -1) glEnableVertexAttribArray(program->a_color0);
if (program->a_color1 != -1) glEnableVertexAttribArray(program->a_color1);
const int vertexSize = sizeof(transformed[0]);
glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, vertexSize, drawBuffer);
if (useTexCoord && program->a_texcoord != -1) glVertexAttribPointer(program->a_texcoord, 2, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 3 * 4);
if (program->a_texcoord != -1) glVertexAttribPointer(program->a_texcoord, 2, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 3 * 4);
if (program->a_color0 != -1) glVertexAttribPointer(program->a_color0, 4, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 5 * 4);
if (program->a_color1 != -1) glVertexAttribPointer(program->a_color1, 4, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 9 * 4);
// NOTICE_LOG(G3D,"DrawPrimitive: %i", numTrans);
if (program->a_color1 != -1) glVertexAttribPointer(program->a_color1, 3, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 9 * 4);
if (drawIndexed) {
glDrawElements(glprim[prim], numTrans, glIndexType, (GLvoid *)inds);
glDrawElements(glprim[prim], numTrans, GL_UNSIGNED_SHORT, (GLvoid *)inds);
} else {
glDrawArrays(glprim[prim], 0, numTrans);
}
glDisableVertexAttribArray(program->a_position);
if (useTexCoord && program->a_texcoord != -1) glDisableVertexAttribArray(program->a_texcoord);
if (program->a_texcoord != -1) glDisableVertexAttribArray(program->a_texcoord);
if (program->a_color0 != -1) glDisableVertexAttribArray(program->a_color0);
if (program->a_color1 != -1) glDisableVertexAttribArray(program->a_color1);
}
void GLES_GPU::UpdateViewportAndProjection()
void GLES_GPU::InitTransform() {
indexGen.Setup(decIndex);
numVerts = 0;
}
void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int vertexCount, float *customUV, int forceIndexType, int *bytesRead)
{
bool throughmode = (gstate.vertType & GE_VTYPE_THROUGH_MASK) != 0;
// For the future
if (!indexGen.PrimCompatible(prim))
Flush();
// We can probably use these to simply set scissors? Maybe we need to offset by regionX1/Y1
int regionX1 = gstate.region1 & 0x3FF;
int regionY1 = (gstate.region1 >> 10) & 0x3FF;
int regionX2 = (gstate.region2 & 0x3FF) + 1;
int regionY2 = ((gstate.region2 >> 10) & 0x3FF) + 1;
if (!indexGen.Empty()) {
gpuStats.numJoins++;
}
gpuStats.numDrawCalls++;
gpuStats.numVertsTransformed += vertexCount;
float offsetX = (float)(gstate.offsetx & 0xFFFF) / 16.0f;
float offsetY = (float)(gstate.offsety & 0xFFFF) / 16.0f;
indexGen.SetIndex(numVerts);
int indexLowerBound, indexUpperBound;
// First, decode the verts and apply morphing
dec.SetVertexType(gstate.vertType);
dec.DecodeVerts(decoded + numVerts * (int)dec.GetDecVtxFmt().stride, verts, inds, prim, vertexCount, &indexLowerBound, &indexUpperBound);
numVerts += indexUpperBound - indexLowerBound + 1;
if (throughmode) {
// No viewport transform here. Let's experiment with using region.
return;
glViewport((0 + regionX1) * renderWidthFactor_, (0 - regionY1) * renderHeightFactor_, (regionX2 - regionX1) * renderWidthFactor_, (regionY2 - regionY1) * renderHeightFactor_);
} else {
// These we can turn into a glViewport call, offset by offsetX and offsetY. Math after.
float vpXa = getFloat24(gstate.viewportx1);
float vpXb = getFloat24(gstate.viewportx2);
float vpYa = getFloat24(gstate.viewporty1);
float vpYb = getFloat24(gstate.viewporty2);
float vpZa = getFloat24(gstate.viewportz1); // / 65536.0f should map it to OpenGL's 0.0-1.0 Z range
float vpZb = getFloat24(gstate.viewportz2); // / 65536.0f
if (bytesRead)
*bytesRead = vertexCount * dec.VertexSize();
// The viewport transform appears to go like this:
// Xscreen = -offsetX + vpXb + vpXa * Xview
// Yscreen = -offsetY + vpYb + vpYa * Yview
// Zscreen = vpZb + vpZa * Zview
int indexType = (gstate.vertType & GE_VTYPE_IDX_MASK);
if (forceIndexType != -1) indexType = forceIndexType;
switch (indexType) {
case GE_VTYPE_IDX_NONE:
switch (prim) {
case GE_PRIM_POINTS: indexGen.AddPoints(vertexCount); break;
case GE_PRIM_LINES: indexGen.AddLineList(vertexCount); break;
case GE_PRIM_LINE_STRIP: indexGen.AddLineStrip(vertexCount); break;
case GE_PRIM_TRIANGLES: indexGen.AddList(vertexCount); break;
case GE_PRIM_TRIANGLE_STRIP: indexGen.AddStrip(vertexCount); break;
case GE_PRIM_TRIANGLE_FAN: indexGen.AddFan(vertexCount); break;
case GE_PRIM_RECTANGLES: indexGen.AddRectangles(vertexCount); break; // Same
}
break;
// This means that to get the analogue glViewport we must:
float vpX0 = vpXb - offsetX - vpXa;
float vpY0 = vpYb - offsetY + vpYa; // Need to account for sign of Y
gstate_c.vpWidth = vpXa * 2;
gstate_c.vpHeight = -vpYa * 2;
case GE_VTYPE_IDX_8BIT:
switch (prim) {
case GE_PRIM_POINTS: indexGen.TranslatePoints(vertexCount, (const u8 *)inds, -indexLowerBound); break;
case GE_PRIM_LINES: indexGen.TranslateLineList(vertexCount, (const u8 *)inds, -indexLowerBound); break;
case GE_PRIM_LINE_STRIP: indexGen.TranslateLineStrip(vertexCount, (const u8 *)inds, -indexLowerBound); break;
case GE_PRIM_TRIANGLES: indexGen.TranslateList(vertexCount, (const u8 *)inds, -indexLowerBound); break;
case GE_PRIM_TRIANGLE_STRIP: indexGen.TranslateStrip(vertexCount, (const u8 *)inds, -indexLowerBound); break;
case GE_PRIM_TRIANGLE_FAN: indexGen.TranslateFan(vertexCount, (const u8 *)inds, -indexLowerBound); break;
case GE_PRIM_RECTANGLES: indexGen.TranslateRectangles(vertexCount, (const u8 *)inds, -indexLowerBound); break; // Same
}
break;
return;
float vpWidth = fabsf(gstate_c.vpWidth);
float vpHeight = fabsf(gstate_c.vpHeight);
// TODO: These two should feed into glDepthRange somehow.
float vpZ0 = (vpZb - vpZa) / 65536.0f;
float vpZ1 = (vpZa * 2) / 65536.0f;
vpX0 *= renderWidthFactor_;
vpY0 *= renderHeightFactor_;
vpWidth *= renderWidthFactor_;
vpHeight *= renderHeightFactor_;
// Flip vpY0 to match the OpenGL coordinate system.
vpY0 = renderHeight_ - (vpY0 + vpHeight);
glViewport(vpX0, vpY0, vpWidth, vpHeight);
// Sadly, as glViewport takes integers, we will not be able to support sub pixel offsets this way. But meh.
shaderManager_->DirtyUniform(DIRTY_PROJMATRIX);
case GE_VTYPE_IDX_16BIT:
switch (prim) {
case GE_PRIM_POINTS: indexGen.TranslatePoints(vertexCount, (const u16 *)inds, -indexLowerBound); break;
case GE_PRIM_LINES: indexGen.TranslateLineList(vertexCount, (const u16 *)inds, -indexLowerBound); break;
case GE_PRIM_LINE_STRIP: indexGen.TranslateLineStrip(vertexCount, (const u16 *)inds, -indexLowerBound); break;
case GE_PRIM_TRIANGLES: indexGen.TranslateList(vertexCount, (const u16 *)inds, -indexLowerBound); break;
case GE_PRIM_TRIANGLE_STRIP: indexGen.TranslateStrip(vertexCount, (const u16 *)inds, -indexLowerBound); break;
case GE_PRIM_TRIANGLE_FAN: indexGen.TranslateFan(vertexCount, (const u16 *)inds, -indexLowerBound); break;
case GE_PRIM_RECTANGLES: indexGen.TranslateRectangles(vertexCount, (const u16 *)inds, -indexLowerBound); break; // Same
}
break;
}
}
void GLES_GPU::Flush()
{
if (indexGen.Empty())
return;
// From here on out, the index type is ALWAYS 16-bit. Deal with it.
// And here we should return, having collected the morphed but untransformed vertices.
// Note that DecodeVerts should convert strips into indexed lists etc, adding to our
// current vertex buffer and index buffer.
// The rest below here should only execute on Flush.
#if 0
for (int i = indexLowerBound; i <= indexUpperBound; i++) {
PrintDecodedVertex(decoded[i], gstate.vertType);
}
#endif
// Check if anything needs updating
if (gstate_c.textureChanged)
{
if ((gstate.textureMapEnable & 1) && !gstate.isModeClear())
{
PSPSetTexture();
}
gstate_c.textureChanged = false;
}
gpuStats.numFlushes++;
// TODO: This should not be done on every drawcall, we should collect vertex data
// until critical state changes. That's when we draw (flush).
int prim = indexGen.Prim();
ApplyDrawState();
UpdateViewportAndProjection();
LinkedShader *program = shaderManager_->ApplyShader(prim);
DEBUG_LOG(G3D, "Flush prim %i! %i verts in one go", prim, numVerts);
if (CanUseHardwareTransform(prim)) {
SetupDecFmtForDraw(program, dec.GetDecVtxFmt(), decoded);
glDrawElements(glprim[prim], indexGen.VertexCount(), GL_UNSIGNED_SHORT, (GLvoid *)decIndex);
DesetupDecFmtForDraw(program, dec.GetDecVtxFmt());
} else {
SoftwareTransformAndDraw(prim, program, indexGen.VertexCount(), (void *)decIndex, GE_VTYPE_IDX_16BIT, dec.GetDecVtxFmt(),
indexGen.MaxIndex());
}
indexGen.Reset();
numVerts = 0;
}

View file

@ -17,4 +17,52 @@
#pragma once
struct LinkedShader;
class LinkedShader;
struct DecVtxFormat;
// Only used by SW transform
struct Color4
{
float r,g,b,a;
Color4() : r(0), g(0), b(0), a(0) { }
Color4(float _r, float _g, float _b, float _a=1.0f)
{
r=_r; g=_g; b=_b; a=_a;
}
Color4(const float in[4]) {r=in[0];g=in[1];b=in[2];a=in[3];}
Color4(const float in[3], float alpha) {r=in[0];g=in[1];b=in[2];a=alpha;}
const float &operator [](int i) const {return *(&r + i);}
Color4 operator *(float f) const
{
return Color4(f*r,f*g,f*b,f*a);
}
Color4 operator *(const Color4 &c) const
{
return Color4(r*c.r,g*c.g,b*c.b,a*c.a);
}
Color4 operator +(const Color4 &c) const
{
return Color4(r+c.r,g+c.g,b+c.b,a+c.a);
}
void operator +=(const Color4 &c)
{
r+=c.r;
g+=c.g;
b+=c.b;
a+=c.a;
}
void GetFromRGB(u32 col)
{
r = ((col>>16) & 0xff)/255.0f;
g = ((col>>8) & 0xff)/255.0f;
b = ((col>>0) & 0xff)/255.0f;
}
void GetFromA(u32 col)
{
a = (col&0xff)/255.0f;
}
};
// void SoftwareTransformAndDraw(int prim, LinkedShader *program, int vertexCount, void *inds, int indexType, const DecVtxFormat &decVtxFormat, int maxIndex, float *customUV);

View file

@ -22,13 +22,32 @@
#include "VertexDecoder.h"
void PrintDecodedVertex(const DecodedVertex &vtx, u32 vtype)
{
if (vtype & GE_VTYPE_NRM_MASK) printf("N: %f %f %f\n", vtx.normal[0], vtx.normal[1], vtx.normal[2]);
if (vtype & GE_VTYPE_TC_MASK) printf("TC: %f %f\n", vtx.uv[0], vtx.uv[1]);
if (vtype & GE_VTYPE_COL_MASK) printf("C: %02x %02x %02x %02x\n", vtx.color[0], vtx.color[1], vtx.color[2], vtx.color[3]);
if (vtype & GE_VTYPE_WEIGHT_MASK) printf("W: TODO\n");
printf("P: %f %f %f\n", vtx.pos[0], vtx.pos[1], vtx.pos[2]);
void PrintDecodedVertex(VertexReader &vtx) {
if (vtx.hasNormal())
{
float nrm[3];
vtx.ReadNrm(nrm);
printf("N: %f %f %f\n", nrm[0], nrm[1], nrm[2]);
}
if (vtx.hasUV()) {
float uv[2];
vtx.ReadUV(uv);
printf("TC: %f %f\n", uv[0], uv[1]);
}
if (vtx.hasColor0()) {
float col0[4];
vtx.ReadColor0(col0);
printf("C0: %f %f %f %f\n", col0[0], col0[1], col0[2], col0[3]);
}
if (vtx.hasColor0()) {
float col1[3];
vtx.ReadColor1(col1);
printf("C1: %f %f %f\n", col1[0], col1[1], col1[2]);
}
// Etc..
float pos[3];
vtx.ReadPos(pos);
printf("P: %f %f %f\n", pos[0], pos[1], pos[2]);
}
const int tcsize[4] = {0,2,4,8}, tcalign[4] = {0,1,2,4};
@ -37,67 +56,559 @@ const int nrmsize[4] = {0,3,6,12}, nrmalign[4] = {0,1,2,4};
const int possize[4] = {0,3,6,12}, posalign[4] = {0,1,2,4};
const int wtsize[4] = {0,1,2,4}, wtalign[4] = {0,1,2,4};
inline int align(int n, int align)
{
inline int align(int n, int align) {
return (n + (align - 1)) & ~(align - 1);
}
void VertexDecoder::SetVertexType(u32 fmt)
int DecFmtSize(u8 fmt) {
switch (fmt) {
case DEC_NONE: return 0;
case DEC_FLOAT_1: return 4;
case DEC_FLOAT_2: return 8;
case DEC_FLOAT_3: return 12;
case DEC_FLOAT_4: return 16;
case DEC_S8_3: return 4;
case DEC_S16_3: return 8;
case DEC_U8_4: return 4;
default:
return 0;
}
}
#if 0
// This is what the software transform spits out, and thus w
DecVtxFormat GetTransformedVtxFormat(const DecVtxFormat &fmt) {
DecVtxFormat tfm = {0};
int size = 0;
int offset = 0;
// Weights disappear during transform.
if (fmt.uvfmt) {
// UV always becomes float2.
tfm.uvfmt = DEC_FLOAT_2;
tfm.uvoff = offset;
offset += DecFmtSize(tfm.uvfmt);
}
// We always (?) get two colors out, they're floats (although we'd probably be fine with less precision).
tfm.c0fmt = DEC_FLOAT_4;
tfm.c0off = offset;
offset += DecFmtSize(tfm.c0fmt);
tfm.c1fmt = DEC_FLOAT_3; // color1 (specular) doesn't have alpha.
tfm.c1off = offset;
offset += DecFmtSize(tfm.c1fmt);
// We never get a normal, it's gone.
// But we do get a position, and it's always float3.
tfm.posfmt = DEC_FLOAT_3;
tfm.posoff = offset;
offset += DecFmtSize(tfm.posfmt);
// Update stride.
tfm.stride = offset;
return tfm;
}
#endif
void VertexDecoder::Step_WeightsU8() const
{
fmt = fmt;
float *wt = (float *)(decoded_ + decFmt.w0off);
const u8 *wdata = (const u8*)(ptr_);
for (int j = 0; j < nweights; j++)
wt[j] = (float)wdata[j] / 128.0f;
}
void VertexDecoder::Step_WeightsU16() const
{
float *wt = (float *)(decoded_ + decFmt.w0off);
const u16 *wdata = (const u16*)(ptr_);
for (int j = 0; j < nweights; j++)
wt[j] = (float)wdata[j] / 32768.0f;
}
void VertexDecoder::Step_WeightsFloat() const
{
float *wt = (float *)(decoded_ + decFmt.w0off);
const float *wdata = (const float*)(ptr_);
for (int j = 0; j < nweights; j++)
wt[j] = wdata[j];
}
void VertexDecoder::Step_TcU8() const
{
float *uv = (float *)(decoded_ + decFmt.uvoff);
const u8 *uvdata = (const u8*)(ptr_ + tcoff);
for (int j = 0; j < 2; j++)
uv[j] = (float)uvdata[j] / 128.0f;
}
void VertexDecoder::Step_TcU16() const
{
float *uv = (float *)(decoded_ + decFmt.uvoff);
const u16 *uvdata = (const u16*)(ptr_ + tcoff);
uv[0] = (float)uvdata[0] / 32768.0f;
uv[1] = (float)uvdata[1] / 32768.0f;
}
void VertexDecoder::Step_TcU16Through() const
{
float *uv = (float *)(decoded_ + decFmt.uvoff);
const u16 *uvdata = (const u16*)(ptr_ + tcoff);
uv[0] = (float)uvdata[0] / (float)(gstate_c.curTextureWidth);
uv[1] = (float)uvdata[1] / (float)(gstate_c.curTextureHeight);
}
void VertexDecoder::Step_TcFloat() const
{
float *uv = (float *)(decoded_ + decFmt.uvoff);
const float *uvdata = (const float*)(ptr_ + tcoff);
uv[0] = uvdata[0];
uv[1] = uvdata[1];
}
void VertexDecoder::Step_TcFloatThrough() const
{
float *uv = (float *)(decoded_ + decFmt.uvoff);
const float *uvdata = (const float*)(ptr_ + tcoff);
uv[0] = uvdata[0] / (float)(gstate_c.curTextureWidth);
uv[1] = uvdata[1] / (float)(gstate_c.curTextureHeight);
}
void VertexDecoder::Step_Color565() const
{
u8 *c = decoded_ + decFmt.c0off;
u16 cdata = *(u16*)(ptr_ + coloff);
c[0] = Convert5To8(cdata & 0x1f);
c[1] = Convert6To8((cdata>>5) & 0x3f);
c[2] = Convert5To8((cdata>>11) & 0x1f);
c[3] = 1.0f;
}
void VertexDecoder::Step_Color5551() const
{
u8 *c = decoded_ + decFmt.c0off;
u16 cdata = *(u16*)(ptr_ + coloff);
c[0] = Convert5To8(cdata & 0x1f);
c[1] = Convert5To8((cdata>>5) & 0x1f);
c[2] = Convert5To8((cdata>>10) & 0x1f);
c[3] = (cdata>>15) ? 255 : 0;
}
void VertexDecoder::Step_Color4444() const
{
u8 *c = decoded_ + decFmt.c0off;
u16 cdata = *(u16*)(ptr_ + coloff);
for (int j = 0; j < 4; j++)
c[j] = Convert4To8((cdata >> (j * 4)) & 0xF);
}
void VertexDecoder::Step_Color8888() const
{
u8 *c = decoded_ + decFmt.c0off;
// TODO: speedup
const u8 *cdata = (const u8*)(ptr_ + coloff);
for (int j = 0; j < 4; j++)
c[j] = cdata[j];
}
void VertexDecoder::Step_Color565Morph() const
{
float col[3] = {0};
for (int n = 0; n < morphcount; n++)
{
float w = gstate_c.morphWeights[n];
u16 cdata = *(u16*)(ptr_ + onesize_*n + coloff);
col[0] += w * (cdata & 0x1f) / 31.f;
col[1] += w * ((cdata>>5) & 0x3f) / 63.f;
col[2] += w * ((cdata>>11) & 0x1f) / 31.f;
}
u8 *c = decoded_ + decFmt.c0off;
for (int i = 0; i < 3; i++) {
c[i] = (u8)(col[i] * 255.0f);
}
c[3] = 255;
}
void VertexDecoder::Step_Color5551Morph() const
{
float col[4] = {0};
for (int n = 0; n < morphcount; n++)
{
float w = gstate_c.morphWeights[n];
u16 cdata = *(u16*)(ptr_ + onesize_*n + coloff);
col[0] += w * (cdata & 0x1f) / 31.f;
col[1] += w * ((cdata>>5) & 0x1f) / 31.f;
col[2] += w * ((cdata>>10) & 0x1f) / 31.f;
col[3] += w * ((cdata>>15) ? 1.0f : 0.0f);
}
u8 *c = decoded_ + decFmt.c0off;
for (int i = 0; i < 4; i++) {
c[i] = (u8)(col[i] * 255.0f);
}
}
void VertexDecoder::Step_Color4444Morph() const
{
float col[4] = {0};
for (int n = 0; n < morphcount; n++)
{
float w = gstate_c.morphWeights[n];
u16 cdata = *(u16*)(ptr_ + onesize_*n + coloff);
for (int j = 0; j < 4; j++)
col[j] += w * ((cdata >> (j * 4)) & 0xF) / 15.f;
}
u8 *c = decoded_ + decFmt.c0off;
for (int i = 0; i < 4; i++) {
c[i] = (u8)(col[i] * 255.0f);
}
}
void VertexDecoder::Step_Color8888Morph() const
{
float col[4] = {0};
for (int n = 0; n < morphcount; n++)
{
float w = gstate_c.morphWeights[n];
const u8 *cdata = (const u8*)(ptr_ + onesize_*n + coloff);
for (int j = 0; j < 4; j++)
col[j] += w * cdata[j];
}
u8 *c = decoded_ + decFmt.c0off;
for (int i = 0; i < 4; i++) {
c[i] = (u8)(col[i]);
}
}
void VertexDecoder::Step_NormalS8() const
{
float *normal = (float *)(decoded_ + decFmt.nrmoff);
float multiplier = 1.0f;
if (gstate.reversenormals & 0xFFFFFF)
multiplier = -multiplier;
const s8 *sv = (const s8*)(ptr_ + nrmoff);
for (int j = 0; j < 3; j++)
normal[j] = (sv[j] / 127.0f) * multiplier;
}
void VertexDecoder::Step_NormalS16() const
{
float *normal = (float *)(decoded_ + decFmt.nrmoff);
float multiplier = 1.0f;
if (gstate.reversenormals & 0xFFFFFF)
multiplier = -multiplier;
const short *sv = (const short*)(ptr_ + nrmoff);
for (int j = 0; j < 3; j++)
normal[j] = (sv[j] / 32767.0f) * multiplier;
}
void VertexDecoder::Step_NormalFloat() const
{
float *normal = (float *)(decoded_ + decFmt.nrmoff);
float multiplier = 1.0f;
if (gstate.reversenormals & 0xFFFFFF)
multiplier = -multiplier;
const float *fv = (const float*)(ptr_ + nrmoff);
for (int j = 0; j < 3; j++)
normal[j] = fv[j] * multiplier;
}
void VertexDecoder::Step_NormalS8Morph() const
{
float *normal = (float *)(decoded_ + decFmt.nrmoff);
memset(normal, 0, sizeof(float)*3);
for (int n = 0; n < morphcount; n++)
{
float multiplier = gstate_c.morphWeights[n];
if (gstate.reversenormals & 0xFFFFFF) {
multiplier = -multiplier;
}
const s8 *sv = (const s8*)(ptr_ + onesize_*n + nrmoff);
for (int j = 0; j < 3; j++)
normal[j] += (sv[j]/32767.0f) * multiplier;
}
}
void VertexDecoder::Step_NormalS16Morph() const
{
float *normal = (float *)(decoded_ + decFmt.nrmoff);
memset(normal, 0, sizeof(float)*3);
for (int n = 0; n < morphcount; n++)
{
float multiplier = gstate_c.morphWeights[n];
if (gstate.reversenormals & 0xFFFFFF) {
multiplier = -multiplier;
}
const float *fv = (const float*)(ptr_ + onesize_*n + nrmoff);
for (int j = 0; j < 3; j++)
normal[j] += fv[j] * multiplier;
}
}
void VertexDecoder::Step_NormalFloatMorph() const
{
float *normal = (float *)(decoded_ + decFmt.nrmoff);
memset(normal, 0, sizeof(float)*3);
for (int n = 0; n < morphcount; n++)
{
float multiplier = gstate_c.morphWeights[n];
if (gstate.reversenormals & 0xFFFFFF) {
multiplier = -multiplier;
}
const float *fv = (const float*)(ptr_ + onesize_*n + nrmoff);
for (int j = 0; j < 3; j++)
normal[j] += fv[j] * multiplier;
}
}
void VertexDecoder::Step_PosS8() const
{
float *v = (float *)(decoded_ + decFmt.posoff);
float multiplier = 1.0f / 127.0f;
const s8 *sv = (const s8*)(ptr_ + posoff);
for (int j = 0; j < 3; j++)
v[j] = sv[j] * multiplier;
}
void VertexDecoder::Step_PosS16() const
{
float *v = (float *)(decoded_ + decFmt.posoff);
float multiplier = 1.0f / 32767.0f;
const short *sv = (const short*)(ptr_ + posoff);
for (int j = 0; j < 3; j++)
v[j] = sv[j] * multiplier;
}
void VertexDecoder::Step_PosFloat() const
{
float *v = (float *)(decoded_ + decFmt.posoff);
const float *fv = (const float*)(ptr_ + posoff);
for (int j = 0; j < 3; j++)
v[j] = fv[j];
}
void VertexDecoder::Step_PosS8Through() const
{
float *v = (float *)(decoded_ + decFmt.posoff);
const s8 *sv = (const s8*)(ptr_ + posoff);
for (int j = 0; j < 3; j++)
v[j] = sv[j];
}
void VertexDecoder::Step_PosS16Through() const
{
float *v = (float *)(decoded_ + decFmt.posoff);
const short *sv = (const short*)(ptr_ + posoff);
for (int j = 0; j < 3; j++)
v[j] = sv[j];
}
void VertexDecoder::Step_PosFloatThrough() const
{
float *v = (float *)(decoded_ + decFmt.posoff);
const float *fv = (const float*)(ptr_ + posoff);
for (int j = 0; j < 3; j++)
v[j] = fv[j];
}
void VertexDecoder::Step_PosS8Morph() const
{
float *v = (float *)(decoded_ + decFmt.posoff);
memset(v, 0, sizeof(float) * 3);
for (int n = 0; n < morphcount; n++) {
const s8 *sv = (const s8*)(ptr_ + onesize_*n + posoff);
for (int j = 0; j < 3; j++)
v[j] += (sv[j] / 127.f) * gstate_c.morphWeights[n];
}
}
void VertexDecoder::Step_PosS16Morph() const
{
float *v = (float *)(decoded_ + decFmt.posoff);
memset(v, 0, sizeof(float) * 3);
for (int n = 0; n < morphcount; n++) {
float multiplier = 1.0f / 32767.0f;
const short *sv = (const short*)(ptr_ + onesize_*n + posoff);
for (int j = 0; j < 3; j++)
v[j] += (sv[j] * multiplier) * gstate_c.morphWeights[n];
}
}
void VertexDecoder::Step_PosFloatMorph() const
{
float *v = (float *)(decoded_ + decFmt.posoff);
memset(v, 0, sizeof(float) * 3);
for (int n = 0; n < morphcount; n++) {
const float *fv = (const float*)(ptr_ + onesize_*n + posoff);
for (int j = 0; j < 3; j++)
v[j] += fv[j] * gstate_c.morphWeights[n];
}
}
const StepFunction wtstep[4] = {
0,
&VertexDecoder::Step_WeightsU8,
&VertexDecoder::Step_WeightsU16,
&VertexDecoder::Step_WeightsFloat,
};
const StepFunction tcstep[4] = {
0,
&VertexDecoder::Step_TcU8,
&VertexDecoder::Step_TcU16,
&VertexDecoder::Step_TcFloat,
};
const StepFunction tcstep_through[4] = {
0,
&VertexDecoder::Step_TcU8,
&VertexDecoder::Step_TcU16Through,
&VertexDecoder::Step_TcFloatThrough,
};
// TODO: Tc Morph
const StepFunction colstep[8] = {
0, 0, 0, 0,
&VertexDecoder::Step_Color565,
&VertexDecoder::Step_Color5551,
&VertexDecoder::Step_Color4444,
&VertexDecoder::Step_Color8888,
};
const StepFunction colstep_morph[8] = {
0, 0, 0, 0,
&VertexDecoder::Step_Color565Morph,
&VertexDecoder::Step_Color5551Morph,
&VertexDecoder::Step_Color4444Morph,
&VertexDecoder::Step_Color8888Morph,
};
const StepFunction nrmstep[4] = {
0,
&VertexDecoder::Step_NormalS8,
&VertexDecoder::Step_NormalS16,
&VertexDecoder::Step_NormalFloat,
};
const StepFunction nrmstep_morph[4] = {
0,
&VertexDecoder::Step_NormalS8Morph,
&VertexDecoder::Step_NormalS16Morph,
&VertexDecoder::Step_NormalFloatMorph,
};
const StepFunction posstep[4] = {
0,
&VertexDecoder::Step_PosS8,
&VertexDecoder::Step_PosS16,
&VertexDecoder::Step_PosFloat,
};
const StepFunction posstep_morph[4] = {
0,
&VertexDecoder::Step_PosS8Morph,
&VertexDecoder::Step_PosS16Morph,
&VertexDecoder::Step_PosFloatMorph,
};
const StepFunction posstep_through[4] = {
0,
&VertexDecoder::Step_PosS8Through,
&VertexDecoder::Step_PosS16Through,
&VertexDecoder::Step_PosFloatThrough,
};
void VertexDecoder::SetVertexType(u32 fmt) {
fmt_ = fmt;
throughmode = (fmt & GE_VTYPE_THROUGH) != 0;
numSteps_ = 0;
int biggest = 0;
size = 0;
tc = fmt & 0x3;
col = (fmt >> 2) & 0x7;
nrm = (fmt >> 5) & 0x3;
pos = (fmt >> 7) & 0x3;
tc = fmt & 0x3;
col = (fmt >> 2) & 0x7;
nrm = (fmt >> 5) & 0x3;
pos = (fmt >> 7) & 0x3;
weighttype = (fmt >> 9) & 0x3;
idx = (fmt >> 11) & 0x3;
idx = (fmt >> 11) & 0x3;
morphcount = ((fmt >> 18) & 0x7)+1;
nweights = ((fmt >> 14) & 0x7)+1;
nweights = ((fmt >> 14) & 0x7)+1;
int decOff = 0;
memset(&decFmt, 0, sizeof(decFmt));
DEBUG_LOG(G3D,"VTYPE: THRU=%i TC=%i COL=%i POS=%i NRM=%i WT=%i NW=%i IDX=%i MC=%i", (int)throughmode, tc,col,pos,nrm,weighttype,nweights,idx,morphcount);
if (weighttype)
{
if (weighttype) { // && nweights?
//size = align(size, wtalign[weighttype]); unnecessary
size += wtsize[weighttype] * nweights;
if (wtalign[weighttype] > biggest)
biggest = wtalign[weighttype];
steps_[numSteps_++] = wtstep[weighttype];
if (nweights < 5) {
decFmt.w0off = decOff;
decFmt.w0fmt = DEC_FLOAT_1 + nweights - 1;
} else {
decFmt.w0off = decOff;
decFmt.w0fmt = DEC_FLOAT_4;
decFmt.w1off = decOff + 4 * 4;
decFmt.w1fmt = DEC_FLOAT_1 + nweights - 5;
}
decOff += nweights * 4;
}
if (tc)
{
if (tc) {
size = align(size, tcalign[tc]);
tcoff = size;
size += tcsize[tc];
if (tcalign[tc] > biggest)
biggest = tcalign[tc];
steps_[numSteps_++] = throughmode ? tcstep_through[tc] : tcstep[tc];
// All UV decode to DEC_FLOAT2 currently.
decFmt.uvfmt = DEC_FLOAT_2;
decFmt.uvoff = decOff;
decOff += DecFmtSize(decFmt.uvfmt);
}
if (col)
{
if (col) {
size = align(size, colalign[col]);
coloff = size;
size += colsize[col];
if (colalign[col] > biggest)
biggest = colalign[col];
}
else
{
steps_[numSteps_++] = morphcount == 1 ? colstep[col] : colstep_morph[col];
// All color formats decode to DEC_U8_4 currently.
// They can become floats later during transform though.
decFmt.c0fmt = DEC_U8_4;
decFmt.c0off = decOff;
decOff += DecFmtSize(decFmt.c0fmt);
} else {
coloff = 0;
}
if (nrm)
{
if (nrm) {
size = align(size, nrmalign[nrm]);
nrmoff = size;
size += nrmsize[nrm];
if (nrmalign[nrm] > biggest)
biggest = nrmalign[nrm];
steps_[numSteps_++] = morphcount == 1 ? nrmstep[nrm] : nrmstep_morph[nrm];
// The normal formats match the gl formats perfectly, let's use 'em.
switch (nrm) {
case GE_VTYPE_NRM_8BIT >> GE_VTYPE_NRM_SHIFT: decFmt.nrmfmt = DEC_S8_3; break;
case GE_VTYPE_NRM_16BIT >> GE_VTYPE_NRM_SHIFT: decFmt.nrmfmt = DEC_S16_3; break;
case GE_VTYPE_NRM_FLOAT >> GE_VTYPE_NRM_SHIFT: decFmt.nrmfmt = DEC_FLOAT_3; break;
}
// Actually, temporarily let's not.
decFmt.nrmfmt = DEC_FLOAT_3;
decFmt.nrmoff = decOff;
decOff += DecFmtSize(decFmt.nrmfmt);
}
//if (pos) - there's always a position
@ -107,7 +618,26 @@ void VertexDecoder::SetVertexType(u32 fmt)
size += possize[pos];
if (posalign[pos] > biggest)
biggest = posalign[pos];
if (throughmode) {
steps_[numSteps_++] = posstep_through[pos];
decFmt.posfmt = DEC_FLOAT_3;
} else {
steps_[numSteps_++] = morphcount == 1 ? posstep[pos] : posstep_morph[pos];
// The non-through-mode position formats match the gl formats perfectly, let's use 'em.
switch (pos) {
case GE_VTYPE_POS_8BIT >> GE_VTYPE_POS_SHIFT: decFmt.posfmt = DEC_S8_3; break;
case GE_VTYPE_POS_16BIT >> GE_VTYPE_POS_SHIFT: decFmt.posfmt = DEC_S16_3; break;
case GE_VTYPE_POS_FLOAT >> GE_VTYPE_POS_SHIFT: decFmt.posfmt = DEC_FLOAT_3; break;
}
// Actually, temporarily let's not.
decFmt.posfmt = DEC_FLOAT_3;
}
decFmt.posoff = decOff;
decOff += DecFmtSize(decFmt.posfmt);
}
decFmt.stride = decOff;
size = align(size, biggest);
onesize_ = size;
@ -115,15 +645,10 @@ void VertexDecoder::SetVertexType(u32 fmt)
DEBUG_LOG(G3D,"SVT : size = %i, aligned to biggest %i", size, biggest);
}
void VertexDecoder::DecodeVerts(DecodedVertex *decoded, const void *verts, const void *inds, int prim, int count, int *indexLowerBound, int *indexUpperBound) const
void VertexDecoder::DecodeVerts(u8 *decodedptr, const void *verts, const void *inds, int prim, int count, int *indexLowerBound, int *indexUpperBound) const
{
// TODO: Remove
if (morphcount == 1)
gstate_c.morphWeights[0] = 1.0f;
char *ptr = (char *)verts;
// Find index bounds. Could cache this in display lists.
// Also, this could be greatly sped up with SSE2, although rarely a bottleneck.
int lowerBound = 0x7FFFFFFF;
int upperBound = 0;
if (idx == (GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT)) {
@ -149,248 +674,15 @@ void VertexDecoder::DecodeVerts(DecodedVertex *decoded, const void *verts, const
*indexLowerBound = lowerBound;
*indexUpperBound = upperBound;
// Decode the vertices within the found bounds, once each (unlike the previous way..)
// Decode the vertices within the found bounds, once each
decoded_ = decodedptr; // + lowerBound * decFmt.stride;
ptr_ = (const u8*)verts + lowerBound * size;
for (int index = lowerBound; index <= upperBound; index++)
{
ptr = (char*)verts + (index * size);
// TODO: Should weights be morphed?
float *wt = decoded[index].weights;
switch (weighttype)
{
case GE_VTYPE_WEIGHT_NONE >> 9:
break;
case GE_VTYPE_WEIGHT_8BIT >> 9:
{
const u8 *wdata = (const u8*)(ptr);
for (int j = 0; j < nweights; j++)
wt[j] = (float)wdata[j] / 128.0f;
}
break;
case GE_VTYPE_WEIGHT_16BIT >> 9:
{
const u16 *wdata = (const u16*)(ptr);
for (int j = 0; j < nweights; j++)
wt[j] = (float)wdata[j] / 32768.0f;
}
break;
case GE_VTYPE_WEIGHT_FLOAT >> 9:
{
const float *wdata = (const float*)(ptr+0);
for (int j = 0; j < nweights; j++)
wt[j] = wdata[j];
}
break;
}
// TODO: Not morphing UV yet
float *uv = decoded[index].uv;
switch (tc)
{
case GE_VTYPE_TC_NONE:
uv[0] = 0.0f;
uv[1] = 0.0f;
break;
case GE_VTYPE_TC_8BIT:
{
const u8 *uvdata = (const u8*)(ptr + tcoff);
for (int j = 0; j < 2; j++)
uv[j] = (float)uvdata[j] / 128.0f;
break;
}
case GE_VTYPE_TC_16BIT:
{
const u16 *uvdata = (const u16*)(ptr + tcoff);
if (throughmode)
{
uv[0] = (float)uvdata[0] / (float)(gstate_c.curTextureWidth);
uv[1] = (float)uvdata[1] / (float)(gstate_c.curTextureHeight);
}
else
{
uv[0] = (float)uvdata[0] / 32768.0f;
uv[1] = (float)uvdata[1] / 32768.0f;
}
}
break;
case GE_VTYPE_TC_FLOAT:
{
const float *uvdata = (const float*)(ptr + tcoff);
if (throughmode) {
uv[0] = uvdata[0] / (float)(gstate_c.curTextureWidth);
uv[1] = uvdata[1] / (float)(gstate_c.curTextureHeight);
} else {
uv[0] = uvdata[0];
uv[1] = uvdata[1];
}
}
break;
}
// TODO: Not morphing color yet
u8 *c = decoded[index].color;
switch (col)
{
case GE_VTYPE_COL_4444 >> 2:
{
u16 cdata = *(u16*)(ptr + coloff);
for (int j = 0; j < 4; j++)
c[j] = Convert4To8((cdata >> (j * 4)) & 0xF);
}
break;
case GE_VTYPE_COL_565 >> 2:
{
u16 cdata = *(u16*)(ptr + coloff);
c[0] = Convert5To8(cdata & 0x1f);
c[1] = Convert6To8((cdata>>5) & 0x3f);
c[2] = Convert5To8((cdata>>11) & 0x1f);
c[3] = 1.0f;
}
break;
case GE_VTYPE_COL_5551 >> 2:
{
u16 cdata = *(u16*)(ptr + coloff);
c[0] = Convert5To8(cdata & 0x1f);
c[1] = Convert5To8((cdata>>5) & 0x1f);
c[2] = Convert5To8((cdata>>10) & 0x1f);
c[3] = (cdata>>15) ? 255 : 0;
}
break;
case GE_VTYPE_COL_8888 >> 2:
{
// TODO: speedup
u8 *cdata = (u8*)(ptr + coloff);
for (int j = 0; j < 4; j++)
c[j] = cdata[j];
}
break;
default:
c[0] = 255;
c[1] = 255;
c[2] = 255;
c[3] = 255;
break;
}
float *normal = decoded[index].normal;
memset(normal, 0, sizeof(float)*3);
for (int n = 0; n < morphcount; n++)
{
float multiplier = gstate_c.morphWeights[n];
if (gstate.reversenormals & 0xFFFFFF) {
multiplier = -multiplier;
}
switch (nrm)
{
case GE_VTYPE_NRM_8BIT:
{
const s8 *sv = (const s8*)(ptr + onesize_*n + nrmoff);
for (int j = 0; j < 3; j++)
normal[j] += (sv[j]/127.0f) * multiplier;
}
break;
case GE_VTYPE_NRM_FLOAT >> 5:
{
const float *fv = (const float*)(ptr + onesize_*n + nrmoff);
for (int j = 0; j < 3; j++)
normal[j] += fv[j] * multiplier;
}
break;
case GE_VTYPE_NRM_16BIT >> 5:
{
const short *sv = (const short*)(ptr + onesize_*n + nrmoff);
for (int j = 0; j < 3; j++)
normal[j] += (sv[j]/32767.0f) * multiplier;
}
break;
}
}
float *v = decoded[index].pos;
if (morphcount == 1) {
switch (pos)
{
case GE_VTYPE_POS_FLOAT >> 7:
{
const float *fv = (const float*)(ptr + posoff);
for (int j = 0; j < 3; j++)
v[j] = fv[j];
}
break;
case GE_VTYPE_POS_16BIT >> 7:
{
float multiplier = 1.0f / 32767.0f;
if (throughmode) multiplier = 1.0f;
const short *sv = (const short*)(ptr + posoff);
for (int j = 0; j < 3; j++)
v[j] = sv[j] * multiplier;
}
break;
case GE_VTYPE_POS_8BIT >> 7:
{
const s8 *sv = (const s8*)(ptr + posoff);
for (int j = 0; j < 3; j++)
v[j] = sv[j] / 127.f;
}
break;
default:
ERROR_LOG(G3D,"Unknown position format %i",pos);
break;
}
} else {
memset(v, 0, sizeof(float) * 3);
for (int n = 0; n < morphcount; n++)
{
switch (pos)
{
case GE_VTYPE_POS_FLOAT >> 7:
{
const float *fv = (const float*)(ptr + onesize_*n + posoff);
for (int j = 0; j < 3; j++)
v[j] += fv[j] * gstate_c.morphWeights[n];
}
break;
case GE_VTYPE_POS_16BIT >> 7:
{
float multiplier = 1.0f / 32767.0f;
if (throughmode) multiplier = 1.0f;
const short *sv = (const short*)(ptr + onesize_*n + posoff);
for (int j = 0; j < 3; j++)
v[j] += (sv[j] * multiplier) * gstate_c.morphWeights[n];
}
break;
case GE_VTYPE_POS_8BIT >> 7:
{
const s8 *sv = (const s8*)(ptr + onesize_*n + posoff);
for (int j = 0; j < 3; j++)
v[j] += (sv[j] / 127.f) * gstate_c.morphWeights[n];
}
break;
default:
ERROR_LOG(G3D,"Unknown position format %i",pos);
break;
}
}
for (int i = 0; i < numSteps_; i++) {
((*this).*steps_[i])();
}
ptr_ += size;
decoded_ += decFmt.stride;
}
}

View file

@ -21,48 +21,132 @@
#include "../Globals.h"
#include "base/basictypes.h"
struct DecodedVertex
{
float pos[3]; // in case of morph, preblend during decode
float normal[3]; // in case of morph, preblend during decode
float uv[2]; // scaled by uscale, vscale, if there
u8 color[4]; // unlit
float weights[8]; // ugh, expensive
// DecVtxFormat - vertex formats for PC
// Kind of like a D3D VertexDeclaration.
// Can write code to easily bind these using OpenGL, or read these manually.
// No morph support, that is taken care of by the VertexDecoder.
enum {
DEC_NONE,
DEC_FLOAT_1,
DEC_FLOAT_2,
DEC_FLOAT_3,
DEC_FLOAT_4,
DEC_S8_3,
DEC_S16_3,
DEC_U8_4,
};
int DecFmtSize(u8 fmt);
struct DecVtxFormat {
u8 w0fmt; u8 w0off; // first 4 weights
u8 w1fmt; u8 w1off; // second 4 weights
u8 uvfmt; u8 uvoff;
u8 c0fmt; u8 c0off; // First color
u8 c1fmt; u8 c1off;
u8 nrmfmt; u8 nrmoff;
u8 posfmt; u8 posoff;
short stride;
};
// This struct too.
struct TransformedVertex
{
float x, y, z; // in case of morph, preblend during decode
float uv[2]; // scaled by uscale, vscale, if there
float color0[4]; // prelit
float color1[4]; // prelit
float color1[3]; // prelit
};
DecVtxFormat GetTransformedVtxFormat(const DecVtxFormat &fmt);
class VertexDecoder;
typedef void (VertexDecoder::*StepFunction)() const;
// Right now
// - only contains computed information
// - does decoding in nasty branchfilled loops
// Future TODO
// - should be cached, not recreated every time
// - will compile into list of called functions
// - will compile into lighting fast specialized x86 and ARM
// - will not bother translating components that can be read directly
// by OpenGL ES. Will still have to translate 565 colors, and things
// by OpenGL ES. Will still have to translate 565 colors and things
// like that. DecodedVertex will not be a fixed struct. Will have to
// do morphing here.
//
// We want 100% perf on 1Ghz even in vertex complex games!
class VertexDecoder
{
public:
VertexDecoder() : coloff(0), nrmoff(0), posoff(0) {}
~VertexDecoder() {}
void SetVertexType(u32 vtype);
void DecodeVerts(DecodedVertex *decoded, const void *verts, const void *inds, int prim, int count, int *indexLowerBound, int *indexUpperBound) const;
const DecVtxFormat &GetDecVtxFmt() { return decFmt; }
void DecodeVerts(u8 *decoded, const void *verts, const void *inds, int prim, int count, int *indexLowerBound, int *indexUpperBound) const;
bool hasColor() const { return col != 0; }
int VertexSize() const { return size; }
private:
u32 fmt;
void Step_WeightsU8() const;
void Step_WeightsU16() const;
void Step_WeightsFloat() const;
void Step_TcU8() const;
void Step_TcU16() const;
void Step_TcFloat() const;
void Step_TcU16Through() const;
void Step_TcFloatThrough() const;
// TODO: tcmorph
void Step_Color4444() const;
void Step_Color565() const;
void Step_Color5551() const;
void Step_Color8888() const;
void Step_Color4444Morph() const;
void Step_Color565Morph() const;
void Step_Color5551Morph() const;
void Step_Color8888Morph() const;
void Step_NormalS8() const;
void Step_NormalS16() const;
void Step_NormalFloat() const;
void Step_NormalS8Morph() const;
void Step_NormalS16Morph() const;
void Step_NormalFloatMorph() const;
void Step_PosS8() const;
void Step_PosS16() const;
void Step_PosFloat() const;
void Step_PosS8Morph() const;
void Step_PosS16Morph() const;
void Step_PosFloatMorph() const;
void Step_PosS8Through() const;
void Step_PosS16Through() const;
void Step_PosFloatThrough() const;
// Mutable decoder state
mutable u8 *decoded_;
mutable const u8 *ptr_;
// "Immutable" state, set at startup
// The decoding steps
StepFunction steps_[5];
int numSteps_;
u32 fmt_;
DecVtxFormat decFmt;
bool throughmode;
int biggest;
int size;
@ -84,6 +168,146 @@ private:
int nweights;
};
// Debugging utilities
void PrintDecodedVertex(const DecodedVertex &vtx, u32 vtype);
// Reads decoded vertex formats in a convenient way. For software transform and debugging.
class VertexReader
{
public:
VertexReader(u8 *base, const DecVtxFormat &decFmt) : base_(base), data_(base), decFmt_(decFmt) {}
void ReadPos(float pos[3]) {
switch (decFmt_.posfmt) {
case DEC_FLOAT_3:
memcpy(pos, data_ + decFmt_.posoff, 12);
break;
case DEC_S16_3:
{
s16 *p = (s16 *)(data_ + decFmt_.posoff);
for (int i = 0; i < 3; i++)
pos[i] = p[i] / 32767.0f;
}
break;
case DEC_S8_3:
{
s8 *p = (s8 *)(data_ + decFmt_.posoff);
for (int i = 0; i < 3; i++)
pos[i] = p[i] / 127.0f;
}
break;
default:
ERROR_LOG(G3D, "Reader: Unsupported Pos Format");
break;
}
}
void ReadNrm(float nrm[3]) {
switch (decFmt_.nrmfmt) {
case DEC_FLOAT_3:
memcpy(nrm, data_ + decFmt_.nrmoff, 12);
break;
case DEC_S16_3:
{
s16 *p = (s16 *)(data_ + decFmt_.nrmoff);
for (int i = 0; i < 3; i++)
nrm[i] = p[i] / 32767.0f;
}
break;
case DEC_S8_3:
{
s8 *p = (s8 *)(data_ + decFmt_.nrmoff);
for (int i = 0; i < 3; i++)
nrm[i] = p[i] / 127.0f;
}
break;
default:
ERROR_LOG(G3D, "Reader: Unsupported Nrm Format");
break;
}
}
void ReadUV(float uv[2]) {
switch (decFmt_.uvfmt) {
case DEC_FLOAT_2:
memcpy(uv, data_ + decFmt_.uvoff, 8); break;
default:
ERROR_LOG(G3D, "Reader: Unsupported UV Format");
break;
}
}
void ReadColor0(float color[4]) {
switch (decFmt_.c0fmt) {
case DEC_U8_4:
{
u8 *p = (u8 *)(data_ + decFmt_.c0off);
for (int i = 0; i < 4; i++)
color[i] = p[i] / 255.0f;
}
break;
case DEC_FLOAT_4:
memcpy(color, data_ + decFmt_.c0off, 16); break;
default:
ERROR_LOG(G3D, "Reader: Unsupported C0 Format");
break;
}
}
void ReadColor1(float color[3]) {
switch (decFmt_.c1fmt) {
case DEC_U8_4:
{
u8 *p = (u8 *)(data_ + decFmt_.c1off);
for (int i = 0; i < 3; i++)
color[i] = p[i] / 255.0f;
}
break;
case DEC_FLOAT_4:
memcpy(color, data_ + decFmt_.c1off, 16); break;
default:
ERROR_LOG(G3D, "Reader: Unsupported C1 Format");
break;
}
}
void ReadWeights(float weights[8]) {
switch (decFmt_.w0fmt) {
case DEC_FLOAT_1: memcpy(weights, data_ + decFmt_.w0off, 4); break;
case DEC_FLOAT_2: memcpy(weights, data_ + decFmt_.w0off, 8); break;
case DEC_FLOAT_3: memcpy(weights, data_ + decFmt_.w0off, 12); break;
case DEC_FLOAT_4: memcpy(weights, data_ + decFmt_.w0off, 16); break;
default:
ERROR_LOG(G3D, "Reader: Unsupported W0 Format");
break;
}
switch (decFmt_.w1fmt) {
case 0:
// It's fine for there to be w0 weights but not w1.
break;
case DEC_FLOAT_1: memcpy(weights + 4, data_ + decFmt_.w1off, 4); break;
case DEC_FLOAT_2: memcpy(weights + 4, data_ + decFmt_.w1off, 8); break;
case DEC_FLOAT_3: memcpy(weights + 4, data_ + decFmt_.w1off, 12); break;
case DEC_FLOAT_4: memcpy(weights + 4, data_ + decFmt_.w1off, 16); break;
default:
ERROR_LOG(G3D, "Reader: Unsupported W1 Format");
break;
}
}
bool hasColor0() const { return decFmt_.c0fmt != 0; }
bool hasNormal() const { return decFmt_.nrmfmt != 0; }
bool hasUV() const { return decFmt_.uvfmt != 0; }
void Goto(int index) {
data_ = base_ + index * decFmt_.stride;
}
private:
u8 *base_;
u8 *data_;
DecVtxFormat decFmt_;
int vtype_;
};
// Debugging utilities
void PrintDecodedVertex(VertexReader &vtx);

View file

@ -15,14 +15,14 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// TODO: We should transition from doing the transform in software, as seen in TransformPipeline.cpp,
// into doing the transform in the vertex shader - except for Rectangles, there we really need to do
// the transforms ourselves.
#include <stdio.h>
#if defined(_WIN32) && defined(_DEBUG)
#include <windows.h>
#endif
#include "../ge_constants.h"
#include "../GPUState.h"
#include "../../Core/Config.h"
#include "VertexShaderGenerator.h"
@ -36,81 +36,360 @@
static char buffer[16384];
#define WRITE(x, ...) p+=sprintf(p, x "\n" __VA_ARGS__)
#define WRITE p+=sprintf
bool CanUseHardwareTransform(int prim)
{
if (!g_Config.bHardwareTransform)
return false;
return !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES;
}
// prim so we can special case for RECTANGLES :(
void ComputeVertexShaderID(VertexShaderID *id, int prim)
{
int doTexture = (gstate.textureMapEnable & 1) && !(gstate.clearmode & 1);
bool hasColor = (gstate.vertType & GE_VTYPE_COL_MASK) != 0;
bool hasNormal = (gstate.vertType & GE_VTYPE_NRM_MASK) != 0;
bool hasBones = (gstate.vertType & GE_VTYPE_WEIGHT_MASK) != 0;
int shadeLight0 = gstate.getUVGenMode() == 2 ? gstate.getUVLS0() : -1;
int shadeLight1 = gstate.getUVGenMode() == 2 ? gstate.getUVLS1() : -1;
memset(id->d, 0, sizeof(id->d));
id->d[0] = gstate.lmode & 1;
id->d[0] |= ((int)gstate.isModeThrough()) << 1;
id->d[0] |= ((int)gstate.isFogEnabled()) << 2;
id->d[0] |= doTexture << 3;
id->d[0] |= (hasColor & 1) << 4;
if (CanUseHardwareTransform(prim)) {
id->d[0] |= 1 << 8;
id->d[0] |= (hasNormal & 1) << 9;
id->d[0] |= (hasBones & 1) << 10;
// Bits that we will need:
// lightenable * 4
// lighttype * 4
// lightcomp * 4
// uv gen:
// mapping type
// texshade light choices (ONLY IF uv mapping type is shade)
// UV generation mode
id->d[0] |= gstate.getUVGenMode() << 16;
// The next bits are used differently depending on UVgen mode
if (gstate.getUVGenMode() == 1) {
id->d[0] |= gstate.getUVProjMode() << 18;
} else if (gstate.getUVGenMode() == 2) {
id->d[0] |= gstate.getUVLS0() << 18;
id->d[0] |= gstate.getUVLS1() << 20;
}
// Bones
id->d[0] |= (gstate.getNumBoneWeights() - 1) << 22;
// Light bits
for (int i = 0; i < 4; i++) {
id->d[1] |= (gstate.ltype[i] & 3) << (i * 4);
id->d[1] |= ((gstate.ltype[i] >> 8) & 3) << (i * 4 + 2);
}
id->d[1] |= (gstate.materialupdate & 7) << 16;
id->d[1] |= (gstate.lightingEnable & 1) << 19;
for (int i = 0; i < 4; i++) {
id->d[1] |= (gstate.lightEnable[i] & 1) << (20 + i);
}
}
}
void WriteLight(char *p, int l) {
// TODO
}
const char *boneWeightAttrDecl[8] = {
"attribute float a_weight0123;\n",
"attribute vec2 a_weight0123;\n",
"attribute vec3 a_weight0123;\n",
"attribute vec4 a_weight0123;\n",
"attribute vec4 a_weight0123;\nattribute float a_weight4567;\n",
"attribute vec4 a_weight0123;\nattribute vec2 a_weight4567;\n",
"attribute vec4 a_weight0123;\nattribute vec3 a_weight4567;\n",
"attribute vec4 a_weight0123;\nattribute vec4 a_weight4567;\n",
};
char *GenerateVertexShader()
const char *boneWeightAttr[8] = {
"a_weight0123.x",
"a_weight0123.y",
"a_weight0123.z",
"a_weight0123.w",
"a_weight4567.x",
"a_weight4567.y",
"a_weight4567.z",
"a_weight4567.w",
};
enum DoLightComputation {
LIGHT_OFF,
LIGHT_DOTONLY,
LIGHT_FULL,
};
char *GenerateVertexShader(int prim)
{
char *p = buffer;
#if defined(USING_GLES2)
WRITE("precision highp float;");
WRITE(p, "precision highp float;\n");
#elif !defined(FORCE_OPENGL_2_0)
WRITE("#version 130");
WRITE(p, "#version 130\n");
#endif
int lmode = gstate.lmode & 1;
int doTexture = (gstate.textureMapEnable & 1) && !(gstate.clearmode & 1);
WRITE("attribute vec3 a_position;");
if (doTexture)
WRITE("attribute vec2 a_texcoord;");
WRITE("attribute vec4 a_color0;");
if (lmode)
WRITE("attribute vec4 a_color1;");
bool hwXForm = CanUseHardwareTransform(prim);
bool hasColor = (gstate.vertType & GE_VTYPE_COL_MASK) != 0 || !hwXForm;
bool hasNormal = (gstate.vertType & GE_VTYPE_NRM_MASK) != 0 && hwXForm;
DoLightComputation doLight[4] = {LIGHT_OFF, LIGHT_OFF, LIGHT_OFF, LIGHT_OFF};
if (hwXForm) {
int shadeLight0 = gstate.getUVGenMode() == 2 ? gstate.getUVLS0() : -1;
int shadeLight1 = gstate.getUVGenMode() == 2 ? gstate.getUVLS1() : -1;
for (int i = 0; i < 4; i++) {
if (!hasNormal)
continue;
if (i == shadeLight0 || i == shadeLight1)
doLight[i] = LIGHT_DOTONLY;
if ((gstate.lightingEnable & 1) && (gstate.lightEnable[i] & 1))
doLight[i] = LIGHT_FULL;
}
}
if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) {
WRITE(p, "%s", boneWeightAttrDecl[gstate.getNumBoneWeights() - 1]);
}
WRITE(p, "attribute vec3 a_position;\n");
if (doTexture) WRITE(p, "attribute vec2 a_texcoord;\n");
if (hasColor) {
WRITE(p, "attribute vec4 a_color0;\n");
if (lmode && !hwXForm) // only software transform supplies color1 as vertex data
WRITE(p, "attribute vec3 a_color1;\n");
}
if (hwXForm && hasNormal)
WRITE(p, "attribute vec3 a_normal;\n");
if (gstate.isModeThrough()) {
WRITE("uniform mat4 u_proj_through;");
WRITE(p, "uniform mat4 u_proj_through;\n");
} else {
WRITE("uniform mat4 u_proj;");
WRITE(p, "uniform mat4 u_proj;\n");
// Add all the uniforms we'll need to transform properly.
}
WRITE("varying vec4 v_color0;");
if (lmode)
WRITE("varying vec4 v_color1;");
if (doTexture)
WRITE("varying vec2 v_texcoord;");
if (gstate.isFogEnabled())
WRITE("varying float v_depth;");
WRITE("void main() {");
WRITE(" v_color0 = a_color0;");
if (lmode)
WRITE(" v_color1 = a_color1;");
if (doTexture)
WRITE(" v_texcoord = a_texcoord;");
if (gstate.isModeThrough()) {
WRITE(" gl_Position = u_proj_through * vec4(a_position, 1.0);");
if (hwXForm || !hasColor)
WRITE(p, "uniform vec4 u_matambientalpha;\n"); // matambient + matalpha
if (hwXForm) {
// When transforming by hardware, we need a great deal more uniforms...
WRITE(p, "uniform mat4 u_world;\n");
WRITE(p, "uniform mat4 u_view;\n");
if (gstate.getUVGenMode() == 0)
WRITE(p, "uniform vec4 u_uvscaleoffset;\n");
else if (gstate.getUVGenMode() == 1)
WRITE(p, "uniform mat4 u_texmtx;\n");
if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) {
int numBones = 1 + ((gstate.vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT);
for (int i = 0; i < numBones; i++) {
WRITE(p, "uniform mat4 u_bone%i;\n", i);
}
}
if (gstate.lightingEnable & 1) {
WRITE(p, "uniform vec4 u_ambient;\n");
if ((gstate.materialupdate & 2) == 0)
WRITE(p, "uniform vec3 u_matdiffuse;\n");
// if ((gstate.materialupdate & 4) == 0)
WRITE(p, "uniform vec4 u_matspecular;\n"); // Specular coef is contained in alpha
WRITE(p, "uniform vec3 u_matemissive;\n");
}
for (int i = 0; i < 4; i++) {
if (doLight[i] != LIGHT_OFF) {
// These are needed for dot product only (for shade mapping)
WRITE(p, "uniform vec3 u_lightpos%i;\n", i);
WRITE(p, "uniform vec3 u_lightdir%i;\n", i);
WRITE(p, "uniform vec3 u_lightatt%i;\n", i);
}
if (doLight[i] == LIGHT_FULL) {
// These are needed for the full thing
WRITE(p, "uniform vec3 u_lightambient%i;\n", i);
WRITE(p, "uniform vec3 u_lightdiffuse%i;\n", i);
WRITE(p, "uniform vec3 u_lightspecular%i;\n", i);
}
}
}
WRITE(p, "varying vec4 v_color0;\n");
if (lmode) WRITE(p, "varying vec3 v_color1;\n");
if (doTexture) WRITE(p, "varying vec2 v_texcoord;\n");
if (gstate.isFogEnabled()) WRITE(p, "varying float v_depth;\n");
WRITE(p, "void main() {\n");
if (!hwXForm) {
// Simple pass-through of vertex data to fragment shader
if (doTexture)
WRITE(p, " v_texcoord = a_texcoord;\n");
if (hasColor) {
WRITE(p, " v_color0 = a_color0;\n");
if (lmode)
WRITE(p, " v_color1 = a_color1;\n");
} else {
WRITE(p, " v_color0 = u_matambientalpha;\n");
if (lmode)
WRITE(p, " v_color1 = vec3(0.0, 0.0, 0.0);\n");
}
if (gstate.isModeThrough()) {
WRITE(p, " gl_Position = u_proj_through * vec4(a_position, 1.0);\n");
} else {
WRITE(p, " gl_Position = u_proj * vec4(a_position, 1.0);\n");
}
} else {
WRITE(" gl_Position = u_proj * vec4(a_position, 1.0);");
// This is the real deal.
// Step 1: World Transform / Skinning
if ((gstate.vertType & GE_VTYPE_WEIGHT_MASK) == GE_VTYPE_WEIGHT_NONE) {
// No skinning, just standard T&L.
WRITE(p, " vec3 worldpos = (u_world * vec4(a_position, 1.0)).xyz;\n");
if (hasNormal)
WRITE(p, " vec3 worldnormal = (u_world * vec4(a_normal, 0.0)).xyz;\n");
} else {
WRITE(p, " vec3 worldpos = vec3(0.0, 0.0, 0.0);\n");
if (hasNormal)
WRITE(p, " vec3 worldnormal = vec3(0.0, 0.0, 0.0);\n");
int numWeights = 1 + ((gstate.vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT);
for (int i = 0; i < numWeights; i++) {
const char *weightAttr = boneWeightAttr[i];
// workaround for "cant do .x of scalar" issue
if (numWeights == 1 && i == 0) weightAttr = "a_weight0123";
if (numWeights == 5 && i == 4) weightAttr = "a_weight4567";
WRITE(p, " worldpos += %s * (u_bone%i * vec4(a_position, 1.0)).xyz;\n", weightAttr, i);
if (hasNormal)
WRITE(p, " worldnormal += %s * (u_bone%i * vec4(a_normal, 0.0)).xyz;\n", weightAttr, i);
}
// Finally, multiply by world matrix (yes, we have to).
WRITE(p, " worldpos = (u_world * vec4(worldpos, 1.0)).xyz;\n");
if (hasNormal)
WRITE(p, " worldnormal = (u_world * vec4(worldnormal, 0.0)).xyz;\n");
}
if (hasNormal)
WRITE(p, " worldnormal = normalize(worldnormal);\n");
// Step 2: Color/Lighting
if (hasColor) {
WRITE(p, " vec3 unlitColor = a_color0.rgb;\n");
} else {
WRITE(p, " vec3 unlitColor = vec3(1.0, 1.0, 1.0);\n");
}
// TODO: Declare variables for dots for shade mapping if needed.
const char *ambient = (gstate.materialupdate & 1) ? "unlitColor" : "u_matambientalpha.rgb";
const char *diffuse = (gstate.materialupdate & 2) ? "unlitColor" : "u_matdiffuse";
const char *specular = (gstate.materialupdate & 4) ? "unlitColor" : "u_matspecular.rgb";
if (gstate.lightingEnable & 1) {
WRITE(p, " vec4 lightSum0 = vec4(0.0);\n");
WRITE(p, " vec3 lightSum1 = vec3(0.0);\n");
}
// Calculate lights if needed. If shade mapping is enabled, lights may need to be
// at least partially calculated
for (int i = 0; i < 4; i++) {
if (doLight[i] == LIGHT_OFF)
continue;
GELightComputation comp = (GELightComputation)(gstate.ltype[i] & 3);
GELightType type = (GELightType)((gstate.ltype[i] >> 8) & 3);
if (type == GE_LIGHTTYPE_DIRECTIONAL)
WRITE(p, " vec3 toLight%i = u_lightpos%i;\n", i, i);
else
WRITE(p, " vec3 toLight%i = u_lightpos%i - worldpos;\n", i, i);
bool doSpecular = (comp != GE_LIGHTCOMP_ONLYDIFFUSE);
bool poweredDiffuse = comp == GE_LIGHTCOMP_BOTHWITHPOWDIFFUSE;
WRITE(p, " float dot%i = dot(normalize(toLight%i), worldnormal);\n", i, i);
if (poweredDiffuse) {
WRITE(p, " dot%i = pow(dot%i, u_matspecular.a);\n", i, i);
}
if (doLight[i] == LIGHT_DOTONLY)
continue; // Actually, might want specular dot.... TODO
WRITE(p, " float lightScale%i = 1.0;\n", i);
if (type != GE_LIGHTTYPE_DIRECTIONAL) {
// Attenuation
WRITE(p, " float distance%i = length(toLight%i);\n", i, i);
WRITE(p, " lightScale%i = 1.0 / dot(u_lightatt%i, vec3(1.0, distance%i, distance%i*distance%i));\n", i, i, i, i, i);
WRITE(p, " if (lightScale%i > 1.0) lightScale%i = 1.0;\n", i, i);
}
WRITE(p, " vec3 diffuse%i = (u_lightdiffuse%i * %s) * (max(dot%i, 0.0) * lightScale%i);\n", i, i, diffuse, i, i);
if (doSpecular) {
WRITE(p, " vec3 halfVec%i = normalize(normalize(toLight%i) + vec3(0, 0, 1));\n", i, i);
WRITE(p, " dot%i = dot(halfVec%i, worldnormal);\n", i, i);
WRITE(p, " if (dot%i > 0.0)\n", i);
WRITE(p, " lightSum1 += u_lightspecular%i * %s * (pow(dot%i, u_matspecular.a) * (dot%i * lightScale%i));\n", i, specular, i, i, i);
}
WRITE(p, " lightSum0 += vec4(u_lightambient%i + diffuse%i, 0.0);\n", i, i);
}
if (gstate.lightingEnable & 1) {
// Sum up ambient, emissive here.
WRITE(p, " v_color0 = lightSum0 + u_ambient * vec4(%s, 1.0) + vec4(u_matemissive, 0.0);\n", ambient);
if (lmode) {
WRITE(p, " v_color1 = lightSum1;\n");
} else {
WRITE(p, " v_color0 += vec4(lightSum1, 0.0);\n");
}
} else {
// Lighting doesn't affect color.
if (hasColor) {
WRITE(p, " v_color0 = a_color0;\n");
} else {
WRITE(p, " v_color0 = u_matambientalpha;\n");
}
if (lmode)
WRITE(p, " v_color1 = vec3(0.0, 0.0, 0.0);\n");
}
// Step 3: UV generation
if (doTexture) {
switch (gstate.getUVGenMode()) {
case 0: // Scale-offset. Easy.
WRITE(p, " v_texcoord = a_texcoord * u_uvscaleoffset.xy + u_uvscaleoffset.zw;\n");
break;
case 1: // Projection mapping.
switch (gstate.getUVProjMode()) {
case 0: // Use model space XYZ as source
WRITE(p, " vec3 temp_tc = a_position;\n");
break;
case 1: // Use unscaled UV as source
WRITE(p, " vec3 temp_tc = vec3(a_texcoord.xy, 0.0);\n");
break;
case 2: // Use normalized transformed normal as source
WRITE(p, " vec3 temp_tc = normalize(worldnormal);\n");
break;
case 3: // Use non-normalized transformed normal as source
WRITE(p, " vec3 temp_tc = worldnormal;\n");
break;
}
// Transform by texture matrix
WRITE(p, " v_texcoord = (u_texmtx * vec4(temp_tc, 1.0)).xy;\n");
break;
case 2: // Shade mapping - use dots from light sources.
WRITE(p, " v_texcoord = vec2(dot%i, dot%i);\n", gstate.getUVLS0(), gstate.getUVLS1());
break;
case 3:
// ILLEGAL
break;
}
}
// Step 4: Final view and projection transforms.
WRITE(p, " gl_Position = u_proj * (u_view * vec4(worldpos, 1.0));\n");
}
if (gstate.isFogEnabled()) {
WRITE(" v_depth = gl_Position.z;");
}
WRITE("}");
if (gstate.isFogEnabled())
WRITE(p, " v_depth = gl_Position.z;\n");
WRITE(p, "}\n");
return buffer;
}

View file

@ -46,7 +46,9 @@ struct VertexShaderID
}
};
bool CanUseHardwareTransform(int prim);
void ComputeVertexShaderID(VertexShaderID *id, int prim);
// The return value is only valid until the function is called again.
char *GenerateVertexShader();
char *GenerateVertexShader(int prim);

View file

@ -91,6 +91,9 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>../common;..;../native;../native/ext/glew;</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -117,6 +120,7 @@
<ClInclude Include="GLES\DisplayListInterpreter.h" />
<ClInclude Include="GLES\FragmentShaderGenerator.h" />
<ClInclude Include="GLES\Framebuffer.h" />
<ClInclude Include="GLES\IndexGenerator.h" />
<ClInclude Include="GLES\ShaderManager.h" />
<ClInclude Include="GLES\StateMapping.h" />
<ClInclude Include="GLES\TextureCache.h" />
@ -132,11 +136,14 @@
<ClCompile Include="GLES\DisplayListInterpreter.cpp" />
<ClCompile Include="GLES\FragmentShaderGenerator.cpp" />
<ClCompile Include="GLES\Framebuffer.cpp" />
<ClCompile Include="GLES\IndexGenerator.cpp" />
<ClCompile Include="GLES\ShaderManager.cpp" />
<ClCompile Include="GLES\StateMapping.cpp" />
<ClCompile Include="GLES\TextureCache.cpp" />
<ClCompile Include="GLES\TransformPipeline.cpp" />
<ClCompile Include="GLES\VertexDecoder.cpp" />
<ClCompile Include="GLES\VertexDecoder.cpp">
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AssemblyAndSourceCode</AssemblerOutput>
</ClCompile>
<ClCompile Include="GLES\VertexShaderGenerator.cpp" />
<ClCompile Include="GPUState.cpp" />
<ClCompile Include="Math3D.cpp" />

View file

@ -57,6 +57,9 @@
<ClInclude Include="GLES\StateMapping.h">
<Filter>GLES</Filter>
</ClInclude>
<ClInclude Include="GLES\IndexGenerator.h">
<Filter>GLES</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Math3D.cpp">
@ -95,6 +98,9 @@
<ClCompile Include="GLES\StateMapping.cpp">
<Filter>GLES</Filter>
</ClCompile>
<ClCompile Include="GLES\IndexGenerator.cpp">
<Filter>GLES</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />

View file

@ -45,6 +45,12 @@ public:
// Tells the GPU to update the gpuStats structure.
virtual void UpdateStats() = 0;
// Invalidate any cached content sourced from the specified range.
// If size = -1, invalidate everything.
virtual void InvalidateCache(u32 addr, int size) = 0;
// Internal hack to avoid interrupts from "PPGe" drawing (utility UI, etc)
virtual void EnableInterrupts(bool enable) = 0;
virtual void Flush() = 0;
};

View file

@ -78,6 +78,7 @@ void ReapplyGfxState()
{
if (!gpu)
return;
gpu->Flush();
// ShaderManager_DirtyShader();
// The commands are embedded in the command memory so we can just reexecute the words. Convenient.
// To be safe we pass 0xFFFFFFF as the diff.

View file

@ -22,58 +22,10 @@
#include "ge_constants.h"
#include <cstring>
// TODO: this doesn't belong here
struct Color4
{
float r,g,b,a;
Color4() : r(0), g(0), b(0), a(0) { }
Color4(float _r, float _g, float _b, float _a=1.0f)
{
r=_r; g=_g; b=_b; a=_a;
}
Color4(const float in[4]) {r=in[0];g=in[1];b=in[2];a=in[3];}
float &operator [](int i) {return *(&r + i);}
const float &operator [](int i) const {return *(&r + i);}
Color4 operator *(float f) const
{
return Color4(f*r,f*g,f*b,f*a);
}
Color4 operator *(const Color4 &c) const
{
return Color4(r*c.r,g*c.g,b*c.b,a*c.a);
}
void operator *=(const Color4 &c)
{
r*=c.r,g*=c.g,b*=c.b,a*=c.a;
}
Color4 operator +(const Color4 &c) const
{
return Color4(r+c.r,g+c.g,b+c.b,a+c.a);
}
void operator +=(const Color4 &c)
{
r+=c.r;
g+=c.g;
b+=c.b;
a+=c.a;
}
void GetFromRGB(u32 col)
{
r = ((col>>16)&0xff)/255.0f;
g = ((col>>8)&0xff)/255.0f;
b = ((col>>0)&0xff)/255.0f;
}
void GetFromA(u32 col)
{
a = (col&0xff)/255.0f;
}
};
struct GPUgstate
{
// Getting rid of this ugly union in favor of the accessor functions
// might be a good idea....
union
{
u32 cmdmem[256];
@ -239,22 +191,34 @@ struct GPUgstate
float tgenMatrix[12];
float boneMatrix[12 * 8]; // Eight bone matrices.
bool isModeThrough() const { return (vertType & GE_VTYPE_THROUGH) != 0; }
// Pixel Pipeline
bool isModeClear() const { return clearmode & 1; }
bool isCullEnabled() const { return cullfaceEnable & 1; }
int getCullMode() const { return cullmode & 1; }
int getBlendFuncA() const { return blend & 0xF; }
int getCullMode() const { return cullmode & 1; }
int getBlendFuncA() const { return blend & 0xF; }
u32 getFixA() const { return blendfixa & 0xFFFFFF; }
u32 getFixB() const { return blendfixb & 0xFFFFFF; }
int getBlendFuncB() const { return (blend >> 4) & 0xF; }
int getBlendEq() const { return (blend >> 8) & 0x7; }
int getBlendFuncB() const { return (blend >> 4) & 0xF; }
int getBlendEq() const { return (blend >> 8) & 0x7; }
bool isDepthTestEnabled() const { return zTestEnable & 1; }
bool isDepthWriteEnabled() const { return !(zmsk & 1); }
int getDepthTestFunc() const { return ztestfunc & 0x7; }
int getDepthTestFunc() const { return ztestfunc & 0x7; }
bool isFogEnabled() const { return fogEnable & 1; }
};
// Real data in the context ends here
// UV gen
int getUVGenMode() const { return texmapmode & 3;} // 2 bits
int getUVProjMode() const { return (texmapmode >> 8) & 3;} // 2 bits
int getUVLS0() const { return texshade & 0x3; } // 2 bits
int getUVLS1() const { return (texshade >> 8) & 0x3; } // 2 bits
// Vertex type
bool isModeThrough() const { return (vertType & GE_VTYPE_THROUGH) != 0; }
int getNumBoneWeights() const {
return 1 + ((vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT);
}
// Real data in the context ends here
};
// The rest is cached simplified/converted data for fast access.
// Does not need to be saved when saving/restoring context.
struct GPUStateCache
@ -270,7 +234,7 @@ struct GPUStateCache
float lightpos[4][3];
float lightdir[4][3];
float lightatt[4][3];
Color4 lightColor[3][4]; //Amtient Diffuse Specular
float lightColor[3][4][3]; //Amtient Diffuse Specular
float morphWeights[8];
// bezier patch subdivision
@ -291,17 +255,23 @@ struct GPUStatistics
memset(this, 0, sizeof(*this));
}
void resetFrame() {
numJoins = 0;
numDrawCalls = 0;
numVertsTransformed = 0;
numTextureSwitches = 0;
numShaderSwitches = 0;
numFlushes = 0;
numTexturesDecoded = 0;
}
// Per frame statistics
int numJoins;
int numDrawCalls;
int numFlushes;
int numVertsTransformed;
int numTextureSwitches;
int numShaderSwitches;
int numTexturesDecoded;
// Total statistics, updated by the GPU core in UpdateStats
int numFrames;

View file

@ -206,8 +206,9 @@ void NullGPU::ExecuteOp(u32 op, u32 diff)
int behaviour = (data >> 16) & 0xFF;
int signal = data & 0xFFFF;
// TODO: Should this run while interrupts are suspended?
if (interruptsEnabled_)
__TriggerInterruptWithArg(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, signal);
__TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL, signal);
}
break;
@ -237,8 +238,9 @@ void NullGPU::ExecuteOp(u32 op, u32 diff)
case GE_CMD_FINISH:
DEBUG_LOG(G3D,"DL CMD FINISH");
// TODO: Should this run while interrupts are suspended?
if (interruptsEnabled_)
__TriggerInterruptWithArg(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, 0);
__TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, PSP_GE_SUBINTR_FINISH, 0);
break;
case GE_CMD_END:
@ -594,9 +596,9 @@ void NullGPU::ExecuteOp(u32 op, u32 diff)
int l = (cmd - GE_CMD_LAC0) / 3;
int t = (cmd - GE_CMD_LAC0) % 3;
gstate_c.lightColor[t][l].r = r;
gstate_c.lightColor[t][l].g = g;
gstate_c.lightColor[t][l].b = b;
gstate_c.lightColor[t][l][0] = r;
gstate_c.lightColor[t][l][1] = g;
gstate_c.lightColor[t][l][2] = b;
}
break;
@ -836,3 +838,8 @@ void NullGPU::UpdateStats()
gpuStats.numShaders = 0;
gpuStats.numTextures = 0;
}
void NullGPU::InvalidateCache(u32 addr, int size)
{
// Nothing to invalidate.
}

View file

@ -40,6 +40,8 @@ public:
virtual void SetDisplayFramebuffer(u32 framebuf, u32 stride, int format) {}
virtual void CopyDisplayToOutput() {}
virtual void UpdateStats();
virtual void InvalidateCache(u32 addr, int size);
virtual void Flush() {}
private:
bool ProcessDLQueue();

View file

@ -80,9 +80,11 @@ SOURCES += ../Core/MIPS/ARM/Asm.cpp \ #CoreARM
../Core/HLE/sceSsl.cpp \
../Core/HLE/scesupPreAcc.cpp \
../Core/HLE/sceUmd.cpp \
../Core/HLE/sceUsb.cpp \
../Core/HLE/sceUtility.cpp \
../Core/HLE/sceVaudio.cpp \
../Core/HW/MemoryStick.cpp \
../Core/HW/SasAudio.cpp \
../Core/Host.cpp \
../Core/Loaders.cpp \
../Core/MIPS/JitCommon/JitCommon.cpp \
@ -186,9 +188,11 @@ HEADERS += ../Core/MIPS/ARM/Asm.h \
../Core/HLE/sceSsl.h \
../Core/HLE/scesupPreAcc.h \
../Core/HLE/sceUmd.h \
../Core/HLE/sceUsb.h \
../Core/HLE/sceUtility.h \
../Core/HLE/sceVaudio.h \
../Core/HW/MemoryStick.h \
../Core/HW/SasAudio.h \
../Core/Host.h \
../Core/Loaders.h \
../Core/MIPS/JitCommon/JitCommon.h \

View file

@ -189,6 +189,6 @@ QMAKE_CXXFLAGS += -std=c++0x -Wno-unused-function -Wno-unused-variable -Wno-mult
DEFINES += ARM USING_GLES2
blackberry: DEFINES += BLACKBERRY BLACKBERRY10
symbian: {
QMAKE_CXXFLAGS += -march=armv6 -mfpu=vfp -mfloat-abi=softfp -marm -Wno-parentheses -Wno-comment
DEFINES += SYMBIAN
QMAKE_CXXFLAGS += -march=armv6 -mfpu=vfp -mfloat-abi=softfp -marm -Wno-parentheses -Wno-comment
DEFINES += SYMBIAN
}

View file

@ -42,7 +42,7 @@ symbian: {
assets.sources = ../android/assets/ui_atlas.zim ../android/assets/ppge_atlas.zim
assets.path = E:/PPSSPP
DEPLOYMENT += my_deployment assets
ICON = ../assets/icon.svg
ICON = ../assets/icon.svg
# 268MB maximum
TARGET.EPOCHEAPSIZE = 0x40000 0x10000000
TARGET.EPOCSTACKSIZE = 0x10000

View file

@ -29,7 +29,8 @@ DWORD TheThread(LPVOID x);
void EmuThread_Start(const char *filename)
{
// _dbg_clear_();
_tcscpy(fileToStart, filename);
_tcsncpy(fileToStart, filename, sizeof(fileToStart) - 1);
fileToStart[sizeof(fileToStart) - 1] = 0;
unsigned int i;
emuThread = (HANDLE)_beginthreadex(0,0,(unsigned int (__stdcall *)(void *))TheThread,(LPVOID)0,0,&i);

View file

@ -37,7 +37,7 @@
#endif
BOOL g_bFullScreen = FALSE;
RECT rc = {0};
RECT g_normalRC = {0};
namespace MainWindow
{
@ -112,14 +112,15 @@ namespace MainWindow
AdjustWindowRect(&rcOuter, WS_OVERLAPPEDWINDOW, TRUE);
}
void SetZoom(int zoom) {
g_Config.iWindowZoom = zoom;
void SetZoom(float zoom) {
if (zoom < 5)
g_Config.iWindowZoom = (int) zoom;
RECT rc, rcOuter;
GetWindowRectAtZoom(zoom, rc, rcOuter);
GetWindowRectAtZoom((int) zoom, rc, rcOuter);
MoveWindow(hwndMain, rcOuter.left, rcOuter.top, rcOuter.right - rcOuter.left, rcOuter.bottom - rcOuter.top, TRUE);
MoveWindow(hwndDisplay, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE);
PSP_CoreParameter().pixelWidth = 480 * zoom;
PSP_CoreParameter().pixelHeight = 272 * zoom;
PSP_CoreParameter().pixelWidth = (int) (480 * zoom);
PSP_CoreParameter().pixelHeight = (int) (272 * zoom);
GL_Resized();
}
@ -353,6 +354,11 @@ namespace MainWindow
UpdateMenus();
break;
case ID_OPTIONS_HARDWARETRANSFORM:
g_Config.bHardwareTransform = !g_Config.bHardwareTransform;
UpdateMenus();
break;
case ID_FILE_EXIT:
DestroyWindow(hWnd);
break;
@ -483,11 +489,19 @@ namespace MainWindow
_ViewFullScreen(hWnd);
}
break;
case ID_OPTIONS_WIREFRAME:
g_Config.bDrawWireframe = !g_Config.bDrawWireframe;
UpdateMenus();
break;
case ID_OPTIONS_DISPLAYRAWFRAMEBUFFER:
g_Config.bDisplayFramebuffer = !g_Config.bDisplayFramebuffer;
UpdateMenus();
break;
case ID_OPTIONS_FASTMEMORY:
g_Config.bFastMemory = !g_Config.bFastMemory;
UpdateMenus();
break;
//////////////////////////////////////////////////////////////////////////
@ -627,6 +641,9 @@ namespace MainWindow
CHECKITEM(ID_CPU_DYNAREC,g_Config.iCpuCore == CPU_JIT);
CHECKITEM(ID_OPTIONS_BUFFEREDRENDERING, g_Config.bBufferedRendering);
CHECKITEM(ID_OPTIONS_SHOWDEBUGSTATISTICS, g_Config.bShowDebugStats);
CHECKITEM(ID_OPTIONS_WIREFRAME, g_Config.bDrawWireframe);
CHECKITEM(ID_OPTIONS_HARDWARETRANSFORM, g_Config.bHardwareTransform);
CHECKITEM(ID_OPTIONS_FASTMEMORY, g_Config.bFastMemory);
BOOL enable = !Core_IsStepping();
EnableMenuItem(menu,ID_EMULATION_RUN,enable);
@ -688,51 +705,50 @@ namespace MainWindow
}
void _ViewNormal(HWND hWnd)
{
// put caption and border styles back
DWORD dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE);
DWORD dwNewStyle = dwOldStyle | WS_CAPTION | WS_THICKFRAME;
::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);
// put caption and border styles back
DWORD dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE);
DWORD dwNewStyle = dwOldStyle | WS_CAPTION | WS_THICKFRAME;
::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);
// put back the menu bar
::SetMenu(hWnd, menu);
// put back the menu bar
::SetMenu(hWnd, menu);
// resize to normal view
// NOTE: use SWP_FRAMECHANGED to force redraw non-client
const int x = rc.left;
const int y = rc.top;
const int cx = rc.right - rc.left;
const int cy = rc.bottom - rc.top;
::SetWindowPos(hWnd, HWND_NOTOPMOST, x, y, cx, cy, SWP_FRAMECHANGED);
// resize to normal view
// NOTE: use SWP_FRAMECHANGED to force redraw non-client
const int x = g_normalRC.left;
const int y = g_normalRC.top;
const int cx = g_normalRC.right - g_normalRC.left;
const int cy = g_normalRC.bottom - g_normalRC.top;
::SetWindowPos(hWnd, HWND_NOTOPMOST, x, y, cx, cy, SWP_FRAMECHANGED);
// reset full screen indicator
g_bFullScreen = FALSE;
// reset full screen indicator
g_bFullScreen = FALSE;
}
void _ViewFullScreen(HWND hWnd)
{
// keep in mind normal window rectangle
::GetWindowRect(hWnd, &rc);
void _ViewFullScreen(HWND hWnd)
{
// keep in mind normal window rectangle
::GetWindowRect(hWnd, &g_normalRC);
// remove caption and border styles
DWORD dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE);
DWORD dwNewStyle = dwOldStyle & ~(WS_CAPTION | WS_THICKFRAME);
::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);
// remove caption and border styles
DWORD dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE);
DWORD dwNewStyle = dwOldStyle & ~(WS_CAPTION | WS_THICKFRAME);
::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);
// remove the menu bar
::SetMenu(hWnd, NULL);
// remove the menu bar
::SetMenu(hWnd, NULL);
// resize to full screen view
// NOTE: use SWP_FRAMECHANGED to force redraw non-client
const int x = 0;
const int y = 0;
const int cx = ::GetSystemMetrics(SM_CXSCREEN);
const int cy = ::GetSystemMetrics(SM_CYSCREEN);
::SetWindowPos(hWnd, HWND_TOPMOST, x, y, cx, cy, SWP_FRAMECHANGED);
// set full screen indicator
g_bFullScreen = TRUE;
}
// resize to full screen view
// NOTE: use SWP_FRAMECHANGED to force redraw non-client
const int x = 0;
const int y = 0;
const int cx = ::GetSystemMetrics(SM_CXSCREEN);
const int cy = ::GetSystemMetrics(SM_CYSCREEN);
::SetWindowPos(hWnd, HWND_TOPMOST, x, y, cx, cy, SWP_FRAMECHANGED);
// set full screen indicator
g_bFullScreen = TRUE;
}
void SetPlaying(const char *text)
{

View file

@ -41,7 +41,7 @@ int XinputDevice::UpdateState() {
if ( dwResult == ERROR_SUCCESS ) {
this->ApplyDiff(state);
Stick left = NormalizedDeadzoneFilter(state);
__CtrlSetAnalog(left.x, left.y);
__CtrlSetAnalog(left.x, -left.y);
this->prevState = state;
this->check_delay = 0;
return 0;

View file

@ -214,6 +214,9 @@ BEGIN
MENUITEM "&Toggle Full Screen\tF12", ID_OPTIONS_FULLSCREEN
MENUITEM "&Display Raw Framebuffer", ID_OPTIONS_DISPLAYRAWFRAMEBUFFER
MENUITEM "&Buffered Rendering\tF5", ID_OPTIONS_BUFFEREDRENDERING
MENUITEM "&Hardware Transform", ID_OPTIONS_HARDWARETRANSFORM
MENUITEM "&Fast Memory (dynarec, unstable)", ID_OPTIONS_FASTMEMORY
MENUITEM "&Wireframe (experimental)", ID_OPTIONS_WIREFRAME
MENUITEM "&Show Debug Statistics", ID_OPTIONS_SHOWDEBUGSTATISTICS
MENUITEM SEPARATOR
MENUITEM "Screen &1x\tCtrl+1", ID_OPTIONS_SCREEN1X

View file

@ -243,13 +243,16 @@
#define ID_EMULATION_FASTINTERPRETER 40120
#define ID_CPU_FASTINTERPRETER 40121
#define ID_OPTIONS_SHOWDEBUGSTATISTICS 40122
#define ID_OPTIONS_WIREFRAME 40123
#define ID_OPTIONS_HARDWARETRANSFORM 40124
#define ID_OPTIONS_FASTMEMORY 40125
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 232
#define _APS_NEXT_COMMAND_VALUE 40123
#define _APS_NEXT_COMMAND_VALUE 40126
#define _APS_NEXT_CONTROL_VALUE 1162
#define _APS_NEXT_SYMED_VALUE 101
#endif

View file

@ -72,6 +72,7 @@ LOCAL_SRC_FILES := \
$(SRC)/GPU/GLES/Framebuffer.cpp \
$(SRC)/GPU/GLES/DisplayListInterpreter.cpp \
$(SRC)/GPU/GLES/TextureCache.cpp \
$(SRC)/GPU/GLES/IndexGenerator.cpp \
$(SRC)/GPU/GLES/TransformPipeline.cpp \
$(SRC)/GPU/GLES/StateMapping.cpp \
$(SRC)/GPU/GLES/VertexDecoder.cpp \

View file

@ -179,7 +179,6 @@ void EmuScreen::render()
ui_draw2d.Begin(DBMODE_NORMAL);
// Make this configurable.
if (g_Config.bShowTouchControls)
DrawGamepad(ui_draw2d);

View file

@ -239,9 +239,9 @@ void SettingsScreen::render() {
// VLinear vlinear(10, 80, 10);
int x = 30;
int y = 50;
UICheckBox(GEN_ID, x, y += 50, "Enable Sound Emulation", ALIGN_TOPLEFT, &g_Config.bEnableSound);
UICheckBox(GEN_ID, x, y += 50, "Sound Emulation", ALIGN_TOPLEFT, &g_Config.bEnableSound);
UICheckBox(GEN_ID, x, y += 50, "Buffered Rendering (may fix flicker)", ALIGN_TOPLEFT, &g_Config.bBufferedRendering);
UICheckBox(GEN_ID, x, y += 50, "Hardware Transform (experimental)", ALIGN_TOPLEFT, &g_Config.bHardwareTransform);
bool useFastInt = g_Config.iCpuCore == CPU_FASTINTERPRETER;
UICheckBox(GEN_ID, x, y += 50, "Slightly faster interpreter (may crash)", ALIGN_TOPLEFT, &useFastInt);

View file

@ -133,7 +133,7 @@ void NativeMix(short *audio, int num_samples)
{
if (g_mixer)
{
g_mixer->Mix(audio, num_samples/2);
g_mixer->Mix(audio, num_samples);
}
else
{

View file

@ -15,4 +15,4 @@ public class PpssppActivity extends NativeActivity {
{
return false;
}
}
}

View file

@ -91,6 +91,9 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>

View file

@ -129,6 +129,8 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

View file

@ -113,6 +113,9 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../Common;..;../Core;../native/ext/glew;</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>

View file

@ -58,6 +58,10 @@ tests_good = [
"misc/testgp",
"string/string",
"gpu/callbacks/ge_callbacks",
"threads/alarm/alarm",
"threads/alarm/cancel/cancel",
"threads/alarm/refer/refer",
"threads/alarm/set/set",
"threads/events/events",
"threads/events/cancel/cancel",
"threads/events/clear/clear",