diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index a81bc8c60b..5bb95172d3 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -127,8 +127,6 @@ UI::EventReturn DevMenu::OnShaderView(UI::EventParams &e) { return UI::EVENT_DONE; } - - UI::EventReturn DevMenu::OnFreezeFrame(UI::EventParams &e) { if (PSP_CoreParameter().frozen) { PSP_CoreParameter().frozen = false; diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp new file mode 100644 index 0000000000..0d46e8a59d --- /dev/null +++ b/UI/GPUDriverTestScreen.cpp @@ -0,0 +1,178 @@ +#include "GPUDriverTestScreen.h" +#include "i18n/i18n.h" +#include "ui/view.h" + +static const std::vector fsDiscard = { + {Draw::ShaderLanguage::GLSL_ES_200, + R"(varying vec4 oColor0; + varying vec2 oTexCoord0; + uniform sampler2D Sampler0; + void main() { + vec4 color = texture2D(Sampler0, oTexCoord0) * oColor0; + if (color.a <= 0.0) + discard; + gl_FragColor = color; + })" + }, + {Draw::ShaderLanguage::GLSL_VULKAN, + R"(layout(location = 0) in vec4 oColor0; + layout(location = 1) in vec2 oTexCoord0; + layout(location = 0) out vec4 fragColor0; + layout(set = 0, binding = 1) uniform sampler2D Sampler0; + void main() { + vec4 color = texture(Sampler0, oTexCoord0) * oColor0; + if (color.a <= 0.0) + discard; + fragColor0 = color; + })" + }, +}; + +GPUDriverTestScreen::GPUDriverTestScreen() { + using namespace Draw; +} + +GPUDriverTestScreen::~GPUDriverTestScreen() { + if (discardWriteStencil_) + discardWriteStencil_->Release(); + if (drawTestStencil_) + drawTestStencil_->Release(); + if (drawTestDepth_) + drawTestDepth_->Release(); + if (discard_) + discard_->Release(); + if (samplerNearest_) + samplerNearest_->Release(); +} + +void GPUDriverTestScreen::CreateViews() { + // Don't bother with views for now. + using namespace UI; + I18NCategory *di = GetI18NCategory("Dialog"); + I18NCategory *cr = GetI18NCategory("PSPCredits"); + + auto tabs = new TabHolder(UI::ORIENT_HORIZONTAL, 30.0f); + root_ = tabs; + tabs->AddTab("Discard", new LinearLayout(UI::ORIENT_VERTICAL)); +} + +void GPUDriverTestScreen::render() { + using namespace Draw; + UIScreen::render(); + + if (!discardWriteStencil_) { + DrawContext *draw = screenManager()->getDrawContext(); + + // Create the special shader module. + + discard_ = CreateShader(draw, Draw::ShaderStage::FRAGMENT, fsDiscard); + + InputLayout *inputLayout = ui_draw2d.CreateInputLayout(draw); + BlendState *blendOff = draw->CreateBlendState({false, 0xF}); + + // Write depth, write stencil. + DepthStencilStateDesc dsDesc{}; + dsDesc.depthTestEnabled = true; + dsDesc.depthWriteEnabled = true; + dsDesc.depthCompare = Comparison::ALWAYS; + dsDesc.stencilEnabled = true; + dsDesc.front.compareMask = 0xFF; + dsDesc.front.compareOp = Comparison::ALWAYS; + dsDesc.front.passOp = StencilOp::REPLACE; + dsDesc.front.failOp = StencilOp::ZERO; + dsDesc.front.depthFailOp = StencilOp::ZERO; + dsDesc.front.reference = 0xFF; + dsDesc.front.writeMask = 0xFF; + dsDesc.back = dsDesc.front; + DepthStencilState *depthStencilWrite = draw->CreateDepthStencilState(dsDesc); + + dsDesc.depthCompare = Comparison::ALWAYS; + dsDesc.front.compareOp = Comparison::EQUAL; + DepthStencilState *stencilTestEqual = draw->CreateDepthStencilState(dsDesc); + + dsDesc.depthCompare = Comparison::LESS_EQUAL; + dsDesc.front.compareOp = Comparison::ALWAYS; + DepthStencilState *depthTestEqual = draw->CreateDepthStencilState(dsDesc); + + RasterState *rasterNoCull = draw->CreateRasterState({}); + + PipelineDesc discardDesc{ + Primitive::TRIANGLE_LIST, + { draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D), discard_ }, + inputLayout, depthStencilWrite, blendOff, rasterNoCull, &vsColBufDesc, + }; + discardWriteStencil_ = draw->CreateGraphicsPipeline(discardDesc); + + PipelineDesc testStencilDesc{ + Primitive::TRIANGLE_LIST, + { draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D), draw->GetFshaderPreset(FS_TEXTURE_COLOR_2D) }, + inputLayout, stencilTestEqual, blendOff, rasterNoCull, &vsColBufDesc, + }; + drawTestStencil_ = draw->CreateGraphicsPipeline(testStencilDesc); + + PipelineDesc testDepthDesc{ + Primitive::TRIANGLE_LIST, + {draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D), draw->GetFshaderPreset(FS_TEXTURE_COLOR_2D)}, + inputLayout, stencilTestEqual, blendOff, rasterNoCull, &vsColBufDesc, + }; + drawTestDepth_ = draw->CreateGraphicsPipeline(testDepthDesc); + + inputLayout->Release(); + blendOff->Release(); + depthStencilWrite->Release(); + stencilTestEqual->Release(); + depthTestEqual->Release(); + rasterNoCull->Release(); + + SamplerStateDesc nearestDesc{}; + samplerNearest_ = draw->CreateSamplerState(nearestDesc); + } + + UIContext &dc = *screenManager()->getUIContext(); + const Bounds &bounds = dc.GetBounds(); + + const char *testNames[] = {"Normal", "Z test", "Stencil test"}; + + const int numTests = ARRAY_SIZE(testNames); + + uint32_t textColorOK = 0xFF30FF30; + uint32_t textColorBAD = 0xFF3030FF; + uint32_t bgColorOK = 0xFF106010; + uint32_t bgColorBAD = 0xFF101060; + + // Don't want any fancy font texture stuff going on here, so use FLAG_DYNAMIC_ASCII everywhere! + + float testW = 200.f; + float padding = 20.0f; + UI::Style style = dc.theme->itemStyle; + + float y = 100; + float x = dc.GetBounds().centerX() - ((float)numTests * testW + (float)(numTests - 1) * padding) / 2.0f; + for (int i = 0; i < 3; i++) { + dc.Begin(); + dc.SetFontScale(1.0f, 1.0f); + Bounds bounds = {x - testW / 2, y + 40, testW, 70}; + dc.DrawText(testNames[i], bounds.x, y, style.fgColor, FLAG_DYNAMIC_ASCII); + + dc.FillRect(UI::Drawable(bgColorOK), bounds); + // test bounds + dc.Flush(); + + dc.BeginPipeline(discardWriteStencil_, samplerNearest_); + + dc.DrawTextRect("TEST OK", bounds, textColorBAD, ALIGN_HCENTER | ALIGN_VCENTER); + dc.Flush(); + + dc.BeginPipeline(drawTestStencil_, samplerNearest_); + dc.FillRect(UI::Drawable(textColorOK), bounds); + dc.Flush(); + + // Methodology: + // 1. Draw text in red, writing to stencil. + // 2. Use stencil test equality to make it green. + + x += testW + padding; + } + dc.SetFontScale(1.0f, 1.0f); + dc.Flush(); +} diff --git a/UI/GPUDriverTestScreen.h b/UI/GPUDriverTestScreen.h new file mode 100644 index 0000000000..3d987b97f1 --- /dev/null +++ b/UI/GPUDriverTestScreen.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "base/display.h" +#include "ui/ui_context.h" +#include "ui/view.h" +#include "ui/viewgroup.h" +#include "ui/ui.h" + +#include "Common/LogManager.h" +#include "UI/MiscScreens.h" +#include "thin3d/thin3d.h" + +class GPUDriverTestScreen : public UIDialogScreenWithBackground { +public: + GPUDriverTestScreen(); + ~GPUDriverTestScreen(); + + void CreateViews() override; + void render() override; + +private: + Draw::ShaderModule *discard_ = nullptr; + Draw::Pipeline *discardWriteStencil_ = nullptr; + Draw::Pipeline *drawTestStencil_ = nullptr; + Draw::Pipeline *drawTestDepth_ = nullptr; + Draw::SamplerState *samplerNearest_ = nullptr; +}; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 1d2e83fe68..579df2fb29 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -44,6 +44,7 @@ #include "UI/TiltAnalogSettingsScreen.h" #include "UI/TiltEventProcessor.h" #include "UI/ComboKeyMappingScreen.h" +#include "UI/GPUDriverTestScreen.h" #include "Common/KeyMap.h" #include "Common/FileUtil.h" @@ -1211,8 +1212,8 @@ UI::EventReturn GameSettingsScreen::OnPostProcShaderChange(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnDeveloperTools(UI::EventParams &e) { - screenManager()->push(new DeveloperToolsScreen()); - return UI::EVENT_DONE; +screenManager()->push(new DeveloperToolsScreen()); +return UI::EVENT_DONE; } UI::EventReturn GameSettingsScreen::OnRemoteISO(UI::EventParams &e) { @@ -1232,12 +1233,12 @@ UI::EventReturn GameSettingsScreen::OnTouchControlLayout(UI::EventParams &e) { //when the tilt event type is modified, we need to reset all tilt settings. //refer to the ResetTiltEvents() function for a detailed explanation. -UI::EventReturn GameSettingsScreen::OnTiltTypeChange(UI::EventParams &e){ +UI::EventReturn GameSettingsScreen::OnTiltTypeChange(UI::EventParams &e) { TiltEventProcessor::ResetTiltEvents(); return UI::EVENT_DONE; }; -UI::EventReturn GameSettingsScreen::OnTiltCustomize(UI::EventParams &e){ +UI::EventReturn GameSettingsScreen::OnTiltCustomize(UI::EventParams &e) { screenManager()->push(new TiltAnalogSettingsScreen()); return UI::EVENT_DONE; }; @@ -1276,7 +1277,7 @@ void DeveloperToolsScreen::CreateViews() { // iOS can now use JIT on all modes, apparently. // The bool may come in handy for future non-jit platforms though (UWP XB1?) - static const char *cpuCores[] = { "Interpreter", "Dynarec (JIT)", "IR Interpreter" }; + static const char *cpuCores[] = {"Interpreter", "Dynarec (JIT)", "IR Interpreter"}; PopupMultiChoice *core = list->Add(new PopupMultiChoice(&g_Config.iCpuCore, gr->T("CPU Core"), cpuCores, 0, ARRAY_SIZE(cpuCores), sy->GetName(), screenManager())); core->OnChoice.Handle(this, &DeveloperToolsScreen::OnJitAffectingSetting); if (!canUseJit) { @@ -1308,6 +1309,14 @@ void DeveloperToolsScreen::CreateViews() { list->Add(new ItemHeader(dev->T("Texture Replacement"))); list->Add(new CheckBox(&g_Config.bSaveNewTextures, dev->T("Save new textures"))); list->Add(new CheckBox(&g_Config.bReplaceTextures, dev->T("Replace textures"))); + + // For now, we only implement GPU driver tests for Vulkan and OpenGL. This is simply + // because the D3D drivers are generally solid enough to not need this type of investigation. + if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { + list->Add(new ItemHeader(dev->T("Hardware Tests"))); + list->Add(new Choice(dev->T("GPU Driver Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnGPUDriverTest); + } + #if !defined(MOBILE_DEVICE) Choice *createTextureIni = list->Add(new Choice(dev->T("Create/Open textures.ini file for current game"))); createTextureIni->OnClick.Handle(this, &DeveloperToolsScreen::OnOpenTexturesIniFile); @@ -1382,6 +1391,11 @@ UI::EventReturn DeveloperToolsScreen::OnLogConfig(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn DeveloperToolsScreen::OnGPUDriverTest(UI::EventParams &e) { + screenManager()->push(new GPUDriverTestScreen()); + return UI::EVENT_DONE; +} + UI::EventReturn DeveloperToolsScreen::OnJitAffectingSetting(UI::EventParams &e) { NativeMessageReceived("clear jit", ""); return UI::EVENT_DONE; diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 2fd3bcc463..915a524a20 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -158,6 +158,7 @@ private: UI::EventReturn OnLogConfig(UI::EventParams &e); UI::EventReturn OnJitAffectingSetting(UI::EventParams &e); UI::EventReturn OnRemoteDebugger(UI::EventParams &e); + UI::EventReturn OnGPUDriverTest(UI::EventParams &e); bool allowDebugger_ = false; bool canAllowDebugger_ = true; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index d4ad6b19ad..9267fe291f 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -96,6 +96,7 @@ #include "UI/BackgroundAudio.h" #include "UI/TextureUtil.h" #include "UI/DiscordIntegration.h" +#include "UI/GPUDriverTestScreen.h" #if !defined(MOBILE_DEVICE) #include "Common/KeyMap.h" diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index f0bec7d79b..935362f6b6 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -32,6 +32,7 @@ + @@ -64,6 +65,7 @@ + diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters index 0b5c2dd240..1b79119b4e 100644 --- a/UI/UI.vcxproj.filters +++ b/UI/UI.vcxproj.filters @@ -71,6 +71,9 @@ + + Screens + @@ -143,6 +146,9 @@ + + Screens + diff --git a/ext/native/gfx_es2/draw_buffer.cpp b/ext/native/gfx_es2/draw_buffer.cpp index 603d49ee8e..93b4fec587 100644 --- a/ext/native/gfx_es2/draw_buffer.cpp +++ b/ext/native/gfx_es2/draw_buffer.cpp @@ -78,6 +78,7 @@ void DrawBuffer::Shutdown() { void DrawBuffer::Begin(Draw::Pipeline *program) { pipeline_ = program; count_ = 0; + curZ_ = 0.0f; } void DrawBuffer::Flush(bool set_blend_state) { diff --git a/ext/native/gfx_es2/draw_buffer.h b/ext/native/gfx_es2/draw_buffer.h index 4b504dfcb8..788e4a125f 100644 --- a/ext/native/gfx_es2/draw_buffer.h +++ b/ext/native/gfx_es2/draw_buffer.h @@ -97,7 +97,7 @@ public: void V(float x, float y, float z, uint32_t color, float u, float v); void V(float x, float y, uint32_t color, float u, float v) { - V(x, y, 0.0f, color, u, v); + V(x, y, curZ_, color, u, v); } void Circle(float x, float y, float radius, float thickness, int segments, float startAngle, uint32_t color, float u_mul); @@ -166,6 +166,10 @@ public: alphaStack_.pop_back(); } + void SetCurZ(float curZ) { + curZ_ = curZ; + } + private: struct Vertex { float x, y, z; @@ -191,5 +195,7 @@ private: bool inited_; float fontscalex; float fontscaley; + + float curZ_ = 0.0f; }; diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index ddc70e2c7e..e8ace0d6a4 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -101,11 +101,6 @@ bool RefCountedObject::ReleaseAssertLast() { // The Vulkan ones can be re-used with modern GL later if desired, as they're just GLSL. -struct ShaderSource { - ShaderLanguage lang; - const char *src; -}; - static const std::vector fsTexCol = { {ShaderLanguage::GLSL_ES_200, "#ifdef GL_ES\n" @@ -196,6 +191,7 @@ static const std::vector vsCol = { "attribute vec3 Position;\n" "attribute vec4 Color0;\n" "varying vec4 oColor0;\n" + "uniform mat4 WorldViewProj;\n" "void main() {\n" " gl_Position = WorldViewProj * vec4(Position, 1.0);\n" @@ -317,7 +313,7 @@ const UniformBufferDesc vsTexColBufDesc{ sizeof(VsTexColUB),{ { "WorldViewProj", 0, -1, UniformType::MATRIX4X4, 0 } } }; -static ShaderModule *CreateShader(DrawContext *draw, ShaderStage stage, const std::vector &sources) { +ShaderModule *CreateShader(DrawContext *draw, ShaderStage stage, const std::vector &sources) { uint32_t supported = draw->GetSupportedShaderLanguages(); for (auto iter : sources) { if ((uint32_t)iter.lang & supported) { diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 4d77dc69fa..81eddb8f40 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -662,4 +662,13 @@ struct VsColUB { }; extern const UniformBufferDesc vsColBufDesc; +// Useful utility for specifying a shader in multiple languages. + +struct ShaderSource { + ShaderLanguage lang; + const char *src; +}; + +ShaderModule *CreateShader(DrawContext *draw, ShaderStage stage, const std::vector &sources); + } // namespace Draw diff --git a/ext/native/ui/ui.cpp b/ext/native/ui/ui.cpp index c0bfb3fad7..f8ea4f574a 100644 --- a/ext/native/ui/ui.cpp +++ b/ext/native/ui/ui.cpp @@ -15,9 +15,9 @@ DrawBuffer ui_draw2d; DrawBuffer ui_draw2d_front; -void UIBegin(Draw::Pipeline *shaderSet) { - ui_draw2d.Begin(shaderSet); - ui_draw2d_front.Begin(shaderSet); +void UIBegin(Draw::Pipeline *pipeline) { + ui_draw2d.Begin(pipeline); + ui_draw2d_front.Begin(pipeline); } void UIFlush() { diff --git a/ext/native/ui/ui_context.cpp b/ext/native/ui/ui_context.cpp index 914c849931..1c214d2820 100644 --- a/ext/native/ui/ui_context.cpp +++ b/ext/native/ui/ui_context.cpp @@ -52,6 +52,12 @@ void UIContext::BeginNoTex() { UIBegin(ui_pipeline_notex_); } +void UIContext::BeginPipeline(Draw::Pipeline *pipeline, Draw::SamplerState *samplerState) { + draw_->BindSamplerStates(0, 1, &sampler_); + draw_->BindTexture(0, uitexture_->GetTexture()); + UIBegin(pipeline); +} + void UIContext::RebindTexture() const { draw_->BindTexture(0, uitexture_->GetTexture()); } @@ -65,6 +71,11 @@ void UIContext::Flush() { } } +void UIContext::SetCurZ(float curZ) { + ui_draw2d.SetCurZ(curZ); + ui_draw2d_front.SetCurZ(curZ); +} + // TODO: Support transformed bounds using stencil instead. void UIContext::PushScissor(const Bounds &bounds) { Flush(); diff --git a/ext/native/ui/ui_context.h b/ext/native/ui/ui_context.h index 8d9d1c1f65..d48b5b1dbf 100644 --- a/ext/native/ui/ui_context.h +++ b/ext/native/ui/ui_context.h @@ -51,6 +51,7 @@ public: void Begin(); void BeginNoTex(); + void BeginPipeline(Draw::Pipeline *pipeline, Draw::SamplerState *samplerState); void Flush(); void RebindTexture() const; @@ -85,6 +86,7 @@ public: void SetBounds(const Bounds &b) { bounds_ = b; } const Bounds &GetBounds() const { return bounds_; } Draw::DrawContext *GetDrawContext() { return draw_; } + void SetCurZ(float curZ); void PushTransform(const UITransform &transform); void PopTransform();