summaryrefslogtreecommitdiffstats
path: root/gfx/layers/IMFYCbCrImage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/IMFYCbCrImage.cpp')
-rw-r--r--gfx/layers/IMFYCbCrImage.cpp306
1 files changed, 306 insertions, 0 deletions
diff --git a/gfx/layers/IMFYCbCrImage.cpp b/gfx/layers/IMFYCbCrImage.cpp
new file mode 100644
index 000000000..d5943b030
--- /dev/null
+++ b/gfx/layers/IMFYCbCrImage.cpp
@@ -0,0 +1,306 @@
+/* -*- 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 "IMFYCbCrImage.h"
+#include "DeviceManagerD3D9.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/TextureClient.h"
+#include "d3d9.h"
+
+namespace mozilla {
+namespace layers {
+
+IMFYCbCrImage::IMFYCbCrImage(IMFMediaBuffer* aBuffer, IMF2DBuffer* a2DBuffer)
+ : RecyclingPlanarYCbCrImage(nullptr)
+ , mBuffer(aBuffer)
+ , m2DBuffer(a2DBuffer)
+{}
+
+IMFYCbCrImage::~IMFYCbCrImage()
+{
+ if (m2DBuffer) {
+ m2DBuffer->Unlock2D();
+ }
+ else {
+ mBuffer->Unlock();
+ }
+}
+
+struct AutoLockTexture
+{
+ AutoLockTexture(ID3D11Texture2D* aTexture)
+ {
+ aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex));
+ if (!mMutex) {
+ return;
+ }
+ HRESULT hr = mMutex->AcquireSync(0, 10000);
+ if (hr == WAIT_TIMEOUT) {
+ MOZ_CRASH("GFX: IMFYCbCrImage timeout");
+ }
+
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to lock the texture");
+ }
+ }
+
+ ~AutoLockTexture()
+ {
+ if (!mMutex) {
+ return;
+ }
+ HRESULT hr = mMutex->ReleaseSync(0);
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to unlock the texture");
+ }
+ }
+
+ RefPtr<IDXGIKeyedMutex> mMutex;
+};
+
+static already_AddRefed<IDirect3DTexture9>
+InitTextures(IDirect3DDevice9* aDevice,
+ const IntSize &aSize,
+ _D3DFORMAT aFormat,
+ RefPtr<IDirect3DSurface9>& aSurface,
+ HANDLE& aHandle,
+ D3DLOCKED_RECT& aLockedRect)
+{
+ if (!aDevice) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> result;
+ if (FAILED(aDevice->CreateTexture(aSize.width, aSize.height,
+ 1, 0, aFormat, D3DPOOL_DEFAULT,
+ getter_AddRefs(result), &aHandle))) {
+ return nullptr;
+ }
+ if (!result) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> tmpTexture;
+ if (FAILED(aDevice->CreateTexture(aSize.width, aSize.height,
+ 1, 0, aFormat, D3DPOOL_SYSTEMMEM,
+ getter_AddRefs(tmpTexture), nullptr))) {
+ return nullptr;
+ }
+ if (!tmpTexture) {
+ return nullptr;
+ }
+
+ tmpTexture->GetSurfaceLevel(0, getter_AddRefs(aSurface));
+ if (FAILED(aSurface->LockRect(&aLockedRect, nullptr, 0)) ||
+ !aLockedRect.pBits) {
+ NS_WARNING("Could not lock surface");
+ return nullptr;
+ }
+
+ return result.forget();
+}
+
+static bool
+FinishTextures(IDirect3DDevice9* aDevice,
+ IDirect3DTexture9* aTexture,
+ IDirect3DSurface9* aSurface)
+{
+ if (!aDevice) {
+ return false;
+ }
+
+ HRESULT hr = aSurface->UnlockRect();
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ RefPtr<IDirect3DSurface9> dstSurface;
+ hr = aTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ hr = aDevice->UpdateSurface(aSurface, nullptr, dstSurface, nullptr);
+ if (FAILED(hr)) {
+ return false;
+ }
+ return true;
+}
+
+static bool UploadData(IDirect3DDevice9* aDevice,
+ RefPtr<IDirect3DTexture9>& aTexture,
+ HANDLE& aHandle,
+ uint8_t* aSrc,
+ const gfx::IntSize& aSrcSize,
+ int32_t aSrcStride)
+{
+ RefPtr<IDirect3DSurface9> surf;
+ D3DLOCKED_RECT rect;
+ aTexture = InitTextures(aDevice, aSrcSize, D3DFMT_A8, surf, aHandle, rect);
+ if (!aTexture) {
+ return false;
+ }
+
+ if (aSrcStride == rect.Pitch) {
+ memcpy(rect.pBits, aSrc, rect.Pitch * aSrcSize.height);
+ } else {
+ for (int i = 0; i < aSrcSize.height; i++) {
+ memcpy((uint8_t*)rect.pBits + i * rect.Pitch,
+ aSrc + i * aSrcStride,
+ aSrcSize.width);
+ }
+ }
+
+ return FinishTextures(aDevice, aTexture, surf);
+}
+
+TextureClient*
+IMFYCbCrImage::GetD3D9TextureClient(KnowsCompositor* aForwarder)
+{
+ RefPtr<IDirect3DDevice9> device = DeviceManagerD3D9::GetDevice();
+ if (!device) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> textureY;
+ HANDLE shareHandleY = 0;
+ if (!UploadData(device, textureY, shareHandleY,
+ mData.mYChannel, mData.mYSize, mData.mYStride)) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> textureCb;
+ HANDLE shareHandleCb = 0;
+ if (!UploadData(device, textureCb, shareHandleCb,
+ mData.mCbChannel, mData.mCbCrSize, mData.mCbCrStride)) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DTexture9> textureCr;
+ HANDLE shareHandleCr = 0;
+ if (!UploadData(device, textureCr, shareHandleCr,
+ mData.mCrChannel, mData.mCbCrSize, mData.mCbCrStride)) {
+ return nullptr;
+ }
+
+ RefPtr<IDirect3DQuery9> query;
+ HRESULT hr = device->CreateQuery(D3DQUERYTYPE_EVENT, getter_AddRefs(query));
+ hr = query->Issue(D3DISSUE_END);
+
+ int iterations = 0;
+ bool valid = false;
+ while (iterations < 10) {
+ HRESULT hr = query->GetData(nullptr, 0, D3DGETDATA_FLUSH);
+ if (hr == S_FALSE) {
+ Sleep(1);
+ iterations++;
+ continue;
+ }
+ if (hr == S_OK) {
+ valid = true;
+ }
+ break;
+ }
+
+ if (!valid) {
+ return nullptr;
+ }
+
+ mTextureClient = TextureClient::CreateWithData(
+ DXGIYCbCrTextureData::Create(TextureFlags::DEFAULT,
+ textureY, textureCb, textureCr,
+ shareHandleY, shareHandleCb, shareHandleCr,
+ GetSize(), mData.mYSize, mData.mCbCrSize),
+ TextureFlags::DEFAULT,
+ aForwarder->GetTextureForwarder()
+ );
+
+ return mTextureClient;
+}
+
+TextureClient*
+IMFYCbCrImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ if (mTextureClient) {
+ return mTextureClient;
+ }
+
+ RefPtr<ID3D11Device> device =
+ gfx::DeviceManagerDx::Get()->GetContentDevice();
+ if (!device) {
+ device =
+ gfx::DeviceManagerDx::Get()->GetCompositorDevice();
+ }
+
+ LayersBackend backend = aForwarder->GetCompositorBackendType();
+ if (!device || backend != LayersBackend::LAYERS_D3D11) {
+ if (backend == LayersBackend::LAYERS_D3D9 ||
+ backend == LayersBackend::LAYERS_D3D11) {
+ return GetD3D9TextureClient(aForwarder);
+ }
+ return nullptr;
+ }
+
+ if (!gfx::DeviceManagerDx::Get()->CanInitializeKeyedMutexTextures()) {
+ return nullptr;
+ }
+
+ if (mData.mYStride < 0 || mData.mCbCrStride < 0) {
+ // D3D11 only supports unsigned stride values.
+ return nullptr;
+ }
+
+ CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_R8_UNORM,
+ mData.mYSize.width, mData.mYSize.height, 1, 1);
+
+ if (device == gfx::DeviceManagerDx::Get()->GetCompositorDevice()) {
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+ } else {
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+ }
+
+ RefPtr<ID3D11Texture2D> textureY;
+ D3D11_SUBRESOURCE_DATA yData = { mData.mYChannel, (UINT)mData.mYStride, 0 };
+ HRESULT hr = device->CreateTexture2D(&newDesc, &yData, getter_AddRefs(textureY));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ newDesc.Width = mData.mCbCrSize.width;
+ newDesc.Height = mData.mCbCrSize.height;
+
+ RefPtr<ID3D11Texture2D> textureCb;
+ D3D11_SUBRESOURCE_DATA cbData = { mData.mCbChannel, (UINT)mData.mCbCrStride, 0 };
+ hr = device->CreateTexture2D(&newDesc, &cbData, getter_AddRefs(textureCb));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ RefPtr<ID3D11Texture2D> textureCr;
+ D3D11_SUBRESOURCE_DATA crData = { mData.mCrChannel, (UINT)mData.mCbCrStride, 0 };
+ hr = device->CreateTexture2D(&newDesc, &crData, getter_AddRefs(textureCr));
+ NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
+
+ // Even though the textures we created are meant to be protected by a keyed mutex,
+ // it appears that D3D doesn't include the initial memory upload within this
+ // synchronization. Add an empty lock/unlock pair since that appears to
+ // be sufficient to make sure we synchronize.
+ {
+ AutoLockTexture lockCr(textureCr);
+ }
+
+ mTextureClient = TextureClient::CreateWithData(
+ DXGIYCbCrTextureData::Create(TextureFlags::DEFAULT,
+ textureY, textureCb, textureCr,
+ GetSize(), mData.mYSize, mData.mCbCrSize),
+ TextureFlags::DEFAULT,
+ aForwarder->GetTextureForwarder()
+ );
+
+ return mTextureClient;
+}
+
+} // namespace layers
+} // namespace mozilla