From f887979ec03f0f2be8fe7d13b0a9cb8b33068d6d Mon Sep 17 00:00:00 2001 From: reuk Date: Wed, 11 Dec 2024 21:42:35 +0000 Subject: [PATCH] Direct2D: Fix performance issue when drawing transformed unclipped regions This issue could be observed in the GraphicsDemo's SVG pane, when the "rotation" option was enabled. Drawables internally enable the unclipped painting flag, which normally prevents slow clipping when drawing subcomponents of the drawable. The Direct2D graphics context was using the frame area as the default area, used to signal that no clipping should be applied. However, when non-axis-aligned transform was active, this area was incorrectly applied as a geometric clipping region. D2D geometric clips are relatively slow, so this caused large slowdowns. This solution adds a flag that is set whenever a clip is explicitly requested. If no clip is explicitly requested, then clipping will be entirely bypassed. This can make rendering of Drawables significantly faster. --- .../juce_Direct2DGraphicsContext_windows.cpp | 9 +++++--- .../juce_Direct2DGraphicsContext_windows.h | 21 +++++++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp index 654996c40e..206f2473d0 100644 --- a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp +++ b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp @@ -1070,21 +1070,24 @@ void Direct2DGraphicsContext::resetPendingClipList() void Direct2DGraphicsContext::applyPendingClipList() { + if (! pendingClipList.isClipApplied()) + return; + auto& transform = currentState->currentTransform; - bool const axisAligned = currentState->isCurrentTransformAxisAligned(); + const auto axisAligned = currentState->isCurrentTransformAxisAligned(); const auto list = pendingClipList.getList(); // Clip if the pending clip list is not empty and smaller than the frame size if (! list.containsRectangle (getPimpl()->getFrameSize().toFloat()) && ! list.isEmpty()) { - if (list.getNumRectangles() == 1 && (transform.isOnlyTranslated || axisAligned)) + if (list.getNumRectangles() == 1 && axisAligned) { auto r = list.getRectangle (0); currentState->pushAliasedAxisAlignedClipLayer (r); } else { - auto clipTransform = transform.isOnlyTranslated || axisAligned ? AffineTransform{} : transform.getTransform(); + auto clipTransform = axisAligned ? AffineTransform{} : transform.getTransform(); if (auto clipGeometry = D2DHelpers::rectListToPathGeometry (getPimpl()->getDirect2DFactory(), list, clipTransform, diff --git a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h index ba906a00cf..bc71df5908 100644 --- a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h +++ b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h @@ -122,29 +122,46 @@ protected: public: void clipTo (Rectangle i) { - list.clipTo (i); + if (std::exchange (clipApplied, true)) + list.clipTo (i); + else + list = i; } template void clipTo (const RectangleList& other) { - list.clipTo (other); + if (std::exchange (clipApplied, true)) + { + list.clipTo (other); + } + else + { + list.clear(); + + for (const auto& r : other) + list.add (r.toFloat()); + } } void subtract (Rectangle i) { list.subtract (i); + clipApplied = true; } RectangleList getList() const { return list; } + bool isClipApplied() const { return clipApplied; } void reset (Rectangle maxBounds) { list = maxBounds; + clipApplied = false; } private: RectangleList list; + bool clipApplied = false; }; PendingClipList pendingClipList;