Bug 1600595: Part 3 - Add D3D11 utilities for creating CPU textures and converting format enums r=jrmuizel

CreateBGRA8DataSourceSurfaceForD3D11Texture is added to create a CPU texture with the same data as the given D3D11 texture.  ReadbackTexture reads a D3D11 texture into a pre-existing CPU texture.  ToPixelFormat is extended to cover DXGI_FORMAT values.

Differential Revision: https://phabricator.services.mozilla.com/D57562

--HG--
extra : moz-landing-system : lando
This commit is contained in:
David Parks 2020-01-04 19:39:40 +00:00
Родитель c8478e9ba2
Коммит 83cbdc257d
3 изменённых файлов: 221 добавлений и 1 удалений

Просмотреть файл

@ -83,6 +83,10 @@ namespace mozilla {
class Mutex;
namespace layers {
class TextureData;
}
namespace wr {
struct FontInstanceOptions;
struct FontInstancePlatformOptions;
@ -1901,6 +1905,15 @@ class GFX2D_API Factory {
static void SetSystemTextQuality(uint8_t aQuality);
static already_AddRefed<DataSourceSurface>
CreateBGRA8DataSourceSurfaceForD3D11Texture(ID3D11Texture2D* aSrcTexture);
static bool ReadbackTexture(layers::TextureData* aDestCpuTexture,
ID3D11Texture2D* aSrcTexture);
static bool ReadbackTexture(DataSourceSurface* aDestCpuTexture,
ID3D11Texture2D* aSrcTexture);
private:
static StaticRefPtr<ID2D1Device> mD2D1Device;
static StaticRefPtr<ID3D11Device> mD3D11Device;
@ -1910,6 +1923,14 @@ class GFX2D_API Factory {
static StaticRefPtr<ID2D1DeviceContext> mMTDC;
static StaticRefPtr<ID2D1DeviceContext> mOffMTDC;
static bool ReadbackTexture(uint8_t* aDestData, int32_t aDestStride,
ID3D11Texture2D* aSrcTexture);
// DestTextureT can be TextureData or DataSourceSurface.
template <typename DestTextureT>
static bool ConvertSourceAndRetryReadback(DestTextureT* aDestCpuTexture,
ID3D11Texture2D* aSrcTexture);
protected:
// This guards access to the singleton devices above, as well as the
// singleton devices in DrawTargetD2D1.
@ -1919,7 +1940,7 @@ class GFX2D_API Factory {
static StaticMutex mDTDependencyLock;
friend class DrawTargetD2D1;
#endif
#endif // WIN32
private:
static DrawEventRecorder* mRecorder;

Просмотреть файл

@ -45,6 +45,8 @@
# include "NativeFontResourceDWrite.h"
# include <d3d10_1.h>
# include "HelpersD2D.h"
# include "DXVA2Manager.h"
# include "mozilla/layers/TextureD3D11.h"
#endif
#include "DrawTargetCapture.h"
@ -62,6 +64,8 @@
#include "mozilla/CheckedInt.h"
#include "mozilla/layers/TextureClient.h"
#ifdef MOZ_ENABLE_FREETYPE
# include "ft2build.h"
# include FT_FREETYPE_H
@ -1161,6 +1165,191 @@ void Factory::SetGlobalEventRecorder(DrawEventRecorder* aRecorder) {
mRecorder = aRecorder;
}
#ifdef WIN32
/* static */
already_AddRefed<DataSourceSurface>
Factory::CreateBGRA8DataSourceSurfaceForD3D11Texture(
ID3D11Texture2D* aSrcTexture) {
D3D11_TEXTURE2D_DESC srcDesc = {0};
aSrcTexture->GetDesc(&srcDesc);
RefPtr<gfx::DataSourceSurface> destTexture =
gfx::Factory::CreateDataSourceSurface(
IntSize(srcDesc.Width, srcDesc.Height), gfx::SurfaceFormat::B8G8R8A8);
if (NS_WARN_IF(!destTexture)) {
return nullptr;
}
if (!ReadbackTexture(destTexture, aSrcTexture)) {
return nullptr;
}
return destTexture.forget();
}
/* static */
template <typename DestTextureT>
bool Factory::ConvertSourceAndRetryReadback(DestTextureT* aDestCpuTexture,
ID3D11Texture2D* aSrcTexture) {
RefPtr<ID3D11Device> device;
aSrcTexture->GetDevice(getter_AddRefs(device));
if (!device) {
gfxWarning() << "Failed to get D3D11 device from source texture";
return false;
}
nsAutoCString error;
std::unique_ptr<DXVA2Manager> manager(
DXVA2Manager::CreateD3D11DXVA(nullptr, error, device));
if (!manager) {
gfxWarning() << "Failed to create DXVA2 manager!";
return false;
}
RefPtr<ID3D11Texture2D> newSrcTexture;
HRESULT hr =
manager->CopyToBGRATexture(aSrcTexture, getter_AddRefs(newSrcTexture));
if (FAILED(hr)) {
gfxWarning() << "Failed to copy to BGRA texture.";
return false;
}
return ReadbackTexture(aDestCpuTexture, newSrcTexture);
}
/* static */
bool Factory::ReadbackTexture(layers::TextureData* aDestCpuTexture,
ID3D11Texture2D* aSrcTexture) {
layers::MappedTextureData mappedData;
if (!aDestCpuTexture->BorrowMappedData(mappedData)) {
gfxWarning() << "Could not access in-memory texture";
return false;
}
D3D11_TEXTURE2D_DESC srcDesc = {0};
aSrcTexture->GetDesc(&srcDesc);
// Special case: If the source and destination have different formats and the
// destination is B8G8R8A8 then convert the source to B8G8R8A8 and readback.
if ((srcDesc.Format != DXGIFormat(mappedData.format)) &&
(mappedData.format == SurfaceFormat::B8G8R8A8)) {
return ConvertSourceAndRetryReadback(aDestCpuTexture, aSrcTexture);
}
if ((IntSize(srcDesc.Width, srcDesc.Height) != mappedData.size) ||
(srcDesc.Format != DXGIFormat(mappedData.format))) {
gfxWarning() << "Attempted readback between incompatible textures";
return false;
}
return ReadbackTexture(mappedData.data, mappedData.stride, aSrcTexture);
}
/* static */
bool Factory::ReadbackTexture(DataSourceSurface* aDestCpuTexture,
ID3D11Texture2D* aSrcTexture) {
D3D11_TEXTURE2D_DESC srcDesc = {0};
aSrcTexture->GetDesc(&srcDesc);
// Special case: If the source and destination have different formats and the
// destination is B8G8R8A8 then convert the source to B8G8R8A8 and readback.
if ((srcDesc.Format != DXGIFormat(aDestCpuTexture->GetFormat())) &&
(aDestCpuTexture->GetFormat() == SurfaceFormat::B8G8R8A8)) {
return ConvertSourceAndRetryReadback(aDestCpuTexture, aSrcTexture);
}
if ((IntSize(srcDesc.Width, srcDesc.Height) != aDestCpuTexture->GetSize()) ||
(srcDesc.Format != DXGIFormat(aDestCpuTexture->GetFormat()))) {
gfxWarning() << "Attempted readback between incompatible textures";
return false;
}
gfx::DataSourceSurface::MappedSurface mappedSurface;
if (!aDestCpuTexture->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) {
return false;
}
bool ret =
ReadbackTexture(mappedSurface.mData, mappedSurface.mStride, aSrcTexture);
aDestCpuTexture->Unmap();
return ret;
}
/* static */
bool Factory::ReadbackTexture(uint8_t* aDestData, int32_t aDestStride,
ID3D11Texture2D* aSrcTexture) {
MOZ_ASSERT(aDestData && aDestStride && aSrcTexture);
RefPtr<ID3D11Device> device;
aSrcTexture->GetDevice(getter_AddRefs(device));
if (!device) {
gfxWarning() << "Failed to get D3D11 device from source texture";
return false;
}
RefPtr<ID3D11DeviceContext> context;
device->GetImmediateContext(getter_AddRefs(context));
if (!context) {
gfxWarning() << "Could not get an immediate D3D11 context";
return false;
}
RefPtr<IDXGIKeyedMutex> mutex;
HRESULT hr = aSrcTexture->QueryInterface(__uuidof(IDXGIKeyedMutex),
(void**)getter_AddRefs(mutex));
if (SUCCEEDED(hr) && mutex) {
hr = mutex->AcquireSync(0, 2000);
if (hr != S_OK) {
gfxWarning() << "Could not acquire DXGI surface lock in 2 seconds";
return false;
}
}
D3D11_TEXTURE2D_DESC srcDesc = {0};
aSrcTexture->GetDesc(&srcDesc);
srcDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
srcDesc.Usage = D3D11_USAGE_STAGING;
srcDesc.BindFlags = 0;
srcDesc.MiscFlags = 0;
srcDesc.MipLevels = 1;
RefPtr<ID3D11Texture2D> srcCpuTexture;
hr =
device->CreateTexture2D(&srcDesc, nullptr, getter_AddRefs(srcCpuTexture));
if (FAILED(hr)) {
gfxWarning() << "Could not create source texture for mapping";
if (mutex) {
mutex->ReleaseSync(0);
}
return false;
}
context->CopyResource(srcCpuTexture, aSrcTexture);
if (mutex) {
mutex->ReleaseSync(0);
mutex = nullptr;
}
D3D11_MAPPED_SUBRESOURCE srcMap;
hr = context->Map(srcCpuTexture, 0, D3D11_MAP_READ, 0, &srcMap);
if (FAILED(hr)) {
gfxWarning() << "Could not map source texture";
return false;
}
uint32_t width = srcDesc.Width;
uint32_t height = srcDesc.Height;
int bpp = BytesPerPixel(gfx::ToPixelFormat(srcDesc.Format));
for (int y = 0; y < height; y++) {
memcpy(aDestData + aDestStride * y,
(unsigned char*)(srcMap.pData) + srcMap.RowPitch * y, width * bpp);
}
context->Unmap(srcCpuTexture, 0);
return true;
}
#endif // WIN32
// static
void CriticalLogger::OutputMessage(const std::string& aString, int aLevel,
bool aNoNewline) {

Просмотреть файл

@ -126,6 +126,16 @@ static inline IntSize ToIntSize(const D2D1_SIZE_U& aSize) {
return IntSize(aSize.width, aSize.height);
}
static inline SurfaceFormat ToPixelFormat(const DXGI_FORMAT& aFormat) {
switch (aFormat) {
case DXGI_FORMAT_A8_UNORM:
case DXGI_FORMAT_R8_UNORM:
return SurfaceFormat::A8;
default:
return SurfaceFormat::B8G8R8A8;
}
}
static inline SurfaceFormat ToPixelFormat(const D2D1_PIXEL_FORMAT& aFormat) {
switch (aFormat.format) {
case DXGI_FORMAT_A8_UNORM: