Browse Source

Windows: Consolidate graphics invalidation regions

Regions marked as dirty by repaint calls are now queued up and
dispatched when the corresponding physical display device is
refreshed
pull/22/head
Tom Poole 3 years ago
parent
commit
05a42424f9
3 changed files with 289 additions and 2 deletions
  1. +2
    -0
      modules/juce_gui_basics/juce_gui_basics.cpp
  2. +1
    -0
      modules/juce_gui_basics/juce_gui_basics.h
  3. +286
    -2
      modules/juce_gui_basics/native/juce_win32_Windowing.cpp

+ 2
- 0
modules/juce_gui_basics/juce_gui_basics.cpp View File

@@ -60,6 +60,7 @@
#include <commctrl.h>
#include <UIAutomation.h>
#include <sapi.h>
#include <Dxgi.h>
#if JUCE_WEB_BROWSER
#include <exdisp.h>
@@ -72,6 +73,7 @@
#pragma comment(lib, "vfw32.lib")
#pragma comment(lib, "imm32.lib")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "dxgi.lib")
#if JUCE_OPENGL
#pragma comment(lib, "OpenGL32.Lib")


+ 1
- 0
modules/juce_gui_basics/juce_gui_basics.h View File

@@ -40,6 +40,7 @@
WeakOSXFrameworks: Metal MetalKit
iOSFrameworks: CoreServices UIKit
WeakiOSFrameworks: Metal MetalKit
mingwLibs: dxgi
END_JUCE_MODULE_DECLARATION


+ 286
- 2
modules/juce_gui_basics/native/juce_win32_Windowing.cpp View File

