summaryrefslogtreecommitdiffstats
path: root/gfx/layers/D3D11YCbCrImage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/D3D11YCbCrImage.cpp')
-rw-r--r--gfx/layers/D3D11YCbCrImage.cpp432
1 files changed, 432 insertions, 0 deletions
diff --git a/gfx/layers/D3D11YCbCrImage.cpp b/gfx/layers/D3D11YCbCrImage.cpp
new file mode 100644
index 0000000000..02b2f4e23f
--- /dev/null
+++ b/gfx/layers/D3D11YCbCrImage.cpp
@@ -0,0 +1,432 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "D3D11YCbCrImage.h"
+
+#include "YCbCrUtils.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/TextureD3D11.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+D3D11YCbCrImage::D3D11YCbCrImage()
+ : Image(NULL, ImageFormat::D3D11_YCBCR_IMAGE) {}
+
+D3D11YCbCrImage::~D3D11YCbCrImage() {}
+
+bool D3D11YCbCrImage::SetData(KnowsCompositor* aAllocator,
+ ImageContainer* aContainer,
+ const PlanarYCbCrData& aData) {
+ mPictureRect = aData.mPictureRect;
+ mColorDepth = aData.mColorDepth;
+ mColorSpace = aData.mYUVColorSpace;
+ mColorRange = aData.mColorRange;
+ mChromaSubsampling = aData.mChromaSubsampling;
+
+ RefPtr<D3D11YCbCrRecycleAllocator> allocator =
+ aContainer->GetD3D11YCbCrRecycleAllocator(aAllocator);
+ if (!allocator) {
+ return false;
+ }
+
+ RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice();
+ if (!device) {
+ return false;
+ }
+
+ {
+ DXGIYCbCrTextureAllocationHelper helper(aData, TextureFlags::DEFAULT,
+ device);
+ mTextureClient = allocator->CreateOrRecycle(helper);
+ }
+
+ if (!mTextureClient) {
+ return false;
+ }
+
+ DXGIYCbCrTextureData* data =
+ mTextureClient->GetInternalData()->AsDXGIYCbCrTextureData();
+
+ ID3D11Texture2D* textureY = data->GetD3D11Texture(0);
+ ID3D11Texture2D* textureCb = data->GetD3D11Texture(1);
+ ID3D11Texture2D* textureCr = data->GetD3D11Texture(2);
+
+ RefPtr<ID3D10Multithread> mt;
+ HRESULT hr = device->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt));
+
+ if (FAILED(hr) || !mt) {
+ gfxCriticalError() << "Multithread safety interface not supported. " << hr;
+ return false;
+ }
+
+ if (!mt->GetMultithreadProtected()) {
+ gfxCriticalError() << "Device used not marked as multithread-safe.";
+ return false;
+ }
+
+ D3D11MTAutoEnter mtAutoEnter(mt.forget());
+
+ RefPtr<ID3D11DeviceContext> ctx;
+ device->GetImmediateContext(getter_AddRefs(ctx));
+ if (!ctx) {
+ gfxCriticalError() << "Failed to get immediate context.";
+ return false;
+ }
+
+ AutoLockD3D11Texture lockY(textureY);
+ AutoLockD3D11Texture lockCb(textureCb);
+ AutoLockD3D11Texture lockCr(textureCr);
+
+ ctx->UpdateSubresource(textureY, 0, nullptr, aData.mYChannel, aData.mYStride,
+ aData.mYStride * aData.YDataSize().height);
+ ctx->UpdateSubresource(textureCb, 0, nullptr, aData.mCbChannel,
+ aData.mCbCrStride,
+ aData.mCbCrStride * aData.CbCrDataSize().height);
+ ctx->UpdateSubresource(textureCr, 0, nullptr, aData.mCrChannel,
+ aData.mCbCrStride,
+ aData.mCbCrStride * aData.CbCrDataSize().height);
+
+ return true;
+}
+
+IntSize D3D11YCbCrImage::GetSize() const { return mPictureRect.Size(); }
+
+TextureClient* D3D11YCbCrImage::GetTextureClient(
+ KnowsCompositor* aKnowsCompositor) {
+ return mTextureClient;
+}
+
+const DXGIYCbCrTextureData* D3D11YCbCrImage::GetData() const {
+ if (!mTextureClient) return nullptr;
+
+ return mTextureClient->GetInternalData()->AsDXGIYCbCrTextureData();
+}
+
+already_AddRefed<SourceSurface> D3D11YCbCrImage::GetAsSourceSurface() {
+ if (!mTextureClient) {
+ gfxWarning()
+ << "GetAsSourceSurface() called on uninitialized D3D11YCbCrImage.";
+ return nullptr;
+ }
+
+ gfx::IntSize size(mPictureRect.Size());
+ gfx::SurfaceFormat format =
+ gfx::ImageFormatToSurfaceFormat(gfxVars::OffscreenFormat());
+ HRESULT hr;
+
+ PlanarYCbCrData data;
+
+ DXGIYCbCrTextureData* dxgiData =
+ mTextureClient->GetInternalData()->AsDXGIYCbCrTextureData();
+
+ if (!dxgiData) {
+ gfxCriticalError() << "Failed to get texture client internal data.";
+ return nullptr;
+ }
+
+ RefPtr<ID3D11Texture2D> texY = dxgiData->GetD3D11Texture(0);
+ RefPtr<ID3D11Texture2D> texCb = dxgiData->GetD3D11Texture(1);
+ RefPtr<ID3D11Texture2D> texCr = dxgiData->GetD3D11Texture(2);
+ RefPtr<ID3D11Texture2D> softTexY, softTexCb, softTexCr;
+ D3D11_TEXTURE2D_DESC desc;
+
+ RefPtr<ID3D11Device> dev;
+ texY->GetDevice(getter_AddRefs(dev));
+
+ if (!dev || dev != gfx::DeviceManagerDx::Get()->GetImageDevice()) {
+ gfxCriticalError() << "D3D11Device is obsoleted";
+ return nullptr;
+ }
+
+ RefPtr<ID3D10Multithread> mt;
+ hr = dev->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt));
+
+ if (FAILED(hr) || !mt) {
+ gfxCriticalError() << "Multithread safety interface not supported.";
+ return nullptr;
+ }
+
+ if (!mt->GetMultithreadProtected()) {
+ gfxCriticalError() << "Device used not marked as multithread-safe.";
+ return nullptr;
+ }
+
+ D3D11MTAutoEnter mtAutoEnter(mt.forget());
+
+ texY->GetDesc(&desc);
+ desc.BindFlags = 0;
+ desc.MiscFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.Usage = D3D11_USAGE_STAGING;
+
+ dev->CreateTexture2D(&desc, nullptr, getter_AddRefs(softTexY));
+ if (!softTexY) {
+ gfxCriticalNote << "Failed to allocate softTexY";
+ return nullptr;
+ }
+
+ texCb->GetDesc(&desc);
+ desc.BindFlags = 0;
+ desc.MiscFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.Usage = D3D11_USAGE_STAGING;
+
+ dev->CreateTexture2D(&desc, nullptr, getter_AddRefs(softTexCb));
+ if (!softTexCb) {
+ gfxCriticalNote << "Failed to allocate softTexCb";
+ return nullptr;
+ }
+
+ texCr->GetDesc(&desc);
+ desc.BindFlags = 0;
+ desc.MiscFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.Usage = D3D11_USAGE_STAGING;
+
+ dev->CreateTexture2D(&desc, nullptr, getter_AddRefs(softTexCr));
+ if (!softTexCr) {
+ gfxCriticalNote << "Failed to allocate softTexCr";
+ return nullptr;
+ }
+
+ RefPtr<ID3D11DeviceContext> ctx;
+ dev->GetImmediateContext(getter_AddRefs(ctx));
+ if (!ctx) {
+ gfxCriticalError() << "Failed to get immediate context.";
+ return nullptr;
+ }
+
+ {
+ AutoLockD3D11Texture lockY(texY);
+ AutoLockD3D11Texture lockCb(texCb);
+ AutoLockD3D11Texture lockCr(texCr);
+ ctx->CopyResource(softTexY, texY);
+ ctx->CopyResource(softTexCb, texCb);
+ ctx->CopyResource(softTexCr, texCr);
+ }
+
+ D3D11_MAPPED_SUBRESOURCE mapY, mapCb, mapCr;
+ RefPtr<gfx::DataSourceSurface> surface;
+ mapY.pData = mapCb.pData = mapCr.pData = nullptr;
+
+ hr = ctx->Map(softTexY, 0, D3D11_MAP_READ, 0, &mapY);
+ if (FAILED(hr)) {
+ gfxCriticalError() << "Failed to map Y plane (" << hr << ")";
+ return nullptr;
+ }
+ hr = ctx->Map(softTexCb, 0, D3D11_MAP_READ, 0, &mapCb);
+ if (FAILED(hr)) {
+ gfxCriticalError() << "Failed to map Y plane (" << hr << ")";
+ return nullptr;
+ }
+ hr = ctx->Map(softTexCr, 0, D3D11_MAP_READ, 0, &mapCr);
+ if (FAILED(hr)) {
+ gfxCriticalError() << "Failed to map Y plane (" << hr << ")";
+ return nullptr;
+ }
+
+ MOZ_ASSERT(mapCb.RowPitch == mapCr.RowPitch);
+
+ data.mPictureRect = mPictureRect;
+ data.mStereoMode = StereoMode::MONO;
+ data.mColorDepth = mColorDepth;
+ data.mYUVColorSpace = mColorSpace;
+ data.mColorRange = mColorRange;
+ data.mChromaSubsampling = mChromaSubsampling;
+ data.mYSkip = data.mCbSkip = data.mCrSkip = 0;
+ data.mYChannel = static_cast<uint8_t*>(mapY.pData);
+ data.mYStride = mapY.RowPitch;
+ data.mCbChannel = static_cast<uint8_t*>(mapCb.pData);
+ data.mCrChannel = static_cast<uint8_t*>(mapCr.pData);
+ data.mCbCrStride = mapCb.RowPitch;
+
+ gfx::GetYCbCrToRGBDestFormatAndSize(data, format, size);
+ if (size.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ size.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ gfxCriticalError() << "Illegal image dest width or height";
+ return nullptr;
+ }
+
+ surface = gfx::Factory::CreateDataSourceSurface(size, format);
+ if (!surface) {
+ gfxCriticalError() << "Failed to create DataSourceSurface for image: "
+ << size << " " << format;
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
+ if (!mapping.IsMapped()) {
+ gfxCriticalError() << "Failed to map DataSourceSurface for D3D11YCbCrImage";
+ return nullptr;
+ }
+
+ gfx::ConvertYCbCrToRGB(data, format, size, mapping.GetData(),
+ mapping.GetStride());
+
+ ctx->Unmap(softTexY, 0);
+ ctx->Unmap(softTexCb, 0);
+ ctx->Unmap(softTexCr, 0);
+
+ return surface.forget();
+}
+
+class AutoCheckLockD3D11Texture final {
+ public:
+ explicit AutoCheckLockD3D11Texture(ID3D11Texture2D* aTexture)
+ : mIsLocked(false) {
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex));
+ if (!mMutex) {
+ // If D3D11Texture does not have keyed mutex, we think that the
+ // D3D11Texture could be locked.
+ mIsLocked = true;
+ return;
+ }
+
+ // Test to see if the keyed mutex has been released
+ HRESULT hr = mMutex->AcquireSync(0, 0);
+ if (hr == S_OK || hr == WAIT_ABANDONED) {
+ mIsLocked = true;
+ // According to Microsoft documentation:
+ // WAIT_ABANDONED - The shared surface and keyed mutex are no longer in a
+ // consistent state. If AcquireSync returns this value, you should release
+ // and recreate both the keyed mutex and the shared surface
+ // So even if we do get WAIT_ABANDONED, the keyed mutex will have to be
+ // released.
+ mSyncAcquired = true;
+ }
+ }
+
+ ~AutoCheckLockD3D11Texture() {
+ if (!mSyncAcquired) {
+ return;
+ }
+ HRESULT hr = mMutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+ }
+
+ bool IsLocked() const { return mIsLocked; }
+
+ private:
+ bool mIsLocked;
+ bool mSyncAcquired = false;
+ RefPtr<IDXGIKeyedMutex> mMutex;
+};
+
+DXGIYCbCrTextureAllocationHelper::DXGIYCbCrTextureAllocationHelper(
+ const PlanarYCbCrData& aData, TextureFlags aTextureFlags,
+ ID3D11Device* aDevice)
+ : ITextureClientAllocationHelper(
+ gfx::SurfaceFormat::YUV, aData.mPictureRect.Size(),
+ BackendSelector::Content, aTextureFlags, ALLOC_DEFAULT),
+ mData(aData),
+ mDevice(aDevice) {}
+
+bool DXGIYCbCrTextureAllocationHelper::IsCompatible(
+ TextureClient* aTextureClient) {
+ MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV);
+
+ DXGIYCbCrTextureData* dxgiData =
+ aTextureClient->GetInternalData()->AsDXGIYCbCrTextureData();
+ if (!dxgiData || aTextureClient->GetSize() != mData.mPictureRect.Size() ||
+ dxgiData->GetYSize() != mData.YDataSize() ||
+ dxgiData->GetCbCrSize() != mData.CbCrDataSize() ||
+ dxgiData->GetColorDepth() != mData.mColorDepth ||
+ dxgiData->GetYUVColorSpace() != mData.mYUVColorSpace) {
+ return false;
+ }
+
+ ID3D11Texture2D* textureY = dxgiData->GetD3D11Texture(0);
+ ID3D11Texture2D* textureCb = dxgiData->GetD3D11Texture(1);
+ ID3D11Texture2D* textureCr = dxgiData->GetD3D11Texture(2);
+
+ RefPtr<ID3D11Device> device;
+ textureY->GetDevice(getter_AddRefs(device));
+ if (!device || device != gfx::DeviceManagerDx::Get()->GetImageDevice()) {
+ return false;
+ }
+
+ // Test to see if the keyed mutex has been released.
+ // If D3D11Texture failed to lock, do not recycle the DXGIYCbCrTextureData.
+
+ AutoCheckLockD3D11Texture lockY(textureY);
+ AutoCheckLockD3D11Texture lockCr(textureCr);
+ AutoCheckLockD3D11Texture lockCb(textureCb);
+
+ if (!lockY.IsLocked() || !lockCr.IsLocked() || !lockCb.IsLocked()) {
+ return false;
+ }
+
+ return true;
+}
+
+already_AddRefed<TextureClient> DXGIYCbCrTextureAllocationHelper::Allocate(
+ KnowsCompositor* aAllocator) {
+ auto ySize = mData.YDataSize();
+ auto cbcrSize = mData.CbCrDataSize();
+ CD3D11_TEXTURE2D_DESC newDesc(mData.mColorDepth == gfx::ColorDepth::COLOR_8
+ ? DXGI_FORMAT_R8_UNORM
+ : DXGI_FORMAT_R16_UNORM,
+ ySize.width, ySize.height, 1, 1);
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+
+ RefPtr<ID3D10Multithread> mt;
+ HRESULT hr = mDevice->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt));
+
+ if (FAILED(hr) || !mt) {
+ gfxCriticalError() << "Multithread safety interface not supported. " << hr;
+ return nullptr;
+ }
+
+ if (!mt->GetMultithreadProtected()) {
+ gfxCriticalError() << "Device used not marked as multithread-safe.";
+ return nullptr;
+ }
+
+ D3D11MTAutoEnter mtAutoEnter(mt.forget());
+
+ RefPtr<ID3D11Texture2D> textureY;
+ hr = mDevice->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(textureY));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ newDesc.Width = cbcrSize.width;
+ newDesc.Height = cbcrSize.height;
+
+ RefPtr<ID3D11Texture2D> textureCb;
+ hr = mDevice->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(textureCb));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ RefPtr<ID3D11Texture2D> textureCr;
+ hr = mDevice->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(textureCr));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ TextureForwarder* forwarder =
+ aAllocator ? aAllocator->GetTextureForwarder() : nullptr;
+
+ return TextureClient::CreateWithData(
+ DXGIYCbCrTextureData::Create(
+ textureY, textureCb, textureCr, mData.mPictureRect.Size(), ySize,
+ cbcrSize, mData.mColorDepth, mData.mYUVColorSpace, mData.mColorRange),
+ mTextureFlags, forwarder);
+}
+
+already_AddRefed<TextureClient> D3D11YCbCrRecycleAllocator::Allocate(
+ SurfaceFormat aFormat, IntSize aSize, BackendSelector aSelector,
+ TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla