Merge branch 'cemu-project:main' into main

This commit is contained in:
rcaridade145 2026-02-21 19:31:59 +00:00 committed by GitHub
commit 752a9f37da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 329 additions and 166 deletions

View File

@ -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();
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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{};
};

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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};

View File

@ -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)

View File

@ -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
{
@ -379,7 +316,7 @@ 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 = (data.size() + bufferAlignmentM1) & ~bufferAlignmentM1;
const uint32 uniformSize = ((uint32)data.size() + bufferAlignmentM1) & ~bufferAlignmentM1;
auto waitWhileCondition = [&](std::function<bool()> condition) {
while (condition())

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -49,5 +49,6 @@ namespace coreinit
void alarm_update();
void MapAlarmExports();
void InitializeAlarm();
}

View File

@ -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);
}
}
};

View File

@ -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();
};

View File

@ -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;

View File

@ -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();
}
}

View File

@ -503,7 +503,9 @@ static_assert(sizeof(OSThread_t) == 0x6A0);
namespace coreinit
{
void MapThreadExports();
void InitializeThread();
void InitializeConcurrency();
bool __CemuIsMulticoreMode();

View File

@ -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();

View File

@ -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;
}

View File

@ -167,18 +167,28 @@ void UnitTests()
bool isConsoleConnected = false;
void requireConsole()
{
#if BOOST_OS_WINDOWS
if (isConsoleConnected)
return;
#if BOOST_OS_WINDOWS
if (isConsoleConnected)
return;
if (AttachConsole(ATTACH_PARENT_PROCESS) != FALSE)
{
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
isConsoleConnected = true;
}
#endif
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwFileType = GetFileType(hOut);
if (dwFileType == FILE_TYPE_UNKNOWN || dwFileType == FILE_TYPE_CHAR)
{
if (AttachConsole(ATTACH_PARENT_PROCESS) != FALSE)
{
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
freopen("CONIN$", "r", stdin);
isConsoleConnected = true;
}
}
else
{
isConsoleConnected = true;
}
#endif
}
void HandlePostUpdate()