/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Corporation code. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bas Schouten * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "LayerManagerD3D10.h" #include "LayerManagerD3D10Effect.h" #include "gfxWindowsPlatform.h" #include "gfxD2DSurface.h" #include "gfxFailure.h" #include "cairo-win32.h" #include "dxgi.h" #include "ContainerLayerD3D10.h" #include "ThebesLayerD3D10.h" #include "ColorLayerD3D10.h" #include "CanvasLayerD3D10.h" #include "ReadbackLayerD3D10.h" #include "ImageLayerD3D10.h" #include "../d3d9/Nv3DVUtils.h" #include "gfxCrashReporterUtils.h" namespace mozilla { namespace layers { typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)( void *pData, SIZE_T DataLength, UINT FXFlags, ID3D10Device *pDevice, ID3D10EffectPool *pEffectPool, ID3D10Effect **ppEffect ); struct Vertex { float position[2]; }; // {592BF306-0EED-4F76-9D03-A0846450F472} static const GUID sDeviceAttachments = { 0x592bf306, 0xeed, 0x4f76, { 0x9d, 0x3, 0xa0, 0x84, 0x64, 0x50, 0xf4, 0x72 } }; // {716AEDB1-C9C3-4B4D-8332-6F65D44AF6A8} static const GUID sLayerManagerCount = { 0x716aedb1, 0xc9c3, 0x4b4d, { 0x83, 0x32, 0x6f, 0x65, 0xd4, 0x4a, 0xf6, 0xa8 } }; cairo_user_data_key_t gKeyD3D10Texture; LayerManagerD3D10::LayerManagerD3D10(nsIWidget *aWidget) : mWidget(aWidget) { } struct DeviceAttachments { nsRefPtr mEffect; nsRefPtr mInputLayout; nsRefPtr mVertexBuffer; nsRefPtr mReadbackManager; }; LayerManagerD3D10::~LayerManagerD3D10() { if (mDevice) { int referenceCount = 0; UINT size = sizeof(referenceCount); HRESULT hr = mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount); NS_ASSERTION(SUCCEEDED(hr), "Reference count not found on device."); referenceCount--; mDevice->SetPrivateData(sLayerManagerCount, sizeof(referenceCount), &referenceCount); if (!referenceCount) { DeviceAttachments *attachments; size = sizeof(attachments); mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments); // No LayerManagers left for this device. Clear out interfaces stored which // hold a reference to the device. mDevice->SetPrivateData(sDeviceAttachments, 0, NULL); delete attachments; } } Destroy(); } bool LayerManagerD3D10::Initialize() { ScopedGfxFeatureReporter reporter("D3D10 Layers"); HRESULT hr; /* Create an Nv3DVUtils instance */ if (!mNv3DVUtils) { mNv3DVUtils = new Nv3DVUtils(); if (!mNv3DVUtils) { NS_WARNING("Could not create a new instance of Nv3DVUtils.\n"); } } /* Initialize the Nv3DVUtils object */ if (mNv3DVUtils) { mNv3DVUtils->Initialize(); } mDevice = gfxWindowsPlatform::GetPlatform()->GetD3D10Device(); if (!mDevice) { return false; } /* * Do some post device creation setup */ if (mNv3DVUtils) { IUnknown* devUnknown = NULL; if (mDevice) { mDevice->QueryInterface(IID_IUnknown, (void **)&devUnknown); } mNv3DVUtils->SetDeviceInfo(devUnknown); } int referenceCount = 0; UINT size = sizeof(referenceCount); // If this isn't there yet it'll fail, count will remain 0, which is correct. mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount); referenceCount++; mDevice->SetPrivateData(sLayerManagerCount, sizeof(referenceCount), &referenceCount); DeviceAttachments *attachments; size = sizeof(DeviceAttachments*); if (FAILED(mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments))) { attachments = new DeviceAttachments; mDevice->SetPrivateData(sDeviceAttachments, sizeof(attachments), &attachments); D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc) GetProcAddress(LoadLibraryA("d3d10_1.dll"), "D3D10CreateEffectFromMemory"); if (!createEffect) { return false; } hr = createEffect((void*)g_main, sizeof(g_main), D3D10_EFFECT_SINGLE_THREADED, mDevice, NULL, getter_AddRefs(mEffect)); if (FAILED(hr)) { return false; } attachments->mEffect = mEffect; D3D10_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, }; D3D10_PASS_DESC passDesc; mEffect->GetTechniqueByName("RenderRGBLayerPremul")->GetPassByIndex(0)-> GetDesc(&passDesc); hr = mDevice->CreateInputLayout(layout, sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC), passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, getter_AddRefs(mInputLayout)); if (FAILED(hr)) { return false; } attachments->mInputLayout = mInputLayout; Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} }; CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); D3D10_SUBRESOURCE_DATA data; data.pSysMem = (void*)vertices; hr = mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mVertexBuffer)); if (FAILED(hr)) { return false; } attachments->mVertexBuffer = mVertexBuffer; } else { mEffect = attachments->mEffect; mVertexBuffer = attachments->mVertexBuffer; mInputLayout = attachments->mInputLayout; } nsRefPtr dxgiDevice; nsRefPtr dxgiAdapter; nsRefPtr dxgiFactory; mDevice->QueryInterface(dxgiDevice.StartAssignment()); dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter)); dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.StartAssignment())); DXGI_SWAP_CHAIN_DESC swapDesc; ::ZeroMemory(&swapDesc, sizeof(swapDesc)); swapDesc.BufferDesc.Width = 0; swapDesc.BufferDesc.Height = 0; swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; swapDesc.BufferDesc.RefreshRate.Numerator = 60; swapDesc.BufferDesc.RefreshRate.Denominator = 1; swapDesc.SampleDesc.Count = 1; swapDesc.SampleDesc.Quality = 0; swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapDesc.BufferCount = 1; // We don't really need this flag, however it seems on some NVidia hardware // smaller area windows do not present properly without this flag. This flag // should have no negative consequences by itself. See bug 613790. This flag // is broken on optimus devices. As a temporary solution we don't set it // there, the only way of reliably detecting we're on optimus is looking for // the DLL. See Bug 623807. if (gfxWindowsPlatform::IsOptimus()) { swapDesc.Flags = 0; } else { swapDesc.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE; } swapDesc.OutputWindow = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW); swapDesc.Windowed = TRUE; /** * Create a swap chain, this swap chain will contain the backbuffer for * the window we draw to. The front buffer is the full screen front * buffer. */ hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, getter_AddRefs(mSwapChain)); if (FAILED(hr)) { return false; } // We need this because we don't want DXGI to respond to Alt+Enter. dxgiFactory->MakeWindowAssociation(swapDesc.OutputWindow, DXGI_MWA_NO_WINDOW_CHANGES); reporter.SetSuccessful(); return true; } void LayerManagerD3D10::Destroy() { if (!IsDestroyed()) { if (mRoot) { static_cast(mRoot->ImplData())->LayerManagerDestroyed(); } } LayerManager::Destroy(); } void LayerManagerD3D10::SetRoot(Layer *aRoot) { mRoot = aRoot; } void LayerManagerD3D10::BeginTransaction() { } void LayerManagerD3D10::BeginTransactionWithTarget(gfxContext* aTarget) { mTarget = aTarget; } bool LayerManagerD3D10::EndEmptyTransaction() { if (!mRoot) return false; EndTransaction(nsnull, nsnull); return true; } void LayerManagerD3D10::EndTransaction(DrawThebesLayerCallback aCallback, void* aCallbackData) { if (mRoot) { mCurrentCallbackInfo.Callback = aCallback; mCurrentCallbackInfo.CallbackData = aCallbackData; // The results of our drawing always go directly into a pixel buffer, // so we don't need to pass any global transform here. mRoot->ComputeEffectiveTransforms(gfx3DMatrix()); Render(); mCurrentCallbackInfo.Callback = nsnull; mCurrentCallbackInfo.CallbackData = nsnull; } mTarget = nsnull; } already_AddRefed LayerManagerD3D10::CreateThebesLayer() { nsRefPtr layer = new ThebesLayerD3D10(this); return layer.forget(); } already_AddRefed LayerManagerD3D10::CreateContainerLayer() { nsRefPtr layer = new ContainerLayerD3D10(this); return layer.forget(); } already_AddRefed LayerManagerD3D10::CreateImageLayer() { nsRefPtr layer = new ImageLayerD3D10(this); return layer.forget(); } already_AddRefed LayerManagerD3D10::CreateColorLayer() { nsRefPtr layer = new ColorLayerD3D10(this); return layer.forget(); } already_AddRefed LayerManagerD3D10::CreateCanvasLayer() { nsRefPtr layer = new CanvasLayerD3D10(this); return layer.forget(); } already_AddRefed LayerManagerD3D10::CreateReadbackLayer() { nsRefPtr layer = new ReadbackLayerD3D10(this); return layer.forget(); } already_AddRefed LayerManagerD3D10::CreateImageContainer() { nsRefPtr layer = new ImageContainerD3D10(mDevice); return layer.forget(); } static void ReleaseTexture(void *texture) { static_cast(texture)->Release(); } already_AddRefed LayerManagerD3D10::CreateOptimalSurface(const gfxIntSize &aSize, gfxASurface::gfxImageFormat aFormat) { if ((aFormat != gfxASurface::ImageFormatRGB24 && aFormat != gfxASurface::ImageFormatARGB32)) { return LayerManager::CreateOptimalSurface(aSize, aFormat); } nsRefPtr texture; CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width, aSize.height, 1, 1); desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; desc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE; HRESULT hr = device()->CreateTexture2D(&desc, NULL, getter_AddRefs(texture)); if (FAILED(hr)) { NS_WARNING("Failed to create new texture for CreateOptimalSurface!"); return LayerManager::CreateOptimalSurface(aSize, aFormat); } nsRefPtr surface = new gfxD2DSurface(texture, aFormat == gfxASurface::ImageFormatRGB24 ? gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA); if (!surface || surface->CairoStatus()) { return LayerManager::CreateOptimalSurface(aSize, aFormat); } surface->SetData(&gKeyD3D10Texture, texture.forget().get(), ReleaseTexture); return surface.forget(); } ReadbackManagerD3D10* LayerManagerD3D10::readbackManager() { EnsureReadbackManager(); return mReadbackManager; } void LayerManagerD3D10::SetViewport(const nsIntSize &aViewport) { mViewport = aViewport; D3D10_VIEWPORT viewport; viewport.MaxDepth = 1.0f; viewport.MinDepth = 0; viewport.Width = aViewport.width; viewport.Height = aViewport.height; viewport.TopLeftX = 0; viewport.TopLeftY = 0; mDevice->RSSetViewports(1, &viewport); gfx3DMatrix projection; /* * Matrix to transform to viewport space ( <-1.0, 1.0> topleft, * <1.0, -1.0> bottomright) */ projection._11 = 2.0f / aViewport.width; projection._22 = -2.0f / aViewport.height; projection._33 = 1.0f; projection._41 = -1.0f; projection._42 = 1.0f; projection._44 = 1.0f; HRESULT hr = mEffect->GetVariableByName("mProjection")-> SetRawValue(&projection._11, 0, 64); if (FAILED(hr)) { NS_WARNING("Failed to set projection matrix."); } } void LayerManagerD3D10::SetupPipeline() { VerifyBufferSize(); UpdateRenderTarget(); nsIntRect rect; mWidget->GetClientBounds(rect); HRESULT hr; hr = mEffect->GetVariableByName("vTextureCoords")->AsVector()-> SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); if (FAILED(hr)) { NS_WARNING("Failed to set Texture Coordinates."); return; } ID3D10RenderTargetView *view = mRTView; mDevice->OMSetRenderTargets(1, &view, NULL); mDevice->IASetInputLayout(mInputLayout); UINT stride = sizeof(Vertex); UINT offset = 0; ID3D10Buffer *buffer = mVertexBuffer; mDevice->IASetVertexBuffers(0, 1, &buffer, &stride, &offset); mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); SetViewport(nsIntSize(rect.width, rect.height)); } void LayerManagerD3D10::UpdateRenderTarget() { if (mRTView) { return; } HRESULT hr; nsRefPtr backBuf; hr = mSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (void**)backBuf.StartAssignment()); if (FAILED(hr)) { return; } mDevice->CreateRenderTargetView(backBuf, NULL, getter_AddRefs(mRTView)); } void LayerManagerD3D10::VerifyBufferSize() { DXGI_SWAP_CHAIN_DESC swapDesc; mSwapChain->GetDesc(&swapDesc); nsIntRect rect; mWidget->GetClientBounds(rect); if (swapDesc.BufferDesc.Width == rect.width && swapDesc.BufferDesc.Height == rect.height) { return; } mRTView = nsnull; if (gfxWindowsPlatform::IsOptimus()) { mSwapChain->ResizeBuffers(1, rect.width, rect.height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); } else { mSwapChain->ResizeBuffers(1, rect.width, rect.height, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE); } } void LayerManagerD3D10::EnsureReadbackManager() { if (mReadbackManager) { return; } DeviceAttachments *attachments; UINT size = sizeof(DeviceAttachments*); if (FAILED(mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments))) { // Strange! This shouldn't happen ... return a readback manager for this // layer manager only. mReadbackManager = new ReadbackManagerD3D10(); gfx::LogFailure(NS_LITERAL_CSTRING("Couldn't get device attachments for device.")); return; } if (attachments->mReadbackManager) { mReadbackManager = attachments->mReadbackManager; return; } mReadbackManager = new ReadbackManagerD3D10(); attachments->mReadbackManager = mReadbackManager; } void LayerManagerD3D10::Render() { static_cast(mRoot->ImplData())->Validate(); SetupPipeline(); float black[] = { 0, 0, 0, 0 }; device()->ClearRenderTargetView(mRTView, black); nsIntRect rect; mWidget->GetClientBounds(rect); const nsIntRect *clipRect = mRoot->GetClipRect(); D3D10_RECT r; if (clipRect) { r.left = (LONG)clipRect->x; r.top = (LONG)clipRect->y; r.right = (LONG)(clipRect->x + clipRect->width); r.bottom = (LONG)(clipRect->y + clipRect->height); } else { r.left = r.top = 0; r.right = rect.width; r.bottom = rect.height; } device()->RSSetScissorRects(1, &r); static_cast(mRoot->ImplData())->RenderLayer(); if (mTarget) { PaintToTarget(); } else { mSwapChain->Present(0, 0); } } void LayerManagerD3D10::PaintToTarget() { nsRefPtr backBuf; mSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (void**)backBuf.StartAssignment()); D3D10_TEXTURE2D_DESC bbDesc; backBuf->GetDesc(&bbDesc); CD3D10_TEXTURE2D_DESC softDesc(bbDesc.Format, bbDesc.Width, bbDesc.Height); softDesc.MipLevels = 1; softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_READ; softDesc.Usage = D3D10_USAGE_STAGING; softDesc.BindFlags = 0; nsRefPtr readTexture; device()->CreateTexture2D(&softDesc, NULL, getter_AddRefs(readTexture)); device()->CopyResource(readTexture, backBuf); D3D10_MAPPED_TEXTURE2D map; readTexture->Map(0, D3D10_MAP_READ, 0, &map); nsRefPtr tmpSurface = new gfxImageSurface((unsigned char*)map.pData, gfxIntSize(bbDesc.Width, bbDesc.Height), map.RowPitch, gfxASurface::ImageFormatARGB32); mTarget->SetSource(tmpSurface); mTarget->SetOperator(gfxContext::OPERATOR_OVER); mTarget->Paint(); readTexture->Unmap(0); } void LayerManagerD3D10::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(PRUint32(aCode)); NS_WARNING(msg.BeginReading()); gfx::LogFailure(msg); } LayerD3D10::LayerD3D10(LayerManagerD3D10 *aManager) : mD3DManager(aManager) { } } }