405 lines
12 KiB
C++
405 lines
12 KiB
C++
/* -*- 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 "TextureHostWrapperD3D11.h"
|
|
|
|
#include <d3d11.h>
|
|
|
|
#include "mozilla/gfx/DeviceManagerDx.h"
|
|
#include "mozilla/layers/AsyncImagePipelineManager.h"
|
|
#include "mozilla/layers/CompositorThread.h"
|
|
#include "mozilla/layers/GpuProcessD3D11TextureMap.h"
|
|
#include "mozilla/layers/TextureD3D11.h"
|
|
#include "mozilla/layers/WebRenderTextureHost.h"
|
|
#include "mozilla/ProfilerMarkers.h"
|
|
#include "mozilla/SharedThreadPool.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
TextureWrapperD3D11Allocator::TextureWrapperD3D11Allocator()
|
|
: mThread(SharedThreadPool::Get("TextureUpdate"_ns, 1)),
|
|
mMutex("TextureWrapperD3D11Allocator::mMutex") {}
|
|
TextureWrapperD3D11Allocator::~TextureWrapperD3D11Allocator() = default;
|
|
|
|
RefPtr<ID3D11Texture2D> TextureWrapperD3D11Allocator::CreateOrRecycle(
|
|
gfx::SurfaceFormat aSurfaceFormat, gfx::IntSize aSize) {
|
|
MOZ_ASSERT(mThread->IsOnCurrentThread());
|
|
|
|
RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice();
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
if (!!mDevice && mDevice != device) {
|
|
// Device reset might happen
|
|
ClearAllTextures(lock);
|
|
mDevice = nullptr;
|
|
}
|
|
|
|
if (!mDevice) {
|
|
mDevice = device;
|
|
MOZ_ASSERT(mDevice == gfx::DeviceManagerDx::Get()->GetCompositorDevice());
|
|
}
|
|
|
|
if (!mDevice) {
|
|
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
|
return nullptr;
|
|
}
|
|
|
|
if (aSurfaceFormat != gfx::SurfaceFormat::NV12) {
|
|
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
|
return nullptr;
|
|
}
|
|
|
|
if (mSize != aSize) {
|
|
ClearAllTextures(lock);
|
|
mSize = aSize;
|
|
}
|
|
|
|
if (!mRecycledTextures.empty()) {
|
|
RefPtr<ID3D11Texture2D> texture2D = mRecycledTextures.front();
|
|
mRecycledTextures.pop_front();
|
|
return texture2D;
|
|
}
|
|
}
|
|
|
|
CD3D11_TEXTURE2D_DESC desc(
|
|
DXGI_FORMAT_NV12, mSize.width, mSize.height, 1, 1,
|
|
D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
|
|
|
|
RefPtr<ID3D11Texture2D> texture2D;
|
|
HRESULT hr =
|
|
device->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture2D));
|
|
if (FAILED(hr) || !texture2D) {
|
|
return nullptr;
|
|
}
|
|
|
|
EnsureStagingTextureNV12(device);
|
|
if (!mStagingTexture) {
|
|
return nullptr;
|
|
}
|
|
|
|
return texture2D;
|
|
}
|
|
|
|
void TextureWrapperD3D11Allocator::EnsureStagingTextureNV12(
|
|
RefPtr<ID3D11Device> aDevice) {
|
|
MOZ_ASSERT(mThread->IsOnCurrentThread());
|
|
MOZ_ASSERT(aDevice);
|
|
|
|
if (mStagingTexture) {
|
|
return;
|
|
}
|
|
|
|
D3D11_TEXTURE2D_DESC desc = {};
|
|
desc.Width = mSize.width;
|
|
desc.Height = mSize.height;
|
|
desc.Format = DXGI_FORMAT_NV12;
|
|
desc.MipLevels = 1;
|
|
desc.ArraySize = 1;
|
|
desc.Usage = D3D11_USAGE_STAGING;
|
|
desc.BindFlags = 0;
|
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
desc.MiscFlags = 0;
|
|
desc.SampleDesc.Count = 1;
|
|
|
|
RefPtr<ID3D11Texture2D> stagingTexture;
|
|
HRESULT hr =
|
|
aDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(stagingTexture));
|
|
if (FAILED(hr) || !stagingTexture) {
|
|
return;
|
|
}
|
|
mStagingTexture = stagingTexture;
|
|
}
|
|
|
|
RefPtr<ID3D11Texture2D> TextureWrapperD3D11Allocator::GetStagingTextureNV12() {
|
|
MOZ_ASSERT(mThread->IsOnCurrentThread());
|
|
|
|
return mStagingTexture;
|
|
}
|
|
|
|
RefPtr<ID3D11Device> TextureWrapperD3D11Allocator::GetDevice() {
|
|
MOZ_ASSERT(mThread->IsOnCurrentThread());
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
return mDevice;
|
|
};
|
|
|
|
void TextureWrapperD3D11Allocator::ClearAllTextures(
|
|
const MutexAutoLock& aProofOfLock) {
|
|
MOZ_ASSERT(mThread->IsOnCurrentThread());
|
|
|
|
mStagingTexture = nullptr;
|
|
mRecycledTextures.clear();
|
|
}
|
|
|
|
void TextureWrapperD3D11Allocator::RecycleTexture(
|
|
RefPtr<ID3D11Texture2D>& aTexture) {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
MOZ_ASSERT(aTexture);
|
|
|
|
RefPtr<ID3D11Device> device;
|
|
aTexture->GetDevice(getter_AddRefs(device));
|
|
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
aTexture->GetDesc(&desc);
|
|
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
if (device != mDevice || desc.Format != DXGI_FORMAT_NV12 ||
|
|
desc.Width != static_cast<UINT>(mSize.width) ||
|
|
desc.Height != static_cast<UINT>(mSize.height)) {
|
|
return;
|
|
}
|
|
|
|
const auto kMaxPooledSized = 5;
|
|
if (mRecycledTextures.size() > kMaxPooledSized) {
|
|
return;
|
|
}
|
|
mRecycledTextures.emplace_back(aTexture);
|
|
}
|
|
}
|
|
|
|
void TextureWrapperD3D11Allocator::RegisterTextureHostWrapper(
|
|
const wr::ExternalImageId& aExternalImageId,
|
|
RefPtr<TextureHost> aTextureHost) {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
|
|
auto it = mTextureHostWrappers.find(wr::AsUint64(aExternalImageId));
|
|
if (it != mTextureHostWrappers.end()) {
|
|
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
|
return;
|
|
}
|
|
mTextureHostWrappers.emplace(wr::AsUint64(aExternalImageId), aTextureHost);
|
|
}
|
|
|
|
void TextureWrapperD3D11Allocator::UnregisterTextureHostWrapper(
|
|
const wr::ExternalImageId& aExternalImageId) {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
|
|
auto it = mTextureHostWrappers.find(wr::AsUint64(aExternalImageId));
|
|
if (it == mTextureHostWrappers.end()) {
|
|
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
|
return;
|
|
}
|
|
mTextureHostWrappers.erase(it);
|
|
}
|
|
|
|
RefPtr<TextureHost> TextureWrapperD3D11Allocator::GetTextureHostWrapper(
|
|
const wr::ExternalImageId& aExternalImageId) {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
|
|
auto it = mTextureHostWrappers.find(wr::AsUint64(aExternalImageId));
|
|
if (it == mTextureHostWrappers.end()) {
|
|
return nullptr;
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
// static
|
|
RefPtr<TextureHost> TextureHostWrapperD3D11::CreateFromBufferTexture(
|
|
const RefPtr<TextureWrapperD3D11Allocator>& aAllocator,
|
|
TextureHost* aTextureHost) {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
MOZ_ASSERT(aAllocator);
|
|
MOZ_ASSERT(aTextureHost);
|
|
|
|
if (!XRE_IsGPUProcess()) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto* bufferTexture = aTextureHost->AsBufferTextureHost();
|
|
if (!bufferTexture ||
|
|
bufferTexture->GetFormat() != gfx::SurfaceFormat::YUV420) {
|
|
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
|
return nullptr;
|
|
}
|
|
|
|
auto extId = aTextureHost->GetMaybeExternalImageId();
|
|
MOZ_RELEASE_ASSERT(extId.isSome());
|
|
|
|
// Reuse TextureHostWrapperD3D11 if it is still valid.
|
|
RefPtr<TextureHost> textureHost =
|
|
aAllocator->GetTextureHostWrapper(extId.ref());
|
|
if (textureHost) {
|
|
return textureHost;
|
|
}
|
|
|
|
auto size = bufferTexture->GetSize();
|
|
auto colorDepth = bufferTexture->GetColorDepth();
|
|
auto colorRange = bufferTexture->GetColorRange();
|
|
auto chromaSubsampling = bufferTexture->GetChromaSubsampling();
|
|
|
|
// Check if data could be used with NV12
|
|
// XXX support gfx::ColorRange::FULL
|
|
if (size.width % 2 != 0 || size.height % 2 != 0 ||
|
|
colorDepth != gfx::ColorDepth::COLOR_8 ||
|
|
colorRange != gfx::ColorRange::LIMITED ||
|
|
chromaSubsampling != gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT) {
|
|
if (profiler_thread_is_being_profiled_for_markers()) {
|
|
nsPrintfCString str(
|
|
"Unsupported size(%dx%d) colorDepth %hhu colorRange %hhu "
|
|
"chromaSubsampling %hhu",
|
|
size.width, size.height, uint8_t(colorDepth), uint8_t(colorRange),
|
|
uint8_t(chromaSubsampling));
|
|
PROFILER_MARKER_TEXT("TextureHostWrapperD3D11", GRAPHICS, {}, str);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
auto id = GpuProcessD3D11TextureMap::GetNextTextureId();
|
|
auto flags = aTextureHost->GetFlags() | TextureFlags::SOFTWARE_DECODED_VIDEO;
|
|
|
|
auto colorSpace = ToColorSpace2(bufferTexture->GetYUVColorSpace());
|
|
|
|
auto descD3D10 = SurfaceDescriptorD3D10(
|
|
nullptr, Some(id),
|
|
/* arrayIndex */ 0, gfx::SurfaceFormat::NV12, size, colorSpace,
|
|
colorRange, /* hasKeyedMutex */ false, /* fencesHolderId */ Nothing());
|
|
|
|
RefPtr<DXGITextureHostD3D11> textureHostD3D11 =
|
|
new DXGITextureHostD3D11(flags, descD3D10);
|
|
|
|
RefPtr<TextureHostWrapperD3D11> textureHostWrapper =
|
|
new TextureHostWrapperD3D11(flags, aAllocator, id, textureHostD3D11,
|
|
aTextureHost, extId.ref());
|
|
textureHostWrapper->PostTask();
|
|
|
|
auto externalImageId = AsyncImagePipelineManager::GetNextExternalImageId();
|
|
|
|
textureHost =
|
|
new WebRenderTextureHost(flags, textureHostWrapper, externalImageId);
|
|
aAllocator->RegisterTextureHostWrapper(extId.ref(), textureHost);
|
|
|
|
return textureHost;
|
|
}
|
|
|
|
TextureHostWrapperD3D11::TextureHostWrapperD3D11(
|
|
TextureFlags aFlags, const RefPtr<TextureWrapperD3D11Allocator>& aAllocator,
|
|
const GpuProcessTextureId aTextureId,
|
|
DXGITextureHostD3D11* aTextureHostD3D11, TextureHost* aWrappedTextureHost,
|
|
const wr::ExternalImageId aWrappedExternalImageId)
|
|
: TextureHost(TextureHostType::DXGI, aFlags),
|
|
mAllocator(aAllocator),
|
|
mTextureId(aTextureId),
|
|
mTextureHostD3D11(aTextureHostD3D11),
|
|
mWrappedTextureHost(aWrappedTextureHost),
|
|
mWrappedExternalImageId(aWrappedExternalImageId) {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
MOZ_ASSERT(mAllocator);
|
|
MOZ_ASSERT(mTextureHostD3D11);
|
|
MOZ_ASSERT(mWrappedTextureHost);
|
|
|
|
MOZ_COUNT_CTOR(TextureHostWrapperD3D11);
|
|
}
|
|
|
|
TextureHostWrapperD3D11::~TextureHostWrapperD3D11() {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
MOZ_COUNT_DTOR(TextureHostWrapperD3D11);
|
|
|
|
auto* textureMap = GpuProcessD3D11TextureMap::Get();
|
|
if (textureMap) {
|
|
RefPtr<ID3D11Texture2D> texture = textureMap->GetTexture(mTextureId);
|
|
if (texture) {
|
|
mAllocator->RecycleTexture(texture);
|
|
textureMap->Unregister(mTextureId);
|
|
}
|
|
} else {
|
|
gfxCriticalNoteOnce << "GpuProcessD3D11TextureMap does not exist";
|
|
}
|
|
}
|
|
|
|
void TextureHostWrapperD3D11::PostTask() {
|
|
GpuProcessD3D11TextureMap::Get()->PostUpdateTextureDataTask(
|
|
mTextureId, this, mWrappedTextureHost, mAllocator);
|
|
}
|
|
|
|
bool TextureHostWrapperD3D11::IsValid() { return true; }
|
|
|
|
gfx::ColorRange TextureHostWrapperD3D11::GetColorRange() const {
|
|
return mTextureHostD3D11->GetColorRange();
|
|
}
|
|
|
|
gfx::IntSize TextureHostWrapperD3D11::GetSize() const {
|
|
return mTextureHostD3D11->GetSize();
|
|
}
|
|
|
|
gfx::SurfaceFormat TextureHostWrapperD3D11::GetFormat() const {
|
|
return mTextureHostD3D11->GetFormat();
|
|
}
|
|
|
|
void TextureHostWrapperD3D11::CreateRenderTexture(
|
|
const wr::ExternalImageId& aExternalImageId) {
|
|
MOZ_ASSERT(mExternalImageId.isSome());
|
|
|
|
mTextureHostD3D11->EnsureRenderTexture(mExternalImageId);
|
|
}
|
|
|
|
void TextureHostWrapperD3D11::MaybeDestroyRenderTexture() {
|
|
// TextureHostWrapperD3D11 does not create RenderTexture, then
|
|
// TextureHostWrapperD3D11 does not need to destroy RenderTexture.
|
|
mExternalImageId = Nothing();
|
|
}
|
|
|
|
uint32_t TextureHostWrapperD3D11::NumSubTextures() {
|
|
return mTextureHostD3D11->NumSubTextures();
|
|
}
|
|
|
|
void TextureHostWrapperD3D11::PushResourceUpdates(
|
|
wr::TransactionBuilder& aResources, ResourceUpdateOp aOp,
|
|
const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) {
|
|
mTextureHostD3D11->PushResourceUpdates(aResources, aOp, aImageKeys, aExtID);
|
|
}
|
|
|
|
void TextureHostWrapperD3D11::PushDisplayItems(
|
|
wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds,
|
|
const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
|
|
const Range<wr::ImageKey>& aImageKeys, PushDisplayItemFlagSet aFlags) {
|
|
MOZ_ASSERT(aImageKeys.length() > 0);
|
|
|
|
mTextureHostD3D11->PushDisplayItems(aBuilder, aBounds, aClip, aFilter,
|
|
aImageKeys, aFlags);
|
|
}
|
|
|
|
bool TextureHostWrapperD3D11::SupportsExternalCompositing(
|
|
WebRenderBackend aBackend) {
|
|
return mTextureHostD3D11->SupportsExternalCompositing(aBackend);
|
|
}
|
|
|
|
void TextureHostWrapperD3D11::UnbindTextureSource() {
|
|
mTextureHostD3D11->UnbindTextureSource();
|
|
// Handle read unlock
|
|
TextureHost::UnbindTextureSource();
|
|
}
|
|
|
|
void TextureHostWrapperD3D11::NotifyNotUsed() {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
|
|
mAllocator->UnregisterTextureHostWrapper(mWrappedExternalImageId);
|
|
|
|
MOZ_ASSERT(mWrappedTextureHost);
|
|
if (mWrappedTextureHost) {
|
|
mWrappedTextureHost = nullptr;
|
|
}
|
|
mTextureHostD3D11->NotifyNotUsed();
|
|
TextureHost::NotifyNotUsed();
|
|
}
|
|
|
|
BufferTextureHost* TextureHostWrapperD3D11::AsBufferTextureHost() {
|
|
return nullptr;
|
|
}
|
|
|
|
bool TextureHostWrapperD3D11::IsWrappingSurfaceTextureHost() { return false; }
|
|
|
|
TextureHostType TextureHostWrapperD3D11::GetTextureHostType() {
|
|
return mTextureHostD3D11->GetTextureHostType();
|
|
}
|
|
|
|
bool TextureHostWrapperD3D11::NeedsDeferredDeletion() const {
|
|
return mTextureHostD3D11->NeedsDeferredDeletion();
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|