namespace ThreadView { static const ImVec4 CurrentColor = HEXTOIMV4(0x000000, 1.0f); static const ImVec4 CurrentBgColor = HEXTOIMV4(0x00E676, 1.0f); struct ThreadInfo { coreinit::OSThread *thread; uint32_t id; std::string name; coreinit::OSThreadState state; int32_t coreId; uint64_t coreTimeNs; int32_t priority; int32_t basePriority; uint32_t affinity; }; bool gIsVisible = true; static std::vector<ThreadInfo> sThreadsCache; void draw() { if (!gIsVisible) { return; } ImGui::SetNextWindowSize(ImVec2(600, 300), ImGuiSetCond_FirstUseEver); if (!ImGui::Begin("Threads", &gIsVisible)) { ImGui::End(); return; } sThreadsCache.clear(); coreinit::internal::lockScheduler(); auto core0Thread = coreinit::internal::getCoreRunningThread(0); auto core1Thread = coreinit::internal::getCoreRunningThread(1); auto core2Thread = coreinit::internal::getCoreRunningThread(2); auto firstThread = coreinit::internal::getFirstActiveThread(); for (auto thread = firstThread; thread; thread = thread->activeLink.next) { ThreadInfo tinfo; tinfo.thread = thread; tinfo.id = thread->id; tinfo.name = thread->name ? thread->name.get() : ""; tinfo.state = thread->state; tinfo.priority = thread->priority; tinfo.basePriority = thread->basePriority; tinfo.affinity = thread->attr & coreinit::OSThreadAttributes::AffinityAny; if (thread == core0Thread) { tinfo.coreId = 0; } else if (thread == core1Thread) { tinfo.coreId = 1; } else if (thread == core2Thread) { tinfo.coreId = 2; } else { tinfo.coreId = -1; } tinfo.coreTimeNs = thread->coreTimeConsumedNs; if (tinfo.coreId != -1) { tinfo.coreTimeNs += coreinit::internal::getCoreThreadRunningTime(0); } sThreadsCache.push_back(tinfo); } coreinit::internal::unlockScheduler(); ImGui::Columns(8, "threadList", false); ImGui::SetColumnOffset(0, ImGui::GetWindowWidth() * 0.00f); ImGui::SetColumnOffset(1, ImGui::GetWindowWidth() * 0.05f); ImGui::SetColumnOffset(2, ImGui::GetWindowWidth() * 0.35f); ImGui::SetColumnOffset(3, ImGui::GetWindowWidth() * 0.50f); ImGui::SetColumnOffset(4, ImGui::GetWindowWidth() * 0.60f); ImGui::SetColumnOffset(5, ImGui::GetWindowWidth() * 0.70f); ImGui::SetColumnOffset(6, ImGui::GetWindowWidth() * 0.75f); ImGui::SetColumnOffset(7, ImGui::GetWindowWidth() * 0.84f); ImGui::Text("ID"); ImGui::NextColumn(); ImGui::Text("Name"); ImGui::NextColumn(); ImGui::Text("NIA"); ImGui::NextColumn(); ImGui::Text("State"); ImGui::NextColumn(); ImGui::Text("Prio"); ImGui::NextColumn(); ImGui::Text("Aff"); ImGui::NextColumn(); ImGui::Text("Core"); ImGui::NextColumn(); ImGui::Text("Core Time"); ImGui::NextColumn(); ImGui::Separator(); for (auto &thread : sThreadsCache) { // ID if (thread.thread == getActiveThread()) { // Highlight the currently active thread // TODO: Clean this up auto idStr = fmt::format("{}", thread.id); auto drawList = ImGui::GetWindowDrawList(); auto lineHeight = ImGui::GetTextLineHeight(); float glyphWidth = ImGui::CalcTextSize("FF").x - ImGui::CalcTextSize("F").x; float idWidth = glyphWidth * idStr.length(); auto rootPos = ImGui::GetCursorScreenPos(); auto idMin = ImVec2(rootPos.x - 1, rootPos.y); auto idMax = ImVec2(rootPos.x + idWidth + 2, rootPos.y + lineHeight + 1); drawList->AddRectFilled(idMin, idMax, ImColor(CurrentBgColor), 2.0f); ImGui::TextColored(CurrentColor, "%s", idStr.c_str()); } else { ImGui::Text("%d", thread.id); } ImGui::NextColumn(); // Name if (isPaused()) { auto threadName = thread.name; if (thread.name.size() == 0) { threadName = fmt::format("(Unnamed Thread {})", thread.id); } if (ImGui::Selectable(threadName.c_str())) { setActiveThread(thread.thread); } } else { ImGui::Text("%s", thread.name.c_str()); } ImGui::NextColumn(); // NIA if (isPaused()) { ImGui::Text("%08x", getThreadNia(thread.thread)); } else { ImGui::Text(" "); } ImGui::NextColumn(); // Thread State ImGui::Text("%s", coreinit::enumAsString(thread.state).c_str()); ImGui::NextColumn(); // Priority ImGui::Text("%d (%d)", thread.priority, thread.basePriority); ImGui::NextColumn(); // Affinity std::string coreAff; if (thread.affinity & coreinit::OSThreadAttributes::AffinityCPU0) { coreAff += "0"; } if (thread.affinity & coreinit::OSThreadAttributes::AffinityCPU1) { if (coreAff.size() != 0) { coreAff += "|1"; } else { coreAff += "1"; } } if (thread.affinity & coreinit::OSThreadAttributes::AffinityCPU2) { if (coreAff.size() != 0) { coreAff += "|2"; } else { coreAff += "2"; } } ImGui::Text("%s", coreAff.c_str()); ImGui::NextColumn(); // Core Id if (thread.coreId != -1) { ImGui::Text("%d", thread.coreId); } ImGui::NextColumn(); // Core Time ImGui::Text("%" PRIu64, thread.coreTimeNs / 1000); ImGui::NextColumn(); } ImGui::Columns(1); ImGui::End(); } } // namespace ThreadView
namespace debugui { static const ImVec4 CurrentColor = HEXTOIMV4(0x000000, 1.0f); static const ImVec4 CurrentBgColor = HEXTOIMV4(0x00E676, 1.0f); VoicesWindow::VoicesWindow(const std::string &name) : Window(name) { } void VoicesWindow::draw() { ImGui::SetNextWindowSize(ImVec2 { 600, 300 }, ImGuiSetCond_FirstUseEver); if (!ImGui::Begin(mName.c_str(), &mVisible)) { ImGui::End(); return; } ImGui::Columns(9, "voicesList", false); ImGui::Text("ID"); ImGui::NextColumn(); ImGui::Text("State"); ImGui::NextColumn(); ImGui::Text("Type"); ImGui::NextColumn(); ImGui::Text("Strm"); ImGui::NextColumn(); ImGui::Text("Base Addr"); ImGui::NextColumn(); ImGui::Text("Current Off"); ImGui::NextColumn(); ImGui::Text("End Off"); ImGui::NextColumn(); ImGui::Text("Loop Off"); ImGui::NextColumn(); ImGui::Text("Loop Mode"); ImGui::NextColumn(); ImGui::Separator(); auto voices = std::vector<decaf::debug::CafeVoice> {}; if (decaf::debug::sampleCafeVoices(voices)) { for (auto &voice : voices) { ImGui::Text("%d", voice.index); ImGui::NextColumn(); if (voice.state == decaf::debug::CafeVoice::Playing) { ImGui::Text("Playing"); } else { ImGui::Text("Stopped"); } ImGui::NextColumn(); if (voice.format == decaf::debug::CafeVoice::ADPCM) { ImGui::Text("ADPCM"); } else if (voice.format == decaf::debug::CafeVoice::LPCM16) { ImGui::Text("LPCM16"); } else if (voice.format == decaf::debug::CafeVoice::LPCM8) { ImGui::Text("LPCM8"); } else { ImGui::Text("Unknown"); } ImGui::NextColumn(); if (voice.type == decaf::debug::CafeVoice::Default) { ImGui::Text("Default"); } else if (voice.type == decaf::debug::CafeVoice::Streaming) { ImGui::Text("Stream"); } else { ImGui::Text("Unknown"); } ImGui::NextColumn(); ImGui::Text("%08x", voice.data); ImGui::NextColumn(); ImGui::Text("%x", voice.currentOffset); ImGui::NextColumn(); ImGui::Text("%x", voice.endOffset); ImGui::NextColumn(); ImGui::Text("%x", voice.loopOffset); ImGui::NextColumn(); ImGui::Text("%d", voice.loopingEnabled); ImGui::NextColumn(); } } ImGui::Columns(1); ImGui::End(); } } // namespace debugui