mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-04-18 13:08:05 -05:00
Merge branch 'cemu-project:main' into cmake-sign
This commit is contained in:
commit
4f53bb06c8
|
|
@ -93,7 +93,7 @@ if (MACOS_BUNDLE)
|
|||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © ${CURRENT_YEAR} Cemu Project")
|
||||
|
||||
set(MACOSX_BUNDLE_CATEGORY "public.app-category.games")
|
||||
set(MACOSX_MINIMUM_SYSTEM_VERSION "12.0")
|
||||
set(MACOSX_MINIMUM_SYSTEM_VERSION "13.4")
|
||||
set(MACOSX_BUNDLE_TYPE_EXTENSION "wua")
|
||||
|
||||
set_target_properties(CemuBin PROPERTIES
|
||||
|
|
|
|||
|
|
@ -427,10 +427,8 @@ void cemu_initForGame()
|
|||
cemuLog_log(LogType::Force, "------- Run title -------");
|
||||
// wait till GPU thread is initialized
|
||||
while (g_isGPUInitFinished == false) std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
// init initial thread
|
||||
OSThread_t* initialThread = coreinit::OSGetDefaultThread(1);
|
||||
coreinit::OSSetThreadPriority(initialThread, 16);
|
||||
coreinit::OSRunThread(initialThread, PPCInterpreter_makeCallableExportDepr(coreinit_start), 0, nullptr);
|
||||
// run coreinit rpl_entry
|
||||
RPLLoader_CallCoreinitEntrypoint();
|
||||
// init AX and start AX I/O thread
|
||||
snd_core::AXOut_init();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -328,7 +328,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
|
|||
}
|
||||
|
||||
m_title_ids = ParseTitleIds(rules, "titleIds");
|
||||
if(m_title_ids.empty())
|
||||
if(m_title_ids.empty() && !m_universal)
|
||||
throw std::exception();
|
||||
|
||||
auto option_fsPriority = rules.FindOption("fsPriority");
|
||||
|
|
@ -532,6 +532,9 @@ std::string GraphicPack2::GetNormalizedPathString() const
|
|||
|
||||
bool GraphicPack2::ContainsTitleId(uint64_t title_id) const
|
||||
{
|
||||
if (m_universal)
|
||||
return true;
|
||||
|
||||
const auto it = std::find_if(m_title_ids.begin(), m_title_ids.end(), [title_id](uint64 id) { return id == title_id; });
|
||||
return it != m_title_ids.end();
|
||||
}
|
||||
|
|
@ -1188,7 +1191,7 @@ std::vector<GraphicPack2::PresetPtr> GraphicPack2::GetActivePresets() const
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint64> GraphicPack2::ParseTitleIds(IniParser& rules, const char* option_name) const
|
||||
std::vector<uint64> GraphicPack2::ParseTitleIds(IniParser& rules, const char* option_name)
|
||||
{
|
||||
std::vector<uint64> result;
|
||||
|
||||
|
|
@ -1196,6 +1199,12 @@ std::vector<uint64> GraphicPack2::ParseTitleIds(IniParser& rules, const char* op
|
|||
if (!option_text)
|
||||
return result;
|
||||
|
||||
if (*option_text == "*")
|
||||
{
|
||||
m_universal = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
for (auto& token : TokenizeView(*option_text, ','))
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ public:
|
|||
bool Reload();
|
||||
|
||||
bool HasName() const { return !m_name.empty(); }
|
||||
bool IsUniversal() const { return m_universal; }
|
||||
|
||||
const std::string& GetName() const { return m_name.empty() ? m_virtualPath : m_name; }
|
||||
const std::string& GetVirtualPath() const { return m_virtualPath; } // returns the path in the gfx tree hierarchy
|
||||
|
|
@ -122,6 +123,8 @@ public:
|
|||
const std::vector<uint64_t>& GetTitleIds() const { return m_title_ids; }
|
||||
bool HasCustomVSyncFrequency() const { return m_vsync_frequency >= 1; }
|
||||
sint32 GetCustomVSyncFrequency() const { return m_vsync_frequency; }
|
||||
|
||||
const std::vector<std::pair<MPTR, GPCallbackType>>& GetCallbacks() const { return m_callbacks; }
|
||||
|
||||
// texture rules
|
||||
const std::vector<TextureRule>& GetTextureRules() const { return m_texture_rules; }
|
||||
|
|
@ -229,6 +232,7 @@ private:
|
|||
bool m_activated = false; // set if the graphic pack is currently used by the running game
|
||||
std::vector<uint64_t> m_title_ids;
|
||||
bool m_patchedFilesLoaded = false; // set to true once patched files are loaded
|
||||
bool m_universal = false; // set if this pack applies to every title id
|
||||
|
||||
sint32 m_vsync_frequency = -1;
|
||||
sint32 m_fs_priority = 100;
|
||||
|
|
@ -256,7 +260,7 @@ private:
|
|||
|
||||
std::unordered_map<std::string, PresetVar> ParsePresetVars(IniParser& rules) const;
|
||||
|
||||
std::vector<uint64> ParseTitleIds(IniParser& rules, const char* option_name) const;
|
||||
std::vector<uint64> ParseTitleIds(IniParser& rules, const char* option_name);
|
||||
|
||||
CustomShader LoadShader(const fs::path& path, uint64 shader_base_hash, uint64 shader_aux_hash, GP_SHADER_TYPE shader_type, bool isMetalShader) const;
|
||||
void ApplyShaderPresets(std::string& shader_source) const;
|
||||
|
|
@ -282,6 +286,8 @@ private:
|
|||
void LogPatchesSyntaxError(sint32 lineNumber, std::string_view errorMsg);
|
||||
|
||||
std::vector<PatchGroup*> list_patchGroups;
|
||||
|
||||
std::vector<std::pair<MPTR, GPCallbackType>> m_callbacks;
|
||||
|
||||
static std::recursive_mutex mtx_patches;
|
||||
static std::vector<const RPLModule*> list_modules;
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ void GraphicPack2::ApplyPatchesForModule(const RPLModule* rpl)
|
|||
std::vector<PatchGroup*> list_groups;
|
||||
for (auto itr : list_patchGroups)
|
||||
{
|
||||
if (itr->matchesCRC(rpl->patchCRC))
|
||||
if (itr->matchesCRC(rpl->patchCRC) || (itr->m_isRpxOnlyTarget && rpl->IsRPX()))
|
||||
list_groups.emplace_back(itr);
|
||||
}
|
||||
// apply all groups at once
|
||||
|
|
@ -188,7 +188,7 @@ void GraphicPack2::RevertPatchesForModule(const RPLModule* rpl)
|
|||
std::vector<PatchGroup*> list_groups;
|
||||
for (auto itr : list_patchGroups)
|
||||
{
|
||||
if (itr->matchesCRC(rpl->patchCRC))
|
||||
if (itr->matchesCRC(rpl->patchCRC) || (itr->m_isRpxOnlyTarget && rpl->IsRPX()))
|
||||
list_groups.emplace_back(itr);
|
||||
}
|
||||
// undo all groups at once
|
||||
|
|
|
|||
|
|
@ -212,6 +212,10 @@ private:
|
|||
bool m_addrRelocated{};
|
||||
};
|
||||
|
||||
enum class GPCallbackType {
|
||||
Entry
|
||||
};
|
||||
|
||||
class PatchGroup
|
||||
{
|
||||
friend class GraphicPack2;
|
||||
|
|
@ -256,7 +260,9 @@ private:
|
|||
std::string name;
|
||||
std::vector<uint32> list_moduleMatches;
|
||||
std::vector<PatchEntry*> list_patches;
|
||||
std::vector<std::pair<std::string, GPCallbackType>> list_callbacks;
|
||||
uint32 codeCaveSize;
|
||||
MEMPTR<void> codeCaveMem;
|
||||
bool m_isApplied{};
|
||||
bool m_isRpxOnlyTarget{};
|
||||
};
|
||||
|
|
@ -710,6 +710,21 @@ void GraphicPack2::ApplyPatchGroups(std::vector<PatchGroup*>& groups, const RPLM
|
|||
continue;
|
||||
patchInstruction->applyPatch();
|
||||
}
|
||||
|
||||
for (const auto& [name, type] : patchGroup->list_callbacks)
|
||||
{
|
||||
auto it = patchContext.map_values.find(name);
|
||||
if (it != patchContext.map_values.end())
|
||||
{
|
||||
m_callbacks.push_back(std::make_pair(it->second, type));
|
||||
}
|
||||
else
|
||||
{
|
||||
patchContext.errorHandler.printError(patchGroup, -1, fmt::format("Failed to resolve .callback symbol: {}", name));
|
||||
patchContext.errorHandler.showStageErrorMessageBox();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// mark groups as applied
|
||||
for (auto patchGroup : groups)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ void GraphicPack2::CancelParsingPatches()
|
|||
|
||||
void GraphicPack2::AddPatchGroup(PatchGroup* group)
|
||||
{
|
||||
if (group->list_moduleMatches.empty())
|
||||
if (group->list_moduleMatches.empty() && !group->m_isRpxOnlyTarget)
|
||||
{
|
||||
LogPatchesSyntaxError(-1, fmt::format("Group \"{}\" has no moduleMatches definition", group->name));
|
||||
CancelParsingPatches();
|
||||
|
|
@ -347,6 +347,12 @@ bool GraphicPack2::ParseCemuPatchesTxtInternal(MemStreamReader& patchesStream)
|
|||
// read the checksums
|
||||
while (true)
|
||||
{
|
||||
if (parser.matchWordI("rpx"))
|
||||
{
|
||||
currentGroup->m_isRpxOnlyTarget = true;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32 checksum = 0;
|
||||
if (parser.parseU32(checksum) == false)
|
||||
{
|
||||
|
|
@ -425,7 +431,32 @@ bool GraphicPack2::ParseCemuPatchesTxtInternal(MemStreamReader& patchesStream)
|
|||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
else if (parser.matchWordI(".callback"))
|
||||
{
|
||||
if (parser.matchWordI("entry"))
|
||||
{
|
||||
const char* symbolStr;
|
||||
sint32 symbolLen;
|
||||
if (parser.parseSymbolName(symbolStr, symbolLen))
|
||||
{
|
||||
currentGroup->list_callbacks.push_back(std::make_pair(std::string(symbolStr, static_cast<size_t>(symbolLen)), GPCallbackType::Entry));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPatchesSyntaxError(lineNumber, "'.callback' must reference a symbol after the type");
|
||||
CancelParsingPatches();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPatchesSyntaxError(lineNumber, "Unrecognized type for '.callback'");
|
||||
CancelParsingPatches();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// next we attempt to parse symbol assignment
|
||||
// symbols can be labels or variables. The type is determined by what comes after the symbol name
|
||||
// <symbolName> = <expression> defines a variable
|
||||
|
|
|
|||
|
|
@ -208,7 +208,24 @@ void debugger_handleSingleStepException(uint64 dr6)
|
|||
if (catchBP)
|
||||
{
|
||||
PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance();
|
||||
debugger_createCodeBreakpoint(hCPU->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT);
|
||||
if (debuggerState.logOnlyMemoryBreakpoints)
|
||||
{
|
||||
float memValueF = memory_readFloat(debuggerState.activeMemoryBreakpoint->address);
|
||||
uint32 memValue = memory_readU32(debuggerState.activeMemoryBreakpoint->address);
|
||||
cemuLog_log(LogType::Force, "[Debugger] 0x{:08X} was read/written! New Value: 0x{:08X} (float {}) IP: {:08X} LR: {:08X}",
|
||||
debuggerState.activeMemoryBreakpoint->address,
|
||||
memValue,
|
||||
memValueF,
|
||||
hCPU->instructionPointer,
|
||||
hCPU->spr.LR
|
||||
);
|
||||
if (cemuLog_advancedPPCLoggingEnabled())
|
||||
DebugLogStackTrace(coreinit::OSGetCurrentThread(), hCPU->gpr[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
debugger_createCodeBreakpoint(hCPU->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -543,6 +560,12 @@ void debugger_createPPCStateSnapshot(PPCInterpreter_t* hCPU)
|
|||
|
||||
void debugger_enterTW(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// Currently, we don't support multiple threads inside the debugger. Spin loop a thread if we already paused for another breakpoint hit.
|
||||
while (debuggerState.debugSession.isTrapped)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
// handle logging points
|
||||
DebuggerBreakpoint* bp = debugger_getFirstBP(hCPU->instructionPointer);
|
||||
bool shouldBreak = debuggerBPChain_hasType(bp, DEBUGGER_BP_T_NORMAL) || debuggerBPChain_hasType(bp, DEBUGGER_BP_T_ONE_SHOT);
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ struct PPCSnapshot
|
|||
typedef struct
|
||||
{
|
||||
bool breakOnEntry;
|
||||
bool logOnlyMemoryBreakpoints;
|
||||
// breakpoints
|
||||
std::vector<DebuggerBreakpoint*> breakpoints;
|
||||
std::vector<DebuggerPatch*> patches;
|
||||
|
|
|
|||
|
|
@ -290,7 +290,6 @@ public:
|
|||
{
|
||||
if (m_hasCacheAlloc)
|
||||
{
|
||||
cemu_assert_debug(isInUse() == false);
|
||||
g_gpuBufferHeap->freeOffset(m_cacheOffset);
|
||||
m_hasCacheAlloc = false;
|
||||
}
|
||||
|
|
@ -836,6 +835,8 @@ public:
|
|||
continue;
|
||||
}
|
||||
// delete range
|
||||
if (node->m_hasCacheAlloc)
|
||||
cemu_assert_debug(!node->isInUse());
|
||||
node->ReleaseCacheMemoryImmediately();
|
||||
LatteBufferCache_removeSingleNodeFromTree(node);
|
||||
delete node;
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ struct LatteDecompilerShader
|
|||
float ufCurrentValueFragCoordScale[2];
|
||||
sint32 loc_verticesPerInstance;
|
||||
sint32 loc_streamoutBufferBase[LATTE_NUM_STREAMOUT_BUFFER];
|
||||
sint32 uniformRangeSize; // entire size of uniform variable block
|
||||
uint32 uniformRangeSize; // entire size of uniform variable block
|
||||
}uniform{ 0 };
|
||||
// fast access
|
||||
struct _RemappedUniformBufferGroup
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace LatteDecompiler
|
|||
}
|
||||
}
|
||||
|
||||
sint32 uniformCurrentOffset = 0;
|
||||
uint32 uniformCurrentOffset = 0;
|
||||
auto shader = decompilerContext->shader;
|
||||
auto shaderType = decompilerContext->shader->shaderType;
|
||||
auto shaderSrc = decompilerContext->shaderSource;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace LatteDecompiler
|
|||
|
||||
src->add("struct SupportBuffer {" _CRLF);
|
||||
|
||||
sint32 uniformCurrentOffset = 0;
|
||||
uint32 uniformCurrentOffset = 0;
|
||||
auto shader = decompilerContext->shader;
|
||||
auto shaderType = decompilerContext->shader->shaderType;
|
||||
if (decompilerContext->shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_REMAPPED)
|
||||
|
|
|
|||
|
|
@ -115,11 +115,7 @@ struct LatteDecompilerCFInstruction
|
|||
cemu_assert_debug(!(instructionsALU.size() != 0 && instructionsTEX.size() != 0)); // make sure we haven't accidentally added the wrong instruction type
|
||||
}
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
LatteDecompilerCFInstruction(LatteDecompilerCFInstruction& mE) = default;
|
||||
#else
|
||||
LatteDecompilerCFInstruction(const LatteDecompilerCFInstruction& mE) = default;
|
||||
#endif
|
||||
LatteDecompilerCFInstruction(LatteDecompilerCFInstruction&& mE) = default;
|
||||
|
||||
LatteDecompilerCFInstruction& operator=(LatteDecompilerCFInstruction&& mE) = default;
|
||||
|
|
|
|||
|
|
@ -231,32 +231,6 @@ RendererShaderMtl::~RendererShaderMtl()
|
|||
m_function->release();
|
||||
}
|
||||
|
||||
sint32 RendererShaderMtl::GetUniformLocation(const char* name)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RendererShaderMtl::SetUniform1i(sint32 location, sint32 value)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
}
|
||||
|
||||
void RendererShaderMtl::SetUniform1f(sint32 location, float value)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
}
|
||||
|
||||
void RendererShaderMtl::SetUniform2fv(sint32 location, void* data, sint32 count)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
}
|
||||
|
||||
void RendererShaderMtl::SetUniform4iv(sint32 location, void* data, sint32 count)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
}
|
||||
|
||||
void RendererShaderMtl::PreponeCompilation(bool isRenderThread)
|
||||
{
|
||||
shaderMtlThreadPool.s_compilationQueueMutex.lock();
|
||||
|
|
|
|||
|
|
@ -36,12 +36,6 @@ public:
|
|||
return m_function;
|
||||
}
|
||||
|
||||
sint32 GetUniformLocation(const char* name) override;
|
||||
void SetUniform1i(sint32 location, sint32 value) override;
|
||||
void SetUniform1f(sint32 location, float value) override;
|
||||
void SetUniform2fv(sint32 location, void* data, sint32 count) override;
|
||||
void SetUniform4iv(sint32 location, void* data, sint32 count) override;
|
||||
|
||||
void PreponeCompilation(bool isRenderThread) override;
|
||||
bool IsCompiled() override;
|
||||
bool WaitForCompiled() override;
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ OpenGLRenderer::~OpenGLRenderer()
|
|||
{
|
||||
if(m_pipeline != 0)
|
||||
glDeleteProgramPipelines(1, &m_pipeline);
|
||||
|
||||
glDeleteBuffers(1, &m_backbufferBlit_uniformBuffer);
|
||||
}
|
||||
|
||||
OpenGLRenderer* OpenGLRenderer::GetInstance()
|
||||
|
|
@ -371,6 +373,10 @@ void OpenGLRenderer::Initialize()
|
|||
glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
||||
}
|
||||
|
||||
// create uniform buffers for backbufferblit
|
||||
glCreateBuffers(1, &m_backbufferBlit_uniformBuffer);
|
||||
glNamedBufferStorage(m_backbufferBlit_uniformBuffer, sizeof(RendererOutputShader::OutputUniformVariables), nullptr, GL_DYNAMIC_STORAGE_BIT);
|
||||
|
||||
draw_init();
|
||||
|
||||
catchOpenGLError();
|
||||
|
|
@ -603,7 +609,12 @@ void OpenGLRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
|
|||
shader_unbind(RendererShader::ShaderType::kGeometry);
|
||||
shader_bind(shader->GetVertexShader());
|
||||
shader_bind(shader->GetFragmentShader());
|
||||
shader->SetUniformParameters(*texView, {imageWidth, imageHeight}, padView);
|
||||
|
||||
// update and bind uniform buffer
|
||||
auto uniformBuffer = shader->FillUniformBlockBuffer(*texView, {imageWidth, imageHeight}, padView);
|
||||
glNamedBufferSubData(m_backbufferBlit_uniformBuffer, 0, sizeof(uniformBuffer), &uniformBuffer);
|
||||
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_backbufferBlit_uniformBuffer);
|
||||
|
||||
// set viewport
|
||||
glViewportIndexedf(0, imageX, imageY, imageWidth, imageHeight);
|
||||
|
|
|
|||
|
|
@ -209,6 +209,9 @@ private:
|
|||
sint32 activeTextureUnit = 0;
|
||||
void* m_latteBoundTextures[Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 3]{};
|
||||
|
||||
// backbuffer blit
|
||||
GLuint m_backbufferBlit_uniformBuffer;
|
||||
|
||||
// attribute stream
|
||||
GLuint glAttributeCacheAB{};
|
||||
GLuint _boundArrayBuffer{};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "RendererShaderGL.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteShader.h"
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ void OpenGLRenderer::uniformData_update()
|
|||
if (!shader)
|
||||
continue;
|
||||
|
||||
auto hostShader = shader->shader;
|
||||
auto hostShader = (RendererShaderGL*)shader->shader;
|
||||
|
||||
if (shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_REMAPPED)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ public:
|
|||
GLuint GetProgram() const { cemu_assert_debug(m_isCompiled); return m_program; }
|
||||
GLuint GetShaderObject() const { cemu_assert_debug(m_isCompiled); return m_shader_object; }
|
||||
|
||||
sint32 GetUniformLocation(const char* name) override;
|
||||
sint32 GetUniformLocation(const char* name);
|
||||
|
||||
void SetUniform1i(sint32 location, sint32 value) override;
|
||||
void SetUniform1f(sint32 location, float value) override;
|
||||
void SetUniform2fv(sint32 location, void* data, sint32 count) override;
|
||||
void SetUniform4iv(sint32 location, void* data, sint32 count) override;
|
||||
void SetUniform1i(sint32 location, sint32 value);
|
||||
void SetUniform1f(sint32 location, float value);
|
||||
void SetUniform2fv(sint32 location, void* data, sint32 count);
|
||||
void SetUniform4iv(sint32 location, void* data, sint32 count);
|
||||
|
||||
static void ShaderCacheLoading_begin(uint64 cacheTitleId);
|
||||
static void ShaderCacheLoading_end();
|
||||
|
|
|
|||
|
|
@ -263,69 +263,24 @@ RendererOutputShader::RendererOutputShader(const std::string& vertex_source, con
|
|||
if(!m_fragment_shader->WaitForCompiled())
|
||||
throw std::exception();
|
||||
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
m_uniformLocations[0].m_loc_textureSrcResolution = m_vertex_shader->GetUniformLocation("textureSrcResolution");
|
||||
m_uniformLocations[0].m_loc_nativeResolution = m_vertex_shader->GetUniformLocation("nativeResolution");
|
||||
m_uniformLocations[0].m_loc_outputResolution = m_vertex_shader->GetUniformLocation("outputResolution");
|
||||
m_uniformLocations[0].m_loc_applySRGBEncoding = m_vertex_shader->GetUniformLocation("applySRGBEncoding");
|
||||
m_uniformLocations[0].m_loc_targetGamma = m_fragment_shader->GetUniformLocation("targetGamma");
|
||||
m_uniformLocations[0].m_loc_displayGamma = m_fragment_shader->GetUniformLocation("displayGamma");
|
||||
|
||||
m_uniformLocations[1].m_loc_textureSrcResolution = m_fragment_shader->GetUniformLocation("textureSrcResolution");
|
||||
m_uniformLocations[1].m_loc_nativeResolution = m_fragment_shader->GetUniformLocation("nativeResolution");
|
||||
m_uniformLocations[1].m_loc_outputResolution = m_fragment_shader->GetUniformLocation("outputResolution");
|
||||
m_uniformLocations[1].m_loc_applySRGBEncoding = m_fragment_shader->GetUniformLocation("applySRGBEncoding");
|
||||
m_uniformLocations[1].m_loc_targetGamma = m_fragment_shader->GetUniformLocation("targetGamma");
|
||||
m_uniformLocations[1].m_loc_displayGamma = m_fragment_shader->GetUniformLocation("displayGamma");
|
||||
}
|
||||
}
|
||||
|
||||
void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& output_res, const bool padView) const
|
||||
RendererOutputShader::OutputUniformVariables RendererOutputShader::FillUniformBlockBuffer(const LatteTextureView& texture_view, const Vector2i& output_res, const bool padView) const
|
||||
{
|
||||
OutputUniformVariables vars;
|
||||
|
||||
sint32 effectiveWidth, effectiveHeight;
|
||||
texture_view.baseTexture->GetEffectiveSize(effectiveWidth, effectiveHeight, 0);
|
||||
auto setUniforms = [&](RendererShader* shader, const UniformLocations& locations){
|
||||
float res[2];
|
||||
if (locations.m_loc_textureSrcResolution != -1)
|
||||
{
|
||||
res[0] = (float)effectiveWidth;
|
||||
res[1] = (float)effectiveHeight;
|
||||
shader->SetUniform2fv(locations.m_loc_textureSrcResolution, res, 1);
|
||||
}
|
||||
vars.textureSrcResolution = {(float)effectiveWidth, (float)effectiveHeight};
|
||||
|
||||
if (locations.m_loc_nativeResolution != -1)
|
||||
{
|
||||
res[0] = (float)texture_view.baseTexture->width;
|
||||
res[1] = (float)texture_view.baseTexture->height;
|
||||
shader->SetUniform2fv(locations.m_loc_nativeResolution, res, 1);
|
||||
}
|
||||
vars.nativeResolution = {(float)texture_view.baseTexture->width, (float)texture_view.baseTexture->height};
|
||||
vars.outputResolution = output_res;
|
||||
|
||||
if (locations.m_loc_outputResolution != -1)
|
||||
{
|
||||
res[0] = (float)output_res.x;
|
||||
res[1] = (float)output_res.y;
|
||||
shader->SetUniform2fv(locations.m_loc_outputResolution, res, 1);
|
||||
}
|
||||
vars.applySRGBEncoding = padView ? LatteGPUState.drcBufferUsesSRGB : LatteGPUState.tvBufferUsesSRGB;
|
||||
vars.targetGamma = padView ? ActiveSettings::GetDRCGamma() : ActiveSettings::GetTVGamma();
|
||||
vars.displayGamma = GetConfig().userDisplayGamma;
|
||||
|
||||
if (locations.m_loc_applySRGBEncoding != -1)
|
||||
{
|
||||
shader->SetUniform1i(locations.m_loc_applySRGBEncoding, padView ? LatteGPUState.drcBufferUsesSRGB : LatteGPUState.tvBufferUsesSRGB);
|
||||
}
|
||||
|
||||
if (locations.m_loc_targetGamma != -1)
|
||||
{
|
||||
shader->SetUniform1f(locations.m_loc_targetGamma, padView ? ActiveSettings::GetDRCGamma() : ActiveSettings::GetTVGamma());
|
||||
}
|
||||
|
||||
if (locations.m_loc_displayGamma != -1)
|
||||
{
|
||||
shader->SetUniform1f(locations.m_loc_displayGamma, GetConfig().userDisplayGamma);
|
||||
}
|
||||
|
||||
};
|
||||
setUniforms(m_vertex_shader.get(), m_uniformLocations[0]);
|
||||
setUniforms(m_fragment_shader.get(), m_uniformLocations[1]);
|
||||
return vars;
|
||||
}
|
||||
|
||||
RendererOutputShader* RendererOutputShader::s_copy_shader;
|
||||
|
|
@ -478,27 +433,23 @@ vertex VertexOut main0(ushort vid [[vertex_id]]) {
|
|||
std::string RendererOutputShader::PrependFragmentPreamble(const std::string& shaderSrc)
|
||||
{
|
||||
return R"(#version 430
|
||||
layout(location = 0) smooth in vec2 passUV;
|
||||
layout(binding = 0) uniform sampler2D textureSrc;
|
||||
layout(location = 0) out vec4 colorOut0;
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(push_constant) uniform pc {
|
||||
vec2 textureSrcResolution;
|
||||
vec2 nativeResolution;
|
||||
vec2 outputResolution;
|
||||
bool applySRGBEncoding; // true = app requested sRGB encoding
|
||||
float targetGamma;
|
||||
float displayGamma;
|
||||
};
|
||||
layout (binding = 1, std140)
|
||||
#else
|
||||
layout (binding = 0, std140)
|
||||
#endif
|
||||
uniform parameters {
|
||||
uniform vec2 textureSrcResolution;
|
||||
uniform vec2 nativeResolution;
|
||||
uniform vec2 outputResolution;
|
||||
uniform bool applySRGBEncoding;
|
||||
uniform float targetGamma;
|
||||
uniform float displayGamma;
|
||||
#endif
|
||||
|
||||
layout(location = 0) smooth in vec2 passUV;
|
||||
layout(binding = 0) uniform sampler2D textureSrc;
|
||||
layout(location = 0) out vec4 colorOut0;
|
||||
};
|
||||
|
||||
float sRGBEncode(float linear)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,15 @@
|
|||
class RendererOutputShader
|
||||
{
|
||||
public:
|
||||
struct OutputUniformVariables
|
||||
{
|
||||
Vector2f textureSrcResolution;
|
||||
Vector2f nativeResolution;
|
||||
Vector2f outputResolution;
|
||||
uint32 applySRGBEncoding;
|
||||
float targetGamma;
|
||||
float displayGamma;
|
||||
};
|
||||
enum Shader
|
||||
{
|
||||
kCopy,
|
||||
|
|
@ -17,7 +26,7 @@ public:
|
|||
RendererOutputShader(const std::string& vertex_source, const std::string& fragment_source);
|
||||
virtual ~RendererOutputShader() = default;
|
||||
|
||||
void SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& output_res, const bool padView) const;
|
||||
OutputUniformVariables FillUniformBlockBuffer(const LatteTextureView& texture_view, const Vector2i& output_res, const bool padView) const;
|
||||
|
||||
RendererShader* GetVertexShader() const
|
||||
{
|
||||
|
|
@ -51,15 +60,6 @@ protected:
|
|||
std::unique_ptr<RendererShader> m_vertex_shader;
|
||||
std::unique_ptr<RendererShader> m_fragment_shader;
|
||||
|
||||
struct UniformLocations
|
||||
{
|
||||
sint32 m_loc_textureSrcResolution = -1;
|
||||
sint32 m_loc_nativeResolution = -1;
|
||||
sint32 m_loc_outputResolution = -1;
|
||||
sint32 m_loc_applySRGBEncoding = -1;
|
||||
sint32 m_loc_targetGamma = -1;
|
||||
sint32 m_loc_displayGamma = -1;
|
||||
} m_uniformLocations[2]{};
|
||||
|
||||
private:
|
||||
static const std::string s_copy_shader_source;
|
||||
|
|
|
|||
|
|
@ -18,12 +18,6 @@ public:
|
|||
virtual bool IsCompiled() = 0;
|
||||
virtual bool WaitForCompiled() = 0;
|
||||
|
||||
virtual sint32 GetUniformLocation(const char* name) = 0;
|
||||
|
||||
virtual void SetUniform1i(sint32 location, sint32 value) = 0;
|
||||
virtual void SetUniform1f(sint32 location, float value) = 0;
|
||||
virtual void SetUniform2fv(sint32 location, void* data, sint32 count) = 0;
|
||||
virtual void SetUniform4iv(sint32 location, void* data, sint32 count) = 0;
|
||||
|
||||
protected:
|
||||
// if isGameShader is true, then baseHash and auxHash are valid
|
||||
|
|
|
|||
|
|
@ -226,32 +226,6 @@ void RendererShaderVk::Shutdown()
|
|||
ShaderVkThreadPool.StopThreads();
|
||||
}
|
||||
|
||||
sint32 RendererShaderVk::GetUniformLocation(const char* name)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RendererShaderVk::SetUniform1i(sint32 location, sint32 value)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
}
|
||||
|
||||
void RendererShaderVk::SetUniform1f(sint32 location, float value)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
}
|
||||
|
||||
void RendererShaderVk::SetUniform2fv(sint32 location, void* data, sint32 count)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
}
|
||||
|
||||
void RendererShaderVk::SetUniform4iv(sint32 location, void* data, sint32 count)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
}
|
||||
|
||||
void RendererShaderVk::CreateVkShaderModule(std::span<uint32> spirvBuffer)
|
||||
{
|
||||
VkShaderModuleCreateInfo createInfo{};
|
||||
|
|
|
|||
|
|
@ -31,11 +31,6 @@ public:
|
|||
static void Init();
|
||||
static void Shutdown();
|
||||
|
||||
sint32 GetUniformLocation(const char* name) override;
|
||||
void SetUniform1i(sint32 location, sint32 value) override;
|
||||
void SetUniform1f(sint32 location, float value) override;
|
||||
void SetUniform2fv(sint32 location, void* data, sint32 count) override;
|
||||
void SetUniform4iv(sint32 location, void* data, sint32 count) override;
|
||||
VkShaderModule& GetShaderModule() { return m_shader_module; }
|
||||
|
||||
static inline FSpinlock s_dependencyLock;
|
||||
|
|
|
|||
|
|
@ -6,11 +6,10 @@
|
|||
#include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h"
|
||||
#include "Cafe/OS/libs/gx2/GX2.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
#include "util/helpers/Serializer.h"
|
||||
#include "Cafe/HW/Latte/Common/RegisterSerializer.h"
|
||||
|
||||
std::mutex s_nvidiaWorkaround;
|
||||
|
||||
/* rects emulation */
|
||||
|
||||
void rectsEmulationGS_outputSingleVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 vIdx, const LatteContextRegister& latteRegister)
|
||||
|
|
@ -923,7 +922,6 @@ bool PipelineCompiler::InitFromCurrentGPUState(PipelineInfo* pipelineInfo, const
|
|||
if (result != VK_SUCCESS)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to create pipeline layout: {}", result);
|
||||
s_nvidiaWorkaround.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -941,7 +939,7 @@ bool PipelineCompiler::InitFromCurrentGPUState(PipelineInfo* pipelineInfo, const
|
|||
|
||||
// increment ref counter for vkrObjPipeline and renderpass object to make sure they dont get released while we are using them
|
||||
m_vkrObjPipeline->incRef();
|
||||
renderPassObj->incRef();
|
||||
m_renderPassObj->incRef();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1121,3 +1119,73 @@ bool PipelineCompiler::CalcRobustBufferAccessRequirement(LatteDecompilerShader*
|
|||
}
|
||||
return requiresRobustBufferAcces;
|
||||
}
|
||||
|
||||
static std::vector<std::thread> s_compileThreads;
|
||||
static std::atomic_bool s_compileThreadsShutdownSignal{};
|
||||
static ConcurrentQueue<PipelineCompiler*> s_pipelineCompileRequests;
|
||||
|
||||
static void compilePipeline_thread(sint32 threadIndex)
|
||||
{
|
||||
SetThreadName("compilePl");
|
||||
#ifdef _WIN32
|
||||
// to avoid starving the main cpu and render threads the pipeline compile threads run at lower priority
|
||||
// except for one thread which we always run at normal priority to prevent the opposite scenario where all compile threads are starved
|
||||
if(threadIndex != 0)
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||
#endif
|
||||
while (!s_compileThreadsShutdownSignal)
|
||||
{
|
||||
PipelineCompiler* request = s_pipelineCompileRequests.pop();
|
||||
if (!request)
|
||||
continue;
|
||||
request->Compile(true, false, true);
|
||||
delete request;
|
||||
}
|
||||
}
|
||||
|
||||
void PipelineCompiler::CompileThreadPool_Start()
|
||||
{
|
||||
cemu_assert_debug(s_compileThreads.empty());
|
||||
s_compileThreadsShutdownSignal = false;
|
||||
uint32 numCompileThreads;
|
||||
|
||||
uint32 cpuCoreCount = GetPhysicalCoreCount();
|
||||
if (cpuCoreCount <= 2)
|
||||
numCompileThreads = 1;
|
||||
else
|
||||
numCompileThreads = 2 + (cpuCoreCount - 3); // 2 plus one additionally for every extra core above 3
|
||||
|
||||
numCompileThreads = std::min(numCompileThreads, 8u); // cap at 8
|
||||
|
||||
for (uint32_t i = 0; i < numCompileThreads; i++)
|
||||
{
|
||||
s_compileThreads.emplace_back(compilePipeline_thread, i);
|
||||
}
|
||||
}
|
||||
|
||||
void PipelineCompiler::CompileThreadPool_Stop()
|
||||
{
|
||||
s_compileThreadsShutdownSignal = true;
|
||||
{
|
||||
// push one empty workload for each thread
|
||||
// this way we can make sure that each waiting thread is woken up to see the shutdown signal
|
||||
for (auto& thread : s_compileThreads)
|
||||
s_pipelineCompileRequests.push(nullptr);
|
||||
}
|
||||
for (auto& thread : s_compileThreads)
|
||||
thread.join();
|
||||
while (!s_pipelineCompileRequests.empty())
|
||||
{
|
||||
PipelineCompiler* pipelineCompiler = s_pipelineCompileRequests.pop();
|
||||
if (!pipelineCompiler)
|
||||
break;
|
||||
if (pipelineCompiler)
|
||||
delete pipelineCompiler;
|
||||
}
|
||||
s_compileThreads.clear();
|
||||
}
|
||||
|
||||
void PipelineCompiler::CompileThreadPool_QueueCompilation(PipelineCompiler* v)
|
||||
{
|
||||
s_pipelineCompileRequests.push(v);
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
#pragma once
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h"
|
||||
#include "VKRBase.h"
|
||||
|
||||
class PipelineCompiler : public VKRMoveableRefCounter
|
||||
{
|
||||
|
|
@ -43,6 +45,11 @@ public:
|
|||
|
||||
static bool CalcRobustBufferAccessRequirement(LatteDecompilerShader* vertexShader, LatteDecompilerShader* pixelShader, LatteDecompilerShader* geometryShader);
|
||||
|
||||
// API for thread pool
|
||||
static void CompileThreadPool_Start();
|
||||
static void CompileThreadPool_Stop();
|
||||
static void CompileThreadPool_QueueCompilation(PipelineCompiler* v);
|
||||
|
||||
VkPipelineLayout m_pipelineLayout;
|
||||
VKRObjectRenderPass* m_renderPassObj{};
|
||||
bool m_requestRobustBufferAccess{false};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanTextureReadback.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h"
|
||||
|
||||
#include "Cafe/HW/Latte/Core/LatteBufferCache.h"
|
||||
#include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h"
|
||||
|
|
@ -653,7 +654,8 @@ VulkanRenderer::VulkanRenderer()
|
|||
m_occlusionQueries.list_availableQueryIndices.emplace_back(i);
|
||||
|
||||
// start compilation threads
|
||||
RendererShaderVk::Init();
|
||||
RendererShaderVk::Init(); // shaders
|
||||
PipelineCompiler::CompileThreadPool_Start(); // pipelines
|
||||
}
|
||||
|
||||
VulkanRenderer::~VulkanRenderer()
|
||||
|
|
@ -661,8 +663,6 @@ VulkanRenderer::~VulkanRenderer()
|
|||
SubmitCommandBuffer();
|
||||
WaitDeviceIdle();
|
||||
WaitCommandBufferFinished(GetCurrentCommandBufferId());
|
||||
// make sure compilation threads have been shut down
|
||||
RendererShaderVk::Shutdown();
|
||||
// shut down pipeline save thread
|
||||
m_destructionRequested = true;
|
||||
m_pipeline_cache_semaphore.notify();
|
||||
|
|
@ -1666,6 +1666,10 @@ void VulkanRenderer::Shutdown()
|
|||
{
|
||||
SubmitCommandBuffer();
|
||||
WaitDeviceIdle();
|
||||
// stop compilation threads
|
||||
RendererShaderVk::Shutdown();
|
||||
PipelineCompiler::CompileThreadPool_Stop();
|
||||
|
||||
DeleteFontTextures();
|
||||
Renderer::Shutdown();
|
||||
if (m_imguiRenderPass != VK_NULL_HANDLE)
|
||||
|
|
@ -2225,14 +2229,20 @@ void VulkanRenderer::CreatePipelineCache()
|
|||
|
||||
void VulkanRenderer::swapchain_createDescriptorSetLayout()
|
||||
{
|
||||
VkDescriptorSetLayoutBinding samplerLayoutBinding = {};
|
||||
VkDescriptorSetLayoutBinding bindings[2]{};
|
||||
VkDescriptorSetLayoutBinding& samplerLayoutBinding = bindings[0];
|
||||
samplerLayoutBinding.binding = 0;
|
||||
samplerLayoutBinding.descriptorCount = 1;
|
||||
samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
samplerLayoutBinding.pImmutableSamplers = nullptr;
|
||||
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
||||
VkDescriptorSetLayoutBinding bindings[] = { samplerLayoutBinding };
|
||||
VkDescriptorSetLayoutBinding& uniformBufferBinding = bindings[1];
|
||||
uniformBufferBinding.binding = 1;
|
||||
uniformBufferBinding.descriptorCount = 1;
|
||||
uniformBufferBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
uniformBufferBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
|
||||
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
layoutInfo.bindingCount = std::size(bindings);
|
||||
|
|
@ -2638,20 +2648,10 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet
|
|||
colorBlending.blendConstants[2] = 0.0f;
|
||||
colorBlending.blendConstants[3] = 0.0f;
|
||||
|
||||
VkPushConstantRange pushConstantRange{
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.offset = 0,
|
||||
.size = 3 * sizeof(float) * 2 // 3 vec2's
|
||||
+ 4 // + 1 VkBool32
|
||||
+ 4 * 2 // + 2 float
|
||||
};
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipelineLayoutInfo.setLayoutCount = 1;
|
||||
pipelineLayoutInfo.pSetLayouts = &descriptorLayout;
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
||||
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
|
||||
|
||||
VkResult result;
|
||||
if (m_pipelineLayout == VK_NULL_HANDLE)
|
||||
|
|
@ -3027,37 +3027,12 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
|
|||
vkCmdBindPipeline(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
m_state.currentPipeline = pipeline;
|
||||
|
||||
vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &descriptSet, 0, nullptr);
|
||||
auto outputUniforms = shader->FillUniformBlockBuffer(*texView, {imageWidth, imageHeight}, padView);
|
||||
|
||||
auto outputUniformOffset = uniformData_uploadUniformDataBufferGetOffset({(uint8*)&outputUniforms, sizeof(decltype(outputUniforms))});
|
||||
|
||||
// update push constants
|
||||
struct
|
||||
{
|
||||
Vector2f vecs[3];
|
||||
VkBool32 applySRGBEncoding;
|
||||
float targetGamma;
|
||||
float displayGamma;
|
||||
} pushData;
|
||||
|
||||
// textureSrcResolution
|
||||
sint32 effectiveWidth, effectiveHeight;
|
||||
texView->baseTexture->GetEffectiveSize(effectiveWidth, effectiveHeight, 0);
|
||||
pushData.vecs[0] = {(float)effectiveWidth, (float)effectiveHeight};
|
||||
|
||||
// nativeResolution
|
||||
pushData.vecs[1] = {
|
||||
(float)texViewVk->baseTexture->width,
|
||||
(float)texViewVk->baseTexture->height,
|
||||
};
|
||||
|
||||
// outputResolution
|
||||
pushData.vecs[2] = {(float)imageWidth,(float)imageHeight};
|
||||
|
||||
pushData.applySRGBEncoding = padView ? LatteGPUState.drcBufferUsesSRGB : LatteGPUState.tvBufferUsesSRGB;
|
||||
pushData.targetGamma = padView ? ActiveSettings::GetDRCGamma() : ActiveSettings::GetTVGamma();
|
||||
pushData.displayGamma = GetConfig().userDisplayGamma;
|
||||
|
||||
vkCmdPushConstants(m_state.currentCommandBuffer, m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(pushData), &pushData);
|
||||
vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &descriptSet,
|
||||
1, &outputUniformOffset);
|
||||
|
||||
vkCmdDraw(m_state.currentCommandBuffer, 6, 1, 0, 0);
|
||||
|
||||
|
|
@ -3119,16 +3094,32 @@ VkDescriptorSet VulkanRenderer::backbufferBlit_createDescriptorSet(VkDescriptorS
|
|||
imageInfo.imageView = texViewVk->GetViewRGBA()->m_textureImageView;
|
||||
imageInfo.sampler = texViewVk->GetDefaultTextureSampler(useLinearTexFilter);
|
||||
|
||||
VkWriteDescriptorSet descriptorWrites = {};
|
||||
descriptorWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
descriptorWrites.dstSet = result;
|
||||
descriptorWrites.dstBinding = 0;
|
||||
descriptorWrites.dstArrayElement = 0;
|
||||
descriptorWrites.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
descriptorWrites.descriptorCount = 1;
|
||||
descriptorWrites.pImageInfo = &imageInfo;
|
||||
VkWriteDescriptorSet descriptorWrites[2]{};
|
||||
|
||||
vkUpdateDescriptorSets(m_logicalDevice, 1, &descriptorWrites, 0, nullptr);
|
||||
VkWriteDescriptorSet& samplerWrite = descriptorWrites[0];
|
||||
samplerWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
samplerWrite.dstSet = result;
|
||||
samplerWrite.dstBinding = 0;
|
||||
samplerWrite.dstArrayElement = 0;
|
||||
samplerWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
samplerWrite.descriptorCount = 1;
|
||||
samplerWrite.pImageInfo = &imageInfo;
|
||||
|
||||
VkWriteDescriptorSet& uniformBufferWrite = descriptorWrites[1];
|
||||
uniformBufferWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
uniformBufferWrite.dstSet = result;
|
||||
uniformBufferWrite.dstBinding = 1;
|
||||
uniformBufferWrite.descriptorCount = 1;
|
||||
uniformBufferWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
|
||||
VkDescriptorBufferInfo uniformBufferInfo{};
|
||||
uniformBufferInfo.buffer = m_uniformVarBuffer;
|
||||
uniformBufferInfo.offset = 0;
|
||||
uniformBufferInfo.range = sizeof(RendererOutputShader::OutputUniformVariables);
|
||||
uniformBufferWrite.pBufferInfo = &uniformBufferInfo;
|
||||
|
||||
|
||||
vkUpdateDescriptorSets(m_logicalDevice, std::size(descriptorWrites), descriptorWrites, 0, nullptr);
|
||||
performanceMonitor.vk.numDescriptorSamplerTextures.increment();
|
||||
|
||||
m_backbufferBlitDescriptorSetCache[hash] = result;
|
||||
|
|
|
|||
|
|
@ -554,6 +554,7 @@ private:
|
|||
VkCommandBuffer getCurrentCommandBuffer() const { return m_state.currentCommandBuffer; }
|
||||
|
||||
// uniform
|
||||
uint32 uniformData_uploadUniformDataBufferGetOffset(std::span<uint8, std::dynamic_extent> data);
|
||||
void uniformData_updateUniformVars(uint32 shaderStageIndex, LatteDecompilerShader* shader);
|
||||
|
||||
// misc
|
||||
|
|
|
|||
|
|
@ -183,63 +183,6 @@ void VulkanRenderer::unregisterGraphicsPipeline(PipelineInfo* pipelineInfo)
|
|||
}
|
||||
}
|
||||
|
||||
bool g_compilePipelineThreadInit{false};
|
||||
std::mutex g_compilePipelineMutex;
|
||||
std::condition_variable g_compilePipelineCondVar;
|
||||
std::queue<PipelineCompiler*> g_compilePipelineRequests;
|
||||
|
||||
void compilePipeline_thread(sint32 threadIndex)
|
||||
{
|
||||
SetThreadName("compilePl");
|
||||
#ifdef _WIN32
|
||||
// one thread runs at normal priority while the others run at lower priority
|
||||
if(threadIndex != 0)
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||
#endif
|
||||
while (true)
|
||||
{
|
||||
std::unique_lock lock(g_compilePipelineMutex);
|
||||
while (g_compilePipelineRequests.empty())
|
||||
g_compilePipelineCondVar.wait(lock);
|
||||
|
||||
PipelineCompiler* request = g_compilePipelineRequests.front();
|
||||
|
||||
g_compilePipelineRequests.pop();
|
||||
|
||||
lock.unlock();
|
||||
|
||||
request->Compile(true, false, true);
|
||||
delete request;
|
||||
}
|
||||
}
|
||||
|
||||
void compilePipelineThread_init()
|
||||
{
|
||||
uint32 numCompileThreads;
|
||||
|
||||
uint32 cpuCoreCount = GetPhysicalCoreCount();
|
||||
if (cpuCoreCount <= 2)
|
||||
numCompileThreads = 1;
|
||||
else
|
||||
numCompileThreads = 2 + (cpuCoreCount - 3); // 2 plus one additionally for every extra core above 3
|
||||
|
||||
numCompileThreads = std::min(numCompileThreads, 8u); // cap at 8
|
||||
|
||||
for (uint32_t i = 0; i < numCompileThreads; i++)
|
||||
{
|
||||
std::thread compileThread(compilePipeline_thread, i);
|
||||
compileThread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
void compilePipelineThread_queue(PipelineCompiler* v)
|
||||
{
|
||||
std::unique_lock lock(g_compilePipelineMutex);
|
||||
g_compilePipelineRequests.push(std::move(v));
|
||||
lock.unlock();
|
||||
g_compilePipelineCondVar.notify_one();
|
||||
}
|
||||
|
||||
// make a guess if a pipeline is not essential
|
||||
// non-essential means that skipping these drawcalls shouldn't lead to permanently corrupted graphics
|
||||
bool VulkanRenderer::IsAsyncPipelineAllowed(uint32 numIndices)
|
||||
|
|
@ -270,12 +213,6 @@ bool VulkanRenderer::IsAsyncPipelineAllowed(uint32 numIndices)
|
|||
// create graphics pipeline for current state
|
||||
PipelineInfo* VulkanRenderer::draw_createGraphicsPipeline(uint32 indexCount)
|
||||
{
|
||||
if (!g_compilePipelineThreadInit)
|
||||
{
|
||||
compilePipelineThread_init();
|
||||
g_compilePipelineThreadInit = true;
|
||||
}
|
||||
|
||||
const auto fetchShader = LatteSHRC_GetActiveFetchShader();
|
||||
const auto vertexShader = LatteSHRC_GetActiveVertexShader();
|
||||
const auto geometryShader = LatteSHRC_GetActiveGeometryShader();
|
||||
|
|
@ -313,7 +250,7 @@ PipelineInfo* VulkanRenderer::draw_createGraphicsPipeline(uint32 indexCount)
|
|||
if (pipelineCompiler->Compile(false, true, true) == false)
|
||||
{
|
||||
// shaders or pipeline not cached -> asynchronous compilation
|
||||
compilePipelineThread_queue(pipelineCompiler);
|
||||
PipelineCompiler::CompileThreadPool_QueueCompilation(pipelineCompiler);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -376,6 +313,68 @@ void VulkanRenderer::indexData_uploadIndexMemory(IndexAllocation& allocation)
|
|||
|
||||
float s_vkUniformData[512 * 4];
|
||||
|
||||
uint32 VulkanRenderer::uniformData_uploadUniformDataBufferGetOffset(std::span<uint8> data)
|
||||
{
|
||||
const uint32 bufferAlignmentM1 = std::max(m_featureControl.limits.minUniformBufferOffsetAlignment, m_featureControl.limits.nonCoherentAtomSize) - 1;
|
||||
const uint32 uniformSize = ((uint32)data.size() + bufferAlignmentM1) & ~bufferAlignmentM1;
|
||||
|
||||
auto waitWhileCondition = [&](std::function<bool()> condition) {
|
||||
while (condition())
|
||||
{
|
||||
if (m_commandBufferSyncIndex == m_commandBufferIndex)
|
||||
{
|
||||
if (m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] != m_uniformVarBufferReadIndex)
|
||||
{
|
||||
draw_endRenderPass();
|
||||
SubmitCommandBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
// submitting work would not change readIndex, so there's no way for conditions based on it to change
|
||||
cemuLog_log(LogType::Force, "draw call overflowed and corrupted uniform ringbuffer. expect visual corruption");
|
||||
cemu_assert_suspicious();
|
||||
break;
|
||||
}
|
||||
}
|
||||
WaitForNextFinishedCommandBuffer();
|
||||
}
|
||||
};
|
||||
|
||||
// wrap around if it doesnt fit consecutively
|
||||
if (m_uniformVarBufferWriteIndex + uniformSize > UNIFORMVAR_RINGBUFFER_SIZE)
|
||||
{
|
||||
waitWhileCondition([&]() {
|
||||
return m_uniformVarBufferReadIndex > m_uniformVarBufferWriteIndex || m_uniformVarBufferReadIndex == 0;
|
||||
});
|
||||
m_uniformVarBufferWriteIndex = 0;
|
||||
}
|
||||
|
||||
auto ringBufRemaining = [&]() {
|
||||
ssize_t ringBufferUsedBytes = (ssize_t)m_uniformVarBufferWriteIndex - m_uniformVarBufferReadIndex;
|
||||
if (ringBufferUsedBytes < 0)
|
||||
ringBufferUsedBytes += UNIFORMVAR_RINGBUFFER_SIZE;
|
||||
return UNIFORMVAR_RINGBUFFER_SIZE - 1 - ringBufferUsedBytes;
|
||||
};
|
||||
waitWhileCondition([&]() {
|
||||
return ringBufRemaining() < uniformSize;
|
||||
});
|
||||
|
||||
const uint32 uniformOffset = m_uniformVarBufferWriteIndex;
|
||||
memcpy(m_uniformVarBufferPtr + uniformOffset, data.data(), data.size());
|
||||
m_uniformVarBufferWriteIndex += uniformSize;
|
||||
// flush if not coherent
|
||||
if (!m_uniformVarBufferMemoryIsCoherent)
|
||||
{
|
||||
VkMappedMemoryRange flushedRange{};
|
||||
flushedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
||||
flushedRange.memory = m_uniformVarBufferMemory;
|
||||
flushedRange.offset = uniformOffset;
|
||||
flushedRange.size = uniformSize;
|
||||
vkFlushMappedMemoryRanges(m_logicalDevice, 1, &flushedRange);
|
||||
}
|
||||
return uniformOffset;
|
||||
}
|
||||
|
||||
void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, LatteDecompilerShader* shader)
|
||||
{
|
||||
auto GET_UNIFORM_DATA_PTR = [](size_t index) { return s_vkUniformData + (index / 4); };
|
||||
|
|
@ -453,66 +452,7 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt
|
|||
}
|
||||
}
|
||||
}
|
||||
// upload
|
||||
const uint32 bufferAlignmentM1 = std::max(m_featureControl.limits.minUniformBufferOffsetAlignment, m_featureControl.limits.nonCoherentAtomSize) - 1;
|
||||
const uint32 uniformSize = (shader->uniform.uniformRangeSize + bufferAlignmentM1) & ~bufferAlignmentM1;
|
||||
|
||||
auto waitWhileCondition = [&](std::function<bool()> condition) {
|
||||
while (condition())
|
||||
{
|
||||
if (m_commandBufferSyncIndex == m_commandBufferIndex)
|
||||
{
|
||||
if (m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] != m_uniformVarBufferReadIndex)
|
||||
{
|
||||
draw_endRenderPass();
|
||||
SubmitCommandBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
// submitting work would not change readIndex, so there's no way for conditions based on it to change
|
||||
cemuLog_log(LogType::Force, "draw call overflowed and corrupted uniform ringbuffer. expect visual corruption");
|
||||
cemu_assert_suspicious();
|
||||
break;
|
||||
}
|
||||
}
|
||||
WaitForNextFinishedCommandBuffer();
|
||||
}
|
||||
};
|
||||
|
||||
// wrap around if it doesnt fit consecutively
|
||||
if (m_uniformVarBufferWriteIndex + uniformSize > UNIFORMVAR_RINGBUFFER_SIZE)
|
||||
{
|
||||
waitWhileCondition([&]() {
|
||||
return m_uniformVarBufferReadIndex > m_uniformVarBufferWriteIndex || m_uniformVarBufferReadIndex == 0;
|
||||
});
|
||||
m_uniformVarBufferWriteIndex = 0;
|
||||
}
|
||||
|
||||
auto ringBufRemaining = [&]() {
|
||||
ssize_t ringBufferUsedBytes = (ssize_t)m_uniformVarBufferWriteIndex - m_uniformVarBufferReadIndex;
|
||||
if (ringBufferUsedBytes < 0)
|
||||
ringBufferUsedBytes += UNIFORMVAR_RINGBUFFER_SIZE;
|
||||
return UNIFORMVAR_RINGBUFFER_SIZE - 1 - ringBufferUsedBytes;
|
||||
};
|
||||
waitWhileCondition([&]() {
|
||||
return ringBufRemaining() < uniformSize;
|
||||
});
|
||||
|
||||
const uint32 uniformOffset = m_uniformVarBufferWriteIndex;
|
||||
memcpy(m_uniformVarBufferPtr + uniformOffset, s_vkUniformData, shader->uniform.uniformRangeSize);
|
||||
m_uniformVarBufferWriteIndex += uniformSize;
|
||||
// update dynamic offset
|
||||
dynamicOffsetInfo.uniformVarBufferOffset[shaderStageIndex] = uniformOffset;
|
||||
// flush if not coherent
|
||||
if (!m_uniformVarBufferMemoryIsCoherent)
|
||||
{
|
||||
VkMappedMemoryRange flushedRange{};
|
||||
flushedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
||||
flushedRange.memory = m_uniformVarBufferMemory;
|
||||
flushedRange.offset = uniformOffset;
|
||||
flushedRange.size = uniformSize;
|
||||
vkFlushMappedMemoryRanges(m_logicalDevice, 1, &flushedRange);
|
||||
}
|
||||
dynamicOffsetInfo.uniformVarBufferOffset[shaderStageIndex] = uniformData_uploadUniformDataBufferGetOffset({(uint8*)s_vkUniformData, shader->uniform.uniformRangeSize});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ bool RPLLoader_ProcessHeaders(std::string_view moduleName, uint8* rplData, uint3
|
|||
rplLoaderContext->fileInfo.tlsModuleIndex = fileInfoPtr->tlsModuleIndex;
|
||||
rplLoaderContext->fileInfo.sdataBase1 = fileInfoPtr->sdataBase1;
|
||||
rplLoaderContext->fileInfo.sdataBase2 = fileInfoPtr->sdataBase2;
|
||||
rplLoaderContext->fileInfo.flags = fileInfoPtr->flags;
|
||||
|
||||
// init section address table
|
||||
rplLoaderContext->sectionAddressTable2.resize(sectionCount);
|
||||
|
|
@ -2304,6 +2305,25 @@ void RPLLoader_CallEntrypoints()
|
|||
}
|
||||
}
|
||||
|
||||
// calls the entrypoint of coreinit and marks it as called so that RPLLoader_CallEntrypoints() wont call it again later
|
||||
void RPLLoader_CallCoreinitEntrypoint()
|
||||
{
|
||||
// for HLE modules we need to check the dependency list
|
||||
for (auto& dependency : rplDependencyList)
|
||||
{
|
||||
if (strcmp(dependency->modulename, "coreinit") != 0)
|
||||
continue;
|
||||
if (!dependency->rplHLEModule)
|
||||
continue;
|
||||
if (dependency->hleEntrypointCalled)
|
||||
continue;
|
||||
dependency->rplHLEModule->rpl_entry(dependency->coreinitHandle, coreinit::RplEntryReason::Loaded);
|
||||
dependency->hleEntrypointCalled = true;
|
||||
return;
|
||||
}
|
||||
cemu_assert_unimplemented(); // coreinit.rpl present in cafelibs? We currently do not support native coreinit and no thread context exists yet to do a PPC call
|
||||
}
|
||||
|
||||
void RPLLoader_NotifyControlPassedToApplication()
|
||||
{
|
||||
rplLoader_applicationHasMemoryControl = true;
|
||||
|
|
@ -2349,11 +2369,13 @@ uint32 RPLLoader_FindModuleOrHLEExport(uint32 moduleHandle, bool isData, const c
|
|||
|
||||
uint32 RPLLoader_GetSDA1Base()
|
||||
{
|
||||
cemu_assert_debug(rplModuleCount > 0); // this should not be called before the main executable was loaded
|
||||
return rplLoader_sdataAddr;
|
||||
}
|
||||
|
||||
uint32 RPLLoader_GetSDA2Base()
|
||||
{
|
||||
cemu_assert_debug(rplModuleCount > 0);
|
||||
return rplLoader_sdata2Addr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ void RPLLoader_SetMainModule(RPLModule* rplLoaderContext);
|
|||
uint32 RPLLoader_GetMainModuleHandle();
|
||||
|
||||
void RPLLoader_CallEntrypoints();
|
||||
void RPLLoader_CallCoreinitEntrypoint();
|
||||
void RPLLoader_NotifyControlPassedToApplication();
|
||||
|
||||
void RPLLoader_AddDependency(std::string_view name);
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ typedef struct
|
|||
/* +0x28 */ uint32be sdataBase2;
|
||||
/* +0x2C */ uint32be ukn2C;
|
||||
/* +0x30 */ uint32be ukn30;
|
||||
/* +0x34 */ uint32be ukn34;
|
||||
/* +0x34 */ uint32be flags;
|
||||
/* +0x38 */ uint32be ukn38;
|
||||
/* +0x3C */ uint32be ukn3C;
|
||||
/* +0x40 */ uint32be minimumToolkitVersion;
|
||||
|
|
@ -198,6 +198,8 @@ struct RPLModule
|
|||
|
||||
uint32 sdataBase1;
|
||||
uint32 sdataBase2;
|
||||
|
||||
uint32 flags;
|
||||
}fileInfo;
|
||||
// parsed CRC
|
||||
std::vector<uint32> crcTable;
|
||||
|
|
@ -208,6 +210,11 @@ struct RPLModule
|
|||
return 0;
|
||||
return crcTable[sectionIndex];
|
||||
}
|
||||
|
||||
bool IsRPX() const
|
||||
{
|
||||
return fileInfo.flags & 2;
|
||||
}
|
||||
|
||||
// state
|
||||
bool isLinked; // set to true if _linkModule was called on this module
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ namespace coreinit
|
|||
|
||||
// init GHS and threads
|
||||
coreinit::PrepareGHSRuntime();
|
||||
coreinit::InitializeThread();
|
||||
coreinit::MapThreadExports();
|
||||
|
||||
// reset threads
|
||||
activeThreadCount = 0;
|
||||
|
|
@ -353,13 +353,13 @@ namespace coreinit
|
|||
coreinit::InitializeLC();
|
||||
coreinit::InitializeMP();
|
||||
coreinit::InitializeTimeAndCalendar();
|
||||
coreinit::InitializeAlarm();
|
||||
coreinit::MapAlarmExports();
|
||||
coreinit::InitializeFS();
|
||||
coreinit::InitializeSystemInfo();
|
||||
coreinit::InitializeConcurrency();
|
||||
coreinit::InitializeSpinlock();
|
||||
coreinit::InitializeMessageQueue();
|
||||
coreinit::InitializeIPC();
|
||||
coreinit::MapIPCExports();
|
||||
coreinit::InitializeIPCBuf();
|
||||
coreinit::InitializeMemoryMapping();
|
||||
coreinit::InitializeCodeGen();
|
||||
|
|
@ -373,16 +373,20 @@ namespace coreinit
|
|||
coreinit::miscInit();
|
||||
osLib_addFunction("coreinit", "OSGetSharedData", coreinitExport_OSGetSharedData);
|
||||
osLib_addFunction("coreinit", "UCReadSysConfig", coreinitExport_UCReadSysConfig);
|
||||
|
||||
// async callbacks
|
||||
InitializeAsyncCallback();
|
||||
};
|
||||
|
||||
void rpl_entry(uint32 moduleHandle, coreinit::RplEntryReason reason) override
|
||||
{
|
||||
if (reason == coreinit::RplEntryReason::Loaded)
|
||||
{
|
||||
// todo
|
||||
coreinit::InitializeThread();
|
||||
coreinit::InitializeAlarm();
|
||||
coreinit::InitializeIPC();
|
||||
InitializeAsyncCallback();
|
||||
// remaining coreinit initialization happens in coreinit_start and requires a valid PPC context
|
||||
OSThread_t* initialThread = coreinit::OSGetDefaultThread(1);
|
||||
coreinit::OSSetThreadPriority(initialThread, 16);
|
||||
coreinit::OSRunThread(initialThread, PPCInterpreter_makeCallableExportDepr(coreinit_start), 0, nullptr);
|
||||
}
|
||||
else if (reason == coreinit::RplEntryReason::Unloaded)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ namespace coreinit
|
|||
}
|
||||
}
|
||||
|
||||
void InitializeAlarm()
|
||||
void MapAlarmExports()
|
||||
{
|
||||
cafeExportRegister("coreinit", OSCreateAlarm, LogType::CoreinitAlarm);
|
||||
cafeExportRegister("coreinit", OSCreateAlarmEx, LogType::CoreinitAlarm);
|
||||
|
|
@ -358,7 +358,10 @@ namespace coreinit
|
|||
cafeExportRegister("coreinit", OSSetPeriodicAlarm, LogType::CoreinitAlarm);
|
||||
cafeExportRegister("coreinit", OSSetAlarmUserData, LogType::CoreinitAlarm);
|
||||
cafeExportRegister("coreinit", OSGetAlarmUserData, LogType::CoreinitAlarm);
|
||||
}
|
||||
|
||||
void InitializeAlarm()
|
||||
{
|
||||
// init event
|
||||
OSInitEvent(g_alarmEvent.GetPtr(), OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_AUTO);
|
||||
|
||||
|
|
|
|||
|
|
@ -49,5 +49,6 @@ namespace coreinit
|
|||
|
||||
void alarm_update();
|
||||
|
||||
void MapAlarmExports();
|
||||
void InitializeAlarm();
|
||||
}
|
||||
|
|
@ -445,15 +445,8 @@ namespace coreinit
|
|||
return r;
|
||||
}
|
||||
|
||||
void InitializeIPC()
|
||||
void MapIPCExports()
|
||||
{
|
||||
for (uint32 i = 0; i < Espresso::CORE_COUNT; i++)
|
||||
{
|
||||
IPCDriver_InitForCore(i);
|
||||
IPCDriver_InitIPCThread(i);
|
||||
}
|
||||
|
||||
// register API
|
||||
cafeExportRegister("coreinit", IOS_Open, LogType::PPC_IPC);
|
||||
cafeExportRegister("coreinit", IOS_Close, LogType::PPC_IPC);
|
||||
cafeExportRegister("coreinit", IOS_Ioctl, LogType::PPC_IPC);
|
||||
|
|
@ -462,4 +455,13 @@ namespace coreinit
|
|||
cafeExportRegister("coreinit", IOS_IoctlvAsync, LogType::PPC_IPC);
|
||||
}
|
||||
|
||||
void InitializeIPC()
|
||||
{
|
||||
for (uint32 i = 0; i < Espresso::CORE_COUNT; i++)
|
||||
{
|
||||
IPCDriver_InitForCore(i);
|
||||
IPCDriver_InitIPCThread(i);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,5 +12,6 @@ namespace coreinit
|
|||
IOS_ERROR IOS_Ioctlv(IOSDevHandle devHandle, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec);
|
||||
IOS_ERROR IOS_IoctlvAsync(IOSDevHandle devHandle, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec, MEMPTR<void> asyncResultFunc, MEMPTR<void> asyncResultUserParam);
|
||||
|
||||
void MapIPCExports();
|
||||
void InitializeIPC();
|
||||
};
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#include "Cafe/OS/libs/coreinit/coreinit_MEM.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_FG.h"
|
||||
#include "Cafe/CafeSystem.h"
|
||||
#include "Cafe/GraphicPack/GraphicPack2.h"
|
||||
|
||||
extern MPTR _entryPoint;
|
||||
extern RPLModule* applicationRPX;
|
||||
|
|
@ -211,6 +212,18 @@ void coreinit_start(PPCInterpreter_t* hCPU)
|
|||
padscore::start();
|
||||
vpad::start();
|
||||
|
||||
// call entry-type callbacks in graphic packs
|
||||
for (const auto gp : GraphicPack2::GetActiveGraphicPacks())
|
||||
{
|
||||
for (const auto [callback, type] : gp->GetCallbacks())
|
||||
{
|
||||
if (type == GPCallbackType::Entry)
|
||||
{
|
||||
PPCCoreCallback(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// continue at main executable entrypoint
|
||||
hCPU->gpr[4] = memory_getVirtualOffsetFromPointer(_coreinitInfo->argv);
|
||||
hCPU->gpr[3] = _coreinitInfo->argc;
|
||||
|
|
|
|||
|
|
@ -1588,7 +1588,7 @@ namespace coreinit
|
|||
}
|
||||
}
|
||||
|
||||
void InitializeThread()
|
||||
void MapThreadExports()
|
||||
{
|
||||
cafeExportRegister("coreinit", OSCreateThreadType, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSCreateThread, LogType::CoreinitThread);
|
||||
|
|
@ -1632,16 +1632,16 @@ namespace coreinit
|
|||
// OSThreadQueue
|
||||
cafeExportRegister("coreinit", OSInitThreadQueue, LogType::CoreinitThread);
|
||||
cafeExportRegister("coreinit", OSInitThreadQueueEx, LogType::CoreinitThread);
|
||||
|
||||
OSInitThreadQueue(g_activeThreadQueue.GetPtr());
|
||||
for (sint32 i = 0; i < PPC_CORE_COUNT; i++)
|
||||
OSInitThreadQueue(g_coreRunQueue.GetPtr() + i);
|
||||
|
||||
for (sint32 i = 0; i < PPC_CORE_COUNT; i++)
|
||||
__currentCoreThread[i] = nullptr;
|
||||
|
||||
__OSInitDefaultThreads();
|
||||
__OSInitTerminatorThreads();
|
||||
|
||||
}
|
||||
|
||||
void InitializeThread()
|
||||
{
|
||||
OSInitThreadQueue(g_activeThreadQueue.GetPtr());
|
||||
for (sint32 i = 0; i < Espresso::CORE_COUNT; i++)
|
||||
OSInitThreadQueue(g_coreRunQueue.GetPtr() + i);
|
||||
for (sint32 i = 0; i < Espresso::CORE_COUNT; i++)
|
||||
__currentCoreThread[i] = nullptr;
|
||||
__OSInitDefaultThreads();
|
||||
__OSInitTerminatorThreads();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -503,7 +503,9 @@ static_assert(sizeof(OSThread_t) == 0x6A0);
|
|||
|
||||
namespace coreinit
|
||||
{
|
||||
void MapThreadExports();
|
||||
void InitializeThread();
|
||||
|
||||
void InitializeConcurrency();
|
||||
|
||||
bool __CemuIsMulticoreMode();
|
||||
|
|
|
|||
|
|
@ -596,7 +596,7 @@ namespace snd_core
|
|||
uint32 srcFilterMode = _swapEndianU16(internalShadowCopy->srcFilterMode);
|
||||
uint16 format = _swapEndianU16(internalShadowCopy->internalOffsets.format);
|
||||
|
||||
if (srcFilterMode == AX_FILTER_MODE_LINEAR || srcFilterMode == AX_FILTER_MODE_TAP)
|
||||
if (srcFilterMode == AX_FILTER_MODE_TAP)
|
||||
{
|
||||
if (format == AX_FORMAT_ADPCM)
|
||||
AX_DecodeSamplesADPCM_Tap(internalShadowCopy, output, sampleCount);
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ namespace WindowSystem
|
|||
std::atomic_int32_t restored_pad_width = -1, restored_pad_height = -1;
|
||||
|
||||
std::atomic_bool is_fullscreen;
|
||||
std::atomic_bool debugger_focused;
|
||||
|
||||
void set_keystate(uint32 keycode, bool state)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "wxgui/helpers/wxHelpers.h"
|
||||
#include "Cemu/ncrypto/ncrypto.h"
|
||||
#include "wxgui/input/HotkeySettings.h"
|
||||
#include "wxgui/debugger/DebuggerWindow2.h"
|
||||
#include <wx/language.h>
|
||||
|
||||
#if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND
|
||||
|
|
@ -434,6 +435,30 @@ int CemuApp::FilterEvent(wxEvent& event)
|
|||
g_window_info.set_keystatesup();
|
||||
}
|
||||
|
||||
// track if debugger window or its child windows are focused
|
||||
if (g_debugger_window && (event.GetEventType() == wxEVT_SET_FOCUS || event.GetEventType() == wxEVT_ACTIVATE))
|
||||
{
|
||||
wxWindow* target_window = wxDynamicCast(event.GetEventObject(), wxWindow);
|
||||
|
||||
if (target_window && event.GetEventType() == wxEVT_ACTIVATE && !((wxActivateEvent&)event).GetActive())
|
||||
target_window = nullptr;
|
||||
|
||||
if (target_window)
|
||||
{
|
||||
g_window_info.debugger_focused = false;
|
||||
wxWindow* window_it = target_window;
|
||||
while (window_it)
|
||||
{
|
||||
if (window_it == g_debugger_window) g_window_info.debugger_focused = true;
|
||||
window_it = window_it->GetParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!g_debugger_window)
|
||||
{
|
||||
g_window_info.debugger_focused = false;
|
||||
}
|
||||
|
||||
return wxApp::FilterEvent(event);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,44 +43,47 @@ void GraphicPacksWindow2::FillGraphicPackList() const
|
|||
|
||||
for(auto& p : graphic_packs)
|
||||
{
|
||||
// filter graphic packs by given title id
|
||||
if (m_filter_installed_games && !m_installed_games.empty())
|
||||
if (!p->IsUniversal())
|
||||
{
|
||||
bool found = false;
|
||||
for (uint64 titleId : p->GetTitleIds())
|
||||
{
|
||||
if (std::find(m_installed_games.cbegin(), m_installed_games.cend(), titleId) != m_installed_games.cend())
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter graphic packs by given title id
|
||||
if(has_filter)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
if (boost::icontains(p->GetVirtualPath(), m_filter))
|
||||
found = true;
|
||||
else
|
||||
// filter graphic packs by given title id
|
||||
if (m_filter_installed_games && !m_installed_games.empty())
|
||||
{
|
||||
bool found = false;
|
||||
for (uint64 titleId : p->GetTitleIds())
|
||||
{
|
||||
if (boost::icontains(fmt::format("{:x}", titleId), m_filter))
|
||||
if (std::find(m_installed_games.cbegin(), m_installed_games.cend(), titleId) != m_installed_games.cend())
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter graphic packs by given title id
|
||||
if(has_filter)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
if (boost::icontains(p->GetVirtualPath(), m_filter))
|
||||
found = true;
|
||||
else
|
||||
{
|
||||
for (uint64 titleId : p->GetTitleIds())
|
||||
{
|
||||
if (boost::icontains(fmt::format("{:x}", titleId), m_filter))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& path = p->GetVirtualPath();
|
||||
|
|
|
|||
|
|
@ -142,6 +142,10 @@ wxString wxDownloadManagerList::OnGetItemText(long item, long column) const
|
|||
wxItemAttr* wxDownloadManagerList::OnGetItemAttr(long item) const
|
||||
{
|
||||
const auto entry = GetTitleEntry(item);
|
||||
|
||||
const wxColour bgColour = GetBackgroundColour();
|
||||
const bool isDarkTheme = wxSystemSettings::GetAppearance().IsDark();
|
||||
|
||||
if (entry.has_value())
|
||||
{
|
||||
auto& entryData = entry.value();
|
||||
|
|
@ -149,26 +153,38 @@ wxItemAttr* wxDownloadManagerList::OnGetItemAttr(long item) const
|
|||
entryData.status == TitleDownloadStatus::Verifying ||
|
||||
entryData.status == TitleDownloadStatus::Installing)
|
||||
{
|
||||
const wxColour kActiveColor{ 0xFFE0E0 };
|
||||
static wxListItemAttr s_error_attr(GetTextColour(), kActiveColor, GetFont());
|
||||
return &s_error_attr;
|
||||
const wxColour kActiveColor = isDarkTheme ? wxColour(80, 40, 40) : wxColour(0xFFE0E0);
|
||||
static wxListItemAttr s_active_attr;
|
||||
s_active_attr.SetBackgroundColour(kActiveColor);
|
||||
s_active_attr.SetTextColour(GetTextColour());
|
||||
s_active_attr.SetFont(GetFont());
|
||||
return &s_active_attr;
|
||||
}
|
||||
else if (entryData.status == TitleDownloadStatus::Installed && entryData.isPackage)
|
||||
{
|
||||
const wxColour kActiveColor{ 0xE0FFE0 };
|
||||
static wxListItemAttr s_error_attr(GetTextColour(), kActiveColor, GetFont());
|
||||
return &s_error_attr;
|
||||
const wxColour kActiveColor = isDarkTheme ? wxColour(40, 80, 40) : wxColour(0xE0FFE0);
|
||||
static wxListItemAttr s_installed_attr;
|
||||
s_installed_attr.SetBackgroundColour(kActiveColor);
|
||||
s_installed_attr.SetTextColour(GetTextColour());
|
||||
s_installed_attr.SetFont(GetFont());
|
||||
return &s_installed_attr;
|
||||
}
|
||||
else if (entryData.status == TitleDownloadStatus::Error)
|
||||
{
|
||||
const wxColour kActiveColor{ 0xCCCCF2 };
|
||||
static wxListItemAttr s_error_attr(GetTextColour(), kActiveColor, GetFont());
|
||||
const wxColour kActiveColor = isDarkTheme ? wxColour(40, 40, 80) : wxColour(0xCCCCF2);
|
||||
static wxListItemAttr s_error_attr;
|
||||
s_error_attr.SetBackgroundColour(kActiveColor);
|
||||
s_error_attr.SetTextColour(GetTextColour());
|
||||
s_error_attr.SetFont(GetFont());
|
||||
return &s_error_attr;
|
||||
}
|
||||
}
|
||||
|
||||
wxColour bgColourSecondary = wxHelper::CalculateAccentColour(GetBackgroundColour());
|
||||
static wxListItemAttr s_coloured_attr(GetTextColour(), bgColourSecondary, GetFont());
|
||||
wxColour bgColourSecondary = wxHelper::CalculateAccentColour(bgColour);
|
||||
static wxListItemAttr s_coloured_attr;
|
||||
s_coloured_attr.SetBackgroundColour(bgColourSecondary);
|
||||
s_coloured_attr.SetTextColour(GetTextColour());
|
||||
s_coloured_attr.SetFont(GetFont());
|
||||
return item % 2 == 0 ? nullptr : &s_coloured_attr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
#include "wxgui/debugger/BreakpointWindow.h"
|
||||
#include "wxgui/debugger/ModuleWindow.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
|
||||
#include "Cemu/Logging/CemuLogging.h"
|
||||
|
||||
#include "resource/embedded/resources.h"
|
||||
|
||||
|
|
@ -30,6 +32,8 @@ enum
|
|||
// settings
|
||||
MENU_ID_OPTIONS_PIN_TO_MAINWINDOW,
|
||||
MENU_ID_OPTIONS_BREAK_ON_START,
|
||||
MENU_ID_OPTIONS_LOG_MEMORY_BREAKPOINTS,
|
||||
MENU_ID_OPTIONS_SWITCH_CPU_MODE,
|
||||
// window
|
||||
MENU_ID_WINDOW_REGISTERS,
|
||||
MENU_ID_WINDOW_DUMP,
|
||||
|
|
@ -75,12 +79,14 @@ wxBEGIN_EVENT_TABLE(DebuggerWindow2, wxFrame)
|
|||
EVT_MENU_RANGE(MENU_ID_WINDOW_REGISTERS, MENU_ID_WINDOW_MODULE, DebuggerWindow2::OnWindowMenu)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
|
||||
DebuggerWindow2* g_debugger_window;
|
||||
|
||||
void DebuggerConfig::Load(XMLConfigParser& parser)
|
||||
{
|
||||
pin_to_main = parser.get("PinToMainWindow", true);
|
||||
break_on_start = parser.get("break_on_start", true);
|
||||
log_memory_breakpoints = parser.get("log_memory_breakpoints", false);
|
||||
|
||||
auto window_parser = parser.get("Windows");
|
||||
show_register = window_parser.get("Registers", true);
|
||||
|
|
@ -95,7 +101,8 @@ void DebuggerConfig::Save(XMLConfigParser& parser)
|
|||
{
|
||||
parser.set("PinToMainWindow", pin_to_main);
|
||||
parser.set("break_on_start", break_on_start);
|
||||
|
||||
parser.set("log_memory_breakpoints", log_memory_breakpoints);
|
||||
|
||||
auto window_parser = parser.set("Windows");
|
||||
window_parser.set("Registers", show_register);
|
||||
window_parser.set("MemoryDump", show_dump);
|
||||
|
|
@ -285,6 +292,7 @@ DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size)
|
|||
m_config.Load();
|
||||
|
||||
debuggerState.breakOnEntry = m_config.data().break_on_start;
|
||||
debuggerState.logOnlyMemoryBreakpoints = m_config.data().log_memory_breakpoints;
|
||||
|
||||
m_main_position = parent.GetPosition();
|
||||
m_main_size = parent.GetSize();
|
||||
|
|
@ -571,6 +579,26 @@ void DebuggerWindow2::OnOptionsInput(wxCommandEvent& event)
|
|||
debuggerState.breakOnEntry = value;
|
||||
break;
|
||||
}
|
||||
case MENU_ID_OPTIONS_LOG_MEMORY_BREAKPOINTS:
|
||||
{
|
||||
const bool value = !m_config.data().log_memory_breakpoints;
|
||||
m_config.data().log_memory_breakpoints = value;
|
||||
debuggerState.logOnlyMemoryBreakpoints = value;
|
||||
break;
|
||||
}
|
||||
case MENU_ID_OPTIONS_SWITCH_CPU_MODE:
|
||||
{
|
||||
if (ppcRecompilerEnabled)
|
||||
{
|
||||
ppcRecompilerEnabled = false;
|
||||
cemuLog_log(LogType::Force, "Debugger: Switched CPU mode to interpreter");
|
||||
}
|
||||
else {
|
||||
ppcRecompilerEnabled = true;
|
||||
cemuLog_log(LogType::Force, "Debugger: Switched CPU mode to recompiler");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
@ -662,6 +690,8 @@ void DebuggerWindow2::CreateMenuBar()
|
|||
wxMenu* options_menu = new wxMenu;
|
||||
options_menu->Append(MENU_ID_OPTIONS_PIN_TO_MAINWINDOW, _("&Pin to main window"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().pin_to_main);
|
||||
options_menu->Append(MENU_ID_OPTIONS_BREAK_ON_START, _("Break on &entry point"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().break_on_start);
|
||||
options_menu->Append(MENU_ID_OPTIONS_LOG_MEMORY_BREAKPOINTS, _("Log only memory breakpoints"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().log_memory_breakpoints);
|
||||
options_menu->Append(MENU_ID_OPTIONS_SWITCH_CPU_MODE, _("Switch to &interpreter CPU mode"), wxEmptyString, wxITEM_CHECK);
|
||||
menu_bar->Append(options_menu, _("&Options"));
|
||||
|
||||
// window
|
||||
|
|
|
|||
|
|
@ -26,13 +26,16 @@ wxDECLARE_EVENT(wxEVT_NOTIFY_MODULE_LOADED, wxCommandEvent);
|
|||
wxDECLARE_EVENT(wxEVT_NOTIFY_MODULE_UNLOADED, wxCommandEvent);
|
||||
wxDECLARE_EVENT(wxEVT_NOTIFY_GRAPHIC_PACKS_MODIFIED, wxCommandEvent);
|
||||
|
||||
extern class DebuggerWindow2* g_debugger_window;
|
||||
|
||||
struct DebuggerConfig
|
||||
{
|
||||
DebuggerConfig()
|
||||
: pin_to_main(true), break_on_start(true), show_register(true), show_dump(true), show_stack(true), show_breakpoints(true), show_modules(true), show_symbols(true) {}
|
||||
|
||||
: pin_to_main(true), break_on_start(true), log_memory_breakpoints(false), show_register(true), show_dump(true), show_stack(true), show_breakpoints(true), show_modules(true), show_symbols(true) {}
|
||||
|
||||
bool pin_to_main;
|
||||
bool break_on_start;
|
||||
bool log_memory_breakpoints;
|
||||
|
||||
bool show_register;
|
||||
bool show_dump;
|
||||
|
|
@ -82,6 +85,7 @@ public:
|
|||
|
||||
bool Show(bool show = true) override;
|
||||
std::wstring GetModuleStoragePath(std::string module_name, uint32_t crc_hash) const;
|
||||
|
||||
private:
|
||||
void OnBreakpointHit(wxCommandEvent& event);
|
||||
void OnRunProgram(wxCommandEvent& event);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ std::string KeyboardController::get_button_name(uint64 button) const
|
|||
ControllerState KeyboardController::raw_state()
|
||||
{
|
||||
ControllerState result{};
|
||||
|
||||
if (WindowSystem::GetWindowInfo().debugger_focused)
|
||||
return result;
|
||||
|
||||
boost::container::small_vector<uint32, 16> pressedKeys;
|
||||
WindowSystem::GetWindowInfo().iter_keystates([&pressedKeys](const std::pair<const uint32, bool>& keyState) { if (keyState.second) pressedKeys.emplace_back(keyState.first); });
|
||||
result.buttons.SetPressedButtons(pressedKeys);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user