mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 01:29:41 +01:00
Merge pull request #21905 from hrydgard/pipeline-debug-util
OpenGL shader compile fix, add simple VK pipeline difference checker
This commit is contained in:
13 files changed
+90
-31
No files matched your search
@@ -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).
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user