Bug 1363677 - Skip Flush() and EndDraw() if the ID2D1DeviceContext is stale; r?dvander
MozReview-Commit-ID: 6KnryMZE8Qj
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1627,16 +1627,17 @@ public:
/*
* Attempts to create and install a D2D1 device from the supplied Direct3D11 device.
* Returns true on success, or false on failure and leaves the D2D1/Direct3D11 devices unset.
*/
static bool SetDirect3D11Device(ID3D11Device *aDevice);
static bool SetDWriteFactory(IDWriteFactory *aFactory);
static ID3D11Device *GetDirect3D11Device();
static ID2D1Device *GetD2D1Device();
+ static uint32_t GetD2D1DeviceSeq();
static IDWriteFactory *GetDWriteFactory();
static bool SupportsD2D1();
static already_AddRefed<GlyphRenderingOptions>
CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams);
static uint64_t GetD2DVRAMUsageDrawTarget();
static uint64_t GetD2DVRAMUsageSourceSurface();
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -38,16 +38,17 @@ ID2D1Factory1 *D2DFactory1()
{
return DrawTargetD2D1::factory();
}
DrawTargetD2D1::DrawTargetD2D1()
: mPushedLayers(1)
, mUsedCommandListsSincePurge(0)
, mDidComplexBlendWithListInList(false)
+ , mDeviceSeq(0)
{
}
DrawTargetD2D1::~DrawTargetD2D1()
{
PopAllClips();
if (mSnapshot) {
@@ -56,17 +57,17 @@ DrawTargetD2D1::~DrawTargetD2D1()
// MarkIndependent is running.
RefPtr<SourceSurfaceD2D1> deathGrip = mSnapshot;
// mSnapshot can be treated as independent of this DrawTarget since we know
// this DrawTarget won't change again.
deathGrip->MarkIndependent();
// mSnapshot will be cleared now.
}
- if (mDC) {
+ if (mDC && IsDeviceContextValid()) {
// The only way mDC can be null is if Init failed, but it can happen and the
// destructor is the only place where we need to check for it since the
// DrawTarget will destroyed right after Init fails.
mDC->EndDraw();
}
// Targets depending on us can break that dependency, since we're obviously not going to
// be modified in the future.
@@ -102,28 +103,30 @@ DrawTargetD2D1::Snapshot()
// this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
// are expensive though, especially relatively when little work is done, so
// we try to reduce the amount of times we execute these purges.
static const uint32_t kPushedLayersBeforePurge = 25;
void
DrawTargetD2D1::Flush()
{
- if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge) &&
- mPushedLayers.size() == 1) {
- // It's important to pop all clips as otherwise layers can forget about
- // their clip when doing an EndDraw. When we have layers pushed we cannot
- // easily pop all underlying clips to delay the purge until we have no
- // layers pushed.
- PopAllClips();
- mUsedCommandListsSincePurge = 0;
- mDC->EndDraw();
- mDC->BeginDraw();
- } else {
- mDC->Flush();
+ if (IsDeviceContextValid()) {
+ if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge) &&
+ mPushedLayers.size() == 1) {
+ // It's important to pop all clips as otherwise layers can forget about
+ // their clip when doing an EndDraw. When we have layers pushed we cannot
+ // easily pop all underlying clips to delay the purge until we have no
+ // layers pushed.
+ PopAllClips();
+ mUsedCommandListsSincePurge = 0;
+ mDC->EndDraw();
+ mDC->BeginDraw();
+ } else {
+ mDC->Flush();
+ }
}
// We no longer depend on any target.
for (TargetSet::iterator iter = mDependingOnTargets.begin();
iter != mDependingOnTargets.end(); iter++) {
(*iter)->mDependentTargets.erase(this);
}
mDependingOnTargets.clear();
@@ -1002,16 +1005,18 @@ bool
DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat)
{
HRESULT hr;
ID2D1Device* device = Factory::GetD2D1Device();
if (!device) {
gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(ID3D11Texture2D*, SurfaceFormat).";
return false;
+ } else {
+ mDeviceSeq = Factory::GetD2D1DeviceSeq();
}
hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
if (FAILED(hr)) {
gfxCriticalError() <<"[D2D1.1] 1Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
return false;
}
@@ -1067,16 +1072,18 @@ bool
DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
{
HRESULT hr;
ID2D1Device* device = Factory::GetD2D1Device();
if (!device) {
gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(IntSize, SurfaceFormat).";
return false;
+ } else {
+ mDeviceSeq = Factory::GetD2D1DeviceSeq();
}
hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
if (FAILED(hr)) {
gfxCriticalError() <<"[D2D1.1] 2Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
return false;
}
@@ -1948,10 +1955,15 @@ DrawTargetD2D1::PushD2DLayer(ID2D1Device
D2D1_ANTIALIAS_MODE antialias =
aPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry, antialias, aTransform,
1.0, nullptr, options), nullptr);
}
+bool
+DrawTargetD2D1::IsDeviceContextValid() {
+ return (mDeviceSeq == Factory::GetD2D1DeviceSeq()) && Factory::GetD2D1Device();
+}
+
}
}
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -220,20 +220,22 @@ private:
already_AddRefed<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
void PushClipGeometry(ID2D1Geometry* aGeometry, const D2D1_MATRIX_3X2_F& aTransform, bool aPixelAligned = false);
void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
bool aPixelAligned = false, bool aForceIgnoreAlpha = false,
const D2D1_RECT_F& aLayerRect = D2D1::InfiniteRect());
+ // This function is used to determine if the mDC is still valid; if it is
+ // stale, we should avoid using it to execute any draw commands.
+ bool IsDeviceContextValid();
+
IntSize mSize;
- RefPtr<ID3D11Device> mDevice;
- RefPtr<ID3D11Texture2D> mTexture;
RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
// This is only valid if mCurrentClippedGeometry is non-null. And will
// only be the intersection of all pixel-aligned retangular clips. This is in
// device space.
IntRect mCurrentClipBounds;
mutable RefPtr<ID2D1DeviceContext> mDC;
RefPtr<ID2D1Bitmap1> mBitmap;
RefPtr<ID2D1CommandList> mCommandList;
@@ -286,14 +288,16 @@ private:
// subsequently used -again- as an input to a blend effect for a command list,
// this causes an infinite recursion inside D2D as it tries to resolve the bounds.
// If we resolve the current command list before this happens
// we can avoid the subsequent hang. (See bug 1293586)
bool mDidComplexBlendWithListInList;
static ID2D1Factory1 *mFactory;
static IDWriteFactory *mDWriteFactory;
+ // This value is uesed to verify if the DrawTarget is created by a stale device.
+ uint32_t mDeviceSeq;
};
}
}
#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -161,16 +161,17 @@ namespace gfx {
// In Gecko, this value is managed by gfx.logging.level in gfxPrefs.
int32_t LoggingPrefs::sGfxLogLevel = LOG_DEFAULT;
#ifdef MOZ_ENABLE_FREETYPE
FT_Library Factory::mFTLibrary = nullptr;
#endif
#ifdef WIN32
+static uint32_t mDeviceSeq = 0;
ID3D11Device *Factory::mD3D11Device = nullptr;
ID2D1Device *Factory::mD2D1Device = nullptr;
IDWriteFactory *Factory::mDWriteFactory = nullptr;
#endif
DrawEventRecorder *Factory::mRecorder;
mozilla::gfx::Config* Factory::sConfig = nullptr;
@@ -674,16 +675,18 @@ Factory::SetDirect3D11Device(ID3D11Devic
RefPtr<IDXGIDevice> device;
aDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(device));
HRESULT hr = factory->CreateDevice(device, &mD2D1Device);
if (FAILED(hr)) {
gfxCriticalError() << "[D2D1] Failed to create gfx factory's D2D1 device, code: " << hexa(hr);
mD3D11Device = nullptr;
return false;
+ } else {
+ mDeviceSeq++;
}
return true;
}
ID3D11Device*
Factory::GetDirect3D11Device()
{
@@ -691,16 +694,22 @@ Factory::GetDirect3D11Device()
}
ID2D1Device*
Factory::GetD2D1Device()
{
return mD2D1Device;
}
+uint32_t
+Factory::GetD2D1DeviceSeq()
+{
+ return mDeviceSeq;
+}
+
IDWriteFactory*
Factory::GetDWriteFactory()
{
return mDWriteFactory;
}
bool
Factory::SupportsD2D1()