summaryrefslogtreecommitdiffstats
path: root/gfx/layers/d3d9/TextureD3D9.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/d3d9/TextureD3D9.cpp')
-rw-r--r--gfx/layers/d3d9/TextureD3D9.cpp1218
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, &currentDesc);
+
+ // 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;
+}
+
+}
+}