mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge remote-tracking branch 'upstream/master'
Conflicts: Core/Dialog/SavedataParam.cpp
This commit is contained in:
commit
d23e751557
91 changed files with 3631 additions and 1262 deletions
33
.gitignore
vendored
33
.gitignore
vendored
|
@ -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*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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] = "";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -49,14 +49,17 @@ void CConfig::Load(const char *iniFileName)
|
|||
general->Get("IgnoreBadMemAccess", &bIgnoreBadMemAccess, true);
|
||||
general->Get("CurrentDirectory", ¤tDirectory, "");
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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*)¶m.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
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
PSPSaveDialog();
|
||||
virtual ~PSPSaveDialog();
|
||||
|
||||
virtual void Init(int paramAddr);
|
||||
virtual u32 Init(int paramAddr);
|
||||
virtual void Update();
|
||||
void Shutdown();
|
||||
|
||||
|
|
|
@ -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*)¶m->dataBuf));
|
||||
u8 *data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->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*)¶m->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*)¶m->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*)¶m->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*)¶m->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*)¶m->dataBuf));
|
||||
u8 *data_ = (u8*)Memory::GetPointer(*((unsigned int*)¶m->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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 ¤tDirectory, const std::string &inPath, std::string &outPath)
|
||||
static bool RealPath(const std::string ¤tDirectory, const std::string &inPath, std::string &outPath)
|
||||
{
|
||||
size_t inLen = inPath.length();
|
||||
if (inLen == 0)
|
||||
|
@ -81,70 +82,27 @@ bool RealPath(const std::string ¤tDirectory, 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 ¤tDirectory, 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(¤tThread->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(¤tThread->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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
338
GPU/GLES/IndexGenerator.cpp
Normal 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
76
GPU/GLES/IndexGenerator.h
Normal 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_;
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -179,7 +179,6 @@ void EmuScreen::render()
|
|||
|
||||
ui_draw2d.Begin(DBMODE_NORMAL);
|
||||
|
||||
// Make this configurable.
|
||||
if (g_Config.bShowTouchControls)
|
||||
DrawGamepad(ui_draw2d);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -15,4 +15,4 @@ public class PpssppActivity extends NativeActivity {
|
|||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
4
test.py
4
test.py
|
@ -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",
|
||||
|
|
Loading…
Add table
Reference in a new issue