gamescope/src/rendervulkan.hpp

415 lines
11 KiB
C++

// Initialize Vulkan and composite stuff with a compute queue
#pragma once
#include <atomic>
#include <stdint.h>
#include <memory>
// 1: Fade Plane (Fade outs between switching focus)
// 2: Video Underlay (The actual video)
// 3: Video Streaming UI (Game, App)
// 4: External Overlay (Mangoapp, etc)
// 5: Primary Overlay (Steam Overlay)
// 6: Cursor
// or
// 1: Fade Plane (Fade outs between switching focus)
// 2: Base Plane (Game, App)
// 3: Override Plane (Dropdowns, etc)
// 4: External Overlay (Mangoapp, etc)
// 5: Primary Overlay (Steam Overlay)
// 6: Cursor
#define k_nMaxLayers 6
#define k_nMaxYcbcrMask 16
#define k_nMaxYcbcrMask_ToPreCompile 3
#define k_nMaxBlurLayers 2
#define kMaxBlurRadius (37u / 2 + 1)
enum BlurMode {
BLUR_MODE_OFF = 0,
BLUR_MODE_COND = 1,
BLUR_MODE_ALWAYS = 2,
};
enum EStreamColorspace : int
{
k_EStreamColorspace_Unknown = 0,
k_EStreamColorspace_BT601 = 1,
k_EStreamColorspace_BT601_Full = 2,
k_EStreamColorspace_BT709 = 3,
k_EStreamColorspace_BT709_Full = 4
};
#include "drm.hpp"
#include <memory>
#include <unordered_map>
#include <vector>
#include <atomic>
#include <wayland-server-core.h>
extern "C" {
#define static
#include <wlr/render/dmabuf.h>
#include <wlr/render/interface.h>
#undef static
}
#define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h>
#include <drm_fourcc.h>
struct VulkanRenderer_t
{
struct wlr_renderer base;
};
struct VulkanWlrTexture_t
{
struct wlr_texture base;
struct wlr_buffer *buf;
};
inline VkFormat ToSrgbVulkanFormat( VkFormat format )
{
switch ( format )
{
case VK_FORMAT_B8G8R8A8_UNORM: return VK_FORMAT_B8G8R8A8_SRGB;
case VK_FORMAT_R8G8B8A8_UNORM: return VK_FORMAT_R8G8B8A8_SRGB;
default: return format;
}
}
inline VkFormat ToLinearVulkanFormat( VkFormat format )
{
switch ( format )
{
case VK_FORMAT_B8G8R8A8_SRGB: return VK_FORMAT_B8G8R8A8_UNORM;
case VK_FORMAT_R8G8B8A8_SRGB: return VK_FORMAT_R8G8B8A8_UNORM;
default: return format;
}
}
inline GamescopeAppTextureColorspace VkColorSpaceToGamescopeAppTextureColorSpace(VkFormat format, VkColorSpaceKHR colorspace)
{
switch (colorspace)
{
default:
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
// We will use image view conversions for these 8888 formats.
if (ToSrgbVulkanFormat(format) != ToLinearVulkanFormat(format))
return GAMESCOPE_APP_TEXTURE_COLORSPACE_LINEAR;
return GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB;
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
return GAMESCOPE_APP_TEXTURE_COLORSPACE_SCRGB;
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
return GAMESCOPE_APP_TEXTURE_COLORSPACE_HDR10_PQ;
}
}
class CVulkanTexture
{
public:
struct createFlags {
createFlags( void )
{
bFlippable = false;
bMappable = false;
bSampled = false;
bStorage = false;
bTransferSrc = false;
bTransferDst = false;
bLinear = false;
bExportable = false;
bSwapchain = false;
imageType = VK_IMAGE_TYPE_2D;
}
bool bFlippable : 1;
bool bMappable : 1;
bool bSampled : 1;
bool bStorage : 1;
bool bTransferSrc : 1;
bool bTransferDst : 1;
bool bLinear : 1;
bool bExportable : 1;
bool bSwapchain : 1;
VkImageType imageType;
};
bool BInit( uint32_t width, uint32_t height, uint32_t depth, uint32_t drmFormat, createFlags flags, wlr_dmabuf_attributes *pDMA = nullptr, uint32_t contentWidth = 0, uint32_t contentHeight = 0 );
bool BInitFromSwapchain( VkImage image, uint32_t width, uint32_t height, VkFormat format );
inline VkImageView view( bool linear ) { return linear ? m_linearView : m_srgbView; }
inline VkImageView linearView() { return m_linearView; }
inline VkImageView srgbView() { return m_srgbView; }
inline VkImageView lumaView() { return m_lumaView; }
inline VkImageView chromaView() { return m_chromaView; }
inline uint32_t width() { return m_width; }
inline uint32_t height() { return m_height; }
inline uint32_t depth() { return m_depth; }
inline uint32_t contentWidth() {return m_contentWidth; }
inline uint32_t contentHeight() {return m_contentHeight; }
inline uint32_t rowPitch() { return m_unRowPitch; }
inline uint32_t fbid() { return m_FBID; }
inline uint8_t *mappedData() { return m_pMappedData; }
inline VkFormat format() const { return m_format; }
inline const struct wlr_dmabuf_attributes& dmabuf() { return m_dmabuf; }
inline VkImage vkImage() { return m_vkImage; }
inline bool swapchainImage() { return m_bSwapchain; }
inline bool externalImage() { return m_bExternal; }
inline VkDeviceSize totalSize() const { return m_size; }
inline uint32_t lumaOffset() const { return m_lumaOffset; }
inline uint32_t lumaRowPitch() const { return m_lumaPitch; }
inline uint32_t chromaOffset() const { return m_chromaOffset; }
inline uint32_t chromaRowPitch() const { return m_chromaPitch; }
inline EStreamColorspace streamColorspace() const { return m_streamColorspace; }
inline void setStreamColorspace(EStreamColorspace colorspace) { m_streamColorspace = colorspace; }
inline bool isYcbcr() const
{
return format() == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
}
int memoryFence();
CVulkanTexture( void );
~CVulkanTexture( void );
private:
bool m_bInitialized = false;
bool m_bExternal = false;
bool m_bSwapchain = false;
VkImage m_vkImage = VK_NULL_HANDLE;
VkDeviceMemory m_vkImageMemory = VK_NULL_HANDLE;
VkImageView m_srgbView = VK_NULL_HANDLE;
VkImageView m_linearView = VK_NULL_HANDLE;
VkImageView m_lumaView = VK_NULL_HANDLE;
VkImageView m_chromaView = VK_NULL_HANDLE;
uint32_t m_width = 0;
uint32_t m_height = 0;
uint32_t m_depth = 0;
uint32_t m_contentWidth = 0;
uint32_t m_contentHeight = 0;
uint32_t m_unRowPitch = 0;
VkDeviceSize m_size = 0;
uint32_t m_lumaOffset = 0;
uint32_t m_lumaPitch = 0;
uint32_t m_chromaOffset = 0;
uint32_t m_chromaPitch = 0;
uint32_t m_FBID = 0;
uint8_t *m_pMappedData = nullptr;
VkFormat m_format = VK_FORMAT_UNDEFINED;
EStreamColorspace m_streamColorspace = k_EStreamColorspace_Unknown;
struct wlr_dmabuf_attributes m_dmabuf = {};
};
struct vec2_t
{
float x, y;
};
struct FrameInfo_t
{
bool useFSRLayer0;
bool useNISLayer0;
BlurMode blurLayer0;
int blurRadius;
std::shared_ptr<CVulkanTexture> shaperLut[EOTF_Count];
std::shared_ptr<CVulkanTexture> lut3D[EOTF_Count];
bool applyOutputColorMgmt;
int layerCount;
struct Layer_t
{
std::shared_ptr<CVulkanTexture> tex;
uint32_t fbid; // TODO pretty sure we can just move this into tex
int zpos;
vec2_t offset;
vec2_t scale;
float opacity;
bool blackBorder;
bool linearFilter;
bool applyColorMgmt;
GamescopeAppTextureColorspace colorspace;
bool isYcbcr() const
{
if ( !tex )
return false;
return tex->isYcbcr();
}
uint32_t integerWidth() const { return tex->width() / scale.x; }
uint32_t integerHeight() const { return tex->height() / scale.y; }
vec2_t offsetPixelCenter() const
{
float x = offset.x + 0.5f / scale.x;
float y = offset.y + 0.5f / scale.y;
return { x, y };
}
} layers[ k_nMaxLayers ];
uint32_t borderMask() const {
uint32_t result = 0;
for (int i = 0; i < layerCount; i++)
{
if (layers[ i ].blackBorder)
result |= 1 << i;
}
return result;
}
uint32_t ycbcrMask() const {
uint32_t result = 0;
for (int i = 0; i < layerCount; i++)
{
if (layers[ i ].isYcbcr())
result |= 1 << i;
}
return result;
}
uint32_t colorspaceMask() const {
uint32_t result = 0;
for (int i = 0; i < layerCount; i++)
{
result |= layers[ i ].colorspace << i * GamescopeAppTextureColorspace_Bits;
}
return result;
}
};
extern uint32_t g_uCompositeDebug;
namespace CompositeDebugFlag
{
static constexpr uint32_t Markers = 1u << 0;
static constexpr uint32_t PlaneBorders = 1u << 1;
static constexpr uint32_t Heatmap = 1u << 2;
static constexpr uint32_t Heatmap_MSWCG = 1u << 3;
static constexpr uint32_t Heatmap_Hard = 1u << 4;
static constexpr uint32_t Tonemap_Reinhard = 1u << 7;
};
VkInstance vulkan_create_instance(void);
bool vulkan_init(VkInstance instance, VkSurfaceKHR surface);
bool vulkan_init_formats(void);
bool vulkan_make_output(VkSurfaceKHR surface);
std::shared_ptr<CVulkanTexture> vulkan_create_texture_from_dmabuf( struct wlr_dmabuf_attributes *pDMA );
std::shared_ptr<CVulkanTexture> vulkan_create_texture_from_bits( uint32_t width, uint32_t height, uint32_t contentWidth, uint32_t contentHeight, uint32_t drmFormat, CVulkanTexture::createFlags texCreateFlags, void *bits );
std::shared_ptr<CVulkanTexture> vulkan_create_texture_from_wlr_buffer( struct wlr_buffer *buf );
bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr<CVulkanTexture> pScreenshotTexture );
std::shared_ptr<CVulkanTexture> vulkan_get_last_output_image( void );
std::shared_ptr<CVulkanTexture> vulkan_acquire_screenshot_texture(uint32_t width, uint32_t height, bool exportable, uint32_t drmFormat, EStreamColorspace colorspace = k_EStreamColorspace_Unknown);
void vulkan_present_to_window( void );
#if HAVE_OPENVR
void vulkan_present_to_openvr( void );
#endif
void vulkan_garbage_collect( void );
bool vulkan_remake_swapchain( void );
bool vulkan_remake_output_images( void );
bool acquire_next_image( void );
bool vulkan_primary_dev_id(dev_t *id);
bool vulkan_supports_modifiers(void);
std::shared_ptr<CVulkanTexture> vulkan_create_1d_lut(uint32_t size);
std::shared_ptr<CVulkanTexture> vulkan_create_3d_lut(uint32_t width, uint32_t height, uint32_t depth);
void vulkan_update_luts(const std::shared_ptr<CVulkanTexture>& lut1d, const std::shared_ptr<CVulkanTexture>& lut3d, void* lut1d_data, void* lut3d_data);
struct wlr_renderer *vulkan_renderer_create( void );
using mat3x4 = std::array<std::array<float, 4>, 3>;
#include "color_helpers.h"
struct gamescope_color_mgmt_t
{
bool enabled;
uint32_t externalDirtyCtr;
nightmode_t nightmode;
float sdrGamutWideness; // user property to widen gamut
float flInternalDisplayBrightness = 500.f;
float flSDROnHDRBrightness = 203.f;
float flHDRInputGain = 1.f;
float flSDRInputGain = 1.f;
// the native colorimetry capabilities of the display
displaycolorimetry_t displayColorimetry;
EOTF displayEOTF;
// the output encoding colorimetry
// ie. for HDR displays we send an explicit 2020 colorimetry packet.
// on SDR displays this is the same as displayColorimetry.
displaycolorimetry_t outputEncodingColorimetry;
EOTF outputEncodingEOTF;
bool operator == (const gamescope_color_mgmt_t&) const = default;
bool operator != (const gamescope_color_mgmt_t&) const = default;
};
static constexpr uint32_t s_nLutEdgeSize3d = 17;
static constexpr uint32_t s_nLutSize1d = 4096;
struct gamescope_color_mgmt_luts
{
bool bHasLut3D = false;
bool bHasLut1D = false;
uint16_t lut3d[s_nLutEdgeSize3d*s_nLutEdgeSize3d*s_nLutEdgeSize3d*4];
uint16_t lut1d[s_nLutSize1d*4];
std::shared_ptr<CVulkanTexture> vk_lut3d;
std::shared_ptr<CVulkanTexture> vk_lut1d;
bool HasLuts() const
{
return bHasLut3D && bHasLut1D;
}
void reset()
{
bHasLut1D = false;
bHasLut3D = false;
}
};
struct gamescope_color_mgmt_tracker_t
{
gamescope_color_mgmt_t pending{};
gamescope_color_mgmt_t current{};
uint32_t serial{};
};
extern gamescope_color_mgmt_tracker_t g_ColorMgmt;
extern gamescope_color_mgmt_luts g_ColorMgmtLuts[ EOTF_Count ];