/* -*- 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 CreateTextureHostD3D9(const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator, TextureFlags aFlags) { RefPtr 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 TextureSourceD3D9::InitTextures(DeviceManagerD3D9* aDeviceManager, const IntSize &aSize, _D3DFORMAT aFormat, RefPtr& aSurface, D3DLOCKED_RECT& aLockedRect) { if (!aDeviceManager) { return nullptr; } RefPtr 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 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 dstSurface; aTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); aDeviceManager->device()->UpdateSurface(aSurface, nullptr, dstSurface, nullptr); } already_AddRefed 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 surface; D3DLOCKED_RECT lockedRect; RefPtr 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 TextureSourceD3D9::TextureToTexture(DeviceManagerD3D9* aDeviceManager, IDirect3DTexture9* aTexture, const IntSize& aSize, _D3DFORMAT aFormat) { if (!aDeviceManager) { return nullptr; } RefPtr 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 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 srcTexture; RefPtr 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 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(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 deviceManager = DeviceManagerD3D9::Get(); RefPtr 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(mTexture.get())); return true; } already_AddRefed D3D9TextureData::BorrowDrawTarget() { MOZ_ASSERT(mD3D9Surface); if (!mD3D9Surface) { return nullptr; } RefPtr dt; if (ContentForFormat(mFormat) == gfxContentType::COLOR) { RefPtr 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 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 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 DXGID3D9TextureData::GetD3D9Surface() const { RefPtr 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(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 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 srcSurface; RefPtr 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 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(aDescriptor.handleY()); mHandles[1] = reinterpret_cast(aDescriptor.handleCb()); mHandles[2] = reinterpret_cast(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; } } }