diff -r 000000000000 -r 6474c204b198 gfx/layers/d3d9/ImageLayerD3D9.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,576 @@ +/* -*- 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 "mozilla/gfx/2D.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/RefPtr.h" +#include "mozilla/layers/PLayerTransaction.h" +#include "gfxSharedImageSurface.h" + +#include "ImageLayerD3D9.h" +#include "ThebesLayerD3D9.h" +#include "gfxPlatform.h" +#include "gfx2DGlue.h" +#include "yuv_convert.h" +#include "nsIServiceManager.h" +#include "nsIConsoleService.h" +#include "Nv3DVUtils.h" +#include "D3D9SurfaceImage.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +static inline _D3DFORMAT +D3dFormatForSurfaceFormat(SurfaceFormat aFormat) +{ + if (aFormat == SurfaceFormat::A8) { + return D3DFMT_A8; + } + + return D3DFMT_A8R8G8B8; +} + +static already_AddRefed +DataToTexture(IDirect3DDevice9 *aDevice, + unsigned char *aData, + int aStride, + const IntSize &aSize, + _D3DFORMAT aFormat) +{ + nsRefPtr texture; + nsRefPtr deviceEx; + aDevice->QueryInterface(IID_IDirect3DDevice9Ex, + (void**)getter_AddRefs(deviceEx)); + + nsRefPtr surface; + D3DLOCKED_RECT lockedRect; + if (deviceEx) { + // D3D9Ex doesn't support managed textures. We could use dynamic textures + // here but since Images are immutable that probably isn't such a great + // idea. + if (FAILED(aDevice-> + CreateTexture(aSize.width, aSize.height, + 1, 0, aFormat, D3DPOOL_DEFAULT, + getter_AddRefs(texture), nullptr))) + { + return nullptr; + } + + nsRefPtr tmpTexture; + if (FAILED(aDevice-> + CreateTexture(aSize.width, aSize.height, + 1, 0, aFormat, D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpTexture), nullptr))) + { + return nullptr; + } + + tmpTexture->GetSurfaceLevel(0, getter_AddRefs(surface)); + surface->LockRect(&lockedRect, nullptr, 0); + NS_ASSERTION(lockedRect.pBits, "Could not lock surface"); + } else { + if (FAILED(aDevice-> + CreateTexture(aSize.width, aSize.height, + 1, 0, aFormat, D3DPOOL_MANAGED, + getter_AddRefs(texture), nullptr))) + { + return nullptr; + } + + /* lock the entire texture */ + texture->LockRect(0, &lockedRect, nullptr, 0); + } + + uint32_t width = aSize.width; + if (aFormat == D3DFMT_A8R8G8B8) { + width *= 4; + } + for (int y = 0; y < aSize.height; y++) { + memcpy((char*)lockedRect.pBits + lockedRect.Pitch * y, + aData + aStride * y, + width); + } + + if (deviceEx) { + surface->UnlockRect(); + nsRefPtr dstSurface; + texture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + aDevice->UpdateSurface(surface, nullptr, dstSurface, nullptr); + } else { + texture->UnlockRect(0); + } + + return texture.forget(); +} + +static already_AddRefed +OpenSharedTexture(const D3DSURFACE_DESC& aDesc, + HANDLE aShareHandle, + IDirect3DDevice9 *aDevice) +{ + MOZ_ASSERT(aDesc.Format == D3DFMT_X8R8G8B8); + + // Open the frame from DXVA's device in our device using the resource + // sharing handle. + nsRefPtr sharedTexture; + HRESULT hr = aDevice->CreateTexture(aDesc.Width, + aDesc.Height, + 1, + D3DUSAGE_RENDERTARGET, + D3DFMT_X8R8G8B8, + D3DPOOL_DEFAULT, + getter_AddRefs(sharedTexture), + &aShareHandle); + if (FAILED(hr)) { + NS_WARNING("Failed to open shared texture on our device"); + } + return sharedTexture.forget(); +} + +static already_AddRefed +SurfaceToTexture(IDirect3DDevice9 *aDevice, + SourceSurface *aSurface, + const IntSize &aSize) +{ + RefPtr dataSurface = aSurface->GetDataSurface(); + if (!dataSurface) { + return nullptr; + } + DataSourceSurface::MappedSurface map; + if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { + return nullptr; + } + nsRefPtr texture = + DataToTexture(aDevice, map.mData, map.mStride, aSize, + D3dFormatForSurfaceFormat(dataSurface->GetFormat())); + dataSurface->Unmap(); + return texture.forget(); +} + +static void AllocateTexturesYCbCr(PlanarYCbCrImage *aImage, + IDirect3DDevice9 *aDevice, + LayerManagerD3D9 *aManager) +{ + nsAutoPtr backendData( + new PlanarYCbCrD3D9BackendData); + + const PlanarYCbCrData *data = aImage->GetData(); + + D3DLOCKED_RECT lockrectY; + D3DLOCKED_RECT lockrectCb; + D3DLOCKED_RECT lockrectCr; + uint8_t* src; + uint8_t* dest; + + nsRefPtr tmpSurfaceY; + nsRefPtr tmpSurfaceCb; + nsRefPtr tmpSurfaceCr; + + nsRefPtr deviceEx; + aDevice->QueryInterface(IID_IDirect3DDevice9Ex, + getter_AddRefs(deviceEx)); + + bool isD3D9Ex = deviceEx; + + if (isD3D9Ex) { + nsRefPtr tmpYTexture; + nsRefPtr tmpCbTexture; + nsRefPtr tmpCrTexture; + // D3D9Ex does not support the managed pool, could use dynamic textures + // here. But since an Image is immutable static textures are probably a + // better idea. + + HRESULT hr; + hr = aDevice->CreateTexture(data->mYSize.width, data->mYSize.height, + 1, 0, D3DFMT_A8, D3DPOOL_DEFAULT, + getter_AddRefs(backendData->mYTexture), nullptr); + if (!FAILED(hr)) { + hr = aDevice->CreateTexture(data->mCbCrSize.width, data->mCbCrSize.height, + 1, 0, D3DFMT_A8, D3DPOOL_DEFAULT, + getter_AddRefs(backendData->mCbTexture), nullptr); + } + if (!FAILED(hr)) { + hr = aDevice->CreateTexture(data->mCbCrSize.width, data->mCbCrSize.height, + 1, 0, D3DFMT_A8, D3DPOOL_DEFAULT, + getter_AddRefs(backendData->mCrTexture), nullptr); + } + if (!FAILED(hr)) { + hr = aDevice->CreateTexture(data->mYSize.width, data->mYSize.height, + 1, 0, D3DFMT_A8, D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpYTexture), nullptr); + } + if (!FAILED(hr)) { + hr = aDevice->CreateTexture(data->mCbCrSize.width, data->mCbCrSize.height, + 1, 0, D3DFMT_A8, D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpCbTexture), nullptr); + } + if (!FAILED(hr)) { + hr = aDevice->CreateTexture(data->mCbCrSize.width, data->mCbCrSize.height, + 1, 0, D3DFMT_A8, D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpCrTexture), nullptr); + } + + if (FAILED(hr)) { + aManager->ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D9::AllocateTextures(): Failed to create texture (isD3D9Ex)"), + hr); + return; + } + + tmpYTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceY)); + tmpCbTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceCb)); + tmpCrTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceCr)); + tmpSurfaceY->LockRect(&lockrectY, nullptr, 0); + tmpSurfaceCb->LockRect(&lockrectCb, nullptr, 0); + tmpSurfaceCr->LockRect(&lockrectCr, nullptr, 0); + } else { + HRESULT hr; + hr = aDevice->CreateTexture(data->mYSize.width, data->mYSize.height, + 1, 0, D3DFMT_A8, D3DPOOL_MANAGED, + getter_AddRefs(backendData->mYTexture), nullptr); + if (!FAILED(hr)) { + aDevice->CreateTexture(data->mCbCrSize.width, data->mCbCrSize.height, + 1, 0, D3DFMT_A8, D3DPOOL_MANAGED, + getter_AddRefs(backendData->mCbTexture), nullptr); + } + if (!FAILED(hr)) { + aDevice->CreateTexture(data->mCbCrSize.width, data->mCbCrSize.height, + 1, 0, D3DFMT_A8, D3DPOOL_MANAGED, + getter_AddRefs(backendData->mCrTexture), nullptr); + } + + if (FAILED(hr)) { + aManager->ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D9::AllocateTextures(): Failed to create texture (!isD3D9Ex)"), + hr); + return; + } + + /* lock the entire texture */ + backendData->mYTexture->LockRect(0, &lockrectY, nullptr, 0); + backendData->mCbTexture->LockRect(0, &lockrectCb, nullptr, 0); + backendData->mCrTexture->LockRect(0, &lockrectCr, nullptr, 0); + } + + src = data->mYChannel; + //FIX cast + dest = (uint8_t*)lockrectY.pBits; + + // copy over data + for (int h=0; hmYSize.height; h++) { + memcpy(dest, src, data->mYSize.width); + dest += lockrectY.Pitch; + src += data->mYStride; + } + + src = data->mCbChannel; + //FIX cast + dest = (uint8_t*)lockrectCb.pBits; + + // copy over data + for (int h=0; hmCbCrSize.height; h++) { + memcpy(dest, src, data->mCbCrSize.width); + dest += lockrectCb.Pitch; + src += data->mCbCrStride; + } + + src = data->mCrChannel; + //FIX cast + dest = (uint8_t*)lockrectCr.pBits; + + // copy over data + for (int h=0; hmCbCrSize.height; h++) { + memcpy(dest, src, data->mCbCrSize.width); + dest += lockrectCr.Pitch; + src += data->mCbCrStride; + } + + if (isD3D9Ex) { + tmpSurfaceY->UnlockRect(); + tmpSurfaceCb->UnlockRect(); + tmpSurfaceCr->UnlockRect(); + nsRefPtr dstSurface; + backendData->mYTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + aDevice->UpdateSurface(tmpSurfaceY, nullptr, dstSurface, nullptr); + backendData->mCbTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + aDevice->UpdateSurface(tmpSurfaceCb, nullptr, dstSurface, nullptr); + backendData->mCrTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + aDevice->UpdateSurface(tmpSurfaceCr, nullptr, dstSurface, nullptr); + } else { + backendData->mYTexture->UnlockRect(0); + backendData->mCbTexture->UnlockRect(0); + backendData->mCrTexture->UnlockRect(0); + } + + aImage->SetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9, backendData.forget()); +} + +Layer* +ImageLayerD3D9::GetLayer() +{ + return this; +} + +/* + * Returns a texture which backs aImage + * Will only work if aImage is a cairo or remote image. + * Returns nullptr if unsuccessful. + * If successful, aHasAlpha will be set to true if the texture has an + * alpha component, false otherwise. + */ +IDirect3DTexture9* +ImageLayerD3D9::GetTexture(Image *aImage, bool& aHasAlpha) +{ + NS_ASSERTION(aImage, "Null image."); + + if (aImage->GetFormat() == ImageFormat::REMOTE_IMAGE_BITMAP) { + RemoteBitmapImage *remoteImage = + static_cast(aImage); + + if (!aImage->GetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9)) { + nsAutoPtr dat(new TextureD3D9BackendData()); + dat->mTexture = DataToTexture(device(), remoteImage->mData, remoteImage->mStride, remoteImage->mSize, D3DFMT_A8R8G8B8); + if (dat->mTexture) { + aImage->SetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9, dat.forget()); + } + } + + aHasAlpha = remoteImage->mFormat == RemoteImageData::BGRA32; + } else if (aImage->GetFormat() == ImageFormat::CAIRO_SURFACE) { + CairoImage *cairoImage = + static_cast(aImage); + + RefPtr surf = cairoImage->GetAsSourceSurface(); + if (!surf) { + return nullptr; + } + + if (!aImage->GetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9)) { + nsAutoPtr dat(new TextureD3D9BackendData()); + dat->mTexture = SurfaceToTexture(device(), surf, cairoImage->GetSize()); + if (dat->mTexture) { + aImage->SetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9, dat.forget()); + } + } + + aHasAlpha = surf->GetFormat() == SurfaceFormat::B8G8R8A8; + } else if (aImage->GetFormat() == ImageFormat::D3D9_RGB32_TEXTURE) { + if (!aImage->GetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9)) { + // The texture in which the frame is stored belongs to DXVA's D3D9 device. + // We need to open it on our device before we can use it. + nsAutoPtr backendData(new TextureD3D9BackendData()); + D3D9SurfaceImage* image = static_cast(aImage); + backendData->mTexture = OpenSharedTexture(image->GetDesc(), image->GetShareHandle(), device()); + if (backendData->mTexture) { + aImage->SetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9, backendData.forget()); + } + } + aHasAlpha = false; + } else { + NS_WARNING("Inappropriate image type."); + return nullptr; + } + + TextureD3D9BackendData *data = + static_cast(aImage->GetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9)); + + if (!data) { + return nullptr; + } + + nsRefPtr dev; + data->mTexture->GetDevice(getter_AddRefs(dev)); + if (dev != device()) { + return nullptr; + } + + return data->mTexture; +} + +void +ImageLayerD3D9::RenderLayer() +{ + ImageContainer *container = GetContainer(); + if (!container || mD3DManager->CompositingDisabled()) { + return; + } + + AutoLockImage autoLock(container); + + Image *image = autoLock.GetImage(); + if (!image) { + return; + } + + SetShaderTransformAndOpacity(); + + gfx::IntSize size = image->GetSize(); + + if (image->GetFormat() == ImageFormat::CAIRO_SURFACE || + image->GetFormat() == ImageFormat::REMOTE_IMAGE_BITMAP || + image->GetFormat() == ImageFormat::D3D9_RGB32_TEXTURE) + { + NS_ASSERTION(image->GetFormat() != ImageFormat::CAIRO_SURFACE || + !static_cast(image)->mSourceSurface || + static_cast(image)->mSourceSurface->GetFormat() != SurfaceFormat::A8, + "Image layer has alpha image"); + + bool hasAlpha = false; + nsRefPtr texture = GetTexture(image, hasAlpha); + + device()->SetVertexShaderConstantF(CBvLayerQuad, + ShaderConstantRect(0, + 0, + size.width, + size.height), + 1); + + if (hasAlpha) { + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER, GetMaskLayer()); + } else { + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER, GetMaskLayer()); + } + + if (mFilter == GraphicsFilter::FILTER_NEAREST) { + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + } + device()->SetTexture(0, texture); + + image = nullptr; + autoLock.Unlock(); + + device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + if (mFilter == GraphicsFilter::FILTER_NEAREST) { + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + } + } else { + PlanarYCbCrImage *yuvImage = + static_cast(image); + + if (!yuvImage->IsValid()) { + return; + } + + if (!yuvImage->GetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9)) { + AllocateTexturesYCbCr(yuvImage, device(), mD3DManager); + } + + PlanarYCbCrD3D9BackendData *data = + static_cast(yuvImage->GetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9)); + + if (!data) { + return; + } + + nsRefPtr dev; + data->mYTexture->GetDevice(getter_AddRefs(dev)); + if (dev != device()) { + return; + } + + device()->SetVertexShaderConstantF(CBvLayerQuad, + ShaderConstantRect(0, + 0, + size.width, + size.height), + 1); + + device()->SetVertexShaderConstantF(CBvTextureCoords, + ShaderConstantRect( + (float)yuvImage->GetData()->mPicX / yuvImage->GetData()->mYSize.width, + (float)yuvImage->GetData()->mPicY / yuvImage->GetData()->mYSize.height, + (float)yuvImage->GetData()->mPicSize.width / yuvImage->GetData()->mYSize.width, + (float)yuvImage->GetData()->mPicSize.height / yuvImage->GetData()->mYSize.height + ), + 1); + + mD3DManager->SetShaderMode(DeviceManagerD3D9::YCBCRLAYER, GetMaskLayer()); + + /* + * Send 3d control data and metadata + */ + if (mD3DManager->GetNv3DVUtils()) { + Nv_Stereo_Mode mode; + switch (yuvImage->GetData()->mStereoMode) { + case StereoMode::LEFT_RIGHT: + mode = NV_STEREO_MODE_LEFT_RIGHT; + break; + case StereoMode::RIGHT_LEFT: + mode = NV_STEREO_MODE_RIGHT_LEFT; + break; + case StereoMode::BOTTOM_TOP: + mode = NV_STEREO_MODE_BOTTOM_TOP; + break; + case StereoMode::TOP_BOTTOM: + mode = NV_STEREO_MODE_TOP_BOTTOM; + break; + case StereoMode::MONO: + mode = NV_STEREO_MODE_MONO; + break; + } + + // Send control data even in mono case so driver knows to leave stereo mode. + mD3DManager->GetNv3DVUtils()->SendNv3DVControl(mode, true, FIREFOX_3DV_APP_HANDLE); + + if (yuvImage->GetData()->mStereoMode != StereoMode::MONO) { + mD3DManager->GetNv3DVUtils()->SendNv3DVControl(mode, true, FIREFOX_3DV_APP_HANDLE); + + nsRefPtr renderTarget; + device()->GetRenderTarget(0, getter_AddRefs(renderTarget)); + mD3DManager->GetNv3DVUtils()->SendNv3DVMetaData((unsigned int)yuvImage->GetSize().width, + (unsigned int)yuvImage->GetSize().height, (HANDLE)(data->mYTexture), (HANDLE)(renderTarget)); + } + } + + // Linear scaling is default here, adhering to mFilter is difficult since + // presumably even with point filtering we'll still want chroma upsampling + // to be linear. In the current approach we can't. + device()->SetTexture(0, data->mYTexture); + device()->SetTexture(1, data->mCbTexture); + device()->SetTexture(2, data->mCrTexture); + + image = nullptr; + data = nullptr; + autoLock.Unlock(); + + device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + + device()->SetVertexShaderConstantF(CBvTextureCoords, + ShaderConstantRect(0, 0, 1.0f, 1.0f), 1); + } + + GetContainer()->NotifyPaintedImage(image); +} + +already_AddRefed +ImageLayerD3D9::GetAsTexture(gfx::IntSize* aSize) +{ + if (!GetContainer()) { + return nullptr; + } + + AutoLockImage autoLock(GetContainer()); + + Image *image = autoLock.GetImage(); + + if (!image) { + return nullptr; + } + + if (image->GetFormat() != ImageFormat::CAIRO_SURFACE && + image->GetFormat() != ImageFormat::REMOTE_IMAGE_BITMAP) { + return nullptr; + } + + bool dontCare; + *aSize = image->GetSize(); + nsRefPtr result = GetTexture(image, dontCare); + return result.forget(); +} + +} /* layers */ +} /* mozilla */