gecko-dev/gfx/layers/d3d11/CompositorD3D11.cpp

1764 строки
56 KiB
C++

/* -*- 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 "CompositorD3D11.h"
#include "TextureD3D11.h"
#include "gfxWindowsPlatform.h"
#include "nsIWidget.h"
#include "Layers.h"
#include "mozilla/gfx/D3D11Checks.h"
#include "mozilla/gfx/DeviceManagerDx.h"
#include "mozilla/gfx/GPUParent.h"
#include "mozilla/gfx/Swizzle.h"
#include "mozilla/layers/ImageHost.h"
#include "mozilla/layers/ContentHost.h"
#include "mozilla/layers/Diagnostics.h"
#include "mozilla/layers/DiagnosticsD3D11.h"
#include "mozilla/layers/Effects.h"
#include "mozilla/layers/HelpersD3D11.h"
#include "nsWindowsHelpers.h"
#include "gfxConfig.h"
#include "gfxCrashReporterUtils.h"
#include "gfxUtils.h"
#include "mozilla/gfx/StackArray.h"
#include "mozilla/widget/WinCompositorWidget.h"
#include "mozilla/EnumeratedArray.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layers.h"
#include "mozilla/Telemetry.h"
#include "BlendShaderConstants.h"
#include "D3D11ShareHandleImage.h"
#include "DeviceAttachmentsD3D11.h"
#include <versionhelpers.h> // For IsWindows8OrGreater
#include <winsdkver.h>
namespace mozilla {
using namespace gfx;
namespace layers {
bool CanUsePartialPresents(ID3D11Device* aDevice);
const FLOAT sBlendFactor[] = {0, 0, 0, 0};
class AsyncReadbackBufferD3D11 final : public AsyncReadbackBuffer {
public:
AsyncReadbackBufferD3D11(ID3D11DeviceContext* aContext,
ID3D11Texture2D* aTexture, const IntSize& aSize);
bool MapAndCopyInto(DataSourceSurface* aSurface,
const IntSize& aReadSize) const override;
ID3D11Texture2D* GetTexture() { return mTexture; }
private:
RefPtr<ID3D11DeviceContext> mContext;
RefPtr<ID3D11Texture2D> mTexture;
};
AsyncReadbackBufferD3D11::AsyncReadbackBufferD3D11(
ID3D11DeviceContext* aContext, ID3D11Texture2D* aTexture,
const IntSize& aSize)
: AsyncReadbackBuffer(aSize), mContext(aContext), mTexture(aTexture) {}
bool AsyncReadbackBufferD3D11::MapAndCopyInto(DataSourceSurface* aSurface,
const IntSize& aReadSize) const {
D3D11_MAPPED_SUBRESOURCE map;
HRESULT hr = mContext->Map(mTexture, 0, D3D11_MAP_READ, 0, &map);
if (FAILED(hr)) {
return false;
}
RefPtr<DataSourceSurface> sourceSurface =
Factory::CreateWrappingDataSourceSurface(static_cast<uint8_t*>(map.pData),
map.RowPitch, mSize,
SurfaceFormat::B8G8R8A8);
bool result;
{
DataSourceSurface::ScopedMap sourceMap(sourceSurface,
DataSourceSurface::READ);
DataSourceSurface::ScopedMap destMap(aSurface, DataSourceSurface::WRITE);
result = SwizzleData(sourceMap.GetData(), sourceMap.GetStride(),
SurfaceFormat::B8G8R8A8, destMap.GetData(),
destMap.GetStride(), aSurface->GetFormat(), aReadSize);
}
mContext->Unmap(mTexture, 0);
return result;
}
CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent,
widget::CompositorWidget* aWidget)
: Compositor(aWidget, aParent),
mWindowRTCopy(nullptr),
mAttachments(nullptr),
mHwnd(nullptr),
mDisableSequenceForNextFrame(false),
mAllowPartialPresents(false),
mIsDoubleBuffered(false),
mVerifyBuffersFailed(false),
mUseMutexOnPresent(false) {
mUseMutexOnPresent = StaticPrefs::gfx_use_mutex_on_present_AtStartup();
}
CompositorD3D11::~CompositorD3D11() {}
template <typename VertexType>
void CompositorD3D11::SetVertexBuffer(ID3D11Buffer* aBuffer) {
UINT size = sizeof(VertexType);
UINT offset = 0;
mContext->IASetVertexBuffers(0, 1, &aBuffer, &size, &offset);
}
bool CompositorD3D11::SupportsLayerGeometry() const {
return StaticPrefs::layers_geometry_d3d11_enabled();
}
bool CompositorD3D11::UpdateDynamicVertexBuffer(
const nsTArray<gfx::TexturedTriangle>& aTriangles) {
HRESULT hr;
// Resize the dynamic vertex buffer if needed.
if (!mAttachments->EnsureTriangleBuffer(aTriangles.Length())) {
return false;
}
D3D11_MAPPED_SUBRESOURCE resource{};
hr = mContext->Map(mAttachments->mDynamicVertexBuffer, 0,
D3D11_MAP_WRITE_DISCARD, 0, &resource);
if (Failed(hr, "map dynamic vertex buffer")) {
return false;
}
const nsTArray<TexturedVertex> vertices =
TexturedTrianglesToVertexArray(aTriangles);
memcpy(resource.pData, vertices.Elements(),
vertices.Length() * sizeof(TexturedVertex));
mContext->Unmap(mAttachments->mDynamicVertexBuffer, 0);
return true;
}
bool CompositorD3D11::Initialize(nsCString* const out_failureReason) {
ScopedGfxFeatureReporter reporter("D3D11 Layers");
HRESULT hr;
DeviceManagerDx::Get()->GetCompositorDevices(&mDevice, &mAttachments);
if (!mDevice) {
gfxCriticalNote << "[D3D11] failed to get compositor device.";
*out_failureReason = "FEATURE_FAILURE_D3D11_NO_DEVICE";
return false;
}
if (!mAttachments || !mAttachments->IsValid()) {
gfxCriticalNote << "[D3D11] failed to get compositor device attachments";
*out_failureReason =
mAttachments ? mAttachments->GetFailureId()
: NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_ATTACHMENTS");
return false;
}
mDevice->GetImmediateContext(getter_AddRefs(mContext));
if (!mContext) {
gfxCriticalNote << "[D3D11] failed to get immediate context";
*out_failureReason = "FEATURE_FAILURE_D3D11_CONTEXT";
return false;
}
mDiagnostics = MakeUnique<DiagnosticsD3D11>(mDevice, mContext);
mFeatureLevel = mDevice->GetFeatureLevel();
mHwnd = mWidget->AsWindows()->GetHwnd();
memset(&mVSConstants, 0, sizeof(VertexShaderConstants));
RefPtr<IDXGIDevice> dxgiDevice;
RefPtr<IDXGIAdapter> dxgiAdapter;
mDevice->QueryInterface(dxgiDevice.StartAssignment());
dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter));
{
RefPtr<IDXGIFactory> dxgiFactory;
dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.StartAssignment()));
RefPtr<IDXGIFactory2> dxgiFactory2;
hr = dxgiFactory->QueryInterface(
(IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
#if (_WIN32_WINDOWS_MAXVER >= 0x0A00)
if (gfxVars::UseDoubleBufferingWithCompositor() && SUCCEEDED(hr) &&
dxgiFactory2) {
// DXGI_SCALING_NONE is not available on Windows 7 with 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. When using
// EFFECT_SEQUENTIAL it looks like windows doesn't stretch the surface
// when resizing. We chose not to run this before Windows 10 because it
// appears sometimes this breaks our ability to test ASAP compositing.
RefPtr<IDXGISwapChain1> swapChain;
DXGI_SWAP_CHAIN_DESC1 swapDesc;
::ZeroMemory(&swapDesc, sizeof(swapDesc));
swapDesc.Width = 0;
swapDesc.Height = 0;
swapDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapDesc.SampleDesc.Count = 1;
swapDesc.SampleDesc.Quality = 0;
swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapDesc.BufferCount = 2;
swapDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
swapDesc.Scaling = DXGI_SCALING_NONE;
mIsDoubleBuffered = true;
swapDesc.Flags = 0;
/**
* Create a swap chain, this swap chain will contain the backbuffer for
* the window we draw to. The front buffer is the full screen front
* buffer.
*/
hr = dxgiFactory2->CreateSwapChainForHwnd(mDevice, mHwnd, &swapDesc,
nullptr, nullptr,
getter_AddRefs(swapChain));
if (SUCCEEDED(hr)) {
DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
swapChain->SetBackgroundColor(&color);
mSwapChain = swapChain;
}
}
// In some configurations double buffering may have failed with an
// ACCESS_DENIED error.
if (!mSwapChain)
#endif
{
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 = mHwnd;
swapDesc.Windowed = TRUE;
swapDesc.Flags = 0;
swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
/**
* Create a swap chain, this swap chain will contain the backbuffer for
* the window we draw to. The front buffer is the full screen front
* buffer.
*/
hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc,
getter_AddRefs(mSwapChain));
if (Failed(hr, "create swap chain")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_SWAP_CHAIN";
return false;
}
}
// We need this because we don't want DXGI to respond to Alt+Enter.
dxgiFactory->MakeWindowAssociation(mHwnd, DXGI_MWA_NO_WINDOW_CHANGES);
}
if (!mWidget->InitCompositor(this)) {
*out_failureReason = "FEATURE_FAILURE_D3D11_INIT_COMPOSITOR";
return false;
}
mAllowPartialPresents = CanUsePartialPresents(mDevice);
reporter.SetSuccessful();
return true;
}
bool CanUsePartialPresents(ID3D11Device* aDevice) {
if (StaticPrefs::gfx_partialpresent_force() > 0) {
return true;
}
if (StaticPrefs::gfx_partialpresent_force() < 0) {
return false;
}
if (DeviceManagerDx::Get()->IsWARP()) {
return true;
}
DXGI_ADAPTER_DESC desc;
if (!D3D11Checks::GetDxgiDesc(aDevice, &desc)) {
return false;
}
// We have to disable partial presents on NVIDIA (bug 1189940).
if (desc.VendorId == 0x10de) {
return false;
}
return true;
}
already_AddRefed<DataTextureSource> CompositorD3D11::CreateDataTextureSource(
TextureFlags aFlags) {
RefPtr<DataTextureSource> result =
new DataTextureSourceD3D11(gfx::SurfaceFormat::UNKNOWN, this, aFlags);
return result.forget();
}
TextureFactoryIdentifier CompositorD3D11::GetTextureFactoryIdentifier() {
TextureFactoryIdentifier ident;
ident.mMaxTextureSize = GetMaxTextureSize();
ident.mParentProcessType = XRE_GetProcessType();
ident.mParentBackend = LayersBackend::LAYERS_D3D11;
if (mWidget) {
ident.mUseCompositorWnd = !!mWidget->AsWindows()->GetCompositorHwnd();
}
if (mAttachments->mSyncObject) {
ident.mSyncHandle = mAttachments->mSyncObject->GetSyncHandle();
}
return ident;
}
bool CompositorD3D11::CanUseCanvasLayerForSize(const gfx::IntSize& aSize) {
int32_t maxTextureSize = GetMaxTextureSize();
if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) {
return false;
}
return true;
}
int32_t CompositorD3D11::GetMaxTextureSize() const {
return GetMaxTextureSizeForFeatureLevel(mFeatureLevel);
}
already_AddRefed<CompositingRenderTarget> CompositorD3D11::CreateRenderTarget(
const gfx::IntRect& aRect, SurfaceInitMode aInit) {
MOZ_ASSERT(!aRect.IsZeroArea());
if (aRect.IsZeroArea()) {
return nullptr;
}
CD3D11_TEXTURE2D_DESC desc(
DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1,
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
RefPtr<ID3D11Texture2D> texture;
HRESULT hr =
mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
if (FAILED(hr) || !texture) {
gfxCriticalNote << "Failed in CreateRenderTarget " << hexa(hr);
return nullptr;
}
RefPtr<CompositingRenderTargetD3D11> rt =
new CompositingRenderTargetD3D11(texture, aRect.TopLeft());
rt->SetSize(IntSize(aRect.Width(), aRect.Height()));
if (aInit == INIT_MODE_CLEAR) {
FLOAT clear[] = {0, 0, 0, 0};
mContext->ClearRenderTargetView(rt->mRTView, clear);
}
return rt.forget();
}
RefPtr<ID3D11Texture2D> CompositorD3D11::CreateTexture(
const gfx::IntRect& aRect, const CompositingRenderTarget* aSource,
const gfx::IntPoint& aSourcePoint) {
MOZ_ASSERT(!aRect.IsZeroArea());
if (aRect.IsZeroArea()) {
return nullptr;
}
CD3D11_TEXTURE2D_DESC desc(
DXGI_FORMAT_B8G8R8A8_UNORM, aRect.Width(), aRect.Height(), 1, 1,
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
RefPtr<ID3D11Texture2D> texture;
HRESULT hr =
mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
if (FAILED(hr) || !texture) {
gfxCriticalNote << "Failed in CreateRenderTargetFromSource " << hexa(hr);
HandleError(hr);
return nullptr;
}
if (aSource) {
const CompositingRenderTargetD3D11* sourceD3D11 =
static_cast<const CompositingRenderTargetD3D11*>(aSource);
const IntSize& srcSize = sourceD3D11->GetSize();
MOZ_ASSERT(srcSize.width >= 0 && srcSize.height >= 0,
"render targets should have nonnegative sizes");
IntRect srcRect(IntPoint(), srcSize);
IntRect copyRect(aSourcePoint, aRect.Size());
if (!srcRect.Contains(copyRect)) {
NS_WARNING("Could not copy the whole copy rect from the render target");
}
copyRect = copyRect.Intersect(srcRect);
if (!copyRect.IsEmpty()) {
D3D11_BOX copyBox;
copyBox.front = 0;
copyBox.back = 1;
copyBox.left = copyRect.X();
copyBox.top = copyRect.Y();
copyBox.right = copyRect.XMost();
copyBox.bottom = copyRect.YMost();
mContext->CopySubresourceRegion(
texture, 0, 0, 0, 0, sourceD3D11->GetD3D11Texture(), 0, &copyBox);
}
}
return texture;
}
already_AddRefed<CompositingRenderTarget>
CompositorD3D11::CreateRenderTargetFromSource(
const gfx::IntRect& aRect, const CompositingRenderTarget* aSource,
const gfx::IntPoint& aSourcePoint) {
RefPtr<ID3D11Texture2D> texture = CreateTexture(aRect, aSource, aSourcePoint);
if (!texture) {
return nullptr;
}
RefPtr<CompositingRenderTargetD3D11> rt =
new CompositingRenderTargetD3D11(texture, aRect.TopLeft());
rt->SetSize(aRect.Size());
return rt.forget();
}
bool CompositorD3D11::ShouldAllowFrameRecording() const {
#ifdef MOZ_GECKO_PROFILER
return mAllowFrameRecording ||
profiler_feature_active(ProfilerFeature::Screenshots);
#else
return mAllowFrameRecording;
#endif
}
already_AddRefed<CompositingRenderTarget>
CompositorD3D11::GetWindowRenderTarget() const {
if (!ShouldAllowFrameRecording()) {
return nullptr;
}
if (!mDefaultRT) {
return nullptr;
}
const IntSize size = mDefaultRT->GetSize();
RefPtr<ID3D11Texture2D> rtTexture;
if (!mWindowRTCopy || mWindowRTCopy->GetSize() != size) {
/*
* The compositor screenshots infrastructure is going to scale down the
* render target returned by this method. However, mDefaultRT does not
* contain a texture created wth the D3D11_BIND_SHADER_RESOURCE flag, so if
* we were to simply return mDefaultRT then scaling would fail.
*/
CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, size.width,
size.height, 1, 1, D3D11_BIND_SHADER_RESOURCE);
HRESULT hr =
mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(rtTexture));
if (FAILED(hr)) {
return nullptr;
}
mWindowRTCopy = MakeRefPtr<CompositingRenderTargetD3D11>(
rtTexture, IntPoint(0, 0), DXGI_FORMAT_B8G8R8A8_UNORM);
mWindowRTCopy->SetSize(size);
} else {
rtTexture = mWindowRTCopy->GetD3D11Texture();
}
const RefPtr<ID3D11Texture2D> sourceTexture = mDefaultRT->GetD3D11Texture();
mContext->CopyResource(rtTexture, sourceTexture);
return RefPtr<CompositingRenderTarget>(
static_cast<CompositingRenderTarget*>(mWindowRTCopy))
.forget();
}
bool CompositorD3D11::ReadbackRenderTarget(CompositingRenderTarget* aSource,
AsyncReadbackBuffer* aDest) {
RefPtr<CompositingRenderTargetD3D11> srcTexture =
static_cast<CompositingRenderTargetD3D11*>(aSource);
RefPtr<AsyncReadbackBufferD3D11> destBuffer =
static_cast<AsyncReadbackBufferD3D11*>(aDest);
mContext->CopyResource(destBuffer->GetTexture(),
srcTexture->GetD3D11Texture());
return true;
}
already_AddRefed<AsyncReadbackBuffer>
CompositorD3D11::CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) {
RefPtr<ID3D11Texture2D> texture;
CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width,
aSize.height, 1, 1, 0, D3D11_USAGE_STAGING,
D3D11_CPU_ACCESS_READ);
HRESULT hr =
mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
if (FAILED(hr)) {
HandleError(hr);
return nullptr;
}
return MakeAndAddRef<AsyncReadbackBufferD3D11>(mContext, texture, aSize);
}
bool CompositorD3D11::BlitRenderTarget(CompositingRenderTarget* aSource,
const gfx::IntSize& aSourceSize,
const gfx::IntSize& aDestSize) {
RefPtr<CompositingRenderTargetD3D11> source =
static_cast<CompositingRenderTargetD3D11*>(aSource);
RefPtr<TexturedEffect> texturedEffect = CreateTexturedEffect(
SurfaceFormat::B8G8R8A8, source, SamplingFilter::LINEAR, true);
texturedEffect->mTextureCoords =
Rect(0, 0, Float(aSourceSize.width) / Float(source->GetSize().width),
Float(aSourceSize.height) / Float(source->GetSize().height));
EffectChain effect;
effect.mPrimaryEffect = texturedEffect;
const Float scaleX = Float(aDestSize.width) / Float(aSourceSize.width);
const Float scaleY = Float(aDestSize.height) / (aSourceSize.height);
const Matrix4x4 transform = Matrix4x4::Scaling(scaleX, scaleY, 1.0f);
const Rect sourceRect(0, 0, aSourceSize.width, aSourceSize.height);
DrawQuad(sourceRect, IntRect(0, 0, aDestSize.width, aDestSize.height), effect,
1.0f, transform, sourceRect);
return true;
}
bool CompositorD3D11::CopyBackdrop(const gfx::IntRect& aRect,
RefPtr<ID3D11Texture2D>* aOutTexture,
RefPtr<ID3D11ShaderResourceView>* aOutView) {
RefPtr<ID3D11Texture2D> texture =
CreateTexture(aRect, mCurrentRT, aRect.TopLeft());
if (!texture) {
return false;
}
CD3D11_SHADER_RESOURCE_VIEW_DESC desc(D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_B8G8R8A8_UNORM);
RefPtr<ID3D11ShaderResourceView> srv;
HRESULT hr =
mDevice->CreateShaderResourceView(texture, &desc, getter_AddRefs(srv));
if (FAILED(hr) || !srv) {
return false;
}
*aOutTexture = texture.forget();
*aOutView = srv.forget();
return true;
}
void CompositorD3D11::SetRenderTarget(CompositingRenderTarget* aRenderTarget) {
MOZ_ASSERT(aRenderTarget);
CompositingRenderTargetD3D11* newRT =
static_cast<CompositingRenderTargetD3D11*>(aRenderTarget);
if (mCurrentRT != newRT) {
mCurrentRT = newRT;
mCurrentRT->BindRenderTarget(mContext);
}
if (newRT->HasComplexProjection()) {
gfx::Matrix4x4 projection;
bool depthEnable;
float zNear, zFar;
newRT->GetProjection(projection, depthEnable, zNear, zFar);
PrepareViewport(newRT->GetSize(), projection, zNear, zFar);
} else {
PrepareViewport(newRT->GetSize());
}
}
ID3D11PixelShader* CompositorD3D11::GetPSForEffect(Effect* aEffect,
const bool aUseBlendShader,
const MaskType aMaskType) {
if (aUseBlendShader) {
return mAttachments->mBlendShader[MaskType::MaskNone];
}
switch (aEffect->mType) {
case EffectTypes::SOLID_COLOR:
return mAttachments->mSolidColorShader[aMaskType];
case EffectTypes::RENDER_TARGET:
return mAttachments->mRGBAShader[aMaskType];
case EffectTypes::RGB: {
SurfaceFormat format =
static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat();
return (format == SurfaceFormat::B8G8R8A8 ||
format == SurfaceFormat::R8G8B8A8)
? mAttachments->mRGBAShader[aMaskType]
: mAttachments->mRGBShader[aMaskType];
}
case EffectTypes::NV12:
return mAttachments->mNV12Shader[aMaskType];
case EffectTypes::YCBCR:
return mAttachments->mYCbCrShader[aMaskType];
case EffectTypes::COMPONENT_ALPHA:
return mAttachments->mComponentAlphaShader[aMaskType];
default:
NS_WARNING("No shader to load");
return nullptr;
}
}
void CompositorD3D11::ClearRect(const gfx::Rect& aRect) {
if (aRect.IsEmpty()) {
return;
}
mContext->OMSetBlendState(mAttachments->mDisabledBlendState, sBlendFactor,
0xFFFFFFFF);
Matrix4x4 identity;
memcpy(&mVSConstants.layerTransform, &identity._11, 64);
mVSConstants.layerQuad = aRect;
mVSConstants.renderTargetOffset[0] = 0;
mVSConstants.renderTargetOffset[1] = 0;
mPSConstants.layerOpacity[0] = 1.0f;
D3D11_RECT scissor;
scissor.left = aRect.X();
scissor.right = aRect.XMost();
scissor.top = aRect.Y();
scissor.bottom = aRect.YMost();
mContext->RSSetScissorRects(1, &scissor);
mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
mContext->VSSetShader(mAttachments->mVSQuadShader[MaskType::MaskNone],
nullptr, 0);
mContext->PSSetShader(mAttachments->mSolidColorShader[MaskType::MaskNone],
nullptr, 0);
mPSConstants.layerColor[0] = 0;
mPSConstants.layerColor[1] = 0;
mPSConstants.layerColor[2] = 0;
mPSConstants.layerColor[3] = 0;
if (!UpdateConstantBuffers()) {
NS_WARNING("Failed to update shader constant buffers");
return;
}
mContext->Draw(4, 0);
// Restore the default blend state.
mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor,
0xFFFFFFFF);
}
static inline bool EffectHasPremultipliedAlpha(Effect* aEffect) {
if (aEffect->mType == EffectTypes::RGB) {
return static_cast<TexturedEffect*>(aEffect)->mPremultiplied;
}
return true;
}
static inline int EffectToBlendLayerType(Effect* aEffect) {
switch (aEffect->mType) {
case EffectTypes::SOLID_COLOR:
return PS_LAYER_COLOR;
case EffectTypes::RGB: {
gfx::SurfaceFormat format =
static_cast<TexturedEffect*>(aEffect)->mTexture->GetFormat();
return (format == gfx::SurfaceFormat::B8G8R8A8 ||
format == gfx::SurfaceFormat::R8G8B8A8)
? PS_LAYER_RGBA
: PS_LAYER_RGB;
}
case EffectTypes::RENDER_TARGET:
return PS_LAYER_RGBA;
case EffectTypes::YCBCR:
return PS_LAYER_YCBCR;
case EffectTypes::NV12:
return PS_LAYER_NV12;
default:
MOZ_ASSERT_UNREACHABLE("blending not supported for this layer type");
return 0;
}
}
void CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
const gfx::IntRect& aClipRect,
const EffectChain& aEffectChain,
gfx::Float aOpacity,
const gfx::Matrix4x4& aTransform,
const gfx::Rect& aVisibleRect) {
DrawGeometry(aRect, aRect, aClipRect, aEffectChain, aOpacity, aTransform,
aVisibleRect);
}
void CompositorD3D11::DrawTriangles(
const nsTArray<gfx::TexturedTriangle>& aTriangles, const gfx::Rect& aRect,
const gfx::IntRect& aClipRect, const EffectChain& aEffectChain,
gfx::Float aOpacity, const gfx::Matrix4x4& aTransform,
const gfx::Rect& aVisibleRect) {
DrawGeometry(aTriangles, aRect, aClipRect, aEffectChain, aOpacity, aTransform,
aVisibleRect);
}
void CompositorD3D11::PrepareDynamicVertexBuffer() {
mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
mContext->IASetInputLayout(mAttachments->mDynamicInputLayout);
SetVertexBuffer<TexturedVertex>(mAttachments->mDynamicVertexBuffer);
}
void CompositorD3D11::PrepareStaticVertexBuffer() {
mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
mContext->IASetInputLayout(mAttachments->mInputLayout);
SetVertexBuffer<Vertex>(mAttachments->mVertexBuffer);
}
void CompositorD3D11::Draw(const nsTArray<gfx::TexturedTriangle>& aTriangles,
const gfx::Rect*) {
if (!UpdateConstantBuffers()) {
NS_WARNING("Failed to update shader constant buffers");
return;
}
PrepareDynamicVertexBuffer();
if (!UpdateDynamicVertexBuffer(aTriangles)) {
NS_WARNING("Failed to update shader dynamic buffers");
return;
}
mContext->Draw(3 * aTriangles.Length(), 0);
PrepareStaticVertexBuffer();
}
void CompositorD3D11::Draw(const gfx::Rect& aRect,
const gfx::Rect* aTexCoords) {
Rect layerRects[4] = {aRect};
Rect textureRects[4] = {};
size_t rects = 1;
if (aTexCoords) {
rects = DecomposeIntoNoRepeatRects(aRect, *aTexCoords, &layerRects,
&textureRects);
}
for (size_t i = 0; i < rects; i++) {
mVSConstants.layerQuad = layerRects[i];
mVSConstants.textureCoords = textureRects[i];
if (!UpdateConstantBuffers()) {
NS_WARNING("Failed to update shader constant buffers");
break;
}
mContext->Draw(4, 0);
}
}
ID3D11VertexShader* CompositorD3D11::GetVSForGeometry(
const nsTArray<gfx::TexturedTriangle>& aTriangles,
const bool aUseBlendShaders, const MaskType aMaskType) {
return aUseBlendShaders ? mAttachments->mVSDynamicBlendShader[aMaskType]
: mAttachments->mVSDynamicShader[aMaskType];
}
ID3D11VertexShader* CompositorD3D11::GetVSForGeometry(
const gfx::Rect& aRect, const bool aUseBlendShaders,
const MaskType aMaskType) {
return aUseBlendShaders ? mAttachments->mVSQuadBlendShader[aMaskType]
: mAttachments->mVSQuadShader[aMaskType];
}
template <typename Geometry>
void CompositorD3D11::DrawGeometry(const Geometry& aGeometry,
const gfx::Rect& aRect,
const gfx::IntRect& aClipRect,
const EffectChain& aEffectChain,
gfx::Float aOpacity,
const gfx::Matrix4x4& aTransform,
const gfx::Rect& aVisibleRect) {
if (mCurrentClip.IsEmpty()) {
return;
}
MOZ_ASSERT(mCurrentRT, "No render target");
memcpy(&mVSConstants.layerTransform, &aTransform._11, 64);
IntPoint origin = mCurrentRT->GetOrigin();
mVSConstants.renderTargetOffset[0] = origin.x;
mVSConstants.renderTargetOffset[1] = origin.y;
mPSConstants.layerOpacity[0] = aOpacity;
bool restoreBlendMode = false;
MaskType maskType = MaskType::MaskNone;
if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
maskType = MaskType::Mask;
EffectMask* maskEffect = static_cast<EffectMask*>(
aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
TextureSourceD3D11* source = maskEffect->mMaskTexture->AsSourceD3D11();
if (!source) {
NS_WARNING("Missing texture source!");
return;
}
ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
mContext->PSSetShaderResources(TexSlot::Mask, 1, &srView);
const gfx::Matrix4x4& maskTransform = maskEffect->mMaskTransform;
NS_ASSERTION(maskTransform.Is2D(),
"How did we end up with a 3D transform here?!");
Rect bounds = Rect(Point(), Size(maskEffect->mSize));
bounds = maskTransform.As2D().TransformBounds(bounds);
Matrix4x4 transform;
transform._11 = 1.0f / bounds.Width();
transform._22 = 1.0f / bounds.Height();
transform._41 = float(-bounds.X()) / bounds.Width();
transform._42 = float(-bounds.Y()) / bounds.Height();
memcpy(mVSConstants.maskTransform, &transform._11, 64);
}
D3D11_RECT scissor;
IntRect clipRect(aClipRect.X(), aClipRect.Y(), aClipRect.Width(),
aClipRect.Height());
if (mCurrentRT == mDefaultRT) {
clipRect = clipRect.Intersect(mCurrentClip);
}
if (clipRect.IsEmpty()) {
return;
}
scissor.left = clipRect.X();
scissor.right = clipRect.XMost();
scissor.top = clipRect.Y();
scissor.bottom = clipRect.YMost();
bool useBlendShaders = false;
RefPtr<ID3D11Texture2D> mixBlendBackdrop;
gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
EffectBlendMode* blendEffect = static_cast<EffectBlendMode*>(
aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
blendMode = blendEffect->mBlendMode;
// If the blend operation needs to read from the backdrop, copy the
// current render target into a new texture and bind it now.
if (BlendOpIsMixBlendMode(blendMode)) {
gfx::Matrix4x4 backdropTransform;
gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform,
&backdropTransform);
RefPtr<ID3D11ShaderResourceView> srv;
if (CopyBackdrop(rect, &mixBlendBackdrop, &srv) &&
mAttachments->InitBlendShaders()) {
useBlendShaders = true;
ID3D11ShaderResourceView* srView = srv.get();
mContext->PSSetShaderResources(TexSlot::Backdrop, 1, &srView);
memcpy(&mVSConstants.backdropTransform, &backdropTransform._11, 64);
mPSConstants.blendConfig[0] =
EffectToBlendLayerType(aEffectChain.mPrimaryEffect);
mPSConstants.blendConfig[1] = int(maskType);
mPSConstants.blendConfig[2] = BlendOpToShaderConstant(blendMode);
mPSConstants.blendConfig[3] =
EffectHasPremultipliedAlpha(aEffectChain.mPrimaryEffect);
}
}
}
mContext->RSSetScissorRects(1, &scissor);
RefPtr<ID3D11VertexShader> vertexShader =
GetVSForGeometry(aGeometry, useBlendShaders, maskType);
RefPtr<ID3D11PixelShader> pixelShader =
GetPSForEffect(aEffectChain.mPrimaryEffect, useBlendShaders, maskType);
mContext->VSSetShader(vertexShader, nullptr, 0);
mContext->PSSetShader(pixelShader, nullptr, 0);
const Rect* pTexCoordRect = nullptr;
switch (aEffectChain.mPrimaryEffect->mType) {
case EffectTypes::SOLID_COLOR: {
DeviceColor color =
static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get())
->mColor;
mPSConstants.layerColor[0] = color.r * color.a * aOpacity;
mPSConstants.layerColor[1] = color.g * color.a * aOpacity;
mPSConstants.layerColor[2] = color.b * color.a * aOpacity;
mPSConstants.layerColor[3] = color.a * aOpacity;
} break;
case EffectTypes::RGB:
case EffectTypes::RENDER_TARGET: {
TexturedEffect* texturedEffect =
static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
pTexCoordRect = &texturedEffect->mTextureCoords;
TextureSourceD3D11* source = texturedEffect->mTexture->AsSourceD3D11();
if (!source) {
NS_WARNING("Missing texture source!");
return;
}
ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
mContext->PSSetShaderResources(TexSlot::RGB, 1, &srView);
if (!texturedEffect->mPremultiplied) {
mContext->OMSetBlendState(mAttachments->mNonPremulBlendState,
sBlendFactor, 0xFFFFFFFF);
restoreBlendMode = true;
}
SetSamplerForSamplingFilter(texturedEffect->mSamplingFilter);
} break;
case EffectTypes::NV12: {
EffectNV12* effectNV12 =
static_cast<EffectNV12*>(aEffectChain.mPrimaryEffect.get());
pTexCoordRect = &effectNV12->mTextureCoords;
TextureSourceD3D11* source = effectNV12->mTexture->AsSourceD3D11();
if (!source) {
NS_WARNING("Missing texture source!");
return;
}
RefPtr<ID3D11Texture2D> texture = source->GetD3D11Texture();
if (!texture) {
NS_WARNING("No texture found in texture source!");
}
D3D11_TEXTURE2D_DESC sourceDesc;
texture->GetDesc(&sourceDesc);
MOZ_DIAGNOSTIC_ASSERT(sourceDesc.Format == DXGI_FORMAT_NV12 ||
sourceDesc.Format == DXGI_FORMAT_P010 ||
sourceDesc.Format == DXGI_FORMAT_P016);
// Might want to cache these for efficiency.
RefPtr<ID3D11ShaderResourceView> srViewY;
RefPtr<ID3D11ShaderResourceView> srViewCbCr;
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc =
CD3D11_SHADER_RESOURCE_VIEW_DESC(D3D11_SRV_DIMENSION_TEXTURE2D,
sourceDesc.Format == DXGI_FORMAT_NV12
? DXGI_FORMAT_R8_UNORM
: DXGI_FORMAT_R16_UNORM);
mDevice->CreateShaderResourceView(texture, &srvDesc,
getter_AddRefs(srViewY));
srvDesc.Format = sourceDesc.Format == DXGI_FORMAT_NV12
? DXGI_FORMAT_R8G8_UNORM
: DXGI_FORMAT_R16G16_UNORM;
mDevice->CreateShaderResourceView(texture, &srvDesc,
getter_AddRefs(srViewCbCr));
ID3D11ShaderResourceView* views[] = {srViewY, srViewCbCr};
mContext->PSSetShaderResources(TexSlot::Y, 2, views);
const float* yuvToRgb =
gfxUtils::YuvToRgbMatrix4x3RowMajor(effectNV12->mYUVColorSpace);
memcpy(&mPSConstants.yuvColorMatrix, yuvToRgb,
sizeof(mPSConstants.yuvColorMatrix));
mPSConstants.vCoefficient[0] =
RescalingFactorForColorDepth(effectNV12->mColorDepth);
SetSamplerForSamplingFilter(effectNV12->mSamplingFilter);
} break;
case EffectTypes::YCBCR: {
EffectYCbCr* ycbcrEffect =
static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
SetSamplerForSamplingFilter(SamplingFilter::LINEAR);
pTexCoordRect = &ycbcrEffect->mTextureCoords;
const int Y = 0, Cb = 1, Cr = 2;
TextureSource* source = ycbcrEffect->mTexture;
if (!source) {
NS_WARNING("No texture to composite");
return;
}
if (!source->GetSubSource(Y) || !source->GetSubSource(Cb) ||
!source->GetSubSource(Cr)) {
// This can happen if we failed to upload the textures, most likely
// because of unsupported dimensions (we don't tile YCbCr textures).
return;
}
const float* yuvToRgb =
gfxUtils::YuvToRgbMatrix4x3RowMajor(ycbcrEffect->mYUVColorSpace);
memcpy(&mPSConstants.yuvColorMatrix, yuvToRgb,
sizeof(mPSConstants.yuvColorMatrix));
// Adjust range according to the bit depth.
mPSConstants.vCoefficient[0] =
RescalingFactorForColorDepth(ycbcrEffect->mColorDepth);
TextureSourceD3D11* sourceY = source->GetSubSource(Y)->AsSourceD3D11();
TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11();
TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11();
ID3D11ShaderResourceView* srViews[3] = {
sourceY->GetShaderResourceView(), sourceCb->GetShaderResourceView(),
sourceCr->GetShaderResourceView()};
mContext->PSSetShaderResources(TexSlot::Y, 3, srViews);
} break;
case EffectTypes::COMPONENT_ALPHA: {
MOZ_ASSERT(LayerManager::LayersComponentAlphaEnabled());
MOZ_ASSERT(mAttachments->mComponentBlendState);
EffectComponentAlpha* effectComponentAlpha =
static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get());
TextureSourceD3D11* sourceOnWhite =
effectComponentAlpha->mOnWhite->AsSourceD3D11();
TextureSourceD3D11* sourceOnBlack =
effectComponentAlpha->mOnBlack->AsSourceD3D11();
if (!sourceOnWhite || !sourceOnBlack) {
NS_WARNING("Missing texture source(s)!");
return;
}
SetSamplerForSamplingFilter(effectComponentAlpha->mSamplingFilter);
pTexCoordRect = &effectComponentAlpha->mTextureCoords;
ID3D11ShaderResourceView* srViews[2] = {
sourceOnBlack->GetShaderResourceView(),
sourceOnWhite->GetShaderResourceView()};
mContext->PSSetShaderResources(TexSlot::RGB, 1, &srViews[0]);
mContext->PSSetShaderResources(TexSlot::RGBWhite, 1, &srViews[1]);
mContext->OMSetBlendState(mAttachments->mComponentBlendState,
sBlendFactor, 0xFFFFFFFF);
restoreBlendMode = true;
} break;
default:
NS_WARNING("Unknown shader type");
return;
}
Draw(aGeometry, pTexCoordRect);
if (restoreBlendMode) {
mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor,
0xFFFFFFFF);
}
}
Maybe<IntRect> CompositorD3D11::BeginFrameForWindow(
const nsIntRegion& aInvalidRegion, const Maybe<IntRect>& aClipRect,
const IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion) {
MOZ_RELEASE_ASSERT(!mTarget, "mTarget not cleared properly");
return BeginFrame(aInvalidRegion, aClipRect, aRenderBounds, aOpaqueRegion);
}
Maybe<IntRect> CompositorD3D11::BeginFrameForTarget(
const nsIntRegion& aInvalidRegion, const Maybe<IntRect>& aClipRect,
const IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion,
DrawTarget* aTarget, const IntRect& aTargetBounds) {
MOZ_RELEASE_ASSERT(!mTarget, "mTarget not cleared properly");
mTarget = aTarget; // Will be cleared in EndFrame().
mTargetBounds = aTargetBounds;
Maybe<IntRect> result =
BeginFrame(aInvalidRegion, aClipRect, aRenderBounds, aOpaqueRegion);
if (!result) {
// Composition has been aborted. Reset mTarget.
mTarget = nullptr;
}
return result;
}
void CompositorD3D11::BeginFrameForNativeLayers() {
MOZ_CRASH("Native layers are not implemented on Windows.");
}
Maybe<gfx::IntRect> CompositorD3D11::BeginRenderingToNativeLayer(
const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
const nsIntRegion& aOpaqueRegion, NativeLayer* aNativeLayer) {
MOZ_CRASH("Native layers are not implemented on Windows.");
}
void CompositorD3D11::EndRenderingToNativeLayer() {
MOZ_CRASH("Native layers are not implemented on Windows.");
}
Maybe<IntRect> CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
const Maybe<IntRect>& aClipRect,
const IntRect& aRenderBounds,
const nsIntRegion& aOpaqueRegion) {
// Don't composite if we are minimised. Other than for the sake of efficency,
// this is important because resizing our buffers when mimised will fail and
// cause a crash when we're restored.
NS_ASSERTION(mHwnd, "Couldn't find an HWND when initialising?");
if (mWidget->IsHidden()) {
// We are not going to render, and not going to call EndFrame so we have to
// read-unlock our textures to prevent them from accumulating.
ReadUnlockTextures();
return Nothing();
}
if (mDevice->GetDeviceRemovedReason() != S_OK) {
ReadUnlockTextures();
if (!mAttachments->IsDeviceReset()) {
gfxCriticalNote << "GFX: D3D11 skip BeginFrame with device-removed.";
// If we are in the GPU process then the main process doesn't
// know that a device reset has happened and needs to be informed
if (XRE_IsGPUProcess()) {
GPUParent::GetSingleton()->NotifyDeviceReset();
}
mAttachments->SetDeviceReset();
}
return Nothing();
}
LayoutDeviceIntSize oldSize = mSize;
EnsureSize();
IntRect rect = IntRect(IntPoint(0, 0), mSize.ToUnknownSize());
// Sometimes the invalid region is larger than we want to draw.
nsIntRegion invalidRegionSafe;
if (mSize != oldSize) {
invalidRegionSafe = rect;
} else {
invalidRegionSafe.And(aInvalidRegion, rect);
}
IntRect invalidRect = invalidRegionSafe.GetBounds();
IntRect clipRect = invalidRect;
if (aClipRect) {
clipRect.IntersectRect(clipRect, *aClipRect);
}
if (clipRect.IsEmpty()) {
CancelFrame();
return Nothing();
}
PrepareStaticVertexBuffer();
mBackBufferInvalid.Or(mBackBufferInvalid, invalidRegionSafe);
if (mIsDoubleBuffered) {
mFrontBufferInvalid.Or(mFrontBufferInvalid, invalidRegionSafe);
}
// We have to call UpdateRenderTarget after we've determined the invalid regi
// Failed to create a render target or the view.
if (!UpdateRenderTarget() || !mDefaultRT || !mDefaultRT->mRTView ||
mSize.width <= 0 || mSize.height <= 0) {
ReadUnlockTextures();
return Nothing();
}
mCurrentClip = mBackBufferInvalid.GetBounds();
mContext->RSSetState(mAttachments->mRasterizerState);
SetRenderTarget(mDefaultRT);
IntRegion regionToClear(mCurrentClip);
regionToClear.Sub(regionToClear, aOpaqueRegion);
ClearRect(Rect(regionToClear.GetBounds()));
mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor,
0xFFFFFFFF);
if (mAttachments->mSyncObject) {
if (!mAttachments->mSyncObject->Synchronize()) {
// It's timeout here. Since the timeout is related to the driver-removed,
// skip this frame.
return Nothing();
}
}
if (StaticPrefs::layers_acceleration_draw_fps()) {
uint32_t pixelsPerFrame = 0;
for (auto iter = mBackBufferInvalid.RectIter(); !iter.Done(); iter.Next()) {
pixelsPerFrame += iter.Get().Width() * iter.Get().Height();
}
mDiagnostics->Start(pixelsPerFrame);
}
return Some(rect);
}
void CompositorD3D11::NormalDrawingDone() { mDiagnostics->End(); }
void CompositorD3D11::EndFrame() {
if (!profiler_feature_active(ProfilerFeature::Screenshots) && mWindowRTCopy) {
mWindowRTCopy = nullptr;
}
if (!mDefaultRT) {
Compositor::EndFrame();
mTarget = nullptr;
return;
}
if (XRE_IsParentProcess() && mDevice->GetDeviceRemovedReason() != S_OK) {
gfxCriticalNote << "GFX: D3D11 skip EndFrame with device-removed.";
Compositor::EndFrame();
mTarget = nullptr;
mCurrentRT = nullptr;
return;
}
LayoutDeviceIntSize oldSize = mSize;
EnsureSize();
if (mSize.width <= 0 || mSize.height <= 0) {
Compositor::EndFrame();
mTarget = nullptr;
return;
}
RefPtr<ID3D11Query> query;
CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
mDevice->CreateQuery(&desc, getter_AddRefs(query));
if (query) {
mContext->End(query);
}
if (oldSize == mSize) {
Present();
if (StaticPrefs::gfx_compositor_clearstate()) {
mContext->ClearState();
}
} else {
mDiagnostics->Cancel();
}
// Block until the previous frame's work has been completed.
if (mQuery) {
BOOL result;
WaitForFrameGPUQuery(mDevice, mContext, mQuery, &result);
}
// Store the query for this frame so we can flush it next time.
mQuery = query;
Compositor::EndFrame();
mTarget = nullptr;
mCurrentRT = nullptr;
}
void CompositorD3D11::GetFrameStats(GPUStats* aStats) {
mDiagnostics->Query(aStats);
}
void CompositorD3D11::Present() {
UINT presentInterval = 0;
bool isWARP = DeviceManagerDx::Get()->IsWARP();
if (isWARP) {
// When we're using WARP we cannot present immediately as it causes us
// to tear when rendering. When not using WARP it appears the DWM takes
// care of tearing for us.
presentInterval = 1;
}
// This must be called before present so our back buffer has the validated
// window content.
if (mTarget) {
PaintToTarget();
}
RefPtr<IDXGISwapChain1> chain;
HRESULT hr =
mSwapChain->QueryInterface((IDXGISwapChain1**)getter_AddRefs(chain));
RefPtr<IDXGIKeyedMutex> mutex;
if (mUseMutexOnPresent && mAttachments->mSyncObject) {
SyncObjectD3D11Host* d3dSyncObj =
(SyncObjectD3D11Host*)mAttachments->mSyncObject.get();
mutex = d3dSyncObj->GetKeyedMutex();
MOZ_ASSERT(mutex);
}
if (SUCCEEDED(hr) && mAllowPartialPresents) {
DXGI_PRESENT_PARAMETERS params;
PodZero(&params);
params.DirtyRectsCount = mBackBufferInvalid.GetNumRects();
StackArray<RECT, 4> rects(params.DirtyRectsCount);
uint32_t i = 0;
for (auto iter = mBackBufferInvalid.RectIter(); !iter.Done(); iter.Next()) {
const IntRect& r = iter.Get();
rects[i].left = r.X();
rects[i].top = r.Y();
rects[i].bottom = r.YMost();
rects[i].right = r.XMost();
i++;
}
params.pDirtyRects = params.DirtyRectsCount ? rects.data() : nullptr;
if (mutex) {
hr = mutex->AcquireSync(0, 2000);
NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
}
chain->Present1(
presentInterval,
mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0,
&params);
if (mutex) {
mutex->ReleaseSync(0);
}
} else {
if (mutex) {
hr = mutex->AcquireSync(0, 2000);
NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
}
hr = mSwapChain->Present(
0, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
if (mutex) {
mutex->ReleaseSync(0);
}
if (FAILED(hr)) {
gfxCriticalNote << "D3D11 swap chain preset failed " << hexa(hr);
HandleError(hr);
}
}
if (mIsDoubleBuffered) {
mBackBufferInvalid = mFrontBufferInvalid;
mFrontBufferInvalid.SetEmpty();
} else {
mBackBufferInvalid.SetEmpty();
}
mDisableSequenceForNextFrame = false;
}
void CompositorD3D11::CancelFrame(bool aNeedFlush) {
ReadUnlockTextures();
// Flush the context, otherwise the driver might hold some resources alive
// until the next flush or present.
if (aNeedFlush) {
mContext->Flush();
}
}
void CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize) {
// This view matrix translates coordinates from 0..width and 0..height to
// -1..1 on the X axis, and -1..1 on the Y axis (flips the Y coordinate)
Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
viewMatrix.PreScale(1.0f, -1.0f);
Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
projection._33 = 0.0f;
PrepareViewport(aSize, projection, 0.0f, 1.0f);
}
void CompositorD3D11::ForcePresent() {
LayoutDeviceIntSize size = mWidget->GetClientSize();
DXGI_SWAP_CHAIN_DESC desc;
mSwapChain->GetDesc(&desc);
if (desc.BufferDesc.Width == size.width &&
desc.BufferDesc.Height == size.height) {
mSwapChain->Present(0, 0);
if (mIsDoubleBuffered) {
// Make sure we present what was the front buffer before that we know is
// completely valid. This non v-synced present should be pretty much
// 'free' for a flip chain.
mSwapChain->Present(0, 0);
}
}
}
void CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize,
const gfx::Matrix4x4& aProjection,
float aZNear, float aZFar) {
D3D11_VIEWPORT viewport;
viewport.MaxDepth = aZFar;
viewport.MinDepth = aZNear;
viewport.Width = aSize.width;
viewport.Height = aSize.height;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
mContext->RSSetViewports(1, &viewport);
memcpy(&mVSConstants.projection, &aProjection._11,
sizeof(mVSConstants.projection));
}
void CompositorD3D11::EnsureSize() { mSize = mWidget->GetClientSize(); }
bool CompositorD3D11::VerifyBufferSize() {
mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
DXGI_SWAP_CHAIN_DESC swapDesc;
HRESULT hr;
hr = mSwapChain->GetDesc(&swapDesc);
if (FAILED(hr)) {
gfxCriticalError() << "Failed to get the description " << hexa(hr) << ", "
<< mSize << ", " << (int)mVerifyBuffersFailed;
HandleError(hr);
return false;
}
if (((swapDesc.BufferDesc.Width == mSize.width &&
swapDesc.BufferDesc.Height == mSize.height) ||
mSize.width <= 0 || mSize.height <= 0) &&
!mVerifyBuffersFailed) {
return true;
}
ID3D11RenderTargetView* view = nullptr;
mContext->OMSetRenderTargets(1, &view, nullptr);
if (mDefaultRT) {
RefPtr<ID3D11RenderTargetView> rtView = mDefaultRT->mRTView;
RefPtr<ID3D11ShaderResourceView> srView = mDefaultRT->mSRV;
// Make sure the texture, which belongs to the swapchain, is destroyed
// before resizing the swapchain.
if (mCurrentRT == mDefaultRT) {
mCurrentRT = nullptr;
}
MOZ_ASSERT(mDefaultRT->hasOneRef());
mDefaultRT = nullptr;
RefPtr<ID3D11Resource> resource;
rtView->GetResource(getter_AddRefs(resource));
ULONG newRefCnt = rtView.forget().take()->Release();
if (newRefCnt > 0) {
gfxCriticalError() << "mRTView not destroyed on final release! RefCnt: "
<< newRefCnt;
}
if (srView) {
newRefCnt = srView.forget().take()->Release();
if (newRefCnt > 0) {
gfxCriticalError() << "mSRV not destroyed on final release! RefCnt: "
<< newRefCnt;
}
}
newRefCnt = resource.forget().take()->Release();
if (newRefCnt > 0) {
gfxCriticalError()
<< "Unexpecting lingering references to backbuffer! RefCnt: "
<< newRefCnt;
}
}
hr = mSwapChain->ResizeBuffers(0, mSize.width, mSize.height,
DXGI_FORMAT_B8G8R8A8_UNORM, 0);
mVerifyBuffersFailed = FAILED(hr);
if (mVerifyBuffersFailed) {
gfxCriticalNote << "D3D11 swap resize buffers failed " << hexa(hr) << " on "
<< mSize;
HandleError(hr);
}
mBackBufferInvalid = mFrontBufferInvalid =
IntRect(0, 0, mSize.width, mSize.height);
return !mVerifyBuffersFailed;
}
bool CompositorD3D11::UpdateRenderTarget() {
HRESULT hr;
RefPtr<ID3D11Texture2D> backBuf;
if (!VerifyBufferSize()) {
gfxCriticalNote << "Failed VerifyBufferSize in UpdateRenderTarget "
<< mSize;
return false;
}
if (mSize.width <= 0 || mSize.height <= 0) {
gfxCriticalNote << "Invalid size in UpdateRenderTarget " << mSize << ", "
<< (int)mVerifyBuffersFailed;
return false;
}
hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
(void**)backBuf.StartAssignment());
if (hr == DXGI_ERROR_INVALID_CALL) {
// This happens on some GPUs/drivers when there's a TDR.
if (mDevice->GetDeviceRemovedReason() != S_OK) {
gfxCriticalError() << "GetBuffer returned invalid call! " << mSize << ", "
<< (int)mVerifyBuffersFailed;
return false;
}
}
if (FAILED(hr)) {
gfxCriticalNote << "Failed in UpdateRenderTarget " << hexa(hr) << ", "
<< mSize << ", " << (int)mVerifyBuffersFailed;
HandleError(hr);
return false;
}
IntRegion validFront;
validFront.Sub(mBackBufferInvalid, mFrontBufferInvalid);
if (!validFront.IsEmpty()) {
RefPtr<ID3D11Texture2D> frontBuf;
hr = mSwapChain->GetBuffer(1, __uuidof(ID3D11Texture2D),
(void**)frontBuf.StartAssignment());
if (SUCCEEDED(hr)) {
for (auto iter = validFront.RectIter(); !iter.Done(); iter.Next()) {
const IntRect& rect = iter.Get();
D3D11_BOX box;
box.back = 1;
box.front = 0;
box.left = rect.X();
box.right = rect.XMost();
box.top = rect.Y();
box.bottom = rect.YMost();
mContext->CopySubresourceRegion(backBuf, 0, rect.X(), rect.Y(), 0,
frontBuf, 0, &box);
}
mBackBufferInvalid = mFrontBufferInvalid;
}
}
mDefaultRT = new CompositingRenderTargetD3D11(backBuf, IntPoint(0, 0));
mDefaultRT->SetSize(mSize.ToUnknownSize());
return true;
}
bool CompositorD3D11::UpdateConstantBuffers() {
HRESULT hr;
D3D11_MAPPED_SUBRESOURCE resource;
resource.pData = nullptr;
hr = mContext->Map(mAttachments->mVSConstantBuffer, 0,
D3D11_MAP_WRITE_DISCARD, 0, &resource);
if (FAILED(hr) || !resource.pData) {
gfxCriticalError() << "Failed to map VSConstantBuffer. Result: " << hexa(hr)
<< ", " << (int)mVerifyBuffersFailed;
HandleError(hr);
return false;
}
*(VertexShaderConstants*)resource.pData = mVSConstants;
mContext->Unmap(mAttachments->mVSConstantBuffer, 0);
resource.pData = nullptr;
hr = mContext->Map(mAttachments->mPSConstantBuffer, 0,
D3D11_MAP_WRITE_DISCARD, 0, &resource);
if (FAILED(hr) || !resource.pData) {
gfxCriticalError() << "Failed to map PSConstantBuffer. Result: " << hexa(hr)
<< ", " << (int)mVerifyBuffersFailed;
HandleError(hr);
return false;
}
*(PixelShaderConstants*)resource.pData = mPSConstants;
mContext->Unmap(mAttachments->mPSConstantBuffer, 0);
ID3D11Buffer* buffer = mAttachments->mVSConstantBuffer;
mContext->VSSetConstantBuffers(0, 1, &buffer);
buffer = mAttachments->mPSConstantBuffer;
mContext->PSSetConstantBuffers(0, 1, &buffer);
return true;
}
void CompositorD3D11::SetSamplerForSamplingFilter(
SamplingFilter aSamplingFilter) {
ID3D11SamplerState* sampler;
switch (aSamplingFilter) {
case SamplingFilter::POINT:
sampler = mAttachments->mPointSamplerState;
break;
case SamplingFilter::LINEAR:
default:
sampler = mAttachments->mLinearSamplerState;
break;
}
mContext->PSSetSamplers(0, 1, &sampler);
}
void CompositorD3D11::PaintToTarget() {
RefPtr<ID3D11Texture2D> backBuf;
HRESULT hr;
hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
(void**)backBuf.StartAssignment());
if (FAILED(hr)) {
gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
<< "Failed in PaintToTarget 1";
HandleError(hr);
return;
}
D3D11_TEXTURE2D_DESC bbDesc;
backBuf->GetDesc(&bbDesc);
CD3D11_TEXTURE2D_DESC softDesc(bbDesc.Format, bbDesc.Width, bbDesc.Height);
softDesc.MipLevels = 1;
softDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
softDesc.Usage = D3D11_USAGE_STAGING;
softDesc.BindFlags = 0;
RefPtr<ID3D11Texture2D> readTexture;
hr =
mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture));
if (FAILED(hr)) {
gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
<< "Failed in PaintToTarget 2";
HandleError(hr);
return;
}
mContext->CopyResource(readTexture, backBuf);
D3D11_MAPPED_SUBRESOURCE map;
hr = mContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &map);
if (FAILED(hr)) {
gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
<< "Failed in PaintToTarget 3";
HandleError(hr);
return;
}
RefPtr<DataSourceSurface> sourceSurface =
Factory::CreateWrappingDataSourceSurface(
(uint8_t*)map.pData, map.RowPitch,
IntSize(bbDesc.Width, bbDesc.Height), SurfaceFormat::B8G8R8A8);
mTarget->CopySurface(sourceSurface,
IntRect(0, 0, bbDesc.Width, bbDesc.Height),
IntPoint(-mTargetBounds.X(), -mTargetBounds.Y()));
mTarget->Flush();
mContext->Unmap(readTexture, 0);
}
bool CompositorD3D11::Failed(HRESULT hr, const char* aContext) {
if (SUCCEEDED(hr)) return false;
gfxCriticalNote << "[D3D11] " << aContext << " failed: " << hexa(hr) << ", "
<< (int)mVerifyBuffersFailed;
return true;
}
void CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity) {
if (SUCCEEDED(hr)) {
return;
}
if (aSeverity == Critical) {
MOZ_CRASH("GFX: Unrecoverable D3D11 error");
}
if (mDevice && DeviceManagerDx::Get()->GetCompositorDevice() != mDevice) {
gfxCriticalNote << "Out of sync D3D11 devices in HandleError, "
<< (int)mVerifyBuffersFailed;
}
HRESULT hrOnReset = S_OK;
bool deviceRemoved = hr == DXGI_ERROR_DEVICE_REMOVED;
if (deviceRemoved && mDevice) {
hrOnReset = mDevice->GetDeviceRemovedReason();
} else if (hr == DXGI_ERROR_INVALID_CALL && mDevice) {
hrOnReset = mDevice->GetDeviceRemovedReason();
if (hrOnReset != S_OK) {
deviceRemoved = true;
}
}
// Device reset may not be an error on our side, but can mess things up so
// it's useful to see it in the reports.
gfxCriticalError(CriticalLog::DefaultOptions(!deviceRemoved))
<< (deviceRemoved ? "[CompositorD3D11] device removed with error code: "
: "[CompositorD3D11] error code: ")
<< hexa(hr) << ", " << hexa(hrOnReset) << ", "
<< (int)mVerifyBuffersFailed;
// Crash if we are making invalid calls outside of device removal
if (hr == DXGI_ERROR_INVALID_CALL) {
gfxDevCrash(deviceRemoved ? LogReason::D3D11InvalidCallDeviceRemoved
: LogReason::D3D11InvalidCall)
<< "Invalid D3D11 api call";
}
if (aSeverity == Recoverable) {
NS_WARNING("Encountered a recoverable D3D11 error");
}
}
} // namespace layers
} // namespace mozilla