diff --git a/CMakeLists.txt b/CMakeLists.txt index f637fd9ce9..1778c9b734 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1855,6 +1855,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/CoreTiming.h Core/CwCheat.cpp Core/CwCheat.h + Core/FrameTiming.cpp + Core/FrameTiming.h Core/HDRemaster.cpp Core/HDRemaster.h Core/Instance.cpp diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 85bc86d880..7c9f8308a1 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -75,10 +75,6 @@ public: return (uint32_t)ShaderLanguage::HLSL_D3D11; } uint32_t GetDataFormatSupport(DataFormat fmt) const override; - PresentMode GetPresentMode() const override { - // TODO: Fix. Not yet used. - return PresentMode::FIFO; - } InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override; DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override; @@ -139,7 +135,7 @@ public: void BeginFrame(DebugFlags debugFlags) override; void EndFrame() override; - void Present(int vblanks) override; + void Present(PresentMode presentMode, int vblanks) override; int GetFrameCount() override { return frameCount_; } @@ -284,6 +280,10 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de caps_.blendMinMaxSupported = true; caps_.multiSampleLevelsMask = 1; // More could be supported with some work. + caps_.presentInstantModeChange = true; + caps_.presentMaxInterval = 4; + caps_.presentModesSupported = PresentMode::FIFO | PresentMode::IMMEDIATE; + D3D11_FEATURE_DATA_D3D11_OPTIONS options{}; HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options)); if (SUCCEEDED(result)) { @@ -433,8 +433,12 @@ void D3D11DrawContext::EndFrame() { curPipeline_ = nullptr; } -void D3D11DrawContext::Present(int vblanks) { - swapChain_->Present(1, 0); +void D3D11DrawContext::Present(PresentMode presentMode, int vblanks) { + int interval = vblanks; + if (presentMode != PresentMode::FIFO) { + interval = 0; + } + swapChain_->Present(interval, 0); curRenderTargetView_ = nullptr; curDepthStencilView_ = nullptr; frameCount_++; diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 641e10e518..3a507291d1 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -518,10 +518,6 @@ public: return (uint32_t)ShaderLanguage::HLSL_D3D9; } uint32_t GetDataFormatSupport(DataFormat fmt) const override; - PresentMode GetPresentMode() const override { - // TODO: Fix. Not yet used. - return PresentMode::FIFO; - } ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override; DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override; @@ -580,7 +576,7 @@ public: } void EndFrame() override; - void Present(int vblanks) override; + void Present(PresentMode presentMode, int vblanks) override; int GetFrameCount() override { return frameCount_; } @@ -785,6 +781,9 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID caps_.multiSampleLevelsMask = 1; // More could be supported with some work. caps_.clipPlanesSupported = caps.MaxUserClipPlanes; + caps_.presentInstantModeChange = false; + caps_.presentMaxInterval = 1; + caps_.presentModesSupported = PresentMode::FIFO; if ((caps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0 && caps.MaxAnisotropy > 1) { caps_.anisoSupported = true; @@ -970,7 +969,7 @@ void D3D9Context::EndFrame() { curPipeline_ = nullptr; } -void D3D9Context::Present(int vblanks) { +void D3D9Context::Present(PresentMode presentMode, int vblanks) { if (deviceEx_) { deviceEx_->EndScene(); deviceEx_->PresentEx(NULL, NULL, NULL, NULL, 0); diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index d0fe44e42c..08738a8105 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -322,7 +322,7 @@ class OpenGLTexture; class OpenGLContext : public DrawContext { public: - OpenGLContext(); + OpenGLContext(bool canChangeSwapInterval); ~OpenGLContext(); void SetTargetSize(int w, int h) override { @@ -347,11 +347,6 @@ public: renderManager_.SetErrorCallback(callback, userdata); } - PresentMode GetPresentMode() const override { - // TODO: Fix. Not yet used. - return PresentMode::FIFO; - } - DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override; BlendState *CreateBlendState(const BlendStateDesc &desc) override; SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override; @@ -366,7 +361,7 @@ public: void BeginFrame(DebugFlags debugFlags) override; void EndFrame() override; - void Present(int vblanks) override; + void Present(PresentMode mode, int vblanks) override; int GetFrameCount() override { return frameCount_; @@ -547,7 +542,7 @@ static bool HasIntelDualSrcBug(const int versions[4]) { } } -OpenGLContext::OpenGLContext() { +OpenGLContext::OpenGLContext(bool canChangeSwapInterval) { if (gl_extensions.IsGLES) { if (gl_extensions.OES_packed_depth_stencil || gl_extensions.OES_depth24) { caps_.preferredDepthBufferFormat = DataFormat::D24_S8; @@ -778,6 +773,16 @@ OpenGLContext::OpenGLContext() { } } + if (canChangeSwapInterval) { + caps_.presentInstantModeChange = true; + caps_.presentMaxInterval = 4; + caps_.presentModesSupported = PresentMode::FIFO | PresentMode::IMMEDIATE; + } else { + caps_.presentInstantModeChange = false; + caps_.presentModesSupported = PresentMode::FIFO; + caps_.presentMaxInterval = 1; + } + renderManager_.SetDeviceCaps(caps_); } @@ -802,7 +807,7 @@ void OpenGLContext::EndFrame() { Invalidate(InvalidationFlags::CACHED_RENDER_STATE); } -void OpenGLContext::Present(int vblanks) { +void OpenGLContext::Present(PresentMode presentMode, int vblanks) { renderManager_.Present(); frameCount_++; } @@ -1414,8 +1419,8 @@ void OpenGLContext::Clear(int mask, uint32_t colorval, float depthVal, int stenc renderManager_.Clear(colorval, depthVal, stencilVal, glMask, 0xF, 0, 0, targetWidth_, targetHeight_); } -DrawContext *T3DCreateGLContext() { - return new OpenGLContext(); +DrawContext *T3DCreateGLContext(bool canChangeSwapInterval) { + return new OpenGLContext(canChangeSwapInterval); } OpenGLInputLayout::~OpenGLInputLayout() { diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 2156d8edad..3111f9db21 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -404,7 +404,7 @@ public: } uint32_t GetDataFormatSupport(DataFormat fmt) const override; - PresentMode GetPresentMode() const override { + PresentMode GetPresentMode() const { switch (vulkan_->GetPresentMode()) { case VK_PRESENT_MODE_FIFO_KHR: return PresentMode::FIFO; case VK_PRESENT_MODE_FIFO_RELAXED_KHR: return PresentMode::FIFO; // We treat is as FIFO for now (and won't ever enable it anyway...) @@ -480,7 +480,7 @@ public: void BeginFrame(DebugFlags debugFlags) override; void EndFrame() override; - void Present(int vblanks) override; + void Present(PresentMode presentMode, int vblanks) override; void WipeQueue() override; @@ -899,6 +899,19 @@ VKContext::VKContext(VulkanContext *vulkan, bool useRenderThread) caps_.sampleRateShadingSupported = vulkan->GetDeviceFeatures().enabled.standard.sampleRateShading != 0; caps_.textureSwizzleSupported = true; + // Present mode stuff + caps_.presentMaxInterval = 1; + caps_.presentInstantModeChange = false; // TODO: Fix this with some work in VulkanContext + caps_.presentModesSupported = (PresentMode)0; + for (auto mode : vulkan->GetAvailablePresentModes()) { + switch (mode) { + case VK_PRESENT_MODE_FIFO_KHR: caps_.presentModesSupported |= PresentMode::FIFO; break; + case VK_PRESENT_MODE_IMMEDIATE_KHR: caps_.presentModesSupported |= PresentMode::IMMEDIATE; break; + case VK_PRESENT_MODE_MAILBOX_KHR: caps_.presentModesSupported |= PresentMode::MAILBOX; break; + default: break; // Ignore any other modes. + } + } + const auto &limits = vulkan->GetPhysicalDeviceProperties().properties.limits; auto deviceProps = vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDeviceIndex()).properties; @@ -1120,8 +1133,10 @@ void VKContext::EndFrame() { Invalidate(InvalidationFlags::CACHED_RENDER_STATE); } -void VKContext::Present(int vblanks) { - _dbg_assert_(vblanks == 0 || vblanks == 1); +void VKContext::Present(PresentMode presentMode, int vblanks) { + if (presentMode == PresentMode::FIFO) { + _dbg_assert_(vblanks == 0 || vblanks == 1); + } renderManager_.Present(); frameCount_++; } diff --git a/Common/GPU/thin3d.cpp b/Common/GPU/thin3d.cpp index 04a937d8eb..0cefdfa461 100644 --- a/Common/GPU/thin3d.cpp +++ b/Common/GPU/thin3d.cpp @@ -768,4 +768,15 @@ const char *Bugs::GetBugName(uint32_t bug) { } } +const char *PresentModeToString(PresentMode presentMode) { + switch (presentMode) { + case (PresentMode)0: return "NONE (bad)"; + case PresentMode::FIFO: return "FIFO"; + case PresentMode::IMMEDIATE: return "IMMEDIATE"; + case PresentMode::MAILBOX: return "MAILBOX"; + default: + return "COMBO"; // TODO: Hardcode all the combinations? + } +} + } // namespace Draw diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 753e4629c3..c0b8baf453 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -570,6 +570,13 @@ struct PipelineDesc { const Slice samplers; }; +enum class PresentMode { + FIFO = 1, + IMMEDIATE = 2, + MAILBOX = 4, +}; +ENUM_CLASS_BITOPS(PresentMode); + struct DeviceCaps { GPUVendor vendor; uint32_t deviceID; // use caution! @@ -614,6 +621,11 @@ struct DeviceCaps { // Old style, for older GL or Direct3D 9. u32 clipPlanesSupported; + // Presentation caps + int presentMaxInterval; // 1 on many backends + bool presentInstantModeChange; // Our VulkanContext doesn't currently support it so we mark it as such, but it can be supported with careful coding. + PresentMode presentModesSupported; + u32 multiSampleLevelsMask; // Bit n is set if (1 << n) is a valid multisample level. Bit 0 is always set. std::string deviceName; // The device name to use when creating the thin3d context, to get the same one. }; @@ -675,13 +687,6 @@ enum class DebugFlags { }; ENUM_CLASS_BITOPS(DebugFlags); -enum class PresentMode { - FIFO, - IMMEDIATE, - MAILBOX, - // Retired FIFO_RELAXED. May reintroduce at some point. -}; - class DrawContext { public: virtual ~DrawContext(); @@ -696,8 +701,6 @@ public: virtual std::vector GetExtensionList(bool device, bool enabledOnly) const { return std::vector(); } virtual std::vector GetDeviceList() const { return std::vector(); } - virtual PresentMode GetPresentMode() const = 0; - // Describes the primary shader language that this implementation prefers. const ShaderLanguageDesc &GetShaderLanguageDesc() { return shaderLanguageDesc_; @@ -815,7 +818,10 @@ public: // Frame management (for the purposes of sync and resource management, necessary with modern APIs). Default implementations here. virtual void BeginFrame(DebugFlags debugFlags) {} virtual void EndFrame() = 0; - virtual void Present(int vblanks) = 0; // NOTE: Not all backends support vblanks > 1. + + // vblanks is only relevant in FIFO present mode. + // NOTE: Not all backends support vblanks > 1. Some backends also can't change presentation mode immediately. + virtual void Present(PresentMode presentMode, int vblanks) = 0; virtual void WipeQueue() {} @@ -895,4 +901,6 @@ struct ShaderSource { ShaderModule *CreateShader(DrawContext *draw, ShaderStage stage, const std::vector &sources); +const char *PresentModeToString(PresentMode presentMode); + } // namespace Draw diff --git a/Common/GPU/thin3d_create.h b/Common/GPU/thin3d_create.h index fa56ebbb09..603db13d7c 100644 --- a/Common/GPU/thin3d_create.h +++ b/Common/GPU/thin3d_create.h @@ -24,7 +24,7 @@ class VulkanContext; namespace Draw { -DrawContext *T3DCreateGLContext(); +DrawContext *T3DCreateGLContext(bool canChangeSwapInterval); #ifdef _WIN32 DrawContext *T3DCreateDX9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, IDirect3DDevice9 *device, IDirect3DDevice9Ex *deviceEx); diff --git a/Common/GraphicsContext.h b/Common/GraphicsContext.h index 73ebf18a7b..91319d72e8 100644 --- a/Common/GraphicsContext.h +++ b/Common/GraphicsContext.h @@ -14,7 +14,6 @@ public: virtual void ShutdownFromRenderThread() {} virtual void Shutdown() = 0; - virtual void SwapInterval(int interval) = 0; // Used during window resize. Must be called from the window thread, // not the rendering thread or CPU thread. diff --git a/Core/Config.cpp b/Core/Config.cpp index c21b82a315..3a9e62dc29 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -147,6 +147,15 @@ static bool DefaultCodeGen() { #endif } +static bool DefaultVSync() { +#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(UWP) + // Previously we didn't allow turning off vsync/FIFO on Android. Let's set the default accordingly. + return true; +#else + return false; +#endif +} + static bool DefaultEnableStateUndo() { #ifdef MOBILE_DEVICE // Off on mobile to save disk space. @@ -603,7 +612,7 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("TexScalingType", &g_Config.iTexScalingType, 0, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("TexDeposterize", &g_Config.bTexDeposterize, false, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("TexHardwareScaling", &g_Config.bTexHardwareScaling, false, CfgFlag::PER_GAME | CfgFlag::REPORT), - ConfigSetting("VSyncInterval", &g_Config.bVSync, false, CfgFlag::PER_GAME), + ConfigSetting("VSync", &g_Config.bVSync, &DefaultVSync, CfgFlag::PER_GAME), ConfigSetting("BloomHack", &g_Config.iBloomHack, 0, CfgFlag::PER_GAME | CfgFlag::REPORT), // Not really a graphics setting... diff --git a/Core/FrameTiming.cpp b/Core/FrameTiming.cpp index 49767bc520..8c36e2b32f 100644 --- a/Core/FrameTiming.cpp +++ b/Core/FrameTiming.cpp @@ -10,3 +10,65 @@ #include "Core/FrameTiming.h" FrameTiming g_frameTiming; + +inline Draw::PresentMode GetBestImmediateMode(Draw::PresentMode supportedModes) { + if (supportedModes & Draw::PresentMode::MAILBOX) { + return Draw::PresentMode::MAILBOX; + } else { + return Draw::PresentMode::IMMEDIATE; + } +} + +void FrameTiming::Reset(Draw::DrawContext *draw) { + if (g_Config.bVSync) { + presentMode = Draw::PresentMode::FIFO; + presentInterval = 1; + } else { + presentMode = GetBestImmediateMode(draw->GetDeviceCaps().presentModesSupported); + presentInterval = 0; + } +} + +Draw::PresentMode ComputePresentMode(Draw::DrawContext *draw, int *interval) { + Draw::PresentMode mode = Draw::PresentMode::FIFO; + + if (draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::IMMEDIATE | Draw::PresentMode::MAILBOX)) { + // Switch to immediate if desired and possible. + bool wantInstant = false; + if (!g_Config.bVSync) { + wantInstant = true; + } + + if (PSP_CoreParameter().fastForward) { + wantInstant = true; + } + if (PSP_CoreParameter().fpsLimit != FPSLimit::NORMAL) { + int limit; + if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1) + limit = g_Config.iFpsLimit1; + else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) + limit = g_Config.iFpsLimit2; + else + limit = PSP_CoreParameter().analogFpsLimit; + + // For an alternative speed that is a clean factor of 60, the user probably still wants vsync. + if (limit == 0 || (limit >= 0 && limit != 15 && limit != 30 && limit != 60)) { + wantInstant = true; + } + } + + if (wantInstant && g_Config.bVSync && !draw->GetDeviceCaps().presentInstantModeChange) { + // If in vsync mode (which will be FIFO), and the backend can't switch immediately, + // stick to FIFO. + wantInstant = false; + } + + // If no instant modes are supported, stick to FIFO. + if (wantInstant && (draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::MAILBOX | Draw::PresentMode::IMMEDIATE))) { + mode = GetBestImmediateMode(draw->GetDeviceCaps().presentModesSupported); + } + } + + *interval = (mode == Draw::PresentMode::FIFO) ? 1 : 0; + return mode; +} diff --git a/Core/FrameTiming.h b/Core/FrameTiming.h index 11bd130678..ac73a491d0 100644 --- a/Core/FrameTiming.h +++ b/Core/FrameTiming.h @@ -1,7 +1,19 @@ #pragma once -struct FrameTiming { +#include "Common/GPU/thin3d.h" +namespace Draw { +class DrawContext; +} + +struct FrameTiming { + // Some backends won't allow changing this willy nilly. + Draw::PresentMode presentMode; + int presentInterval; + + void Reset(Draw::DrawContext *draw); }; extern FrameTiming g_frameTiming; + +Draw::PresentMode ComputePresentMode(Draw::DrawContext *draw, int *interval); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 7195e77280..411653eb7e 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -566,12 +566,17 @@ void __DisplayFlip(int cyclesLate) { bool fastForwardSkipFlip = g_Config.iFastForwardMode != (int)FastForwardMode::CONTINUOUS; - bool fifo = gpu && gpu->GetDrawContext() && gpu->GetDrawContext()->GetPresentMode() == Draw::PresentMode::FIFO; + if (gpu) { + Draw::DrawContext *draw = gpu->GetDrawContext(); - if (fifo && GetGPUBackend() == GPUBackend::VULKAN) { - // Vulkan doesn't support the interval setting, so we force skipping the flip. - // TODO: We'll clean this up in a more backend-independent way later. - fastForwardSkipFlip = true; + g_frameTiming.presentMode = ComputePresentMode(draw, &g_frameTiming.presentInterval); + + if (!draw->GetDeviceCaps().presentInstantModeChange && g_frameTiming.presentMode == Draw::PresentMode::FIFO) { + // Some backends can't just flip into MAILBOX/IMMEDIATE mode instantly. + // Vulkan doesn't support the interval setting, so we force skipping the flip. + // TODO: We'll clean this up in a more backend-independent way later. + fastForwardSkipFlip = true; + } } if (!g_Config.bSkipBufferEffects) { diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index e021ccbe12..a1e83814e7 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -846,6 +846,8 @@ namespace MIPSComp { case VecDo3Op::VSLT: allowSIMD = false; break; + default: + break; } u8 sregs[4], tregs[4], dregs[4]; @@ -941,6 +943,8 @@ namespace MIPSComp { ir.Write(IROp::FMovFromGPR, tempregs[i], IRTEMP_1); ir.Write(IROp::FCvtSW, tempregs[i], tempregs[i]); break; + default: + break; } } diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 41c0abc253..c59ca74aa8 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -49,7 +49,6 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : GPUCommonHW(gfxCtx, draw), drawEngine_(draw), fragmentTestCache_(draw) { - UpdateVsyncInterval(true); gstate_c.SetUseFlags(CheckGPUFeatures()); shaderManagerGL_ = new ShaderManagerGLES(draw); @@ -86,8 +85,6 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) UpdateCmdInfo(); BuildReportingInfo(); - // Update again after init to be sure of any silly driver problems. - UpdateVsyncInterval(true); textureCache_->NotifyConfigChanged(); @@ -251,7 +248,6 @@ void GPU_GLES::DeviceRestore(Draw::DrawContext *draw) { GPUCommonHW::DeviceRestore(draw); fragmentTestCache_.DeviceRestore(draw_); - UpdateVsyncInterval(true); } void GPU_GLES::BeginHostFrame() { diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index c92a233a8b..cd8824f2e3 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -67,13 +67,11 @@ GPUCommon::GPUCommon(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : gstate_c.Reset(); gpuStats.Reset(); - UpdateVsyncInterval(true); PPGeSetDrawContext(draw); ResetMatrices(); } void GPUCommon::BeginHostFrame() { - UpdateVsyncInterval(displayResized_); ReapplyGfxState(); // TODO: Assume config may have changed - maybe move to resize. @@ -115,38 +113,6 @@ void GPUCommon::Reinitialize() { framebufferManager_->DestroyAllFBOs(); } -void GPUCommon::UpdateVsyncInterval(bool force) { -#if !(PPSSPP_PLATFORM(ANDROID) || defined(USING_QT_UI) || PPSSPP_PLATFORM(UWP) || PPSSPP_PLATFORM(IOS)) - int desiredVSyncInterval = g_Config.bVSync ? 1 : 0; - if (PSP_CoreParameter().fastForward) { - desiredVSyncInterval = 0; - } - if (PSP_CoreParameter().fpsLimit != FPSLimit::NORMAL) { - int limit; - if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1) - limit = g_Config.iFpsLimit1; - else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) - limit = g_Config.iFpsLimit2; - else - limit = PSP_CoreParameter().analogFpsLimit; - - // For an alternative speed that is a clean factor of 60, the user probably still wants vsync. - if (limit == 0 || (limit >= 0 && limit != 15 && limit != 30 && limit != 60)) { - desiredVSyncInterval = 0; - } - } - - if (desiredVSyncInterval != lastVsync_ || force) { - // Disabled EXT_swap_control_tear for now, it never seems to settle at the correct timing - // so it just keeps tearing. Not what I hoped for... (gl_extensions.EXT_swap_control_tear) - // See http://developer.download.nvidia.com/opengl/specs/WGL_EXT_swap_control_tear.txt - if (gfxCtx_) - gfxCtx_->SwapInterval(desiredVSyncInterval); - lastVsync_ = desiredVSyncInterval; - } -#endif -} - int GPUCommon::EstimatePerVertexCost() { // TODO: This is transform cost, also account for rasterization cost somehow... although it probably // runs in parallel with transform. diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index aebd34ed3c..401572d380 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -243,7 +243,6 @@ protected: } void BeginFrame() override; - void UpdateVsyncInterval(bool force); virtual void CheckDepthUsage(VirtualFramebuffer *vfb) {} virtual void FastRunLoop(DisplayList &list) = 0; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 340d2391fa..7c66a7a170 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -84,8 +84,6 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) } BuildReportingInfo(); - // Update again after init to be sure of any silly driver problems. - UpdateVsyncInterval(true); textureCache_->NotifyConfigChanged(); diff --git a/Qt/QtMain.h b/Qt/QtMain.h index d1cf6c7d40..16bcc6d62d 100644 --- a/Qt/QtMain.h +++ b/Qt/QtMain.h @@ -49,7 +49,7 @@ class QtGLGraphicsContext : public GraphicsContext { public: QtGLGraphicsContext() { CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(); + draw_ = Draw::T3DCreateGLContext(false); SetGPUBackend(GPUBackend::OPENGL); renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); @@ -66,10 +66,6 @@ public: } void Shutdown() override {} - void SwapInterval(int interval) override { - // See TODO in constructor. - // renderManager_->SwapInterval(interval); - } void Resize() override {} Draw::DrawContext *GetDrawContext() override { diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index 462cf25996..a21e68d84d 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -414,7 +414,7 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, // Finally we can do the regular initialization. CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(); + draw_ = Draw::T3DCreateGLContext(true); renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); SetGPUBackend(GPUBackend::OPENGL); diff --git a/SDL/SDLGLGraphicsContext.h b/SDL/SDLGLGraphicsContext.h index b71b99349a..a16d1df865 100644 --- a/SDL/SDLGLGraphicsContext.h +++ b/SDL/SDLGLGraphicsContext.h @@ -19,9 +19,6 @@ public: void Shutdown() override; void ShutdownFromRenderThread() override; - // Gets forwarded to the render thread. - void SwapInterval(int interval) override; - void Resize() override {} Draw::DrawContext *GetDrawContext() override { diff --git a/SDL/SDLVulkanGraphicsContext.h b/SDL/SDLVulkanGraphicsContext.h index 1fe5b0c94f..f10edf8d2b 100644 --- a/SDL/SDLVulkanGraphicsContext.h +++ b/SDL/SDLVulkanGraphicsContext.h @@ -30,8 +30,6 @@ public: void Poll() override; - void SwapInterval(int interval) override { - } void *GetAPIContext() override { return vulkan_; } diff --git a/UI/DebugOverlay.cpp b/UI/DebugOverlay.cpp index eb3088325c..ca444bdef9 100644 --- a/UI/DebugOverlay.cpp +++ b/UI/DebugOverlay.cpp @@ -3,6 +3,7 @@ #include "Common/System/System.h" #include "UI/DebugOverlay.h" #include "Core/HW/Display.h" +#include "Core/FrameTiming.h" #include "Core/HLE/sceSas.h" #include "Core/ControlMapper.h" #include "Core/Config.h" @@ -113,7 +114,14 @@ static void DrawFrameTiming(UIContext *ctx, const Bounds &bounds) { ctx->BindFontTexture(); ctx->Draw()->SetFontScale(0.5f, 0.5f); - for (int i = 0; i < 8; i++) { + snprintf(statBuf, sizeof(statBuf), + "Present mode (interval): %s (%d)", + Draw::PresentModeToString(g_frameTiming.presentMode), + g_frameTiming.presentInterval); + + ctx->Draw()->DrawTextRect(ubuntu24, statBuf, bounds.x + 10, bounds.y + 50, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + + for (int i = 0; i < 5; i++) { FrameTimeData data = ctx->GetDrawContext()->GetFrameTimeData(6 + i); FrameTimeData prevData = ctx->GetDrawContext()->GetFrameTimeData(7 + i); if (data.frameBegin == 0.0) { @@ -153,7 +161,7 @@ static void DrawFrameTiming(UIContext *ctx, const Bounds &bounds) { presentStats ); } - ctx->Draw()->DrawTextRect(ubuntu24, statBuf, bounds.x + 10 + i * 150, bounds.y + 50, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + ctx->Draw()->DrawTextRect(ubuntu24, statBuf, bounds.x + 10 + i * 150, bounds.y + 150, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); } ctx->Draw()->SetFontScale(1.0f, 1.0f); ctx->Flush(); diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 176526c01a..31ccef44af 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -605,10 +605,17 @@ void SystemInfoScreen::CreateTabs() { g_display.dpi))); #endif -#if !PPSSPP_PLATFORM(WINDOWS) // Don't show on Windows, since it's always treated as 60 there. displayInfo->Add(new InfoItem(si->T("Refresh rate"), StringFromFormat(si->T("%0.2f Hz"), (float)System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE)))); -#endif + std::string presentModes; + if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO) presentModes += "FIFO, "; + if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::IMMEDIATE) presentModes += "IMMEDIATE, "; + if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::MAILBOX) presentModes += "MAILBOX, "; + if (!presentModes.empty()) { + presentModes.pop_back(); + presentModes.pop_back(); + } + displayInfo->Add(new InfoItem(si->T("Present modes"), presentModes)); CollapsibleSection *versionInfo = deviceSpecs->Add(new CollapsibleSection(si->T("Version Information"))); std::string apiVersion; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 4668774caa..48fd7887dd 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -335,13 +335,14 @@ void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) } #endif -#if !(PPSSPP_PLATFORM(ANDROID) || defined(USING_QT_UI) || PPSSPP_PLATFORM(UWP) || PPSSPP_PLATFORM(IOS)) + // All backends support FIFO. Check if any immediate modes are supported, if so we can allow the user to choose. + if (draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::IMMEDIATE | Draw::PresentMode::MAILBOX)) { CheckBox *vSync = graphicsSettings->Add(new CheckBox(&g_Config.bVSync, gr->T("VSync"))); vSync->OnClick.Add([=](EventParams &e) { NativeResized(); return UI::EVENT_CONTINUE; }); -#endif + } #if PPSSPP_PLATFORM(ANDROID) // Hide Immersive Mode on pre-kitkat Android diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 6b4599b631..de55463a9d 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1051,6 +1051,7 @@ void RenderOverlays(UIContext *dc, void *userdata) { } static Matrix4x4 ComputeOrthoMatrix(float xres, float yres) { + // TODO: Should be able to share the y-flip logic here with the one in postprocessing/presentation, for example. Matrix4x4 ortho; switch (GetGPUBackend()) { case GPUBackend::VULKAN: @@ -1132,6 +1133,8 @@ void NativeFrame(GraphicsContext *graphicsContext) { if (g_Config.bGpuLogProfiler) debugFlags |= Draw::DebugFlags::PROFILE_SCOPES; + g_frameTiming.Reset(g_draw); + g_draw->BeginFrame(debugFlags); ui_draw2d.PushDrawMatrix(ortho); @@ -1158,7 +1161,9 @@ void NativeFrame(GraphicsContext *graphicsContext) { ClearFailedGPUBackends(); } - g_draw->Present(1); + int interval; + Draw::PresentMode presentMode = ComputePresentMode(g_draw, &interval); + g_draw->Present(presentMode, interval); if (resized) { INFO_LOG(G3D, "Resized flag set - recalculating bounds"); diff --git a/UWP/PPSSPP_UWPMain.h b/UWP/PPSSPP_UWPMain.h index e2cc2edefc..211966400c 100644 --- a/UWP/PPSSPP_UWPMain.h +++ b/UWP/PPSSPP_UWPMain.h @@ -19,7 +19,6 @@ public: UWPGraphicsContext(std::shared_ptr resources); void Shutdown() override; - void SwapInterval(int interval) override {} void Resize() override {} Draw::DrawContext * GetDrawContext() override { return draw_; diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index 4173f9a6ac..d24c422554 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -33,10 +33,6 @@ #error This file should not be compiled for UWP. #endif -void D3D11Context::SwapInterval(int interval) { - swapInterval_ = interval; -} - HRESULT D3D11Context::CreateTheDevice(IDXGIAdapter *adapter) { bool windowed = true; // D3D11 has no need for display rotation. diff --git a/Windows/GPU/D3D11Context.h b/Windows/GPU/D3D11Context.h index 3d40355899..f3d8ff4d8a 100644 --- a/Windows/GPU/D3D11Context.h +++ b/Windows/GPU/D3D11Context.h @@ -30,7 +30,6 @@ class D3D11Context : public WindowsGraphicsContext { public: bool Init(HINSTANCE hInst, HWND window, std::string *error_message) override; void Shutdown() override; - void SwapInterval(int interval) override; void Resize() override; diff --git a/Windows/GPU/D3D9Context.cpp b/Windows/GPU/D3D9Context.cpp index 776ed26fc0..11ce38367a 100644 --- a/Windows/GPU/D3D9Context.cpp +++ b/Windows/GPU/D3D9Context.cpp @@ -23,10 +23,6 @@ typedef HRESULT (__stdcall *DIRECT3DCREATE9EX)(UINT, IDirect3D9Ex**); -void D3D9Context::SwapInterval(int interval) { - swapInterval_ = interval; -} - bool D3D9Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { bool windowed = true; hWnd_ = wnd; diff --git a/Windows/GPU/D3D9Context.h b/Windows/GPU/D3D9Context.h index 8f1806bf44..b9c40fdd92 100644 --- a/Windows/GPU/D3D9Context.h +++ b/Windows/GPU/D3D9Context.h @@ -34,7 +34,6 @@ public: bool Init(HINSTANCE hInst, HWND window, std::string *error_message) override; void Shutdown() override; - void SwapInterval(int interval) override; void Resize() override; diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index a4aa0e511a..026d70f29c 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -412,7 +412,7 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { resumeRequested = false; CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(); + draw_ = Draw::T3DCreateGLContext(wglSwapIntervalEXT != nullptr); bool success = draw_->CreatePresets(); // if we get this far, there will always be a GLSL compiler capable of compiling these. if (!success) { delete draw_; @@ -443,11 +443,6 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { return true; // Success } -void WindowsGLContext::SwapInterval(int interval) { - // Delegate to the render manager to make sure it's done on the right thread. - renderManager_->SwapInterval(interval); -} - void WindowsGLContext::Shutdown() { glslang::FinalizeProcess(); } diff --git a/Windows/GPU/WindowsGLContext.h b/Windows/GPU/WindowsGLContext.h index eac76176c3..eacc6709db 100644 --- a/Windows/GPU/WindowsGLContext.h +++ b/Windows/GPU/WindowsGLContext.h @@ -17,7 +17,6 @@ public: void ShutdownFromRenderThread() override; void Shutdown() override; - void SwapInterval(int interval) override; void Poll() override; diff --git a/Windows/GPU/WindowsVulkanContext.h b/Windows/GPU/WindowsVulkanContext.h index c331ec1dbb..f71522c69f 100644 --- a/Windows/GPU/WindowsVulkanContext.h +++ b/Windows/GPU/WindowsVulkanContext.h @@ -29,7 +29,6 @@ public: WindowsVulkanContext() : draw_(nullptr) {} bool Init(HINSTANCE hInst, HWND window, std::string *error_message) override; void Shutdown() override; - void SwapInterval(int interval) override {} void Resize() override; void Poll() override; diff --git a/android/jni/AndroidJavaGLContext.cpp b/android/jni/AndroidJavaGLContext.cpp index a2cada60f5..84010df9c4 100644 --- a/android/jni/AndroidJavaGLContext.cpp +++ b/android/jni/AndroidJavaGLContext.cpp @@ -22,7 +22,7 @@ bool AndroidJavaEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int g_display.rotation = DisplayRotation::ROTATE_0; g_display.rot_matrix.setIdentity(); - draw_ = Draw::T3DCreateGLContext(); // Can't fail + draw_ = Draw::T3DCreateGLContext(false); // Can't fail renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); diff --git a/android/jni/AndroidJavaGLContext.h b/android/jni/AndroidJavaGLContext.h index d02ffcfac1..b89c41d0f2 100644 --- a/android/jni/AndroidJavaGLContext.h +++ b/android/jni/AndroidJavaGLContext.h @@ -15,7 +15,6 @@ public: void ShutdownFromRenderThread() override; void Shutdown() override {} - void SwapInterval(int interval) override {} void Resize() override {} Draw::DrawContext *GetDrawContext() override { diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp index 467880aab6..c763ce9350 100644 --- a/android/jni/AndroidVulkanContext.cpp +++ b/android/jni/AndroidVulkanContext.cpp @@ -175,6 +175,3 @@ void AndroidVulkanContext::Resize() { draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); INFO_LOG(G3D, "AndroidVulkanContext::Resize end (final size: %dx%d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } - -void AndroidVulkanContext::SwapInterval(int interval) { -} diff --git a/android/jni/AndroidVulkanContext.h b/android/jni/AndroidVulkanContext.h index 69d302bdea..7da96fbd92 100644 --- a/android/jni/AndroidVulkanContext.h +++ b/android/jni/AndroidVulkanContext.h @@ -15,7 +15,6 @@ public: void ShutdownFromRenderThread() override; // Inverses InitFromRenderThread. void Shutdown() override; - void SwapInterval(int interval) override; void Resize() override; void *GetAPIContext() override { return g_Vulkan; } diff --git a/headless/SDLHeadlessHost.cpp b/headless/SDLHeadlessHost.cpp index 5d6fede8b8..61583e4e4f 100644 --- a/headless/SDLHeadlessHost.cpp +++ b/headless/SDLHeadlessHost.cpp @@ -95,7 +95,6 @@ public: void Shutdown() override {} void Resize() override {} - void SwapInterval(int interval) override {} private: Draw::DrawContext *draw_ = nullptr; @@ -157,7 +156,7 @@ bool GLDummyGraphicsContext::InitFromRenderThread(std::string *errorMessage) { #endif CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(); + draw_ = Draw::T3DCreateGLContext(false); renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); SetGPUBackend(GPUBackend::OPENGL); @@ -166,6 +165,7 @@ bool GLDummyGraphicsContext::InitFromRenderThread(std::string *errorMessage) { renderManager_->SetSwapFunction([&]() { SDL_GL_SwapWindow(screen_); }); + // TODO: Support SwapInterval return success; } diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 89eff3a67b..34297dcd8b 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -49,7 +49,7 @@ class IOSGraphicsContext : public GraphicsContext { public: IOSGraphicsContext() { CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(); + draw_ = Draw::T3DCreateGLContext(false); renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); SetGPUBackend(GPUBackend::OPENGL); @@ -63,7 +63,6 @@ public: return draw_; } - void SwapInterval(int interval) override {} void Resize() override {} void Shutdown() override {} diff --git a/libretro/LibretroGLContext.cpp b/libretro/LibretroGLContext.cpp index c820f6675b..240b3e1e1a 100644 --- a/libretro/LibretroGLContext.cpp +++ b/libretro/LibretroGLContext.cpp @@ -31,7 +31,7 @@ void LibretroGLContext::CreateDrawContext() { #endif CheckGLExtensions(); - draw_ = Draw::T3DCreateGLContext(); + draw_ = Draw::T3DCreateGLContext(false); renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); SetGPUBackend(GPUBackend::OPENGL); diff --git a/libretro/LibretroGLCoreContext.cpp b/libretro/LibretroGLCoreContext.cpp index 66261dea82..54e324c4c7 100644 --- a/libretro/LibretroGLCoreContext.cpp +++ b/libretro/LibretroGLCoreContext.cpp @@ -26,7 +26,7 @@ void LibretroGLCoreContext::CreateDrawContext() { glewInitDone = true; CheckGLExtensions(); } - draw_ = Draw::T3DCreateGLContext(); + draw_ = Draw::T3DCreateGLContext(false); renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager_->SetInflightFrames(g_Config.iInflightFrames); SetGPUBackend(GPUBackend::OPENGL); diff --git a/libretro/LibretroGraphicsContext.h b/libretro/LibretroGraphicsContext.h index ca267afdf8..958c5218a1 100644 --- a/libretro/LibretroGraphicsContext.h +++ b/libretro/LibretroGraphicsContext.h @@ -21,7 +21,6 @@ public: void Shutdown() override { DestroyDrawContext(); } - void SwapInterval(int interval) override {} virtual void SwapBuffers() = 0; void Resize() override {} diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 7ca2ac3181..2a35bf2453 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -1178,7 +1178,7 @@ namespace Libretro if (ctx->GetDrawContext()) { ctx->GetDrawContext()->EndFrame(); - ctx->GetDrawContext()->Present(1); + ctx->GetDrawContext()->Present(Draw::PresentMode::FIFO, 1); } }