Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
266 changes: 251 additions & 15 deletions src/Menu/AdvancedSettingsRenderer.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "AdvancedSettingsRenderer.h"

#include <algorithm>
#include <format>
#include <imgui.h>
#include <imgui_stdlib.h>
Expand Down Expand Up @@ -28,6 +29,7 @@ void AdvancedSettingsRenderer::RenderAdvancedSettings(
// Disable at boot settings
drawDisableAtBootSettings();

RenderShaderDebugSection();
RenderDeveloperSection();
}

Expand Down Expand Up @@ -125,21 +127,6 @@ void AdvancedSettingsRenderer::RenderAdvancedSection()
ImGui::Text("Clear all compiled shaders from memory. Forces recompilation of all shaders on next use.");
}

// Blocking shader controls
if (!shaderCache->blockedKey.empty()) {
auto blockingButtonString = std::format("Stop Blocking {} Shaders", shaderCache->blockedIDs.size());
if (ImGui::Button(blockingButtonString.c_str(), { -1, 0 })) {
shaderCache->DisableShaderBlocking();
}
if (auto _tt = Util::HoverTooltipWrapper()) {
ImGui::Text(
"Stop blocking Community Shaders shader. "
"Blocking is helpful when debugging shader errors in game to determine which shader has issues. "
"Blocking is enabled if in developer mode and pressing PAGEUP and PAGEDOWN. "
"Specific shader will be printed to logfile. ");
}
}

// Debug addresses section
if (ImGui::TreeNodeEx("Addresses")) {
auto Renderer = globals::game::renderer;
Expand Down Expand Up @@ -207,6 +194,255 @@ void AdvancedSettingsRenderer::RenderShaderReplacementSection()
}
}

void AdvancedSettingsRenderer::RenderShaderDebugSection()
{
auto shaderCache = globals::shaderCache;

if (!globals::state->IsDeveloperMode()) {
return;
}

// Show blocked shader status as a regular section
if (!shaderCache->blockedKey.empty()) {
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.2f, 0.1f, 0.1f, 0.8f));
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 4.0f);
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f);

if (ImGui::CollapsingHeader("Currently Blocked Shader", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::TextColored(Util::Colors::GetError(), "Shader Blocking Active");
ImGui::SameLine();
if (ImGui::SmallButton("Stop Blocking##Section")) {
shaderCache->DisableShaderBlocking();
}

ImGui::Text("Blocked: %s", shaderCache->blockedKey.c_str());

// Try to get more details from active shaders
auto activeShaders = shaderCache->GetActiveShaders();
for (const auto& shader : activeShaders) {
if (shader.key == shaderCache->blockedKey) {
ImGui::Text("Type: %s | Class: %s | Descriptor: 0x%X",
magic_enum::enum_name(shader.shaderType).data(),
magic_enum::enum_name(shader.shaderClass).data(),
shader.descriptor);

// Add copy button with full information including disk cache
ImGui::SameLine();
ImGui::PushID("copy_blocked_shader");
if (ImGui::SmallButton("Copy Info")) {
// Convert wstring to string for display
std::string diskPathStr;
diskPathStr.resize(shader.diskPath.size());
std::transform(shader.diskPath.begin(), shader.diskPath.end(), diskPathStr.begin(),
[](wchar_t c) { return static_cast<char>(c); });

std::string fullInfo = std::format("Type: {}\nClass: {}\nDescriptor: 0x{:X}\nKey: {}\nCache Path: {}",
magic_enum::enum_name(shader.shaderType).data(),
magic_enum::enum_name(shader.shaderClass).data(),
shader.descriptor,
shader.key,
diskPathStr);
ImGui::SetClipboardText(fullInfo.c_str());
}
ImGui::PopID();
if (ImGui::IsItemHovered()) {
if (auto _tt = Util::HoverTooltipWrapper()) {
ImGui::Text("Copy complete shader information including cache path to clipboard");
}
}

break;
}
}
}

ImGui::PopStyleVar(); // ChildRounding
ImGui::PopStyleVar(); // WindowBorderSize
ImGui::PopStyleColor(); // WindowBg
}

