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