mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 18:53:28 +01:00
Merge pull request #1089 from vlj/gl
Store Vertex/Fragment program in a hashtable and do not rebuilt FP if a constant changes
This commit is contained in:
commit
2a6f11b390
@ -57,7 +57,7 @@ LogChannel::LogChannel() : LogChannel("unknown")
|
||||
LogChannel::LogChannel(const std::string& name) :
|
||||
name(name)
|
||||
, mEnabled(true)
|
||||
, mLogLevel(Warning)
|
||||
, mLogLevel(LogSeverityWarning)
|
||||
{}
|
||||
|
||||
void LogChannel::log(const LogMessage &msg)
|
||||
@ -186,16 +186,16 @@ void LogManager::log(LogMessage msg)
|
||||
std::string prefix;
|
||||
switch (msg.mServerity)
|
||||
{
|
||||
case Success:
|
||||
case LogSeveritySuccess:
|
||||
prefix = "S ";
|
||||
break;
|
||||
case Notice:
|
||||
case LogSeverityNotice:
|
||||
prefix = "! ";
|
||||
break;
|
||||
case Warning:
|
||||
case LogSeverityWarning:
|
||||
prefix = "W ";
|
||||
break;
|
||||
case Error:
|
||||
case LogSeverityError:
|
||||
prefix = "E ";
|
||||
break;
|
||||
}
|
||||
@ -265,12 +265,12 @@ void log_message(Log::LogType type, Log::LogSeverity sev, std::string text)
|
||||
else
|
||||
{
|
||||
rMessageBox(text,
|
||||
sev == Notice ? "Notice" :
|
||||
sev == Warning ? "Warning" :
|
||||
sev == Success ? "Success" :
|
||||
sev == Error ? "Error" : "Unknown",
|
||||
sev == Notice ? rICON_INFORMATION :
|
||||
sev == Warning ? rICON_EXCLAMATION :
|
||||
sev == Error ? rICON_ERROR : rICON_INFORMATION);
|
||||
sev == LogSeverityNotice ? "Notice" :
|
||||
sev == LogSeverityWarning ? "Warning" :
|
||||
sev == LogSeveritySuccess ? "Success" :
|
||||
sev == LogSeverityError ? "Error" : "Unknown",
|
||||
sev == LogSeverityNotice ? rICON_INFORMATION :
|
||||
sev == LogSeverityWarning ? rICON_EXCLAMATION :
|
||||
sev == LogSeverityError ? rICON_ERROR : rICON_INFORMATION);
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,10 @@
|
||||
|
||||
//first parameter is of type Log::LogType and text is of type std::string
|
||||
|
||||
#define LOG_SUCCESS(logType, text, ...) log_message(logType, Log::Success, text, ##__VA_ARGS__)
|
||||
#define LOG_NOTICE(logType, text, ...) log_message(logType, Log::Notice, text, ##__VA_ARGS__)
|
||||
#define LOG_WARNING(logType, text, ...) log_message(logType, Log::Warning, text, ##__VA_ARGS__)
|
||||
#define LOG_ERROR(logType, text, ...) log_message(logType, Log::Error, text, ##__VA_ARGS__)
|
||||
#define LOG_SUCCESS(logType, text, ...) log_message(logType, Log::LogSeveritySuccess, text, ##__VA_ARGS__)
|
||||
#define LOG_NOTICE(logType, text, ...) log_message(logType, Log::LogSeverityNotice, text, ##__VA_ARGS__)
|
||||
#define LOG_WARNING(logType, text, ...) log_message(logType, Log::LogSeverityWarning, text, ##__VA_ARGS__)
|
||||
#define LOG_ERROR(logType, text, ...) log_message(logType, Log::LogSeverityError, text, ##__VA_ARGS__)
|
||||
|
||||
namespace Log
|
||||
{
|
||||
@ -50,10 +50,10 @@ namespace Log
|
||||
|
||||
enum LogSeverity : u32
|
||||
{
|
||||
Notice = 0,
|
||||
Warning,
|
||||
Success,
|
||||
Error,
|
||||
LogSeverityNotice = 0,
|
||||
LogSeverityWarning,
|
||||
LogSeveritySuccess,
|
||||
LogSeverityError,
|
||||
};
|
||||
|
||||
struct LogMessage
|
||||
|
337
rpcs3/Emu/RSX/Common/ProgramStateCache.h
Normal file
337
rpcs3/Emu/RSX/Common/ProgramStateCache.h
Normal file
@ -0,0 +1,337 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/RSX/RSXFragmentProgram.h"
|
||||
#include "Emu/RSX/RSXVertexProgram.h"
|
||||
#include "Utilities/Log.h"
|
||||
|
||||
|
||||
enum class SHADER_TYPE
|
||||
{
|
||||
SHADER_TYPE_VERTEX,
|
||||
SHADER_TYPE_FRAGMENT
|
||||
};
|
||||
|
||||
namespace ProgramHashUtil
|
||||
{
|
||||
// Based on
|
||||
// https://github.com/AlexAltea/nucleus/blob/master/nucleus/gpu/rsx_pgraph.cpp
|
||||
union qword
|
||||
{
|
||||
u64 dword[2];
|
||||
u32 word[4];
|
||||
};
|
||||
|
||||
struct HashVertexProgram
|
||||
{
|
||||
size_t operator()(const std::vector<u32> &program) const
|
||||
{
|
||||
// 64-bit Fowler/Noll/Vo FNV-1a hash code
|
||||
size_t hash = 0xCBF29CE484222325ULL;
|
||||
const qword *instbuffer = (const qword*)program.data();
|
||||
size_t instIndex = 0;
|
||||
bool end = false;
|
||||
for (unsigned i = 0; i < program.size() / 4; i++)
|
||||
{
|
||||
const qword inst = instbuffer[instIndex];
|
||||
hash ^= inst.dword[0];
|
||||
hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + (hash << 8) + (hash << 40);
|
||||
hash ^= inst.dword[1];
|
||||
hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + (hash << 8) + (hash << 40);
|
||||
instIndex++;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct VertexProgramCompare
|
||||
{
|
||||
bool operator()(const std::vector<u32> &binary1, const std::vector<u32> &binary2) const
|
||||
{
|
||||
if (binary1.size() != binary2.size()) return false;
|
||||
const qword *instBuffer1 = (const qword*)binary1.data();
|
||||
const qword *instBuffer2 = (const qword*)binary2.data();
|
||||
size_t instIndex = 0;
|
||||
for (unsigned i = 0; i < binary1.size() / 4; i++)
|
||||
{
|
||||
const qword& inst1 = instBuffer1[instIndex];
|
||||
const qword& inst2 = instBuffer2[instIndex];
|
||||
if (inst1.dword[0] != inst2.dword[0] || inst1.dword[1] != inst2.dword[1])
|
||||
return false;
|
||||
instIndex++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct FragmentProgramUtil
|
||||
{
|
||||
/**
|
||||
* returns true if the given source Operand is a constant
|
||||
*/
|
||||
static bool isConstant(u32 sourceOperand)
|
||||
{
|
||||
return ((sourceOperand >> 8) & 0x3) == 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* RSX fragment program constants are inlined inside shader code.
|
||||
* This function takes an instruction from a fragment program and
|
||||
* returns an equivalent instruction where inlined constants
|
||||
* are masked.
|
||||
* This allows to hash/compare fragment programs even if their
|
||||
* inlined constants are modified inbetween
|
||||
*/
|
||||
static qword fragmentMaskConstant(const qword &initialQword)
|
||||
{
|
||||
qword result = initialQword;
|
||||
if (isConstant(initialQword.word[1]))
|
||||
result.word[1] = 0;
|
||||
if (isConstant(initialQword.word[2]))
|
||||
result.word[2] = 0;
|
||||
if (isConstant(initialQword.word[3]))
|
||||
result.word[3] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
size_t getFPBinarySize(void *ptr)
|
||||
{
|
||||
const qword *instBuffer = (const qword*)ptr;
|
||||
size_t instIndex = 0;
|
||||
while (true)
|
||||
{
|
||||
const qword& inst = instBuffer[instIndex];
|
||||
bool isSRC0Constant = isConstant(inst.word[1]);
|
||||
bool isSRC1Constant = isConstant(inst.word[2]);
|
||||
bool isSRC2Constant = isConstant(inst.word[3]);
|
||||
bool end = (inst.word[0] >> 8) & 0x1;
|
||||
|
||||
if (isSRC0Constant || isSRC1Constant || isSRC2Constant)
|
||||
{
|
||||
instIndex += 2;
|
||||
if (end)
|
||||
return instIndex * 4 * 4;
|
||||
continue;
|
||||
}
|
||||
instIndex++;
|
||||
if (end)
|
||||
return (instIndex)* 4 * 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct HashFragmentProgram
|
||||
{
|
||||
size_t operator()(const void *program) const
|
||||
{
|
||||
// 64-bit Fowler/Noll/Vo FNV-1a hash code
|
||||
size_t hash = 0xCBF29CE484222325ULL;
|
||||
const qword *instbuffer = (const qword*)program;
|
||||
size_t instIndex = 0;
|
||||
while (true)
|
||||
{
|
||||
const qword& inst = instbuffer[instIndex];
|
||||
bool end = (inst.word[0] >> 8) & 0x1;
|
||||
if (end)
|
||||
return hash;
|
||||
const qword& maskedInst = FragmentProgramUtil::fragmentMaskConstant(inst);
|
||||
hash ^= maskedInst.dword[0];
|
||||
hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + (hash << 8) + (hash << 40);
|
||||
hash ^= maskedInst.dword[1];
|
||||
hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + (hash << 8) + (hash << 40);
|
||||
instIndex++;
|
||||
// Skip constants
|
||||
if (FragmentProgramUtil::isConstant(inst.word[1]) ||
|
||||
FragmentProgramUtil::isConstant(inst.word[2]) ||
|
||||
FragmentProgramUtil::isConstant(inst.word[3]))
|
||||
instIndex++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct FragmentProgramCompare
|
||||
{
|
||||
bool operator()(const void *binary1, const void *binary2) const
|
||||
{
|
||||
const qword *instBuffer1 = (const qword*)binary1;
|
||||
const qword *instBuffer2 = (const qword*)binary2;
|
||||
size_t instIndex = 0;
|
||||
while (true)
|
||||
{
|
||||
const qword& inst1 = instBuffer1[instIndex];
|
||||
const qword& inst2 = instBuffer2[instIndex];
|
||||
bool end = ((inst1.word[0] >> 8) & 0x1) && ((inst2.word[0] >> 8) & 0x1);
|
||||
if (end)
|
||||
return true;
|
||||
|
||||
const qword& maskedInst1 = FragmentProgramUtil::fragmentMaskConstant(inst1);
|
||||
const qword& maskedInst2 = FragmentProgramUtil::fragmentMaskConstant(inst2);
|
||||
|
||||
if (maskedInst1.dword[0] != maskedInst2.dword[0] || maskedInst1.dword[1] != maskedInst2.dword[1])
|
||||
return false;
|
||||
instIndex++;
|
||||
// Skip constants
|
||||
if (FragmentProgramUtil::isConstant(inst1.word[1]) ||
|
||||
FragmentProgramUtil::isConstant(inst1.word[2]) ||
|
||||
FragmentProgramUtil::isConstant(inst1.word[3]))
|
||||
instIndex++;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cache for program help structure (blob, string...)
|
||||
* The class is responsible for creating the object so the state only has to call getGraphicPipelineState
|
||||
* Template argument is a struct which has the following type declaration :
|
||||
* - a typedef VertexProgramData to a type that encapsulate vertex program info. It should provide an Id member.
|
||||
* - a typedef FragmentProgramData to a types that encapsulate fragment program info. It should provide an Id member and a fragment constant offset vector.
|
||||
* - a typedef PipelineData encapsulating monolithic program.
|
||||
* - a typedef PipelineProperties to a type that encapsulate various state info relevant to program compilation (alpha test, primitive type,...)
|
||||
* - a typedef ExtraData type that will be passed to the buildProgram function.
|
||||
* It should also contains the following function member :
|
||||
* - static void RecompileFragmentProgram(RSXFragmentProgram *RSXFP, FragmentProgramData& fragmentProgramData, size_t ID);
|
||||
* - static void RecompileVertexProgram(RSXVertexProgram *RSXVP, VertexProgramData& vertexProgramData, size_t ID);
|
||||
* - static PipelineData *BuildProgram(VertexProgramData &vertexProgramData, FragmentProgramData &fragmentProgramData, const PipelineProperties &pipelineProperties, const ExtraData& extraData);
|
||||
* - void DeleteProgram(PipelineData *ptr);
|
||||
*/
|
||||
template<typename BackendTraits>
|
||||
class ProgramStateCache
|
||||
{
|
||||
private:
|
||||
typedef std::unordered_map<std::vector<u32>, typename BackendTraits::VertexProgramData, ProgramHashUtil::HashVertexProgram, ProgramHashUtil::VertexProgramCompare> binary2VS;
|
||||
typedef std::unordered_map<void *, typename BackendTraits::FragmentProgramData, ProgramHashUtil::HashFragmentProgram, ProgramHashUtil::FragmentProgramCompare> binary2FS;
|
||||
binary2VS m_cacheVS;
|
||||
binary2FS m_cacheFS;
|
||||
|
||||
size_t m_currentShaderId;
|
||||
std::vector<size_t> dummyFragmentConstantCache;
|
||||
|
||||
struct PSOKey
|
||||
{
|
||||
u32 vpIdx;
|
||||
u32 fpIdx;
|
||||
typename BackendTraits::PipelineProperties properties;
|
||||
};
|
||||
|
||||
struct PSOKeyHash
|
||||
{
|
||||
size_t operator()(const PSOKey &key) const
|
||||
{
|
||||
size_t hashValue = 0;
|
||||
hashValue ^= std::hash<unsigned>()(key.vpIdx);
|
||||
return hashValue;
|
||||
}
|
||||
};
|
||||
|
||||
struct PSOKeyCompare
|
||||
{
|
||||
size_t operator()(const PSOKey &key1, const PSOKey &key2) const
|
||||
{
|
||||
return (key1.vpIdx == key2.vpIdx) && (key1.fpIdx == key2.fpIdx) && (key1.properties == key2.properties);
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<PSOKey, typename BackendTraits::PipelineData*, PSOKeyHash, PSOKeyCompare> m_cachePSO;
|
||||
|
||||
typename BackendTraits::FragmentProgramData& SearchFp(RSXFragmentProgram* rsx_fp, bool& found)
|
||||
{
|
||||
typename binary2FS::iterator It = m_cacheFS.find(vm::get_ptr<void>(rsx_fp->addr));
|
||||
if (It != m_cacheFS.end())
|
||||
{
|
||||
found = true;
|
||||
return It->second;
|
||||
}
|
||||
found = false;
|
||||
LOG_WARNING(RSX, "FP not found in buffer!");
|
||||
size_t actualFPSize = ProgramHashUtil::FragmentProgramUtil::getFPBinarySize(vm::get_ptr<u8>(rsx_fp->addr));
|
||||
void *fpShadowCopy = malloc(actualFPSize);
|
||||
memcpy(fpShadowCopy, vm::get_ptr<u8>(rsx_fp->addr), actualFPSize);
|
||||
typename BackendTraits::FragmentProgramData &newShader = m_cacheFS[fpShadowCopy];
|
||||
BackendTraits::RecompileFragmentProgram(rsx_fp, newShader, m_currentShaderId++);
|
||||
|
||||
return newShader;
|
||||
}
|
||||
|
||||
typename BackendTraits::VertexProgramData& SearchVp(RSXVertexProgram* rsx_vp, bool &found)
|
||||
{
|
||||
typename binary2VS::iterator It = m_cacheVS.find(rsx_vp->data);
|
||||
if (It != m_cacheVS.end())
|
||||
{
|
||||
found = true;
|
||||
return It->second;
|
||||
}
|
||||
found = false;
|
||||
LOG_WARNING(RSX, "VP not found in buffer!");
|
||||
typename BackendTraits::VertexProgramData& newShader = m_cacheVS[rsx_vp->data];
|
||||
BackendTraits::RecompileVertexProgram(rsx_vp, newShader, m_currentShaderId++);
|
||||
|
||||
return newShader;
|
||||
}
|
||||
|
||||
typename BackendTraits::PipelineData *GetProg(const PSOKey &psoKey) const
|
||||
{
|
||||
typename std::unordered_map<PSOKey, typename BackendTraits::PipelineData *, PSOKeyHash, PSOKeyCompare>::const_iterator It = m_cachePSO.find(psoKey);
|
||||
if (It == m_cachePSO.end())
|
||||
return nullptr;
|
||||
return It->second;
|
||||
}
|
||||
|
||||
void Add(typename BackendTraits::PipelineData *prog, const PSOKey& PSOKey)
|
||||
{
|
||||
m_cachePSO.insert(std::make_pair(PSOKey, prog));
|
||||
}
|
||||
|
||||
public:
|
||||
ProgramStateCache() : m_currentShaderId(0) {}
|
||||
~ProgramStateCache()
|
||||
{
|
||||
for (auto pair : m_cachePSO)
|
||||
BackendTraits::DeleteProgram(pair.second);
|
||||
for (auto pair : m_cacheFS)
|
||||
free(pair.first);
|
||||
}
|
||||
|
||||
typename BackendTraits::PipelineData *getGraphicPipelineState(
|
||||
RSXVertexProgram *vertexShader,
|
||||
RSXFragmentProgram *fragmentShader,
|
||||
const typename BackendTraits::PipelineProperties &pipelineProperties,
|
||||
const typename BackendTraits::ExtraData& extraData
|
||||
)
|
||||
{
|
||||
typename BackendTraits::PipelineData *result = nullptr;
|
||||
bool fpFound, vpFound;
|
||||
typename BackendTraits::VertexProgramData &vertexProg = SearchVp(vertexShader, vpFound);
|
||||
typename BackendTraits::FragmentProgramData &fragmentProg = SearchFp(fragmentShader, fpFound);
|
||||
|
||||
if (fpFound && vpFound)
|
||||
{
|
||||
result = GetProg({ vertexProg.id, fragmentProg.id, pipelineProperties });
|
||||
}
|
||||
|
||||
if (result != nullptr)
|
||||
return result;
|
||||
else
|
||||
{
|
||||
LOG_WARNING(RSX, "Add program :");
|
||||
LOG_WARNING(RSX, "*** vp id = %d", vertexProg.id);
|
||||
LOG_WARNING(RSX, "*** fp id = %d", fragmentProg.id);
|
||||
|
||||
result = BackendTraits::BuildProgram(vertexProg, fragmentProg, pipelineProperties, extraData);
|
||||
Add(result, { vertexProg.id, fragmentProg.id, pipelineProperties });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::vector<size_t> &getFragmentConstantOffsetsCache(const RSXFragmentProgram *fragmentShader) const
|
||||
{
|
||||
typename binary2FS::const_iterator It = m_cacheFS.find(vm::get_ptr<void>(fragmentShader->addr));
|
||||
if (It != m_cacheFS.end())
|
||||
return It->second.FragmentConstantOffsetCache;
|
||||
LOG_ERROR(RSX, "Can't retrieve constant offset cache");
|
||||
return dummyFragmentConstantCache;
|
||||
}
|
||||
};
|
@ -105,10 +105,10 @@ std::string GLFragmentDecompilerThread::AddConst()
|
||||
auto data = vm::ptr<u32>::make(m_addr + m_size + 4 * sizeof(u32));
|
||||
|
||||
m_offset = 2 * 4 * sizeof(u32);
|
||||
u32 x = GetData(data[0]);
|
||||
u32 y = GetData(data[1]);
|
||||
u32 z = GetData(data[2]);
|
||||
u32 w = GetData(data[3]);
|
||||
u32 x = 0;//GetData(data[0]);
|
||||
u32 y = 0;//GetData(data[1]);
|
||||
u32 z = 0;//GetData(data[2]);
|
||||
u32 w = 0;//GetData(data[3]);
|
||||
return m_parr.AddParam(PARAM_UNIFORM, "vec4", name,
|
||||
std::string("vec4(") + std::to_string((float&)x) + ", " + std::to_string((float&)y)
|
||||
+ ", " + std::to_string((float&)z) + ", " + std::to_string((float&)w) + ")");
|
||||
@ -613,7 +613,7 @@ void GLFragmentDecompilerThread::Task()
|
||||
m_code_level = 1;
|
||||
m_shader = BuildCode();
|
||||
main.clear();
|
||||
m_parr.params.clear();
|
||||
// m_parr.params.clear();
|
||||
}
|
||||
|
||||
GLFragmentProgram::GLFragmentProgram()
|
||||
@ -673,7 +673,7 @@ void GLFragmentProgram::DecompileAsync(RSXFragmentProgram& prog)
|
||||
|
||||
void GLFragmentProgram::Compile()
|
||||
{
|
||||
if (id)
|
||||
if (id)
|
||||
{
|
||||
glDeleteShader(id);
|
||||
}
|
||||
@ -707,6 +707,15 @@ void GLFragmentProgram::Compile()
|
||||
LOG_NOTICE(RSX, shader.c_str()); // Log the text of the shader that failed to compile
|
||||
Emu.Pause(); // Pause the emulator, we can't really continue from here
|
||||
}
|
||||
for (const GLParamType& PT : parr.params)
|
||||
{
|
||||
if (PT.flag != PARAM_UNIFORM) continue;
|
||||
for (const GLParamItem PI : PT.items)
|
||||
{
|
||||
size_t offset = atoi(PI.name.c_str() + 2);
|
||||
FragmentConstantOffsetCache.push_back(offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFragmentProgram::Delete()
|
||||
|
@ -65,6 +65,7 @@ public:
|
||||
GLParamArray parr;
|
||||
u32 id;
|
||||
std::string shader;
|
||||
std::vector<size_t> FragmentConstantOffsetCache;
|
||||
|
||||
/**
|
||||
* Decompile a fragment shader located in the PS3's Memory. This function operates synchronously.
|
||||
|
@ -1089,6 +1089,28 @@ void GLGSRender::InitFragmentData()
|
||||
return;
|
||||
}
|
||||
|
||||
// Get constant from fragment program
|
||||
const std::vector<size_t> &fragmentOffset = m_prog_buffer.getFragmentConstantOffsetsCache(m_cur_fragment_prog);
|
||||
for (size_t offsetInFP : fragmentOffset)
|
||||
{
|
||||
auto data = vm::ptr<u32>::make(m_cur_fragment_prog->addr + (u32)offsetInFP);
|
||||
|
||||
u32 c0 = (data[0] >> 16 | data[0] << 16);
|
||||
u32 c1 = (data[1] >> 16 | data[1] << 16);
|
||||
u32 c2 = (data[2] >> 16 | data[2] << 16);
|
||||
u32 c3 = (data[3] >> 16 | data[3] << 16);
|
||||
const std::string name = fmt::Format("fc%u", offsetInFP);
|
||||
const int l = m_program.GetLocation(name);
|
||||
checkForGlError("glGetUniformLocation " + name);
|
||||
|
||||
float f0 = (float&)c0;
|
||||
float f1 = (float&)c1;
|
||||
float f2 = (float&)c2;
|
||||
float f3 = (float&)c3;
|
||||
glUniform4f(l, f0, f1, f2, f3);
|
||||
checkForGlError("glUniform4f " + name + fmt::Format(" %u [%f %f %f %f]", l, f0, f1, f2, f3));
|
||||
}
|
||||
|
||||
for (const RSXTransformConstant& c : m_fragment_constants)
|
||||
{
|
||||
u32 id = c.id - m_cur_fragment_prog->offset;
|
||||
@ -1103,6 +1125,7 @@ void GLGSRender::InitFragmentData()
|
||||
checkForGlError("glUniform4f " + name + fmt::Format(" %u [%f %f %f %f]", l, c.x, c.y, c.z, c.w));
|
||||
}
|
||||
|
||||
|
||||
//if (m_fragment_constants.GetCount())
|
||||
// LOG_NOTICE(HLE, "");
|
||||
}
|
||||
@ -1123,88 +1146,9 @@ bool GLGSRender::LoadProgram()
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fp_buf_num = m_prog_buffer.SearchFp(*m_cur_fragment_prog, m_fragment_prog);
|
||||
m_vp_buf_num = m_prog_buffer.SearchVp(*m_cur_vertex_prog, m_vertex_prog);
|
||||
|
||||
if (m_fp_buf_num == -1)
|
||||
{
|
||||
LOG_WARNING(RSX, "FP not found in buffer!");
|
||||
m_fragment_prog.Decompile(*m_cur_fragment_prog);
|
||||
m_fragment_prog.Compile();
|
||||
checkForGlError("m_fragment_prog.Compile");
|
||||
|
||||
// TODO: This shouldn't use current dir
|
||||
fs::file("./FragmentProgram.txt", o_write | o_create | o_trunc).write(m_fragment_prog.shader.c_str(), m_fragment_prog.shader.size());
|
||||
}
|
||||
|
||||
if (m_vp_buf_num == -1)
|
||||
{
|
||||
LOG_WARNING(RSX, "VP not found in buffer!");
|
||||
m_vertex_prog.Decompile(*m_cur_vertex_prog);
|
||||
m_vertex_prog.Compile();
|
||||
checkForGlError("m_vertex_prog.Compile");
|
||||
|
||||
// TODO: This shouldn't use current dir
|
||||
fs::file("./VertexProgram.txt", o_write | o_create | o_trunc).write(m_vertex_prog.shader.c_str(), m_vertex_prog.shader.size());
|
||||
}
|
||||
|
||||
if (m_fp_buf_num != -1 && m_vp_buf_num != -1)
|
||||
{
|
||||
m_program.id = m_prog_buffer.GetProg(m_fp_buf_num, m_vp_buf_num);
|
||||
}
|
||||
|
||||
if (m_program.id)
|
||||
{
|
||||
// RSX Debugger: Check if this program was modified and update it
|
||||
if (Ini.GSLogPrograms.GetValue())
|
||||
{
|
||||
for (auto& program : m_debug_programs)
|
||||
{
|
||||
if (program.id == m_program.id && program.modified)
|
||||
{
|
||||
// TODO: This isn't working perfectly. Is there any better/shorter way to update the program
|
||||
m_vertex_prog.shader = program.vp_shader;
|
||||
m_fragment_prog.shader = program.fp_shader;
|
||||
m_vertex_prog.Wait();
|
||||
m_vertex_prog.Compile();
|
||||
checkForGlError("m_vertex_prog.Compile");
|
||||
m_fragment_prog.Wait();
|
||||
m_fragment_prog.Compile();
|
||||
checkForGlError("m_fragment_prog.Compile");
|
||||
glAttachShader(m_program.id, m_vertex_prog.id);
|
||||
glAttachShader(m_program.id, m_fragment_prog.id);
|
||||
glLinkProgram(m_program.id);
|
||||
checkForGlError("glLinkProgram");
|
||||
glDetachShader(m_program.id, m_vertex_prog.id);
|
||||
glDetachShader(m_program.id, m_fragment_prog.id);
|
||||
program.vp_id = m_vertex_prog.id;
|
||||
program.fp_id = m_fragment_prog.id;
|
||||
program.modified = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_program.Use();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_program.Create(m_vertex_prog.id, m_fragment_prog.id);
|
||||
checkForGlError("m_program.Create");
|
||||
m_prog_buffer.Add(m_program, m_fragment_prog, *m_cur_fragment_prog, m_vertex_prog, *m_cur_vertex_prog);
|
||||
checkForGlError("m_prog_buffer.Add");
|
||||
m_program.Use();
|
||||
|
||||
// RSX Debugger
|
||||
if (Ini.GSLogPrograms.GetValue())
|
||||
{
|
||||
RSXDebuggerProgram program;
|
||||
program.id = m_program.id;
|
||||
program.vp_id = m_vertex_prog.id;
|
||||
program.fp_id = m_fragment_prog.id;
|
||||
program.vp_shader = m_vertex_prog.shader;
|
||||
program.fp_shader = m_fragment_prog.shader;
|
||||
m_debug_programs.push_back(program);
|
||||
}
|
||||
}
|
||||
GLProgram *result = m_prog_buffer.getGraphicPipelineState(m_cur_vertex_prog, m_cur_fragment_prog, nullptr, nullptr);
|
||||
m_program.id = result->id;
|
||||
m_program.Use();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1477,7 +1421,6 @@ void GLGSRender::OnExitThread()
|
||||
m_fbo.Delete();
|
||||
m_vbo.Delete();
|
||||
m_vao.Delete();
|
||||
m_prog_buffer.Clear();
|
||||
}
|
||||
|
||||
void GLGSRender::OnReset()
|
||||
|
@ -1,16 +1,13 @@
|
||||
#pragma once
|
||||
#include "Emu/RSX/GSRender.h"
|
||||
#include "GLBuffers.h"
|
||||
#include "GLProgramBuffer.h"
|
||||
|
||||
#pragma comment(lib, "opengl32.lib")
|
||||
|
||||
#define RSX_DEBUG 1
|
||||
|
||||
extern GLenum g_last_gl_error;
|
||||
void printGlError(GLenum err, const char* situation);
|
||||
void printGlError(GLenum err, const std::string& situation);
|
||||
u32 LinearToSwizzleAddress(u32 x, u32 y, u32 z, u32 log2_width, u32 log2_height, u32 log2_depth);
|
||||
|
||||
#include "GLProgramBuffer.h"
|
||||
|
||||
#pragma comment(lib, "opengl32.lib")
|
||||
|
||||
#if RSX_DEBUG
|
||||
#define checkForGlError(sit) if((g_last_gl_error = glGetError()) != GL_NO_ERROR) printGlError(g_last_gl_error, sit)
|
||||
@ -18,6 +15,12 @@ u32 LinearToSwizzleAddress(u32 x, u32 y, u32 z, u32 log2_width, u32 log2_height,
|
||||
#define checkForGlError(sit)
|
||||
#endif
|
||||
|
||||
extern GLenum g_last_gl_error;
|
||||
void printGlError(GLenum err, const char* situation);
|
||||
void printGlError(GLenum err, const std::string& situation);
|
||||
u32 LinearToSwizzleAddress(u32 x, u32 y, u32 z, u32 log2_width, u32 log2_height, u32 log2_depth);
|
||||
|
||||
|
||||
class GLTexture
|
||||
{
|
||||
u32 m_id;
|
||||
|
@ -38,7 +38,6 @@ void GLProgram::Create(const u32 vp, const u32 fp)
|
||||
Delete();
|
||||
|
||||
id = glCreateProgram();
|
||||
|
||||
glAttachShader(id, vp);
|
||||
glAttachShader(id, fp);
|
||||
|
||||
|
@ -2,133 +2,4 @@
|
||||
#include "Utilities/Log.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
|
||||
#include "GLProgramBuffer.h"
|
||||
|
||||
int GLProgramBuffer::SearchFp(const RSXFragmentProgram& rsx_fp, GLFragmentProgram& gl_fp)
|
||||
{
|
||||
for(u32 i=0; i<m_buf.size(); ++i)
|
||||
{
|
||||
if(memcmp(&m_buf[i].fp_data[0], vm::get_ptr<void>(rsx_fp.addr), m_buf[i].fp_data.size()) != 0) continue;
|
||||
|
||||
gl_fp.id = m_buf[i].fp_id;
|
||||
gl_fp.shader = m_buf[i].fp_shader.c_str();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int GLProgramBuffer::SearchVp(const RSXVertexProgram& rsx_vp, GLVertexProgram& gl_vp)
|
||||
{
|
||||
for(u32 i=0; i<m_buf.size(); ++i)
|
||||
{
|
||||
if(m_buf[i].vp_data.size() != rsx_vp.data.size()) continue;
|
||||
if(memcmp(m_buf[i].vp_data.data(), rsx_vp.data.data(), rsx_vp.data.size() * 4) != 0) continue;
|
||||
|
||||
gl_vp.id = m_buf[i].vp_id;
|
||||
gl_vp.shader = m_buf[i].vp_shader.c_str();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool GLProgramBuffer::CmpVP(const u32 a, const u32 b) const
|
||||
{
|
||||
if(m_buf[a].vp_data.size() != m_buf[b].vp_data.size()) return false;
|
||||
return memcmp(m_buf[a].vp_data.data(), m_buf[b].vp_data.data(), m_buf[a].vp_data.size() * 4) == 0;
|
||||
}
|
||||
|
||||
bool GLProgramBuffer::CmpFP(const u32 a, const u32 b) const
|
||||
{
|
||||
if(m_buf[a].fp_data.size() != m_buf[b].fp_data.size()) return false;
|
||||
return memcmp(m_buf[a].fp_data.data(), m_buf[b].fp_data.data(), m_buf[a].fp_data.size()) == 0;
|
||||
}
|
||||
|
||||
u32 GLProgramBuffer::GetProg(u32 fp, u32 vp) const
|
||||
{
|
||||
if(fp == vp)
|
||||
{
|
||||
/*
|
||||
LOG_NOTICE(RSX, "Get program (%d):", fp);
|
||||
LOG_NOTICE(RSX, "*** prog id = %d", m_buf[fp].prog_id);
|
||||
LOG_NOTICE(RSX, "*** vp id = %d", m_buf[fp].vp_id);
|
||||
LOG_NOTICE(RSX, "*** fp id = %d", m_buf[fp].fp_id);
|
||||
|
||||
LOG_NOTICE(RSX, "*** vp shader = \n%s", m_buf[fp].vp_shader.wx_str());
|
||||
LOG_NOTICE(RSX, "*** fp shader = \n%s", m_buf[fp].fp_shader.wx_str());
|
||||
*/
|
||||
return m_buf[fp].prog_id;
|
||||
}
|
||||
|
||||
for(u32 i=0; i<m_buf.size(); ++i)
|
||||
{
|
||||
if(i == fp || i == vp) continue;
|
||||
|
||||
if(CmpVP(vp, i) && CmpFP(fp, i))
|
||||
{
|
||||
/*
|
||||
LOG_NOTICE(RSX, "Get program (%d):", i);
|
||||
LOG_NOTICE(RSX, "*** prog id = %d", m_buf[i].prog_id);
|
||||
LOG_NOTICE(RSX, "*** vp id = %d", m_buf[i].vp_id);
|
||||
LOG_NOTICE(RSX, "*** fp id = %d", m_buf[i].fp_id);
|
||||
|
||||
LOG_NOTICE(RSX, "*** vp shader = \n%s", m_buf[i].vp_shader.wx_str());
|
||||
LOG_NOTICE(RSX, "*** fp shader = \n%s", m_buf[i].fp_shader.wx_str());
|
||||
*/
|
||||
return m_buf[i].prog_id;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GLProgramBuffer::Add(GLProgram& prog, GLFragmentProgram& gl_fp, RSXFragmentProgram& rsx_fp, GLVertexProgram& gl_vp, RSXVertexProgram& rsx_vp)
|
||||
{
|
||||
GLBufferInfo new_buf;
|
||||
|
||||
LOG_NOTICE(RSX, "Add program (%d):", m_buf.size());
|
||||
LOG_NOTICE(RSX, "*** prog id = %d", prog.id);
|
||||
LOG_NOTICE(RSX, "*** vp id = %d", gl_vp.id);
|
||||
LOG_NOTICE(RSX, "*** fp id = %d", gl_fp.id);
|
||||
LOG_NOTICE(RSX, "*** vp data size = %d", rsx_vp.data.size() * 4);
|
||||
LOG_NOTICE(RSX, "*** fp data size = %d", rsx_fp.size);
|
||||
|
||||
LOG_NOTICE(RSX, "*** vp shader = \n%s", gl_vp.shader.c_str());
|
||||
LOG_NOTICE(RSX, "*** fp shader = \n%s", gl_fp.shader.c_str());
|
||||
|
||||
|
||||
new_buf.prog_id = prog.id;
|
||||
new_buf.vp_id = gl_vp.id;
|
||||
new_buf.fp_id = gl_fp.id;
|
||||
|
||||
new_buf.fp_data.insert(new_buf.fp_data.end(), vm::get_ptr<u8>(rsx_fp.addr), vm::get_ptr<u8>(rsx_fp.addr + rsx_fp.size));
|
||||
new_buf.vp_data = rsx_vp.data;
|
||||
|
||||
new_buf.vp_shader = gl_vp.shader;
|
||||
new_buf.fp_shader = gl_fp.shader;
|
||||
|
||||
m_buf.push_back(new_buf);
|
||||
}
|
||||
|
||||
void GLProgramBuffer::Clear()
|
||||
{
|
||||
for (u32 i = 0; i < m_buf.size(); ++i)
|
||||
{
|
||||
glDetachShader(m_buf[i].prog_id, m_buf[i].fp_id);
|
||||
glDetachShader(m_buf[i].prog_id, m_buf[i].vp_id);
|
||||
glDeleteShader(m_buf[i].fp_id);
|
||||
glDeleteShader(m_buf[i].vp_id);
|
||||
glDeleteProgram(m_buf[i].prog_id);
|
||||
|
||||
m_buf[i].fp_data.clear();
|
||||
m_buf[i].vp_data.clear();
|
||||
|
||||
m_buf[i].vp_shader.clear();
|
||||
m_buf[i].fp_shader.clear();
|
||||
}
|
||||
|
||||
m_buf.clear();
|
||||
}
|
||||
//#include "GLProgramBuffer.h"
|
@ -1,29 +1,63 @@
|
||||
#pragma once
|
||||
#include "GLProgram.h"
|
||||
#include "../Common/ProgramStateCache.h"
|
||||
#include "Utilities/File.h"
|
||||
|
||||
struct GLBufferInfo
|
||||
struct GLTraits
|
||||
{
|
||||
u32 prog_id;
|
||||
u32 fp_id;
|
||||
u32 vp_id;
|
||||
std::vector<u8> fp_data;
|
||||
std::vector<u32> vp_data;
|
||||
std::string fp_shader;
|
||||
std::string vp_shader;
|
||||
typedef GLVertexProgram VertexProgramData;
|
||||
typedef GLFragmentProgram FragmentProgramData;
|
||||
typedef GLProgram PipelineData;
|
||||
typedef void* PipelineProperties;
|
||||
typedef void* ExtraData;
|
||||
|
||||
static
|
||||
void RecompileFragmentProgram(RSXFragmentProgram *RSXFP, FragmentProgramData& fragmentProgramData, size_t ID)
|
||||
{
|
||||
fragmentProgramData.Decompile(*RSXFP);
|
||||
fragmentProgramData.Compile();
|
||||
//checkForGlError("m_fragment_prog.Compile");
|
||||
|
||||
// TODO: This shouldn't use current dir
|
||||
fs::file("./FragmentProgram.txt", o_write | o_create | o_trunc).write(fragmentProgramData.shader.c_str(), fragmentProgramData.shader.size());
|
||||
}
|
||||
|
||||
static
|
||||
void RecompileVertexProgram(RSXVertexProgram *RSXVP, VertexProgramData& vertexProgramData, size_t ID)
|
||||
{
|
||||
vertexProgramData.Decompile(*RSXVP);
|
||||
vertexProgramData.Compile();
|
||||
//checkForGlError("m_vertex_prog.Compile");
|
||||
|
||||
// TODO: This shouldn't use current dir
|
||||
fs::file("./VertexProgram.txt", o_write | o_create | o_trunc).write(vertexProgramData.shader.c_str(), vertexProgramData.shader.size());
|
||||
}
|
||||
|
||||
static
|
||||
PipelineData *BuildProgram(VertexProgramData &vertexProgramData, FragmentProgramData &fragmentProgramData, const PipelineProperties &pipelineProperties, const ExtraData& extraData)
|
||||
{
|
||||
GLProgram *result = new GLProgram();
|
||||
result->Create(vertexProgramData.id, fragmentProgramData.id);
|
||||
//checkForGlError("m_program.Create");
|
||||
result->Use();
|
||||
|
||||
LOG_NOTICE(RSX, "*** prog id = %d", result->id);
|
||||
LOG_NOTICE(RSX, "*** vp id = %d", vertexProgramData.id);
|
||||
LOG_NOTICE(RSX, "*** fp id = %d", fragmentProgramData.id);
|
||||
|
||||
LOG_NOTICE(RSX, "*** vp shader = \n%s", vertexProgramData.shader.c_str());
|
||||
LOG_NOTICE(RSX, "*** fp shader = \n%s", fragmentProgramData.shader.c_str());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
void DeleteProgram(PipelineData *ptr)
|
||||
{
|
||||
ptr->Delete();
|
||||
}
|
||||
};
|
||||
|
||||
struct GLProgramBuffer
|
||||
class GLProgramBuffer : public ProgramStateCache<GLTraits>
|
||||
{
|
||||
std::vector<GLBufferInfo> m_buf;
|
||||
|
||||
int SearchFp(const RSXFragmentProgram& rsx_fp, GLFragmentProgram& gl_fp);
|
||||
int SearchVp(const RSXVertexProgram& rsx_vp, GLVertexProgram& gl_vp);
|
||||
|
||||
bool CmpVP(const u32 a, const u32 b) const;
|
||||
bool CmpFP(const u32 a, const u32 b) const;
|
||||
|
||||
u32 GetProg(u32 fp, u32 vp) const;
|
||||
|
||||
void Add(GLProgram& prog, GLFragmentProgram& gl_fp, RSXFragmentProgram& rsx_fp, GLVertexProgram& gl_vp, RSXVertexProgram& rsx_vp);
|
||||
void Clear();
|
||||
};
|
||||
|
@ -822,7 +822,7 @@ void GLVertexProgram::DecompileAsync(RSXVertexProgram& prog)
|
||||
|
||||
void GLVertexProgram::Compile()
|
||||
{
|
||||
if (id)
|
||||
if (id)
|
||||
{
|
||||
glDeleteShader(id);
|
||||
}
|
||||
|
@ -76,16 +76,16 @@ struct wxWriter : Log::LogListener
|
||||
{
|
||||
switch (msg.mServerity)
|
||||
{
|
||||
case Log::Notice:
|
||||
case Log::LogSeverityNotice:
|
||||
llogcon->SetDefaultStyle(m_color_white);
|
||||
break;
|
||||
case Log::Warning:
|
||||
case Log::LogSeverityWarning:
|
||||
llogcon->SetDefaultStyle(m_color_yellow);
|
||||
break;
|
||||
case Log::Error:
|
||||
case Log::LogSeverityError:
|
||||
llogcon->SetDefaultStyle(m_color_red);
|
||||
break;
|
||||
case Log::Success:
|
||||
case Log::LogSeveritySuccess:
|
||||
llogcon->SetDefaultStyle(m_color_green);
|
||||
break;
|
||||
default:
|
||||
|
@ -416,6 +416,7 @@
|
||||
<ClInclude Include="Emu\Memory\atomic.h" />
|
||||
<ClInclude Include="Emu\Memory\refcnt.h" />
|
||||
<ClInclude Include="Emu\RSX\CgBinaryProgram.h" />
|
||||
<ClInclude Include="Emu\RSX\Common\ProgramStateCache.h" />
|
||||
<ClInclude Include="Emu\RSX\GCM.h" />
|
||||
<ClInclude Include="Emu\RSX\GL\GLBuffers.h" />
|
||||
<ClInclude Include="Emu\RSX\GL\GLFragmentProgram.h" />
|
||||
|
@ -90,6 +90,9 @@
|
||||
<Filter Include="Emu\CPU\ARMv7\Objects">
|
||||
<UniqueIdentifier>{368770cf-c8d9-4f4a-9ac3-5bdf48101ffe}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Emu\GPU\RSX\Common">
|
||||
<UniqueIdentifier>{2a8841dc-bce0-41bb-9fcb-5bf1f8dda213}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Crypto\aes.cpp">
|
||||
@ -1549,5 +1552,8 @@
|
||||
<ClInclude Include="..\Utilities\File.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\RSX\Common\ProgramStateCache.h">
|
||||
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue
Block a user