michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "LayerManagerD3D9.h" michael@0: michael@0: #include "ThebesLayerD3D9.h" michael@0: #include "ContainerLayerD3D9.h" michael@0: #include "ImageLayerD3D9.h" michael@0: #include "ColorLayerD3D9.h" michael@0: #include "CanvasLayerD3D9.h" michael@0: #include "ReadbackLayerD3D9.h" michael@0: #include "gfxWindowsPlatform.h" michael@0: #include "nsIGfxInfo.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "gfxFailure.h" michael@0: #include "gfxPrefs.h" michael@0: michael@0: #include "gfxCrashReporterUtils.h" michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: LayerManagerD3D9::LayerManagerD3D9(nsIWidget *aWidget) michael@0: : mWidget(aWidget) michael@0: , mDeviceResetCount(0) michael@0: { michael@0: mCurrentCallbackInfo.Callback = nullptr; michael@0: mCurrentCallbackInfo.CallbackData = nullptr; michael@0: } michael@0: michael@0: LayerManagerD3D9::~LayerManagerD3D9() michael@0: { michael@0: Destroy(); michael@0: } michael@0: michael@0: bool michael@0: LayerManagerD3D9::Initialize(bool force) michael@0: { michael@0: ScopedGfxFeatureReporter reporter("D3D9 Layers", force); michael@0: michael@0: /* XXX: this preference and blacklist code should move out of the layer manager */ michael@0: bool forceAccelerate = gfxPrefs::LayersAccelerationForceEnabled(); michael@0: michael@0: nsCOMPtr gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); michael@0: if (gfxInfo) { michael@0: int32_t status; michael@0: if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &status))) { michael@0: if (status != nsIGfxInfo::FEATURE_NO_INFO && !forceAccelerate) michael@0: { michael@0: NS_WARNING("Direct3D 9-accelerated layers are not supported on this system."); michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: mDeviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(); michael@0: if (!mDeviceManager) { michael@0: return false; michael@0: } michael@0: michael@0: mSwapChain = mDeviceManager-> michael@0: CreateSwapChain((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW)); michael@0: michael@0: if (!mSwapChain) { michael@0: return false; michael@0: } michael@0: michael@0: reporter.SetSuccessful(); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::SetClippingRegion(const nsIntRegion &aClippingRegion) michael@0: { michael@0: mClippingRegion = aClippingRegion; michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::Destroy() michael@0: { michael@0: if (!IsDestroyed()) { michael@0: if (mRoot) { michael@0: static_cast(mRoot->ImplData())->LayerManagerDestroyed(); michael@0: } michael@0: /* Important to release this first since it also holds a reference to the michael@0: * device manager michael@0: */ michael@0: mSwapChain = nullptr; michael@0: mDeviceManager = nullptr; michael@0: } michael@0: LayerManager::Destroy(); michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::BeginTransaction() michael@0: { michael@0: mInTransaction = true; michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::BeginTransactionWithTarget(gfxContext *aTarget) michael@0: { michael@0: mInTransaction = true; michael@0: mTarget = aTarget; michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::EndConstruction() michael@0: { michael@0: } michael@0: michael@0: bool michael@0: LayerManagerD3D9::EndEmptyTransaction(EndTransactionFlags aFlags) michael@0: { michael@0: mInTransaction = false; michael@0: michael@0: // If the device reset count from our last EndTransaction doesn't match michael@0: // the current device reset count, the device must have been reset one or michael@0: // more times since our last transaction. In that case, an empty transaction michael@0: // is not possible, because layers may need to be rerendered. michael@0: if (!mRoot || mDeviceResetCount != mDeviceManager->GetDeviceResetCount()) michael@0: return false; michael@0: michael@0: EndTransaction(nullptr, nullptr, aFlags); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::EndTransaction(DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData, michael@0: EndTransactionFlags aFlags) michael@0: { michael@0: mInTransaction = false; michael@0: michael@0: mDeviceResetCount = mDeviceManager->GetDeviceResetCount(); michael@0: michael@0: if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { michael@0: mCurrentCallbackInfo.Callback = aCallback; michael@0: mCurrentCallbackInfo.CallbackData = aCallbackData; michael@0: michael@0: if (aFlags & END_NO_COMPOSITE) { michael@0: // Apply pending tree updates before recomputing effective michael@0: // properties. michael@0: mRoot->ApplyPendingUpdatesToSubtree(); michael@0: } michael@0: michael@0: // The results of our drawing always go directly into a pixel buffer, michael@0: // so we don't need to pass any global transform here. michael@0: mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4()); michael@0: michael@0: SetCompositingDisabled(aFlags & END_NO_COMPOSITE); michael@0: Render(); michael@0: /* Clean this out for sanity */ michael@0: mCurrentCallbackInfo.Callback = nullptr; michael@0: mCurrentCallbackInfo.CallbackData = nullptr; michael@0: } michael@0: michael@0: // Clear mTarget, next transaction could have no target michael@0: mTarget = nullptr; michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::SetRoot(Layer *aLayer) michael@0: { michael@0: mRoot = aLayer; michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerD3D9::CreateThebesLayer() michael@0: { michael@0: nsRefPtr layer = new ThebesLayerD3D9(this); michael@0: return layer.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerD3D9::CreateContainerLayer() michael@0: { michael@0: nsRefPtr layer = new ContainerLayerD3D9(this); michael@0: return layer.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerD3D9::CreateImageLayer() michael@0: { michael@0: nsRefPtr layer = new ImageLayerD3D9(this); michael@0: return layer.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerD3D9::CreateColorLayer() michael@0: { michael@0: nsRefPtr layer = new ColorLayerD3D9(this); michael@0: return layer.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerD3D9::CreateCanvasLayer() michael@0: { michael@0: nsRefPtr layer = new CanvasLayerD3D9(this); michael@0: return layer.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: LayerManagerD3D9::CreateReadbackLayer() michael@0: { michael@0: nsRefPtr layer = new ReadbackLayerD3D9(this); michael@0: return layer.forget(); michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::ReportFailure(const nsACString &aMsg, HRESULT aCode) michael@0: { michael@0: // We could choose to abort here when hr == E_OUTOFMEMORY. michael@0: nsCString msg; michael@0: msg.Append(aMsg); michael@0: msg.AppendLiteral(" Error code: "); michael@0: msg.AppendInt(uint32_t(aCode)); michael@0: NS_WARNING(msg.BeginReading()); michael@0: michael@0: gfx::LogFailure(msg); michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::Render() michael@0: { michael@0: if (mSwapChain->PrepareForRendering() != DeviceOK) { michael@0: return; michael@0: } michael@0: michael@0: deviceManager()->SetupRenderState(); michael@0: michael@0: SetupPipeline(); michael@0: michael@0: if (CompositingDisabled()) { michael@0: static_cast(mRoot->ImplData())->RenderLayer(); michael@0: return; michael@0: } michael@0: michael@0: nsIntRect rect; michael@0: mWidget->GetClientBounds(rect); michael@0: michael@0: device()->Clear(0, nullptr, D3DCLEAR_TARGET, 0x00000000, 0, 0); michael@0: michael@0: device()->BeginScene(); michael@0: michael@0: const nsIntRect *clipRect = mRoot->GetClipRect(); michael@0: RECT r; michael@0: if (clipRect) { michael@0: r.left = (LONG)clipRect->x; michael@0: r.top = (LONG)clipRect->y; michael@0: r.right = (LONG)(clipRect->x + clipRect->width); michael@0: r.bottom = (LONG)(clipRect->y + clipRect->height); michael@0: } else { michael@0: r.left = r.top = 0; michael@0: r.right = rect.width; michael@0: r.bottom = rect.height; michael@0: } michael@0: device()->SetScissorRect(&r); michael@0: michael@0: static_cast(mRoot->ImplData())->RenderLayer(); michael@0: michael@0: if (!mRegionToClear.IsEmpty()) { michael@0: D3DRECT* rects = new D3DRECT[mRegionToClear.GetNumRects()]; michael@0: nsIntRegionRectIterator iter(mRegionToClear); michael@0: const nsIntRect *r; michael@0: size_t i = 0; michael@0: while ((r = iter.Next())) { michael@0: rects[i].x1 = r->x; michael@0: rects[i].y1 = r->y; michael@0: rects[i].x2 = r->x + r->width; michael@0: rects[i].y2 = r->y + r->height; michael@0: i++; michael@0: } michael@0: michael@0: device()->Clear(i, rects, D3DCLEAR_TARGET, michael@0: 0x00000000, 0, 0); michael@0: michael@0: delete [] rects; michael@0: } michael@0: michael@0: device()->EndScene(); michael@0: michael@0: if (!mTarget) { michael@0: const nsIntRect *r; michael@0: for (nsIntRegionRectIterator iter(mClippingRegion); michael@0: (r = iter.Next()) != nullptr;) { michael@0: mSwapChain->Present(*r); michael@0: } michael@0: RecordFrame(); michael@0: PostPresent(); michael@0: } else { michael@0: PaintToTarget(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::SetupPipeline() michael@0: { michael@0: nsIntRect rect; michael@0: mWidget->GetClientBounds(rect); michael@0: michael@0: gfx3DMatrix viewMatrix; michael@0: /* michael@0: * Matrix to transform to viewport space ( <-1.0, 1.0> topleft, michael@0: * <1.0, -1.0> bottomright) michael@0: */ michael@0: viewMatrix._11 = 2.0f / rect.width; michael@0: viewMatrix._22 = -2.0f / rect.height; michael@0: viewMatrix._33 = 0.0f; michael@0: viewMatrix._41 = -1.0f; michael@0: viewMatrix._42 = 1.0f; michael@0: michael@0: HRESULT hr = device()->SetVertexShaderConstantF(CBmProjection, michael@0: &viewMatrix._11, 4); michael@0: michael@0: if (FAILED(hr)) { michael@0: NS_WARNING("Failed to set projection shader constant!"); michael@0: } michael@0: michael@0: hr = device()->SetVertexShaderConstantF(CBvTextureCoords, michael@0: ShaderConstantRect(0, 0, 1.0f, 1.0f), michael@0: 1); michael@0: michael@0: if (FAILED(hr)) { michael@0: NS_WARNING("Failed to set texCoords shader constant!"); michael@0: } michael@0: michael@0: float offset[] = { 0, 0, 0, 0 }; michael@0: hr = device()->SetVertexShaderConstantF(CBvRenderTargetOffset, offset, 1); michael@0: michael@0: if (FAILED(hr)) { michael@0: NS_WARNING("Failed to set RenderTargetOffset shader constant!"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerManagerD3D9::PaintToTarget() michael@0: { michael@0: nsRefPtr backBuff; michael@0: nsRefPtr destSurf; michael@0: device()->GetRenderTarget(0, getter_AddRefs(backBuff)); michael@0: michael@0: D3DSURFACE_DESC desc; michael@0: backBuff->GetDesc(&desc); michael@0: michael@0: device()->CreateOffscreenPlainSurface(desc.Width, desc.Height, michael@0: D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, michael@0: getter_AddRefs(destSurf), nullptr); michael@0: michael@0: device()->GetRenderTargetData(backBuff, destSurf); michael@0: michael@0: D3DLOCKED_RECT rect; michael@0: destSurf->LockRect(&rect, nullptr, D3DLOCK_READONLY); michael@0: michael@0: nsRefPtr imageSurface = michael@0: new gfxImageSurface((unsigned char*)rect.pBits, michael@0: gfxIntSize(desc.Width, desc.Height), michael@0: rect.Pitch, michael@0: gfxImageFormat::ARGB32); michael@0: michael@0: mTarget->SetSource(imageSurface); michael@0: mTarget->SetOperator(gfxContext::OPERATOR_OVER); michael@0: mTarget->Paint(); michael@0: destSurf->UnlockRect(); michael@0: } michael@0: michael@0: LayerD3D9::LayerD3D9(LayerManagerD3D9 *aManager) michael@0: : mD3DManager(aManager) michael@0: { michael@0: } michael@0: michael@0: } /* namespace layers */ michael@0: } /* namespace mozilla */