Cameron Gutman 17af71fe7a 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.
2024-06-09 15:00:56 -05:00

104 lines
4.8 KiB
C++

#pragma once
#include "renderer.h"
#ifdef Q_OS_WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#endif
#include <libplacebo/log.h>
#include <libplacebo/renderer.h>
#include <libplacebo/vulkan.h>
class PlVkRenderer : public IFFmpegRenderer {
public:
PlVkRenderer(IFFmpegRenderer* backendRenderer);
virtual ~PlVkRenderer() override;
virtual bool initialize(PDECODER_PARAMETERS params) override;
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
virtual void renderFrame(AVFrame* frame) override;
virtual bool testRenderFrame(AVFrame* frame) override;
virtual void waitToRender() override;
virtual void cleanupRenderContext() override;
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override;
virtual int getRendererAttributes() override;
virtual int getDecoderColorRange() override;
virtual int getDecoderCapabilities() override;
virtual bool needsTestFrame() override;
virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override;
virtual AVPixelFormat getPreferredPixelFormat(int videoFormat) override;
virtual RendererType getRendererType() override;
private:
static void lockQueue(AVHWDeviceContext *dev_ctx, uint32_t queue_family, uint32_t index);
static void unlockQueue(AVHWDeviceContext *dev_ctx, uint32_t queue_family, uint32_t index);
static void overlayUploadComplete(void* opaque);
bool mapAvFrameToPlacebo(const AVFrame *frame, pl_frame* mappedFrame);
bool getQueue(VkQueueFlags requiredFlags, uint32_t* queueIndex, uint32_t* queueCount);
bool chooseVulkanDevice(PDECODER_PARAMETERS params, bool hdrOutputRequired);
bool tryInitializeDevice(VkPhysicalDevice device, VkPhysicalDeviceProperties* deviceProps,
PDECODER_PARAMETERS decoderParams, bool hdrOutputRequired);
bool isExtensionSupportedByPhysicalDevice(VkPhysicalDevice device, const char* extensionName);
bool isPresentModeSupportedByPhysicalDevice(VkPhysicalDevice device, VkPresentModeKHR presentMode);
bool isColorSpaceSupportedByPhysicalDevice(VkPhysicalDevice device, VkColorSpaceKHR colorSpace);
bool isSurfacePresentationSupportedByPhysicalDevice(VkPhysicalDevice device);
// The backend renderer if we're frontend-only
IFFmpegRenderer* m_Backend;
// SDL state
SDL_Window* m_Window = nullptr;
// The libplacebo rendering state
pl_log m_Log = nullptr;
pl_vk_inst m_PlVkInstance = nullptr;
VkSurfaceKHR m_VkSurface = nullptr;
pl_vulkan m_Vulkan = nullptr;
pl_swapchain m_Swapchain = nullptr;
pl_renderer m_Renderer = nullptr;
pl_tex m_Textures[PL_MAX_PLANES] = {};
pl_color_space m_LastColorspace = {};
// Pending swapchain state shared between waitToRender(), renderFrame(), and cleanupRenderContext()
pl_swapchain_frame m_SwapchainFrame = {};
bool m_HasPendingSwapchainFrame = false;
// Overlay state
SDL_SpinLock m_OverlayLock = 0;
struct {
// The staging overlay state is copied here under the overlay lock in the render thread.
//
// These values can be safely read by the render thread outside of the overlay lock,
// but the copy from stagingOverlay to overlay must only happen under the overlay
// lock when hasStagingOverlay is true.
bool hasOverlay;
pl_overlay overlay;
// This state is written by the overlay update thread
//
// NB: hasStagingOverlay may be false even if there is a staging overlay texture present,
// because this is how the overlay update path indicates that the overlay is not currently
// safe for the render thread to read.
//
// It is safe for the overlay update thread to write to stagingOverlay outside of the lock,
// as long as hasStagingOverlay is false.
bool hasStagingOverlay;
pl_overlay stagingOverlay;
} m_Overlays[Overlay::OverlayMax] = {};
// Device context used for hwaccel decoders
AVBufferRef* m_HwDeviceCtx = nullptr;
// Vulkan functions we call directly
PFN_vkDestroySurfaceKHR fn_vkDestroySurfaceKHR = nullptr;
PFN_vkGetPhysicalDeviceQueueFamilyProperties fn_vkGetPhysicalDeviceQueueFamilyProperties = nullptr;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fn_vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fn_vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr;
PFN_vkEnumeratePhysicalDevices fn_vkEnumeratePhysicalDevices = nullptr;
PFN_vkGetPhysicalDeviceProperties fn_vkGetPhysicalDeviceProperties = nullptr;
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fn_vkGetPhysicalDeviceSurfaceSupportKHR = nullptr;
PFN_vkEnumerateDeviceExtensionProperties fn_vkEnumerateDeviceExtensionProperties = nullptr;
};