summaryrefslogtreecommitdiffstats
path: root/gfx/layers/d3d9/CompositorD3D9.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/d3d9/CompositorD3D9.cpp')
-rw-r--r--gfx/layers/d3d9/CompositorD3D9.cpp1045
1 files changed, 1045 insertions, 0 deletions
diff --git a/gfx/layers/d3d9/CompositorD3D9.cpp b/gfx/layers/d3d9/CompositorD3D9.cpp
new file mode 100644
index 000000000..0f7e942c1
--- /dev/null
+++ b/gfx/layers/d3d9/CompositorD3D9.cpp
@@ -0,0 +1,1045 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorD3D9.h"
+#include "LayerManagerD3D9Shaders.h"
+#include "gfxWindowsPlatform.h"
+#include "nsIWidget.h"
+#include "mozilla/layers/ImageHost.h"
+#include "mozilla/layers/ContentHost.h"
+#include "mozilla/layers/Effects.h"
+#include "nsWindowsHelpers.h"
+#include "Nv3DVUtils.h"
+#include "gfxFailure.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "gfxPrefs.h"
+#include "gfxCrashReporterUtils.h"
+#include "gfxUtils.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/widget/WinCompositorWidget.h"
+#include "D3D9SurfaceImage.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+CompositorD3D9::CompositorD3D9(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
+ : Compositor(aWidget, aParent)
+ , mDeviceResetCount(0)
+ , mFailedResetAttempts(0)
+{
+}
+
+CompositorD3D9::~CompositorD3D9()
+{
+ mSwapChain = nullptr;
+ mDeviceManager = nullptr;
+}
+
+bool
+CompositorD3D9::Initialize(nsCString* const out_failureReason)
+{
+ ScopedGfxFeatureReporter reporter("D3D9 Layers");
+
+ mDeviceManager = DeviceManagerD3D9::Get();
+ if (!mDeviceManager) {
+ *out_failureReason = "FEATURE_FAILURE_D3D9_DEVICE_MANAGER";
+ return false;
+ }
+
+ mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindows()->GetHwnd());
+ if (!mSwapChain) {
+ *out_failureReason = "FEATURE_FAILURE_D3D9_SWAP_CHAIN";
+ return false;
+ }
+
+ if (!mWidget->InitCompositor(this)) {
+ *out_failureReason = "FEATURE_FAILURE_D3D9_INIT_COMPOSITOR";
+ return false;
+ }
+
+ reporter.SetSuccessful();
+
+ return true;
+}
+
+TextureFactoryIdentifier
+CompositorD3D9::GetTextureFactoryIdentifier()
+{
+ TextureFactoryIdentifier ident;
+ ident.mMaxTextureSize = GetMaxTextureSize();
+ ident.mParentBackend = LayersBackend::LAYERS_D3D9;
+ ident.mParentProcessType = XRE_GetProcessType();
+ ident.mSupportsComponentAlpha = SupportsEffect(EffectTypes::COMPONENT_ALPHA);
+ return ident;
+}
+
+bool
+CompositorD3D9::CanUseCanvasLayerForSize(const IntSize &aSize)
+{
+ int32_t maxTextureSize = GetMaxTextureSize();
+
+ if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) {
+ return false;
+ }
+
+ return true;
+}
+
+int32_t
+CompositorD3D9::GetMaxTextureSize() const
+{
+ return mDeviceManager ? mDeviceManager->GetMaxTextureSize() : INT32_MAX;
+}
+
+already_AddRefed<DataTextureSource>
+CompositorD3D9::CreateDataTextureSource(TextureFlags aFlags)
+{
+ return MakeAndAddRef<DataTextureSourceD3D9>(SurfaceFormat::UNKNOWN, this, aFlags);
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorD3D9::CreateRenderTarget(const gfx::IntRect &aRect,
+ SurfaceInitMode aInit)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ if (!mDeviceManager) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> texture;
+ HRESULT hr = device()->CreateTexture(aRect.width, aRect.height, 1,
+ D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
+ D3DPOOL_DEFAULT, getter_AddRefs(texture),
+ nullptr);
+ if (FAILED(hr)) {
+ ReportFailure(NS_LITERAL_CSTRING("CompositorD3D9::CreateRenderTarget: Failed to create texture"),
+ hr);
+ return nullptr;
+ }
+
+ return MakeAndAddRef<CompositingRenderTargetD3D9>(texture, aInit, aRect);
+}
+
+already_AddRefed<IDirect3DTexture9>
+CompositorD3D9::CreateTexture(const gfx::IntRect& aRect,
+ const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint)
+{
+ MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
+
+ if (aRect.width * aRect.height == 0) {
+ return nullptr;
+ }
+
+ if (!mDeviceManager) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> texture;
+ HRESULT hr = device()->CreateTexture(aRect.width, aRect.height, 1,
+ D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
+ D3DPOOL_DEFAULT, getter_AddRefs(texture),
+ nullptr);
+ if (FAILED(hr)) {
+ ReportFailure(NS_LITERAL_CSTRING("CompositorD3D9::CreateRenderTargetFromSource: Failed to create texture"),
+ hr);
+ return nullptr;
+ }
+
+ if (aSource) {
+ RefPtr<IDirect3DSurface9> sourceSurface =
+ static_cast<const CompositingRenderTargetD3D9*>(aSource)->GetD3D9Surface();
+
+ RefPtr<IDirect3DSurface9> destSurface;
+ hr = texture->GetSurfaceLevel(0, getter_AddRefs(destSurface));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to get texture surface level for dest.");
+ }
+
+ if (sourceSurface && destSurface) {
+ RECT sourceRect;
+ sourceRect.left = aSourcePoint.x;
+ sourceRect.right = aSourcePoint.x + aRect.width;
+ sourceRect.top = aSourcePoint.y;
+ sourceRect.bottom = aSourcePoint.y + aRect.height;
+ RECT destRect;
+ destRect.left = 0;
+ destRect.right = aRect.width;
+ destRect.top = 0;
+ destRect.bottom = aRect.height;
+
+ // copy the source to the dest
+ hr = device()->StretchRect(sourceSurface,
+ &sourceRect,
+ destSurface,
+ &destRect,
+ D3DTEXF_NONE);
+ if (FAILED(hr)) {
+ ReportFailure(NS_LITERAL_CSTRING("CompositorD3D9::CreateRenderTargetFromSource: Failed to update texture"),
+ hr);
+ }
+ }
+ }
+
+ return texture.forget();
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorD3D9::CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+ const CompositingRenderTarget *aSource,
+ const gfx::IntPoint &aSourcePoint)
+{
+ RefPtr<IDirect3DTexture9> texture = CreateTexture(aRect, aSource, aSourcePoint);
+
+ if (!texture) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<CompositingRenderTargetD3D9>(texture,
+ INIT_MODE_NONE,
+ aRect);
+}
+
+void
+CompositorD3D9::SetRenderTarget(CompositingRenderTarget *aRenderTarget)
+{
+ MOZ_ASSERT(aRenderTarget && mDeviceManager);
+ RefPtr<CompositingRenderTargetD3D9> oldRT = mCurrentRT;
+ mCurrentRT = static_cast<CompositingRenderTargetD3D9*>(aRenderTarget);
+ mCurrentRT->BindRenderTarget(device());
+ PrepareViewport(mCurrentRT->GetSize());
+}
+
+static DeviceManagerD3D9::ShaderMode
+ShaderModeForEffectType(EffectTypes aEffectType, gfx::SurfaceFormat aFormat)
+{
+ switch (aEffectType) {
+ case EffectTypes::SOLID_COLOR:
+ return DeviceManagerD3D9::SOLIDCOLORLAYER;
+ case EffectTypes::RENDER_TARGET:
+ return DeviceManagerD3D9::RGBALAYER;
+ case EffectTypes::RGB:
+ if (aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat::R8G8B8A8)
+ return DeviceManagerD3D9::RGBALAYER;
+ return DeviceManagerD3D9::RGBLAYER;
+ case EffectTypes::YCBCR:
+ return DeviceManagerD3D9::YCBCRLAYER;
+ }
+
+ MOZ_CRASH("GFX: Bad effect type");
+}
+
+void
+CompositorD3D9::ClearRect(const gfx::Rect& aRect)
+{
+ D3DRECT rect;
+ rect.x1 = aRect.X();
+ rect.y1 = aRect.Y();
+ rect.x2 = aRect.XMost();
+ rect.y2 = aRect.YMost();
+
+ device()->Clear(1, &rect, D3DCLEAR_TARGET,
+ 0x00000000, 0, 0);
+}
+
+void
+CompositorD3D9::DrawQuad(const gfx::Rect &aRect,
+ const gfx::IntRect &aClipRect,
+ const EffectChain &aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect)
+{
+ if (!mDeviceManager) {
+ return;
+ }
+
+ IDirect3DDevice9* d3d9Device = device();
+ MOZ_ASSERT(d3d9Device, "We should be able to get a device now");
+
+ MOZ_ASSERT(mCurrentRT, "No render target");
+ d3d9Device->SetVertexShaderConstantF(CBmLayerTransform, &aTransform._11, 4);
+
+ IntPoint origin = mCurrentRT->GetOrigin();
+ float renderTargetOffset[] = { float(origin.x), float(origin.y), 0, 0 };
+ d3d9Device->SetVertexShaderConstantF(CBvRenderTargetOffset,
+ renderTargetOffset,
+ 1);
+ d3d9Device->SetVertexShaderConstantF(CBvLayerQuad,
+ ShaderConstantRect(aRect.x,
+ aRect.y,
+ aRect.width,
+ aRect.height),
+ 1);
+
+ if (aEffectChain.mPrimaryEffect->mType != EffectTypes::SOLID_COLOR) {
+ float opacity[4];
+ /*
+ * We always upload a 4 component float, but the shader will use only the
+ * first component since it's declared as a 'float'.
+ */
+ opacity[0] = aOpacity;
+ d3d9Device->SetPixelShaderConstantF(CBfLayerOpacity, opacity, 1);
+ }
+
+ bool isPremultiplied = true;
+
+ MaskType maskType = MaskType::MaskNone;
+
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ maskType = MaskType::Mask;
+ }
+
+ gfx::Rect backdropDest;
+ gfx::IntRect backdropRect;
+ gfx::Matrix4x4 backdropTransform;
+ RefPtr<IDirect3DTexture9> backdropTexture;
+ gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
+
+ if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
+ EffectBlendMode *blendEffect =
+ static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
+ blendMode = blendEffect->mBlendMode;
+
+ // Pixel Shader Model 2.0 is too limited to perform blending in the same way
+ // as Direct3D 11 - there are too many instructions, and we don't have
+ // configurable shaders (as we do with OGL) that would avoid a huge shader
+ // matrix.
+ //
+ // Instead, we use a multi-step process for blending on D3D9:
+ // (1) Capture the backdrop into a temporary surface.
+ // (2) Render the effect chain onto the backdrop, with OP_SOURCE.
+ // (3) Capture the backdrop again into another surface - these are our source pixels.
+ // (4) Perform a final blend step using software.
+ // (5) Blit the blended result back to the render target.
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ backdropRect = ComputeBackdropCopyRect(
+ aRect, aClipRect, aTransform, &backdropTransform, &backdropDest);
+
+ // If this fails, don't set a blend op.
+ backdropTexture = CreateTexture(backdropRect, mCurrentRT, backdropRect.TopLeft());
+ if (!backdropTexture) {
+ blendMode = gfx::CompositionOp::OP_OVER;
+ }
+ }
+ }
+
+ RECT scissor;
+ scissor.left = aClipRect.x;
+ scissor.right = aClipRect.XMost();
+ scissor.top = aClipRect.y;
+ scissor.bottom = aClipRect.YMost();
+ d3d9Device->SetScissorRect(&scissor);
+
+ uint32_t maskTexture = 0;
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR:
+ {
+ // output color is premultiplied, so we need to adjust all channels.
+ Color layerColor =
+ static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get())->mColor;
+ float color[4];
+ color[0] = layerColor.r * layerColor.a * aOpacity;
+ color[1] = layerColor.g * layerColor.a * aOpacity;
+ color[2] = layerColor.b * layerColor.a * aOpacity;
+ color[3] = layerColor.a * aOpacity;
+
+ d3d9Device->SetPixelShaderConstantF(CBvColor, color, 1);
+
+ maskTexture = mDeviceManager
+ ->SetShaderMode(DeviceManagerD3D9::SOLIDCOLORLAYER, maskType);
+ }
+ break;
+ case EffectTypes::RENDER_TARGET:
+ case EffectTypes::RGB:
+ {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+
+ Rect textureCoords = texturedEffect->mTextureCoords;
+ d3d9Device->SetVertexShaderConstantF(CBvTextureCoords,
+ ShaderConstantRect(
+ textureCoords.x,
+ textureCoords.y,
+ textureCoords.width,
+ textureCoords.height),
+ 1);
+
+ SetSamplerForSamplingFilter(texturedEffect->mSamplingFilter);
+
+ TextureSourceD3D9* source = texturedEffect->mTexture->AsSourceD3D9();
+ d3d9Device->SetTexture(0, source->GetD3D9Texture());
+
+ maskTexture = mDeviceManager
+ ->SetShaderMode(ShaderModeForEffectType(aEffectChain.mPrimaryEffect->mType,
+ texturedEffect->mTexture->GetFormat()),
+ maskType);
+
+ isPremultiplied = texturedEffect->mPremultiplied;
+ }
+ break;
+ case EffectTypes::YCBCR:
+ {
+ EffectYCbCr* ycbcrEffect =
+ static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
+
+ SetSamplerForSamplingFilter(SamplingFilter::LINEAR);
+
+ Rect textureCoords = ycbcrEffect->mTextureCoords;
+ d3d9Device->SetVertexShaderConstantF(CBvTextureCoords,
+ ShaderConstantRect(
+ textureCoords.x,
+ textureCoords.y,
+ textureCoords.width,
+ textureCoords.height),
+ 1);
+
+ const int Y = 0, Cb = 1, Cr = 2;
+ TextureSource* source = ycbcrEffect->mTexture;
+
+ if (!source) {
+ NS_WARNING("No texture to composite");
+ return;
+ }
+
+ if (!source->GetSubSource(Y) || !source->GetSubSource(Cb) || !source->GetSubSource(Cr)) {
+ // This can happen if we failed to upload the textures, most likely
+ // because of unsupported dimensions (we don't tile YCbCr textures).
+ return;
+ }
+
+
+ float* yuvToRgb = gfxUtils::Get4x3YuvColorMatrix(ycbcrEffect->mYUVColorSpace);
+ d3d9Device->SetPixelShaderConstantF(CBmYuvColorMatrix, yuvToRgb, 3);
+
+ TextureSourceD3D9* sourceY = source->GetSubSource(Y)->AsSourceD3D9();
+ TextureSourceD3D9* sourceCb = source->GetSubSource(Cb)->AsSourceD3D9();
+ TextureSourceD3D9* sourceCr = source->GetSubSource(Cr)->AsSourceD3D9();
+
+
+ MOZ_ASSERT(sourceY->GetD3D9Texture());
+ MOZ_ASSERT(sourceCb->GetD3D9Texture());
+ MOZ_ASSERT(sourceCr->GetD3D9Texture());
+
+ /*
+ * Send 3d control data and metadata
+ */
+ if (mDeviceManager->GetNv3DVUtils()) {
+ Nv_Stereo_Mode mode;
+ switch (source->AsSourceD3D9()->GetStereoMode()) {
+ case StereoMode::LEFT_RIGHT:
+ mode = NV_STEREO_MODE_LEFT_RIGHT;
+ break;
+ case StereoMode::RIGHT_LEFT:
+ mode = NV_STEREO_MODE_RIGHT_LEFT;
+ break;
+ case StereoMode::BOTTOM_TOP:
+ mode = NV_STEREO_MODE_BOTTOM_TOP;
+ break;
+ case StereoMode::TOP_BOTTOM:
+ mode = NV_STEREO_MODE_TOP_BOTTOM;
+ break;
+ case StereoMode::MONO:
+ mode = NV_STEREO_MODE_MONO;
+ break;
+ }
+
+ // Send control data even in mono case so driver knows to leave stereo mode.
+ mDeviceManager->GetNv3DVUtils()->SendNv3DVControl(mode, true, FIREFOX_3DV_APP_HANDLE);
+
+ if (source->AsSourceD3D9()->GetStereoMode() != StereoMode::MONO) {
+ mDeviceManager->GetNv3DVUtils()->SendNv3DVControl(mode, true, FIREFOX_3DV_APP_HANDLE);
+
+ RefPtr<IDirect3DSurface9> renderTarget;
+ d3d9Device->GetRenderTarget(0, getter_AddRefs(renderTarget));
+ mDeviceManager->GetNv3DVUtils()->SendNv3DVMetaData((unsigned int)aRect.width,
+ (unsigned int)aRect.height,
+ (HANDLE)(sourceY->GetD3D9Texture()),
+ (HANDLE)(renderTarget));
+ }
+ }
+
+ // Linear scaling is default here, adhering to mFilter is difficult since
+ // presumably even with point filtering we'll still want chroma upsampling
+ // to be linear. In the current approach we can't.
+ device()->SetTexture(Y, sourceY->GetD3D9Texture());
+ device()->SetTexture(Cb, sourceCb->GetD3D9Texture());
+ device()->SetTexture(Cr, sourceCr->GetD3D9Texture());
+ maskTexture = mDeviceManager->SetShaderMode(DeviceManagerD3D9::YCBCRLAYER, maskType);
+ }
+ break;
+ case EffectTypes::COMPONENT_ALPHA:
+ {
+ MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled());
+ EffectComponentAlpha* effectComponentAlpha =
+ static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
+ TextureSourceD3D9* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceD3D9();
+ TextureSourceD3D9* sourceOnBlack = effectComponentAlpha->mOnBlack->AsSourceD3D9();
+
+ Rect textureCoords = effectComponentAlpha->mTextureCoords;
+ d3d9Device->SetVertexShaderConstantF(CBvTextureCoords,
+ ShaderConstantRect(
+ textureCoords.x,
+ textureCoords.y,
+ textureCoords.width,
+ textureCoords.height),
+ 1);
+
+ SetSamplerForSamplingFilter(effectComponentAlpha->mSamplingFilter);
+
+ maskTexture = mDeviceManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS1, maskType);
+ SetMask(aEffectChain, maskTexture);
+ d3d9Device->SetTexture(0, sourceOnBlack->GetD3D9Texture());
+ d3d9Device->SetTexture(1, sourceOnWhite->GetD3D9Texture());
+ d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
+ d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);
+ d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+
+ maskTexture = mDeviceManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS2, maskType);
+ SetMask(aEffectChain, maskTexture);
+ d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
+ d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+
+ // Restore defaults
+ d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ d3d9Device->SetTexture(1, nullptr);
+ }
+ return;
+ default:
+ NS_WARNING("Unknown shader type");
+ return;
+ }
+
+ SetMask(aEffectChain, maskTexture);
+
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ // Use SOURCE instead of OVER to get the original source pixels without
+ // having to render to another intermediate target.
+ d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
+ }
+ if (!isPremultiplied) {
+ d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ }
+
+ d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+
+ // Restore defaults.
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ }
+ if (!isPremultiplied) {
+ d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ }
+
+ // Final pass - if mix-blending, do it now that we have the backdrop and
+ // source textures.
+ if (BlendOpIsMixBlendMode(blendMode)) {
+ FinishMixBlend(
+ backdropRect,
+ backdropDest,
+ backdropTransform,
+ backdropTexture,
+ blendMode);
+ }
+}
+
+void
+CompositorD3D9::SetMask(const EffectChain &aEffectChain, uint32_t aMaskTexture)
+{
+ EffectMask *maskEffect =
+ static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ if (!maskEffect) {
+ return;
+ }
+
+ TextureSourceD3D9 *source = maskEffect->mMaskTexture->AsSourceD3D9();
+
+ device()->SetTexture(aMaskTexture, source->GetD3D9Texture());
+
+ const gfx::Matrix4x4& maskTransform = maskEffect->mMaskTransform;
+ NS_ASSERTION(maskTransform.Is2D(), "How did we end up with a 3D transform here?!");
+ Rect bounds = Rect(Point(), Size(maskEffect->mSize));
+ bounds = maskTransform.As2D().TransformBounds(bounds);
+
+ device()->SetVertexShaderConstantF(DeviceManagerD3D9::sMaskQuadRegister,
+ ShaderConstantRect(bounds.x,
+ bounds.y,
+ bounds.width,
+ bounds.height),
+ 1);
+}
+
+/**
+ * In the next few methods we call |mParent->InvalidateRemoteLayers()| - that has
+ * a few uses - if our device or swap chain is not ready, it causes us to try
+ * to render again, that means we keep trying to get a good device and swap
+ * chain and don't block the main thread (which we would if we kept trying in
+ * a busy loop because this is likely to happen in a sync transaction).
+ * If we had to recreate our device, then we have new textures and we
+ * need to reupload everything (not just what is currently invalid) from the
+ * client side. That means we need to invalidate everything on the client.
+ * If we just reset and didn't need to recreate, then we don't need to reupload
+ * our textures, but we do need to redraw the whole window, which means we still
+ * need to invalidate everything.
+ * Currently we probably do this complete invalidation too much. But it is better
+ * to do that than to miss an invalidation which would result in a black layer
+ * (or multiple layers) until the user moves the mouse. The unnecessary invalidtion
+ * only happens when the device is reset, so that should be pretty rare and when
+ * other things are happening so the user does not expect super performance.
+ */
+
+bool
+CompositorD3D9::EnsureSwapChain()
+{
+ MOZ_ASSERT(mDeviceManager, "Don't call EnsureSwapChain without a device manager");
+
+ if (!mSwapChain) {
+ mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindows()->GetHwnd());
+ // We could not create a swap chain, return false
+ if (!mSwapChain) {
+ // Check the state of the device too
+ DeviceManagerState state = mDeviceManager->VerifyReadyForRendering();
+ if (state == DeviceMustRecreate) {
+ mDeviceManager = nullptr;
+ }
+ mParent->InvalidateRemoteLayers();
+ return false;
+ }
+ }
+
+ // We have a swap chain, lets initialise it
+ DeviceManagerState state = mSwapChain->PrepareForRendering();
+ if (state == DeviceOK) {
+ mFailedResetAttempts = 0;
+ return true;
+ }
+ // Swap chain could not be initialised, handle the failure
+ if (state == DeviceMustRecreate) {
+ mDeviceManager = nullptr;
+ mSwapChain = nullptr;
+ }
+ mParent->InvalidateRemoteLayers();
+ return false;
+}
+
+void
+CompositorD3D9::CheckResetCount()
+{
+ if (mDeviceResetCount != mDeviceManager->GetDeviceResetCount()) {
+ mParent->InvalidateRemoteLayers();
+ }
+ mDeviceResetCount = mDeviceManager->GetDeviceResetCount();
+}
+
+bool
+CompositorD3D9::Ready()
+{
+ if (mDeviceManager) {
+ if (EnsureSwapChain()) {
+ // We don't need to call VerifyReadyForRendering because that is
+ // called by mSwapChain->PrepareForRendering() via EnsureSwapChain().
+ CheckResetCount();
+ return true;
+ }
+ return false;
+ }
+
+ NS_ASSERTION(!mCurrentRT && !mDefaultRT,
+ "Shouldn't have any render targets around, they must be released before our device");
+ mSwapChain = nullptr;
+
+ mDeviceManager = DeviceManagerD3D9::Get();
+ if (!mDeviceManager) {
+ FailedToResetDevice();
+ mParent->InvalidateRemoteLayers();
+ return false;
+ }
+ if (EnsureSwapChain()) {
+ CheckResetCount();
+ return true;
+ }
+ return false;
+}
+
+void
+CompositorD3D9::FailedToResetDevice() {
+ mFailedResetAttempts += 1;
+ // 10 is a totally arbitrary number that we may want to increase or decrease
+ // depending on how things behave in the wild.
+ if (mFailedResetAttempts > 10) {
+ mFailedResetAttempts = 0;
+ DeviceManagerDx::Get()->NotifyD3D9DeviceReset();
+ gfxCriticalNote << "[D3D9] Unable to get a working D3D9 Compositor";
+ }
+}
+
+void
+CompositorD3D9::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const IntRect *aClipRectIn,
+ const IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion,
+ IntRect *aClipRectOut,
+ IntRect *aRenderBoundsOut)
+{
+ MOZ_ASSERT(mDeviceManager && mSwapChain);
+
+ mDeviceManager->SetupRenderState();
+
+ EnsureSize();
+
+ device()->Clear(0, nullptr, D3DCLEAR_TARGET, 0x00000000, 0, 0);
+ device()->BeginScene();
+
+ if (aClipRectOut) {
+ *aClipRectOut = IntRect(0, 0, mSize.width, mSize.height);
+ }
+ if (aRenderBoundsOut) {
+ *aRenderBoundsOut = IntRect(0, 0, mSize.width, mSize.height);
+ }
+
+ RECT r;
+ if (aClipRectIn) {
+ r.left = (LONG)aClipRectIn->x;
+ r.top = (LONG)aClipRectIn->y;
+ r.right = (LONG)(aClipRectIn->x + aClipRectIn->width);
+ r.bottom = (LONG)(aClipRectIn->y + aClipRectIn->height);
+ } else {
+ r.left = r.top = 0;
+ r.right = mSize.width;
+ r.bottom = mSize.height;
+ }
+ device()->SetScissorRect(&r);
+
+ RefPtr<IDirect3DSurface9> backBuffer = mSwapChain->GetBackBuffer();
+ mDefaultRT = new CompositingRenderTargetD3D9(backBuffer,
+ INIT_MODE_CLEAR,
+ IntRect(0, 0, mSize.width, mSize.height));
+ SetRenderTarget(mDefaultRT);
+}
+
+void
+CompositorD3D9::EndFrame()
+{
+ if (mDeviceManager) {
+ device()->EndScene();
+
+ LayoutDeviceIntSize oldSize = mSize;
+ EnsureSize();
+ if (oldSize == mSize) {
+ if (mTarget) {
+ PaintToTarget();
+ } else {
+ mSwapChain->Present();
+ }
+ }
+ }
+
+ Compositor::EndFrame();
+
+ mCurrentRT = nullptr;
+ mDefaultRT = nullptr;
+}
+
+void
+CompositorD3D9::PrepareViewport(const gfx::IntSize& aSize)
+{
+ Matrix4x4 viewMatrix;
+ /*
+ * Matrix to transform to viewport space ( <-1.0, 1.0> topleft,
+ * <1.0, -1.0> bottomright)
+ */
+ viewMatrix._11 = 2.0f / aSize.width;
+ viewMatrix._22 = -2.0f / aSize.height;
+ viewMatrix._41 = -1.0f;
+ viewMatrix._42 = 1.0f;
+ viewMatrix._33 = 0.0f;
+
+ HRESULT hr = device()->SetVertexShaderConstantF(CBmProjection, &viewMatrix._11, 4);
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to set projection matrix");
+ }
+}
+
+bool
+CompositorD3D9::SupportsEffect(EffectTypes aEffect)
+{
+ if (aEffect == EffectTypes::COMPONENT_ALPHA &&
+ !mDeviceManager->HasComponentAlpha()) {
+ return false;
+ }
+
+ return Compositor::SupportsEffect(aEffect);
+}
+
+void
+CompositorD3D9::EnsureSize()
+{
+ mSize = mWidget->GetClientSize();
+}
+
+void
+CompositorD3D9::SetSamplerForSamplingFilter(SamplingFilter aSamplingFilter)
+{
+ switch (aSamplingFilter) {
+ case SamplingFilter::LINEAR:
+ device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ return;
+ case SamplingFilter::POINT:
+ device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
+ device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
+ return;
+ default:
+ device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ }
+}
+
+void
+CompositorD3D9::PaintToTarget()
+{
+ if (!mDeviceManager) {
+ return;
+ }
+
+ RefPtr<IDirect3DSurface9> backBuff;
+ RefPtr<IDirect3DSurface9> destSurf;
+ device()->GetRenderTarget(0, getter_AddRefs(backBuff));
+
+ D3DSURFACE_DESC desc;
+ backBuff->GetDesc(&desc);
+
+ device()->CreateOffscreenPlainSurface(desc.Width, desc.Height,
+ D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM,
+ getter_AddRefs(destSurf), nullptr);
+
+ device()->GetRenderTargetData(backBuff, destSurf);
+
+ D3DLOCKED_RECT rect;
+ HRESULT hr = destSurf->LockRect(&rect, nullptr, D3DLOCK_READONLY);
+ if (FAILED(hr) || !rect.pBits) {
+ gfxCriticalError() << "Failed to lock rect in paint to target D3D9 " << hexa(hr);
+ return;
+ }
+ RefPtr<DataSourceSurface> sourceSurface =
+ Factory::CreateWrappingDataSourceSurface((uint8_t*)rect.pBits,
+ rect.Pitch,
+ IntSize(desc.Width, desc.Height),
+ SurfaceFormat::B8G8R8A8);
+ mTarget->CopySurface(sourceSurface,
+ IntRect(0, 0, desc.Width, desc.Height),
+ IntPoint(-mTargetBounds.x, -mTargetBounds.y));
+ mTarget->Flush();
+ destSurf->UnlockRect();
+}
+
+void
+CompositorD3D9::ReportFailure(const nsACString &aMsg, HRESULT aCode)
+{
+ // We could choose to abort here when hr == E_OUTOFMEMORY.
+ nsCString msg;
+ msg.Append(aMsg);
+ msg.AppendLiteral(" Error code: ");
+ msg.AppendInt(uint32_t(aCode));
+ NS_WARNING(msg.BeginReading());
+
+ gfx::LogFailure(msg);
+}
+
+static inline already_AddRefed<IDirect3DSurface9>
+GetSurfaceOfTexture(IDirect3DTexture9* aTexture)
+{
+ RefPtr<IDirect3DSurface9> surface;
+ HRESULT hr = aTexture->GetSurfaceLevel(0, getter_AddRefs(surface));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to grab texture surface " << hexa(hr);
+ return nullptr;
+ }
+ return surface.forget();
+}
+
+static inline already_AddRefed<IDirect3DSurface9>
+CreateDataSurfaceForTexture(IDirect3DDevice9* aDevice,
+ IDirect3DSurface9* aSource,
+ const D3DSURFACE_DESC& aDesc)
+{
+ RefPtr<IDirect3DSurface9> dest;
+ HRESULT hr = aDevice->CreateOffscreenPlainSurface(
+ aDesc.Width, aDesc.Height,
+ aDesc.Format, D3DPOOL_SYSTEMMEM,
+ getter_AddRefs(dest), nullptr);
+ if (FAILED(hr) || !dest) {
+ gfxCriticalNote << "Failed to create offscreen plain surface " << hexa(hr);
+ return nullptr;
+ }
+
+ hr = aDevice->GetRenderTargetData(aSource, dest);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to get render target data " << hexa(hr);
+ return nullptr;
+ }
+
+ return dest.forget();
+}
+
+class AutoSurfaceLock
+{
+ public:
+ AutoSurfaceLock(IDirect3DSurface9* aSurface, DWORD aFlags = 0) {
+ PodZero(&mRect);
+
+ HRESULT hr = aSurface->LockRect(&mRect, nullptr, aFlags);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to lock surface rect " << hexa(hr);
+ return;
+ }
+ mSurface = aSurface;
+ }
+ ~AutoSurfaceLock() {
+ if (mSurface) {
+ mSurface->UnlockRect();
+ }
+ }
+
+ bool Okay() const {
+ return !!mSurface;
+ }
+ int Pitch() const {
+ MOZ_ASSERT(Okay());
+ return mRect.Pitch;
+ }
+ uint8_t* Bits() const {
+ MOZ_ASSERT(Okay());
+ return reinterpret_cast<uint8_t*>(mRect.pBits);
+ }
+
+ private:
+ RefPtr<IDirect3DSurface9> mSurface;
+ D3DLOCKED_RECT mRect;
+};
+
+void
+CompositorD3D9::FinishMixBlend(const gfx::IntRect& aBackdropRect,
+ const gfx::Rect& aBackdropDest,
+ const gfx::Matrix4x4& aBackdropTransform,
+ RefPtr<IDirect3DTexture9> aBackdrop,
+ gfx::CompositionOp aBlendMode)
+{
+ HRESULT hr;
+
+ RefPtr<IDirect3DTexture9> source =
+ CreateTexture(aBackdropRect, mCurrentRT, aBackdropRect.TopLeft());
+ if (!source) {
+ return;
+ }
+
+ // Slow path - do everything in software. Unfortunately this requires
+ // a lot of copying, since we have to readback the source and backdrop,
+ // then upload the blended result, then blit it back.
+
+ IDirect3DDevice9* d3d9Device = device();
+
+ // Query geometry/format of the two surfaces.
+ D3DSURFACE_DESC backdropDesc, sourceDesc;
+ if (FAILED(aBackdrop->GetLevelDesc(0, &backdropDesc)) ||
+ FAILED(source->GetLevelDesc(0, &sourceDesc)))
+ {
+ gfxCriticalNote << "Failed to query mix-blend texture descriptor";
+ return;
+ }
+
+ MOZ_ASSERT(backdropDesc.Format == D3DFMT_A8R8G8B8);
+ MOZ_ASSERT(sourceDesc.Format == D3DFMT_A8R8G8B8);
+
+ // Acquire a temporary data surface for the backdrop texture.
+ RefPtr<IDirect3DSurface9> backdropSurface = GetSurfaceOfTexture(aBackdrop);
+ if (!backdropSurface) {
+ return;
+ }
+ RefPtr<IDirect3DSurface9> tmpBackdrop =
+ CreateDataSurfaceForTexture(d3d9Device, backdropSurface, backdropDesc);
+ if (!tmpBackdrop) {
+ return;
+ }
+
+ // New scope for locks and temporary surfaces.
+ {
+ // Acquire a temporary data surface for the source texture.
+ RefPtr<IDirect3DSurface9> sourceSurface = GetSurfaceOfTexture(source);
+ if (!sourceSurface) {
+ return;
+ }
+ RefPtr<IDirect3DSurface9> tmpSource =
+ CreateDataSurfaceForTexture(d3d9Device, sourceSurface, sourceDesc);
+ if (!tmpSource) {
+ return;
+ }
+
+ // Perform the readback and blend in software.
+ AutoSurfaceLock backdropLock(tmpBackdrop);
+ AutoSurfaceLock sourceLock(tmpSource, D3DLOCK_READONLY);
+ if (!backdropLock.Okay() || !sourceLock.Okay()) {
+ return;
+ }
+
+ RefPtr<DataSourceSurface> source = Factory::CreateWrappingDataSourceSurface(
+ sourceLock.Bits(), sourceLock.Pitch(),
+ gfx::IntSize(sourceDesc.Width, sourceDesc.Height),
+ SurfaceFormat::B8G8R8A8);
+
+ RefPtr<DrawTarget> dest = Factory::CreateDrawTargetForData(
+ BackendType::CAIRO,
+ backdropLock.Bits(),
+ gfx::IntSize(backdropDesc.Width, backdropDesc.Height),
+ backdropLock.Pitch(),
+ SurfaceFormat::B8G8R8A8);
+
+ // The backdrop rect is rounded out - account for any difference between
+ // it and the actual destination.
+ gfx::Rect destRect(
+ aBackdropDest.x - aBackdropRect.x,
+ aBackdropDest.y - aBackdropRect.y,
+ aBackdropDest.width,
+ aBackdropDest.height);
+
+ dest->DrawSurface(
+ source, destRect, destRect,
+ gfx::DrawSurfaceOptions(),
+ gfx::DrawOptions(1.0f, aBlendMode));
+ }
+
+ // Upload the new blended surface to the backdrop texture.
+ d3d9Device->UpdateSurface(tmpBackdrop, nullptr, backdropSurface, nullptr);
+
+ // Finally, drop in the new backdrop. We don't need to do another
+ // DrawPrimitive() since the software blend will have included the
+ // final OP_OVER step for us.
+ RECT destRect = {
+ aBackdropRect.x, aBackdropRect.y,
+ aBackdropRect.XMost(), aBackdropRect.YMost()
+ };
+ hr = d3d9Device->StretchRect(backdropSurface,
+ nullptr,
+ mCurrentRT->GetD3D9Surface(),
+ &destRect,
+ D3DTEXF_NONE);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "StretcRect with mix-blend failed " << hexa(hr);
+ }
+}
+
+}
+}