blob: fb0ca913e91b1b65780404a4704467dc472c52f6 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/renderers/win/media_foundation_texture_pool.h"
#include "base/memory/raw_ptr.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "media/base/mock_filters.h"
#include "media/base/test_helpers.h"
#include "media/base/win/test_utils.h"
#include <d3d11.h>
#include <dxgi1_2.h>
#include <wrl/client.h>
using Microsoft::WRL::ComPtr;
namespace media {
class MockD3D11Texture2D;
class MockD3D11Resource final : public IDXGIResource1 {
public:
MockD3D11Resource() {}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
void** ppvObject) override;
ULONG STDMETHODCALLTYPE AddRef(void) override {
return InterlockedIncrement(&refcount_);
}
ULONG STDMETHODCALLTYPE Release(void) override {
ULONG refcount = InterlockedDecrement(&refcount_);
if (refcount == 0) {
refcount_ = 0xBAADF00D;
delete this;
}
return refcount;
}
// IDXGIResource1
HRESULT STDMETHODCALLTYPE
CreateSubresourceSurface(UINT index, IDXGISurface2** ppSurface) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateSharedHandle(const SECURITY_ATTRIBUTES* pAttributes,
DWORD dwAccess,
LPCWSTR lpName,
HANDLE* pHandle) override;
// IDXGIResource
HRESULT STDMETHODCALLTYPE GetSharedHandle(HANDLE* pSharedHandle) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE GetUsage(DXGI_USAGE* pUsage) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
SetEvictionPriority(UINT eviction_priority) override;
HRESULT STDMETHODCALLTYPE
GetEvictionPriority(UINT* eviction_priority) override;
// IDXGIDeviceSubObject
HRESULT STDMETHODCALLTYPE GetDevice(REFIID riid, void** ppDevice) override {
return E_NOTIMPL;
}
// IDXGIObject
HRESULT STDMETHODCALLTYPE GetParent(REFIID riid, void** ppParent) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE GetPrivateData(REFGUID guid,
UINT* pDataSize,
void* pData) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE SetPrivateData(REFGUID guid,
UINT DataSize,
const void* pData) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
SetPrivateDataInterface(REFGUID guid, const IUnknown* pData) override;
private:
raw_ptr<MockD3D11Texture2D> parent_;
volatile ULONG refcount_ = 1;
};
class MockD3D11Texture2D final : public ID3D11Texture2D {
private:
MockD3D11Texture2D(const D3D11_TEXTURE2D_DESC* texture_description)
: resource_(new MockD3D11Resource()) {
memcpy(&texture_description_, texture_description,
sizeof(D3D11_TEXTURE2D_DESC));
}
public:
static HRESULT CreateInstance(const D3D11_TEXTURE2D_DESC* texture_description,
ID3D11Texture2D** texture2D);
// IUnknown
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
void** ppvObject) override {
if (ppvObject == nullptr) {
return E_POINTER;
}
if (FAILED(QueryInterfaceInternal(riid, ppvObject))) {
if (resource_ != nullptr)
return resource_->QueryInterface(riid, ppvObject);
else
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryInterfaceInternal(REFIID riid,
void** ppvObject) {
if (ppvObject == nullptr) {
return E_POINTER;
}
if (riid == IID_ID3D11Texture2D || riid == IID_IUnknown) {
*ppvObject = static_cast<ID3D11Texture2D*>(this);
} else if (riid == IID_ID3D11Resource) {
*ppvObject = static_cast<ID3D11Resource*>(this);
} else if (riid == IID_ID3D11DeviceChild) {
*ppvObject = static_cast<ID3D11DeviceChild*>(this);
} else {
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG STDMETHODCALLTYPE AddRef(void) override {
return InterlockedIncrement(&refcount_);
}
ULONG STDMETHODCALLTYPE Release(void) override {
ULONG refcount = InterlockedDecrement(&refcount_);
if (refcount == 0) {
refcount_ = 0xBAADF00D;
delete this;
}
return refcount;
}
// ID3D11Texture2D
void STDMETHODCALLTYPE GetDesc(D3D11_TEXTURE2D_DESC* description) override {
memset(description, 0, sizeof(D3D11_TEXTURE2D_DESC));
}
// ID3D11Resource
void STDMETHODCALLTYPE
GetType(D3D11_RESOURCE_DIMENSION* resource_dimension) override {
*resource_dimension =
D3D11_RESOURCE_DIMENSION::D3D11_RESOURCE_DIMENSION_TEXTURE2D;
}
void STDMETHODCALLTYPE SetEvictionPriority(UINT eviction_priority) override {
eviction_priority_ = eviction_priority;
}
UINT STDMETHODCALLTYPE GetEvictionPriority() override {
return eviction_priority_;
}
// ID3D11DeviceChild
void STDMETHODCALLTYPE GetDevice(ID3D11Device** ppDevice) override {
device_.CopyTo(ppDevice);
}
HRESULT STDMETHODCALLTYPE GetPrivateData(REFGUID guid,
UINT* pDataSize,
void* pData) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE SetPrivateData(REFGUID guid,
UINT DataSize,
const void* pData) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
SetPrivateDataInterface(REFGUID guid, const IUnknown* pData) override {
// Our tests aren't checking for this right now
return S_OK;
}
private:
volatile ULONG refcount_ = 1;
UINT eviction_priority_ = 0;
D3D11_TEXTURE2D_DESC texture_description_;
ComPtr<ID3D11Device> device_;
ComPtr<IDXGIResource1> resource_;
};
class MockD3D11Device : public ID3D11Device {
public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
void** ppvObject) override {
if (ppvObject == nullptr) {
return E_POINTER;
}
if (riid == IID_ID3D11Device || riid == IID_IUnknown) {
*ppvObject = this;
return S_OK;
}
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef(void) override { return 1; }
ULONG STDMETHODCALLTYPE Release(void) override { return 1; }
HRESULT STDMETHODCALLTYPE
CreateBuffer(const D3D11_BUFFER_DESC* pDesc,
const D3D11_SUBRESOURCE_DATA* pInitialData,
ID3D11Buffer** ppBuffer) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateTexture1D(const D3D11_TEXTURE1D_DESC* pDesc,
const D3D11_SUBRESOURCE_DATA* pInitialData,
ID3D11Texture1D** ppTexture1D) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateTexture2D(const D3D11_TEXTURE2D_DESC* pDesc,
const D3D11_SUBRESOURCE_DATA* pInitialData,
ID3D11Texture2D** ppTexture2D) override;
HRESULT STDMETHODCALLTYPE
CreateTexture3D(const D3D11_TEXTURE3D_DESC* pDesc,
const D3D11_SUBRESOURCE_DATA* pInitialData,
ID3D11Texture3D** ppTexture3D) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateShaderResourceView(ID3D11Resource* pResource,
const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc,
ID3D11ShaderResourceView** ppSRView) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateUnorderedAccessView(ID3D11Resource* pResource,
const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc,
ID3D11UnorderedAccessView** ppUAView) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateRenderTargetView(ID3D11Resource* pResource,
const D3D11_RENDER_TARGET_VIEW_DESC* pDesc,
ID3D11RenderTargetView** ppRTView) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateDepthStencilView(ID3D11Resource* pResource,
const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc,
ID3D11DepthStencilView** ppDepthStencilView) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateInputLayout(const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs,
UINT NumElements,
const void* pShaderBytecodeWithInputSignature,
SIZE_T BytecodeLength,
ID3D11InputLayout** ppInputLayout) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateVertexShader(const void* pShaderBytecode,
SIZE_T BytecodeLength,
ID3D11ClassLinkage* pClassLinkage,
ID3D11VertexShader** ppVertexShader) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateGeometryShader(const void* pShaderBytecode,
SIZE_T BytecodeLength,
ID3D11ClassLinkage* pClassLinkage,
ID3D11GeometryShader** ppGeometryShader) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CreateGeometryShaderWithStreamOutput(
const void* pShaderBytecode,
SIZE_T BytecodeLength,
const D3D11_SO_DECLARATION_ENTRY* pSODeclaration,
UINT NumEntries,
const UINT* pBufferStrides,
UINT NumStrides,
UINT RasterizedStream,
ID3D11ClassLinkage* pClassLinkage,
ID3D11GeometryShader** ppGeometryShader) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreatePixelShader(const void* pShaderBytecode,
SIZE_T BytecodeLength,
ID3D11ClassLinkage* pClassLinkage,
ID3D11PixelShader** ppPixelShader) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateHullShader(const void* pShaderBytecode,
SIZE_T BytecodeLength,
ID3D11ClassLinkage* pClassLinkage,
ID3D11HullShader** ppHullShader) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateDomainShader(const void* pShaderBytecode,
SIZE_T BytecodeLength,
ID3D11ClassLinkage* pClassLinkage,
ID3D11DomainShader** ppDomainShader) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateComputeShader(const void* pShaderBytecode,
SIZE_T BytecodeLength,
ID3D11ClassLinkage* pClassLinkage,
ID3D11ComputeShader** ppComputeShader) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateClassLinkage(ID3D11ClassLinkage** ppLinkage) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateBlendState(const D3D11_BLEND_DESC* pBlendStateDesc,
ID3D11BlendState** ppBlendState) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CreateDepthStencilState(
const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc,
ID3D11DepthStencilState** ppDepthStencilState) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateRasterizerState(const D3D11_RASTERIZER_DESC* pRasterizerDesc,
ID3D11RasterizerState** ppRasterizerState) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateSamplerState(const D3D11_SAMPLER_DESC* pSamplerDesc,
ID3D11SamplerState** ppSamplerState) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CreateQuery(const D3D11_QUERY_DESC* pQueryDesc,
ID3D11Query** ppQuery) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreatePredicate(const D3D11_QUERY_DESC* pPredicateDesc,
ID3D11Predicate** ppPredicate) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateCounter(const D3D11_COUNTER_DESC* pCounterDesc,
ID3D11Counter** ppCounter) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CreateDeferredContext(UINT ContextFlags,
ID3D11DeviceContext** ppDeferredContext) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE OpenSharedResource(HANDLE hResource,
REFIID ReturnedInterface,
void** ppResource) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CheckFormatSupport(DXGI_FORMAT Format,
UINT* pFormatSupport) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CheckMultisampleQualityLevels(DXGI_FORMAT Format,
UINT SampleCount,
UINT* pNumQualityLevels) override {
return E_NOTIMPL;
}
void STDMETHODCALLTYPE
CheckCounterInfo(D3D11_COUNTER_INFO* pCounterInfo) override {}
HRESULT STDMETHODCALLTYPE CheckCounter(const D3D11_COUNTER_DESC* pDesc,
D3D11_COUNTER_TYPE* pType,
UINT* pActiveCounters,
LPSTR szName,
UINT* pNameLength,
LPSTR szUnits,
UINT* pUnitsLength,
LPSTR szDescription,
UINT* pDescriptionLength) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
CheckFeatureSupport(D3D11_FEATURE Feature,
void* pFeatureSupportData,
UINT FeatureSupportDataSize) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE GetPrivateData(REFGUID guid,
UINT* pDataSize,
void* pData) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE SetPrivateData(REFGUID guid,
UINT DataSize,
const void* pData) override {
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
SetPrivateDataInterface(REFGUID guid, const IUnknown* pData) override {
return E_NOTIMPL;
}
D3D_FEATURE_LEVEL STDMETHODCALLTYPE GetFeatureLevel(void) override {
return D3D_FEATURE_LEVEL_11_1;
}
UINT STDMETHODCALLTYPE GetCreationFlags(void) override { return 0; }
HRESULT STDMETHODCALLTYPE GetDeviceRemovedReason(void) override {
return E_NOTIMPL;
}
void STDMETHODCALLTYPE
GetImmediateContext(ID3D11DeviceContext** ppImmediateContext) override {}
HRESULT STDMETHODCALLTYPE SetExceptionMode(UINT RaiseFlags) override {
return E_NOTIMPL;
}
UINT STDMETHODCALLTYPE GetExceptionMode(void) override { return 0; }
};
// MockD3D11Resource
HRESULT STDMETHODCALLTYPE MockD3D11Resource::QueryInterface(REFIID riid,
void** ppvObject) {
if (ppvObject == nullptr) {
return E_POINTER;
}
if (riid == IID_IDXGIResource1) {
*ppvObject = static_cast<IDXGIResource1*>(this);
} else if (riid == IID_IDXGIResource) {
*ppvObject = static_cast<IDXGIResource*>(this);
} else if (riid == IID_IDXGIDeviceSubObject) {
*ppvObject = static_cast<IDXGIDeviceSubObject*>(this);
} else if (riid == IID_IDXGIObject) {
*ppvObject = static_cast<IDXGIObject*>(this);
} else if (parent_ != nullptr) {
return parent_->QueryInterfaceInternal(riid, ppvObject);
} else {
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
HRESULT STDMETHODCALLTYPE
MockD3D11Resource::SetEvictionPriority(UINT eviction_priority) {
parent_->SetEvictionPriority(eviction_priority);
return S_OK;
}
HRESULT STDMETHODCALLTYPE
MockD3D11Resource::GetEvictionPriority(UINT* eviction_priority) {
*eviction_priority = parent_->GetEvictionPriority();
return S_OK;
}
HRESULT STDMETHODCALLTYPE
MockD3D11Resource::SetPrivateDataInterface(REFGUID guid,
const IUnknown* pData) {
return parent_->SetPrivateDataInterface(guid, pData);
}
HRESULT STDMETHODCALLTYPE
MockD3D11Resource::CreateSharedHandle(const SECURITY_ATTRIBUTES* pAttributes,
DWORD dwAccess,
LPCWSTR lpName,
HANDLE* pHandle) {
// Using an event to create a valid nt handle
*pHandle = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (*pHandle == nullptr) {
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
HRESULT MockD3D11Texture2D::CreateInstance(
const D3D11_TEXTURE2D_DESC* texture_description,
ID3D11Texture2D** texture2D) {
MockD3D11Texture2D* mock_texture =
new MockD3D11Texture2D(texture_description);
if (!mock_texture) {
return E_OUTOFMEMORY;
}
*texture2D = mock_texture;
return S_OK;
}
HRESULT STDMETHODCALLTYPE
MockD3D11Device::CreateTexture2D(const D3D11_TEXTURE2D_DESC* pDesc,
const D3D11_SUBRESOURCE_DATA* pInitialData,
ID3D11Texture2D** ppTexture2D) {
return MockD3D11Texture2D::CreateInstance(pDesc, ppTexture2D);
}
class MediaFoundationTexturePoolTest : public testing::Test {
public:
MediaFoundationTexturePoolTest() {}
base::WeakPtrFactory<MediaFoundationTexturePoolTest> weak_factory_{this};
};
TEST_F(MediaFoundationTexturePoolTest, VerifyTextureInitialization) {
MockD3D11Device mock_d3d_device;
media::MediaFoundationTexturePool test;
base::WaitableEvent wait_event;
gfx::Size frame_size(1920, 1080);
class SpecialCallback {
private:
raw_ptr<base::WaitableEvent> wait_event_;
raw_ptr<gfx::Size> frame_size_;
public:
SpecialCallback(base::WaitableEvent* wait_event, gfx::Size* frame_size)
: wait_event_(wait_event), frame_size_(frame_size) {}
void Invoke(std::vector<media::MediaFoundationFrameInfo> frame_textures,
const gfx::Size& texture_size) {
EXPECT_EQ(texture_size.width(), frame_size_->width());
EXPECT_EQ(texture_size.height(), frame_size_->height());
wait_event_->Signal();
}
} callback(&wait_event, &frame_size);
EXPECT_HRESULT_SUCCEEDED(
test.Initialize(&mock_d3d_device,
base::BindRepeating(&SpecialCallback::Invoke,
base::Unretained(&callback)),
frame_size));
wait_event.Wait();
}
} // namespace media