// Active shaders list
if (ImGui::CollapsingHeader("Active Shaders", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("Active Shaders (Used Recently)");
if (auto _tt = Util::HoverTooltipWrapper()) {
ImGui::Text(
"List of shaders that have been used in recent frames. "
"Use PAGEUP/PAGEDOWN to cycle through and block shaders for debugging. "
"Shaders not used for ~1 second are removed from this list.");
}

// Get fresh active shaders data for accurate count and table
auto activeShaders = shaderCache->GetActiveShaders();
ImGui::Text("Total Active: %zu", activeShaders.size());

// Calculate total draw calls for percentage calculation
uint32_t totalDrawCalls = 0;
for (const auto& shader : activeShaders) {
totalDrawCalls += shader.drawCalls;
}

// Filter controls (now handled by ShowFilteredStringTableCustom)
static char filterText[256] = "";
static int searchColumn = 0; // 0 = All Columns, 1 = Type, 2 = Class, 3 = Descriptor, 4 = Draw Calls, 5 = Key

// Create shader rows for the table utility (simplified - no filter data needed)
struct ShaderRow
{
SIE::ShaderCache::ActiveShaderInfo shader;
uint32_t totalDrawCalls;
};

std::vector<ShaderRow> shaderRows;
for (const auto& shader : activeShaders) {
shaderRows.push_back({ shader, totalDrawCalls });
}

// Build column configurations
std::vector<Util::TableColumnConfig<ShaderRow>> columns = {
{ "Type", "Shader type", [](const ShaderRow& row) {
return std::string(magic_enum::enum_name(row.shader.shaderType));
} },
{ "Class", "Shader class", [](const ShaderRow& row) {
return std::string(magic_enum::enum_name(row.shader.shaderClass));
} },
{ "Descriptor", "Shader descriptor hash", [](const ShaderRow& row) {
return std::format("0x{:X}", row.shader.descriptor);
} },
{ "Frame %", "Percentage of total draw calls in current frame", [](const ShaderRow& row) {
float percentage = Util::CalculatePercentage(static_cast<float>(row.shader.drawCalls), static_cast<float>(row.totalDrawCalls));
return Util::FormatPercent(percentage);
} },
{ "Key", "Shader key", [](const ShaderRow& row) {
return row.shader.key;
} }
};

// Row click callbacks
auto onRowLeftClick = [shaderCache](const ShaderRow& row) {
if (row.shader.key == shaderCache->blockedKey) {
// Clicking on already blocked shader - unblock it
shaderCache->DisableShaderBlocking();
} else {
// Clicking on different shader - block it
shaderCache->blockedKey = row.shader.key;
shaderCache->blockedKeyIndex = 0;
shaderCache->blockedIDs.clear();
logger::debug("Manually blocking shader: {}", row.shader.key);
}
};

auto onRowRightClick = [shaderCache](const ShaderRow& row) {
// Convert wstring to string for display
std::string diskPathStr;
diskPathStr.resize(row.shader.diskPath.size());
std::transform(row.shader.diskPath.begin(), row.shader.diskPath.end(), diskPathStr.begin(),
[](wchar_t c) { return static_cast<char>(c); });

std::string fullInfo = std::format("Type: {}\nClass: {}\nDescriptor: 0x{:X}\nKey: {}\nCache Path: {}",
magic_enum::enum_name(row.shader.shaderType).data(),
magic_enum::enum_name(row.shader.shaderClass).data(),
row.shader.descriptor,
row.shader.key,
diskPathStr);
ImGui::SetClipboardText(fullInfo.c_str());
};

auto getRowTooltip = [shaderCache](const ShaderRow& row) {
std::string clickAction = (row.shader.key == shaderCache->blockedKey) ? "Left-click to unblock this shader" : "Left-click to block this shader";

return std::format("Type: {}\nClass: {}\nDescriptor: 0x{:X}\nKey: {}\n\n{}",
magic_enum::enum_name(row.shader.shaderType).data(),
magic_enum::enum_name(row.shader.shaderClass).data(),
row.shader.descriptor,
row.shader.key,
clickAction);
};

// Define function to extract filterable fields (for TableFilterState)
auto getFilterableFields = [](const ShaderRow& row) -> std::vector<std::string> {
return {
std::string(magic_enum::enum_name(row.shader.shaderType)), // Type
std::string(magic_enum::enum_name(row.shader.shaderClass)), // Class
std::format("0x{:X}", row.shader.descriptor), // Descriptor
Util::FormatPercent(Util::CalculatePercentage(static_cast<float>(row.shader.drawCalls), static_cast<float>(row.totalDrawCalls))), // Frame %
row.shader.key // Key
};
};

// Define sorting comparators (customSorts parameter)
std::vector<std::function<bool(const ShaderRow&, const ShaderRow&, bool)>> sorters = {
// Type - string sort
[](const ShaderRow& a, const ShaderRow& b, bool ascending) {
std::string aVal = std::string(magic_enum::enum_name(a.shader.shaderType));
std::string bVal = std::string(magic_enum::enum_name(b.shader.shaderType));
return ascending ? (aVal < bVal) : (aVal > bVal);
},
// Class - string sort
[](const ShaderRow& a, const ShaderRow& b, bool ascending) {
std::string aVal = std::string(magic_enum::enum_name(a.shader.shaderClass));
std::string bVal = std::string(magic_enum::enum_name(b.shader.shaderClass));
return ascending ? (aVal < bVal) : (aVal > bVal);
},
// Descriptor - numeric sort
[](const ShaderRow& a, const ShaderRow& b, bool ascending) {
return ascending ? (a.shader.descriptor < b.shader.descriptor) : (a.shader.descriptor > b.shader.descriptor);
},
// Frame % - numeric sort
[](const ShaderRow& a, const ShaderRow& b, bool ascending) {
float aPercent = Util::CalculatePercentage(static_cast<float>(a.shader.drawCalls), static_cast<float>(a.totalDrawCalls));
float bPercent = Util::CalculatePercentage(static_cast<float>(b.shader.drawCalls), static_cast<float>(b.totalDrawCalls));
return ascending ? (aPercent < bPercent) : (aPercent > bPercent);
},
// Key - string sort
[](const ShaderRow& a, const ShaderRow& b, bool ascending) {
return ascending ? (a.shader.key < b.shader.key) : (a.shader.key > b.shader.key);
}
};

// Create filter state
Util::TableFilterState<ShaderRow> filterState(getFilterableFields);

// Initialize filter state from existing variables
filterState.filterText = std::string(filterText, filterText + strlen(filterText));
filterState.searchColumn = searchColumn;

// Define input events for row interactions
std::vector<Util::TableInputEvent<ShaderRow>> inputEvents = {
// Left-click to block/unblock shader
{ Util::TableInputEventType::MouseClick, onRowLeftClick, "", 0 },
// Right-click context menu for copying info
{ Util::TableInputEventType::ContextMenu, onRowRightClick, "Copy Info", 1 }
};

// Define function to get row text color (highlight blocked shaders)
auto getRowTextColor = [shaderCache](const ShaderRow& row) -> ImVec4 {
if (row.shader.key == shaderCache->blockedKey) {
// Use theme error color for blocked shader text
return Util::Colors::GetError();
}
return ImVec4(0, 0, 0, 0); // Default text color for normal rows
};

// Use the new interactive table
Util::ShowInteractiveTable<ShaderRow>(
"ActiveShadersTable",
columns,
shaderRows,
3, // Default sort column (Frame %)
false, // Default descending (for "hot" shaders)
sorters,
filterState,
inputEvents,
getRowTooltip,
nullptr, // No background color
getRowTextColor); // Pass the new text color function

// Update the filter text back to the char array
strncpy_s(filterText, filterState.filterText.c_str(), sizeof(filterText) - 1);
searchColumn = filterState.searchColumn;
}
}

