/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SourceSurfaceD2DTarget.h" #include "Logging.h" #include "DrawTargetD2D.h" #include "Tools.h" #include namespace mozilla { namespace gfx { SourceSurfaceD2DTarget::SourceSurfaceD2DTarget(DrawTargetD2D* aDrawTarget, ID3D10Texture2D* aTexture, SurfaceFormat aFormat) : mDrawTarget(aDrawTarget) , mTexture(aTexture) , mFormat(aFormat) , mOwnsCopy(false) { } SourceSurfaceD2DTarget::~SourceSurfaceD2DTarget() { // We don't need to do anything special here to notify our mDrawTarget. It must // already have cleared its mSnapshot field, otherwise this object would // be kept alive. if (mOwnsCopy) { IntSize size = GetSize(); DrawTargetD2D::mVRAMUsageSS -= size.width * size.height * BytesPerPixel(mFormat); } } IntSize SourceSurfaceD2DTarget::GetSize() const { D3D10_TEXTURE2D_DESC desc; mTexture->GetDesc(&desc); return IntSize(desc.Width, desc.Height); } SurfaceFormat SourceSurfaceD2DTarget::GetFormat() const { return mFormat; } already_AddRefed SourceSurfaceD2DTarget::GetDataSurface() { RefPtr dataSurf = new DataSourceSurfaceD2DTarget(mFormat); D3D10_TEXTURE2D_DESC desc; mTexture->GetDesc(&desc); desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ; desc.Usage = D3D10_USAGE_STAGING; desc.BindFlags = 0; desc.MiscFlags = 0; if (!Factory::GetDirect3D10Device()) { gfxCriticalError() << "Invalid D3D10 device in D2D target surface"; return nullptr; } HRESULT hr = Factory::GetDirect3D10Device()->CreateTexture2D(&desc, nullptr, getter_AddRefs(dataSurf->mTexture)); if (FAILED(hr)) { gfxDebug() << "Failed to create staging texture for SourceSurface. Code: " << hexa(hr); return nullptr; } Factory::GetDirect3D10Device()->CopyResource(dataSurf->mTexture, mTexture); return dataSurf.forget(); } void* SourceSurfaceD2DTarget::GetNativeSurface(NativeSurfaceType aType) { if (aType == NativeSurfaceType::D3D10_TEXTURE) { return static_cast(mTexture.get()); } return nullptr; } ID3D10ShaderResourceView* SourceSurfaceD2DTarget::GetSRView() { if (mSRView) { return mSRView; } if (!Factory::GetDirect3D10Device()) { gfxCriticalError() << "Invalid D3D10 device in D2D target surface"; return nullptr; } HRESULT hr = Factory::GetDirect3D10Device()->CreateShaderResourceView(mTexture, nullptr, getter_AddRefs(mSRView)); if (FAILED(hr)) { gfxWarning() << "Failed to create ShaderResourceView. Code: " << hexa(hr); } return mSRView; } void SourceSurfaceD2DTarget::DrawTargetWillChange() { RefPtr oldTexture = mTexture; D3D10_TEXTURE2D_DESC desc; mTexture->GetDesc(&desc); // Our original texture might implement the keyed mutex flag. We shouldn't // need that here. We actually specifically don't want it since we don't lock // our texture for usage! desc.MiscFlags = 0; // Get a copy of the surface data so the content at snapshot time was saved. Factory::GetDirect3D10Device()->CreateTexture2D(&desc, nullptr, getter_AddRefs(mTexture)); Factory::GetDirect3D10Device()->CopyResource(mTexture, oldTexture); mBitmap = nullptr; DrawTargetD2D::mVRAMUsageSS += desc.Width * desc.Height * BytesPerPixel(mFormat); mOwnsCopy = true; // We now no longer depend on the source surface content remaining the same. MarkIndependent(); } ID2D1Bitmap* SourceSurfaceD2DTarget::GetBitmap(ID2D1RenderTarget *aRT) { if (mBitmap) { return mBitmap; } HRESULT hr; D3D10_TEXTURE2D_DESC desc; mTexture->GetDesc(&desc); IntSize size(desc.Width, desc.Height); RefPtr surf; hr = mTexture->QueryInterface((IDXGISurface**)getter_AddRefs(surf)); if (FAILED(hr)) { gfxWarning() << "Failed to query interface texture to DXGISurface. Code: " << hexa(hr); return nullptr; } D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(mFormat)); hr = aRT->CreateSharedBitmap(IID_IDXGISurface, surf, &props, getter_AddRefs(mBitmap)); if (FAILED(hr)) { // This seems to happen for SurfaceFormat::A8 sometimes... hr = aRT->CreateBitmap(D2D1::SizeU(desc.Width, desc.Height), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(mBitmap)); if (FAILED(hr)) { gfxWarning() << "Failed in CreateBitmap. Code: " << hexa(hr); return nullptr; } RefPtr rt; if (mDrawTarget) { rt = mDrawTarget->mRT; } if (!rt) { // Okay, we already separated from our drawtarget. And we're an A8 // surface the only way we can get to a bitmap is by creating a // a rendertarget and from there copying to a bitmap! Terrible! RefPtr surface; hr = mTexture->QueryInterface((IDXGISurface**)getter_AddRefs(surface)); if (FAILED(hr)) { gfxWarning() << "Failed to QI texture to surface."; return nullptr; } D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2DPixelFormat(mFormat)); hr = DrawTargetD2D::factory()->CreateDxgiSurfaceRenderTarget(surface, props, getter_AddRefs(rt)); if (FAILED(hr)) { gfxWarning() << "Failed to create D2D render target for texture."; return nullptr; } } mBitmap->CopyFromRenderTarget(nullptr, rt, nullptr); return mBitmap; } return mBitmap; } void SourceSurfaceD2DTarget::MarkIndependent() { if (mDrawTarget) { MOZ_ASSERT(mDrawTarget->mSnapshot == this); mDrawTarget->mSnapshot = nullptr; mDrawTarget = nullptr; } } DataSourceSurfaceD2DTarget::DataSourceSurfaceD2DTarget(SurfaceFormat aFormat) : mFormat(aFormat) , mMapped(false) { } DataSourceSurfaceD2DTarget::~DataSourceSurfaceD2DTarget() { if (mMapped) { mTexture->Unmap(0); } } IntSize DataSourceSurfaceD2DTarget::GetSize() const { D3D10_TEXTURE2D_DESC desc; mTexture->GetDesc(&desc); return IntSize(desc.Width, desc.Height); } SurfaceFormat DataSourceSurfaceD2DTarget::GetFormat() const { return mFormat; } uint8_t* DataSourceSurfaceD2DTarget::GetData() { EnsureMapped(); return (unsigned char*)mMap.pData; } int32_t DataSourceSurfaceD2DTarget::Stride() { EnsureMapped(); return mMap.RowPitch; } bool DataSourceSurfaceD2DTarget::Map(MapType aMapType, MappedSurface *aMappedSurface) { // DataSourceSurfaces used with the new Map API should not be used with GetData!! MOZ_ASSERT(!mMapped); MOZ_ASSERT(!mIsMapped); if (!mTexture) { return false; } D3D10_MAP mapType; if (aMapType == MapType::READ) { mapType = D3D10_MAP_READ; } else if (aMapType == MapType::WRITE) { mapType = D3D10_MAP_WRITE; } else { mapType = D3D10_MAP_READ_WRITE; } D3D10_MAPPED_TEXTURE2D map; HRESULT hr = mTexture->Map(0, mapType, 0, &map); if (FAILED(hr)) { gfxWarning() << "Texture map failed with code: " << hexa(hr); return false; } aMappedSurface->mData = (uint8_t*)map.pData; aMappedSurface->mStride = map.RowPitch; mIsMapped = !!aMappedSurface->mData; return mIsMapped; } void DataSourceSurfaceD2DTarget::Unmap() { MOZ_ASSERT(mIsMapped); mIsMapped = false; mTexture->Unmap(0); } void DataSourceSurfaceD2DTarget::EnsureMapped() { // Do not use GetData() after having used Map! MOZ_ASSERT(!mIsMapped); if (!mMapped) { HRESULT hr = mTexture->Map(0, D3D10_MAP_READ, 0, &mMap); if (FAILED(hr)) { gfxWarning() << "Failed to map texture to memory. Code: " << hexa(hr); return; } mMapped = true; } } } }