Allow the Vulkan renderer to accept HDR input even without HDR output capability

In addition to resolving issues with mixing HDR and SDR displays and moving
between them while streaming, it also allows streaming HDR content to an SDR
display with tone mapping handled transparently by libplacebo.
This commit is contained in:
Cameron Gutman 2024-06-09 15:00:56 -05:00
parent a0c77d0ad8
commit 17af71fe7a
2 changed files with 21 additions and 19 deletions

View File

@ -148,7 +148,7 @@ PlVkRenderer::~PlVkRenderer()
pl_log_destroy(&m_Log); pl_log_destroy(&m_Log);
} }
bool PlVkRenderer::chooseVulkanDevice(PDECODER_PARAMETERS params) bool PlVkRenderer::chooseVulkanDevice(PDECODER_PARAMETERS params, bool hdrOutputRequired)
{ {
uint32_t physicalDeviceCount = 0; uint32_t physicalDeviceCount = 0;
fn_vkEnumeratePhysicalDevices(m_PlVkInstance->instance, &physicalDeviceCount, nullptr); fn_vkEnumeratePhysicalDevices(m_PlVkInstance->instance, &physicalDeviceCount, nullptr);
@ -161,7 +161,7 @@ bool PlVkRenderer::chooseVulkanDevice(PDECODER_PARAMETERS params)
// First, try the first device in the list to support device selection layers // First, try the first device in the list to support device selection layers
// that put the user's preferred GPU in the first slot. // that put the user's preferred GPU in the first slot.
fn_vkGetPhysicalDeviceProperties(physicalDevices[0], &deviceProps); fn_vkGetPhysicalDeviceProperties(physicalDevices[0], &deviceProps);
if (tryInitializeDevice(physicalDevices[0], &deviceProps, params)) { if (tryInitializeDevice(physicalDevices[0], &deviceProps, params, hdrOutputRequired)) {
return true; return true;
} }
devicesTried.emplace(0); devicesTried.emplace(0);
@ -177,7 +177,7 @@ bool PlVkRenderer::chooseVulkanDevice(PDECODER_PARAMETERS params)
VkPhysicalDeviceProperties deviceProps; VkPhysicalDeviceProperties deviceProps;
fn_vkGetPhysicalDeviceProperties(physicalDevices[i], &deviceProps); fn_vkGetPhysicalDeviceProperties(physicalDevices[i], &deviceProps);
if (deviceProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { if (deviceProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) {
if (tryInitializeDevice(physicalDevices[i], &deviceProps, params)) { if (tryInitializeDevice(physicalDevices[i], &deviceProps, params, hdrOutputRequired)) {
return true; return true;
} }
devicesTried.emplace(i); devicesTried.emplace(i);
@ -194,7 +194,7 @@ bool PlVkRenderer::chooseVulkanDevice(PDECODER_PARAMETERS params)
VkPhysicalDeviceProperties deviceProps; VkPhysicalDeviceProperties deviceProps;
fn_vkGetPhysicalDeviceProperties(physicalDevices[i], &deviceProps); fn_vkGetPhysicalDeviceProperties(physicalDevices[i], &deviceProps);
if (deviceProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { if (deviceProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
if (tryInitializeDevice(physicalDevices[i], &deviceProps, params)) { if (tryInitializeDevice(physicalDevices[i], &deviceProps, params, hdrOutputRequired)) {
return true; return true;
} }
devicesTried.emplace(i); devicesTried.emplace(i);
@ -210,18 +210,20 @@ bool PlVkRenderer::chooseVulkanDevice(PDECODER_PARAMETERS params)
VkPhysicalDeviceProperties deviceProps; VkPhysicalDeviceProperties deviceProps;
fn_vkGetPhysicalDeviceProperties(physicalDevices[i], &deviceProps); fn_vkGetPhysicalDeviceProperties(physicalDevices[i], &deviceProps);
if (tryInitializeDevice(physicalDevices[i], &deviceProps, params)) { if (tryInitializeDevice(physicalDevices[i], &deviceProps, params, hdrOutputRequired)) {
return true; return true;
} }
devicesTried.emplace(i); devicesTried.emplace(i);
} }
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"No suitable Vulkan devices found!"); "No suitable %sVulkan devices found!",
hdrOutputRequired ? "HDR-capable " : "");
return false; return false;
} }
bool PlVkRenderer::tryInitializeDevice(VkPhysicalDevice device, VkPhysicalDeviceProperties* deviceProps, PDECODER_PARAMETERS decoderParams) bool PlVkRenderer::tryInitializeDevice(VkPhysicalDevice device, VkPhysicalDeviceProperties* deviceProps,
PDECODER_PARAMETERS decoderParams, bool hdrOutputRequired)
{ {
// Check the Vulkan API version first to ensure it meets libplacebo's minimum // Check the Vulkan API version first to ensure it meets libplacebo's minimum
if (deviceProps->apiVersion < PL_VK_MIN_VERSION) { if (deviceProps->apiVersion < PL_VK_MIN_VERSION) {
@ -271,7 +273,7 @@ bool PlVkRenderer::tryInitializeDevice(VkPhysicalDevice device, VkPhysicalDevice
return false; return false;
} }
if ((decoderParams->videoFormat & VIDEO_FORMAT_MASK_10BIT) && !isColorSpaceSupportedByPhysicalDevice(device, VK_COLOR_SPACE_HDR10_ST2084_EXT)) { if (hdrOutputRequired && !isColorSpaceSupportedByPhysicalDevice(device, VK_COLOR_SPACE_HDR10_ST2084_EXT)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Vulkan device '%s' does not support HDR10 (ST.2084 PQ)", "Vulkan device '%s' does not support HDR10 (ST.2084 PQ)",
deviceProps->deviceName); deviceProps->deviceName);
@ -390,8 +392,12 @@ bool PlVkRenderer::initialize(PDECODER_PARAMETERS params)
return false; return false;
} }
// Enumerate physical devices and choose one that is suitable for our needs // Enumerate physical devices and choose one that is suitable for our needs.
if (!chooseVulkanDevice(params)) { //
// For HDR streaming, we try to find an HDR-capable Vulkan device first then
// try another search without the HDR requirement if the first attempt fails.
if (!chooseVulkanDevice(params, params->videoFormat & VIDEO_FORMAT_MASK_10BIT) &&
(!(params->videoFormat & VIDEO_FORMAT_MASK_10BIT) || !chooseVulkanDevice(params, false))) {
return false; return false;
} }
@ -896,13 +902,8 @@ bool PlVkRenderer::notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO info)
int PlVkRenderer::getRendererAttributes() int PlVkRenderer::getRendererAttributes()
{ {
int attributes = 0; // This renderer supports HDR (including tone mapping to SDR displays)
return RENDERER_ATTRIBUTE_HDR_SUPPORT;
if (isColorSpaceSupportedByPhysicalDevice(m_Vulkan->phys_device, VK_COLOR_SPACE_HDR10_ST2084_EXT)) {
attributes |= RENDERER_ATTRIBUTE_HDR_SUPPORT;
}
return attributes;
} }
int PlVkRenderer::getDecoderColorRange() int PlVkRenderer::getDecoderColorRange()

View File

@ -37,8 +37,9 @@ private:
bool mapAvFrameToPlacebo(const AVFrame *frame, pl_frame* mappedFrame); bool mapAvFrameToPlacebo(const AVFrame *frame, pl_frame* mappedFrame);
bool getQueue(VkQueueFlags requiredFlags, uint32_t* queueIndex, uint32_t* queueCount); bool getQueue(VkQueueFlags requiredFlags, uint32_t* queueIndex, uint32_t* queueCount);
bool chooseVulkanDevice(PDECODER_PARAMETERS params); bool chooseVulkanDevice(PDECODER_PARAMETERS params, bool hdrOutputRequired);
bool tryInitializeDevice(VkPhysicalDevice device, VkPhysicalDeviceProperties* deviceProps, PDECODER_PARAMETERS decoderParams); bool tryInitializeDevice(VkPhysicalDevice device, VkPhysicalDeviceProperties* deviceProps,
PDECODER_PARAMETERS decoderParams, bool hdrOutputRequired);
bool isExtensionSupportedByPhysicalDevice(VkPhysicalDevice device, const char* extensionName); bool isExtensionSupportedByPhysicalDevice(VkPhysicalDevice device, const char* extensionName);
bool isPresentModeSupportedByPhysicalDevice(VkPhysicalDevice device, VkPresentModeKHR presentMode); bool isPresentModeSupportedByPhysicalDevice(VkPhysicalDevice device, VkPresentModeKHR presentMode);
bool isColorSpaceSupportedByPhysicalDevice(VkPhysicalDevice device, VkColorSpaceKHR colorSpace); bool isColorSpaceSupportedByPhysicalDevice(VkPhysicalDevice device, VkColorSpaceKHR colorSpace);