Merge pull request #21905 from hrydgard/pipeline-debug-util

OpenGL shader compile fix, add simple VK pipeline difference checker
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-07-10 12:35:55 +02:00
commit 449c53751e
13 files changed
+90 -31

No files matched your search

+1 -1
View File
@@ -35,7 +35,7 @@ enum class ShaderWriterFlags {
};
ENUM_CLASS_BITOPS(ShaderWriterFlags);
// TODO: Somehow merge with StringWriter from Parsers.h
// TODO: Somehow merge with StringWriter from StringWriter.h
class ShaderWriter {
public:
// Extensions are supported for both OpenGL ES and Vulkan (though of course, they're different).
+5 -1
View File
@@ -264,7 +264,11 @@ static ShaderModule *GenerateFShader(DrawContext *draw, FragmentShaderPreset pre
const bool rbSwizzle = preset == FS_TEXTURE_COLOR_2D_RB_SWIZZLE;
fsWriter.DeclareSamplers(g_samplers);
fsWriter.BeginFSMain(g_uniforms, texturing ? Slice(g_varyingsTex) : Slice(g_varyings));
// NOTE (first argument): We do not declare the uniforms here, as they aren't used in any of the preset shaders.
// Declaring them currently causes a precision clash with some GLSL compilers, see issue #21904.
// So if we need them in the future, that needs to be dealt with.
fsWriter.BeginFSMain(Slice<UniformDef>::empty(), texturing ? Slice(g_varyingsTex) : Slice(g_varyings));
if (texturing) {
fsWriter.C("vec4 col = ");
fsWriter.SampleTexture2D("tex", "oTexCoord0");
+5 -9
View File
@@ -165,11 +165,6 @@ struct TouchInput {
double timestamp;
};
#undef MOD_CTRL
#undef MOD_ALT
#undef MOD_SHIFT
#undef MOD_META
enum class KeyInputFlags {
DOWN = 1 << 0,
UP = 1 << 1,
@@ -178,16 +173,17 @@ enum class KeyInputFlags {
CHAR = 1 << 4, // Unicode character input. Cannot detect keyups of these so KeyInputFlags::DOWN and KeyInputFlags::UP are zero when this is set.
// NOTE: These are only available in sync input.
MOD_CTRL = 1 << 5,
MOD_SHIFT = 1 << 6,
MOD_ALT = 1 << 7,
MOD_META = 1 << 8, // Windows key, command key, etc.
ModCtrl = 1 << 5,
ModShift = 1 << 6,
ModAlt = 1 << 7,
ModMeta = 1 << 8, // Windows key, command key, etc.
};
ENUM_CLASS_BITOPS(KeyInputFlags);
struct KeyInput {
KeyInput() {}
// These utility constructors are used a lot in SDL/Qt.
KeyInput(InputDeviceID devId, InputKeyCode code, KeyInputFlags fl) : deviceId(devId), keyCode(code), flags(fl) {}
KeyInput(InputDeviceID devId, int unicode) : deviceId(devId), unicodeChar(unicode), flags(KeyInputFlags::CHAR) {}
InputDeviceID deviceId;
+2 -2
View File
@@ -362,8 +362,8 @@ bool ChoiceStrip::Key(const KeyInput &input) {
}
// Support Ctrl+Tab / Ctrl+Shift+Tab as well, as these are common shortcuts for tab switching even outside of browsers.
if (input.keyCode == NKCODE_TAB && (input.flags & KeyInputFlags::MOD_CTRL)) {
if (input.flags & KeyInputFlags::MOD_SHIFT) {
if (input.keyCode == NKCODE_TAB && (input.flags & KeyInputFlags::ModCtrl)) {
if (input.flags & KeyInputFlags::ModShift) {
if (selected_ > 0) {
SetSelection(selected_ - 1, true);
} else if (!choices_.empty()) {
+2 -2
View File
@@ -1450,7 +1450,7 @@ bool TextEdit::Key(const KeyInput &input) {
break;
}
if ((input.flags & KeyInputFlags::MOD_CTRL) || (input.flags & KeyInputFlags::MOD_META)) {
if ((input.flags & KeyInputFlags::ModCtrl) || (input.flags & KeyInputFlags::ModMeta)) {
switch (input.keyCode) {
case NKCODE_C:
// Just copy the entire text contents, until we get selection support.
@@ -1501,7 +1501,7 @@ bool TextEdit::Key(const KeyInput &input) {
// Process chars.
if (input.flags & KeyInputFlags::CHAR) {
const int unichar = input.keyCode;
if (unichar >= 0x20 && !(input.flags & KeyInputFlags::MOD_CTRL)) { // Ignore control characters.
if (unichar >= 0x20 && !(input.flags & KeyInputFlags::ModCtrl)) { // Ignore control characters.
// Insert it! (todo: do it with a string insert)
char buf[8];
buf[u8_wc_toutf8(buf, unichar)] = '\0';
+2 -3
View File
@@ -56,9 +56,8 @@ public:
std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) override;
std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override;
TextureCacheVulkan *GetTextureCache() {
return textureCacheVulkan_;
}
TextureCacheVulkan *GetTextureCache() { return textureCacheVulkan_; }
const PipelineManagerVulkan *GetPipelineManager() const { return pipelineManager_; }
protected:
void FinishDeferred() override;
+3
View File
@@ -104,6 +104,9 @@ public:
void SavePipelineCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext);
bool LoadPipelineCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext, VKRPipelineLayout *layout, int multiSampleLevel);
// For analysis only.
const DenseHashMap<VulkanPipelineKey, VulkanPipeline *> &GetPipelines() const { return pipelines_; }
private:
DenseHashMap<VulkanPipelineKey, VulkanPipeline *> pipelines_;
VkPipelineCache pipelineCache_ = VK_NULL_HANDLE;
+5 -1
View File
@@ -60,7 +60,11 @@ struct VulkanPipelineRasterStateKey {
unsigned int topology : 4; // VkPrimitiveTopology
bool operator < (const VulkanPipelineRasterStateKey &other) const {
size_t size = sizeof(VulkanPipelineRasterStateKey);
constexpr size_t size = sizeof(VulkanPipelineRasterStateKey);
return memcmp(this, &other, size) < 0;
}
bool operator == (const VulkanPipelineRasterStateKey &other) const {
constexpr size_t size = sizeof(VulkanPipelineRasterStateKey);
return memcmp(this, &other, size) == 0;
}
};
+58 -5
View File
@@ -66,6 +66,10 @@
#include "GPU/Debugger/Record.h"
#include "GPU/GPUCommon.h"
#include "GPU/GPUState.h"
// These two are for pipeline analysis, maybe should move elsewhere.
#include "GPU/Vulkan/PipelineManagerVulkan.h"
#include "GPU/Vulkan/GPU_Vulkan.h"
#include "UI/BaseScreens.h"
#include "UI/DevScreens.h"
#include "UI/MainScreen.h"
@@ -474,7 +478,7 @@ int ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout *
return count;
}
struct { DebugShaderType type; const char *name; } shaderTypes[] = {
static constexpr struct { DebugShaderType type; const char *name; } shaderTypes[] = {
{ SHADER_TYPE_VERTEX, "Vertex" },
{ SHADER_TYPE_FRAGMENT, "Fragment" },
{ SHADER_TYPE_VERTEXLOADER, "VertexLoader" },
@@ -489,6 +493,12 @@ void ShaderListScreen::CreateTabs() {
for (size_t i = 0; i < ARRAY_SIZE(shaderTypes); i++) {
int count = (int)gpu->DebugGetShaderIDs(shaderTypes[i].type).size();
AddTab(shaderTypes[i].name, StringFromFormat("%s (%d)", shaderTypes[i].name, count), [this, i](UI::LinearLayout *tabContent) {
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN && shaderTypes[i].type == SHADER_TYPE_PIPELINE) {
CollapsibleSection *pipelineAnalysisSection = tabContent->Add(new CollapsibleSection("Pipeline Analysis"));
AddPipelineAnalysis(pipelineAnalysisSection);
pipelineAnalysisSection->SetOpen(false);
}
LinearLayout *shaderList = new LinearLayoutList(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
int count = ListShaders(shaderTypes[i].type, shaderList);
tabContent->Add(shaderList);
@@ -496,6 +506,47 @@ void ShaderListScreen::CreateTabs() {
}
}
const bool IsSameExceptForShaders(const VulkanPipelineKey &a, const VulkanPipelineKey &b) {
return a.useHWTransform == b.useHWTransform && a.raster == b.raster && a.vtxFmtId == b.vtxFmtId;
}
void ShaderListScreen::AddPipelineAnalysis(UI::LinearLayout *tabContent) {
#if !PPSSPP_PLATFORM(UWP)
GPU_Vulkan *vgpu = dynamic_cast<GPU_Vulkan *>(gpu);
const PipelineManagerVulkan *pipelineManager = vgpu->GetPipelineManager();
// Collect all pipeline keys in a vector.
std::vector<VulkanPipelineKey> keys;
pipelineManager->GetPipelines().Iterate([&keys](const VulkanPipelineKey &key, VulkanPipeline *pipeline) {
keys.push_back(key);
});
using namespace UI;
// Now, see if there are two pipelines that are identical except for the shader IDs. If so, we may be able to
// merge them by making the shaders a bit more "uber".
for (size_t i = 0; i < keys.size(); i++) {
std::string toBeMerged;
for (size_t j = i + 1; j < keys.size(); j++) {
const VulkanPipelineKey &keyA = keys[i];
const VulkanPipelineKey &keyB = keys[j];
if (IsSameExceptForShaders(keyA, keyB)) {
// Found a pair of pipelines that are identical except for the shaders.
if (keyA.vid != keyB.vid) {
toBeMerged += "VShader: " + keyA.vid.Description() + " vs " + keyB.vid.Description() + "\n";
}
if (keyA.fid != keyB.fid) {
toBeMerged += "FShader: " + keyA.fid.Description() + " vs " + keyB.fid.Description() + "\n";
}
}
}
if (!toBeMerged.empty()) {
tabContent->Add(new UI::TextView(keys[i].GetDescription(SHADER_STRING_SHORT_DESC), FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT, true));
tabContent->Add(new UI::TextView(toBeMerged, FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT, true));
}
}
#endif
}
void ShaderListScreen::OnShaderClick(UI::EventParams &e) {
using namespace UI;
std::string id = e.v->Tag();
@@ -729,10 +780,10 @@ bool TouchTestScreen::key(const KeyInput &key) {
(key.flags & KeyInputFlags::UP) ? "UP " : "",
(key.flags & KeyInputFlags::DOWN) ? "DOWN " : "",
(key.flags & KeyInputFlags::CHAR) ? "CHAR " : "",
(key.flags & KeyInputFlags::MOD_CTRL) ? "CTRL " : "",
(key.flags & KeyInputFlags::MOD_SHIFT) ? "SHIFT " : "",
(key.flags & KeyInputFlags::MOD_ALT) ? "ALT " : "",
(key.flags & KeyInputFlags::MOD_META) ? "META " : "");
(key.flags & KeyInputFlags::ModCtrl) ? "CTRL " : "",
(key.flags & KeyInputFlags::ModShift) ? "SHIFT " : "",
(key.flags & KeyInputFlags::ModAlt) ? "ALT " : "",
(key.flags & KeyInputFlags::ModMeta) ? "META " : "");
keyEventLog_.push_back(buf);
UpdateLogView();
return true;
@@ -755,6 +806,8 @@ void TouchTestScreen::axis(const AxisInput &axis) {
UpdateLogView();
}
#undef DrawText
void TouchTestScreen::DrawForeground(UIContext &dc) {
Bounds bounds = GetLayoutBounds(dc);
+1 -1
View File
@@ -118,7 +118,7 @@ public:
private:
bool ForceHorizontalTabs() const override {return true; }
int ListShaders(DebugShaderType shaderType, UI::LinearLayout *view);
void AddPipelineAnalysis(UI::LinearLayout *view);
void OnShaderClick(UI::EventParams &e);
};
+1 -1
View File
@@ -516,7 +516,7 @@ void MainScreen::CreateViews() {
bool MainScreen::key(const KeyInput &key) {
if (key.flags & KeyInputFlags::DOWN) {
if (key.keyCode == NKCODE_F && (key.flags & KeyInputFlags::MOD_CTRL) && System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) {
if (key.keyCode == NKCODE_F && (key.flags & KeyInputFlags::ModCtrl) && System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) {
auto se = GetI18NCategory(I18NCat::SEARCH);
System_InputBoxGetString(GetRequesterToken(), se->T("Search term"), searchFilter_, false, [this](std::string_view value, int) {
searchFilter_ = StripSpaces(value);
+1 -1
View File
@@ -458,7 +458,7 @@ bool ViewSearch::Key(UI::ViewGroup *viewGroup, const KeyInput &input) {
} else if (input.flags & KeyInputFlags::DOWN) {
if (input.keyCode == NKCODE_DEL) {
if (!searchFilter.empty()) {
if (input.flags & KeyInputFlags::MOD_CTRL) {
if (input.flags & KeyInputFlags::ModCtrl) {
// Ctrl+Backspace deletes the last word. Delete until the last space.
size_t pos = searchFilter.find_last_of(' ');
if (pos != searchFilter.npos) {
+4 -4
View File
@@ -1521,16 +1521,16 @@ bool NativeKey(const KeyInput &key) {
KeyInputFlags modifierFlags{};
if (g_modifiersPressed & (KeyModifier::LCTRL | KeyModifier::RCTRL)) {
modifierFlags |= KeyInputFlags::MOD_CTRL;
modifierFlags |= KeyInputFlags::ModCtrl;
}
if (g_modifiersPressed & (KeyModifier::LSHIFT | KeyModifier::RSHIFT)) {
modifierFlags |= KeyInputFlags::MOD_SHIFT;
modifierFlags |= KeyInputFlags::ModShift;
}
if (g_modifiersPressed & (KeyModifier::LALT | KeyModifier::RALT)) {
modifierFlags |= KeyInputFlags::MOD_ALT;
modifierFlags |= KeyInputFlags::ModAlt;
}
if (g_modifiersPressed & (KeyModifier::LMETA | KeyModifier::RMETA)) {
modifierFlags |= KeyInputFlags::MOD_META;
modifierFlags |= KeyInputFlags::ModMeta;
}
KeyInput modKey = key;