You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
9.8 KiB
267 lines
9.8 KiB
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
/*****************************************************************************
|
|
* d3d11_tonemap: Direct3D11 VideoProcessor to handle tonemapping
|
|
*****************************************************************************
|
|
* Copyright © 2024 Videolabs, VLC authors and VideoLAN
|
|
*
|
|
* Authors: Steve Lhomme <robux4@videolabs.io>
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "d3d11_tonemap.h"
|
|
|
|
#include <cassert>
|
|
|
|
#include <wrl/client.h>
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
struct d3d11_tonemapper
|
|
{
|
|
ComPtr<ID3D11VideoDevice> d3dviddev;
|
|
ComPtr<ID3D11VideoContext> d3dvidctx;
|
|
ComPtr<ID3D11VideoProcessorEnumerator> enumerator;
|
|
ComPtr<ID3D11VideoProcessor> processor;
|
|
|
|
ComPtr<ID3D11VideoProcessorOutputView> outputView;
|
|
ComPtr<ID3D11ShaderResourceView> SRV;
|
|
picture_sys_d3d11_t picsys{};
|
|
};
|
|
|
|
d3d11_tonemapper *D3D11_TonemapperCreate(vlc_object_t *vd, d3d11_device_t *d3d_dev,
|
|
const video_format_t *in)
|
|
{
|
|
if (!is_d3d11_opaque(in->i_chroma))
|
|
{
|
|
msg_Dbg(vd, "VideoProcessor tone mapping not supported by CPU formats");
|
|
return nullptr;
|
|
}
|
|
|
|
if (in->transfer == TRANSFER_FUNC_SMPTE_ST2084 ||
|
|
in->transfer == TRANSFER_FUNC_HLG)
|
|
{
|
|
return nullptr; // the source is already in HDR
|
|
}
|
|
|
|
ComPtr<ID3D11Texture2D> texture;
|
|
ID3D11Texture2D *_texture[DXGI_MAX_SHADER_VIEW] = {};
|
|
D3D11_TEXTURE2D_DESC texDesc { };
|
|
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outDesc{ };
|
|
d3d11_tonemapper *tonemapProc = new d3d11_tonemapper();
|
|
const auto d3d_fmt = D3D11_RenderFormat(DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_UNKNOWN, false);
|
|
assert(d3d_fmt != nullptr);
|
|
|
|
HRESULT hr;
|
|
hr = d3d_dev->d3ddevice->QueryInterface(IID_GRAPHICS_PPV_ARGS(&tonemapProc->d3dviddev));
|
|
if (unlikely(FAILED(hr)))
|
|
{
|
|
msg_Err(vd, "Could not Query ID3D11VideoDevice Interface. (hr=0x%lX)", hr);
|
|
goto error;
|
|
}
|
|
|
|
hr = d3d_dev->d3dcontext->QueryInterface(IID_GRAPHICS_PPV_ARGS(&tonemapProc->d3dvidctx));
|
|
if (unlikely(FAILED(hr)))
|
|
{
|
|
msg_Err(vd, "Could not Query ID3D11VideoContext Interface. (hr=0x%lX)", hr);
|
|
goto error;
|
|
}
|
|
|
|
{
|
|
D3D11_VIDEO_PROCESSOR_CONTENT_DESC processorDesc{};
|
|
processorDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
|
|
processorDesc.InputFrameRate = {
|
|
in->i_frame_rate, in->i_frame_rate_base,
|
|
};
|
|
processorDesc.InputWidth = in->i_width;
|
|
processorDesc.InputHeight = in->i_height;
|
|
processorDesc.OutputWidth = in->i_width;
|
|
processorDesc.OutputHeight = in->i_height;
|
|
processorDesc.OutputFrameRate = {
|
|
in->i_frame_rate, in->i_frame_rate_base,
|
|
};
|
|
processorDesc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
|
|
hr = tonemapProc->d3dviddev->CreateVideoProcessorEnumerator(&processorDesc, &tonemapProc->enumerator);
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Dbg(vd, "Can't get a video processor for the video (error 0x%lx).", hr);
|
|
goto error;
|
|
}
|
|
|
|
hr = tonemapProc->d3dviddev->CreateVideoProcessor(tonemapProc->enumerator.Get(), 0,
|
|
&tonemapProc->processor);
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Dbg(vd, "failed to create the processor (error 0x%lx).", hr);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
// we can only use this filter with the NVIDIA extension as the VideoProcessor
|
|
// doesn't provide a proper API to set the input and output colorimetry
|
|
|
|
// NVIDIA 545+ driver
|
|
if (d3d_dev->adapterDesc.VendorId != GPU_MANUFACTURER_NVIDIA ||
|
|
(d3d_dev->WDDM.revision * 10000 + d3d_dev->WDDM.build) < 154500)
|
|
goto error;
|
|
|
|
{
|
|
constexpr GUID kNvidiaTrueHDRInterfaceGUID{ 0xfdd62bb4, 0x620b, 0x4fd7, {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3} };
|
|
UINT available = 0;
|
|
d3d11_device_lock(d3d_dev);
|
|
hr = tonemapProc->d3dvidctx->VideoProcessorGetStreamExtension(tonemapProc->processor.Get(),
|
|
0, &kNvidiaTrueHDRInterfaceGUID, sizeof(available), &available);
|
|
|
|
if (!available)
|
|
{
|
|
msg_Warn(vd, "True HDR not supported");
|
|
d3d11_device_unlock(d3d_dev);
|
|
goto error;
|
|
}
|
|
|
|
constexpr UINT kStreamExtensionMethodTrueHDR = 0x3;
|
|
constexpr UINT TrueHDRVersion4 = 4;
|
|
struct {
|
|
UINT version;
|
|
UINT method;
|
|
UINT enable : 1;
|
|
UINT reserved : 31;
|
|
} stream_extension_info = {TrueHDRVersion4,
|
|
kStreamExtensionMethodTrueHDR,
|
|
1u,
|
|
0u};
|
|
hr = tonemapProc->d3dvidctx->VideoProcessorSetStreamExtension(
|
|
tonemapProc->processor.Get(),
|
|
0, &kNvidiaTrueHDRInterfaceGUID,
|
|
sizeof(stream_extension_info), &stream_extension_info);
|
|
if (unlikely(FAILED(hr)))
|
|
{
|
|
msg_Warn(vd, "Failed to enable NVIDIA True HDR");
|
|
}
|
|
d3d11_device_unlock(d3d_dev);
|
|
}
|
|
|
|
// we need a texture that will receive the upscale version
|
|
texDesc.MipLevels = 1;
|
|
texDesc.SampleDesc.Count = 1;
|
|
texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
|
|
texDesc.Usage = D3D11_USAGE_DEFAULT;
|
|
texDesc.CPUAccessFlags = 0;
|
|
texDesc.ArraySize = 1;
|
|
texDesc.Format = d3d_fmt->formatTexture;
|
|
texDesc.Width = in->i_width;
|
|
texDesc.Height = in->i_height;
|
|
texDesc.MiscFlags = 0;
|
|
hr = d3d_dev->d3ddevice->CreateTexture2D(&texDesc, nullptr, texture.GetAddressOf());
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Err(vd, "Failed to create the tonemap texture. (hr=0x%lX)", hr);
|
|
goto error;
|
|
}
|
|
|
|
outDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
|
|
outDesc.Texture2D.MipSlice = 0;
|
|
|
|
hr = tonemapProc->d3dviddev->CreateVideoProcessorOutputView(
|
|
texture.Get(),
|
|
tonemapProc->enumerator.Get(),
|
|
&outDesc,
|
|
tonemapProc->outputView.ReleaseAndGetAddressOf());
|
|
if (FAILED(hr))
|
|
{
|
|
msg_Dbg(vd,"Failed to create processor output. (hr=0x%lX)", hr);
|
|
goto error;
|
|
}
|
|
|
|
_texture[0] = texture.Get();
|
|
_texture[1] = texture.Get();
|
|
_texture[2] = texture.Get();
|
|
_texture[3] = texture.Get();
|
|
if (D3D11_AllocateResourceView(vlc_object_logger(vd), d3d_dev->d3ddevice, d3d_fmt,
|
|
_texture, 0, tonemapProc->SRV.GetAddressOf()) != VLC_SUCCESS)
|
|
goto error;
|
|
|
|
{
|
|
RECT srcRect;
|
|
srcRect.left = 0;
|
|
srcRect.top = 0;
|
|
srcRect.right = texDesc.Width;
|
|
srcRect.bottom = texDesc.Height;
|
|
|
|
RECT dstRect = srcRect;
|
|
|
|
d3d11_device_lock(d3d_dev);
|
|
tonemapProc->d3dvidctx->VideoProcessorSetStreamSourceRect(tonemapProc->processor.Get(),
|
|
0, TRUE, &srcRect);
|
|
|
|
tonemapProc->d3dvidctx->VideoProcessorSetStreamDestRect(tonemapProc->processor.Get(),
|
|
0, TRUE, &dstRect);
|
|
|
|
d3d11_device_unlock(d3d_dev);
|
|
}
|
|
|
|
tonemapProc->picsys.texture[0] = texture.Get();
|
|
tonemapProc->picsys.renderSrc[0] = tonemapProc->SRV.Get();
|
|
|
|
return tonemapProc;
|
|
error:
|
|
delete tonemapProc;
|
|
return nullptr;
|
|
}
|
|
|
|
void D3D11_TonemapperDestroy(d3d11_tonemapper *tonemapProc)
|
|
{
|
|
delete tonemapProc;
|
|
}
|
|
|
|
picture_sys_d3d11_t *D3D11_TonemapperGetOutput(d3d11_tonemapper *tonemapProc)
|
|
{
|
|
return &tonemapProc->picsys;
|
|
}
|
|
|
|
static HRESULT assert_ProcessorInput(vlc_object_t *vd, d3d11_tonemapper *tonemapProc, picture_sys_d3d11_t *p_sys_src)
|
|
{
|
|
if (!p_sys_src->processorInput)
|
|
{
|
|
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inDesc{};
|
|
inDesc.FourCC = 0;
|
|
inDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
|
|
inDesc.Texture2D.MipSlice = 0;
|
|
inDesc.Texture2D.ArraySlice = p_sys_src->slice_index;
|
|
|
|
HRESULT hr;
|
|
|
|
hr = tonemapProc->d3dviddev->CreateVideoProcessorInputView(
|
|
p_sys_src->resource[KNOWN_DXGI_INDEX],
|
|
tonemapProc->enumerator.Get(),
|
|
&inDesc,
|
|
&p_sys_src->processorInput);
|
|
if (FAILED(hr))
|
|
{
|
|
#ifndef NDEBUG
|
|
msg_Dbg(vd,"Failed to create processor input for slice %d. (hr=0x%lX)", p_sys_src->slice_index, hr);
|
|
#endif
|
|
return hr;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT D3D11_TonemapperProcess(vlc_object_t *vd, d3d11_tonemapper *tonemapProc, picture_sys_d3d11_t *in)
|
|
{
|
|
HRESULT hr = assert_ProcessorInput(vd, tonemapProc, in);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
D3D11_VIDEO_PROCESSOR_STREAM stream{};
|
|
stream.Enable = TRUE;
|
|
stream.pInputSurface = in->processorInput;
|
|
|
|
hr = tonemapProc->d3dvidctx->VideoProcessorBlt(tonemapProc->processor.Get(),
|
|
tonemapProc->outputView.Get(),
|
|
0, 1, &stream);
|
|
if (FAILED(hr))
|
|
msg_Err(vd, "Failed to render the texture. (hr=0x%lX)", hr);
|
|
return hr;
|
|
}
|
|
|