/* -*- 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 "mozilla/layers/PLayerTransaction.h" // This must occur *after* layers/PLayerTransaction.h to avoid // typedefs conflicts. #include "mozilla/ArrayUtils.h" #include "ThebesLayerD3D10.h" #include "gfxPlatform.h" #include "gfxWindowsPlatform.h" #ifdef CAIRO_HAS_D2D_SURFACE #include "gfxD2DSurface.h" #endif #include "../d3d9/Nv3DVUtils.h" #include "gfxTeeSurface.h" #include "gfxUtils.h" #include "ReadbackLayer.h" #include "ReadbackProcessor.h" #include "mozilla/Preferences.h" #include "mozilla/gfx/2D.h" using namespace mozilla::gfx; namespace mozilla { namespace layers { ThebesLayerD3D10::ThebesLayerD3D10(LayerManagerD3D10 *aManager) : ThebesLayer(aManager, nullptr) , LayerD3D10(aManager) , mCurrentSurfaceMode(SurfaceMode::SURFACE_OPAQUE) { mImplData = static_cast(this); } ThebesLayerD3D10::~ThebesLayerD3D10() { } void ThebesLayerD3D10::InvalidateRegion(const nsIntRegion &aRegion) { mInvalidRegion.Or(mInvalidRegion, aRegion); mInvalidRegion.SimplifyOutward(10); mValidRegion.Sub(mValidRegion, mInvalidRegion); } void ThebesLayerD3D10::CopyRegion(ID3D10Texture2D* aSrc, const nsIntPoint &aSrcOffset, ID3D10Texture2D* aDest, const nsIntPoint &aDestOffset, const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion) { nsIntRegion retainedRegion; nsIntRegionRectIterator iter(aCopyRegion); const nsIntRect *r; while ((r = iter.Next())) { // Calculate the retained rectangle's position on the old and the new // surface. D3D10_BOX box; box.left = r->x - aSrcOffset.x; box.top = r->y - aSrcOffset.y; box.right = box.left + r->width; box.bottom = box.top + r->height; box.back = 1; box.front = 0; device()->CopySubresourceRegion(aDest, 0, r->x - aDestOffset.x, r->y - aDestOffset.y, 0, aSrc, 0, &box); retainedRegion.Or(retainedRegion, *r); } // Areas which were valid and were retained are still valid aValidRegion->And(*aValidRegion, retainedRegion); } void ThebesLayerD3D10::RenderLayer() { if (!mTexture) { return; } SetEffectTransformAndOpacity(); ID3D10EffectTechnique *technique; switch (mCurrentSurfaceMode) { case SurfaceMode::SURFACE_COMPONENT_ALPHA: technique = SelectShader(SHADER_COMPONENT_ALPHA | LoadMaskTexture()); break; case SurfaceMode::SURFACE_OPAQUE: technique = SelectShader(SHADER_RGB | SHADER_PREMUL | LoadMaskTexture()); break; case SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA: technique = SelectShader(SHADER_RGBA | SHADER_PREMUL | LoadMaskTexture()); break; default: NS_ERROR("Unknown mode"); return; } nsIntRegionRectIterator iter(mVisibleRegion); const nsIntRect *iterRect; if (mSRView) { effect()->GetVariableByName("tRGB")->AsShaderResource()->SetResource(mSRView); } if (mSRViewOnWhite) { effect()->GetVariableByName("tRGBWhite")->AsShaderResource()->SetResource(mSRViewOnWhite); } while ((iterRect = iter.Next())) { effect()->GetVariableByName("vLayerQuad")->AsVector()->SetFloatVector( ShaderConstantRectD3D10( (float)iterRect->x, (float)iterRect->y, (float)iterRect->width, (float)iterRect->height) ); effect()->GetVariableByName("vTextureCoords")->AsVector()->SetFloatVector( ShaderConstantRectD3D10( (float)(iterRect->x - mTextureRect.x) / (float)mTextureRect.width, (float)(iterRect->y - mTextureRect.y) / (float)mTextureRect.height, (float)iterRect->width / (float)mTextureRect.width, (float)iterRect->height / (float)mTextureRect.height) ); technique->GetPassByIndex(0)->Apply(0); device()->Draw(4, 0); } // Set back to default. effect()->GetVariableByName("vTextureCoords")->AsVector()-> SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); } void ThebesLayerD3D10::Validate(ReadbackProcessor *aReadback) { if (mVisibleRegion.IsEmpty()) { return; } if (FAILED(gfxWindowsPlatform::GetPlatform()->GetD3D10Device()->GetDeviceRemovedReason())) { // Device removed, this will be discovered on the next rendering pass. // Do no validate. return; } nsIntRect newTextureRect = mVisibleRegion.GetBounds(); SurfaceMode mode = GetSurfaceMode(); if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA && (!mParent || !mParent->SupportsComponentAlphaChildren())) { mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; } // If we have a transform that requires resampling of our texture, then // we need to make sure we don't sample pixels that haven't been drawn. // We clamp sample coordinates to the texture rect, but when the visible region // doesn't fill the entire texture rect we need to make sure we draw all the // pixels in the texture rect anyway in case they get sampled. nsIntRegion neededRegion = mVisibleRegion; if (!neededRegion.GetBounds().IsEqualInterior(newTextureRect) || neededRegion.GetNumRects() > 1) { if (MayResample()) { neededRegion = newTextureRect; if (mode == SurfaceMode::SURFACE_OPAQUE) { // We're going to paint outside the visible region, but layout hasn't // promised that it will paint opaquely there, so we'll have to // treat this layer as transparent. mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; } } } mCurrentSurfaceMode = mode; VerifyContentType(mode); nsTArray readbackUpdates; nsIntRegion readbackRegion; if (aReadback && UsedForReadback()) { aReadback->GetThebesLayerUpdates(this, &readbackUpdates, &readbackRegion); } if (mTexture) { if (!mTextureRect.IsEqualInterior(newTextureRect)) { nsRefPtr oldTexture = mTexture; mTexture = nullptr; nsRefPtr oldTextureOnWhite = mTextureOnWhite; mTextureOnWhite = nullptr; nsIntRegion retainRegion = mTextureRect; // Old visible region will become the region that is covered by both the // old and the new visible region. retainRegion.And(retainRegion, mVisibleRegion); // No point in retaining parts which were not valid. retainRegion.And(retainRegion, mValidRegion); CreateNewTextures(gfx::IntSize(newTextureRect.width, newTextureRect.height), mode); nsIntRect largeRect = retainRegion.GetLargestRectangle(); // If we had no hardware texture before, or have no retained area larger than // the retention threshold, we're not retaining and are done here. // If our texture creation failed this can mean a device reset is pending // and we should silently ignore the failure. In the future when device // failures are properly handled we should test for the type of failure // and gracefully handle different failures. See bug 569081. if (!oldTexture || !mTexture) { mValidRegion.SetEmpty(); } else { CopyRegion(oldTexture, mTextureRect.TopLeft(), mTexture, newTextureRect.TopLeft(), retainRegion, &mValidRegion); if (oldTextureOnWhite) { CopyRegion(oldTextureOnWhite, mTextureRect.TopLeft(), mTextureOnWhite, newTextureRect.TopLeft(), retainRegion, &mValidRegion); } } } } mTextureRect = newTextureRect; if (!mTexture || (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA && !mTextureOnWhite)) { CreateNewTextures(gfx::IntSize(newTextureRect.width, newTextureRect.height), mode); mValidRegion.SetEmpty(); } nsIntRegion drawRegion; drawRegion.Sub(neededRegion, mValidRegion); if (!drawRegion.IsEmpty()) { LayerManagerD3D10::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); if (!cbInfo.Callback) { NS_ERROR("D3D10 should never need to update ThebesLayers in an empty transaction"); return; } DrawRegion(drawRegion, mode); if (readbackUpdates.Length() > 0) { CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, newTextureRect.width, newTextureRect.height, 1, 1, 0, D3D10_USAGE_STAGING, D3D10_CPU_ACCESS_READ); nsRefPtr readbackTexture; HRESULT hr = device()->CreateTexture2D(&desc, nullptr, getter_AddRefs(readbackTexture)); if (FAILED(hr)) { LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("ThebesLayerD3D10::Validate(): Failed to create texture"), hr); return; } device()->CopyResource(readbackTexture, mTexture); for (uint32_t i = 0; i < readbackUpdates.Length(); i++) { mD3DManager->readbackManager()->PostTask(readbackTexture, &readbackUpdates[i], gfxPoint(newTextureRect.x, newTextureRect.y)); } } mValidRegion = neededRegion; } } void ThebesLayerD3D10::LayerManagerDestroyed() { mD3DManager = nullptr; } Layer* ThebesLayerD3D10::GetLayer() { return this; } void ThebesLayerD3D10::VerifyContentType(SurfaceMode aMode) { if (mD2DSurface) { gfxContentType type = aMode != SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA ? gfxContentType::COLOR : gfxContentType::COLOR_ALPHA; if (type != mD2DSurface->GetContentType()) { mD2DSurface = new gfxD2DSurface(mTexture, type); if (!mD2DSurface || mD2DSurface->CairoStatus()) { NS_WARNING("Failed to create surface for ThebesLayerD3D10."); mD2DSurface = nullptr; return; } mValidRegion.SetEmpty(); } } else if (mDrawTarget) { SurfaceFormat format = aMode != SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8; if (format != mDrawTarget->GetFormat()) { mDrawTarget = Factory::CreateDrawTargetForD3D10Texture(mTexture, format); if (!mDrawTarget) { NS_WARNING("Failed to create drawtarget for ThebesLayerD3D10."); return; } mValidRegion.SetEmpty(); } } if (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA && mTextureOnWhite) { // If we've transitioned away from component alpha, we can delete those resources. mD2DSurfaceOnWhite = nullptr; mSRViewOnWhite = nullptr; mTextureOnWhite = nullptr; mValidRegion.SetEmpty(); } } void ThebesLayerD3D10::FillTexturesBlackWhite(const nsIntRegion& aRegion, const nsIntPoint& aOffset) { if (mTexture && mTextureOnWhite) { // It would be more optimal to draw the actual geometry, but more code // and probably not worth the win here as this will often be a single // rect. nsRefPtr oldRT; device()->OMGetRenderTargets(1, getter_AddRefs(oldRT), nullptr); nsRefPtr viewBlack; nsRefPtr viewWhite; device()->CreateRenderTargetView(mTexture, nullptr, getter_AddRefs(viewBlack)); device()->CreateRenderTargetView(mTextureOnWhite, nullptr, getter_AddRefs(viewWhite)); D3D10_RECT oldScissor; UINT numRects = 1; device()->RSGetScissorRects(&numRects, &oldScissor); D3D10_TEXTURE2D_DESC desc; mTexture->GetDesc(&desc); D3D10_RECT scissor = { 0, 0, desc.Width, desc.Height }; device()->RSSetScissorRects(1, &scissor); mD3DManager->SetupInputAssembler(); nsIntSize oldVP = mD3DManager->GetViewport(); mD3DManager->SetViewport(nsIntSize(desc.Width, desc.Height)); ID3D10RenderTargetView *views[2] = { viewBlack, viewWhite }; device()->OMSetRenderTargets(2, views, nullptr); gfx3DMatrix transform; transform.Translate(gfxPoint3D(-aOffset.x, -aOffset.y, 0)); void* raw = &const_cast(transform)._11; effect()->GetVariableByName("mLayerTransform")->SetRawValue(raw, 0, 64); ID3D10EffectTechnique *technique = effect()->GetTechniqueByName("PrepareAlphaExtractionTextures"); nsIntRegionRectIterator iter(aRegion); const nsIntRect *iterRect; while ((iterRect = iter.Next())) { effect()->GetVariableByName("vLayerQuad")->AsVector()->SetFloatVector( ShaderConstantRectD3D10( (float)iterRect->x, (float)iterRect->y, (float)iterRect->width, (float)iterRect->height) ); technique->GetPassByIndex(0)->Apply(0); device()->Draw(4, 0); } views[0] = oldRT; device()->OMSetRenderTargets(1, views, nullptr); mD3DManager->SetViewport(oldVP); device()->RSSetScissorRects(1, &oldScissor); } } void ThebesLayerD3D10::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode) { nsIntRect visibleRect = mVisibleRegion.GetBounds(); if (!mD2DSurface && !mDrawTarget) { return; } nsRefPtr destinationSurface; if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { FillTexturesBlackWhite(aRegion, visibleRect.TopLeft()); } else { destinationSurface = mD2DSurface; } MOZ_ASSERT(mDrawTarget); nsRefPtr context = new gfxContext(mDrawTarget); context->Translate(gfxPoint(-visibleRect.x, -visibleRect.y)); if (aMode == SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA) { nsIntRegionRectIterator iter(aRegion); const nsIntRect *iterRect; while ((iterRect = iter.Next())) { mDrawTarget->ClearRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height)); } } mDrawTarget->SetPermitSubpixelAA(!(mContentFlags & CONTENT_COMPONENT_ALPHA)); LayerManagerD3D10::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); cbInfo.Callback(this, context, aRegion, DrawRegionClip::DRAW, nsIntRegion(), cbInfo.CallbackData); } void ThebesLayerD3D10::CreateNewTextures(const gfx::IntSize &aSize, SurfaceMode aMode) { if (aSize.width == 0 || aSize.height == 0) { // Nothing to do. return; } CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width, aSize.height, 1, 1); desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; desc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE; HRESULT hr; if (!mTexture) { hr = device()->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture)); if (FAILED(hr)) { NS_WARNING("Failed to create new texture for ThebesLayerD3D10!"); return; } hr = device()->CreateShaderResourceView(mTexture, nullptr, getter_AddRefs(mSRView)); if (FAILED(hr)) { NS_WARNING("Failed to create shader resource view for ThebesLayerD3D10."); } mDrawTarget = nullptr; } if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA && !mTextureOnWhite) { hr = device()->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTextureOnWhite)); if (FAILED(hr)) { NS_WARNING("Failed to create new texture for ThebesLayerD3D10!"); return; } hr = device()->CreateShaderResourceView(mTextureOnWhite, nullptr, getter_AddRefs(mSRViewOnWhite)); if (FAILED(hr)) { NS_WARNING("Failed to create shader resource view for ThebesLayerD3D10."); } mDrawTarget = nullptr; } if (!mDrawTarget) { if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { mDrawTarget = Factory::CreateDualDrawTargetForD3D10Textures(mTexture, mTextureOnWhite, SurfaceFormat::B8G8R8X8); } else { mDrawTarget = Factory::CreateDrawTargetForD3D10Texture(mTexture, aMode != SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8); } if (!mDrawTarget) { NS_WARNING("Failed to create DrawTarget for ThebesLayerD3D10."); mDrawTarget = nullptr; return; } } } } /* namespace layers */ } /* namespace mozilla */