diff options
Diffstat (limited to 'gfx/layers/d3d9/TextureD3D9.cpp')
-rw-r--r-- | gfx/layers/d3d9/TextureD3D9.cpp | 1218 |
1 files changed, 1218 insertions, 0 deletions
diff --git a/gfx/layers/d3d9/TextureD3D9.cpp b/gfx/layers/d3d9/TextureD3D9.cpp new file mode 100644 index 000000000..9828450d1 --- /dev/null +++ b/gfx/layers/d3d9/TextureD3D9.cpp @@ -0,0 +1,1218 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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 "TextureD3D9.h" +#include "CompositorD3D9.h" +#include "gfxContext.h" +#include "gfxImageSurface.h" +#include "Effects.h" +#include "gfxWindowsPlatform.h" +#include "gfx2DGlue.h" +#include "gfxUtils.h" +#include "mozilla/gfx/2D.h" +#include "GeckoProfiler.h" + +using namespace mozilla::gfx; + +namespace mozilla { +namespace layers { + +static uint32_t GetRequiredTilesD3D9(uint32_t aSize, uint32_t aMaxSize) +{ + uint32_t requiredTiles = aSize / aMaxSize; + if (aSize % aMaxSize) { + requiredTiles++; + } + return requiredTiles; +} + +TextureSourceD3D9::~TextureSourceD3D9() +{ + MOZ_ASSERT(!mCreatingDeviceManager || + mCreatingDeviceManager->IsInTextureHostList(this), + "Inconsistency in list of texture hosts."); + // Remove ourselves from the list of d3d9 texture hosts. + if (mPreviousHost) { + MOZ_ASSERT(mPreviousHost->mNextHost == this); + mPreviousHost->mNextHost = mNextHost; + } else if (mCreatingDeviceManager) { + mCreatingDeviceManager->RemoveTextureListHead(this); + } + if (mNextHost) { + MOZ_ASSERT(mNextHost->mPreviousHost == this); + mNextHost->mPreviousHost = mPreviousHost; + } +} + +already_AddRefed<TextureHost> +CreateTextureHostD3D9(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags) +{ + RefPtr<TextureHost> result; + switch (aDesc.type()) { + case SurfaceDescriptor::TSurfaceDescriptorBuffer: { + result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags); + break; + } + case SurfaceDescriptor::TSurfaceDescriptorD3D9: { + result = new TextureHostD3D9(aFlags, aDesc); + break; + } + case SurfaceDescriptor::TSurfaceDescriptorD3D10: { + result = new DXGITextureHostD3D9(aFlags, aDesc); + break; + } + case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: { + result = new DXGIYCbCrTextureHostD3D9(aFlags, aDesc.get_SurfaceDescriptorDXGIYCbCr()); + break; + } + default: { + NS_WARNING("Unsupported SurfaceDescriptor type"); + } + } + return result.forget(); +} + +static SurfaceFormat +D3D9FormatToSurfaceFormat(_D3DFORMAT format) +{ + switch (format) { + case D3DFMT_X8R8G8B8: + return SurfaceFormat::B8G8R8X8; + case D3DFMT_A8R8G8B8: + return SurfaceFormat::B8G8R8A8; + case D3DFMT_A8: + return SurfaceFormat::A8; + default: + NS_ERROR("Bad texture format"); + } + return SurfaceFormat::UNKNOWN; +} + +static _D3DFORMAT +SurfaceFormatToD3D9Format(SurfaceFormat format) +{ + switch (format) { + case SurfaceFormat::B8G8R8X8: + return D3DFMT_X8R8G8B8; + case SurfaceFormat::B8G8R8A8: + return D3DFMT_A8R8G8B8; + case SurfaceFormat::A8: + return D3DFMT_A8; + default: + NS_ERROR("Bad texture format"); + } + return D3DFMT_A8R8G8B8; +} + +CompositingRenderTargetD3D9::CompositingRenderTargetD3D9(IDirect3DTexture9* aTexture, + SurfaceInitMode aInit, + const gfx::IntRect& aRect) + : CompositingRenderTarget(aRect.TopLeft()) + , mInitMode(aInit) +{ + MOZ_COUNT_CTOR(CompositingRenderTargetD3D9); + MOZ_ASSERT(aTexture); + + mTexture = aTexture; + mTexture->GetSurfaceLevel(0, getter_AddRefs(mSurface)); + NS_ASSERTION(mSurface, "Couldn't create surface for texture"); + TextureSourceD3D9::SetSize(aRect.Size()); + + if (aInit == INIT_MODE_CLEAR) { + ClearOnBind(); + } +} + +CompositingRenderTargetD3D9::CompositingRenderTargetD3D9(IDirect3DSurface9* aSurface, + SurfaceInitMode aInit, + const gfx::IntRect& aRect) + : CompositingRenderTarget(aRect.TopLeft()) + , mSurface(aSurface) + , mInitMode(aInit) +{ + MOZ_COUNT_CTOR(CompositingRenderTargetD3D9); + MOZ_ASSERT(mSurface); + TextureSourceD3D9::SetSize(aRect.Size()); + + if (aInit == INIT_MODE_CLEAR) { + ClearOnBind(); + } +} + +CompositingRenderTargetD3D9::~CompositingRenderTargetD3D9() +{ + MOZ_COUNT_DTOR(CompositingRenderTargetD3D9); +} + +void +CompositingRenderTargetD3D9::BindRenderTarget(IDirect3DDevice9* aDevice) +{ + aDevice->SetRenderTarget(0, mSurface); + if (mClearOnBind) { + aDevice->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 0), 0, 0); + mClearOnBind = false; + } +} + +IntSize +CompositingRenderTargetD3D9::GetSize() const +{ + return TextureSourceD3D9::GetSize(); +} + +/** + * Helper method for DataToTexture. + * The last three params are out params. + * Returns the created texture, or null if we fail. + */ +already_AddRefed<IDirect3DTexture9> +TextureSourceD3D9::InitTextures(DeviceManagerD3D9* aDeviceManager, + const IntSize &aSize, + _D3DFORMAT aFormat, + RefPtr<IDirect3DSurface9>& aSurface, + D3DLOCKED_RECT& aLockedRect) +{ + if (!aDeviceManager) { + return nullptr; + } + RefPtr<IDirect3DTexture9> result; + // D3D9Ex doesn't support managed textures and we don't want the hassle even + // if we don't have Ex. We could use dynamic textures + // here but since Images are immutable that probably isn't such a great + // idea. + result = aDeviceManager->CreateTexture(aSize, aFormat, D3DPOOL_DEFAULT, this); + if (!result) { + return nullptr; + } + + RefPtr<IDirect3DTexture9> tmpTexture = + aDeviceManager->CreateTexture(aSize, aFormat, D3DPOOL_SYSTEMMEM, this); + if (!tmpTexture) { + return nullptr; + } + + tmpTexture->GetSurfaceLevel(0, getter_AddRefs(aSurface)); + + HRESULT hr = aSurface->LockRect(&aLockedRect, nullptr, 0); + if (FAILED(hr) || !aLockedRect.pBits) { + gfxCriticalError() << "Failed to lock rect initialize texture in D3D9 " << hexa(hr); + return nullptr; + } + + return result.forget(); +} + +/** + * Helper method for DataToTexture. + */ +static void +FinishTextures(DeviceManagerD3D9* aDeviceManager, + IDirect3DTexture9* aTexture, + IDirect3DSurface9* aSurface) +{ + if (!aDeviceManager) { + return; + } + + aSurface->UnlockRect(); + RefPtr<IDirect3DSurface9> dstSurface; + aTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + aDeviceManager->device()->UpdateSurface(aSurface, nullptr, dstSurface, + nullptr); +} + +already_AddRefed<IDirect3DTexture9> +TextureSourceD3D9::DataToTexture(DeviceManagerD3D9* aDeviceManager, + unsigned char *aData, + int aStride, + const IntSize &aSize, + _D3DFORMAT aFormat, + uint32_t aBPP) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); + RefPtr<IDirect3DSurface9> surface; + D3DLOCKED_RECT lockedRect; + RefPtr<IDirect3DTexture9> texture = InitTextures(aDeviceManager, aSize, aFormat, + surface, lockedRect); + if (!texture) { + return nullptr; + } + + uint32_t width = aSize.width * aBPP; + + for (int y = 0; y < aSize.height; y++) { + memcpy((char*)lockedRect.pBits + lockedRect.Pitch * y, + aData + aStride * y, + width); + } + + FinishTextures(aDeviceManager, texture, surface); + + return texture.forget(); +} + +already_AddRefed<IDirect3DTexture9> +TextureSourceD3D9::TextureToTexture(DeviceManagerD3D9* aDeviceManager, + IDirect3DTexture9* aTexture, + const IntSize& aSize, + _D3DFORMAT aFormat) +{ + if (!aDeviceManager) { + return nullptr; + } + + RefPtr<IDirect3DTexture9> texture = + aDeviceManager->CreateTexture(aSize, aFormat, D3DPOOL_DEFAULT, this); + if (!texture) { + return nullptr; + } + + HRESULT hr = aDeviceManager->device()->UpdateTexture(aTexture, texture); + if (FAILED(hr)) { + return nullptr; + } + + return texture.forget(); +} + +DataTextureSourceD3D9::DataTextureSourceD3D9(gfx::SurfaceFormat aFormat, + CompositorD3D9* aCompositor, + TextureFlags aFlags, + StereoMode aStereoMode) + : mCompositor(aCompositor) + , mFormat(aFormat) + , mCurrentTile(0) + , mFlags(aFlags) + , mIsTiled(false) + , mIterating(false) + , mAllowTextureUploads(true) +{ + mStereoMode = aStereoMode; + MOZ_COUNT_CTOR(DataTextureSourceD3D9); +} + +DataTextureSourceD3D9::DataTextureSourceD3D9(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + CompositorD3D9* aCompositor, + IDirect3DTexture9* aTexture, + TextureFlags aFlags) + : mCompositor(aCompositor) + , mFormat(aFormat) + , mCurrentTile(0) + , mFlags(aFlags) + , mIsTiled(false) + , mIterating(false) + , mAllowTextureUploads(false) +{ + mSize = aSize; + mTexture = aTexture; + mStereoMode = StereoMode::MONO; + MOZ_COUNT_CTOR(DataTextureSourceD3D9); +} + +DataTextureSourceD3D9::~DataTextureSourceD3D9() +{ + MOZ_COUNT_DTOR(DataTextureSourceD3D9); +} + +IDirect3DTexture9* +DataTextureSourceD3D9::GetD3D9Texture() +{ + return mIterating ? mTileTextures[mCurrentTile] + : mTexture; +} + +bool +DataTextureSourceD3D9::Update(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion, + gfx::IntPoint* aSrcOffset) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); + // Right now we only support full surface update. If aDestRegion is provided, + // It will be ignored. Incremental update with a source offset is only used + // on Mac so it is not clear that we ever will need to support it for D3D. + MOZ_ASSERT(!aSrcOffset); + + MOZ_ASSERT(mAllowTextureUploads); + if (!mAllowTextureUploads) { + return false; + } + + if (!mCompositor || !mCompositor->device()) { + NS_WARNING("No D3D device to update the texture."); + return false; + } + + uint32_t bpp = BytesPerPixel(aSurface->GetFormat()); + RefPtr<DeviceManagerD3D9> deviceManager = DeviceManagerD3D9::Get(); + + mSize = aSurface->GetSize(); + mFormat = aSurface->GetFormat(); + + _D3DFORMAT format = D3DFMT_A8R8G8B8; + switch (mFormat) { + case SurfaceFormat::B8G8R8X8: + format = D3DFMT_X8R8G8B8; + bpp = 4; + break; + case SurfaceFormat::B8G8R8A8: + format = D3DFMT_A8R8G8B8; + bpp = 4; + break; + case SurfaceFormat::A8: + format = D3DFMT_A8; + bpp = 1; + break; + default: + NS_WARNING("Bad image format"); + return false; + } + + int32_t maxSize = mCompositor->GetMaxTextureSize(); + if ((mSize.width <= maxSize && mSize.height <= maxSize) || + (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) { + mIsTiled = false; + + if (mTexture) { + D3DSURFACE_DESC currentDesc; + mTexture->GetLevelDesc(0, ¤tDesc); + + // Make sure there's no size mismatch, if there is, recreate. + if (currentDesc.Width != mSize.width || currentDesc.Height != mSize.height || + currentDesc.Format != format) { + mTexture = nullptr; + // Make sure we upload the whole surface. + aDestRegion = nullptr; + } + } + + if (!mTexture) { + // TODO Improve: Reallocating this texture is costly enough + // that it causes us to skip frames on scrolling + // important pages like Facebook. + mTexture = deviceManager->CreateTexture(mSize, format, D3DPOOL_DEFAULT, this); + mIsTiled = false; + if (!mTexture) { + Reset(); + return false; + } + + if (mFlags & TextureFlags::COMPONENT_ALPHA) { + aDestRegion = nullptr; + } + } + + DataSourceSurface::MappedSurface map; + if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { + gfxCriticalError() << "Failed to map surface."; + Reset(); + return false; + } + + nsIntRegion regionToUpdate = aDestRegion ? *aDestRegion : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)); + + RefPtr<IDirect3DTexture9> srcTexture; + RefPtr<IDirect3DSurface9> srcSurface; + + if (mFormat == SurfaceFormat::A8) { + // A8 doesn't appear to work with CreateOffscreenPlainSurface + srcTexture = deviceManager->CreateTexture(mSize, format, D3DPOOL_SYSTEMMEM, this); + if (!srcTexture) { + aSurface->Unmap(); + return false; + } + srcTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); + } else { + HRESULT hr = mCompositor->device()->CreateOffscreenPlainSurface(mSize.width, mSize.height, format, D3DPOOL_SYSTEMMEM, getter_AddRefs(srcSurface), nullptr); + if (FAILED(hr)) { + aSurface->Unmap(); + return false; + } + } + + RefPtr<IDirect3DSurface9> destSurface; + mTexture->GetSurfaceLevel(0, getter_AddRefs(destSurface)); + + D3DLOCKED_RECT rect; + HRESULT hr = srcSurface->LockRect(&rect, nullptr, 0); + if (FAILED(hr) || !rect.pBits) { + gfxCriticalError() << "Failed to lock rect initialize texture in D3D9 " << hexa(hr); + return false; + } + + for (auto iter = regionToUpdate.RectIter(); !iter.Done(); iter.Next()) { + const nsIntRect& iterRect = iter.Get(); + uint8_t* src = map.mData + map.mStride * iterRect.y + BytesPerPixel(aSurface->GetFormat()) * iterRect.x; + uint8_t* dest = reinterpret_cast<uint8_t*>(rect.pBits) + rect.Pitch * iterRect.y + BytesPerPixel(aSurface->GetFormat()) * iterRect.x; + + for (int y = 0; y < iterRect.height; y++) { + memcpy(dest + rect.Pitch * y, + src + map.mStride * y, + iterRect.width * bpp); + } + } + + srcSurface->UnlockRect(); + aSurface->Unmap(); + + for (auto iter = regionToUpdate.RectIter(); !iter.Done(); iter.Next()) { + const nsIntRect& iterRect = iter.Get(); + + RECT updateRect; + updateRect.left = iterRect.x; + updateRect.top = iterRect.y; + updateRect.right = iterRect.XMost(); + updateRect.bottom = iterRect.YMost(); + POINT point = { updateRect.left, updateRect.top }; + + mCompositor->device()->UpdateSurface(srcSurface, &updateRect, destSurface, &point); + } + } else { + mIsTiled = true; + uint32_t tileCount = GetRequiredTilesD3D9(mSize.width, maxSize) * + GetRequiredTilesD3D9(mSize.height, maxSize); + mTileTextures.resize(tileCount); + mTexture = nullptr; + + for (uint32_t i = 0; i < tileCount; i++) { + IntRect tileRect = GetTileRect(i); + unsigned char* data = aSurface->GetData() + + tileRect.y * aSurface->Stride() + + tileRect.x * bpp; + mTileTextures[i] = DataToTexture(deviceManager, + data, + aSurface->Stride(), + IntSize(tileRect.width, tileRect.height), + format, + bpp); + if (!mTileTextures[i]) { + NS_WARNING("Could not upload texture"); + Reset(); + return false; + } + } + } + + return true; +} + +static CompositorD3D9* AssertD3D9Compositor(Compositor* aCompositor) +{ + CompositorD3D9* compositor = aCompositor ? aCompositor->AsCompositorD3D9() + : nullptr; + if (!compositor) { + // We probably had a device reset and this D3D9 texture was already sent but + // we are now falling back to a basic compositor. That can happen if a video + // is playing while the device reset occurs and it's not too bad if we miss a + // few frames. + gfxCriticalNote << "[D3D9] Attempt to set an incompatible compositor"; + } + return compositor; +} + +void +DataTextureSourceD3D9::SetCompositor(Compositor* aCompositor) +{ + CompositorD3D9* d3dCompositor = AssertD3D9Compositor(aCompositor); + if (!d3dCompositor) { + Reset(); + return; + } + if (mCompositor && mCompositor != d3dCompositor) { + Reset(); + } + mCompositor = d3dCompositor; +} + +void +DataTextureSourceD3D9::Reset() +{ + mSize.width = 0; + mSize.height = 0; + mIsTiled = false; + mTexture = nullptr; + mTileTextures.clear(); +} + +IntRect +DataTextureSourceD3D9::GetTileRect(uint32_t aTileIndex) const +{ + uint32_t maxSize = mCompositor->GetMaxTextureSize(); + uint32_t horizontalTiles = GetRequiredTilesD3D9(mSize.width, maxSize); + uint32_t verticalTiles = GetRequiredTilesD3D9(mSize.height, maxSize); + + uint32_t verticalTile = aTileIndex / horizontalTiles; + uint32_t horizontalTile = aTileIndex % horizontalTiles; + + return IntRect(horizontalTile * maxSize, + verticalTile * maxSize, + horizontalTile < (horizontalTiles - 1) ? maxSize : mSize.width % maxSize, + verticalTile < (verticalTiles - 1) ? maxSize : mSize.height % maxSize); +} + +IntRect +DataTextureSourceD3D9::GetTileRect() +{ + return GetTileRect(mCurrentTile); +} + + +D3D9TextureData::D3D9TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + IDirect3DTexture9* aTexture) +: mTexture(aTexture) +, mSize(aSize) +, mFormat(aFormat) +, mNeedsClear(false) +, mNeedsClearWhite(false) +{ + MOZ_COUNT_CTOR(D3D9TextureData); +} + +D3D9TextureData::~D3D9TextureData() +{ + MOZ_COUNT_DTOR(D3D9TextureData); +} + +D3D9TextureData* +D3D9TextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + TextureAllocationFlags aAllocFlags) +{ + _D3DFORMAT format = SurfaceFormatToD3D9Format(aFormat); + RefPtr<DeviceManagerD3D9> deviceManager = DeviceManagerD3D9::Get(); + RefPtr<IDirect3DTexture9> d3d9Texture = deviceManager ? deviceManager->CreateTexture(aSize, format, + D3DPOOL_SYSTEMMEM, + nullptr) + : nullptr; + if (!d3d9Texture) { + NS_WARNING("Could not create a d3d9 texture"); + return nullptr; + } + D3D9TextureData* data = new D3D9TextureData(aSize, aFormat, d3d9Texture); + + data->mNeedsClear = aAllocFlags & ALLOC_CLEAR_BUFFER; + data->mNeedsClearWhite = aAllocFlags & ALLOC_CLEAR_BUFFER_WHITE; + + return data; +} + +TextureData* +D3D9TextureData::CreateSimilar(LayersIPCChannel*, LayersBackend, TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const +{ + return D3D9TextureData::Create(mSize, mFormat, aAllocFlags); +} + +void +D3D9TextureData::FillInfo(TextureData::Info& aInfo) const +{ + aInfo.size = mSize; + aInfo.format = mFormat; + aInfo.hasIntermediateBuffer = true; + aInfo.supportsMoz2D = true; + aInfo.canExposeMappedData = false; + aInfo.hasSynchronization = false; +} + +bool +D3D9TextureData::Lock(OpenMode aMode) +{ + if (!DeviceManagerD3D9::GetDevice()) { + // If the device has failed then we should not lock the surface, + // even if we could. + mD3D9Surface = nullptr; + return false; + } + + if (!mD3D9Surface) { + HRESULT hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(mD3D9Surface)); + if (FAILED(hr)) { + NS_WARNING("Failed to get texture surface level."); + return false; + } + } + return true; +} +void +D3D9TextureData::Unlock() +{ + if (mLockRect) { + mD3D9Surface->UnlockRect(); + mLockRect = false; + } +} + +bool +D3D9TextureData::Serialize(SurfaceDescriptor& aOutDescriptor) +{ + mTexture->AddRef(); // Release in TextureHostD3D9::TextureHostD3D9 + aOutDescriptor = SurfaceDescriptorD3D9(reinterpret_cast<uintptr_t>(mTexture.get())); + return true; +} + +already_AddRefed<gfx::DrawTarget> +D3D9TextureData::BorrowDrawTarget() +{ + MOZ_ASSERT(mD3D9Surface); + if (!mD3D9Surface) { + return nullptr; + } + + RefPtr<DrawTarget> dt; + if (ContentForFormat(mFormat) == gfxContentType::COLOR) { + RefPtr<gfxASurface> surface = new gfxWindowsSurface(mD3D9Surface); + if (!surface || surface->CairoStatus()) { + NS_WARNING("Could not create gfxASurface for d3d9 surface"); + return nullptr; + } + dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surface, mSize); + + if (!dt) { + return nullptr; + } + } else { + // gfxWindowsSurface don't support transparency so we can't use the d3d9 + // windows surface optimization. + // Instead we have to use a gfxImageSurface and fallback for font drawing. + D3DLOCKED_RECT rect; + HRESULT hr = mD3D9Surface->LockRect(&rect, nullptr, 0); + if (FAILED(hr) || !rect.pBits) { + gfxCriticalError() << "Failed to lock rect borrowing the target in D3D9 (BDT) " << hexa(hr); + return nullptr; + } + dt = gfxPlatform::CreateDrawTargetForData((uint8_t*)rect.pBits, mSize, + rect.Pitch, mFormat); + if (!dt) { + return nullptr; + } + + mLockRect = true; + } + + if (mNeedsClear) { + dt->ClearRect(Rect(0, 0, mSize.width, mSize.height)); + mNeedsClear = false; + } + if (mNeedsClearWhite) { + dt->FillRect(Rect(0, 0, mSize.width, mSize.height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0))); + mNeedsClearWhite = false; + } + + return dt.forget(); +} + +bool +D3D9TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) +{ + MOZ_ASSERT(mD3D9Surface); + + // gfxWindowsSurface don't support transparency so we can't use the d3d9 + // windows surface optimization. + // Instead we have to use a gfxImageSurface and fallback for font drawing. + D3DLOCKED_RECT rect; + HRESULT hr = mD3D9Surface->LockRect(&rect, nullptr, 0); + if (FAILED(hr) || !rect.pBits) { + gfxCriticalError() << "Failed to lock rect borrowing the target in D3D9 (UFS) " << hexa(hr); + return false; + } + + RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface(); + + if (!srcSurf) { + gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (D3D9)."; + mD3D9Surface->UnlockRect(); + return false; + } + + DataSourceSurface::MappedSurface sourceMap; + if (!srcSurf->Map(DataSourceSurface::READ, &sourceMap)) { + gfxCriticalError() << "Failed to map source surface for UpdateFromSurface (D3D9)."; + return false; + } + + for (int y = 0; y < srcSurf->GetSize().height; y++) { + memcpy((uint8_t*)rect.pBits + rect.Pitch * y, + sourceMap.mData + sourceMap.mStride * y, + srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat())); + } + + srcSurf->Unmap(); + mD3D9Surface->UnlockRect(); + + return true; +} + +DXGID3D9TextureData::DXGID3D9TextureData(gfx::SurfaceFormat aFormat, + IDirect3DTexture9* aTexture, HANDLE aHandle, + IDirect3DDevice9* aDevice) +: mDevice(aDevice) +, mTexture(aTexture) +, mFormat(aFormat) +, mHandle(aHandle) +{ + MOZ_COUNT_CTOR(DXGID3D9TextureData); +} + +DXGID3D9TextureData::~DXGID3D9TextureData() +{ + gfxWindowsPlatform::sD3D9SharedTextures -= mDesc.Width * mDesc.Height * 4; + MOZ_COUNT_DTOR(DXGID3D9TextureData); +} + +// static +DXGID3D9TextureData* +DXGID3D9TextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + TextureFlags aFlags, + IDirect3DDevice9* aDevice) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); + MOZ_ASSERT(aFormat == gfx::SurfaceFormat::B8G8R8A8); + if (aFormat != gfx::SurfaceFormat::B8G8R8A8) { + return nullptr; + } + + RefPtr<IDirect3DTexture9> texture; + HANDLE shareHandle = nullptr; + HRESULT hr = aDevice->CreateTexture(aSize.width, aSize.height, + 1, + D3DUSAGE_RENDERTARGET, + D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, + getter_AddRefs(texture), + &shareHandle); + if (FAILED(hr) || !shareHandle) { + return nullptr; + } + + D3DSURFACE_DESC surfaceDesc; + hr = texture->GetLevelDesc(0, &surfaceDesc); + if (FAILED(hr)) { + return nullptr; + } + DXGID3D9TextureData* data = new DXGID3D9TextureData(aFormat, texture, shareHandle, aDevice); + data->mDesc = surfaceDesc; + + gfxWindowsPlatform::sD3D9SharedTextures += aSize.width * aSize.height * 4; + return data; +} + +void +DXGID3D9TextureData::FillInfo(TextureData::Info& aInfo) const +{ + aInfo.size = GetSize(); + aInfo.format = mFormat; + aInfo.supportsMoz2D = false; + aInfo.canExposeMappedData = false; + aInfo.hasIntermediateBuffer = false; + aInfo.hasSynchronization = false; +} + +already_AddRefed<IDirect3DSurface9> +DXGID3D9TextureData::GetD3D9Surface() const +{ + RefPtr<IDirect3DSurface9> textureSurface; + HRESULT hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(textureSurface)); + NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); + + return textureSurface.forget(); +} + +bool +DXGID3D9TextureData::Serialize(SurfaceDescriptor& aOutDescriptor) +{ + aOutDescriptor = SurfaceDescriptorD3D10((WindowsHandle)(mHandle), mFormat, GetSize()); + return true; +} + + +TextureHostD3D9::TextureHostD3D9(TextureFlags aFlags, + const SurfaceDescriptorD3D9& aDescriptor) + : TextureHost(aFlags) + , mFormat(SurfaceFormat::UNKNOWN) + , mIsLocked(false) +{ + mTexture = reinterpret_cast<IDirect3DTexture9*>(aDescriptor.texture()); + MOZ_ASSERT(mTexture); + mTexture->Release(); // see AddRef in TextureClientD3D9::ToSurfaceDescriptor + MOZ_ASSERT(mTexture); + D3DSURFACE_DESC desc; + HRESULT hr = mTexture->GetLevelDesc(0, &desc); + if (!FAILED(hr)) { + mFormat = D3D9FormatToSurfaceFormat(desc.Format); + mSize.width = desc.Width; + mSize.height = desc.Height; + } +} + +bool +DataTextureSourceD3D9::UpdateFromTexture(IDirect3DTexture9* aTexture, + const nsIntRegion* aRegion) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); + MOZ_ASSERT(aTexture); + + D3DSURFACE_DESC desc; + HRESULT hr = aTexture->GetLevelDesc(0, &desc); + if (FAILED(hr)) { + return false; + } else { + // If we changed the compositor, the size might have been reset to zero + // Otherwise the texture size must not change. + MOZ_ASSERT(mFormat == D3D9FormatToSurfaceFormat(desc.Format)); + MOZ_ASSERT(!mSize.width || mSize.width == desc.Width); + MOZ_ASSERT(!mSize.height || mSize.height == desc.Height); + mSize = IntSize(desc.Width, desc.Height); + } + + RefPtr<DeviceManagerD3D9> dm = DeviceManagerD3D9::Get(); + if (!dm || !dm->device()) { + return false; + } + + if (!mTexture) { + mTexture = dm->CreateTexture(mSize, SurfaceFormatToD3D9Format(mFormat), + D3DPOOL_DEFAULT, this); + if (!mTexture) { + NS_WARNING("Failed to create a texture"); + return false; + } + } + + RefPtr<IDirect3DSurface9> srcSurface; + RefPtr<IDirect3DSurface9> dstSurface; + + hr = aTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); + if (FAILED(hr)) { + return false; + } + hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + if (FAILED(hr)) { + return false; + } + + if (aRegion) { + for (auto iter = aRegion->RectIter(); !iter.Done(); iter.Next()) { + const IntRect& iterRect = iter.Get(); + RECT rect; + rect.left = iterRect.x; + rect.top = iterRect.y; + rect.right = iterRect.XMost(); + rect.bottom = iterRect.YMost(); + + POINT point; + point.x = iterRect.x; + point.y = iterRect.y; + hr = dm->device()->UpdateSurface(srcSurface, &rect, dstSurface, &point); + if (FAILED(hr)) { + NS_WARNING("Failed Update the surface"); + return false; + } + } + } else { + hr = dm->device()->UpdateSurface(srcSurface, nullptr, dstSurface, nullptr); + if (FAILED(hr)) { + NS_WARNING("Failed Update the surface"); + return false; + } + } + mIsTiled = false; + return true; +} + +void +TextureHostD3D9::UpdatedInternal(const nsIntRegion* aRegion) +{ + MOZ_ASSERT(mTexture); + if (!mTexture) { + return; + } + + const nsIntRegion* regionToUpdate = aRegion; + if (!mTextureSource) { + mTextureSource = new DataTextureSourceD3D9(mFormat, mSize, mCompositor, + nullptr, mFlags); + if (mFlags & TextureFlags::COMPONENT_ALPHA) { + // Update the full region the first time for component alpha textures. + regionToUpdate = nullptr; + } + } + + if (!mTextureSource->UpdateFromTexture(mTexture, regionToUpdate)) { + gfxCriticalNote << "[D3D9] DataTextureSourceD3D9::UpdateFromTexture failed"; + } + + ReadUnlock(); +} + +IDirect3DDevice9* +TextureHostD3D9::GetDevice() +{ + if (mFlags & TextureFlags::INVALID_COMPOSITOR) { + return nullptr; + } + return mCompositor ? mCompositor->device() : nullptr; +} + +void +TextureHostD3D9::SetCompositor(Compositor* aCompositor) +{ + mCompositor = AssertD3D9Compositor(aCompositor); + if (!mCompositor) { + mTextureSource = nullptr; + return; + } + if (mTextureSource) { + mTextureSource->SetCompositor(aCompositor); + } +} + +Compositor* +TextureHostD3D9::GetCompositor() +{ + return mCompositor; +} + +bool +TextureHostD3D9::BindTextureSource(CompositableTextureSourceRef& aTexture) +{ + MOZ_ASSERT(mIsLocked); + MOZ_ASSERT(mTextureSource); + aTexture = mTextureSource; + return !!aTexture; +} + +bool +TextureHostD3D9::Lock() +{ + MOZ_ASSERT(!mIsLocked); + // XXX - Currently if a TextureHostD3D9 is created but Update is never called, + // it will not have a TextureSource although it could since it has a valid + // D3D9 texture. + mIsLocked = !!mTextureSource; + return mIsLocked; +} + +void +TextureHostD3D9::Unlock() +{ + MOZ_ASSERT(mIsLocked); + mIsLocked = false; +} + +void +TextureHostD3D9::DeallocateDeviceData() +{ + mTextureSource = nullptr; +} + +DXGITextureHostD3D9::DXGITextureHostD3D9(TextureFlags aFlags, + const SurfaceDescriptorD3D10& aDescriptor) + : TextureHost(aFlags) + , mHandle(aDescriptor.handle()) + , mFormat(aDescriptor.format()) + , mSize(aDescriptor.size()) + , mIsLocked(false) +{ + MOZ_ASSERT(mHandle); + OpenSharedHandle(); +} + +IDirect3DDevice9* +DXGITextureHostD3D9::GetDevice() +{ + if (mFlags & TextureFlags::INVALID_COMPOSITOR) { + return nullptr; + } + return mCompositor ? mCompositor->device() : nullptr; +} + +void +DXGITextureHostD3D9::OpenSharedHandle() +{ + MOZ_ASSERT(!mTextureSource); + + if (!GetDevice()) { + return; + } + + RefPtr<IDirect3DTexture9> texture; + HRESULT hr = GetDevice()->CreateTexture(mSize.width, mSize.height, 1, + D3DUSAGE_RENDERTARGET, + SurfaceFormatToD3D9Format(mFormat), + D3DPOOL_DEFAULT, + getter_AddRefs(texture), + (HANDLE*)&mHandle); + if (FAILED(hr)) { + NS_WARNING("Failed to open shared texture"); + return; + } + + mTextureSource = new DataTextureSourceD3D9(mFormat, mSize, mCompositor, texture); + + return; +} + +bool +DXGITextureHostD3D9::BindTextureSource(CompositableTextureSourceRef& aTexture) +{ + MOZ_ASSERT(mIsLocked); + MOZ_ASSERT(mTextureSource); + aTexture = mTextureSource; + return !!aTexture; +} + +bool +DXGITextureHostD3D9::Lock() +{ + MOZ_ASSERT(!mIsLocked); + + if (!mCompositor) { + NS_WARNING("no suitable compositor"); + return false; + } + + if (!GetDevice()) { + return false; + } + + if (!mTextureSource) { + OpenSharedHandle(); + } + mIsLocked = !!mTextureSource; + return mIsLocked; +} + +void +DXGITextureHostD3D9::Unlock() +{ + MOZ_ASSERT(mIsLocked); + mIsLocked = false; +} + +void +DXGITextureHostD3D9::SetCompositor(Compositor* aCompositor) +{ + mCompositor = AssertD3D9Compositor(aCompositor); + if (!mCompositor) { + mTextureSource = nullptr; + } +} + +Compositor* +DXGITextureHostD3D9::GetCompositor() +{ + return mCompositor; +} + +void +DXGITextureHostD3D9::DeallocateDeviceData() +{ + mTextureSource = nullptr; +} + +DXGIYCbCrTextureHostD3D9::DXGIYCbCrTextureHostD3D9(TextureFlags aFlags, + const SurfaceDescriptorDXGIYCbCr& aDescriptor) + : TextureHost(aFlags) + , mSize(aDescriptor.size()) + , mSizeY(aDescriptor.sizeY()) + , mSizeCbCr(aDescriptor.sizeCbCr()) + , mIsLocked(false) +{ + mHandles[0] = reinterpret_cast<HANDLE>(aDescriptor.handleY()); + mHandles[1] = reinterpret_cast<HANDLE>(aDescriptor.handleCb()); + mHandles[2] = reinterpret_cast<HANDLE>(aDescriptor.handleCr()); +} + +IDirect3DDevice9* +DXGIYCbCrTextureHostD3D9::GetDevice() +{ + if (mFlags & TextureFlags::INVALID_COMPOSITOR) { + return nullptr; + } + return mCompositor ? mCompositor->device() : nullptr; +} + +void +DXGIYCbCrTextureHostD3D9::SetCompositor(Compositor* aCompositor) +{ + mCompositor = AssertD3D9Compositor(aCompositor); + if (!mCompositor) { + mTextureSources[0] = nullptr; + mTextureSources[1] = nullptr; + mTextureSources[2] = nullptr; + } +} + +Compositor* +DXGIYCbCrTextureHostD3D9::GetCompositor() +{ + return mCompositor; +} + +bool +DXGIYCbCrTextureHostD3D9::Lock() +{ + if (!mCompositor) { + NS_WARNING("no suitable compositor"); + return false; + } + + if (!GetDevice()) { + NS_WARNING("trying to lock a TextureHost without a D3D device"); + return false; + } + if (!mTextureSources[0]) { + if (!mHandles[0]) { + return false; + } + + if (FAILED(GetDevice()->CreateTexture(mSizeY.width, mSizeY.height, + 1, 0, D3DFMT_A8, D3DPOOL_DEFAULT, + getter_AddRefs(mTextures[0]), &mHandles[0]))) { + return false; + } + + if (FAILED(GetDevice()->CreateTexture(mSizeCbCr.width, mSizeCbCr.height, + 1, 0, D3DFMT_A8, D3DPOOL_DEFAULT, + getter_AddRefs(mTextures[1]), &mHandles[1]))) { + return false; + } + + if (FAILED(GetDevice()->CreateTexture(mSizeCbCr.width, mSizeCbCr.height, + 1, 0, D3DFMT_A8, D3DPOOL_DEFAULT, + getter_AddRefs(mTextures[2]), &mHandles[2]))) { + return false; + } + + mTextureSources[0] = new DataTextureSourceD3D9(SurfaceFormat::A8, mSize, mCompositor, mTextures[0]); + mTextureSources[1] = new DataTextureSourceD3D9(SurfaceFormat::A8, mSize, mCompositor, mTextures[1]); + mTextureSources[2] = new DataTextureSourceD3D9(SurfaceFormat::A8, mSize, mCompositor, mTextures[2]); + mTextureSources[0]->SetNextSibling(mTextureSources[1]); + mTextureSources[1]->SetNextSibling(mTextureSources[2]); + } + + mIsLocked = true; + return mIsLocked; +} + +void +DXGIYCbCrTextureHostD3D9::Unlock() +{ + MOZ_ASSERT(mIsLocked); + mIsLocked = false; +} + +bool +DXGIYCbCrTextureHostD3D9::BindTextureSource(CompositableTextureSourceRef& aTexture) +{ + MOZ_ASSERT(mIsLocked); + // If Lock was successful we must have a valid TextureSource. + MOZ_ASSERT(mTextureSources[0] && mTextureSources[1] && mTextureSources[2]); + aTexture = mTextureSources[0].get(); + return !!aTexture; +} + +} +} |