From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- gfx/layers/d3d11/MLGDeviceD3D11.cpp | 2034 +++++++++++++++++++++++++++++++++++ 1 file changed, 2034 insertions(+) create mode 100644 gfx/layers/d3d11/MLGDeviceD3D11.cpp (limited to 'gfx/layers/d3d11/MLGDeviceD3D11.cpp') diff --git a/gfx/layers/d3d11/MLGDeviceD3D11.cpp b/gfx/layers/d3d11/MLGDeviceD3D11.cpp new file mode 100644 index 0000000000..219662ead1 --- /dev/null +++ b/gfx/layers/d3d11/MLGDeviceD3D11.cpp @@ -0,0 +1,2034 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "MLGDeviceD3D11.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Telemetry.h" +#include "mozilla/WindowsVersion.h" +#include "mozilla/gfx/GPUParent.h" +#include "mozilla/gfx/StackArray.h" +#include "mozilla/layers/DiagnosticsD3D11.h" +#include "mozilla/layers/HelpersD3D11.h" +#include "mozilla/layers/LayerMLGPU.h" +#include "mozilla/layers/MemoryReportingMLGPU.h" +#include "mozilla/layers/ShaderDefinitionsMLGPU.h" +#include "mozilla/layers/UtilityMLGPU.h" +#include "mozilla/widget/CompositorWidget.h" +#include "mozilla/widget/WinCompositorWidget.h" +#include "MLGShaders.h" +#include "TextureD3D11.h" +#include "gfxConfig.h" +#include "mozilla/StaticPrefs_layers.h" +#include "FxROutputHandler.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; +using namespace mozilla::widget; +using namespace mozilla::layers::mlg; + +// Defined in CompositorD3D11.cpp. +bool CanUsePartialPresents(ID3D11Device* aDevice); + +static D3D11_BOX RectToBox(const gfx::IntRect& aRect); + +MLGRenderTargetD3D11::MLGRenderTargetD3D11(const gfx::IntSize& aSize, + MLGRenderTargetFlags aFlags) + : MLGRenderTarget(aFlags), mSize(aSize) {} + +MLGRenderTargetD3D11::~MLGRenderTargetD3D11() { + if (mDepthBuffer) { + sRenderTargetUsage -= mSize.width * mSize.height * 1; + } + ForgetTexture(); +} + +bool MLGRenderTargetD3D11::Initialize(ID3D11Device* aDevice) { + D3D11_TEXTURE2D_DESC desc; + ::ZeroMemory(&desc, sizeof(desc)); + desc.Width = mSize.width; + desc.Height = mSize.height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.SampleDesc.Count = 1; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + + RefPtr texture; + HRESULT hr = + aDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture)); + if (FAILED(hr) || !texture) { + gfxCriticalNote << "Failed to create render target texture: " << hexa(hr); + return false; + } + + return Initialize(aDevice, texture); +} + +bool MLGRenderTargetD3D11::Initialize(ID3D11Device* aDevice, + ID3D11Texture2D* aTexture) { + if (!UpdateTexture(aTexture)) { + return false; + } + if ((mFlags & MLGRenderTargetFlags::ZBuffer) && !CreateDepthBuffer(aDevice)) { + return false; + } + return true; +} + +bool MLGRenderTargetD3D11::UpdateTexture(ID3D11Texture2D* aTexture) { + // Save the view first, in case we can re-use it. + RefPtr view = mRTView.forget(); + + ForgetTexture(); + + if (!aTexture) { + return true; + } + +#ifdef DEBUG + D3D11_TEXTURE2D_DESC desc; + aTexture->GetDesc(&desc); + MOZ_ASSERT(desc.Width == mSize.width && desc.Height == mSize.height); +#endif + + RefPtr device; + aTexture->GetDevice(getter_AddRefs(device)); + + if (view) { + // Check that the view matches the backing texture. + RefPtr resource; + view->GetResource(getter_AddRefs(resource)); + if (resource != aTexture) { + view = nullptr; + } + } + + // If we couldn't re-use a view from before, make one now. + if (!view) { + HRESULT hr = + device->CreateRenderTargetView(aTexture, nullptr, getter_AddRefs(view)); + if (FAILED(hr) || !view) { + gfxCriticalNote << "Failed to create render target view: " << hexa(hr); + return false; + } + } + + mTexture = aTexture; + mRTView = view.forget(); + sRenderTargetUsage += mSize.width * mSize.height * 4; + return true; +} + +void MLGRenderTargetD3D11::ForgetTexture() { + if (mTexture) { + sRenderTargetUsage -= mSize.width * mSize.height * 4; + mTexture = nullptr; + } + mRTView = nullptr; + mTextureSource = nullptr; +} + +bool MLGRenderTargetD3D11::CreateDepthBuffer(ID3D11Device* aDevice) { + D3D11_TEXTURE2D_DESC desc; + ::ZeroMemory(&desc, sizeof(desc)); + desc.Width = mSize.width; + desc.Height = mSize.height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_D32_FLOAT; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + + RefPtr buffer; + HRESULT hr = aDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(buffer)); + if (FAILED(hr) || !buffer) { + gfxCriticalNote << "Could not create depth-stencil buffer: " << hexa(hr); + return false; + } + + D3D11_DEPTH_STENCIL_VIEW_DESC viewDesc; + ::ZeroMemory(&viewDesc, sizeof(viewDesc)); + viewDesc.Format = DXGI_FORMAT_D32_FLOAT; + viewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + + RefPtr dsv; + hr = aDevice->CreateDepthStencilView(buffer, &viewDesc, getter_AddRefs(dsv)); + if (FAILED(hr) || !dsv) { + gfxCriticalNote << "Could not create depth-stencil view: " << hexa(hr); + return false; + } + + mDepthBuffer = buffer; + mDepthStencilView = dsv; + sRenderTargetUsage += mSize.width * mSize.height * 1; + return true; +} + +ID3D11DepthStencilView* MLGRenderTargetD3D11::GetDSV() { + return mDepthStencilView; +} + +ID3D11RenderTargetView* MLGRenderTargetD3D11::GetRenderTargetView() { + return mRTView; +} + +IntSize MLGRenderTargetD3D11::GetSize() const { return mSize; } + +MLGTexture* MLGRenderTargetD3D11::GetTexture() { + if (!mTextureSource) { + mTextureSource = new MLGTextureD3D11(mTexture); + } + return mTextureSource; +} + +MLGSwapChainD3D11::MLGSwapChainD3D11(MLGDeviceD3D11* aParent, + ID3D11Device* aDevice) + : mParent(aParent), + mDevice(aDevice), + mWidget(nullptr), + mCanUsePartialPresents(CanUsePartialPresents(aDevice)) {} + +MLGSwapChainD3D11::~MLGSwapChainD3D11() {} + +void MLGSwapChainD3D11::Destroy() { + if (mRT == mParent->GetRenderTarget()) { + mParent->SetRenderTarget(nullptr); + } + mWidget = nullptr; + mRT = nullptr; + mSwapChain = nullptr; + mSwapChain1 = nullptr; +} + +RefPtr MLGSwapChainD3D11::Create(MLGDeviceD3D11* aParent, + ID3D11Device* aDevice, + CompositorWidget* aWidget) { + RefPtr swapChain = new MLGSwapChainD3D11(aParent, aDevice); + if (!swapChain->Initialize(aWidget)) { + return nullptr; + } + return swapChain.forget(); +} + +bool MLGSwapChainD3D11::Initialize(CompositorWidget* aWidget) { + HWND hwnd = aWidget->AsWindows()->GetHwnd(); + + RefPtr dxgiDevice; + mDevice->QueryInterface(dxgiDevice.StartAssignment()); + + RefPtr dxgiFactory; + { + RefPtr adapter; + dxgiDevice->GetAdapter(getter_AddRefs(adapter)); + + adapter->GetParent(IID_PPV_ARGS(dxgiFactory.StartAssignment())); + } + + RefPtr dxgiFactory2; + if (gfxVars::UseDoubleBufferingWithCompositor() && + SUCCEEDED(dxgiFactory->QueryInterface(dxgiFactory2.StartAssignment())) && + dxgiFactory2 && XRE_IsGPUProcess()) { + // DXGI_SCALING_NONE is not available on Windows 7 with the Platform Update: + // This looks awful for things like the awesome bar and browser window + // resizing, so we don't use a flip buffer chain here. (Note when using + // EFFECT_SEQUENTIAL Windows doesn't stretch the surface when resizing). + // + // We choose not to run this on platforms earlier than Windows 10 because + // it appears sometimes this breaks our ability to test ASAP compositing, + // which breaks Talos. + // + // When the GPU process is disabled we don't have a compositor window which + // can lead to issues with Window re-use so we don't use this. + DXGI_SWAP_CHAIN_DESC1 desc; + ::ZeroMemory(&desc, sizeof(desc)); + desc.Width = 0; + desc.Height = 0; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = 2; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + desc.Scaling = DXGI_SCALING_NONE; + desc.Flags = 0; + + HRESULT hr = dxgiFactory2->CreateSwapChainForHwnd( + mDevice, hwnd, &desc, nullptr, nullptr, getter_AddRefs(mSwapChain1)); + if (SUCCEEDED(hr) && mSwapChain1) { + DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f}; + mSwapChain1->SetBackgroundColor(&color); + mSwapChain = mSwapChain1; + mIsDoubleBuffered = true; + } else if (aWidget->AsWindows()->GetCompositorHwnd()) { + // Destroy compositor window. + aWidget->AsWindows()->DestroyCompositorWindow(); + hwnd = aWidget->AsWindows()->GetHwnd(); + } + } + + if (!mSwapChain) { + 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; + swapDesc.OutputWindow = hwnd; + swapDesc.Windowed = TRUE; + swapDesc.Flags = 0; + swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; + + HRESULT hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, + getter_AddRefs(mSwapChain)); + if (FAILED(hr)) { + gfxCriticalNote << "Could not create swap chain: " << hexa(hr); + return false; + } + + // Try to get an IDXGISwapChain1 if we can, for partial presents. + mSwapChain->QueryInterface(mSwapChain1.StartAssignment()); + } + + // We need this because we don't want DXGI to respond to Alt+Enter. + dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES); + mWidget = aWidget; + return true; +} + +RefPtr MLGSwapChainD3D11::AcquireBackBuffer() { + RefPtr texture; + HRESULT hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), + getter_AddRefs(texture)); + if (hr == DXGI_ERROR_INVALID_CALL && + mDevice->GetDeviceRemovedReason() != S_OK) { + // This can happen on some drivers when there's a TDR. + mParent->HandleDeviceReset("SwapChain::GetBuffer"); + return nullptr; + } + if (FAILED(hr)) { + gfxCriticalNote << "Failed to acquire swap chain's backbuffer: " + << hexa(hr); + return nullptr; + } + + if (!mRT) { + MLGRenderTargetFlags flags = MLGRenderTargetFlags::Default; + if (StaticPrefs::layers_mlgpu_enable_depth_buffer_AtStartup()) { + flags |= MLGRenderTargetFlags::ZBuffer; + } + + mRT = new MLGRenderTargetD3D11(mSize, flags); + if (!mRT->Initialize(mDevice, nullptr)) { + return nullptr; + } + } + + if (!mRT->UpdateTexture(texture)) { + return nullptr; + } + + if (mIsDoubleBuffered) { + UpdateBackBufferContents(texture); + } + return mRT; +} + +void MLGSwapChainD3D11::UpdateBackBufferContents(ID3D11Texture2D* aBack) { + MOZ_ASSERT(mIsDoubleBuffered); + + // The front region contains the newly invalid region for this frame. The + // back region contains that, plus the region that was only drawn into the + // back buffer on the previous frame. Thus by subtracting the two, we can + // find the region that needs to be copied from the front buffer to the + // back. We do this so we don't have to re-render those pixels. + nsIntRegion frontValid; + frontValid.Sub(mBackBufferInvalid, mFrontBufferInvalid); + if (frontValid.IsEmpty()) { + return; + } + + RefPtr front; + HRESULT hr = mSwapChain->GetBuffer(1, __uuidof(ID3D11Texture2D), + getter_AddRefs(front)); + if (FAILED(hr) || !front) { + return; + } + + RefPtr context; + mDevice->GetImmediateContext(getter_AddRefs(context)); + + for (auto iter = frontValid.RectIter(); !iter.Done(); iter.Next()) { + const IntRect& rect = iter.Get(); + D3D11_BOX box = RectToBox(rect); + context->CopySubresourceRegion(aBack, 0, rect.X(), rect.Y(), 0, front, 0, + &box); + } + + // The back and front buffers are now in sync. + mBackBufferInvalid = mFrontBufferInvalid; + MOZ_ASSERT(!mBackBufferInvalid.IsEmpty()); +} + +bool MLGSwapChainD3D11::ResizeBuffers(const IntSize& aSize) { + // We have to clear all references to the old backbuffer before resizing. + mRT = nullptr; + + // Clear the size before re-allocating. If allocation fails we want to try + // again, because we had to sacrifice our original backbuffer to try + // resizing. + mSize = IntSize(0, 0); + + HRESULT hr = mSwapChain->ResizeBuffers(0, aSize.width, aSize.height, + DXGI_FORMAT_B8G8R8A8_UNORM, 0); + if (hr == DXGI_ERROR_DEVICE_REMOVED) { + mParent->HandleDeviceReset("ResizeBuffers"); + return false; + } + if (FAILED(hr)) { + gfxCriticalNote << "Failed to resize swap chain buffers: " << hexa(hr); + return false; + } + + mSize = aSize; + mBackBufferInvalid = IntRect(IntPoint(0, 0), mSize); + mFrontBufferInvalid = IntRect(IntPoint(0, 0), mSize); + return true; +} + +IntSize MLGSwapChainD3D11::GetSize() const { return mSize; } + +void MLGSwapChainD3D11::Present() { + MOZ_ASSERT(!mBackBufferInvalid.IsEmpty()); + MOZ_ASSERT(mBackBufferInvalid.GetNumRects() > 0); + + // See bug 1260611 comment #28 for why we do this. + mParent->InsertPresentWaitQuery(); + + if (mWidget->AsWindows()->HasFxrOutputHandler()) { + // There is a Firefox Reality handler for this swapchain. Update this + // window's contents to the VR window. + FxROutputHandler* fxrHandler = mWidget->AsWindows()->GetFxrOutputHandler(); + if (fxrHandler->TryInitialize(mSwapChain, mDevice)) { + RefPtr context; + mDevice->GetImmediateContext(getter_AddRefs(context)); + fxrHandler->UpdateOutput(context); + } + } + + HRESULT hr; + if (mCanUsePartialPresents && mSwapChain1) { + StackArray rects(mBackBufferInvalid.GetNumRects()); + size_t i = 0; + for (auto iter = mBackBufferInvalid.RectIter(); !iter.Done(); iter.Next()) { + const IntRect& rect = iter.Get(); + rects[i].left = rect.X(); + rects[i].top = rect.Y(); + rects[i].bottom = rect.YMost(); + rects[i].right = rect.XMost(); + i++; + } + + DXGI_PRESENT_PARAMETERS params; + PodZero(¶ms); + params.DirtyRectsCount = mBackBufferInvalid.GetNumRects(); + params.pDirtyRects = rects.data(); + hr = mSwapChain1->Present1(0, 0, ¶ms); + } else { + hr = mSwapChain->Present(0, 0); + } + + if (hr == DXGI_ERROR_DEVICE_REMOVED) { + mParent->HandleDeviceReset("Present"); + } + + if (FAILED(hr)) { + gfxCriticalNote << "D3D11 swap chain failed to present: " << hexa(hr); + } + + if (mIsDoubleBuffered) { + // Both the front and back buffer invalid regions are in sync, but now the + // presented buffer (the front buffer) is clean, so we clear its invalid + // region. The back buffer that will be used next frame however is now + // dirty. + MOZ_ASSERT(mFrontBufferInvalid.GetBounds() == + mBackBufferInvalid.GetBounds()); + mFrontBufferInvalid.SetEmpty(); + } else { + mBackBufferInvalid.SetEmpty(); + } + mLastPresentSize = mSize; + + // Note: this waits on the query we inserted in the previous frame, + // not the one we just inserted now. Example: + // Insert query #1 + // Present #1 + // (first frame, no wait) + // Insert query #2 + // Present #2 + // Wait for query #1. + // Insert query #3 + // Present #3 + // Wait for query #2. + // + // This ensures we're done reading textures before swapping buffers. + mParent->WaitForPreviousPresentQuery(); +} + +void MLGSwapChainD3D11::ForcePresent() { + DXGI_SWAP_CHAIN_DESC desc; + mSwapChain->GetDesc(&desc); + + LayoutDeviceIntSize size = mWidget->GetClientSize(); + + if (desc.BufferDesc.Width != size.width || + desc.BufferDesc.Height != size.height) { + return; + } + + mSwapChain->Present(0, 0); + if (mIsDoubleBuffered) { + // Make sure we present the old front buffer since we know it is completely + // valid. This non-vsynced present should be pretty much 'free' for a flip + // chain. + mSwapChain->Present(0, 0); + } + + mLastPresentSize = mSize; +} + +void MLGSwapChainD3D11::CopyBackbuffer(gfx::DrawTarget* aTarget, + const gfx::IntRect& aBounds) { + RefPtr context; + mDevice->GetImmediateContext(getter_AddRefs(context)); + + RefPtr backbuffer; + HRESULT hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), + (void**)backbuffer.StartAssignment()); + if (FAILED(hr)) { + gfxWarning() << "Failed to acquire swapchain backbuffer: " << hexa(hr); + return; + } + + D3D11_TEXTURE2D_DESC bbDesc; + backbuffer->GetDesc(&bbDesc); + + CD3D11_TEXTURE2D_DESC tempDesc(bbDesc.Format, bbDesc.Width, bbDesc.Height); + tempDesc.MipLevels = 1; + tempDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + tempDesc.Usage = D3D11_USAGE_STAGING; + tempDesc.BindFlags = 0; + + RefPtr temp; + hr = mDevice->CreateTexture2D(&tempDesc, nullptr, getter_AddRefs(temp)); + if (FAILED(hr)) { + gfxWarning() << "Failed to create a temporary texture for PresentAndCopy: " + << hexa(hr); + return; + } + + context->CopyResource(temp, backbuffer); + + D3D11_MAPPED_SUBRESOURCE map; + hr = context->Map(temp, 0, D3D11_MAP_READ, 0, &map); + if (FAILED(hr)) { + gfxWarning() << "Failed to map temporary texture for PresentAndCopy: " + << hexa(hr); + return; + } + + RefPtr source = Factory::CreateWrappingDataSourceSurface( + (uint8_t*)map.pData, map.RowPitch, IntSize(bbDesc.Width, bbDesc.Height), + SurfaceFormat::B8G8R8A8); + + aTarget->CopySurface(source, IntRect(0, 0, bbDesc.Width, bbDesc.Height), + IntPoint(-aBounds.X(), -aBounds.Y())); + aTarget->Flush(); + + context->Unmap(temp, 0); +} + +RefPtr MLGBufferD3D11::Create(ID3D11Device* aDevice, + MLGBufferType aType, + uint32_t aSize, MLGUsage aUsage, + const void* aInitialData) { + D3D11_BUFFER_DESC desc; + desc.ByteWidth = aSize; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + + switch (aUsage) { + case MLGUsage::Dynamic: + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + break; + case MLGUsage::Immutable: + desc.Usage = D3D11_USAGE_IMMUTABLE; + desc.CPUAccessFlags = 0; + break; + default: + MOZ_ASSERT_UNREACHABLE("Unknown buffer usage type"); + return nullptr; + } + + switch (aType) { + case MLGBufferType::Vertex: + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + break; + case MLGBufferType::Constant: + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + break; + default: + MOZ_ASSERT_UNREACHABLE("Unknown buffer type"); + return nullptr; + } + + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = aInitialData; + data.SysMemPitch = aSize; + data.SysMemSlicePitch = 0; + + RefPtr buffer; + HRESULT hr = aDevice->CreateBuffer(&desc, aInitialData ? &data : nullptr, + getter_AddRefs(buffer)); + if (FAILED(hr) || !buffer) { + gfxCriticalError() << "Failed to create ID3D11Buffer."; + return nullptr; + } + + return new MLGBufferD3D11(buffer, aType, aSize); +} + +MLGBufferD3D11::MLGBufferD3D11(ID3D11Buffer* aBuffer, MLGBufferType aType, + size_t aSize) + : mBuffer(aBuffer), mType(aType), mSize(aSize) { + switch (mType) { + case MLGBufferType::Vertex: + mlg::sVertexBufferUsage += mSize; + break; + case MLGBufferType::Constant: + mlg::sConstantBufferUsage += mSize; + break; + } +} + +MLGBufferD3D11::~MLGBufferD3D11() { + switch (mType) { + case MLGBufferType::Vertex: + mlg::sVertexBufferUsage -= mSize; + break; + case MLGBufferType::Constant: + mlg::sConstantBufferUsage -= mSize; + break; + } +} + +MLGTextureD3D11::MLGTextureD3D11(ID3D11Texture2D* aTexture) + : mTexture(aTexture) { + D3D11_TEXTURE2D_DESC desc; + aTexture->GetDesc(&desc); + + mSize.width = desc.Width; + mSize.height = desc.Height; +} + +/* static */ +RefPtr MLGTextureD3D11::Create(ID3D11Device* aDevice, + const gfx::IntSize& aSize, + gfx::SurfaceFormat aFormat, + MLGUsage aUsage, + MLGTextureFlags aFlags) { + D3D11_TEXTURE2D_DESC desc; + ::ZeroMemory(&desc, sizeof(desc)); + desc.Width = aSize.width; + desc.Height = aSize.height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.SampleDesc.Count = 1; + + switch (aFormat) { + case SurfaceFormat::B8G8R8A8: + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + break; + default: + MOZ_ASSERT_UNREACHABLE("Unsupported surface format"); + return nullptr; + } + + switch (aUsage) { + case MLGUsage::Immutable: + desc.Usage = D3D11_USAGE_IMMUTABLE; + break; + case MLGUsage::Default: + desc.Usage = D3D11_USAGE_DEFAULT; + break; + case MLGUsage::Dynamic: + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + break; + case MLGUsage::Staging: + desc.Usage = D3D11_USAGE_STAGING; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + break; + default: + MOZ_ASSERT_UNREACHABLE("Unsupported usage type"); + break; + } + + if (aFlags & MLGTextureFlags::ShaderResource) { + desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + } + if (aFlags & MLGTextureFlags::RenderTarget) { + desc.BindFlags |= D3D11_BIND_RENDER_TARGET; + } + + RefPtr texture; + HRESULT hr = + aDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture)); + if (FAILED(hr) || !texture) { + gfxCriticalNote << "Failed to create 2D texture: " << hexa(hr); + return nullptr; + } + + ReportTextureMemoryUsage(texture, aSize.width * aSize.height * 4); + + return new MLGTextureD3D11(texture); +} + +ID3D11ShaderResourceView* MLGTextureD3D11::GetShaderResourceView() { + if (!mView) { + RefPtr device; + mTexture->GetDevice(getter_AddRefs(device)); + + HRESULT hr = device->CreateShaderResourceView(mTexture, nullptr, + getter_AddRefs(mView)); + if (FAILED(hr) || !mView) { + gfxWarning() << "Could not create shader resource view: " << hexa(hr); + return nullptr; + } + } + return mView; +} + +MLGDeviceD3D11::MLGDeviceD3D11(ID3D11Device* aDevice) + : mDevice(aDevice), mScissored(false) {} + +MLGDeviceD3D11::~MLGDeviceD3D11() { + // Caller should have unlocked all textures after presenting. + MOZ_ASSERT(mLockedTextures.IsEmpty()); + MOZ_ASSERT(mLockAttemptedTextures.IsEmpty()); +} + +bool MLGDeviceD3D11::Initialize() { + if (!mDevice) { + return Fail("FEATURE_FAILURE_NO_DEVICE"); + } + + if (mDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) { + return Fail("FEATURE_FAILURE_NEED_LEVEL_10_0"); + } + + mDevice->GetImmediateContext(getter_AddRefs(mCtx)); + if (!mCtx) { + return Fail("FEATURE_FAILURE_NO_CONTEXT"); + } + + mCtx->QueryInterface((ID3D11DeviceContext1**)getter_AddRefs(mCtx1)); + + if (mCtx1) { + // Windows 7 can have Direct3D 11.1 if the platform update is installed, + // but according to some NVIDIA presentations it is known to be buggy. + // It's not clear whether that only refers to command list emulation, + // or whether it just has performance penalties. To be safe we only use + // it on Windows 8 or higher. + // + // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/directx-feature-improvements-in-windows-8#buffers + D3D11_FEATURE_DATA_D3D11_OPTIONS options; + HRESULT hr = mDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, + &options, sizeof(options)); + if (SUCCEEDED(hr)) { + if (IsWin8OrLater()) { + mCanUseConstantBufferOffsetBinding = + (options.ConstantBufferOffsetting != FALSE); + } else { + gfxConfig::EnableFallback(Fallback::NO_CONSTANT_BUFFER_OFFSETTING, + "Unsupported by driver"); + } + mCanUseClearView = (options.ClearView != FALSE); + } else { + gfxCriticalNote << "Failed to query D3D11.1 feature support: " + << hexa(hr); + } + } + + // Get capabilities. + switch (mDevice->GetFeatureLevel()) { + case D3D_FEATURE_LEVEL_11_1: + case D3D_FEATURE_LEVEL_11_0: + mMaxConstantBufferBindSize = D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16; + break; + case D3D_FEATURE_LEVEL_10_1: + case D3D_FEATURE_LEVEL_10_0: + mMaxConstantBufferBindSize = D3D10_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16; + break; + default: + MOZ_ASSERT_UNREACHABLE("Unknown feature level"); + } + + mDiagnostics = MakeUnique(mDevice, mCtx); + + { + struct Vertex2D { + float x; + float y; + }; + Vertex2D vertices[] = {{0, 0}, {1.0f, 0}, {0, 1.0f}, {1.0f, 1.0f}}; + mUnitQuadVB = CreateBuffer(MLGBufferType::Vertex, sizeof(Vertex2D) * 4, + MLGUsage::Immutable, &vertices); + if (!mUnitQuadVB) { + return Fail("FEATURE_FAILURE_UNIT_QUAD_BUFFER"); + } + } + + { + struct Vertex3D { + float x; + float y; + float z; + }; + Vertex3D vertices[3] = { + {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}; + mUnitTriangleVB = CreateBuffer(MLGBufferType::Vertex, sizeof(Vertex3D) * 3, + MLGUsage::Immutable, &vertices); + if (!mUnitTriangleVB) { + return Fail("FEATURE_FAILURE_UNIT_TRIANGLE_BUFFER"); + } + } + + // Define pixel shaders. +#define LAZY_PS(cxxName, enumName) \ + mLazyPixelShaders[PixelShaderID::enumName] = &s##cxxName; + LAZY_PS(TexturedVertexRGB, TexturedVertexRGB); + LAZY_PS(TexturedVertexRGBA, TexturedVertexRGBA); + LAZY_PS(TexturedQuadRGB, TexturedQuadRGB); + LAZY_PS(TexturedQuadRGBA, TexturedQuadRGBA); + LAZY_PS(ColoredQuadPS, ColoredQuad); + LAZY_PS(ColoredVertexPS, ColoredVertex); + LAZY_PS(ComponentAlphaQuadPS, ComponentAlphaQuad); + LAZY_PS(ComponentAlphaVertexPS, ComponentAlphaVertex); + LAZY_PS(TexturedVertexIMC4, TexturedVertexIMC4); + LAZY_PS(TexturedVertexIdentityIMC4, TexturedVertexIdentityIMC4); + LAZY_PS(TexturedVertexNV12, TexturedVertexNV12); + LAZY_PS(TexturedQuadIMC4, TexturedQuadIMC4); + LAZY_PS(TexturedQuadIdentityIMC4, TexturedQuadIdentityIMC4); + LAZY_PS(TexturedQuadNV12, TexturedQuadNV12); + LAZY_PS(BlendMultiplyPS, BlendMultiply); + LAZY_PS(BlendScreenPS, BlendScreen); + LAZY_PS(BlendOverlayPS, BlendOverlay); + LAZY_PS(BlendDarkenPS, BlendDarken); + LAZY_PS(BlendLightenPS, BlendLighten); + LAZY_PS(BlendColorDodgePS, BlendColorDodge); + LAZY_PS(BlendColorBurnPS, BlendColorBurn); + LAZY_PS(BlendHardLightPS, BlendHardLight); + LAZY_PS(BlendSoftLightPS, BlendSoftLight); + LAZY_PS(BlendDifferencePS, BlendDifference); + LAZY_PS(BlendExclusionPS, BlendExclusion); + LAZY_PS(BlendHuePS, BlendHue); + LAZY_PS(BlendSaturationPS, BlendSaturation); + LAZY_PS(BlendColorPS, BlendColor); + LAZY_PS(BlendLuminosityPS, BlendLuminosity); + LAZY_PS(ClearPS, Clear); + LAZY_PS(MaskCombinerPS, MaskCombiner); + LAZY_PS(DiagnosticTextPS, DiagnosticText); +#undef LAZY_PS + + // Define vertex shaders. +#define LAZY_VS(cxxName, enumName) \ + mLazyVertexShaders[VertexShaderID::enumName] = &s##cxxName; + LAZY_VS(TexturedQuadVS, TexturedQuad); + LAZY_VS(TexturedVertexVS, TexturedVertex); + LAZY_VS(BlendVertexVS, BlendVertex); + LAZY_VS(ColoredQuadVS, ColoredQuad); + LAZY_VS(ColoredVertexVS, ColoredVertex); + LAZY_VS(ClearVS, Clear); + LAZY_VS(MaskCombinerVS, MaskCombiner); + LAZY_VS(DiagnosticTextVS, DiagnosticText); +#undef LAZY_VS + + // Force critical shaders to initialize early. + if (!InitPixelShader(PixelShaderID::TexturedQuadRGB) || + !InitPixelShader(PixelShaderID::TexturedQuadRGBA) || + !InitPixelShader(PixelShaderID::ColoredQuad) || + !InitPixelShader(PixelShaderID::ComponentAlphaQuad) || + !InitPixelShader(PixelShaderID::Clear) || + !InitVertexShader(VertexShaderID::TexturedQuad) || + !InitVertexShader(VertexShaderID::ColoredQuad) || + !InitVertexShader(VertexShaderID::Clear)) { + return Fail("FEATURE_FAILURE_CRITICAL_SHADER_FAILURE"); + } + + // Common unit quad layout: vPos, vRect, vLayerIndex, vDepth +#define BASE_UNIT_QUAD_LAYOUT \ + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, \ + 0}, \ + {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, \ + 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, \ + 1}, \ + {"TEXCOORD", \ + 1, \ + DXGI_FORMAT_R32_UINT, \ + 1, \ + D3D11_APPEND_ALIGNED_ELEMENT, \ + D3D11_INPUT_PER_INSTANCE_DATA, \ + 1}, \ + { \ + "TEXCOORD", 2, DXGI_FORMAT_R32_SINT, 1, D3D11_APPEND_ALIGNED_ELEMENT, \ + D3D11_INPUT_PER_INSTANCE_DATA, 1 \ + } + + // Common unit triangle layout: vUnitPos, vPos1-3, vLayerIndex, vDepth +#define BASE_UNIT_TRIANGLE_LAYOUT \ + {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, \ + 0, 0, D3D11_INPUT_PER_VERTEX_DATA, \ + 0}, \ + {"POSITION", 1, DXGI_FORMAT_R32G32_FLOAT, \ + 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, \ + 1}, \ + {"POSITION", \ + 2, \ + DXGI_FORMAT_R32G32_FLOAT, \ + 1, \ + D3D11_APPEND_ALIGNED_ELEMENT, \ + D3D11_INPUT_PER_INSTANCE_DATA, \ + 1}, \ + {"POSITION", \ + 3, \ + DXGI_FORMAT_R32G32_FLOAT, \ + 1, \ + D3D11_APPEND_ALIGNED_ELEMENT, \ + D3D11_INPUT_PER_INSTANCE_DATA, \ + 1}, \ + {"TEXCOORD", \ + 0, \ + DXGI_FORMAT_R32_UINT, \ + 1, \ + D3D11_APPEND_ALIGNED_ELEMENT, \ + D3D11_INPUT_PER_INSTANCE_DATA, \ + 1}, \ + {"TEXCOORD", \ + 1, \ + DXGI_FORMAT_R32_SINT, \ + 1, \ + D3D11_APPEND_ALIGNED_ELEMENT, \ + D3D11_INPUT_PER_INSTANCE_DATA, \ + 1} + + // Initialize input layouts. + { + D3D11_INPUT_ELEMENT_DESC inputDesc[] = { + BASE_UNIT_QUAD_LAYOUT, + // vTexRect + {"TEXCOORD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, + D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}}; + if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc), + sTexturedQuadVS, VertexShaderID::TexturedQuad)) { + return Fail("FEATURE_FAILURE_UNIT_QUAD_TEXTURED_LAYOUT"); + } + } + { + D3D11_INPUT_ELEMENT_DESC inputDesc[] = { + BASE_UNIT_QUAD_LAYOUT, + // vColor + {"TEXCOORD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, + D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}}; + if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc), sColoredQuadVS, + VertexShaderID::ColoredQuad)) { + return Fail("FEATURE_FAILURE_UNIT_QUAD_COLORED_LAYOUT"); + } + } + { + D3D11_INPUT_ELEMENT_DESC inputDesc[] = { + BASE_UNIT_TRIANGLE_LAYOUT, + // vTexCoord1, vTexCoord2, vTexCoord3 + {"TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT, 1, + D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}, + {"TEXCOORD", 3, DXGI_FORMAT_R32G32_FLOAT, 1, + D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}, + {"TEXCOORD", 4, DXGI_FORMAT_R32G32_FLOAT, 1, + D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}}; + if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc), + sTexturedVertexVS, VertexShaderID::TexturedVertex)) { + return Fail("FEATURE_FAILURE_TEXTURED_INPUT_LAYOUT"); + } + // Propagate the input layout to other vertex shaders that use the same. + mInputLayouts[VertexShaderID::BlendVertex] = + mInputLayouts[VertexShaderID::TexturedVertex]; + } + { + D3D11_INPUT_ELEMENT_DESC inputDesc[] = { + BASE_UNIT_TRIANGLE_LAYOUT, + {"TEXCOORD", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, + D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}}; + if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc), + sColoredVertexVS, VertexShaderID::ColoredVertex)) { + return Fail("FEATURE_FAILURE_COLORED_INPUT_LAYOUT"); + } + } + +#undef BASE_UNIT_QUAD_LAYOUT +#undef BASE_UNIT_TRIANGLE_LAYOUT + + // Ancillary shaders that are not used for batching. + { + D3D11_INPUT_ELEMENT_DESC inputDesc[] = { + // vPos + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, + D3D11_INPUT_PER_VERTEX_DATA, 0}, + // vRect + {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_SINT, 1, 0, + D3D11_INPUT_PER_INSTANCE_DATA, 1}, + // vDepth + {"TEXCOORD", 1, DXGI_FORMAT_R32_SINT, 1, D3D11_APPEND_ALIGNED_ELEMENT, + D3D11_INPUT_PER_INSTANCE_DATA, 1}, + }; + if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc), sClearVS, + VertexShaderID::Clear)) { + return Fail("FEATURE_FAILURE_CLEAR_INPUT_LAYOUT"); + } + } + { + D3D11_INPUT_ELEMENT_DESC inputDesc[] = { + // vPos + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, + D3D11_INPUT_PER_VERTEX_DATA, 0}, + // vTexCoords + {"POSITION", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, + D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}}; + if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc), + sMaskCombinerVS, VertexShaderID::MaskCombiner)) { + return Fail("FEATURE_FAILURE_MASK_COMBINER_INPUT_LAYOUT"); + } + } + { + D3D11_INPUT_ELEMENT_DESC inputDesc[] = { + // vPos + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, + D3D11_INPUT_PER_VERTEX_DATA, 0}, + // vRect + {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, + D3D11_INPUT_PER_INSTANCE_DATA, 1}, + // vTexCoords + {"TEXCOORD", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, + D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1}, + }; + if (!InitInputLayout(inputDesc, MOZ_ARRAY_LENGTH(inputDesc), + sDiagnosticTextVS, VertexShaderID::DiagnosticText)) { + return Fail("FEATURE_FAILURE_DIAGNOSTIC_INPUT_LAYOUT"); + } + } + + if (!InitRasterizerStates() || !InitDepthStencilState() || + !InitBlendStates() || !InitSamplerStates() || !InitSyncObject()) { + return false; + } + + mCtx->RSSetState(mRasterizerStateNoScissor); + + return MLGDevice::Initialize(); +} + +bool MLGDeviceD3D11::InitPixelShader(PixelShaderID aShaderID) { + const ShaderBytes* code = mLazyPixelShaders[aShaderID]; + HRESULT hr = + mDevice->CreatePixelShader(code->mData, code->mLength, nullptr, + getter_AddRefs(mPixelShaders[aShaderID])); + if (FAILED(hr)) { + gfxCriticalNote << "Could not create pixel shader " + << hexa(unsigned(aShaderID)) << ": " << hexa(hr); + return false; + } + return true; +} + +bool MLGDeviceD3D11::InitRasterizerStates() { + { + CD3D11_RASTERIZER_DESC desc = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT()); + desc.CullMode = D3D11_CULL_NONE; + desc.FillMode = D3D11_FILL_SOLID; + desc.ScissorEnable = TRUE; + HRESULT hr = mDevice->CreateRasterizerState( + &desc, getter_AddRefs(mRasterizerStateScissor)); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_SCISSOR_RASTERIZER", + "Could not create scissor rasterizer (%x)", hr); + } + } + { + CD3D11_RASTERIZER_DESC desc = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT()); + desc.CullMode = D3D11_CULL_NONE; + desc.FillMode = D3D11_FILL_SOLID; + HRESULT hr = mDevice->CreateRasterizerState( + &desc, getter_AddRefs(mRasterizerStateNoScissor)); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_DEFAULT_RASTERIZER", + "Could not create default rasterizer (%x)", hr); + } + } + return true; +} + +bool MLGDeviceD3D11::InitSamplerStates() { + { + CD3D11_SAMPLER_DESC desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT()); + HRESULT hr = mDevice->CreateSamplerState( + &desc, getter_AddRefs(mSamplerStates[SamplerMode::LinearClamp])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_LINEAR_CLAMP_SAMPLER", + "Could not create linear clamp sampler (%x)", hr); + } + } + { + CD3D11_SAMPLER_DESC desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT()); + desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER; + desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER; + desc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER; + memset(desc.BorderColor, 0, sizeof(desc.BorderColor)); + HRESULT hr = mDevice->CreateSamplerState( + &desc, getter_AddRefs(mSamplerStates[SamplerMode::LinearClampToZero])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_LINEAR_CLAMP_ZERO_SAMPLER", + "Could not create linear clamp to zero sampler (%x)", hr); + } + } + { + CD3D11_SAMPLER_DESC desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT()); + desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + HRESULT hr = mDevice->CreateSamplerState( + &desc, getter_AddRefs(mSamplerStates[SamplerMode::LinearRepeat])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_LINEAR_CLAMP_ZERO_SAMPLER", + "Could not create linear clamp to zero sampler (%x)", hr); + } + } + + { + CD3D11_SAMPLER_DESC desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT()); + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + HRESULT hr = mDevice->CreateSamplerState( + &desc, getter_AddRefs(mSamplerStates[SamplerMode::Point])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_POINT_SAMPLER", + "Could not create point sampler (%x)", hr); + } + } + return true; +} + +bool MLGDeviceD3D11::InitBlendStates() { + { + CD3D11_BLEND_DESC desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT()); + HRESULT hr = mDevice->CreateBlendState( + &desc, getter_AddRefs(mBlendStates[MLGBlendState::Copy])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_COPY_BLEND_STATE", + "Could not create copy blend state (%x)", hr); + } + } + + { + CD3D11_BLEND_DESC desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT()); + desc.RenderTarget[0].BlendEnable = TRUE; + desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + HRESULT hr = mDevice->CreateBlendState( + &desc, getter_AddRefs(mBlendStates[MLGBlendState::Over])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_OVER_BLEND_STATE", + "Could not create over blend state (%x)", hr); + } + } + + { + CD3D11_BLEND_DESC desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT()); + desc.RenderTarget[0].BlendEnable = TRUE; + desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + HRESULT hr = mDevice->CreateBlendState( + &desc, getter_AddRefs(mBlendStates[MLGBlendState::OverAndPremultiply])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_OVER_BLEND_STATE", + "Could not create over blend state (%x)", hr); + } + } + + { + CD3D11_BLEND_DESC desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT()); + desc.RenderTarget[0].BlendEnable = TRUE; + desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE; + desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_MIN; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_MIN; + HRESULT hr = mDevice->CreateBlendState( + &desc, getter_AddRefs(mBlendStates[MLGBlendState::Min])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_MIN_BLEND_STATE", + "Could not create min blend state (%x)", hr); + } + } + + { + CD3D11_BLEND_DESC desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT()); + desc.RenderTarget[0].BlendEnable = TRUE; + desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC1_COLOR; + desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + HRESULT hr = mDevice->CreateBlendState( + &desc, getter_AddRefs(mBlendStates[MLGBlendState::ComponentAlpha])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_COMPONENT_ALPHA_BLEND_STATE", + "Could not create component alpha blend state (%x)", hr); + } + } + return true; +} + +bool MLGDeviceD3D11::InitDepthStencilState() { + D3D11_DEPTH_STENCIL_DESC desc = CD3D11_DEPTH_STENCIL_DESC(D3D11_DEFAULT); + + HRESULT hr = mDevice->CreateDepthStencilState( + &desc, getter_AddRefs(mDepthStencilStates[MLGDepthTestMode::Write])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_WRITE_DEPTH_STATE", + "Could not create write depth stencil state (%x)", hr); + } + + desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + hr = mDevice->CreateDepthStencilState( + &desc, getter_AddRefs(mDepthStencilStates[MLGDepthTestMode::ReadOnly])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_READ_DEPTH_STATE", + "Could not create read depth stencil state (%x)", hr); + } + + desc.DepthEnable = FALSE; + hr = mDevice->CreateDepthStencilState( + &desc, getter_AddRefs(mDepthStencilStates[MLGDepthTestMode::Disabled])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_DISABLED_DEPTH_STATE", + "Could not create disabled depth stencil state (%x)", hr); + } + + desc = CD3D11_DEPTH_STENCIL_DESC(D3D11_DEFAULT); + desc.DepthFunc = D3D11_COMPARISON_ALWAYS; + hr = mDevice->CreateDepthStencilState( + &desc, + getter_AddRefs(mDepthStencilStates[MLGDepthTestMode::AlwaysWrite])); + if (FAILED(hr)) { + return Fail("FEATURE_FAILURE_WRITE_DEPTH_STATE", + "Could not create always-write depth stencil state (%x)", hr); + } + + return true; +} + +bool MLGDeviceD3D11::InitVertexShader(VertexShaderID aShaderID) { + const ShaderBytes* code = mLazyVertexShaders[aShaderID]; + HRESULT hr = + mDevice->CreateVertexShader(code->mData, code->mLength, nullptr, + getter_AddRefs(mVertexShaders[aShaderID])); + if (FAILED(hr)) { + gfxCriticalNote << "Could not create vertex shader " + << hexa(unsigned(aShaderID)) << ": " << hexa(hr); + return false; + } + return true; +} + +bool MLGDeviceD3D11::InitInputLayout(D3D11_INPUT_ELEMENT_DESC* aDesc, + size_t aNumElements, + const ShaderBytes& aCode, + VertexShaderID aShaderID) { + HRESULT hr = mDevice->CreateInputLayout( + aDesc, aNumElements, aCode.mData, aCode.mLength, + getter_AddRefs(mInputLayouts[aShaderID])); + if (FAILED(hr)) { + gfxCriticalNote << "Could not create input layout for shader " + << hexa(unsigned(aShaderID)) << ": " << hexa(hr); + return false; + } + return true; +} + +TextureFactoryIdentifier MLGDeviceD3D11::GetTextureFactoryIdentifier( + widget::CompositorWidget* aWidget) const { + TextureFactoryIdentifier ident(GetLayersBackend(), XRE_GetProcessType(), + GetMaxTextureSize()); + if (aWidget) { + ident.mUseCompositorWnd = !!aWidget->AsWindows()->GetCompositorHwnd(); + } + if (mSyncObject) { + ident.mSyncHandle = mSyncObject->GetSyncHandle(); + } + + return ident; +} + +inline uint32_t GetMaxTextureSizeForFeatureLevel1( + D3D_FEATURE_LEVEL aFeatureLevel) { + int32_t maxTextureSize; + switch (aFeatureLevel) { + case D3D_FEATURE_LEVEL_11_1: + case D3D_FEATURE_LEVEL_11_0: + maxTextureSize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + break; + case D3D_FEATURE_LEVEL_10_1: + case D3D_FEATURE_LEVEL_10_0: + maxTextureSize = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; + break; + case D3D_FEATURE_LEVEL_9_3: + maxTextureSize = D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION; + break; + default: + maxTextureSize = D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION; + } + return maxTextureSize; +} + +LayersBackend MLGDeviceD3D11::GetLayersBackend() const { + return LayersBackend::LAYERS_D3D11; +} + +int32_t MLGDeviceD3D11::GetMaxTextureSize() const { + return GetMaxTextureSizeForFeatureLevel1(mDevice->GetFeatureLevel()); +} + +RefPtr MLGDeviceD3D11::CreateSwapChainForWidget( + widget::CompositorWidget* aWidget) { + return MLGSwapChainD3D11::Create(this, mDevice, aWidget); +} + +RefPtr MLGDeviceD3D11::CreateDataTextureSource( + TextureFlags aFlags) { + return new DataTextureSourceD3D11(mDevice, gfx::SurfaceFormat::UNKNOWN, + aFlags); +} + +static inline D3D11_MAP ToD3D11Map(MLGMapType aType) { + switch (aType) { + case MLGMapType::READ: + return D3D11_MAP_READ; + case MLGMapType::READ_WRITE: + return D3D11_MAP_READ_WRITE; + case MLGMapType::WRITE: + return D3D11_MAP_WRITE; + case MLGMapType::WRITE_DISCARD: + return D3D11_MAP_WRITE_DISCARD; + } + return D3D11_MAP_WRITE; +} + +bool MLGDeviceD3D11::Map(MLGResource* aResource, MLGMapType aType, + MLGMappedResource* aMap) { + ID3D11Resource* resource = aResource->AsResourceD3D11()->GetResource(); + MOZ_ASSERT(resource); + + D3D11_MAPPED_SUBRESOURCE map; + HRESULT hr = mCtx->Map(resource, 0, ToD3D11Map(aType), 0, &map); + + if (FAILED(hr)) { + gfxWarning() << "Could not map MLG resource: " << hexa(hr); + return false; + } + + aMap->mData = reinterpret_cast(map.pData); + aMap->mStride = map.RowPitch; + return true; +} + +void MLGDeviceD3D11::Unmap(MLGResource* aResource) { + ID3D11Resource* resource = aResource->AsResourceD3D11()->GetResource(); + mCtx->Unmap(resource, 0); +} + +void MLGDeviceD3D11::UpdatePartialResource(MLGResource* aResource, + const gfx::IntRect* aRect, + void* aData, uint32_t aStride) { + D3D11_BOX box; + if (aRect) { + box = RectToBox(*aRect); + } + + ID3D11Resource* resource = aResource->AsResourceD3D11()->GetResource(); + mCtx->UpdateSubresource(resource, 0, aRect ? &box : nullptr, aData, aStride, + 0); +} + +void MLGDeviceD3D11::SetRenderTarget(MLGRenderTarget* aRT) { + ID3D11RenderTargetView* rtv = nullptr; + ID3D11DepthStencilView* dsv = nullptr; + + if (aRT) { + MLGRenderTargetD3D11* rt = aRT->AsD3D11(); + rtv = rt->GetRenderTargetView(); + dsv = rt->GetDSV(); + } + + mCtx->OMSetRenderTargets(1, &rtv, dsv); + mCurrentRT = aRT; +} + +MLGRenderTarget* MLGDeviceD3D11::GetRenderTarget() { return mCurrentRT; } + +void MLGDeviceD3D11::SetViewport(const gfx::IntRect& aViewport) { + D3D11_VIEWPORT vp; + vp.MaxDepth = 1.0f; + vp.MinDepth = 0.0f; + vp.TopLeftX = aViewport.X(); + vp.TopLeftY = aViewport.Y(); + vp.Width = aViewport.Width(); + vp.Height = aViewport.Height(); + mCtx->RSSetViewports(1, &vp); +} + +static inline D3D11_RECT ToD3D11Rect(const gfx::IntRect& aRect) { + D3D11_RECT rect; + rect.left = aRect.X(); + rect.top = aRect.Y(); + rect.right = aRect.XMost(); + rect.bottom = aRect.YMost(); + return rect; +} + +void MLGDeviceD3D11::SetScissorRect(const Maybe& aScissorRect) { + if (!aScissorRect) { + if (mScissored) { + mCtx->RSSetState(mRasterizerStateNoScissor); + mScissored = false; + } + return; + } + D3D11_RECT rect = ToD3D11Rect(aScissorRect.value()); + mCtx->RSSetScissorRects(1, &rect); + if (!mScissored) { + mScissored = true; + mCtx->RSSetState(mRasterizerStateScissor); + } +} + +void MLGDeviceD3D11::SetVertexShader(VertexShaderID aShader) { + if (!mVertexShaders[aShader]) { + InitVertexShader(aShader); + MOZ_ASSERT(mInputLayouts[aShader]); + } + SetVertexShader(mVertexShaders[aShader]); + SetInputLayout(mInputLayouts[aShader]); +} + +void MLGDeviceD3D11::SetInputLayout(ID3D11InputLayout* aLayout) { + if (mCurrentInputLayout == aLayout) { + return; + } + mCtx->IASetInputLayout(aLayout); + mCurrentInputLayout = aLayout; +} + +void MLGDeviceD3D11::SetVertexShader(ID3D11VertexShader* aShader) { + if (mCurrentVertexShader == aShader) { + return; + } + mCtx->VSSetShader(aShader, nullptr, 0); + mCurrentVertexShader = aShader; +} + +void MLGDeviceD3D11::SetPixelShader(PixelShaderID aShader) { + if (!mPixelShaders[aShader]) { + InitPixelShader(aShader); + } + if (mCurrentPixelShader != mPixelShaders[aShader]) { + mCtx->PSSetShader(mPixelShaders[aShader], nullptr, 0); + mCurrentPixelShader = mPixelShaders[aShader]; + } +} + +void MLGDeviceD3D11::SetSamplerMode(uint32_t aIndex, SamplerMode aMode) { + ID3D11SamplerState* sampler = mSamplerStates[aMode]; + mCtx->PSSetSamplers(aIndex, 1, &sampler); +} + +void MLGDeviceD3D11::SetBlendState(MLGBlendState aState) { + if (mCurrentBlendState != mBlendStates[aState]) { + FLOAT blendFactor[4] = {0, 0, 0, 0}; + mCtx->OMSetBlendState(mBlendStates[aState], blendFactor, 0xFFFFFFFF); + mCurrentBlendState = mBlendStates[aState]; + } +} + +void MLGDeviceD3D11::SetVertexBuffer(uint32_t aSlot, MLGBuffer* aBuffer, + uint32_t aStride, uint32_t aOffset) { + ID3D11Buffer* buffer = aBuffer ? aBuffer->AsD3D11()->GetBuffer() : nullptr; + mCtx->IASetVertexBuffers(aSlot, 1, &buffer, &aStride, &aOffset); +} + +void MLGDeviceD3D11::SetVSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer) { + MOZ_ASSERT(aSlot < kMaxVertexShaderConstantBuffers); + + ID3D11Buffer* buffer = aBuffer ? aBuffer->AsD3D11()->GetBuffer() : nullptr; + mCtx->VSSetConstantBuffers(aSlot, 1, &buffer); +} + +void MLGDeviceD3D11::SetPSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer) { + MOZ_ASSERT(aSlot < kMaxPixelShaderConstantBuffers); + + ID3D11Buffer* buffer = aBuffer ? aBuffer->AsD3D11()->GetBuffer() : nullptr; + mCtx->PSSetConstantBuffers(aSlot, 1, &buffer); +} + +void MLGDeviceD3D11::SetVSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer, + uint32_t aFirstConstant, + uint32_t aNumConstants) { + MOZ_ASSERT(aSlot < kMaxVertexShaderConstantBuffers); + MOZ_ASSERT(mCanUseConstantBufferOffsetBinding); + MOZ_ASSERT(mCtx1); + MOZ_ASSERT(aFirstConstant % 16 == 0); + + ID3D11Buffer* buffer = aBuffer ? aBuffer->AsD3D11()->GetBuffer() : nullptr; + mCtx1->VSSetConstantBuffers1(aSlot, 1, &buffer, &aFirstConstant, + &aNumConstants); +} + +void MLGDeviceD3D11::SetPSConstantBuffer(uint32_t aSlot, MLGBuffer* aBuffer, + uint32_t aFirstConstant, + uint32_t aNumConstants) { + MOZ_ASSERT(aSlot < kMaxPixelShaderConstantBuffers); + MOZ_ASSERT(mCanUseConstantBufferOffsetBinding); + MOZ_ASSERT(mCtx1); + MOZ_ASSERT(aFirstConstant % 16 == 0); + + ID3D11Buffer* buffer = aBuffer ? aBuffer->AsD3D11()->GetBuffer() : nullptr; + mCtx1->PSSetConstantBuffers1(aSlot, 1, &buffer, &aFirstConstant, + &aNumConstants); +} + +void MLGDeviceD3D11::SetPrimitiveTopology(MLGPrimitiveTopology aTopology) { + D3D11_PRIMITIVE_TOPOLOGY topology; + switch (aTopology) { + case MLGPrimitiveTopology::TriangleStrip: + topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + break; + case MLGPrimitiveTopology::TriangleList: + topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + break; + case MLGPrimitiveTopology::UnitQuad: + SetVertexBuffer(0, mUnitQuadVB, sizeof(float) * 2, 0); + topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + break; + case MLGPrimitiveTopology::UnitTriangle: + SetVertexBuffer(0, mUnitTriangleVB, sizeof(float) * 3, 0); + topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + break; + default: + MOZ_ASSERT_UNREACHABLE("Unknown topology"); + topology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; + break; + } + + mCtx->IASetPrimitiveTopology(topology); +} + +RefPtr MLGDeviceD3D11::CreateBuffer(MLGBufferType aType, + uint32_t aSize, MLGUsage aUsage, + const void* aInitialData) { + return MLGBufferD3D11::Create(mDevice, aType, aSize, aUsage, aInitialData); +} + +RefPtr MLGDeviceD3D11::CreateRenderTarget( + const gfx::IntSize& aSize, MLGRenderTargetFlags aFlags) { + RefPtr rt = new MLGRenderTargetD3D11(aSize, aFlags); + if (!rt->Initialize(mDevice)) { + return nullptr; + } + return rt; +} + +void MLGDeviceD3D11::Clear(MLGRenderTarget* aRT, + const gfx::DeviceColor& aColor) { + MLGRenderTargetD3D11* rt = aRT->AsD3D11(); + FLOAT rgba[4] = {aColor.r, aColor.g, aColor.b, aColor.a}; + mCtx->ClearRenderTargetView(rt->GetRenderTargetView(), rgba); + if (ID3D11DepthStencilView* dsv = rt->GetDSV()) { + mCtx->ClearDepthStencilView(dsv, D3D11_CLEAR_DEPTH, 1.0, 0); + } +} + +void MLGDeviceD3D11::ClearDepthBuffer(MLGRenderTarget* aRT) { + MLGRenderTargetD3D11* rt = aRT->AsD3D11(); + if (ID3D11DepthStencilView* dsv = rt->GetDSV()) { + mCtx->ClearDepthStencilView(dsv, D3D11_CLEAR_DEPTH, 1.0, 0); + } +} + +void MLGDeviceD3D11::ClearView(MLGRenderTarget* aRT, const DeviceColor& aColor, + const IntRect* aRects, size_t aNumRects) { + MOZ_ASSERT(mCanUseClearView); + MOZ_ASSERT(mCtx1); + + MLGRenderTargetD3D11* rt = aRT->AsD3D11(); + FLOAT rgba[4] = {aColor.r, aColor.g, aColor.b, aColor.a}; + + StackArray rects(aNumRects); + for (size_t i = 0; i < aNumRects; i++) { + rects[i] = ToD3D11Rect(aRects[i]); + } + + // Batch ClearView calls since too many will crash NVIDIA drivers. + size_t remaining = aNumRects; + size_t cursor = 0; + while (remaining > 0) { + size_t amount = std::min(remaining, kMaxClearViewRects); + mCtx1->ClearView(rt->GetRenderTargetView(), rgba, rects.data() + cursor, + amount); + + remaining -= amount; + cursor += amount; + } +} + +void MLGDeviceD3D11::Draw(uint32_t aVertexCount, uint32_t aOffset) { + mCtx->Draw(aVertexCount, aOffset); +} + +void MLGDeviceD3D11::DrawInstanced(uint32_t aVertexCountPerInstance, + uint32_t aInstanceCount, + uint32_t aVertexOffset, + uint32_t aInstanceOffset) { + mCtx->DrawInstanced(aVertexCountPerInstance, aInstanceCount, aVertexOffset, + aInstanceOffset); +} + +void MLGDeviceD3D11::SetPSTextures(uint32_t aSlot, uint32_t aNumTextures, + TextureSource* const* aTextures) { + // TextureSource guarantees that the ID3D11ShaderResourceView will be cached, + // so we don't hold a RefPtr here. + StackArray views(aNumTextures); + + for (size_t i = 0; i < aNumTextures; i++) { + views[i] = ResolveTextureSourceForShader(aTextures[i]); + } + + mCtx->PSSetShaderResources(aSlot, aNumTextures, views.data()); +} + +ID3D11ShaderResourceView* MLGDeviceD3D11::ResolveTextureSourceForShader( + TextureSource* aTexture) { + if (!aTexture) { + return nullptr; + } + + if (TextureSourceD3D11* source = aTexture->AsSourceD3D11()) { + ID3D11Texture2D* texture = source->GetD3D11Texture(); + if (!texture) { + gfxWarning() << "No D3D11 texture present in SetPSTextures"; + return nullptr; + } + + MaybeLockTexture(texture); + return source->GetShaderResourceView(); + } + + gfxWarning() << "Unknown texture type in SetPSTextures"; + return nullptr; +} + +void MLGDeviceD3D11::SetPSTexture(uint32_t aSlot, MLGTexture* aTexture) { + RefPtr view; + if (aTexture) { + MLGTextureD3D11* texture = aTexture->AsD3D11(); + view = texture->GetShaderResourceView(); + } + + ID3D11ShaderResourceView* viewPtr = view.get(); + mCtx->PSSetShaderResources(aSlot, 1, &viewPtr); +} + +void MLGDeviceD3D11::MaybeLockTexture(ID3D11Texture2D* aTexture) { + RefPtr mutex; + HRESULT hr = aTexture->QueryInterface(__uuidof(IDXGIKeyedMutex), + (void**)getter_AddRefs(mutex)); + if (FAILED(hr) || !mutex) { + return; + } + + hr = mutex->AcquireSync(0, 10000); + + if (hr == WAIT_TIMEOUT) { + gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout"; + mLockAttemptedTextures.PutEntry(mutex); + } else if (hr == WAIT_ABANDONED) { + gfxCriticalNote << "GFX: D3D11 lock mutex abandoned"; + mLockAttemptedTextures.PutEntry(mutex); + } else if (FAILED(hr)) { + gfxCriticalNote << "D3D11 lock mutex failed: " << hexa(hr); + mLockAttemptedTextures.PutEntry(mutex); + } else { + mLockedTextures.PutEntry(mutex); + } +} + +void MLGDeviceD3D11::SetPSTexturesNV12(uint32_t aSlot, + TextureSource* aTexture) { + MOZ_ASSERT(aTexture->GetFormat() == SurfaceFormat::NV12 || + aTexture->GetFormat() == SurfaceFormat::P010 || + aTexture->GetFormat() == SurfaceFormat::P016); + + TextureSourceD3D11* source = aTexture->AsSourceD3D11(); + if (!source) { + gfxWarning() << "Unknown texture type in SetPSCompoundTexture"; + return; + } + + ID3D11Texture2D* texture = source->GetD3D11Texture(); + if (!texture) { + gfxWarning() << "TextureSourceD3D11 does not have an ID3D11Texture"; + return; + } + + MaybeLockTexture(texture); + + const bool isNV12 = aTexture->GetFormat() == SurfaceFormat::NV12; + + RefPtr views[2]; + D3D11_SHADER_RESOURCE_VIEW_DESC desc = CD3D11_SHADER_RESOURCE_VIEW_DESC( + D3D11_SRV_DIMENSION_TEXTURE2D, + isNV12 ? DXGI_FORMAT_R8_UNORM : DXGI_FORMAT_R16_UNORM); + + HRESULT hr = mDevice->CreateShaderResourceView(texture, &desc, + getter_AddRefs(views[0])); + if (FAILED(hr) || !views[0]) { + gfxWarning() << "Could not bind an SRV for Y plane of NV12 texture: " + << hexa(hr); + return; + } + + desc.Format = isNV12 ? DXGI_FORMAT_R8G8_UNORM : DXGI_FORMAT_R16G16_UNORM; + hr = mDevice->CreateShaderResourceView(texture, &desc, + getter_AddRefs(views[1])); + if (FAILED(hr) || !views[1]) { + gfxWarning() << "Could not bind an SRV for CbCr plane of NV12 texture: " + << hexa(hr); + return; + } + + ID3D11ShaderResourceView* bind[2] = {views[0], views[1]}; + mCtx->PSSetShaderResources(aSlot, 2, bind); +} + +bool MLGDeviceD3D11::InitSyncObject() { + MOZ_ASSERT(!mSyncObject); + MOZ_ASSERT(mDevice); + + mSyncObject = SyncObjectHost::CreateSyncObjectHost(mDevice); + MOZ_ASSERT(mSyncObject); + + return mSyncObject->Init(); +} + +void MLGDeviceD3D11::StartDiagnostics(uint32_t aInvalidPixels) { + mDiagnostics->Start(aInvalidPixels); +} + +void MLGDeviceD3D11::EndDiagnostics() { mDiagnostics->End(); } + +void MLGDeviceD3D11::GetDiagnostics(GPUStats* aStats) { + mDiagnostics->Query(aStats); +} + +bool MLGDeviceD3D11::Synchronize() { + MOZ_ASSERT(mSyncObject); + + if (mSyncObject) { + if (!mSyncObject->Synchronize()) { + // It's timeout or other error. Handle the device-reset here. + HandleDeviceReset("SyncObject"); + return false; + } + } + + return true; +} + +void MLGDeviceD3D11::UnlockAllTextures() { + for (auto iter = mLockedTextures.Iter(); !iter.Done(); iter.Next()) { + RefPtr mutex = iter.Get()->GetKey(); + mutex->ReleaseSync(0); + } + mLockedTextures.Clear(); + mLockAttemptedTextures.Clear(); +} + +void MLGDeviceD3D11::SetDepthTestMode(MLGDepthTestMode aMode) { + mCtx->OMSetDepthStencilState(mDepthStencilStates[aMode], 0xffffffff); +} + +void MLGDeviceD3D11::InsertPresentWaitQuery() { + CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT); + HRESULT hr = + mDevice->CreateQuery(&desc, getter_AddRefs(mNextWaitForPresentQuery)); + if (FAILED(hr) || !mNextWaitForPresentQuery) { + gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << hexa(hr); + return; + } + + mCtx->End(mNextWaitForPresentQuery); +} + +void MLGDeviceD3D11::WaitForPreviousPresentQuery() { + if (mWaitForPresentQuery) { + BOOL result; + WaitForFrameGPUQuery(mDevice, mCtx, mWaitForPresentQuery, &result); + } + mWaitForPresentQuery = mNextWaitForPresentQuery.forget(); +} + +void MLGDeviceD3D11::Flush() { mCtx->Flush(); } + +void MLGDeviceD3D11::EndFrame() { + // On our Windows 8 x64 machines, we have observed a driver bug related to + // XXSetConstantBuffers1. It appears binding the same buffer to multiple + // slots, and potentially leaving slots bound for many frames (as can + // happen if we bind a high slot, like for blending), can consistently + // cause shaders to read wrong values much later. It is possible there is + // a driver bug related to aliasing and partial binding. + // + // Configuration: GeForce GT 610 (0x104a), Driver 9.18.13.3523, 3-4-2014, + // on Windows 8 x64. + // + // To alleviate this we unbind all buffers at the end of the frame. + static ID3D11Buffer* nullBuffers[6] = { + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + }; + MOZ_ASSERT(MOZ_ARRAY_LENGTH(nullBuffers) >= kMaxVertexShaderConstantBuffers); + MOZ_ASSERT(MOZ_ARRAY_LENGTH(nullBuffers) >= kMaxPixelShaderConstantBuffers); + + mCtx->VSSetConstantBuffers(0, kMaxVertexShaderConstantBuffers, nullBuffers); + mCtx->VSSetConstantBuffers(0, kMaxPixelShaderConstantBuffers, nullBuffers); + + MLGDevice::EndFrame(); +} + +void MLGDeviceD3D11::HandleDeviceReset(const char* aWhere) { + if (!IsValid()) { + return; + } + + Fail("FEATURE_FAILURE_DEVICE_RESET"_ns, nullptr); + + gfxCriticalNote << "GFX: D3D11 detected a device reset in " << aWhere; + if (XRE_IsGPUProcess()) { + GPUParent::GetSingleton()->NotifyDeviceReset(); + } + + UnmapSharedBuffers(); + mIsValid = false; +} + +RefPtr MLGDeviceD3D11::CreateTexture(const gfx::IntSize& aSize, + gfx::SurfaceFormat aFormat, + MLGUsage aUsage, + MLGTextureFlags aFlags) { + return MLGTextureD3D11::Create(mDevice, aSize, aFormat, aUsage, aFlags); +} + +RefPtr MLGDeviceD3D11::CreateTexture(TextureSource* aSource) { + TextureSourceD3D11* source = aSource->AsSourceD3D11(); + if (!source) { + gfxWarning() << "Attempted to wrap a non-D3D11 texture"; + return nullptr; + } + if (!source->GetD3D11Texture()) { + return nullptr; + } + return new MLGTextureD3D11(source->GetD3D11Texture()); +} + +void MLGDeviceD3D11::CopyTexture(MLGTexture* aDest, + const gfx::IntPoint& aTarget, + MLGTexture* aSource, + const gfx::IntRect& aRect) { + MLGTextureD3D11* dest = aDest->AsD3D11(); + MLGTextureD3D11* source = aSource->AsD3D11(); + + // We check both the source and destination copy regions, because + // CopySubresourceRegion is documented as causing a device reset if + // the operation is out-of-bounds. And it's not lying. + IntRect sourceBounds(IntPoint(0, 0), aSource->GetSize()); + if (!sourceBounds.Contains(aRect)) { + gfxWarning() << "Attempt to read out-of-bounds in CopySubresourceRegion: " + << sourceBounds << ", " << aRect; + return; + } + + IntRect destBounds(IntPoint(0, 0), aDest->GetSize()); + if (!destBounds.Contains(IntRect(aTarget, aRect.Size()))) { + gfxWarning() << "Attempt to write out-of-bounds in CopySubresourceRegion: " + << destBounds << ", " << aTarget << ", " << aRect.Size(); + return; + } + + D3D11_BOX box = RectToBox(aRect); + mCtx->CopySubresourceRegion(dest->GetTexture(), 0, aTarget.x, aTarget.y, 0, + source->GetTexture(), 0, &box); +} + +bool MLGDeviceD3D11::VerifyConstantBufferOffsetting() { + RefPtr vs; + HRESULT hr = mDevice->CreateVertexShader(sTestConstantBuffersVS.mData, + sTestConstantBuffersVS.mLength, + nullptr, getter_AddRefs(vs)); + if (FAILED(hr)) { + gfxCriticalNote << "Failed creating vertex shader for buffer test: " + << hexa(hr); + return false; + } + + D3D11_INPUT_ELEMENT_DESC inputDesc[] = {{"POSITION", 0, + DXGI_FORMAT_R32G32_FLOAT, 0, 0, + D3D11_INPUT_PER_VERTEX_DATA, 0}}; + + RefPtr layout; + hr = mDevice->CreateInputLayout( + inputDesc, sizeof(inputDesc) / sizeof(inputDesc[0]), + sTestConstantBuffersVS.mData, sTestConstantBuffersVS.mLength, + getter_AddRefs(layout)); + if (FAILED(hr)) { + gfxCriticalNote << "Failed creating input layout for buffer test: " + << hexa(hr); + return false; + } + + RefPtr rt = + CreateRenderTarget(IntSize(2, 2), MLGRenderTargetFlags::Default); + if (!rt) { + return false; + } + + static const size_t kConstantSize = 4 * sizeof(float); + static const size_t kMinConstants = 16; + static const size_t kNumBindings = 3; + + RefPtr buffer = CreateBuffer( + MLGBufferType::Constant, kConstantSize * kMinConstants * kNumBindings, + MLGUsage::Dynamic, nullptr); + if (!buffer) { + return false; + } + + // Populate the buffer. The shader will pick R from buffer 1, G from buffer + // 2, and B from buffer 3. + { + MLGMappedResource map; + if (!Map(buffer, MLGMapType::WRITE_DISCARD, &map)) { + return false; + } + + *reinterpret_cast(map.mData) = + DeviceColor(1.0f, 0.2f, 0.3f, 1.0f); + *reinterpret_cast(map.mData + kConstantSize * kMinConstants) = + DeviceColor(0.4f, 0.0f, 0.5f, 1.0f); + *reinterpret_cast(map.mData + + (kConstantSize * kMinConstants) * 2) = + DeviceColor(0.6f, 0.7f, 1.0f, 1.0f); + + Unmap(buffer); + } + + Clear(rt, DeviceColor(0.0f, 0.0f, 0.0f, 1.0f)); + SetRenderTarget(rt); + SetViewport(IntRect(0, 0, 2, 2)); + SetScissorRect(Nothing()); + SetBlendState(MLGBlendState::Over); + + SetTopology(MLGPrimitiveTopology::UnitQuad); + SetInputLayout(layout); + SetVertexShader(vs); + SetPixelShader(PixelShaderID::ColoredQuad); + + ID3D11Buffer* buffers[3] = {buffer->AsD3D11()->GetBuffer(), + buffer->AsD3D11()->GetBuffer(), + buffer->AsD3D11()->GetBuffer()}; + UINT offsets[3] = {0 * kMinConstants, 1 * kMinConstants, 2 * kMinConstants}; + UINT counts[3] = {kMinConstants, kMinConstants, kMinConstants}; + + mCtx1->VSSetConstantBuffers1(0, 3, buffers, offsets, counts); + mCtx->Draw(4, 0); + + // Kill bindings to resources. + SetRenderTarget(nullptr); + + ID3D11Buffer* nulls[3] = {nullptr, nullptr, nullptr}; + mCtx->VSSetConstantBuffers(0, 3, nulls); + + RefPtr copy = + CreateTexture(IntSize(2, 2), SurfaceFormat::B8G8R8A8, MLGUsage::Staging, + MLGTextureFlags::None); + if (!copy) { + return false; + } + + CopyTexture(copy, IntPoint(0, 0), rt->GetTexture(), IntRect(0, 0, 2, 2)); + + uint8_t r, g, b, a; + { + MLGMappedResource map; + if (!Map(copy, MLGMapType::READ, &map)) { + return false; + } + r = map.mData[0]; + g = map.mData[1]; + b = map.mData[2]; + a = map.mData[3]; + Unmap(copy); + } + + return r == 255 && g == 0 && b == 255 && a == 255; +} + +static D3D11_BOX RectToBox(const gfx::IntRect& aRect) { + D3D11_BOX box; + box.front = 0; + box.back = 1; + box.left = aRect.X(); + box.top = aRect.Y(); + box.right = aRect.XMost(); + box.bottom = aRect.YMost(); + return box; +} + +} // namespace layers +} // namespace mozilla -- cgit v1.2.3