dxva2: Introduce progressive processor device.

The point is to provide a device, with similar caps and NV12 support,
while keeping software device on its own, the way it should be.

This is based on research by Derek Lesho.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2021-11-23 15:21:45 +03:00 committed by Alexandre Julliard
parent 996126b2de
commit 12a1b4b360
3 changed files with 257 additions and 20 deletions

View File

@ -34,6 +34,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(dxva2);
#define D3DFMT_NV12 MAKEFOURCC('N','V','1','2')
enum device_handle_flags
{
HANDLE_FLAG_OPEN = 0x1,
@ -125,6 +127,22 @@ static struct video_processor *impl_from_IDirectXVideoProcessor(IDirectXVideoPro
return CONTAINING_RECORD(iface, struct video_processor, IDirectXVideoProcessor_iface);
}
static const DXVA2_VideoProcessorCaps software_processor_caps =
{
.DeviceCaps = DXVA2_VPDev_SoftwareDevice,
.InputPool = D3DPOOL_SYSTEMMEM,
.VideoProcessorOperations = DXVA2_VideoProcess_PlanarAlpha | DXVA2_VideoProcess_YUV2RGB |
DXVA2_VideoProcess_StretchX | DXVA2_VideoProcess_StretchY | DXVA2_VideoProcess_SubRects |
DXVA2_VideoProcess_SubStreams | DXVA2_VideoProcess_SubStreamsExtended | DXVA2_VideoProcess_YUV2RGBExtended,
};
static const DXVA2_VideoProcessorCaps progressive_processor_caps =
{
.DeviceCaps = DXVA2_VPDev_HardwareDevice,
.InputPool = D3DPOOL_DEFAULT,
.VideoProcessorOperations = DXVA2_VideoProcess_YUV2RGB | DXVA2_VideoProcess_StretchX | DXVA2_VideoProcess_StretchY,
};
static HRESULT WINAPI video_processor_QueryInterface(IDirectXVideoProcessor *iface, REFIID riid, void **obj)
{
if (IsEqualIID(riid, &IID_IDirectXVideoProcessor) ||
@ -210,12 +228,11 @@ static HRESULT WINAPI video_processor_GetVideoProcessorCaps(IDirectXVideoProcess
if (IsEqualGUID(&processor->device, &DXVA2_VideoProcSoftwareDevice))
{
memset(caps, 0, sizeof(*caps));
caps->DeviceCaps = DXVA2_VPDev_SoftwareDevice;
caps->InputPool = D3DPOOL_SYSTEMMEM;
caps->VideoProcessorOperations = DXVA2_VideoProcess_PlanarAlpha | DXVA2_VideoProcess_YUV2RGB |
DXVA2_VideoProcess_StretchX | DXVA2_VideoProcess_StretchY | DXVA2_VideoProcess_SubRects |
DXVA2_VideoProcess_SubStreams | DXVA2_VideoProcess_SubStreamsExtended | DXVA2_VideoProcess_YUV2RGBExtended;
*caps = software_processor_caps;
}
else if (IsEqualGUID(&processor->device, &DXVA2_VideoProcProgressiveDevice))
{
*caps = progressive_processor_caps;
}
else
{
@ -419,28 +436,66 @@ static HRESULT WINAPI device_manager_processor_service_RegisterVideoProcessorSof
return E_NOTIMPL;
}
static BOOL dxva_is_supported_stream_format(const DXVA2_VideoDesc *video_desc)
struct dxva_processor_device_desc
{
return video_desc->Format == D3DFMT_A8R8G8B8 ||
video_desc->Format == D3DFMT_X8R8G8B8 ||
video_desc->Format == D3DFMT_YUY2;
const GUID *guid;
const D3DFORMAT *input_formats;
};
static const D3DFORMAT software_processor_input_formats[] =
{
D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_YUY2, 0
};
static const D3DFORMAT progressive_processor_input_formats[] =
{
D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_YUY2, D3DFMT_NV12, 0
};
static const struct dxva_processor_device_desc processor_devices[] =
{
{ &DXVA2_VideoProcProgressiveDevice, progressive_processor_input_formats },
{ &DXVA2_VideoProcSoftwareDevice, software_processor_input_formats },
};
static BOOL dxva_is_supported_stream_format(const DXVA2_VideoDesc *video_desc, const D3DFORMAT *formats)
{
while (*formats)
{
if (*formats == video_desc->Format) return TRUE;
formats++;
}
return FALSE;
}
static HRESULT WINAPI device_manager_processor_service_GetVideoProcessorDeviceGuids(
IDirectXVideoProcessorService *iface, const DXVA2_VideoDesc *video_desc, UINT *count, GUID **guids)
{
unsigned int i;
FIXME("%p, %p, %p, %p semi-stub.\n", iface, video_desc, count, guids);
*count = 0;
if (!dxva_is_supported_stream_format(video_desc))
return E_FAIL;
if (!(*guids = CoTaskMemAlloc(sizeof(**guids))))
if (!(*guids = CoTaskMemAlloc(ARRAY_SIZE(processor_devices) * sizeof(**guids))))
return E_OUTOFMEMORY;
memcpy(*guids, &DXVA2_VideoProcSoftwareDevice, sizeof(**guids));
*count = 1;
for (i = 0; i < ARRAY_SIZE(processor_devices); ++i)
{
if (dxva_is_supported_stream_format(video_desc, processor_devices[i].input_formats))
{
(*guids)[*count] = *processor_devices[i].guid;
*count += 1;
}
}
if (!*count)
{
CoTaskMemFree(*guids);
*guids = NULL;
return E_FAIL;
}
return S_OK;
}
@ -453,7 +508,7 @@ static HRESULT WINAPI device_manager_processor_service_GetVideoProcessorRenderTa
if (IsEqualGUID(deviceguid, &DXVA2_VideoProcSoftwareDevice))
{
if (!dxva_is_supported_stream_format(video_desc))
if (!dxva_is_supported_stream_format(video_desc, software_processor_input_formats))
{
WARN("Unsupported content format %#x.\n", video_desc->Format);
return E_FAIL;
@ -468,6 +523,23 @@ static HRESULT WINAPI device_manager_processor_service_GetVideoProcessorRenderTa
return S_OK;
}
else if (IsEqualGUID(deviceguid, &DXVA2_VideoProcProgressiveDevice))
{
if (!dxva_is_supported_stream_format(video_desc, progressive_processor_input_formats))
{
WARN("Unsupported content format %#x.\n", video_desc->Format);
return E_FAIL;
}
if (!(*formats = CoTaskMemAlloc(2 * sizeof(**formats))))
return E_OUTOFMEMORY;
*count = 2;
(*formats)[0] = D3DFMT_X8R8G8B8;
(*formats)[1] = D3DFMT_NV12;
return S_OK;
}
else
FIXME("Unsupported device %s.\n", debugstr_guid(deviceguid));
@ -484,12 +556,26 @@ static HRESULT WINAPI device_manager_processor_service_GetVideoProcessorSubStrea
}
static HRESULT WINAPI device_manager_processor_service_GetVideoProcessorCaps(
IDirectXVideoProcessorService *iface, REFGUID deviceguid, const DXVA2_VideoDesc *video_desc,
IDirectXVideoProcessorService *iface, REFGUID device, const DXVA2_VideoDesc *video_desc,
D3DFORMAT rt_format, DXVA2_VideoProcessorCaps *caps)
{
FIXME("%p, %s, %p, %u, %p.\n", iface, debugstr_guid(deviceguid), video_desc, rt_format, caps);
TRACE("%p, %s, %p, %u, %p.\n", iface, debugstr_guid(device), video_desc, rt_format, caps);
return E_NOTIMPL;
if (IsEqualGUID(device, &DXVA2_VideoProcSoftwareDevice))
{
*caps = software_processor_caps;
}
else if (IsEqualGUID(device, &DXVA2_VideoProcProgressiveDevice))
{
*caps = progressive_processor_caps;
}
else
{
FIXME("Unrecognized device %s.\n", debugstr_guid(device));
return E_NOTIMPL;
}
return S_OK;
}
static HRESULT WINAPI device_manager_processor_service_GetProcAmpRange(

View File

@ -546,8 +546,157 @@ done:
DestroyWindow(window);
}
static BOOL check_format_list(D3DFORMAT format, const D3DFORMAT *list, unsigned int count)
{
unsigned int i;
for (i = 0; i < count; ++i)
if (list[i] == format) return TRUE;
return FALSE;
}
static void test_progressive_device(void)
{
static const unsigned int processor_ops = DXVA2_VideoProcess_YUV2RGB |
DXVA2_VideoProcess_StretchX | DXVA2_VideoProcess_StretchY;
IDirectXVideoProcessorService *service;
IDirectXVideoProcessor *processor;
IDirect3DDeviceManager9 *manager;
D3DFORMAT format, *rt_formats;
DXVA2_VideoProcessorCaps caps;
DXVA2_VideoDesc video_desc;
IDirect3DDevice9 *device;
unsigned int count, i, j;
GUID guid, *guids;
IDirect3D9 *d3d;
HANDLE handle;
HWND window;
UINT token;
HRESULT hr;
static const D3DFORMAT input_formats[] =
{
D3DFMT_A8R8G8B8,
D3DFMT_X8R8G8B8,
D3DFMT_YUY2,
MAKEFOURCC('N','V','1','2'),
};
static const D3DFORMAT support_rt_formats[] =
{
D3DFMT_X8R8G8B8,
MAKEFOURCC('N','V','1','2'),
D3DFMT_YUY2,
};
window = create_window();
d3d = Direct3DCreate9(D3D_SDK_VERSION);
ok(!!d3d, "Failed to create a D3D object.\n");
if (!(device = create_device(d3d, window)))
{
skip("Failed to create a D3D device, skipping tests.\n");
goto done;
}
hr = DXVA2CreateDirect3DDeviceManager9(&token, &manager);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
hr = IDirect3DDeviceManager9_ResetDevice(manager, device, token);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
handle = NULL;
hr = IDirect3DDeviceManager9_OpenDeviceHandle(manager, &handle);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
hr = IDirect3DDeviceManager9_GetVideoService(manager, handle, &IID_IDirectXVideoProcessorService,
(void **)&service);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
memset(&video_desc, 0, sizeof(video_desc));
video_desc.SampleWidth = 64;
video_desc.SampleHeight = 64;
video_desc.Format = MAKEFOURCC('N','V','1','2');
hr = IDirectXVideoProcessorService_CreateVideoProcessor(service, &DXVA2_VideoProcProgressiveDevice, &video_desc,
D3DFMT_A8R8G8B8, 0, &processor);
if (FAILED(hr))
{
win_skip("VideoProcProgressiveDevice is not supported.\n");
goto unsupported;
}
IDirectXVideoProcessor_Release(processor);
for (i = 0; i < ARRAY_SIZE(input_formats); ++i)
{
init_video_desc(&video_desc, input_formats[i]);
/* Check that progressive device is returned for given input format. */
count = 0;
hr = IDirectXVideoProcessorService_GetVideoProcessorDeviceGuids(service, &video_desc, &count, &guids);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
ok(count > 0, "Unexpected device count.\n");
for (j = 0; j < count; ++j)
{
if (IsEqualGUID(&guids[j], &DXVA2_VideoProcProgressiveDevice)) break;
}
ok(j < count, "Expected progressive device for format %#x.\n", input_formats[i]);
CoTaskMemFree(guids);
count = 0;
hr = IDirectXVideoProcessorService_GetVideoProcessorRenderTargets(service, &DXVA2_VideoProcProgressiveDevice,
&video_desc, &count, &rt_formats);
ok(hr == S_OK, "Unexpected hr %#x, format %d.\n", hr, input_formats[i]);
ok(count > 0, "Unexpected format count %u.\n", count);
for (j = 0; j < count; ++j)
{
ok(check_format_list(rt_formats[j], support_rt_formats, ARRAY_SIZE(support_rt_formats)),
"Unexpected rt format %#x for input format %#x.\n", rt_formats[j], input_formats[i]);
}
CoTaskMemFree(rt_formats);
}
memset(&video_desc, 0, sizeof(video_desc));
video_desc.SampleWidth = 64;
video_desc.SampleHeight = 64;
video_desc.Format = MAKEFOURCC('N','V','1','2');
hr = IDirectXVideoProcessorService_CreateVideoProcessor(service, &DXVA2_VideoProcProgressiveDevice, &video_desc,
D3DFMT_A8R8G8B8, 0, &processor);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
hr = IDirectXVideoProcessor_GetVideoProcessorCaps(processor, &caps);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
ok(caps.DeviceCaps == DXVA2_VPDev_HardwareDevice, "Unexpected device type %#x.\n", caps.DeviceCaps);
ok(caps.InputPool == D3DPOOL_DEFAULT, "Unexpected input pool %#x.\n", caps.InputPool);
ok(!caps.NumForwardRefSamples, "Unexpected sample count.\n");
ok(!caps.NumBackwardRefSamples, "Unexpected sample count.\n");
ok(!caps.Reserved, "Unexpected field.\n");
ok((caps.VideoProcessorOperations & processor_ops) == processor_ops, "Unexpected processor operations %#x.\n",
caps.VideoProcessorOperations);
hr = IDirectXVideoProcessor_GetCreationParameters(processor, &guid, NULL, &format, NULL);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
ok(IsEqualGUID(&guid, &DXVA2_VideoProcProgressiveDevice), "Unexpected device guid.\n");
ok(format == D3DFMT_A8R8G8B8, "Unexpected format %u.\n", format);
IDirectXVideoProcessor_Release(processor);
unsupported:
IDirectXVideoProcessorService_Release(service);
hr = IDirect3DDeviceManager9_CloseDeviceHandle(manager, handle);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
IDirect3DDevice9_Release(device);
IDirect3DDeviceManager9_Release(manager);
done:
IDirect3D9_Release(d3d);
DestroyWindow(window);
}
START_TEST(dxva2)
{
test_device_manager();
test_video_processor();
test_progressive_device();
}

View File

@ -956,6 +956,8 @@ static HRESULT WINAPI video_mixer_transform_SetOutputType(IMFTransform *iface, D
if (SUCCEEDED(hr = IDirectXVideoProcessorService_CreateVideoProcessor(service, &mixer->output.rt_formats[i].device,
&video_desc, rt_format, MAX_MIXER_INPUT_SUBSTREAMS, &mixer->processor)))
{
ERR("picked dxva device %s\n", debugstr_guid(&mixer->output.rt_formats[i].device));
if (FAILED(IMFMediaType_GetBlob(type, &MF_MT_GEOMETRIC_APERTURE, (UINT8 *)&mixer->aperture,
sizeof(mixer->aperture), NULL)))
{