@@ -1476,8 +1476,252 @@ private:
WindowsDeleteStringFuncPtr deleteHString;
};
//==============================================================================
static HMONITOR getMonitorFromOutput (ComSmartPtr<IDXGIOutput> output)
{
DXGI_OUTPUT_DESC desc = {};
return (FAILED (output->GetDesc (&desc)) || ! desc.AttachedToDesktop)
? nullptr
: desc.Monitor;
}
struct VBlankListener
{
virtual void onVBlank() = 0;
};
//==============================================================================
class VSyncThread : private Thread,
private AsyncUpdater
{
public:
VSyncThread (ComSmartPtr<IDXGIOutput> out,
HMONITOR mon,
VBlankListener& listener)
: Thread ("VSyncThread"),
output (out),
monitor (mon)
{
listeners.push_back (listener);
startThread (10);
}
~VSyncThread() override
{
stopThread (-1);
cancelPendingUpdate();
}
void updateMonitor()
{
monitor = getMonitorFromOutput (output);
}
HMONITOR getMonitor() const noexcept { return monitor; }
void addListener (VBlankListener& listener)
{
listeners.push_back (listener);
}
bool removeListener (const VBlankListener& listener)
{
auto it = std::find_if (listeners.cbegin(),
listeners.cend(),
[&listener] (const auto& l) { return &(l.get()) == &listener; });
if (it != listeners.cend())
{
listeners.erase (it);
return true;
}
return false;
}
bool hasNoListeners() const noexcept
{
return listeners.empty();
}
bool hasListener (const VBlankListener& listener) const noexcept
{
return std::any_of (listeners.cbegin(),
listeners.cend(),
[&listener] (const auto& l) { return &(l.get()) == &listener; });
}
private:
//==============================================================================
void run() override
{
while (! threadShouldExit())
{
if (output->WaitForVBlank() == S_OK)
triggerAsyncUpdate();
else
Thread::sleep (1);
}
}
void handleAsyncUpdate() override
{
for (auto& listener : listeners)
listener.get().onVBlank();
}
//==============================================================================
ComSmartPtr<IDXGIOutput> output;
HMONITOR monitor = nullptr;
std::vector<std::reference_wrapper<VBlankListener>> listeners;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSyncThread)
JUCE_DECLARE_NON_MOVEABLE (VSyncThread)
};
//==============================================================================
class VBlankDispatcher : public DeletedAtShutdown
{
public:
void updateDisplay (VBlankListener& listener, HMONITOR monitor)
{
if (monitor == nullptr)
{
removeListener (listener);
return;
}
auto threadWithListener = threads.end();
auto threadWithMonitor = threads.end();
for (auto it = threads.begin(); it != threads.end(); ++it)
{
if ((*it)->hasListener (listener))
threadWithListener = it;
if ((*it)->getMonitor() == monitor)
threadWithMonitor = it;
if (threadWithListener != threads.end()
&& threadWithMonitor != threads.end())
{
if (threadWithListener == threadWithMonitor)
return;
(*threadWithMonitor)->addListener (listener);
// This may invalidate iterators, so be careful!
removeListener (threadWithListener, listener);
return;
}
}
if (threadWithMonitor != threads.end())
{
(*threadWithMonitor)->addListener (listener);
return;
}
if (threadWithListener != threads.end())
removeListener (threadWithListener, listener);
for (auto adapter : adapters)
{
UINT i = 0;
ComSmartPtr<IDXGIOutput> output;
while (adapter->EnumOutputs (i, output.resetAndGetPointerAddress()) != DXGI_ERROR_NOT_FOUND)
{
if (getMonitorFromOutput (output) == monitor)
{
threads.emplace_back (std::make_unique<VSyncThread> (output, monitor, listener));
return;
}
++i;
}
}
}
void removeListener (const VBlankListener& listener)
{
for (auto it = threads.begin(); it != threads.end(); ++it)
if (removeListener (it, listener))
return;
}
void reconfigureDisplays()
{
adapters.clear();
ComSmartPtr<IDXGIFactory> factory;
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
CreateDXGIFactory (__uuidof (IDXGIFactory), (void**)factory.resetAndGetPointerAddress());
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
UINT i = 0;
ComSmartPtr<IDXGIAdapter> adapter;
while (factory->EnumAdapters (i, adapter.resetAndGetPointerAddress()) != DXGI_ERROR_NOT_FOUND)
{
adapters.push_back (adapter);
++i;
}
for (auto& thread : threads)
thread->updateMonitor();
threads.erase (std::remove_if (threads.begin(),
threads.end(),
[] (const auto& thread) { return thread->getMonitor() == nullptr; }),
threads.end());
}
JUCE_DECLARE_SINGLETON_SINGLETHREADED (VBlankDispatcher, true)
private:
//==============================================================================
using Threads = std::vector<std::unique_ptr<VSyncThread>>;
VBlankDispatcher()
{
reconfigureDisplays();
}
~VBlankDispatcher() override
{
threads.clear();
clearSingletonInstance();
}
// This may delete the corresponding thread and invalidate iterators,
// so be careful!
bool removeListener (Threads::iterator it, const VBlankListener& listener)
{
if ((*it)->removeListener (listener))
{
if ((*it)->hasNoListeners())
threads.erase (it);
return true;
}
return false;
}
//==============================================================================
std::vector<ComSmartPtr<IDXGIAdapter>> adapters;
Threads threads;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VBlankDispatcher)
JUCE_DECLARE_NON_MOVEABLE (VBlankDispatcher)
};
JUCE_IMPLEMENT_SINGLETON (VBlankDispatcher)
//==============================================================================
class HWNDComponentPeer : public ComponentPeer,
private VBlankListener,
private Timer
#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client
, public ModifierKeyReceiver
@@ -1517,10 +1761,15 @@ public:
return ModifierKeys::currentModifiers;
};
if (updateCurrentMonitor())
VBlankDispatcher::getInstance()->updateDisplay (*this, currentMonitor);
}
~HWNDComponentPeer() override
{
VBlankDispatcher::getInstance()->removeListener (*this);
// do this first to avoid messages arriving for this window before it's destroyed
JuceWindowIdentifier::setAsJUCEWindow (hwnd, false);
@@ -1889,14 +2138,26 @@ public:
void repaint (const Rectangle<int>& area) override
{
auto r = RECTFromRectangle ((area.toDouble() * getPlatformScaleFactor()).getSmallestIntegerContainer());
InvalidateRect (hwnd, &r, FALSE);
deferredRepaints.add ((area.toDouble() * getPlatformScaleFactor()).getSmallestIntegerContainer());
}
void dispatchDeferredRepaints()
{
for (auto deferredRect : deferredRepaints)
{
auto r = RECTFromRectangle (deferredRect);
InvalidateRect (hwnd, &r, FALSE);
}
deferredRepaints.clear();
}
void performAnyPendingRepaintsNow() override
{
if (component.isVisible())
{
dispatchDeferredRepaints();
WeakReference<Component> localRef (&component);
MSG m;
@@ -1906,6 +2167,12 @@ public:
}
}
//==============================================================================
void onVBlank() override
{
dispatchDeferredRepaints();
}
//==============================================================================
static HWNDComponentPeer* getOwnerOfWindow (HWND h) noexcept
{
@@ -2156,6 +2423,7 @@ private:
double scaleFactor = 1.0;
bool inDpiChange = 0, inHandlePositionChanged = 0;
HMONITOR currentMonitor = nullptr;
bool isAccessibilityActive = false;
@@ -3479,6 +3747,12 @@ private:
return 0;
}
bool updateCurrentMonitor()
{
auto monitor = MonitorFromWindow (hwnd, MONITOR_DEFAULTTONULL);
return std::exchange (currentMonitor, monitor) != monitor;
}
bool handlePositionChanged()
{
auto pos = getCurrentMousePos();
@@ -3496,6 +3770,9 @@ private:
handleMovedOrResized();
if (updateCurrentMonitor())
VBlankDispatcher::getInstance()->updateDisplay (*this, currentMonitor);
return ! dontRepaint; // to allow non-accelerated openGL windows to draw themselves correctly.
}
@@ -3649,6 +3926,11 @@ private:
setWindowPos (hwnd, ScalingHelpers::scaledScreenPosToUnscaled (component, Desktop::getInstance().getDisplays()
.getDisplayForRect (component.getScreenBounds())->userArea),
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING);
auto* dispatcher = VBlankDispatcher::getInstance();
dispatcher->reconfigureDisplays();
updateCurrentMonitor();
dispatcher->updateDisplay (*this, currentMonitor);
}
//==============================================================================
@@ -4408,6 +4690,8 @@ private:
IMEHandler imeHandler;
bool shouldIgnoreModalDismiss = false;
RectangleList<int> deferredRepaints;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponentPeer)
};


Loading…
Cancel
Save