Add caching of clipping box values for Direct2D graphics context

Since retrieving actual clipping box is an expensive operation so to improve performance of wxD2DContext::GetClipBox() we need to store just retrieved clipping box data in the cache. These stored data will be then used in the forthcoming requests for clipping box values. Cached clipping box data are invalidated whenever clipping region is explicitly set using Clip()/ResetClip() or whenever transformation matrix is changed (to take into account new coordinates). If there is a call for clipping box (with GetClipBox) and cached data are marked as invalid then clipping box is retrieved/recalculated and stored in the cache.
This commit is contained in:
Artur Wieczorek
2016-09-01 21:23:08 +02:00
parent ba4b8d5670
commit 588425a9d4

View File

@@ -54,6 +54,8 @@
#pragma hdrstop #pragma hdrstop
#endif #endif
#include <float.h> // for FLT_MAX, FLT_MIN
#ifndef WX_PRECOMP #ifndef WX_PRECOMP
#include "wx/dc.h" #include "wx/dc.h"
#include "wx/dcclient.h" #include "wx/dcclient.h"
@@ -62,7 +64,6 @@
#include "wx/module.h" #include "wx/module.h"
#include "wx/window.h" #include "wx/window.h"
#include "wx/msw/private.h" #include "wx/msw/private.h"
#include <float.h> // for FLT_MAX
#endif // !WX_PRECOMP #endif // !WX_PRECOMP
#include "wx/graphics.h" #include "wx/graphics.h"
@@ -3442,16 +3443,15 @@ private:
private: private:
ID2D1Factory* m_direct2dFactory; ID2D1Factory* m_direct2dFactory;
wxSharedPtr<wxD2DRenderTargetResourceHolder> m_renderTargetHolder; wxSharedPtr<wxD2DRenderTargetResourceHolder> m_renderTargetHolder;
wxStack<StateData> m_stateStack; wxStack<StateData> m_stateStack;
wxStack<LayerData> m_layers; wxStack<LayerData> m_layers;
ID2D1RenderTarget* m_cachedRenderTarget; ID2D1RenderTarget* m_cachedRenderTarget;
D2D1::Matrix3x2F m_initTransform; D2D1::Matrix3x2F m_initTransform;
// Clipping box
bool m_isClipBoxValid;
double m_clipX1, m_clipY1, m_clipX2, m_clipY2;
private: private:
wxDECLARE_NO_COPY_CLASS(wxD2DContext); wxDECLARE_NO_COPY_CLASS(wxD2DContext);
}; };
@@ -3510,6 +3510,8 @@ void wxD2DContext::Init()
m_composition = wxCOMPOSITION_OVER; m_composition = wxCOMPOSITION_OVER;
m_renderTargetHolder->Bind(this); m_renderTargetHolder->Bind(this);
m_enableOffset = true; m_enableOffset = true;
m_isClipBoxValid = false;
m_clipX1 = m_clipY1 = m_clipX2 = m_clipY2 = 0.0;
EnsureInitialized(); EnsureInitialized();
} }
@@ -3575,6 +3577,8 @@ void wxD2DContext::SetClipLayer(ID2D1Geometry* clipGeometry)
GetRenderTarget()->PushLayer(ld.params, clipLayer); GetRenderTarget()->PushLayer(ld.params, clipLayer);
// Store layer parameters. // Store layer parameters.
m_layers.push(ld); m_layers.push(ld);
m_isClipBoxValid = false;
} }
void wxD2DContext::ResetClip() void wxD2DContext::ResetClip()
@@ -3619,102 +3623,113 @@ void wxD2DContext::ResetClip()
} }
// Restore current transformation matrix. // Restore current transformation matrix.
GetRenderTarget()->SetTransform(&currTransform); GetRenderTarget()->SetTransform(&currTransform);
m_isClipBoxValid = false;
} }
void wxD2DContext::GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h) void wxD2DContext::GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h)
{ {
// To obtain actual clipping box we have to start with rectangle if ( !m_isClipBoxValid )
// covering the entire render target and interesect with this rectangle
// all clipping layers. Bounding box of the final geometry
// (being intersection of all clipping layers) is a clipping box.
HRESULT hr;
wxCOMPtr<ID2D1RectangleGeometry> rectGeometry;
hr = m_direct2dFactory->CreateRectangleGeometry(
D2D1::RectF(0.0F, 0.0F, (FLOAT)m_width, (FLOAT)m_height),
&rectGeometry);
wxCHECK_HRESULT_RET(hr);
wxCOMPtr<ID2D1Geometry> clipGeometry(rectGeometry);
wxStack<LayerData> layers(m_layers);
while( !layers.empty() )
{ {
LayerData ld = layers.top(); // To obtain actual clipping box we have to start with rectangle
layers.pop(); // covering the entire render target and interesect with this rectangle
// all clipping layers. Bounding box of the final geometry
// (being intersection of all clipping layers) is a clipping box.
if ( ld.type == CLIP_LAYER ) HRESULT hr;
wxCOMPtr<ID2D1RectangleGeometry> rectGeometry;
hr = m_direct2dFactory->CreateRectangleGeometry(
D2D1::RectF(0.0F, 0.0F, (FLOAT)m_width, (FLOAT)m_height),
&rectGeometry);
wxCHECK_HRESULT_RET(hr);
wxCOMPtr<ID2D1Geometry> clipGeometry(rectGeometry);
wxStack<LayerData> layers(m_layers);
while( !layers.empty() )
{ {
// If current geometry is empty (null region) LayerData ld = layers.top();
// or there is no intersection between geometries layers.pop();
// then final result is "null" rectangle geometry.
FLOAT area; if ( ld.type == CLIP_LAYER )
hr = ld.geometry->ComputeArea(ld.transformMatrix, &area);
wxCHECK_HRESULT_RET(hr);
D2D1_GEOMETRY_RELATION geomRel;
hr = clipGeometry->CompareWithGeometry(ld.geometry, ld.transformMatrix, &geomRel);
wxCHECK_HRESULT_RET(hr);
if ( area <= FLT_MIN || geomRel == D2D1_GEOMETRY_RELATION_DISJOINT )
{ {
wxCOMPtr<ID2D1RectangleGeometry> nullGeometry; // If current geometry is empty (null region)
hr = m_direct2dFactory->CreateRectangleGeometry( // or there is no intersection between geometries
D2D1::RectF(0.0F, 0.0F, 0.0F, 0.0F), &nullGeometry); // then final result is "null" rectangle geometry.
FLOAT area;
hr = ld.geometry->ComputeArea(ld.transformMatrix, &area);
wxCHECK_HRESULT_RET(hr);
D2D1_GEOMETRY_RELATION geomRel;
hr = clipGeometry->CompareWithGeometry(ld.geometry, ld.transformMatrix, &geomRel);
wxCHECK_HRESULT_RET(hr);
if ( area <= FLT_MIN || geomRel == D2D1_GEOMETRY_RELATION_DISJOINT )
{
wxCOMPtr<ID2D1RectangleGeometry> nullGeometry;
hr = m_direct2dFactory->CreateRectangleGeometry(
D2D1::RectF(0.0F, 0.0F, 0.0F, 0.0F), &nullGeometry);
wxCHECK_HRESULT_RET(hr);
clipGeometry.reset();
clipGeometry = nullGeometry;
break;
}
wxCOMPtr<ID2D1PathGeometry> pathGeometryClip;
hr = m_direct2dFactory->CreatePathGeometry(&pathGeometryClip);
wxCHECK_HRESULT_RET(hr);
wxCOMPtr<ID2D1GeometrySink> pGeometrySink;
hr = pathGeometryClip->Open(&pGeometrySink);
wxCHECK_HRESULT_RET(hr); wxCHECK_HRESULT_RET(hr);
clipGeometry.reset(); hr = clipGeometry->CombineWithGeometry(ld.geometry, D2D1_COMBINE_MODE_INTERSECT,
clipGeometry = nullGeometry; ld.transformMatrix, pGeometrySink);
break; wxCHECK_HRESULT_RET(hr);
hr = pGeometrySink->Close();
wxCHECK_HRESULT_RET(hr);
pGeometrySink.reset();
clipGeometry = pathGeometryClip;
pathGeometryClip.reset();
} }
wxCOMPtr<ID2D1PathGeometry> pathGeometryClip;
hr = m_direct2dFactory->CreatePathGeometry(&pathGeometryClip);
wxCHECK_HRESULT_RET(hr);
wxCOMPtr<ID2D1GeometrySink> pGeometrySink;
hr = pathGeometryClip->Open(&pGeometrySink);
wxCHECK_HRESULT_RET(hr);
hr = clipGeometry->CombineWithGeometry(ld.geometry, D2D1_COMBINE_MODE_INTERSECT,
ld.transformMatrix, pGeometrySink);
wxCHECK_HRESULT_RET(hr);
hr = pGeometrySink->Close();
wxCHECK_HRESULT_RET(hr);
pGeometrySink.reset();
clipGeometry = pathGeometryClip;
pathGeometryClip.reset();
} }
}
// Final clipping geometry is given in device coordinates // Final clipping geometry is given in device coordinates
// so we need to transform its bounds to logical coordinates. // so we need to transform its bounds to logical coordinates.
D2D1::Matrix3x2F currTransform; D2D1::Matrix3x2F currTransform;
GetRenderTarget()->GetTransform(&currTransform); GetRenderTarget()->GetTransform(&currTransform);
currTransform.Invert(); currTransform.Invert();
D2D1_RECT_F bounds; D2D1_RECT_F bounds;
// First check if clip region is empty. // First check if clip region is empty.
FLOAT clipArea; FLOAT clipArea;
hr = clipGeometry->ComputeArea(currTransform, &clipArea); hr = clipGeometry->ComputeArea(currTransform, &clipArea);
wxCHECK_HRESULT_RET(hr);
if ( clipArea <= FLT_MIN )
{
bounds.left = bounds.top = bounds.right = bounds.bottom = 0.0F;
}
else
{
// If it is not empty then get it bounds.
hr = clipGeometry->GetBounds(currTransform, &bounds);
wxCHECK_HRESULT_RET(hr); wxCHECK_HRESULT_RET(hr);
if ( clipArea <= FLT_MIN )
{
bounds.left = bounds.top = bounds.right = bounds.bottom = 0.0F;
}
else
{
// If it is not empty then get it bounds.
hr = clipGeometry->GetBounds(currTransform, &bounds);
wxCHECK_HRESULT_RET(hr);
}
m_clipX1 = bounds.left;
m_clipY1 = bounds.top;
m_clipX2 = bounds.right;
m_clipY2 = bounds.bottom;
m_isClipBoxValid = true;
} }
if ( x ) if ( x )
*x = bounds.left; *x = m_clipX1;
if ( y ) if ( y )
*y = bounds.top; *y = m_clipY1;
if ( w ) if ( w )
*w = (double)bounds.right - bounds.left; *w = m_clipX2 - m_clipX1;
if ( h ) if ( h )
*h = (double)bounds.bottom - bounds.top; *h = m_clipY2 - m_clipY1;
} }
void* wxD2DContext::GetNativeContext() void* wxD2DContext::GetNativeContext()
@@ -3924,6 +3939,8 @@ void wxD2DContext::SetTransform(const wxGraphicsMatrix& matrix)
D2D1::Matrix3x2F m; D2D1::Matrix3x2F m;
m.SetProduct(wxGetD2DMatrixData(matrix)->GetMatrix3x2F(), m_initTransform); m.SetProduct(wxGetD2DMatrixData(matrix)->GetMatrix3x2F(), m_initTransform);
GetRenderTarget()->SetTransform(&m); GetRenderTarget()->SetTransform(&m);
m_isClipBoxValid = false;
} }
wxGraphicsMatrix wxD2DContext::GetTransform() const wxGraphicsMatrix wxD2DContext::GetTransform() const
@@ -4043,6 +4060,8 @@ void wxD2DContext::PopState()
// Restore drawing state. // Restore drawing state.
GetRenderTarget()->RestoreDrawingState(state.drawingState); GetRenderTarget()->RestoreDrawingState(state.drawingState);
m_isClipBoxValid = false;
} }
void wxD2DContext::GetTextExtent( void wxD2DContext::GetTextExtent(