/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * 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 "CompositorD3D11Shaders.h" #include "CompositorD3D11ShadersVR.h" #include "gfxWindowsPlatform.h" #include "nsIWidget.h" #include "mozilla/layers/ImageHost.h" #include "mozilla/layers/ContentHost.h" #include "mozilla/layers/Effects.h" #include "nsWindowsHelpers.h" #include "gfxPrefs.h" #include "gfxVR.h" #include "mozilla/EnumeratedArray.h" #include namespace mozilla { using namespace gfx; namespace layers { struct Vertex { float position[2]; }; // {1E4D7BEB-D8EC-4A0B-BF0A-63E6DE129425} static const GUID sDeviceAttachmentsD3D11 = { 0x1e4d7beb, 0xd8ec, 0x4a0b, { 0xbf, 0xa, 0x63, 0xe6, 0xde, 0x12, 0x94, 0x25 } }; // {88041664-C835-4AA8-ACB8-7EC832357ED8} static const GUID sLayerManagerCount = { 0x88041664, 0xc835, 0x4aa8, { 0xac, 0xb8, 0x7e, 0xc8, 0x32, 0x35, 0x7e, 0xd8 } }; const FLOAT sBlendFactor[] = { 0, 0, 0, 0 }; struct DeviceAttachmentsD3D11 { typedef EnumeratedArray> VertexShaderArray; typedef EnumeratedArray> PixelShaderArray; RefPtr mInputLayout; RefPtr mVertexBuffer; VertexShaderArray mVSQuadShader; PixelShaderArray mSolidColorShader; PixelShaderArray mRGBAShader; PixelShaderArray mRGBShader; PixelShaderArray mYCbCrShader; PixelShaderArray mComponentAlphaShader; RefPtr mPSConstantBuffer; RefPtr mVSConstantBuffer; RefPtr mRasterizerState; RefPtr mLinearSamplerState; RefPtr mPointSamplerState; RefPtr mPremulBlendState; RefPtr mNonPremulBlendState; RefPtr mComponentBlendState; RefPtr mDisabledBlendState; RefPtr mSyncTexture; // // VR pieces // RefPtr mVRDistortionInputLayout; RefPtr mVRDistortionConstants; typedef EnumeratedArray> VRVertexShaderArray; typedef EnumeratedArray> VRPixelShaderArray; VRVertexShaderArray mVRDistortionVS; VRPixelShaderArray mVRDistortionPS; // These will be created/filled in as needed during rendering whenever the configuration // changes. VRHMDConfiguration mVRConfiguration; RefPtr mVRDistortionVertices[2]; // one for each eye RefPtr mVRDistortionIndices[2]; uint32_t mVRDistortionIndexCount[2]; }; CompositorD3D11::CompositorD3D11(nsIWidget* aWidget) : mAttachments(nullptr) , mWidget(aWidget) , mHwnd(nullptr) , mDisableSequenceForNextFrame(false) { SetBackend(LayersBackend::LAYERS_D3D11); } CompositorD3D11::~CompositorD3D11() { if (mDevice) { int referenceCount = 0; UINT size = sizeof(referenceCount); HRESULT hr = mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount); NS_ASSERTION(SUCCEEDED(hr), "Reference count not found on device."); referenceCount--; mDevice->SetPrivateData(sLayerManagerCount, sizeof(referenceCount), &referenceCount); if (!referenceCount) { DeviceAttachmentsD3D11 *attachments; size = sizeof(attachments); mDevice->GetPrivateData(sDeviceAttachmentsD3D11, &size, &attachments); // No LayerManagers left for this device. Clear out interfaces stored // which hold a reference to the device. mDevice->SetPrivateData(sDeviceAttachmentsD3D11, 0, nullptr); delete attachments; } } } bool CompositorD3D11::Initialize() { bool force = gfxPrefs::LayersAccelerationForceEnabled(); if (!gfxPlatform::CanUseDirect3D11()) { NS_WARNING("Direct3D 11-accelerated layers are not supported on this system."); return false; } HRESULT hr; mDevice = gfxWindowsPlatform::GetPlatform()->GetD3D11Device(); if (!mDevice) { return false; } mDevice->GetImmediateContext(byRef(mContext)); if (!mContext) { return false; } mFeatureLevel = mDevice->GetFeatureLevel(); mHwnd = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW); memset(&mVSConstants, 0, sizeof(VertexShaderConstants)); int referenceCount = 0; UINT size = sizeof(referenceCount); // If this isn't there yet it'll fail, count will remain 0, which is correct. mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount); referenceCount++; mDevice->SetPrivateData(sLayerManagerCount, sizeof(referenceCount), &referenceCount); size = sizeof(DeviceAttachmentsD3D11*); if (FAILED(mDevice->GetPrivateData(sDeviceAttachmentsD3D11, &size, &mAttachments))) { mAttachments = new DeviceAttachmentsD3D11; mDevice->SetPrivateData(sDeviceAttachmentsD3D11, sizeof(mAttachments), &mAttachments); D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; hr = mDevice->CreateInputLayout(layout, sizeof(layout) / sizeof(D3D11_INPUT_ELEMENT_DESC), LayerQuadVS, sizeof(LayerQuadVS), byRef(mAttachments->mInputLayout)); if (FAILED(hr)) { return false; } Vertex vertices[] = { {{0.0, 0.0}}, {{1.0, 0.0}}, {{0.0, 1.0}}, {{1.0, 1.0}} }; CD3D11_BUFFER_DESC bufferDesc(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER); D3D11_SUBRESOURCE_DATA data; data.pSysMem = (void*)vertices; hr = mDevice->CreateBuffer(&bufferDesc, &data, byRef(mAttachments->mVertexBuffer)); if (FAILED(hr)) { return false; } if (!CreateShaders()) { return false; } CD3D11_BUFFER_DESC cBufferDesc(sizeof(VertexShaderConstants), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, byRef(mAttachments->mVSConstantBuffer)); if (FAILED(hr)) { return false; } cBufferDesc.ByteWidth = sizeof(PixelShaderConstants); hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, byRef(mAttachments->mPSConstantBuffer)); if (FAILED(hr)) { return false; } CD3D11_RASTERIZER_DESC rastDesc(D3D11_DEFAULT); rastDesc.CullMode = D3D11_CULL_NONE; rastDesc.ScissorEnable = TRUE; hr = mDevice->CreateRasterizerState(&rastDesc, byRef(mAttachments->mRasterizerState)); if (FAILED(hr)) { return false; } CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT); hr = mDevice->CreateSamplerState(&samplerDesc, byRef(mAttachments->mLinearSamplerState)); if (FAILED(hr)) { return false; } samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; hr = mDevice->CreateSamplerState(&samplerDesc, byRef(mAttachments->mPointSamplerState)); if (FAILED(hr)) { return false; } CD3D11_BLEND_DESC blendDesc(D3D11_DEFAULT); D3D11_RENDER_TARGET_BLEND_DESC rtBlendPremul = { TRUE, D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD, D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD, D3D11_COLOR_WRITE_ENABLE_ALL }; blendDesc.RenderTarget[0] = rtBlendPremul; hr = mDevice->CreateBlendState(&blendDesc, byRef(mAttachments->mPremulBlendState)); if (FAILED(hr)) { return false; } D3D11_RENDER_TARGET_BLEND_DESC rtBlendNonPremul = { TRUE, D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD, D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD, D3D11_COLOR_WRITE_ENABLE_ALL }; blendDesc.RenderTarget[0] = rtBlendNonPremul; hr = mDevice->CreateBlendState(&blendDesc, byRef(mAttachments->mNonPremulBlendState)); if (FAILED(hr)) { return false; } if (gfxPrefs::ComponentAlphaEnabled()) { D3D11_RENDER_TARGET_BLEND_DESC rtBlendComponent = { TRUE, D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC1_COLOR, D3D11_BLEND_OP_ADD, D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD, D3D11_COLOR_WRITE_ENABLE_ALL }; blendDesc.RenderTarget[0] = rtBlendComponent; hr = mDevice->CreateBlendState(&blendDesc, byRef(mAttachments->mComponentBlendState)); if (FAILED(hr)) { return false; } } D3D11_RENDER_TARGET_BLEND_DESC rtBlendDisabled = { FALSE, D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD, D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD, D3D11_COLOR_WRITE_ENABLE_ALL }; blendDesc.RenderTarget[0] = rtBlendDisabled; hr = mDevice->CreateBlendState(&blendDesc, byRef(mAttachments->mDisabledBlendState)); if (FAILED(hr)) { return false; } if (!gfxWindowsPlatform::GetPlatform()->IsWARP()) { // It's okay to do this on Windows 8. But for now we'll just bail // whenever we're using WARP. CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, 1, 1, 1, 1, D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET); desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; RefPtr texture; hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(texture)); if (FAILED(hr)) { return false; } hr = texture->QueryInterface((IDXGIResource**)byRef(mAttachments->mSyncTexture)); if (FAILED(hr)) { return false; } } // // VR additions // D3D11_INPUT_ELEMENT_DESC vrlayout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; hr = mDevice->CreateInputLayout(vrlayout, sizeof(vrlayout) / sizeof(D3D11_INPUT_ELEMENT_DESC), OculusVRDistortionVS, sizeof(OculusVRDistortionVS), byRef(mAttachments->mVRDistortionInputLayout)); cBufferDesc.ByteWidth = sizeof(gfx::VRDistortionConstants); hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, byRef(mAttachments->mVRDistortionConstants)); if (FAILED(hr)) { return false; } } nsRefPtr dxgiDevice; nsRefPtr dxgiAdapter; mDevice->QueryInterface(dxgiDevice.StartAssignment()); dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter)); { nsRefPtr dxgiFactory; dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.StartAssignment())); DXGI_SWAP_CHAIN_DESC swapDesc; ::ZeroMemory(&swapDesc, sizeof(swapDesc)); swapDesc.BufferDesc.Width = 0; swapDesc.BufferDesc.Height = 0; swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; swapDesc.BufferDesc.RefreshRate.Numerator = 60; swapDesc.BufferDesc.RefreshRate.Denominator = 1; swapDesc.SampleDesc.Count = 1; swapDesc.SampleDesc.Quality = 0; swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapDesc.BufferCount = 1; 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, byRef(mSwapChain)); if (FAILED(hr)) { return false; } // We need this because we don't want DXGI to respond to Alt+Enter. dxgiFactory->MakeWindowAssociation(swapDesc.OutputWindow, DXGI_MWA_NO_WINDOW_CHANGES); } return true; } TemporaryRef CompositorD3D11::CreateDataTextureSource(TextureFlags aFlags) { RefPtr result = new DataTextureSourceD3D11(gfx::SurfaceFormat::UNKNOWN, this, aFlags); return result.forget(); } TextureFactoryIdentifier CompositorD3D11::GetTextureFactoryIdentifier() { TextureFactoryIdentifier ident; ident.mMaxTextureSize = GetMaxTextureSize(); ident.mParentProcessId = XRE_GetProcessType(); ident.mParentBackend = LayersBackend::LAYERS_D3D11; if (mAttachments->mSyncTexture) { HRESULT hr = mAttachments->mSyncTexture->GetSharedHandle(&ident.mSyncHandle); if (FAILED(hr) || !ident.mSyncHandle) { gfxCriticalError() << "Failed to get SharedHandle for sync texture. Result: " << hr; MOZ_CRASH(); } } 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); } TemporaryRef CompositorD3D11::CreateRenderTarget(const gfx::IntRect& aRect, SurfaceInitMode aInit) { MOZ_ASSERT(aRect.width != 0 && aRect.height != 0); if (aRect.width * aRect.height == 0) { 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 texture; HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(texture)); if (Failed(hr) || !texture) { return nullptr; } RefPtr 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; } TemporaryRef CompositorD3D11::CreateRenderTargetFromSource(const gfx::IntRect &aRect, const CompositingRenderTarget* aSource, const gfx::IntPoint &aSourcePoint) { MOZ_ASSERT(aRect.width != 0 && aRect.height != 0); if (aRect.width * aRect.height == 0) { 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 texture; HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(texture)); NS_ASSERTION(texture, "Could not create texture"); if (Failed(hr) || !texture) { return nullptr; } if (aSource) { const CompositingRenderTargetD3D11* sourceD3D11 = static_cast(aSource); D3D11_BOX srcBox; srcBox.left = aSourcePoint.x; srcBox.top = aSourcePoint.y; srcBox.front = 0; srcBox.right = aSourcePoint.x + aRect.width; srcBox.bottom = aSourcePoint.y + aRect.height; srcBox.back = 1; const IntSize& srcSize = sourceD3D11->GetSize(); MOZ_ASSERT(srcSize.width >= 0 && srcSize.height >= 0, "render targets should have nonnegative sizes"); if (srcBox.left >= 0 && srcBox.top >= 0 && srcBox.left < srcBox.right && srcBox.top < srcBox.bottom && srcBox.right <= static_cast(srcSize.width) && srcBox.bottom <= static_cast(srcSize.height)) { mContext->CopySubresourceRegion(texture, 0, 0, 0, 0, sourceD3D11->GetD3D11Texture(), 0, &srcBox); } else { NS_WARNING("Could not copy render target - source rect out of bounds"); } } RefPtr rt = new CompositingRenderTargetD3D11(texture, aRect.TopLeft()); rt->SetSize(aRect.Size()); return rt; } void CompositorD3D11::SetRenderTarget(CompositingRenderTarget* aRenderTarget) { MOZ_ASSERT(aRenderTarget); CompositingRenderTargetD3D11* newRT = static_cast(aRenderTarget); mCurrentRT = newRT; mCurrentRT->BindRenderTarget(mContext); PrepareViewport(newRT->GetSize()); } void CompositorD3D11::SetPSForEffect(Effect* aEffect, MaskType aMaskType, gfx::SurfaceFormat aFormat) { switch (aEffect->mType) { case EffectTypes::SOLID_COLOR: mContext->PSSetShader(mAttachments->mSolidColorShader[aMaskType], nullptr, 0); return; case EffectTypes::RENDER_TARGET: mContext->PSSetShader(mAttachments->mRGBAShader[aMaskType], nullptr, 0); return; case EffectTypes::RGB: mContext->PSSetShader((aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat::R8G8B8A8) ? mAttachments->mRGBAShader[aMaskType] : mAttachments->mRGBShader[aMaskType], nullptr, 0); return; case EffectTypes::YCBCR: mContext->PSSetShader(mAttachments->mYCbCrShader[aMaskType], nullptr, 0); return; case EffectTypes::COMPONENT_ALPHA: mContext->PSSetShader(mAttachments->mComponentAlphaShader[aMaskType], nullptr, 0); return; default: NS_WARNING("No shader to load"); return; } } void CompositorD3D11::ClearRect(const gfx::Rect& aRect) { 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); mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF); } void CompositorD3D11::DrawVRDistortion(const gfx::Rect& aRect, const gfx::Rect& aClipRect, const EffectChain& aEffectChain, gfx::Float aOpacity, const gfx::Matrix4x4& aTransform) { MOZ_ASSERT(aEffectChain.mPrimaryEffect->mType == EffectTypes::VR_DISTORTION); if (aEffectChain.mSecondaryEffects[EffectTypes::MASK] || aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) { NS_WARNING("DrawVRDistortion: ignoring secondary effect!"); } HRESULT hr; EffectVRDistortion* vrEffect = static_cast(aEffectChain.mPrimaryEffect.get()); TextureSourceD3D11* source = vrEffect->mTexture->AsSourceD3D11(); gfx::IntSize size = vrEffect->mRenderTarget->GetSize(); // XXX source->GetSize() VRHMDInfo* hmdInfo = vrEffect->mHMD; VRDistortionConstants shaderConstants; // do we need to recreate the VR buffers, since the config has changed? if (hmdInfo->GetConfiguration() != mAttachments->mVRConfiguration) { D3D11_SUBRESOURCE_DATA sdata = { 0 }; CD3D11_BUFFER_DESC desc(0, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_IMMUTABLE); // XXX as an optimization, we should really pack the indices and vertices for both eyes // into one buffer instead of needing one eye each. Then we can just bind them once. for (uint32_t eye = 0; eye < 2; eye++) { const gfx::VRDistortionMesh& mesh = hmdInfo->GetDistortionMesh(eye); desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; desc.ByteWidth = mesh.mVertices.Length() * sizeof(gfx::VRDistortionVertex); sdata.pSysMem = mesh.mVertices.Elements(); hr = mDevice->CreateBuffer(&desc, &sdata, byRef(mAttachments->mVRDistortionVertices[eye])); if (FAILED(hr)) { NS_WARNING("CreateBuffer failed"); return; } desc.BindFlags = D3D11_BIND_INDEX_BUFFER; desc.ByteWidth = mesh.mIndices.Length() * sizeof(uint16_t); sdata.pSysMem = mesh.mIndices.Elements(); hr = mDevice->CreateBuffer(&desc, &sdata, byRef(mAttachments->mVRDistortionIndices[eye])); if (FAILED(hr)) { NS_WARNING("CreateBuffer failed"); return; } mAttachments->mVRDistortionIndexCount[eye] = mesh.mIndices.Length(); } mAttachments->mVRConfiguration = hmdInfo->GetConfiguration(); } // XXX do I need to set a scissor rect? Is this the right scissor rect? D3D11_RECT scissor; scissor.left = aClipRect.x; scissor.right = aClipRect.XMost(); scissor.top = aClipRect.y; scissor.bottom = aClipRect.YMost(); mContext->RSSetScissorRects(1, &scissor); // Triangle lists and same layout for both eyes mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); mContext->IASetInputLayout(mAttachments->mVRDistortionInputLayout); // Shaders for this HMD mContext->VSSetShader(mAttachments->mVRDistortionVS[mAttachments->mVRConfiguration.hmdType], nullptr, 0); mContext->PSSetShader(mAttachments->mVRDistortionPS[mAttachments->mVRConfiguration.hmdType], nullptr, 0); // This is the source texture SRV for the pixel shader // XXX, um should we cache this SRV? RefPtr view; mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view)); ID3D11ShaderResourceView* srView = view; mContext->PSSetShaderResources(0, 1, &srView); gfx::IntSize vpSizeInt = mCurrentRT->GetSize(); gfx::Size vpSize(vpSizeInt.width, vpSizeInt.height); ID3D11Buffer* vbuffer; UINT vsize, voffset; for (uint32_t eye = 0; eye < 2; eye++) { gfx::IntRect eyeViewport; eyeViewport.x = eye * size.width / 2; eyeViewport.y = 0; eyeViewport.width = size.width / 2; eyeViewport.height = size.height; hmdInfo->FillDistortionConstants(eye, size, eyeViewport, vpSize, aRect, shaderConstants); // D3D has clip space top-left as -1,1 so we need to flip the Y coordinate offset here shaderConstants.destinationScaleAndOffset[1] = - shaderConstants.destinationScaleAndOffset[1]; // XXX I really want to write a templated helper for these next 4 lines D3D11_MAPPED_SUBRESOURCE resource; mContext->Map(mAttachments->mVRDistortionConstants, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource); *(gfx::VRDistortionConstants*)resource.pData = shaderConstants; mContext->Unmap(mAttachments->mVRDistortionConstants, 0); // XXX is there a better way to change a bunch of these things from what they were set to // in BeginFrame/etc? vbuffer = mAttachments->mVRDistortionVertices[eye]; vsize = sizeof(gfx::VRDistortionVertex); voffset = 0; mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset); mContext->IASetIndexBuffer(mAttachments->mVRDistortionIndices[eye], DXGI_FORMAT_R16_UINT, 0); ID3D11Buffer* constBuf = mAttachments->mVRDistortionConstants; mContext->VSSetConstantBuffers(0, 1, &constBuf); mContext->DrawIndexed(mAttachments->mVRDistortionIndexCount[eye], 0, 0); } // restore previous configurations vbuffer = mAttachments->mVertexBuffer; vsize = sizeof(Vertex); voffset = 0; mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset); mContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0); mContext->IASetInputLayout(mAttachments->mInputLayout); } void CompositorD3D11::DrawQuad(const gfx::Rect& aRect, const gfx::Rect& aClipRect, const EffectChain& aEffectChain, gfx::Float aOpacity, const gfx::Matrix4x4& aTransform) { if (mCurrentClip.IsEmpty()) { return; } MOZ_ASSERT(mCurrentRT, "No render target"); if (aEffectChain.mPrimaryEffect->mType == EffectTypes::VR_DISTORTION) { DrawVRDistortion(aRect, aClipRect, aEffectChain, aOpacity, aTransform); return; } 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]) { if (aTransform.Is2D()) { maskType = MaskType::Mask2d; } else { MOZ_ASSERT(aEffectChain.mPrimaryEffect->mType == EffectTypes::RGB); maskType = MaskType::Mask3d; } EffectMask* maskEffect = static_cast(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get()); TextureSourceD3D11* source = maskEffect->mMaskTexture->AsSourceD3D11(); if (!source) { NS_WARNING("Missing texture source!"); return; } RefPtr view; HRESULT hr = mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view)); if (Failed(hr)) { // XXX - There's a chance we won't be able to render anything, should we // just crash release builds? return; } ID3D11ShaderResourceView* srView = view; mContext->PSSetShaderResources(3, 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)); mVSConstants.maskQuad = maskTransform.As2D().TransformBounds(bounds); } 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(); mContext->RSSetScissorRects(1, &scissor); mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); mContext->VSSetShader(mAttachments->mVSQuadShader[maskType], nullptr, 0); const Rect* pTexCoordRect = nullptr; switch (aEffectChain.mPrimaryEffect->mType) { case EffectTypes::SOLID_COLOR: { SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, SurfaceFormat::UNKNOWN); Color color = static_cast(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(aEffectChain.mPrimaryEffect.get()); pTexCoordRect = &texturedEffect->mTextureCoords; TextureSourceD3D11* source = texturedEffect->mTexture->AsSourceD3D11(); if (!source) { NS_WARNING("Missing texture source!"); return; } SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, texturedEffect->mTexture->GetFormat()); RefPtr view; HRESULT hr = mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view)); if (Failed(hr)) { // XXX - There's a chance we won't be able to render anything, should we // just crash release builds? return; } ID3D11ShaderResourceView* srView = view; mContext->PSSetShaderResources(0, 1, &srView); if (!texturedEffect->mPremultiplied) { mContext->OMSetBlendState(mAttachments->mNonPremulBlendState, sBlendFactor, 0xFFFFFFFF); restoreBlendMode = true; } SetSamplerForFilter(texturedEffect->mFilter); } break; case EffectTypes::YCBCR: { EffectYCbCr* ycbcrEffect = static_cast(aEffectChain.mPrimaryEffect.get()); SetSamplerForFilter(Filter::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; } SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, ycbcrEffect->mTexture->GetFormat()); 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; } TextureSourceD3D11* sourceY = source->GetSubSource(Y)->AsSourceD3D11(); TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11(); TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11(); HRESULT hr; RefPtr views[3]; hr = mDevice->CreateShaderResourceView(sourceY->GetD3D11Texture(), nullptr, byRef(views[0])); if (Failed(hr)) { return; } hr = mDevice->CreateShaderResourceView(sourceCb->GetD3D11Texture(), nullptr, byRef(views[1])); if (Failed(hr)) { return; } hr = mDevice->CreateShaderResourceView(sourceCr->GetD3D11Texture(), nullptr, byRef(views[2])); if (Failed(hr)) { return; } ID3D11ShaderResourceView* srViews[3] = { views[0], views[1], views[2] }; mContext->PSSetShaderResources(0, 3, srViews); } break; case EffectTypes::COMPONENT_ALPHA: { MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled()); MOZ_ASSERT(mAttachments->mComponentBlendState); EffectComponentAlpha* effectComponentAlpha = static_cast(aEffectChain.mPrimaryEffect.get()); TextureSourceD3D11* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceD3D11(); TextureSourceD3D11* sourceOnBlack = effectComponentAlpha->mOnBlack->AsSourceD3D11(); if (!sourceOnWhite || !sourceOnBlack) { NS_WARNING("Missing texture source(s)!"); return; } SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, effectComponentAlpha->mOnWhite->GetFormat()); SetSamplerForFilter(effectComponentAlpha->mFilter); pTexCoordRect = &effectComponentAlpha->mTextureCoords; RefPtr views[2]; HRESULT hr; hr = mDevice->CreateShaderResourceView(sourceOnBlack->GetD3D11Texture(), nullptr, byRef(views[0])); if (Failed(hr)) { return; } hr = mDevice->CreateShaderResourceView(sourceOnWhite->GetD3D11Texture(), nullptr, byRef(views[1])); if (Failed(hr)) { return; } ID3D11ShaderResourceView* srViews[2] = { views[0], views[1] }; mContext->PSSetShaderResources(0, 2, srViews); mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF); restoreBlendMode = true; } break; default: NS_WARNING("Unknown shader type"); return; } if (pTexCoordRect) { Rect layerRects[4]; Rect textureRects[4]; size_t rects = DecomposeIntoNoRepeatRects(aRect, *pTexCoordRect, &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); } } else { mVSConstants.layerQuad = aRect; if (!UpdateConstantBuffers()) { NS_WARNING("Failed to update shader constant buffers"); } else { mContext->Draw(4, 0); } } if (restoreBlendMode) { mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF); } } void CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion, const Rect* aClipRectIn, const Rect& aRenderBounds, Rect* aClipRectOut, Rect* aRenderBoundsOut) { // 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 (::IsIconic(mHwnd) || mDevice->GetDeviceRemovedReason() != S_OK) { *aRenderBoundsOut = Rect(); return; } nsIntSize oldSize = mSize; UpdateRenderTarget(); // Failed to create a render target or the view. if (!mDefaultRT || !mDefaultRT->mRTView || mSize.width == 0 || mSize.height == 0) { *aRenderBoundsOut = Rect(); return; } mContext->IASetInputLayout(mAttachments->mInputLayout); ID3D11Buffer* buffer = mAttachments->mVertexBuffer; UINT size = sizeof(Vertex); UINT offset = 0; mContext->IASetVertexBuffers(0, 1, &buffer, &size, &offset); nsIntRect intRect = nsIntRect(nsIntPoint(0, 0), mSize); // Sometimes the invalid region is larger than we want to draw. nsIntRegion invalidRegionSafe; if (mSize != oldSize) { invalidRegionSafe = intRect; } else { invalidRegionSafe.And(aInvalidRegion, intRect); } nsIntRect invalidRect = invalidRegionSafe.GetBounds(); mInvalidRect = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height); mInvalidRegion = invalidRegionSafe; if (aClipRectOut) { *aClipRectOut = Rect(0, 0, mSize.width, mSize.height); } if (aRenderBoundsOut) { *aRenderBoundsOut = Rect(0, 0, mSize.width, mSize.height); } if (aClipRectIn) { invalidRect.IntersectRect(invalidRect, nsIntRect(aClipRectIn->x, aClipRectIn->y, aClipRectIn->width, aClipRectIn->height)); } mCurrentClip = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height); mContext->RSSetState(mAttachments->mRasterizerState); SetRenderTarget(mDefaultRT); // ClearRect will set the correct blend state for us. ClearRect(Rect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height)); if (mAttachments->mSyncTexture) { RefPtr mutex; mAttachments->mSyncTexture->QueryInterface((IDXGIKeyedMutex**)byRef(mutex)); MOZ_ASSERT(mutex); HRESULT hr = mutex->AcquireSync(0, 10000); if (hr == WAIT_TIMEOUT) { MOZ_CRASH(); } mutex->ReleaseSync(0); } } void CompositorD3D11::EndFrame() { if (!mDefaultRT) { return; } mContext->Flush(); nsIntSize oldSize = mSize; EnsureSize(); UINT presentInterval = 0; if (gfxWindowsPlatform::GetPlatform()->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; } if (oldSize == mSize) { RefPtr chain; HRESULT hr = mSwapChain->QueryInterface((IDXGISwapChain1**)byRef(chain)); if (SUCCEEDED(hr) && chain) { DXGI_PRESENT_PARAMETERS params; PodZero(¶ms); params.DirtyRectsCount = mInvalidRegion.GetNumRects(); std::vector rects; rects.reserve(params.DirtyRectsCount); nsIntRegionRectIterator iter(mInvalidRegion); const nsIntRect* r; uint32_t i = 0; while ((r = iter.Next()) != nullptr) { RECT rect; rect.left = r->x; rect.top = r->y; rect.bottom = r->YMost(); rect.right = r->XMost(); rects.push_back(rect); } params.pDirtyRects = &rects.front(); chain->Present1(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, ¶ms); } else { mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0); } mDisableSequenceForNextFrame = false; if (mTarget) { PaintToTarget(); } } mCurrentRT = nullptr; } void CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize) { D3D11_VIEWPORT viewport; viewport.MaxDepth = 1.0f; viewport.MinDepth = 0.0f; viewport.Width = aSize.width; viewport.Height = aSize.height; viewport.TopLeftX = 0; viewport.TopLeftY = 0; mContext->RSSetViewports(1, &viewport); // 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; memcpy(&mVSConstants.projection, &projection, sizeof(mVSConstants.projection)); } void CompositorD3D11::EnsureSize() { nsIntRect rect; mWidget->GetClientBounds(rect); mSize = rect.Size(); } bool CompositorD3D11::VerifyBufferSize() { DXGI_SWAP_CHAIN_DESC swapDesc; HRESULT hr; hr = mSwapChain->GetDesc(&swapDesc); if (Failed(hr)) { return false; } if ((swapDesc.BufferDesc.Width == mSize.width && swapDesc.BufferDesc.Height == mSize.height) || mSize.width <= 0 || mSize.height <= 0) { return true; } if (mDefaultRT) { // 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; } hr = mSwapChain->ResizeBuffers(1, mSize.width, mSize.height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); return Succeeded(hr); } void CompositorD3D11::UpdateRenderTarget() { EnsureSize(); VerifyBufferSize(); if (mDefaultRT) { return; } HRESULT hr; nsRefPtr backBuf; 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!"; return; } } if (Failed(hr)) { return; } mDefaultRT = new CompositingRenderTargetD3D11(backBuf, IntPoint(0, 0)); mDefaultRT->SetSize(mSize.ToIntSize()); } bool CompositorD3D11::CreateShaders() { HRESULT hr; hr = mDevice->CreateVertexShader(LayerQuadVS, sizeof(LayerQuadVS), nullptr, byRef(mAttachments->mVSQuadShader[MaskType::MaskNone])); if (FAILED(hr)) { return false; } hr = mDevice->CreateVertexShader(LayerQuadMaskVS, sizeof(LayerQuadMaskVS), nullptr, byRef(mAttachments->mVSQuadShader[MaskType::Mask2d])); if (FAILED(hr)) { return false; } hr = mDevice->CreateVertexShader(LayerQuadMask3DVS, sizeof(LayerQuadMask3DVS), nullptr, byRef(mAttachments->mVSQuadShader[MaskType::Mask3d])); if (FAILED(hr)) { return false; } #define LOAD_PIXEL_SHADER(x) hr = mDevice->CreatePixelShader(x, sizeof(x), nullptr, byRef(mAttachments->m##x[MaskType::MaskNone])); \ if (FAILED(hr)) { \ return false; \ } \ hr = mDevice->CreatePixelShader(x##Mask, sizeof(x##Mask), nullptr, byRef(mAttachments->m##x[MaskType::Mask2d])); \ if (FAILED(hr)) { \ return false; \ } LOAD_PIXEL_SHADER(SolidColorShader); LOAD_PIXEL_SHADER(RGBShader); LOAD_PIXEL_SHADER(RGBAShader); LOAD_PIXEL_SHADER(YCbCrShader); if (gfxPrefs::ComponentAlphaEnabled()) { LOAD_PIXEL_SHADER(ComponentAlphaShader); } #undef LOAD_PIXEL_SHADER hr = mDevice->CreatePixelShader(RGBAShaderMask3D, sizeof(RGBAShaderMask3D), nullptr, byRef(mAttachments->mRGBAShader[MaskType::Mask3d])); if (FAILED(hr)) { return false; } /* VR stuff */ hr = mDevice->CreateVertexShader(OculusVRDistortionVS, sizeof(OculusVRDistortionVS), nullptr, byRef(mAttachments->mVRDistortionVS[VRHMDType::Oculus])); if (FAILED(hr)) { return false; } hr = mDevice->CreatePixelShader(OculusVRDistortionPS, sizeof(OculusVRDistortionPS), nullptr, byRef(mAttachments->mVRDistortionPS[VRHMDType::Oculus])); if (FAILED(hr)) { return false; } return true; } bool CompositorD3D11::UpdateConstantBuffers() { HRESULT hr; D3D11_MAPPED_SUBRESOURCE resource; hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource); if (Failed(hr)) { return false; } *(VertexShaderConstants*)resource.pData = mVSConstants; mContext->Unmap(mAttachments->mVSConstantBuffer, 0); hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource); if (Failed(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::SetSamplerForFilter(Filter aFilter) { ID3D11SamplerState *sampler; switch (aFilter) { case Filter::POINT: sampler = mAttachments->mPointSamplerState; break; case Filter::LINEAR: default: sampler = mAttachments->mLinearSamplerState; break; } mContext->PSSetSamplers(0, 1, &sampler); } void CompositorD3D11::PaintToTarget() { nsRefPtr backBuf; HRESULT hr; hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment()); if (Failed(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; nsRefPtr readTexture; hr = mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture)); if (Failed(hr)) { return; } mContext->CopyResource(readTexture, backBuf); D3D11_MAPPED_SUBRESOURCE map; hr = mContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &map); if (Failed(hr)) { return; } RefPtr 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); } void CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity) { if (SUCCEEDED(hr)) { return; } // XXX - It would be nice to use gfxCriticalError, but it needs to // be made to work off the main thread first. //MOZ_ASSERT(aSeverity != DebugAssert); if (aSeverity == Critical) { MOZ_CRASH("Unrecoverable D3D11 error"); } if (mDevice && hr == DXGI_ERROR_DEVICE_REMOVED) { hr = mDevice->GetDeviceRemovedReason(); } // Always crash if we are making invalid calls if (hr == DXGI_ERROR_INVALID_CALL) { MOZ_CRASH("Invalid D3D11 api call"); } if (aSeverity == Recoverable) { NS_WARNING("Encountered a recoverable D3D11 error"); } } bool CompositorD3D11::Failed(HRESULT hr, Severity aSeverity) { HandleError(hr, aSeverity); return FAILED(hr); } bool CompositorD3D11::Succeeded(HRESULT hr, Severity aSeverity) { HandleError(hr, aSeverity); return SUCCEEDED(hr); } } }