void AdvancedSettingsRenderer::RenderDeveloperSection()
{
// Developer Mode Testing Section
Expand Down
1 change: 1 addition & 0 deletions src/Menu/AdvancedSettingsRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ class AdvancedSettingsRenderer
private:
static void RenderAdvancedSection();
static void RenderShaderReplacementSection();
static void RenderShaderDebugSection();
static void RenderDeveloperSection();
};
53 changes: 53 additions & 0 deletions src/Menu/OverlayRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Menu.h"
#include "ShaderCache.h"
#include "State.h"
#include "Util.h"

#include "Features/PerformanceOverlay.h"
#include "Features/PerformanceOverlay/ABTesting/ABTesting.h"
Expand Down Expand Up @@ -42,6 +43,7 @@ void OverlayRenderer::RenderOverlay(
InitializeImGuiFrame(menu);

RenderShaderCompilationStatus(keyIdToString);
RenderShaderBlockingStatus();
RenderFirstTimeSetupOverlay();

if (menu.IsEnabled || HomePageRenderer::ShouldShowFirstTimeSetup()) {
Expand Down Expand Up @@ -203,4 +205,55 @@ void OverlayRenderer::RenderFirstTimeSetupOverlay()
if (HomePageRenderer::ShouldShowFirstTimeSetup()) {
HomePageRenderer::RenderFirstTimeSetupDialog();
}
}

void OverlayRenderer::RenderShaderBlockingStatus()
{
auto shaderCache = globals::shaderCache;
auto state = globals::state;

if (!state->IsDeveloperMode() || shaderCache->blockedKey.empty()) {
return;
}

ImGui::SetNextWindowPos(ImVec2(ThemeManager::Constants::OVERLAY_WINDOW_POSITION, ThemeManager::Constants::OVERLAY_WINDOW_POSITION + 100));
if (!ImGui::Begin("ShaderBlockingInfo", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings)) {
ImGui::End();
return;
}

ImGui::TextColored(Util::Colors::GetError(), "Shader Blocking Active");
ImGui::Text("Blocked: %s", shaderCache->blockedKey.c_str());

// Try to get more details from active shaders
auto activeShaders = shaderCache->GetActiveShaders();

// Find the index of the blocked shader in the active list (or show N/A if not found)
size_t blockedIndex = 0;
bool foundBlocked = false;
for (size_t i = 0; i < activeShaders.size(); ++i) {
if (activeShaders[i].key == shaderCache->blockedKey) {
blockedIndex = i + 1; // 1-based indexing for display
foundBlocked = true;
break;
}
}

if (foundBlocked) {
ImGui::Text("Index: %zu/%zu", blockedIndex, activeShaders.size());
} else {
ImGui::Text("Index: N/A (%zu active)", activeShaders.size());
}

for (const auto& shader : activeShaders) {
if (shader.key == shaderCache->blockedKey) {
ImGui::Text("Type: %s | Class: %s | Descriptor: 0x%X",
magic_enum::enum_name(shader.shaderType).data(),
magic_enum::enum_name(shader.shaderClass).data(),
shader.descriptor);
break;
}
}

ImGui::End();
}
1 change: 1 addition & 0 deletions src/Menu/OverlayRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class OverlayRenderer
static void HandleFontReload(Menu& menu, float& cachedFontSize, float currentFontSize);
static void InitializeImGuiFrame(Menu& menu);
static void RenderShaderCompilationStatus(const std::function<const char*(uint32_t)>& keyIdToString);
static void RenderShaderBlockingStatus();
static void RenderFirstTimeSetupOverlay();
static void RenderFeatureOverlays();
static void HandleABTesting();
Expand Down
Loading