/* -*- 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 "LayersLogging.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; } } 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) { mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary(); // 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(TexturedVertexNV12, TexturedVertexNV12); LAZY_PS(TexturedQuadIMC4, TexturedQuadIMC4); 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::Color& 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 Color& 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(NS_LITERAL_CSTRING("FEATURE_FAILURE_DEVICE_RESET"), 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: " << Stringify(sourceBounds) << ", " << Stringify(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: " << Stringify(destBounds) << ", " << Stringify(aTarget) << ", " << Stringify(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) = Color(1.0f, 0.2f, 0.3f, 1.0f); *reinterpret_cast(map.mData + kConstantSize * kMinConstants) = Color(0.4f, 0.0f, 0.5f, 1.0f); *reinterpret_cast(map.mData + (kConstantSize * kMinConstants) * 2) = Color(0.6f, 0.7f, 1.0f, 1.0f); Unmap(buffer); } Clear(rt, Color(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